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