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