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