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