xref: /openbmc/qemu/hw/9pfs/9p-local.c (revision f00d4f596b1c85cf9449ef4b65fca46148bcc9bb)
1*f00d4f59SWei Liu /*
2*f00d4f59SWei Liu  * 9p Posix callback
3*f00d4f59SWei Liu  *
4*f00d4f59SWei Liu  * Copyright IBM, Corp. 2010
5*f00d4f59SWei Liu  *
6*f00d4f59SWei Liu  * Authors:
7*f00d4f59SWei Liu  *  Anthony Liguori   <aliguori@us.ibm.com>
8*f00d4f59SWei Liu  *
9*f00d4f59SWei Liu  * This work is licensed under the terms of the GNU GPL, version 2.  See
10*f00d4f59SWei Liu  * the COPYING file in the top-level directory.
11*f00d4f59SWei Liu  *
12*f00d4f59SWei Liu  */
13*f00d4f59SWei Liu 
14*f00d4f59SWei Liu #include "virtio-9p.h"
15*f00d4f59SWei Liu #include "virtio-9p-xattr.h"
16*f00d4f59SWei Liu #include "fsdev/qemu-fsdev.h"   /* local_ops */
17*f00d4f59SWei Liu #include <arpa/inet.h>
18*f00d4f59SWei Liu #include <pwd.h>
19*f00d4f59SWei Liu #include <grp.h>
20*f00d4f59SWei Liu #include <sys/socket.h>
21*f00d4f59SWei Liu #include <sys/un.h>
22*f00d4f59SWei Liu #include "qemu/xattr.h"
23*f00d4f59SWei Liu #include <libgen.h>
24*f00d4f59SWei Liu #include <linux/fs.h>
25*f00d4f59SWei Liu #ifdef CONFIG_LINUX_MAGIC_H
26*f00d4f59SWei Liu #include <linux/magic.h>
27*f00d4f59SWei Liu #endif
28*f00d4f59SWei Liu #include <sys/ioctl.h>
29*f00d4f59SWei Liu 
30*f00d4f59SWei Liu #ifndef XFS_SUPER_MAGIC
31*f00d4f59SWei Liu #define XFS_SUPER_MAGIC  0x58465342
32*f00d4f59SWei Liu #endif
33*f00d4f59SWei Liu #ifndef EXT2_SUPER_MAGIC
34*f00d4f59SWei Liu #define EXT2_SUPER_MAGIC 0xEF53
35*f00d4f59SWei Liu #endif
36*f00d4f59SWei Liu #ifndef REISERFS_SUPER_MAGIC
37*f00d4f59SWei Liu #define REISERFS_SUPER_MAGIC 0x52654973
38*f00d4f59SWei Liu #endif
39*f00d4f59SWei Liu #ifndef BTRFS_SUPER_MAGIC
40*f00d4f59SWei Liu #define BTRFS_SUPER_MAGIC 0x9123683E
41*f00d4f59SWei Liu #endif
42*f00d4f59SWei Liu 
43*f00d4f59SWei Liu #define VIRTFS_META_DIR ".virtfs_metadata"
44*f00d4f59SWei Liu 
45*f00d4f59SWei Liu static char *local_mapped_attr_path(FsContext *ctx, const char *path)
46*f00d4f59SWei Liu {
47*f00d4f59SWei Liu     int dirlen;
48*f00d4f59SWei Liu     const char *name = strrchr(path, '/');
49*f00d4f59SWei Liu     if (name) {
50*f00d4f59SWei Liu         dirlen = name - path;
51*f00d4f59SWei Liu         ++name;
52*f00d4f59SWei Liu     } else {
53*f00d4f59SWei Liu         name = path;
54*f00d4f59SWei Liu         dirlen = 0;
55*f00d4f59SWei Liu     }
56*f00d4f59SWei Liu     return g_strdup_printf("%s/%.*s/%s/%s", ctx->fs_root,
57*f00d4f59SWei Liu                            dirlen, path, VIRTFS_META_DIR, name);
58*f00d4f59SWei Liu }
59*f00d4f59SWei Liu 
60*f00d4f59SWei Liu static FILE *local_fopen(const char *path, const char *mode)
61*f00d4f59SWei Liu {
62*f00d4f59SWei Liu     int fd, o_mode = 0;
63*f00d4f59SWei Liu     FILE *fp;
64*f00d4f59SWei Liu     int flags = O_NOFOLLOW;
65*f00d4f59SWei Liu     /*
66*f00d4f59SWei Liu      * only supports two modes
67*f00d4f59SWei Liu      */
68*f00d4f59SWei Liu     if (mode[0] == 'r') {
69*f00d4f59SWei Liu         flags |= O_RDONLY;
70*f00d4f59SWei Liu     } else if (mode[0] == 'w') {
71*f00d4f59SWei Liu         flags |= O_WRONLY | O_TRUNC | O_CREAT;
72*f00d4f59SWei Liu         o_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
73*f00d4f59SWei Liu     } else {
74*f00d4f59SWei Liu         return NULL;
75*f00d4f59SWei Liu     }
76*f00d4f59SWei Liu     fd = open(path, flags, o_mode);
77*f00d4f59SWei Liu     if (fd == -1) {
78*f00d4f59SWei Liu         return NULL;
79*f00d4f59SWei Liu     }
80*f00d4f59SWei Liu     fp = fdopen(fd, mode);
81*f00d4f59SWei Liu     if (!fp) {
82*f00d4f59SWei Liu         close(fd);
83*f00d4f59SWei Liu     }
84*f00d4f59SWei Liu     return fp;
85*f00d4f59SWei Liu }
86*f00d4f59SWei Liu 
87*f00d4f59SWei Liu #define ATTR_MAX 100
88*f00d4f59SWei Liu static void local_mapped_file_attr(FsContext *ctx, const char *path,
89*f00d4f59SWei Liu                                    struct stat *stbuf)
90*f00d4f59SWei Liu {
91*f00d4f59SWei Liu     FILE *fp;
92*f00d4f59SWei Liu     char buf[ATTR_MAX];
93*f00d4f59SWei Liu     char *attr_path;
94*f00d4f59SWei Liu 
95*f00d4f59SWei Liu     attr_path = local_mapped_attr_path(ctx, path);
96*f00d4f59SWei Liu     fp = local_fopen(attr_path, "r");
97*f00d4f59SWei Liu     g_free(attr_path);
98*f00d4f59SWei Liu     if (!fp) {
99*f00d4f59SWei Liu         return;
100*f00d4f59SWei Liu     }
101*f00d4f59SWei Liu     memset(buf, 0, ATTR_MAX);
102*f00d4f59SWei Liu     while (fgets(buf, ATTR_MAX, fp)) {
103*f00d4f59SWei Liu         if (!strncmp(buf, "virtfs.uid", 10)) {
104*f00d4f59SWei Liu             stbuf->st_uid = atoi(buf+11);
105*f00d4f59SWei Liu         } else if (!strncmp(buf, "virtfs.gid", 10)) {
106*f00d4f59SWei Liu             stbuf->st_gid = atoi(buf+11);
107*f00d4f59SWei Liu         } else if (!strncmp(buf, "virtfs.mode", 11)) {
108*f00d4f59SWei Liu             stbuf->st_mode = atoi(buf+12);
109*f00d4f59SWei Liu         } else if (!strncmp(buf, "virtfs.rdev", 11)) {
110*f00d4f59SWei Liu             stbuf->st_rdev = atoi(buf+12);
111*f00d4f59SWei Liu         }
112*f00d4f59SWei Liu         memset(buf, 0, ATTR_MAX);
113*f00d4f59SWei Liu     }
114*f00d4f59SWei Liu     fclose(fp);
115*f00d4f59SWei Liu }
116*f00d4f59SWei Liu 
117*f00d4f59SWei Liu static int local_lstat(FsContext *fs_ctx, V9fsPath *fs_path, struct stat *stbuf)
118*f00d4f59SWei Liu {
119*f00d4f59SWei Liu     int err;
120*f00d4f59SWei Liu     char *buffer;
121*f00d4f59SWei Liu     char *path = fs_path->data;
122*f00d4f59SWei Liu 
123*f00d4f59SWei Liu     buffer = rpath(fs_ctx, path);
124*f00d4f59SWei Liu     err =  lstat(buffer, stbuf);
125*f00d4f59SWei Liu     if (err) {
126*f00d4f59SWei Liu         goto err_out;
127*f00d4f59SWei Liu     }
128*f00d4f59SWei Liu     if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
129*f00d4f59SWei Liu         /* Actual credentials are part of extended attrs */
130*f00d4f59SWei Liu         uid_t tmp_uid;
131*f00d4f59SWei Liu         gid_t tmp_gid;
132*f00d4f59SWei Liu         mode_t tmp_mode;
133*f00d4f59SWei Liu         dev_t tmp_dev;
134*f00d4f59SWei Liu         if (getxattr(buffer, "user.virtfs.uid", &tmp_uid, sizeof(uid_t)) > 0) {
135*f00d4f59SWei Liu             stbuf->st_uid = le32_to_cpu(tmp_uid);
136*f00d4f59SWei Liu         }
137*f00d4f59SWei Liu         if (getxattr(buffer, "user.virtfs.gid", &tmp_gid, sizeof(gid_t)) > 0) {
138*f00d4f59SWei Liu             stbuf->st_gid = le32_to_cpu(tmp_gid);
139*f00d4f59SWei Liu         }
140*f00d4f59SWei Liu         if (getxattr(buffer, "user.virtfs.mode",
141*f00d4f59SWei Liu                     &tmp_mode, sizeof(mode_t)) > 0) {
142*f00d4f59SWei Liu             stbuf->st_mode = le32_to_cpu(tmp_mode);
143*f00d4f59SWei Liu         }
144*f00d4f59SWei Liu         if (getxattr(buffer, "user.virtfs.rdev", &tmp_dev, sizeof(dev_t)) > 0) {
145*f00d4f59SWei Liu             stbuf->st_rdev = le64_to_cpu(tmp_dev);
146*f00d4f59SWei Liu         }
147*f00d4f59SWei Liu     } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
148*f00d4f59SWei Liu         local_mapped_file_attr(fs_ctx, path, stbuf);
149*f00d4f59SWei Liu     }
150*f00d4f59SWei Liu 
151*f00d4f59SWei Liu err_out:
152*f00d4f59SWei Liu     g_free(buffer);
153*f00d4f59SWei Liu     return err;
154*f00d4f59SWei Liu }
155*f00d4f59SWei Liu 
156*f00d4f59SWei Liu static int local_create_mapped_attr_dir(FsContext *ctx, const char *path)
157*f00d4f59SWei Liu {
158*f00d4f59SWei Liu     int err;
159*f00d4f59SWei Liu     char *attr_dir;
160*f00d4f59SWei Liu     char *tmp_path = g_strdup(path);
161*f00d4f59SWei Liu 
162*f00d4f59SWei Liu     attr_dir = g_strdup_printf("%s/%s/%s",
163*f00d4f59SWei Liu              ctx->fs_root, dirname(tmp_path), VIRTFS_META_DIR);
164*f00d4f59SWei Liu 
165*f00d4f59SWei Liu     err = mkdir(attr_dir, 0700);
166*f00d4f59SWei Liu     if (err < 0 && errno == EEXIST) {
167*f00d4f59SWei Liu         err = 0;
168*f00d4f59SWei Liu     }
169*f00d4f59SWei Liu     g_free(attr_dir);
170*f00d4f59SWei Liu     g_free(tmp_path);
171*f00d4f59SWei Liu     return err;
172*f00d4f59SWei Liu }
173*f00d4f59SWei Liu 
174*f00d4f59SWei Liu static int local_set_mapped_file_attr(FsContext *ctx,
175*f00d4f59SWei Liu                                       const char *path, FsCred *credp)
176*f00d4f59SWei Liu {
177*f00d4f59SWei Liu     FILE *fp;
178*f00d4f59SWei Liu     int ret = 0;
179*f00d4f59SWei Liu     char buf[ATTR_MAX];
180*f00d4f59SWei Liu     char *attr_path;
181*f00d4f59SWei Liu     int uid = -1, gid = -1, mode = -1, rdev = -1;
182*f00d4f59SWei Liu 
183*f00d4f59SWei Liu     attr_path = local_mapped_attr_path(ctx, path);
184*f00d4f59SWei Liu     fp = local_fopen(attr_path, "r");
185*f00d4f59SWei Liu     if (!fp) {
186*f00d4f59SWei Liu         goto create_map_file;
187*f00d4f59SWei Liu     }
188*f00d4f59SWei Liu     memset(buf, 0, ATTR_MAX);
189*f00d4f59SWei Liu     while (fgets(buf, ATTR_MAX, fp)) {
190*f00d4f59SWei Liu         if (!strncmp(buf, "virtfs.uid", 10)) {
191*f00d4f59SWei Liu             uid = atoi(buf+11);
192*f00d4f59SWei Liu         } else if (!strncmp(buf, "virtfs.gid", 10)) {
193*f00d4f59SWei Liu             gid = atoi(buf+11);
194*f00d4f59SWei Liu         } else if (!strncmp(buf, "virtfs.mode", 11)) {
195*f00d4f59SWei Liu             mode = atoi(buf+12);
196*f00d4f59SWei Liu         } else if (!strncmp(buf, "virtfs.rdev", 11)) {
197*f00d4f59SWei Liu             rdev = atoi(buf+12);
198*f00d4f59SWei Liu         }
199*f00d4f59SWei Liu         memset(buf, 0, ATTR_MAX);
200*f00d4f59SWei Liu     }
201*f00d4f59SWei Liu     fclose(fp);
202*f00d4f59SWei Liu     goto update_map_file;
203*f00d4f59SWei Liu 
204*f00d4f59SWei Liu create_map_file:
205*f00d4f59SWei Liu     ret = local_create_mapped_attr_dir(ctx, path);
206*f00d4f59SWei Liu     if (ret < 0) {
207*f00d4f59SWei Liu         goto err_out;
208*f00d4f59SWei Liu     }
209*f00d4f59SWei Liu 
210*f00d4f59SWei Liu update_map_file:
211*f00d4f59SWei Liu     fp = local_fopen(attr_path, "w");
212*f00d4f59SWei Liu     if (!fp) {
213*f00d4f59SWei Liu         ret = -1;
214*f00d4f59SWei Liu         goto err_out;
215*f00d4f59SWei Liu     }
216*f00d4f59SWei Liu 
217*f00d4f59SWei Liu     if (credp->fc_uid != -1) {
218*f00d4f59SWei Liu         uid = credp->fc_uid;
219*f00d4f59SWei Liu     }
220*f00d4f59SWei Liu     if (credp->fc_gid != -1) {
221*f00d4f59SWei Liu         gid = credp->fc_gid;
222*f00d4f59SWei Liu     }
223*f00d4f59SWei Liu     if (credp->fc_mode != -1) {
224*f00d4f59SWei Liu         mode = credp->fc_mode;
225*f00d4f59SWei Liu     }
226*f00d4f59SWei Liu     if (credp->fc_rdev != -1) {
227*f00d4f59SWei Liu         rdev = credp->fc_rdev;
228*f00d4f59SWei Liu     }
229*f00d4f59SWei Liu 
230*f00d4f59SWei Liu 
231*f00d4f59SWei Liu     if (uid != -1) {
232*f00d4f59SWei Liu         fprintf(fp, "virtfs.uid=%d\n", uid);
233*f00d4f59SWei Liu     }
234*f00d4f59SWei Liu     if (gid != -1) {
235*f00d4f59SWei Liu         fprintf(fp, "virtfs.gid=%d\n", gid);
236*f00d4f59SWei Liu     }
237*f00d4f59SWei Liu     if (mode != -1) {
238*f00d4f59SWei Liu         fprintf(fp, "virtfs.mode=%d\n", mode);
239*f00d4f59SWei Liu     }
240*f00d4f59SWei Liu     if (rdev != -1) {
241*f00d4f59SWei Liu         fprintf(fp, "virtfs.rdev=%d\n", rdev);
242*f00d4f59SWei Liu     }
243*f00d4f59SWei Liu     fclose(fp);
244*f00d4f59SWei Liu 
245*f00d4f59SWei Liu err_out:
246*f00d4f59SWei Liu     g_free(attr_path);
247*f00d4f59SWei Liu     return ret;
248*f00d4f59SWei Liu }
249*f00d4f59SWei Liu 
250*f00d4f59SWei Liu static int local_set_xattr(const char *path, FsCred *credp)
251*f00d4f59SWei Liu {
252*f00d4f59SWei Liu     int err;
253*f00d4f59SWei Liu 
254*f00d4f59SWei Liu     if (credp->fc_uid != -1) {
255*f00d4f59SWei Liu         uint32_t tmp_uid = cpu_to_le32(credp->fc_uid);
256*f00d4f59SWei Liu         err = setxattr(path, "user.virtfs.uid", &tmp_uid, sizeof(uid_t), 0);
257*f00d4f59SWei Liu         if (err) {
258*f00d4f59SWei Liu             return err;
259*f00d4f59SWei Liu         }
260*f00d4f59SWei Liu     }
261*f00d4f59SWei Liu     if (credp->fc_gid != -1) {
262*f00d4f59SWei Liu         uint32_t tmp_gid = cpu_to_le32(credp->fc_gid);
263*f00d4f59SWei Liu         err = setxattr(path, "user.virtfs.gid", &tmp_gid, sizeof(gid_t), 0);
264*f00d4f59SWei Liu         if (err) {
265*f00d4f59SWei Liu             return err;
266*f00d4f59SWei Liu         }
267*f00d4f59SWei Liu     }
268*f00d4f59SWei Liu     if (credp->fc_mode != -1) {
269*f00d4f59SWei Liu         uint32_t tmp_mode = cpu_to_le32(credp->fc_mode);
270*f00d4f59SWei Liu         err = setxattr(path, "user.virtfs.mode", &tmp_mode, sizeof(mode_t), 0);
271*f00d4f59SWei Liu         if (err) {
272*f00d4f59SWei Liu             return err;
273*f00d4f59SWei Liu         }
274*f00d4f59SWei Liu     }
275*f00d4f59SWei Liu     if (credp->fc_rdev != -1) {
276*f00d4f59SWei Liu         uint64_t tmp_rdev = cpu_to_le64(credp->fc_rdev);
277*f00d4f59SWei Liu         err = setxattr(path, "user.virtfs.rdev", &tmp_rdev, sizeof(dev_t), 0);
278*f00d4f59SWei Liu         if (err) {
279*f00d4f59SWei Liu             return err;
280*f00d4f59SWei Liu         }
281*f00d4f59SWei Liu     }
282*f00d4f59SWei Liu     return 0;
283*f00d4f59SWei Liu }
284*f00d4f59SWei Liu 
285*f00d4f59SWei Liu static int local_post_create_passthrough(FsContext *fs_ctx, const char *path,
286*f00d4f59SWei Liu                                          FsCred *credp)
287*f00d4f59SWei Liu {
288*f00d4f59SWei Liu     char *buffer;
289*f00d4f59SWei Liu 
290*f00d4f59SWei Liu     buffer = rpath(fs_ctx, path);
291*f00d4f59SWei Liu     if (lchown(buffer, credp->fc_uid, credp->fc_gid) < 0) {
292*f00d4f59SWei Liu         /*
293*f00d4f59SWei Liu          * If we fail to change ownership and if we are
294*f00d4f59SWei Liu          * using security model none. Ignore the error
295*f00d4f59SWei Liu          */
296*f00d4f59SWei Liu         if ((fs_ctx->export_flags & V9FS_SEC_MASK) != V9FS_SM_NONE) {
297*f00d4f59SWei Liu             goto err;
298*f00d4f59SWei Liu         }
299*f00d4f59SWei Liu     }
300*f00d4f59SWei Liu 
301*f00d4f59SWei Liu     if (chmod(buffer, credp->fc_mode & 07777) < 0) {
302*f00d4f59SWei Liu         goto err;
303*f00d4f59SWei Liu     }
304*f00d4f59SWei Liu 
305*f00d4f59SWei Liu     g_free(buffer);
306*f00d4f59SWei Liu     return 0;
307*f00d4f59SWei Liu err:
308*f00d4f59SWei Liu     g_free(buffer);
309*f00d4f59SWei Liu     return -1;
310*f00d4f59SWei Liu }
311*f00d4f59SWei Liu 
312*f00d4f59SWei Liu static ssize_t local_readlink(FsContext *fs_ctx, V9fsPath *fs_path,
313*f00d4f59SWei Liu                               char *buf, size_t bufsz)
314*f00d4f59SWei Liu {
315*f00d4f59SWei Liu     ssize_t tsize = -1;
316*f00d4f59SWei Liu     char *buffer;
317*f00d4f59SWei Liu     char *path = fs_path->data;
318*f00d4f59SWei Liu 
319*f00d4f59SWei Liu     if ((fs_ctx->export_flags & V9FS_SM_MAPPED) ||
320*f00d4f59SWei Liu         (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE)) {
321*f00d4f59SWei Liu         int fd;
322*f00d4f59SWei Liu         buffer = rpath(fs_ctx, path);
323*f00d4f59SWei Liu         fd = open(buffer, O_RDONLY | O_NOFOLLOW);
324*f00d4f59SWei Liu         g_free(buffer);
325*f00d4f59SWei Liu         if (fd == -1) {
326*f00d4f59SWei Liu             return -1;
327*f00d4f59SWei Liu         }
328*f00d4f59SWei Liu         do {
329*f00d4f59SWei Liu             tsize = read(fd, (void *)buf, bufsz);
330*f00d4f59SWei Liu         } while (tsize == -1 && errno == EINTR);
331*f00d4f59SWei Liu         close(fd);
332*f00d4f59SWei Liu     } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
333*f00d4f59SWei Liu                (fs_ctx->export_flags & V9FS_SM_NONE)) {
334*f00d4f59SWei Liu         buffer = rpath(fs_ctx, path);
335*f00d4f59SWei Liu         tsize = readlink(buffer, buf, bufsz);
336*f00d4f59SWei Liu         g_free(buffer);
337*f00d4f59SWei Liu     }
338*f00d4f59SWei Liu     return tsize;
339*f00d4f59SWei Liu }
340*f00d4f59SWei Liu 
341*f00d4f59SWei Liu static int local_close(FsContext *ctx, V9fsFidOpenState *fs)
342*f00d4f59SWei Liu {
343*f00d4f59SWei Liu     return close(fs->fd);
344*f00d4f59SWei Liu }
345*f00d4f59SWei Liu 
346*f00d4f59SWei Liu static int local_closedir(FsContext *ctx, V9fsFidOpenState *fs)
347*f00d4f59SWei Liu {
348*f00d4f59SWei Liu     return closedir(fs->dir);
349*f00d4f59SWei Liu }
350*f00d4f59SWei Liu 
351*f00d4f59SWei Liu static int local_open(FsContext *ctx, V9fsPath *fs_path,
352*f00d4f59SWei Liu                       int flags, V9fsFidOpenState *fs)
353*f00d4f59SWei Liu {
354*f00d4f59SWei Liu     char *buffer;
355*f00d4f59SWei Liu     char *path = fs_path->data;
356*f00d4f59SWei Liu 
357*f00d4f59SWei Liu     buffer = rpath(ctx, path);
358*f00d4f59SWei Liu     fs->fd = open(buffer, flags | O_NOFOLLOW);
359*f00d4f59SWei Liu     g_free(buffer);
360*f00d4f59SWei Liu     return fs->fd;
361*f00d4f59SWei Liu }
362*f00d4f59SWei Liu 
363*f00d4f59SWei Liu static int local_opendir(FsContext *ctx,
364*f00d4f59SWei Liu                          V9fsPath *fs_path, V9fsFidOpenState *fs)
365*f00d4f59SWei Liu {
366*f00d4f59SWei Liu     char *buffer;
367*f00d4f59SWei Liu     char *path = fs_path->data;
368*f00d4f59SWei Liu 
369*f00d4f59SWei Liu     buffer = rpath(ctx, path);
370*f00d4f59SWei Liu     fs->dir = opendir(buffer);
371*f00d4f59SWei Liu     g_free(buffer);
372*f00d4f59SWei Liu     if (!fs->dir) {
373*f00d4f59SWei Liu         return -1;
374*f00d4f59SWei Liu     }
375*f00d4f59SWei Liu     return 0;
376*f00d4f59SWei Liu }
377*f00d4f59SWei Liu 
378*f00d4f59SWei Liu static void local_rewinddir(FsContext *ctx, V9fsFidOpenState *fs)
379*f00d4f59SWei Liu {
380*f00d4f59SWei Liu     rewinddir(fs->dir);
381*f00d4f59SWei Liu }
382*f00d4f59SWei Liu 
383*f00d4f59SWei Liu static off_t local_telldir(FsContext *ctx, V9fsFidOpenState *fs)
384*f00d4f59SWei Liu {
385*f00d4f59SWei Liu     return telldir(fs->dir);
386*f00d4f59SWei Liu }
387*f00d4f59SWei Liu 
388*f00d4f59SWei Liu static int local_readdir_r(FsContext *ctx, V9fsFidOpenState *fs,
389*f00d4f59SWei Liu                            struct dirent *entry,
390*f00d4f59SWei Liu                            struct dirent **result)
391*f00d4f59SWei Liu {
392*f00d4f59SWei Liu     int ret;
393*f00d4f59SWei Liu 
394*f00d4f59SWei Liu again:
395*f00d4f59SWei Liu     ret = readdir_r(fs->dir, entry, result);
396*f00d4f59SWei Liu     if (ctx->export_flags & V9FS_SM_MAPPED) {
397*f00d4f59SWei Liu         entry->d_type = DT_UNKNOWN;
398*f00d4f59SWei Liu     } else if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
399*f00d4f59SWei Liu         if (!ret && *result != NULL &&
400*f00d4f59SWei Liu             !strcmp(entry->d_name, VIRTFS_META_DIR)) {
401*f00d4f59SWei Liu             /* skp the meta data directory */
402*f00d4f59SWei Liu             goto again;
403*f00d4f59SWei Liu         }
404*f00d4f59SWei Liu         entry->d_type = DT_UNKNOWN;
405*f00d4f59SWei Liu     }
406*f00d4f59SWei Liu     return ret;
407*f00d4f59SWei Liu }
408*f00d4f59SWei Liu 
409*f00d4f59SWei Liu static void local_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off)
410*f00d4f59SWei Liu {
411*f00d4f59SWei Liu     seekdir(fs->dir, off);
412*f00d4f59SWei Liu }
413*f00d4f59SWei Liu 
414*f00d4f59SWei Liu static ssize_t local_preadv(FsContext *ctx, V9fsFidOpenState *fs,
415*f00d4f59SWei Liu                             const struct iovec *iov,
416*f00d4f59SWei Liu                             int iovcnt, off_t offset)
417*f00d4f59SWei Liu {
418*f00d4f59SWei Liu #ifdef CONFIG_PREADV
419*f00d4f59SWei Liu     return preadv(fs->fd, iov, iovcnt, offset);
420*f00d4f59SWei Liu #else
421*f00d4f59SWei Liu     int err = lseek(fs->fd, offset, SEEK_SET);
422*f00d4f59SWei Liu     if (err == -1) {
423*f00d4f59SWei Liu         return err;
424*f00d4f59SWei Liu     } else {
425*f00d4f59SWei Liu         return readv(fs->fd, iov, iovcnt);
426*f00d4f59SWei Liu     }
427*f00d4f59SWei Liu #endif
428*f00d4f59SWei Liu }
429*f00d4f59SWei Liu 
430*f00d4f59SWei Liu static ssize_t local_pwritev(FsContext *ctx, V9fsFidOpenState *fs,
431*f00d4f59SWei Liu                              const struct iovec *iov,
432*f00d4f59SWei Liu                              int iovcnt, off_t offset)
433*f00d4f59SWei Liu {
434*f00d4f59SWei Liu     ssize_t ret
435*f00d4f59SWei Liu ;
436*f00d4f59SWei Liu #ifdef CONFIG_PREADV
437*f00d4f59SWei Liu     ret = pwritev(fs->fd, iov, iovcnt, offset);
438*f00d4f59SWei Liu #else
439*f00d4f59SWei Liu     int err = lseek(fs->fd, offset, SEEK_SET);
440*f00d4f59SWei Liu     if (err == -1) {
441*f00d4f59SWei Liu         return err;
442*f00d4f59SWei Liu     } else {
443*f00d4f59SWei Liu         ret = writev(fs->fd, iov, iovcnt);
444*f00d4f59SWei Liu     }
445*f00d4f59SWei Liu #endif
446*f00d4f59SWei Liu #ifdef CONFIG_SYNC_FILE_RANGE
447*f00d4f59SWei Liu     if (ret > 0 && ctx->export_flags & V9FS_IMMEDIATE_WRITEOUT) {
448*f00d4f59SWei Liu         /*
449*f00d4f59SWei Liu          * Initiate a writeback. This is not a data integrity sync.
450*f00d4f59SWei Liu          * We want to ensure that we don't leave dirty pages in the cache
451*f00d4f59SWei Liu          * after write when writeout=immediate is sepcified.
452*f00d4f59SWei Liu          */
453*f00d4f59SWei Liu         sync_file_range(fs->fd, offset, ret,
454*f00d4f59SWei Liu                         SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE);
455*f00d4f59SWei Liu     }
456*f00d4f59SWei Liu #endif
457*f00d4f59SWei Liu     return ret;
458*f00d4f59SWei Liu }
459*f00d4f59SWei Liu 
460*f00d4f59SWei Liu static int local_chmod(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp)
461*f00d4f59SWei Liu {
462*f00d4f59SWei Liu     char *buffer;
463*f00d4f59SWei Liu     int ret = -1;
464*f00d4f59SWei Liu     char *path = fs_path->data;
465*f00d4f59SWei Liu 
466*f00d4f59SWei Liu     if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
467*f00d4f59SWei Liu         buffer = rpath(fs_ctx, path);
468*f00d4f59SWei Liu         ret = local_set_xattr(buffer, credp);
469*f00d4f59SWei Liu         g_free(buffer);
470*f00d4f59SWei Liu     } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
471*f00d4f59SWei Liu         return local_set_mapped_file_attr(fs_ctx, path, credp);
472*f00d4f59SWei Liu     } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
473*f00d4f59SWei Liu                (fs_ctx->export_flags & V9FS_SM_NONE)) {
474*f00d4f59SWei Liu         buffer = rpath(fs_ctx, path);
475*f00d4f59SWei Liu         ret = chmod(buffer, credp->fc_mode);
476*f00d4f59SWei Liu         g_free(buffer);
477*f00d4f59SWei Liu     }
478*f00d4f59SWei Liu     return ret;
479*f00d4f59SWei Liu }
480*f00d4f59SWei Liu 
481*f00d4f59SWei Liu static int local_mknod(FsContext *fs_ctx, V9fsPath *dir_path,
482*f00d4f59SWei Liu                        const char *name, FsCred *credp)
483*f00d4f59SWei Liu {
484*f00d4f59SWei Liu     char *path;
485*f00d4f59SWei Liu     int err = -1;
486*f00d4f59SWei Liu     int serrno = 0;
487*f00d4f59SWei Liu     V9fsString fullname;
488*f00d4f59SWei Liu     char *buffer = NULL;
489*f00d4f59SWei Liu 
490*f00d4f59SWei Liu     v9fs_string_init(&fullname);
491*f00d4f59SWei Liu     v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
492*f00d4f59SWei Liu     path = fullname.data;
493*f00d4f59SWei Liu 
494*f00d4f59SWei Liu     /* Determine the security model */
495*f00d4f59SWei Liu     if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
496*f00d4f59SWei Liu         buffer = rpath(fs_ctx, path);
497*f00d4f59SWei Liu         err = mknod(buffer, SM_LOCAL_MODE_BITS|S_IFREG, 0);
498*f00d4f59SWei Liu         if (err == -1) {
499*f00d4f59SWei Liu             goto out;
500*f00d4f59SWei Liu         }
501*f00d4f59SWei Liu         err = local_set_xattr(buffer, credp);
502*f00d4f59SWei Liu         if (err == -1) {
503*f00d4f59SWei Liu             serrno = errno;
504*f00d4f59SWei Liu             goto err_end;
505*f00d4f59SWei Liu         }
506*f00d4f59SWei Liu     } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
507*f00d4f59SWei Liu 
508*f00d4f59SWei Liu         buffer = rpath(fs_ctx, path);
509*f00d4f59SWei Liu         err = mknod(buffer, SM_LOCAL_MODE_BITS|S_IFREG, 0);
510*f00d4f59SWei Liu         if (err == -1) {
511*f00d4f59SWei Liu             goto out;
512*f00d4f59SWei Liu         }
513*f00d4f59SWei Liu         err = local_set_mapped_file_attr(fs_ctx, path, credp);
514*f00d4f59SWei Liu         if (err == -1) {
515*f00d4f59SWei Liu             serrno = errno;
516*f00d4f59SWei Liu             goto err_end;
517*f00d4f59SWei Liu         }
518*f00d4f59SWei Liu     } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
519*f00d4f59SWei Liu                (fs_ctx->export_flags & V9FS_SM_NONE)) {
520*f00d4f59SWei Liu         buffer = rpath(fs_ctx, path);
521*f00d4f59SWei Liu         err = mknod(buffer, credp->fc_mode, credp->fc_rdev);
522*f00d4f59SWei Liu         if (err == -1) {
523*f00d4f59SWei Liu             goto out;
524*f00d4f59SWei Liu         }
525*f00d4f59SWei Liu         err = local_post_create_passthrough(fs_ctx, path, credp);
526*f00d4f59SWei Liu         if (err == -1) {
527*f00d4f59SWei Liu             serrno = errno;
528*f00d4f59SWei Liu             goto err_end;
529*f00d4f59SWei Liu         }
530*f00d4f59SWei Liu     }
531*f00d4f59SWei Liu     goto out;
532*f00d4f59SWei Liu 
533*f00d4f59SWei Liu err_end:
534*f00d4f59SWei Liu     remove(buffer);
535*f00d4f59SWei Liu     errno = serrno;
536*f00d4f59SWei Liu out:
537*f00d4f59SWei Liu     g_free(buffer);
538*f00d4f59SWei Liu     v9fs_string_free(&fullname);
539*f00d4f59SWei Liu     return err;
540*f00d4f59SWei Liu }
541*f00d4f59SWei Liu 
542*f00d4f59SWei Liu static int local_mkdir(FsContext *fs_ctx, V9fsPath *dir_path,
543*f00d4f59SWei Liu                        const char *name, FsCred *credp)
544*f00d4f59SWei Liu {
545*f00d4f59SWei Liu     char *path;
546*f00d4f59SWei Liu     int err = -1;
547*f00d4f59SWei Liu     int serrno = 0;
548*f00d4f59SWei Liu     V9fsString fullname;
549*f00d4f59SWei Liu     char *buffer = NULL;
550*f00d4f59SWei Liu 
551*f00d4f59SWei Liu     v9fs_string_init(&fullname);
552*f00d4f59SWei Liu     v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
553*f00d4f59SWei Liu     path = fullname.data;
554*f00d4f59SWei Liu 
555*f00d4f59SWei Liu     /* Determine the security model */
556*f00d4f59SWei Liu     if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
557*f00d4f59SWei Liu         buffer = rpath(fs_ctx, path);
558*f00d4f59SWei Liu         err = mkdir(buffer, SM_LOCAL_DIR_MODE_BITS);
559*f00d4f59SWei Liu         if (err == -1) {
560*f00d4f59SWei Liu             goto out;
561*f00d4f59SWei Liu         }
562*f00d4f59SWei Liu         credp->fc_mode = credp->fc_mode|S_IFDIR;
563*f00d4f59SWei Liu         err = local_set_xattr(buffer, credp);
564*f00d4f59SWei Liu         if (err == -1) {
565*f00d4f59SWei Liu             serrno = errno;
566*f00d4f59SWei Liu             goto err_end;
567*f00d4f59SWei Liu         }
568*f00d4f59SWei Liu     } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
569*f00d4f59SWei Liu         buffer = rpath(fs_ctx, path);
570*f00d4f59SWei Liu         err = mkdir(buffer, SM_LOCAL_DIR_MODE_BITS);
571*f00d4f59SWei Liu         if (err == -1) {
572*f00d4f59SWei Liu             goto out;
573*f00d4f59SWei Liu         }
574*f00d4f59SWei Liu         credp->fc_mode = credp->fc_mode|S_IFDIR;
575*f00d4f59SWei Liu         err = local_set_mapped_file_attr(fs_ctx, path, credp);
576*f00d4f59SWei Liu         if (err == -1) {
577*f00d4f59SWei Liu             serrno = errno;
578*f00d4f59SWei Liu             goto err_end;
579*f00d4f59SWei Liu         }
580*f00d4f59SWei Liu     } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
581*f00d4f59SWei Liu                (fs_ctx->export_flags & V9FS_SM_NONE)) {
582*f00d4f59SWei Liu         buffer = rpath(fs_ctx, path);
583*f00d4f59SWei Liu         err = mkdir(buffer, credp->fc_mode);
584*f00d4f59SWei Liu         if (err == -1) {
585*f00d4f59SWei Liu             goto out;
586*f00d4f59SWei Liu         }
587*f00d4f59SWei Liu         err = local_post_create_passthrough(fs_ctx, path, credp);
588*f00d4f59SWei Liu         if (err == -1) {
589*f00d4f59SWei Liu             serrno = errno;
590*f00d4f59SWei Liu             goto err_end;
591*f00d4f59SWei Liu         }
592*f00d4f59SWei Liu     }
593*f00d4f59SWei Liu     goto out;
594*f00d4f59SWei Liu 
595*f00d4f59SWei Liu err_end:
596*f00d4f59SWei Liu     remove(buffer);
597*f00d4f59SWei Liu     errno = serrno;
598*f00d4f59SWei Liu out:
599*f00d4f59SWei Liu     g_free(buffer);
600*f00d4f59SWei Liu     v9fs_string_free(&fullname);
601*f00d4f59SWei Liu     return err;
602*f00d4f59SWei Liu }
603*f00d4f59SWei Liu 
604*f00d4f59SWei Liu static int local_fstat(FsContext *fs_ctx, int fid_type,
605*f00d4f59SWei Liu                        V9fsFidOpenState *fs, struct stat *stbuf)
606*f00d4f59SWei Liu {
607*f00d4f59SWei Liu     int err, fd;
608*f00d4f59SWei Liu 
609*f00d4f59SWei Liu     if (fid_type == P9_FID_DIR) {
610*f00d4f59SWei Liu         fd = dirfd(fs->dir);
611*f00d4f59SWei Liu     } else {
612*f00d4f59SWei Liu         fd = fs->fd;
613*f00d4f59SWei Liu     }
614*f00d4f59SWei Liu 
615*f00d4f59SWei Liu     err = fstat(fd, stbuf);
616*f00d4f59SWei Liu     if (err) {
617*f00d4f59SWei Liu         return err;
618*f00d4f59SWei Liu     }
619*f00d4f59SWei Liu     if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
620*f00d4f59SWei Liu         /* Actual credentials are part of extended attrs */
621*f00d4f59SWei Liu         uid_t tmp_uid;
622*f00d4f59SWei Liu         gid_t tmp_gid;
623*f00d4f59SWei Liu         mode_t tmp_mode;
624*f00d4f59SWei Liu         dev_t tmp_dev;
625*f00d4f59SWei Liu 
626*f00d4f59SWei Liu         if (fgetxattr(fd, "user.virtfs.uid", &tmp_uid, sizeof(uid_t)) > 0) {
627*f00d4f59SWei Liu             stbuf->st_uid = le32_to_cpu(tmp_uid);
628*f00d4f59SWei Liu         }
629*f00d4f59SWei Liu         if (fgetxattr(fd, "user.virtfs.gid", &tmp_gid, sizeof(gid_t)) > 0) {
630*f00d4f59SWei Liu             stbuf->st_gid = le32_to_cpu(tmp_gid);
631*f00d4f59SWei Liu         }
632*f00d4f59SWei Liu         if (fgetxattr(fd, "user.virtfs.mode", &tmp_mode, sizeof(mode_t)) > 0) {
633*f00d4f59SWei Liu             stbuf->st_mode = le32_to_cpu(tmp_mode);
634*f00d4f59SWei Liu         }
635*f00d4f59SWei Liu         if (fgetxattr(fd, "user.virtfs.rdev", &tmp_dev, sizeof(dev_t)) > 0) {
636*f00d4f59SWei Liu             stbuf->st_rdev = le64_to_cpu(tmp_dev);
637*f00d4f59SWei Liu         }
638*f00d4f59SWei Liu     } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
639*f00d4f59SWei Liu         errno = EOPNOTSUPP;
640*f00d4f59SWei Liu         return -1;
641*f00d4f59SWei Liu     }
642*f00d4f59SWei Liu     return err;
643*f00d4f59SWei Liu }
644*f00d4f59SWei Liu 
645*f00d4f59SWei Liu static int local_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name,
646*f00d4f59SWei Liu                        int flags, FsCred *credp, V9fsFidOpenState *fs)
647*f00d4f59SWei Liu {
648*f00d4f59SWei Liu     char *path;
649*f00d4f59SWei Liu     int fd = -1;
650*f00d4f59SWei Liu     int err = -1;
651*f00d4f59SWei Liu     int serrno = 0;
652*f00d4f59SWei Liu     V9fsString fullname;
653*f00d4f59SWei Liu     char *buffer = NULL;
654*f00d4f59SWei Liu 
655*f00d4f59SWei Liu     /*
656*f00d4f59SWei Liu      * Mark all the open to not follow symlinks
657*f00d4f59SWei Liu      */
658*f00d4f59SWei Liu     flags |= O_NOFOLLOW;
659*f00d4f59SWei Liu 
660*f00d4f59SWei Liu     v9fs_string_init(&fullname);
661*f00d4f59SWei Liu     v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
662*f00d4f59SWei Liu     path = fullname.data;
663*f00d4f59SWei Liu 
664*f00d4f59SWei Liu     /* Determine the security model */
665*f00d4f59SWei Liu     if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
666*f00d4f59SWei Liu         buffer = rpath(fs_ctx, path);
667*f00d4f59SWei Liu         fd = open(buffer, flags, SM_LOCAL_MODE_BITS);
668*f00d4f59SWei Liu         if (fd == -1) {
669*f00d4f59SWei Liu             err = fd;
670*f00d4f59SWei Liu             goto out;
671*f00d4f59SWei Liu         }
672*f00d4f59SWei Liu         credp->fc_mode = credp->fc_mode|S_IFREG;
673*f00d4f59SWei Liu         /* Set cleint credentials in xattr */
674*f00d4f59SWei Liu         err = local_set_xattr(buffer, credp);
675*f00d4f59SWei Liu         if (err == -1) {
676*f00d4f59SWei Liu             serrno = errno;
677*f00d4f59SWei Liu             goto err_end;
678*f00d4f59SWei Liu         }
679*f00d4f59SWei Liu     } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
680*f00d4f59SWei Liu         buffer = rpath(fs_ctx, path);
681*f00d4f59SWei Liu         fd = open(buffer, flags, SM_LOCAL_MODE_BITS);
682*f00d4f59SWei Liu         if (fd == -1) {
683*f00d4f59SWei Liu             err = fd;
684*f00d4f59SWei Liu             goto out;
685*f00d4f59SWei Liu         }
686*f00d4f59SWei Liu         credp->fc_mode = credp->fc_mode|S_IFREG;
687*f00d4f59SWei Liu         /* Set client credentials in .virtfs_metadata directory files */
688*f00d4f59SWei Liu         err = local_set_mapped_file_attr(fs_ctx, path, credp);
689*f00d4f59SWei Liu         if (err == -1) {
690*f00d4f59SWei Liu             serrno = errno;
691*f00d4f59SWei Liu             goto err_end;
692*f00d4f59SWei Liu         }
693*f00d4f59SWei Liu     } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
694*f00d4f59SWei Liu                (fs_ctx->export_flags & V9FS_SM_NONE)) {
695*f00d4f59SWei Liu         buffer = rpath(fs_ctx, path);
696*f00d4f59SWei Liu         fd = open(buffer, flags, credp->fc_mode);
697*f00d4f59SWei Liu         if (fd == -1) {
698*f00d4f59SWei Liu             err = fd;
699*f00d4f59SWei Liu             goto out;
700*f00d4f59SWei Liu         }
701*f00d4f59SWei Liu         err = local_post_create_passthrough(fs_ctx, path, credp);
702*f00d4f59SWei Liu         if (err == -1) {
703*f00d4f59SWei Liu             serrno = errno;
704*f00d4f59SWei Liu             goto err_end;
705*f00d4f59SWei Liu         }
706*f00d4f59SWei Liu     }
707*f00d4f59SWei Liu     err = fd;
708*f00d4f59SWei Liu     fs->fd = fd;
709*f00d4f59SWei Liu     goto out;
710*f00d4f59SWei Liu 
711*f00d4f59SWei Liu err_end:
712*f00d4f59SWei Liu     close(fd);
713*f00d4f59SWei Liu     remove(buffer);
714*f00d4f59SWei Liu     errno = serrno;
715*f00d4f59SWei Liu out:
716*f00d4f59SWei Liu     g_free(buffer);
717*f00d4f59SWei Liu     v9fs_string_free(&fullname);
718*f00d4f59SWei Liu     return err;
719*f00d4f59SWei Liu }
720*f00d4f59SWei Liu 
721*f00d4f59SWei Liu 
722*f00d4f59SWei Liu static int local_symlink(FsContext *fs_ctx, const char *oldpath,
723*f00d4f59SWei Liu                          V9fsPath *dir_path, const char *name, FsCred *credp)
724*f00d4f59SWei Liu {
725*f00d4f59SWei Liu     int err = -1;
726*f00d4f59SWei Liu     int serrno = 0;
727*f00d4f59SWei Liu     char *newpath;
728*f00d4f59SWei Liu     V9fsString fullname;
729*f00d4f59SWei Liu     char *buffer = NULL;
730*f00d4f59SWei Liu 
731*f00d4f59SWei Liu     v9fs_string_init(&fullname);
732*f00d4f59SWei Liu     v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
733*f00d4f59SWei Liu     newpath = fullname.data;
734*f00d4f59SWei Liu 
735*f00d4f59SWei Liu     /* Determine the security model */
736*f00d4f59SWei Liu     if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
737*f00d4f59SWei Liu         int fd;
738*f00d4f59SWei Liu         ssize_t oldpath_size, write_size;
739*f00d4f59SWei Liu         buffer = rpath(fs_ctx, newpath);
740*f00d4f59SWei Liu         fd = open(buffer, O_CREAT|O_EXCL|O_RDWR|O_NOFOLLOW, SM_LOCAL_MODE_BITS);
741*f00d4f59SWei Liu         if (fd == -1) {
742*f00d4f59SWei Liu             err = fd;
743*f00d4f59SWei Liu             goto out;
744*f00d4f59SWei Liu         }
745*f00d4f59SWei Liu         /* Write the oldpath (target) to the file. */
746*f00d4f59SWei Liu         oldpath_size = strlen(oldpath);
747*f00d4f59SWei Liu         do {
748*f00d4f59SWei Liu             write_size = write(fd, (void *)oldpath, oldpath_size);
749*f00d4f59SWei Liu         } while (write_size == -1 && errno == EINTR);
750*f00d4f59SWei Liu 
751*f00d4f59SWei Liu         if (write_size != oldpath_size) {
752*f00d4f59SWei Liu             serrno = errno;
753*f00d4f59SWei Liu             close(fd);
754*f00d4f59SWei Liu             err = -1;
755*f00d4f59SWei Liu             goto err_end;
756*f00d4f59SWei Liu         }
757*f00d4f59SWei Liu         close(fd);
758*f00d4f59SWei Liu         /* Set cleint credentials in symlink's xattr */
759*f00d4f59SWei Liu         credp->fc_mode = credp->fc_mode|S_IFLNK;
760*f00d4f59SWei Liu         err = local_set_xattr(buffer, credp);
761*f00d4f59SWei Liu         if (err == -1) {
762*f00d4f59SWei Liu             serrno = errno;
763*f00d4f59SWei Liu             goto err_end;
764*f00d4f59SWei Liu         }
765*f00d4f59SWei Liu     } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
766*f00d4f59SWei Liu         int fd;
767*f00d4f59SWei Liu         ssize_t oldpath_size, write_size;
768*f00d4f59SWei Liu         buffer = rpath(fs_ctx, newpath);
769*f00d4f59SWei Liu         fd = open(buffer, O_CREAT|O_EXCL|O_RDWR|O_NOFOLLOW, SM_LOCAL_MODE_BITS);
770*f00d4f59SWei Liu         if (fd == -1) {
771*f00d4f59SWei Liu             err = fd;
772*f00d4f59SWei Liu             goto out;
773*f00d4f59SWei Liu         }
774*f00d4f59SWei Liu         /* Write the oldpath (target) to the file. */
775*f00d4f59SWei Liu         oldpath_size = strlen(oldpath);
776*f00d4f59SWei Liu         do {
777*f00d4f59SWei Liu             write_size = write(fd, (void *)oldpath, oldpath_size);
778*f00d4f59SWei Liu         } while (write_size == -1 && errno == EINTR);
779*f00d4f59SWei Liu 
780*f00d4f59SWei Liu         if (write_size != oldpath_size) {
781*f00d4f59SWei Liu             serrno = errno;
782*f00d4f59SWei Liu             close(fd);
783*f00d4f59SWei Liu             err = -1;
784*f00d4f59SWei Liu             goto err_end;
785*f00d4f59SWei Liu         }
786*f00d4f59SWei Liu         close(fd);
787*f00d4f59SWei Liu         /* Set cleint credentials in symlink's xattr */
788*f00d4f59SWei Liu         credp->fc_mode = credp->fc_mode|S_IFLNK;
789*f00d4f59SWei Liu         err = local_set_mapped_file_attr(fs_ctx, newpath, credp);
790*f00d4f59SWei Liu         if (err == -1) {
791*f00d4f59SWei Liu             serrno = errno;
792*f00d4f59SWei Liu             goto err_end;
793*f00d4f59SWei Liu         }
794*f00d4f59SWei Liu     } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
795*f00d4f59SWei Liu                (fs_ctx->export_flags & V9FS_SM_NONE)) {
796*f00d4f59SWei Liu         buffer = rpath(fs_ctx, newpath);
797*f00d4f59SWei Liu         err = symlink(oldpath, buffer);
798*f00d4f59SWei Liu         if (err) {
799*f00d4f59SWei Liu             goto out;
800*f00d4f59SWei Liu         }
801*f00d4f59SWei Liu         err = lchown(buffer, credp->fc_uid, credp->fc_gid);
802*f00d4f59SWei Liu         if (err == -1) {
803*f00d4f59SWei Liu             /*
804*f00d4f59SWei Liu              * If we fail to change ownership and if we are
805*f00d4f59SWei Liu              * using security model none. Ignore the error
806*f00d4f59SWei Liu              */
807*f00d4f59SWei Liu             if ((fs_ctx->export_flags & V9FS_SEC_MASK) != V9FS_SM_NONE) {
808*f00d4f59SWei Liu                 serrno = errno;
809*f00d4f59SWei Liu                 goto err_end;
810*f00d4f59SWei Liu             } else
811*f00d4f59SWei Liu                 err = 0;
812*f00d4f59SWei Liu         }
813*f00d4f59SWei Liu     }
814*f00d4f59SWei Liu     goto out;
815*f00d4f59SWei Liu 
816*f00d4f59SWei Liu err_end:
817*f00d4f59SWei Liu     remove(buffer);
818*f00d4f59SWei Liu     errno = serrno;
819*f00d4f59SWei Liu out:
820*f00d4f59SWei Liu     g_free(buffer);
821*f00d4f59SWei Liu     v9fs_string_free(&fullname);
822*f00d4f59SWei Liu     return err;
823*f00d4f59SWei Liu }
824*f00d4f59SWei Liu 
825*f00d4f59SWei Liu static int local_link(FsContext *ctx, V9fsPath *oldpath,
826*f00d4f59SWei Liu                       V9fsPath *dirpath, const char *name)
827*f00d4f59SWei Liu {
828*f00d4f59SWei Liu     int ret;
829*f00d4f59SWei Liu     V9fsString newpath;
830*f00d4f59SWei Liu     char *buffer, *buffer1;
831*f00d4f59SWei Liu 
832*f00d4f59SWei Liu     v9fs_string_init(&newpath);
833*f00d4f59SWei Liu     v9fs_string_sprintf(&newpath, "%s/%s", dirpath->data, name);
834*f00d4f59SWei Liu 
835*f00d4f59SWei Liu     buffer = rpath(ctx, oldpath->data);
836*f00d4f59SWei Liu     buffer1 = rpath(ctx, newpath.data);
837*f00d4f59SWei Liu     ret = link(buffer, buffer1);
838*f00d4f59SWei Liu     g_free(buffer);
839*f00d4f59SWei Liu     g_free(buffer1);
840*f00d4f59SWei Liu 
841*f00d4f59SWei Liu     /* now link the virtfs_metadata files */
842*f00d4f59SWei Liu     if (!ret && (ctx->export_flags & V9FS_SM_MAPPED_FILE)) {
843*f00d4f59SWei Liu         /* Link the .virtfs_metadata files. Create the metada directory */
844*f00d4f59SWei Liu         ret = local_create_mapped_attr_dir(ctx, newpath.data);
845*f00d4f59SWei Liu         if (ret < 0) {
846*f00d4f59SWei Liu             goto err_out;
847*f00d4f59SWei Liu         }
848*f00d4f59SWei Liu         buffer = local_mapped_attr_path(ctx, oldpath->data);
849*f00d4f59SWei Liu         buffer1 = local_mapped_attr_path(ctx, newpath.data);
850*f00d4f59SWei Liu         ret = link(buffer, buffer1);
851*f00d4f59SWei Liu         g_free(buffer);
852*f00d4f59SWei Liu         g_free(buffer1);
853*f00d4f59SWei Liu         if (ret < 0 && errno != ENOENT) {
854*f00d4f59SWei Liu             goto err_out;
855*f00d4f59SWei Liu         }
856*f00d4f59SWei Liu     }
857*f00d4f59SWei Liu err_out:
858*f00d4f59SWei Liu     v9fs_string_free(&newpath);
859*f00d4f59SWei Liu     return ret;
860*f00d4f59SWei Liu }
861*f00d4f59SWei Liu 
862*f00d4f59SWei Liu static int local_truncate(FsContext *ctx, V9fsPath *fs_path, off_t size)
863*f00d4f59SWei Liu {
864*f00d4f59SWei Liu     char *buffer;
865*f00d4f59SWei Liu     int ret;
866*f00d4f59SWei Liu     char *path = fs_path->data;
867*f00d4f59SWei Liu 
868*f00d4f59SWei Liu     buffer = rpath(ctx, path);
869*f00d4f59SWei Liu     ret = truncate(buffer, size);
870*f00d4f59SWei Liu     g_free(buffer);
871*f00d4f59SWei Liu     return ret;
872*f00d4f59SWei Liu }
873*f00d4f59SWei Liu 
874*f00d4f59SWei Liu static int local_rename(FsContext *ctx, const char *oldpath,
875*f00d4f59SWei Liu                         const char *newpath)
876*f00d4f59SWei Liu {
877*f00d4f59SWei Liu     int err;
878*f00d4f59SWei Liu     char *buffer, *buffer1;
879*f00d4f59SWei Liu 
880*f00d4f59SWei Liu     if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
881*f00d4f59SWei Liu         err = local_create_mapped_attr_dir(ctx, newpath);
882*f00d4f59SWei Liu         if (err < 0) {
883*f00d4f59SWei Liu             return err;
884*f00d4f59SWei Liu         }
885*f00d4f59SWei Liu         /* rename the .virtfs_metadata files */
886*f00d4f59SWei Liu         buffer = local_mapped_attr_path(ctx, oldpath);
887*f00d4f59SWei Liu         buffer1 = local_mapped_attr_path(ctx, newpath);
888*f00d4f59SWei Liu         err = rename(buffer, buffer1);
889*f00d4f59SWei Liu         g_free(buffer);
890*f00d4f59SWei Liu         g_free(buffer1);
891*f00d4f59SWei Liu         if (err < 0 && errno != ENOENT) {
892*f00d4f59SWei Liu             return err;
893*f00d4f59SWei Liu         }
894*f00d4f59SWei Liu     }
895*f00d4f59SWei Liu 
896*f00d4f59SWei Liu     buffer = rpath(ctx, oldpath);
897*f00d4f59SWei Liu     buffer1 = rpath(ctx, newpath);
898*f00d4f59SWei Liu     err = rename(buffer, buffer1);
899*f00d4f59SWei Liu     g_free(buffer);
900*f00d4f59SWei Liu     g_free(buffer1);
901*f00d4f59SWei Liu     return err;
902*f00d4f59SWei Liu }
903*f00d4f59SWei Liu 
904*f00d4f59SWei Liu static int local_chown(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp)
905*f00d4f59SWei Liu {
906*f00d4f59SWei Liu     char *buffer;
907*f00d4f59SWei Liu     int ret = -1;
908*f00d4f59SWei Liu     char *path = fs_path->data;
909*f00d4f59SWei Liu 
910*f00d4f59SWei Liu     if ((credp->fc_uid == -1 && credp->fc_gid == -1) ||
911*f00d4f59SWei Liu         (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
912*f00d4f59SWei Liu         (fs_ctx->export_flags & V9FS_SM_NONE)) {
913*f00d4f59SWei Liu         buffer = rpath(fs_ctx, path);
914*f00d4f59SWei Liu         ret = lchown(buffer, credp->fc_uid, credp->fc_gid);
915*f00d4f59SWei Liu         g_free(buffer);
916*f00d4f59SWei Liu     } else if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
917*f00d4f59SWei Liu         buffer = rpath(fs_ctx, path);
918*f00d4f59SWei Liu         ret = local_set_xattr(buffer, credp);
919*f00d4f59SWei Liu         g_free(buffer);
920*f00d4f59SWei Liu     } else if (fs_ctx->export_flags & V9FS_SM_MAPPED_FILE) {
921*f00d4f59SWei Liu         return local_set_mapped_file_attr(fs_ctx, path, credp);
922*f00d4f59SWei Liu     }
923*f00d4f59SWei Liu     return ret;
924*f00d4f59SWei Liu }
925*f00d4f59SWei Liu 
926*f00d4f59SWei Liu static int local_utimensat(FsContext *s, V9fsPath *fs_path,
927*f00d4f59SWei Liu                            const struct timespec *buf)
928*f00d4f59SWei Liu {
929*f00d4f59SWei Liu     char *buffer;
930*f00d4f59SWei Liu     int ret;
931*f00d4f59SWei Liu     char *path = fs_path->data;
932*f00d4f59SWei Liu 
933*f00d4f59SWei Liu     buffer = rpath(s, path);
934*f00d4f59SWei Liu     ret = qemu_utimens(buffer, buf);
935*f00d4f59SWei Liu     g_free(buffer);
936*f00d4f59SWei Liu     return ret;
937*f00d4f59SWei Liu }
938*f00d4f59SWei Liu 
939*f00d4f59SWei Liu static int local_remove(FsContext *ctx, const char *path)
940*f00d4f59SWei Liu {
941*f00d4f59SWei Liu     int err;
942*f00d4f59SWei Liu     struct stat stbuf;
943*f00d4f59SWei Liu     char *buffer;
944*f00d4f59SWei Liu 
945*f00d4f59SWei Liu     if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
946*f00d4f59SWei Liu         buffer = rpath(ctx, path);
947*f00d4f59SWei Liu         err =  lstat(buffer, &stbuf);
948*f00d4f59SWei Liu         g_free(buffer);
949*f00d4f59SWei Liu         if (err) {
950*f00d4f59SWei Liu             goto err_out;
951*f00d4f59SWei Liu         }
952*f00d4f59SWei Liu         /*
953*f00d4f59SWei Liu          * If directory remove .virtfs_metadata contained in the
954*f00d4f59SWei Liu          * directory
955*f00d4f59SWei Liu          */
956*f00d4f59SWei Liu         if (S_ISDIR(stbuf.st_mode)) {
957*f00d4f59SWei Liu             buffer = g_strdup_printf("%s/%s/%s", ctx->fs_root,
958*f00d4f59SWei Liu                                      path, VIRTFS_META_DIR);
959*f00d4f59SWei Liu             err = remove(buffer);
960*f00d4f59SWei Liu             g_free(buffer);
961*f00d4f59SWei Liu             if (err < 0 && errno != ENOENT) {
962*f00d4f59SWei Liu                 /*
963*f00d4f59SWei Liu                  * We didn't had the .virtfs_metadata file. May be file created
964*f00d4f59SWei Liu                  * in non-mapped mode ?. Ignore ENOENT.
965*f00d4f59SWei Liu                  */
966*f00d4f59SWei Liu                 goto err_out;
967*f00d4f59SWei Liu             }
968*f00d4f59SWei Liu         }
969*f00d4f59SWei Liu         /*
970*f00d4f59SWei Liu          * Now remove the name from parent directory
971*f00d4f59SWei Liu          * .virtfs_metadata directory
972*f00d4f59SWei Liu          */
973*f00d4f59SWei Liu         buffer = local_mapped_attr_path(ctx, path);
974*f00d4f59SWei Liu         err = remove(buffer);
975*f00d4f59SWei Liu         g_free(buffer);
976*f00d4f59SWei Liu         if (err < 0 && errno != ENOENT) {
977*f00d4f59SWei Liu             /*
978*f00d4f59SWei Liu              * We didn't had the .virtfs_metadata file. May be file created
979*f00d4f59SWei Liu              * in non-mapped mode ?. Ignore ENOENT.
980*f00d4f59SWei Liu              */
981*f00d4f59SWei Liu             goto err_out;
982*f00d4f59SWei Liu         }
983*f00d4f59SWei Liu     }
984*f00d4f59SWei Liu 
985*f00d4f59SWei Liu     buffer = rpath(ctx, path);
986*f00d4f59SWei Liu     err = remove(buffer);
987*f00d4f59SWei Liu     g_free(buffer);
988*f00d4f59SWei Liu err_out:
989*f00d4f59SWei Liu     return err;
990*f00d4f59SWei Liu }
991*f00d4f59SWei Liu 
992*f00d4f59SWei Liu static int local_fsync(FsContext *ctx, int fid_type,
993*f00d4f59SWei Liu                        V9fsFidOpenState *fs, int datasync)
994*f00d4f59SWei Liu {
995*f00d4f59SWei Liu     int fd;
996*f00d4f59SWei Liu 
997*f00d4f59SWei Liu     if (fid_type == P9_FID_DIR) {
998*f00d4f59SWei Liu         fd = dirfd(fs->dir);
999*f00d4f59SWei Liu     } else {
1000*f00d4f59SWei Liu         fd = fs->fd;
1001*f00d4f59SWei Liu     }
1002*f00d4f59SWei Liu 
1003*f00d4f59SWei Liu     if (datasync) {
1004*f00d4f59SWei Liu         return qemu_fdatasync(fd);
1005*f00d4f59SWei Liu     } else {
1006*f00d4f59SWei Liu         return fsync(fd);
1007*f00d4f59SWei Liu     }
1008*f00d4f59SWei Liu }
1009*f00d4f59SWei Liu 
1010*f00d4f59SWei Liu static int local_statfs(FsContext *s, V9fsPath *fs_path, struct statfs *stbuf)
1011*f00d4f59SWei Liu {
1012*f00d4f59SWei Liu     char *buffer;
1013*f00d4f59SWei Liu     int ret;
1014*f00d4f59SWei Liu     char *path = fs_path->data;
1015*f00d4f59SWei Liu 
1016*f00d4f59SWei Liu     buffer = rpath(s, path);
1017*f00d4f59SWei Liu     ret = statfs(buffer, stbuf);
1018*f00d4f59SWei Liu     g_free(buffer);
1019*f00d4f59SWei Liu     return ret;
1020*f00d4f59SWei Liu }
1021*f00d4f59SWei Liu 
1022*f00d4f59SWei Liu static ssize_t local_lgetxattr(FsContext *ctx, V9fsPath *fs_path,
1023*f00d4f59SWei Liu                                const char *name, void *value, size_t size)
1024*f00d4f59SWei Liu {
1025*f00d4f59SWei Liu     char *path = fs_path->data;
1026*f00d4f59SWei Liu 
1027*f00d4f59SWei Liu     return v9fs_get_xattr(ctx, path, name, value, size);
1028*f00d4f59SWei Liu }
1029*f00d4f59SWei Liu 
1030*f00d4f59SWei Liu static ssize_t local_llistxattr(FsContext *ctx, V9fsPath *fs_path,
1031*f00d4f59SWei Liu                                 void *value, size_t size)
1032*f00d4f59SWei Liu {
1033*f00d4f59SWei Liu     char *path = fs_path->data;
1034*f00d4f59SWei Liu 
1035*f00d4f59SWei Liu     return v9fs_list_xattr(ctx, path, value, size);
1036*f00d4f59SWei Liu }
1037*f00d4f59SWei Liu 
1038*f00d4f59SWei Liu static int local_lsetxattr(FsContext *ctx, V9fsPath *fs_path, const char *name,
1039*f00d4f59SWei Liu                            void *value, size_t size, int flags)
1040*f00d4f59SWei Liu {
1041*f00d4f59SWei Liu     char *path = fs_path->data;
1042*f00d4f59SWei Liu 
1043*f00d4f59SWei Liu     return v9fs_set_xattr(ctx, path, name, value, size, flags);
1044*f00d4f59SWei Liu }
1045*f00d4f59SWei Liu 
1046*f00d4f59SWei Liu static int local_lremovexattr(FsContext *ctx, V9fsPath *fs_path,
1047*f00d4f59SWei Liu                               const char *name)
1048*f00d4f59SWei Liu {
1049*f00d4f59SWei Liu     char *path = fs_path->data;
1050*f00d4f59SWei Liu 
1051*f00d4f59SWei Liu     return v9fs_remove_xattr(ctx, path, name);
1052*f00d4f59SWei Liu }
1053*f00d4f59SWei Liu 
1054*f00d4f59SWei Liu static int local_name_to_path(FsContext *ctx, V9fsPath *dir_path,
1055*f00d4f59SWei Liu                               const char *name, V9fsPath *target)
1056*f00d4f59SWei Liu {
1057*f00d4f59SWei Liu     if (dir_path) {
1058*f00d4f59SWei Liu         v9fs_string_sprintf((V9fsString *)target, "%s/%s",
1059*f00d4f59SWei Liu                             dir_path->data, name);
1060*f00d4f59SWei Liu     } else {
1061*f00d4f59SWei Liu         v9fs_string_sprintf((V9fsString *)target, "%s", name);
1062*f00d4f59SWei Liu     }
1063*f00d4f59SWei Liu     /* Bump the size for including terminating NULL */
1064*f00d4f59SWei Liu     target->size++;
1065*f00d4f59SWei Liu     return 0;
1066*f00d4f59SWei Liu }
1067*f00d4f59SWei Liu 
1068*f00d4f59SWei Liu static int local_renameat(FsContext *ctx, V9fsPath *olddir,
1069*f00d4f59SWei Liu                           const char *old_name, V9fsPath *newdir,
1070*f00d4f59SWei Liu                           const char *new_name)
1071*f00d4f59SWei Liu {
1072*f00d4f59SWei Liu     int ret;
1073*f00d4f59SWei Liu     V9fsString old_full_name, new_full_name;
1074*f00d4f59SWei Liu 
1075*f00d4f59SWei Liu     v9fs_string_init(&old_full_name);
1076*f00d4f59SWei Liu     v9fs_string_init(&new_full_name);
1077*f00d4f59SWei Liu 
1078*f00d4f59SWei Liu     v9fs_string_sprintf(&old_full_name, "%s/%s", olddir->data, old_name);
1079*f00d4f59SWei Liu     v9fs_string_sprintf(&new_full_name, "%s/%s", newdir->data, new_name);
1080*f00d4f59SWei Liu 
1081*f00d4f59SWei Liu     ret = local_rename(ctx, old_full_name.data, new_full_name.data);
1082*f00d4f59SWei Liu     v9fs_string_free(&old_full_name);
1083*f00d4f59SWei Liu     v9fs_string_free(&new_full_name);
1084*f00d4f59SWei Liu     return ret;
1085*f00d4f59SWei Liu }
1086*f00d4f59SWei Liu 
1087*f00d4f59SWei Liu static int local_unlinkat(FsContext *ctx, V9fsPath *dir,
1088*f00d4f59SWei Liu                           const char *name, int flags)
1089*f00d4f59SWei Liu {
1090*f00d4f59SWei Liu     int ret;
1091*f00d4f59SWei Liu     V9fsString fullname;
1092*f00d4f59SWei Liu     char *buffer;
1093*f00d4f59SWei Liu 
1094*f00d4f59SWei Liu     v9fs_string_init(&fullname);
1095*f00d4f59SWei Liu 
1096*f00d4f59SWei Liu     v9fs_string_sprintf(&fullname, "%s/%s", dir->data, name);
1097*f00d4f59SWei Liu     if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
1098*f00d4f59SWei Liu         if (flags == AT_REMOVEDIR) {
1099*f00d4f59SWei Liu             /*
1100*f00d4f59SWei Liu              * If directory remove .virtfs_metadata contained in the
1101*f00d4f59SWei Liu              * directory
1102*f00d4f59SWei Liu              */
1103*f00d4f59SWei Liu             buffer = g_strdup_printf("%s/%s/%s", ctx->fs_root,
1104*f00d4f59SWei Liu                                      fullname.data, VIRTFS_META_DIR);
1105*f00d4f59SWei Liu             ret = remove(buffer);
1106*f00d4f59SWei Liu             g_free(buffer);
1107*f00d4f59SWei Liu             if (ret < 0 && errno != ENOENT) {
1108*f00d4f59SWei Liu                 /*
1109*f00d4f59SWei Liu                  * We didn't had the .virtfs_metadata file. May be file created
1110*f00d4f59SWei Liu                  * in non-mapped mode ?. Ignore ENOENT.
1111*f00d4f59SWei Liu                  */
1112*f00d4f59SWei Liu                 goto err_out;
1113*f00d4f59SWei Liu             }
1114*f00d4f59SWei Liu         }
1115*f00d4f59SWei Liu         /*
1116*f00d4f59SWei Liu          * Now remove the name from parent directory
1117*f00d4f59SWei Liu          * .virtfs_metadata directory.
1118*f00d4f59SWei Liu          */
1119*f00d4f59SWei Liu         buffer = local_mapped_attr_path(ctx, fullname.data);
1120*f00d4f59SWei Liu         ret = remove(buffer);
1121*f00d4f59SWei Liu         g_free(buffer);
1122*f00d4f59SWei Liu         if (ret < 0 && errno != ENOENT) {
1123*f00d4f59SWei Liu             /*
1124*f00d4f59SWei Liu              * We didn't had the .virtfs_metadata file. May be file created
1125*f00d4f59SWei Liu              * in non-mapped mode ?. Ignore ENOENT.
1126*f00d4f59SWei Liu              */
1127*f00d4f59SWei Liu             goto err_out;
1128*f00d4f59SWei Liu         }
1129*f00d4f59SWei Liu     }
1130*f00d4f59SWei Liu     /* Remove the name finally */
1131*f00d4f59SWei Liu     buffer = rpath(ctx, fullname.data);
1132*f00d4f59SWei Liu     ret = remove(buffer);
1133*f00d4f59SWei Liu     g_free(buffer);
1134*f00d4f59SWei Liu 
1135*f00d4f59SWei Liu err_out:
1136*f00d4f59SWei Liu     v9fs_string_free(&fullname);
1137*f00d4f59SWei Liu     return ret;
1138*f00d4f59SWei Liu }
1139*f00d4f59SWei Liu 
1140*f00d4f59SWei Liu static int local_ioc_getversion(FsContext *ctx, V9fsPath *path,
1141*f00d4f59SWei Liu                                 mode_t st_mode, uint64_t *st_gen)
1142*f00d4f59SWei Liu {
1143*f00d4f59SWei Liu #ifdef FS_IOC_GETVERSION
1144*f00d4f59SWei Liu     int err;
1145*f00d4f59SWei Liu     V9fsFidOpenState fid_open;
1146*f00d4f59SWei Liu 
1147*f00d4f59SWei Liu     /*
1148*f00d4f59SWei Liu      * Do not try to open special files like device nodes, fifos etc
1149*f00d4f59SWei Liu      * We can get fd for regular files and directories only
1150*f00d4f59SWei Liu      */
1151*f00d4f59SWei Liu     if (!S_ISREG(st_mode) && !S_ISDIR(st_mode)) {
1152*f00d4f59SWei Liu         errno = ENOTTY;
1153*f00d4f59SWei Liu         return -1;
1154*f00d4f59SWei Liu     }
1155*f00d4f59SWei Liu     err = local_open(ctx, path, O_RDONLY, &fid_open);
1156*f00d4f59SWei Liu     if (err < 0) {
1157*f00d4f59SWei Liu         return err;
1158*f00d4f59SWei Liu     }
1159*f00d4f59SWei Liu     err = ioctl(fid_open.fd, FS_IOC_GETVERSION, st_gen);
1160*f00d4f59SWei Liu     local_close(ctx, &fid_open);
1161*f00d4f59SWei Liu     return err;
1162*f00d4f59SWei Liu #else
1163*f00d4f59SWei Liu     errno = ENOTTY;
1164*f00d4f59SWei Liu     return -1;
1165*f00d4f59SWei Liu #endif
1166*f00d4f59SWei Liu }
1167*f00d4f59SWei Liu 
1168*f00d4f59SWei Liu static int local_init(FsContext *ctx)
1169*f00d4f59SWei Liu {
1170*f00d4f59SWei Liu     int err = 0;
1171*f00d4f59SWei Liu     struct statfs stbuf;
1172*f00d4f59SWei Liu 
1173*f00d4f59SWei Liu     if (ctx->export_flags & V9FS_SM_PASSTHROUGH) {
1174*f00d4f59SWei Liu         ctx->xops = passthrough_xattr_ops;
1175*f00d4f59SWei Liu     } else if (ctx->export_flags & V9FS_SM_MAPPED) {
1176*f00d4f59SWei Liu         ctx->xops = mapped_xattr_ops;
1177*f00d4f59SWei Liu     } else if (ctx->export_flags & V9FS_SM_NONE) {
1178*f00d4f59SWei Liu         ctx->xops = none_xattr_ops;
1179*f00d4f59SWei Liu     } else if (ctx->export_flags & V9FS_SM_MAPPED_FILE) {
1180*f00d4f59SWei Liu         /*
1181*f00d4f59SWei Liu          * xattr operation for mapped-file and passthrough
1182*f00d4f59SWei Liu          * remain same.
1183*f00d4f59SWei Liu          */
1184*f00d4f59SWei Liu         ctx->xops = passthrough_xattr_ops;
1185*f00d4f59SWei Liu     }
1186*f00d4f59SWei Liu     ctx->export_flags |= V9FS_PATHNAME_FSCONTEXT;
1187*f00d4f59SWei Liu #ifdef FS_IOC_GETVERSION
1188*f00d4f59SWei Liu     /*
1189*f00d4f59SWei Liu      * use ioc_getversion only if the iocl is definied
1190*f00d4f59SWei Liu      */
1191*f00d4f59SWei Liu     err = statfs(ctx->fs_root, &stbuf);
1192*f00d4f59SWei Liu     if (!err) {
1193*f00d4f59SWei Liu         switch (stbuf.f_type) {
1194*f00d4f59SWei Liu         case EXT2_SUPER_MAGIC:
1195*f00d4f59SWei Liu         case BTRFS_SUPER_MAGIC:
1196*f00d4f59SWei Liu         case REISERFS_SUPER_MAGIC:
1197*f00d4f59SWei Liu         case XFS_SUPER_MAGIC:
1198*f00d4f59SWei Liu             ctx->exops.get_st_gen = local_ioc_getversion;
1199*f00d4f59SWei Liu             break;
1200*f00d4f59SWei Liu         }
1201*f00d4f59SWei Liu     }
1202*f00d4f59SWei Liu #endif
1203*f00d4f59SWei Liu     return err;
1204*f00d4f59SWei Liu }
1205*f00d4f59SWei Liu 
1206*f00d4f59SWei Liu static int local_parse_opts(QemuOpts *opts, struct FsDriverEntry *fse)
1207*f00d4f59SWei Liu {
1208*f00d4f59SWei Liu     const char *sec_model = qemu_opt_get(opts, "security_model");
1209*f00d4f59SWei Liu     const char *path = qemu_opt_get(opts, "path");
1210*f00d4f59SWei Liu 
1211*f00d4f59SWei Liu     if (!sec_model) {
1212*f00d4f59SWei Liu         fprintf(stderr, "security model not specified, "
1213*f00d4f59SWei Liu                 "local fs needs security model\nvalid options are:"
1214*f00d4f59SWei Liu                 "\tsecurity_model=[passthrough|mapped|none]\n");
1215*f00d4f59SWei Liu         return -1;
1216*f00d4f59SWei Liu     }
1217*f00d4f59SWei Liu 
1218*f00d4f59SWei Liu     if (!strcmp(sec_model, "passthrough")) {
1219*f00d4f59SWei Liu         fse->export_flags |= V9FS_SM_PASSTHROUGH;
1220*f00d4f59SWei Liu     } else if (!strcmp(sec_model, "mapped") ||
1221*f00d4f59SWei Liu                !strcmp(sec_model, "mapped-xattr")) {
1222*f00d4f59SWei Liu         fse->export_flags |= V9FS_SM_MAPPED;
1223*f00d4f59SWei Liu     } else if (!strcmp(sec_model, "none")) {
1224*f00d4f59SWei Liu         fse->export_flags |= V9FS_SM_NONE;
1225*f00d4f59SWei Liu     } else if (!strcmp(sec_model, "mapped-file")) {
1226*f00d4f59SWei Liu         fse->export_flags |= V9FS_SM_MAPPED_FILE;
1227*f00d4f59SWei Liu     } else {
1228*f00d4f59SWei Liu         fprintf(stderr, "Invalid security model %s specified, valid options are"
1229*f00d4f59SWei Liu                 "\n\t [passthrough|mapped-xattr|mapped-file|none]\n",
1230*f00d4f59SWei Liu                 sec_model);
1231*f00d4f59SWei Liu         return -1;
1232*f00d4f59SWei Liu     }
1233*f00d4f59SWei Liu 
1234*f00d4f59SWei Liu     if (!path) {
1235*f00d4f59SWei Liu         fprintf(stderr, "fsdev: No path specified.\n");
1236*f00d4f59SWei Liu         return -1;
1237*f00d4f59SWei Liu     }
1238*f00d4f59SWei Liu     fse->path = g_strdup(path);
1239*f00d4f59SWei Liu 
1240*f00d4f59SWei Liu     return 0;
1241*f00d4f59SWei Liu }
1242*f00d4f59SWei Liu 
1243*f00d4f59SWei Liu FileOperations local_ops = {
1244*f00d4f59SWei Liu     .parse_opts = local_parse_opts,
1245*f00d4f59SWei Liu     .init  = local_init,
1246*f00d4f59SWei Liu     .lstat = local_lstat,
1247*f00d4f59SWei Liu     .readlink = local_readlink,
1248*f00d4f59SWei Liu     .close = local_close,
1249*f00d4f59SWei Liu     .closedir = local_closedir,
1250*f00d4f59SWei Liu     .open = local_open,
1251*f00d4f59SWei Liu     .opendir = local_opendir,
1252*f00d4f59SWei Liu     .rewinddir = local_rewinddir,
1253*f00d4f59SWei Liu     .telldir = local_telldir,
1254*f00d4f59SWei Liu     .readdir_r = local_readdir_r,
1255*f00d4f59SWei Liu     .seekdir = local_seekdir,
1256*f00d4f59SWei Liu     .preadv = local_preadv,
1257*f00d4f59SWei Liu     .pwritev = local_pwritev,
1258*f00d4f59SWei Liu     .chmod = local_chmod,
1259*f00d4f59SWei Liu     .mknod = local_mknod,
1260*f00d4f59SWei Liu     .mkdir = local_mkdir,
1261*f00d4f59SWei Liu     .fstat = local_fstat,
1262*f00d4f59SWei Liu     .open2 = local_open2,
1263*f00d4f59SWei Liu     .symlink = local_symlink,
1264*f00d4f59SWei Liu     .link = local_link,
1265*f00d4f59SWei Liu     .truncate = local_truncate,
1266*f00d4f59SWei Liu     .rename = local_rename,
1267*f00d4f59SWei Liu     .chown = local_chown,
1268*f00d4f59SWei Liu     .utimensat = local_utimensat,
1269*f00d4f59SWei Liu     .remove = local_remove,
1270*f00d4f59SWei Liu     .fsync = local_fsync,
1271*f00d4f59SWei Liu     .statfs = local_statfs,
1272*f00d4f59SWei Liu     .lgetxattr = local_lgetxattr,
1273*f00d4f59SWei Liu     .llistxattr = local_llistxattr,
1274*f00d4f59SWei Liu     .lsetxattr = local_lsetxattr,
1275*f00d4f59SWei Liu     .lremovexattr = local_lremovexattr,
1276*f00d4f59SWei Liu     .name_to_path = local_name_to_path,
1277*f00d4f59SWei Liu     .renameat  = local_renameat,
1278*f00d4f59SWei Liu     .unlinkat = local_unlinkat,
1279*f00d4f59SWei Liu };
1280