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