1 /* 2 * 9p backend 3 * 4 * Copyright IBM, Corp. 2011 5 * 6 * Authors: 7 * Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com> 8 * 9 * This work is licensed under the terms of the GNU GPL, version 2. See 10 * the COPYING file in the top-level directory. 11 * 12 */ 13 14 #include "qemu/osdep.h" 15 #include "fsdev/qemu-fsdev.h" 16 #include "qemu/thread.h" 17 #include "qemu/coroutine.h" 18 #include "coth.h" 19 20 static ssize_t __readlink(V9fsState *s, V9fsPath *path, V9fsString *buf) 21 { 22 ssize_t len, maxlen = PATH_MAX; 23 24 buf->data = g_malloc(PATH_MAX); 25 for(;;) { 26 len = s->ops->readlink(&s->ctx, path, buf->data, maxlen); 27 if (len < 0) { 28 g_free(buf->data); 29 buf->data = NULL; 30 buf->size = 0; 31 break; 32 } else if (len == maxlen) { 33 /* 34 * We dodn't have space to put the NULL or we have more 35 * to read. Increase the size and try again 36 */ 37 maxlen *= 2; 38 g_free(buf->data); 39 buf->data = g_malloc(maxlen); 40 continue; 41 } 42 /* 43 * Null terminate the readlink output 44 */ 45 buf->data[len] = '\0'; 46 buf->size = len; 47 break; 48 } 49 return len; 50 } 51 52 int coroutine_fn v9fs_co_readlink(V9fsPDU *pdu, V9fsPath *path, V9fsString *buf) 53 { 54 int err; 55 V9fsState *s = pdu->s; 56 57 if (v9fs_request_cancelled(pdu)) { 58 return -EINTR; 59 } 60 v9fs_path_read_lock(s); 61 v9fs_co_run_in_worker( 62 { 63 err = __readlink(s, path, buf); 64 if (err < 0) { 65 err = -errno; 66 } 67 }); 68 v9fs_path_unlock(s); 69 return err; 70 } 71 72 int coroutine_fn v9fs_co_statfs(V9fsPDU *pdu, V9fsPath *path, 73 struct statfs *stbuf) 74 { 75 int err; 76 V9fsState *s = pdu->s; 77 78 if (v9fs_request_cancelled(pdu)) { 79 return -EINTR; 80 } 81 v9fs_path_read_lock(s); 82 v9fs_co_run_in_worker( 83 { 84 err = s->ops->statfs(&s->ctx, path, stbuf); 85 if (err < 0) { 86 err = -errno; 87 } 88 }); 89 v9fs_path_unlock(s); 90 return err; 91 } 92 93 int coroutine_fn v9fs_co_chmod(V9fsPDU *pdu, V9fsPath *path, mode_t mode) 94 { 95 int err; 96 FsCred cred; 97 V9fsState *s = pdu->s; 98 99 if (v9fs_request_cancelled(pdu)) { 100 return -EINTR; 101 } 102 cred_init(&cred); 103 cred.fc_mode = mode; 104 v9fs_path_read_lock(s); 105 v9fs_co_run_in_worker( 106 { 107 err = s->ops->chmod(&s->ctx, path, &cred); 108 if (err < 0) { 109 err = -errno; 110 } 111 }); 112 v9fs_path_unlock(s); 113 return err; 114 } 115 116 int coroutine_fn v9fs_co_utimensat(V9fsPDU *pdu, V9fsPath *path, 117 struct timespec times[2]) 118 { 119 int err; 120 V9fsState *s = pdu->s; 121 122 if (v9fs_request_cancelled(pdu)) { 123 return -EINTR; 124 } 125 v9fs_path_read_lock(s); 126 v9fs_co_run_in_worker( 127 { 128 err = s->ops->utimensat(&s->ctx, path, times); 129 if (err < 0) { 130 err = -errno; 131 } 132 }); 133 v9fs_path_unlock(s); 134 return err; 135 } 136 137 int coroutine_fn v9fs_co_chown(V9fsPDU *pdu, V9fsPath *path, uid_t uid, 138 gid_t gid) 139 { 140 int err; 141 FsCred cred; 142 V9fsState *s = pdu->s; 143 144 if (v9fs_request_cancelled(pdu)) { 145 return -EINTR; 146 } 147 cred_init(&cred); 148 cred.fc_uid = uid; 149 cred.fc_gid = gid; 150 v9fs_path_read_lock(s); 151 v9fs_co_run_in_worker( 152 { 153 err = s->ops->chown(&s->ctx, path, &cred); 154 if (err < 0) { 155 err = -errno; 156 } 157 }); 158 v9fs_path_unlock(s); 159 return err; 160 } 161 162 int coroutine_fn v9fs_co_truncate(V9fsPDU *pdu, V9fsPath *path, off_t size) 163 { 164 int err; 165 V9fsState *s = pdu->s; 166 167 if (v9fs_request_cancelled(pdu)) { 168 return -EINTR; 169 } 170 v9fs_path_read_lock(s); 171 v9fs_co_run_in_worker( 172 { 173 err = s->ops->truncate(&s->ctx, path, size); 174 if (err < 0) { 175 err = -errno; 176 } 177 }); 178 v9fs_path_unlock(s); 179 return err; 180 } 181 182 int coroutine_fn v9fs_co_mknod(V9fsPDU *pdu, V9fsFidState *fidp, 183 V9fsString *name, uid_t uid, gid_t gid, 184 dev_t dev, mode_t mode, struct stat *stbuf) 185 { 186 int err; 187 V9fsPath path; 188 FsCred cred; 189 V9fsState *s = pdu->s; 190 191 if (v9fs_request_cancelled(pdu)) { 192 return -EINTR; 193 } 194 cred_init(&cred); 195 cred.fc_uid = uid; 196 cred.fc_gid = gid; 197 cred.fc_mode = mode; 198 cred.fc_rdev = dev; 199 v9fs_path_read_lock(s); 200 v9fs_co_run_in_worker( 201 { 202 err = s->ops->mknod(&s->ctx, &fidp->path, name->data, &cred); 203 if (err < 0) { 204 err = -errno; 205 } else { 206 v9fs_path_init(&path); 207 err = v9fs_name_to_path(s, &fidp->path, name->data, &path); 208 if (!err) { 209 err = s->ops->lstat(&s->ctx, &path, stbuf); 210 if (err < 0) { 211 err = -errno; 212 } 213 } 214 v9fs_path_free(&path); 215 } 216 }); 217 v9fs_path_unlock(s); 218 return err; 219 } 220 221 /* Only works with path name based fid */ 222 int coroutine_fn v9fs_co_remove(V9fsPDU *pdu, V9fsPath *path) 223 { 224 int err; 225 V9fsState *s = pdu->s; 226 227 if (v9fs_request_cancelled(pdu)) { 228 return -EINTR; 229 } 230 v9fs_path_read_lock(s); 231 v9fs_co_run_in_worker( 232 { 233 err = s->ops->remove(&s->ctx, path->data); 234 if (err < 0) { 235 err = -errno; 236 } 237 }); 238 v9fs_path_unlock(s); 239 return err; 240 } 241 242 int coroutine_fn v9fs_co_unlinkat(V9fsPDU *pdu, V9fsPath *path, 243 V9fsString *name, int flags) 244 { 245 int err; 246 V9fsState *s = pdu->s; 247 248 if (v9fs_request_cancelled(pdu)) { 249 return -EINTR; 250 } 251 v9fs_path_read_lock(s); 252 v9fs_co_run_in_worker( 253 { 254 err = s->ops->unlinkat(&s->ctx, path, name->data, flags); 255 if (err < 0) { 256 err = -errno; 257 } 258 }); 259 v9fs_path_unlock(s); 260 return err; 261 } 262 263 /* Only work with path name based fid */ 264 int coroutine_fn v9fs_co_rename(V9fsPDU *pdu, V9fsPath *oldpath, 265 V9fsPath *newpath) 266 { 267 int err; 268 V9fsState *s = pdu->s; 269 270 if (v9fs_request_cancelled(pdu)) { 271 return -EINTR; 272 } 273 v9fs_co_run_in_worker( 274 { 275 err = s->ops->rename(&s->ctx, oldpath->data, newpath->data); 276 if (err < 0) { 277 err = -errno; 278 } 279 }); 280 return err; 281 } 282 283 int coroutine_fn v9fs_co_renameat(V9fsPDU *pdu, V9fsPath *olddirpath, 284 V9fsString *oldname, V9fsPath *newdirpath, 285 V9fsString *newname) 286 { 287 int err; 288 V9fsState *s = pdu->s; 289 290 if (v9fs_request_cancelled(pdu)) { 291 return -EINTR; 292 } 293 v9fs_co_run_in_worker( 294 { 295 err = s->ops->renameat(&s->ctx, olddirpath, oldname->data, 296 newdirpath, newname->data); 297 if (err < 0) { 298 err = -errno; 299 } 300 }); 301 return err; 302 } 303 304 int coroutine_fn v9fs_co_symlink(V9fsPDU *pdu, V9fsFidState *dfidp, 305 V9fsString *name, const char *oldpath, 306 gid_t gid, struct stat *stbuf) 307 { 308 int err; 309 FsCred cred; 310 V9fsPath path; 311 V9fsState *s = pdu->s; 312 313 if (v9fs_request_cancelled(pdu)) { 314 return -EINTR; 315 } 316 cred_init(&cred); 317 cred.fc_uid = dfidp->uid; 318 cred.fc_gid = gid; 319 cred.fc_mode = 0777; 320 v9fs_path_read_lock(s); 321 v9fs_co_run_in_worker( 322 { 323 err = s->ops->symlink(&s->ctx, oldpath, &dfidp->path, 324 name->data, &cred); 325 if (err < 0) { 326 err = -errno; 327 } else { 328 v9fs_path_init(&path); 329 err = v9fs_name_to_path(s, &dfidp->path, name->data, &path); 330 if (!err) { 331 err = s->ops->lstat(&s->ctx, &path, stbuf); 332 if (err < 0) { 333 err = -errno; 334 } 335 } 336 v9fs_path_free(&path); 337 } 338 }); 339 v9fs_path_unlock(s); 340 return err; 341 } 342 343 /* 344 * For path name based fid we don't block. So we can 345 * directly call the fs driver ops. 346 */ 347 int coroutine_fn v9fs_co_name_to_path(V9fsPDU *pdu, V9fsPath *dirpath, 348 const char *name, V9fsPath *path) 349 { 350 int err; 351 V9fsState *s = pdu->s; 352 353 if (s->ctx.export_flags & V9FS_PATHNAME_FSCONTEXT) { 354 err = s->ops->name_to_path(&s->ctx, dirpath, name, path); 355 if (err < 0) { 356 err = -errno; 357 } 358 } else { 359 if (v9fs_request_cancelled(pdu)) { 360 return -EINTR; 361 } 362 v9fs_co_run_in_worker( 363 { 364 err = s->ops->name_to_path(&s->ctx, dirpath, name, path); 365 if (err < 0) { 366 err = -errno; 367 } 368 }); 369 } 370 return err; 371 } 372