]>
Commit | Line | Data |
---|---|---|
9f107513 AL |
1 | /* |
2 | * Virtio 9p Posix callback | |
3 | * | |
4 | * Copyright IBM, Corp. 2010 | |
5 | * | |
6 | * Authors: | |
7 | * Anthony Liguori <[email protected]> | |
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 SW |
13 | |
14 | #include "hw/virtio.h" | |
9f107513 | 15 | #include "virtio-9p.h" |
fc22118d | 16 | #include "virtio-9p-xattr.h" |
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> | |
4f26f2b6 | 22 | #include "qemu-xattr.h" |
e06a765e HPB |
23 | #include <linux/fs.h> |
24 | #ifdef CONFIG_LINUX_MAGIC_H | |
25 | #include <linux/magic.h> | |
26 | #endif | |
27 | #include <sys/ioctl.h> | |
28 | ||
29 | #ifndef XFS_SUPER_MAGIC | |
30 | #define XFS_SUPER_MAGIC 0x58465342 | |
31 | #endif | |
32 | #ifndef EXT2_SUPER_MAGIC | |
33 | #define EXT2_SUPER_MAGIC 0xEF53 | |
34 | #endif | |
35 | #ifndef REISERFS_SUPER_MAGIC | |
36 | #define REISERFS_SUPER_MAGIC 0x52654973 | |
37 | #endif | |
38 | #ifndef BTRFS_SUPER_MAGIC | |
39 | #define BTRFS_SUPER_MAGIC 0x9123683E | |
40 | #endif | |
131dcb25 | 41 | |
2289be19 | 42 | static int local_lstat(FsContext *fs_ctx, V9fsPath *fs_path, struct stat *stbuf) |
131dcb25 | 43 | { |
1237ad76 | 44 | int err; |
faa44e3d | 45 | char buffer[PATH_MAX]; |
2289be19 AK |
46 | char *path = fs_path->data; |
47 | ||
faa44e3d | 48 | err = lstat(rpath(fs_ctx, path, buffer), stbuf); |
1237ad76 VJ |
49 | if (err) { |
50 | return err; | |
51 | } | |
b97400ca | 52 | if (fs_ctx->export_flags & V9FS_SM_MAPPED) { |
1237ad76 VJ |
53 | /* Actual credentials are part of extended attrs */ |
54 | uid_t tmp_uid; | |
55 | gid_t tmp_gid; | |
56 | mode_t tmp_mode; | |
57 | dev_t tmp_dev; | |
faa44e3d | 58 | if (getxattr(rpath(fs_ctx, path, buffer), "user.virtfs.uid", &tmp_uid, |
1237ad76 VJ |
59 | sizeof(uid_t)) > 0) { |
60 | stbuf->st_uid = tmp_uid; | |
61 | } | |
faa44e3d | 62 | if (getxattr(rpath(fs_ctx, path, buffer), "user.virtfs.gid", &tmp_gid, |
1237ad76 VJ |
63 | sizeof(gid_t)) > 0) { |
64 | stbuf->st_gid = tmp_gid; | |
65 | } | |
faa44e3d VJ |
66 | if (getxattr(rpath(fs_ctx, path, buffer), "user.virtfs.mode", |
67 | &tmp_mode, sizeof(mode_t)) > 0) { | |
1237ad76 VJ |
68 | stbuf->st_mode = tmp_mode; |
69 | } | |
faa44e3d | 70 | if (getxattr(rpath(fs_ctx, path, buffer), "user.virtfs.rdev", &tmp_dev, |
1237ad76 VJ |
71 | sizeof(dev_t)) > 0) { |
72 | stbuf->st_rdev = tmp_dev; | |
73 | } | |
74 | } | |
75 | return err; | |
131dcb25 AL |
76 | } |
77 | ||
758e8e38 | 78 | static int local_set_xattr(const char *path, FsCred *credp) |
131dcb25 | 79 | { |
758e8e38 | 80 | int err; |
2289be19 | 81 | |
758e8e38 VJ |
82 | if (credp->fc_uid != -1) { |
83 | err = setxattr(path, "user.virtfs.uid", &credp->fc_uid, sizeof(uid_t), | |
84 | 0); | |
85 | if (err) { | |
86 | return err; | |
87 | } | |
131dcb25 | 88 | } |
758e8e38 VJ |
89 | if (credp->fc_gid != -1) { |
90 | err = setxattr(path, "user.virtfs.gid", &credp->fc_gid, sizeof(gid_t), | |
91 | 0); | |
92 | if (err) { | |
93 | return err; | |
94 | } | |
131dcb25 | 95 | } |
758e8e38 VJ |
96 | if (credp->fc_mode != -1) { |
97 | err = setxattr(path, "user.virtfs.mode", &credp->fc_mode, | |
98 | sizeof(mode_t), 0); | |
99 | if (err) { | |
100 | return err; | |
101 | } | |
131dcb25 | 102 | } |
758e8e38 VJ |
103 | if (credp->fc_rdev != -1) { |
104 | err = setxattr(path, "user.virtfs.rdev", &credp->fc_rdev, | |
105 | sizeof(dev_t), 0); | |
106 | if (err) { | |
107 | return err; | |
108 | } | |
131dcb25 | 109 | } |
131dcb25 AL |
110 | return 0; |
111 | } | |
112 | ||
4750a96f | 113 | static int local_post_create_passthrough(FsContext *fs_ctx, const char *path, |
2289be19 | 114 | FsCred *credp) |
4750a96f | 115 | { |
faa44e3d | 116 | char buffer[PATH_MAX]; |
2289be19 | 117 | |
faa44e3d | 118 | if (chmod(rpath(fs_ctx, path, buffer), credp->fc_mode & 07777) < 0) { |
4750a96f VJ |
119 | return -1; |
120 | } | |
faa44e3d VJ |
121 | if (lchown(rpath(fs_ctx, path, buffer), credp->fc_uid, |
122 | credp->fc_gid) < 0) { | |
12848bfc AK |
123 | /* |
124 | * If we fail to change ownership and if we are | |
125 | * using security model none. Ignore the error | |
126 | */ | |
b97400ca | 127 | if ((fs_ctx->export_flags & V9FS_SEC_MASK) != V9FS_SM_NONE) { |
12848bfc AK |
128 | return -1; |
129 | } | |
4750a96f VJ |
130 | } |
131 | return 0; | |
132 | } | |
133 | ||
2289be19 AK |
134 | static ssize_t local_readlink(FsContext *fs_ctx, V9fsPath *fs_path, |
135 | char *buf, size_t bufsz) | |
131dcb25 | 136 | { |
879c2813 | 137 | ssize_t tsize = -1; |
faa44e3d | 138 | char buffer[PATH_MAX]; |
2289be19 AK |
139 | char *path = fs_path->data; |
140 | ||
b97400ca | 141 | if (fs_ctx->export_flags & V9FS_SM_MAPPED) { |
879c2813 | 142 | int fd; |
faa44e3d | 143 | fd = open(rpath(fs_ctx, path, buffer), O_RDONLY); |
879c2813 VJ |
144 | if (fd == -1) { |
145 | return -1; | |
146 | } | |
147 | do { | |
148 | tsize = read(fd, (void *)buf, bufsz); | |
149 | } while (tsize == -1 && errno == EINTR); | |
150 | close(fd); | |
151 | return tsize; | |
b97400ca AK |
152 | } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) || |
153 | (fs_ctx->export_flags & V9FS_SM_NONE)) { | |
faa44e3d | 154 | tsize = readlink(rpath(fs_ctx, path, buffer), buf, bufsz); |
879c2813 VJ |
155 | } |
156 | return tsize; | |
131dcb25 AL |
157 | } |
158 | ||
cc720ddb | 159 | static int local_close(FsContext *ctx, V9fsFidOpenState *fs) |
131dcb25 | 160 | { |
cc720ddb | 161 | return close(fs->fd); |
131dcb25 AL |
162 | } |
163 | ||
cc720ddb | 164 | static int local_closedir(FsContext *ctx, V9fsFidOpenState *fs) |
131dcb25 | 165 | { |
cc720ddb | 166 | return closedir(fs->dir); |
131dcb25 | 167 | } |
9f107513 | 168 | |
cc720ddb AK |
169 | static int local_open(FsContext *ctx, V9fsPath *fs_path, |
170 | int flags, V9fsFidOpenState *fs) | |
a6568fe2 | 171 | { |
faa44e3d | 172 | char buffer[PATH_MAX]; |
2289be19 AK |
173 | char *path = fs_path->data; |
174 | ||
cc720ddb AK |
175 | fs->fd = open(rpath(ctx, path, buffer), flags); |
176 | return fs->fd; | |
a6568fe2 AL |
177 | } |
178 | ||
cc720ddb AK |
179 | static int local_opendir(FsContext *ctx, |
180 | V9fsPath *fs_path, V9fsFidOpenState *fs) | |
a6568fe2 | 181 | { |
faa44e3d | 182 | char buffer[PATH_MAX]; |
2289be19 AK |
183 | char *path = fs_path->data; |
184 | ||
cc720ddb AK |
185 | fs->dir = opendir(rpath(ctx, path, buffer)); |
186 | if (!fs->dir) { | |
187 | return -1; | |
188 | } | |
189 | return 0; | |
a6568fe2 AL |
190 | } |
191 | ||
cc720ddb | 192 | static void local_rewinddir(FsContext *ctx, V9fsFidOpenState *fs) |
a9231555 | 193 | { |
cc720ddb | 194 | return rewinddir(fs->dir); |
a9231555 AL |
195 | } |
196 | ||
cc720ddb | 197 | static off_t local_telldir(FsContext *ctx, V9fsFidOpenState *fs) |
a9231555 | 198 | { |
cc720ddb | 199 | return telldir(fs->dir); |
a9231555 AL |
200 | } |
201 | ||
cc720ddb AK |
202 | static int local_readdir_r(FsContext *ctx, V9fsFidOpenState *fs, |
203 | struct dirent *entry, | |
2289be19 | 204 | struct dirent **result) |
a9231555 | 205 | { |
cc720ddb | 206 | return readdir_r(fs->dir, entry, result); |
a9231555 AL |
207 | } |
208 | ||
cc720ddb | 209 | static void local_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off) |
a9231555 | 210 | { |
cc720ddb | 211 | return seekdir(fs->dir, off); |
a9231555 AL |
212 | } |
213 | ||
cc720ddb AK |
214 | static ssize_t local_preadv(FsContext *ctx, V9fsFidOpenState *fs, |
215 | const struct iovec *iov, | |
56d15a53 | 216 | int iovcnt, off_t offset) |
a9231555 | 217 | { |
56d15a53 | 218 | #ifdef CONFIG_PREADV |
cc720ddb | 219 | return preadv(fs->fd, iov, iovcnt, offset); |
56d15a53 | 220 | #else |
cc720ddb | 221 | int err = lseek(fs->fd, offset, SEEK_SET); |
56d15a53 SG |
222 | if (err == -1) { |
223 | return err; | |
224 | } else { | |
cc720ddb | 225 | return readv(fs->fd, iov, iovcnt); |
56d15a53 SG |
226 | } |
227 | #endif | |
a9231555 AL |
228 | } |
229 | ||
cc720ddb AK |
230 | static ssize_t local_pwritev(FsContext *ctx, V9fsFidOpenState *fs, |
231 | const struct iovec *iov, | |
2289be19 | 232 | int iovcnt, off_t offset) |
8449360c | 233 | { |
d3ab98e6 AK |
234 | ssize_t ret |
235 | ; | |
56d15a53 | 236 | #ifdef CONFIG_PREADV |
cc720ddb | 237 | ret = pwritev(fs->fd, iov, iovcnt, offset); |
56d15a53 | 238 | #else |
cc720ddb | 239 | int err = lseek(fs->fd, offset, SEEK_SET); |
56d15a53 SG |
240 | if (err == -1) { |
241 | return err; | |
242 | } else { | |
cc720ddb | 243 | ret = writev(fs->fd, iov, iovcnt); |
56d15a53 SG |
244 | } |
245 | #endif | |
d3ab98e6 AK |
246 | #ifdef CONFIG_SYNC_FILE_RANGE |
247 | if (ret > 0 && ctx->export_flags & V9FS_IMMEDIATE_WRITEOUT) { | |
248 | /* | |
249 | * Initiate a writeback. This is not a data integrity sync. | |
250 | * We want to ensure that we don't leave dirty pages in the cache | |
251 | * after write when writeout=immediate is sepcified. | |
252 | */ | |
cc720ddb | 253 | sync_file_range(fs->fd, offset, ret, |
d3ab98e6 AK |
254 | SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE); |
255 | } | |
256 | #endif | |
257 | return ret; | |
8449360c AL |
258 | } |
259 | ||
2289be19 | 260 | static int local_chmod(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp) |
c494dd6f | 261 | { |
faa44e3d | 262 | char buffer[PATH_MAX]; |
2289be19 AK |
263 | char *path = fs_path->data; |
264 | ||
b97400ca | 265 | if (fs_ctx->export_flags & V9FS_SM_MAPPED) { |
faa44e3d | 266 | return local_set_xattr(rpath(fs_ctx, path, buffer), credp); |
b97400ca AK |
267 | } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) || |
268 | (fs_ctx->export_flags & V9FS_SM_NONE)) { | |
faa44e3d | 269 | return chmod(rpath(fs_ctx, path, buffer), credp->fc_mode); |
e95ead32 VJ |
270 | } |
271 | return -1; | |
c494dd6f AL |
272 | } |
273 | ||
2289be19 AK |
274 | static int local_mknod(FsContext *fs_ctx, V9fsPath *dir_path, |
275 | const char *name, FsCred *credp) | |
c494dd6f | 276 | { |
2289be19 | 277 | char *path; |
1c293312 VJ |
278 | int err = -1; |
279 | int serrno = 0; | |
2289be19 | 280 | V9fsString fullname; |
faa44e3d | 281 | char buffer[PATH_MAX]; |
1c293312 | 282 | |
2289be19 AK |
283 | v9fs_string_init(&fullname); |
284 | v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name); | |
285 | path = fullname.data; | |
286 | ||
1c293312 | 287 | /* Determine the security model */ |
b97400ca | 288 | if (fs_ctx->export_flags & V9FS_SM_MAPPED) { |
faa44e3d VJ |
289 | err = mknod(rpath(fs_ctx, path, buffer), |
290 | SM_LOCAL_MODE_BITS|S_IFREG, 0); | |
1c293312 | 291 | if (err == -1) { |
2289be19 | 292 | goto out; |
1c293312 | 293 | } |
17b1971f | 294 | err = local_set_xattr(rpath(fs_ctx, path, buffer), credp); |
1c293312 VJ |
295 | if (err == -1) { |
296 | serrno = errno; | |
297 | goto err_end; | |
298 | } | |
b97400ca AK |
299 | } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) || |
300 | (fs_ctx->export_flags & V9FS_SM_NONE)) { | |
faa44e3d VJ |
301 | err = mknod(rpath(fs_ctx, path, buffer), credp->fc_mode, |
302 | credp->fc_rdev); | |
1c293312 | 303 | if (err == -1) { |
2289be19 | 304 | goto out; |
1c293312 VJ |
305 | } |
306 | err = local_post_create_passthrough(fs_ctx, path, credp); | |
307 | if (err == -1) { | |
308 | serrno = errno; | |
309 | goto err_end; | |
310 | } | |
311 | } | |
2289be19 | 312 | goto out; |
1c293312 VJ |
313 | |
314 | err_end: | |
faa44e3d | 315 | remove(rpath(fs_ctx, path, buffer)); |
1c293312 | 316 | errno = serrno; |
2289be19 AK |
317 | out: |
318 | v9fs_string_free(&fullname); | |
1c293312 | 319 | return err; |
c494dd6f AL |
320 | } |
321 | ||
2289be19 AK |
322 | static int local_mkdir(FsContext *fs_ctx, V9fsPath *dir_path, |
323 | const char *name, FsCred *credp) | |
c494dd6f | 324 | { |
2289be19 | 325 | char *path; |
00ec5c37 VJ |
326 | int err = -1; |
327 | int serrno = 0; | |
2289be19 | 328 | V9fsString fullname; |
faa44e3d | 329 | char buffer[PATH_MAX]; |
00ec5c37 | 330 | |
2289be19 AK |
331 | v9fs_string_init(&fullname); |
332 | v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name); | |
333 | path = fullname.data; | |
334 | ||
00ec5c37 | 335 | /* Determine the security model */ |
b97400ca | 336 | if (fs_ctx->export_flags & V9FS_SM_MAPPED) { |
faa44e3d | 337 | err = mkdir(rpath(fs_ctx, path, buffer), SM_LOCAL_DIR_MODE_BITS); |
00ec5c37 | 338 | if (err == -1) { |
2289be19 | 339 | goto out; |
00ec5c37 VJ |
340 | } |
341 | credp->fc_mode = credp->fc_mode|S_IFDIR; | |
faa44e3d | 342 | err = local_set_xattr(rpath(fs_ctx, path, buffer), credp); |
00ec5c37 VJ |
343 | if (err == -1) { |
344 | serrno = errno; | |
345 | goto err_end; | |
346 | } | |
b97400ca AK |
347 | } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) || |
348 | (fs_ctx->export_flags & V9FS_SM_NONE)) { | |
faa44e3d | 349 | err = mkdir(rpath(fs_ctx, path, buffer), credp->fc_mode); |
00ec5c37 | 350 | if (err == -1) { |
2289be19 | 351 | goto out; |
00ec5c37 VJ |
352 | } |
353 | err = local_post_create_passthrough(fs_ctx, path, credp); | |
354 | if (err == -1) { | |
355 | serrno = errno; | |
356 | goto err_end; | |
357 | } | |
358 | } | |
2289be19 | 359 | goto out; |
00ec5c37 VJ |
360 | |
361 | err_end: | |
faa44e3d | 362 | remove(rpath(fs_ctx, path, buffer)); |
00ec5c37 | 363 | errno = serrno; |
2289be19 AK |
364 | out: |
365 | v9fs_string_free(&fullname); | |
00ec5c37 | 366 | return err; |
c494dd6f AL |
367 | } |
368 | ||
8b888272 | 369 | static int local_fstat(FsContext *fs_ctx, int fid_type, |
cc720ddb | 370 | V9fsFidOpenState *fs, struct stat *stbuf) |
c494dd6f | 371 | { |
8b888272 AK |
372 | int err, fd; |
373 | ||
374 | if (fid_type == P9_FID_DIR) { | |
375 | fd = dirfd(fs->dir); | |
376 | } else { | |
377 | fd = fs->fd; | |
378 | } | |
379 | ||
380 | err = fstat(fd, stbuf); | |
1237ad76 VJ |
381 | if (err) { |
382 | return err; | |
383 | } | |
b97400ca | 384 | if (fs_ctx->export_flags & V9FS_SM_MAPPED) { |
1237ad76 VJ |
385 | /* Actual credentials are part of extended attrs */ |
386 | uid_t tmp_uid; | |
387 | gid_t tmp_gid; | |
388 | mode_t tmp_mode; | |
389 | dev_t tmp_dev; | |
390 | ||
8b888272 | 391 | if (fgetxattr(fd, "user.virtfs.uid", |
cc720ddb | 392 | &tmp_uid, sizeof(uid_t)) > 0) { |
1237ad76 VJ |
393 | stbuf->st_uid = tmp_uid; |
394 | } | |
8b888272 | 395 | if (fgetxattr(fd, "user.virtfs.gid", |
cc720ddb | 396 | &tmp_gid, sizeof(gid_t)) > 0) { |
1237ad76 VJ |
397 | stbuf->st_gid = tmp_gid; |
398 | } | |
8b888272 | 399 | if (fgetxattr(fd, "user.virtfs.mode", |
cc720ddb | 400 | &tmp_mode, sizeof(mode_t)) > 0) { |
1237ad76 VJ |
401 | stbuf->st_mode = tmp_mode; |
402 | } | |
8b888272 | 403 | if (fgetxattr(fd, "user.virtfs.rdev", |
cc720ddb | 404 | &tmp_dev, sizeof(dev_t)) > 0) { |
1237ad76 VJ |
405 | stbuf->st_rdev = tmp_dev; |
406 | } | |
407 | } | |
408 | return err; | |
c494dd6f AL |
409 | } |
410 | ||
2289be19 | 411 | static int local_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name, |
cc720ddb | 412 | int flags, FsCred *credp, V9fsFidOpenState *fs) |
c494dd6f | 413 | { |
2289be19 | 414 | char *path; |
4750a96f VJ |
415 | int fd = -1; |
416 | int err = -1; | |
417 | int serrno = 0; | |
2289be19 | 418 | V9fsString fullname; |
faa44e3d | 419 | char buffer[PATH_MAX]; |
4750a96f | 420 | |
2289be19 AK |
421 | v9fs_string_init(&fullname); |
422 | v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name); | |
423 | path = fullname.data; | |
424 | ||
4750a96f | 425 | /* Determine the security model */ |
b97400ca | 426 | if (fs_ctx->export_flags & V9FS_SM_MAPPED) { |
faa44e3d | 427 | fd = open(rpath(fs_ctx, path, buffer), flags, SM_LOCAL_MODE_BITS); |
4750a96f | 428 | if (fd == -1) { |
2289be19 AK |
429 | err = fd; |
430 | goto out; | |
4750a96f VJ |
431 | } |
432 | credp->fc_mode = credp->fc_mode|S_IFREG; | |
433 | /* Set cleint credentials in xattr */ | |
faa44e3d | 434 | err = local_set_xattr(rpath(fs_ctx, path, buffer), credp); |
4750a96f VJ |
435 | if (err == -1) { |
436 | serrno = errno; | |
437 | goto err_end; | |
438 | } | |
b97400ca AK |
439 | } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) || |
440 | (fs_ctx->export_flags & V9FS_SM_NONE)) { | |
faa44e3d | 441 | fd = open(rpath(fs_ctx, path, buffer), flags, credp->fc_mode); |
4750a96f | 442 | if (fd == -1) { |
2289be19 AK |
443 | err = fd; |
444 | goto out; | |
4750a96f VJ |
445 | } |
446 | err = local_post_create_passthrough(fs_ctx, path, credp); | |
447 | if (err == -1) { | |
448 | serrno = errno; | |
449 | goto err_end; | |
450 | } | |
451 | } | |
2289be19 | 452 | err = fd; |
cc720ddb | 453 | fs->fd = fd; |
2289be19 | 454 | goto out; |
4750a96f VJ |
455 | |
456 | err_end: | |
457 | close(fd); | |
faa44e3d | 458 | remove(rpath(fs_ctx, path, buffer)); |
4750a96f | 459 | errno = serrno; |
2289be19 AK |
460 | out: |
461 | v9fs_string_free(&fullname); | |
4750a96f | 462 | return err; |
c494dd6f AL |
463 | } |
464 | ||
758e8e38 | 465 | |
879c2813 | 466 | static int local_symlink(FsContext *fs_ctx, const char *oldpath, |
2289be19 | 467 | V9fsPath *dir_path, const char *name, FsCred *credp) |
c494dd6f | 468 | { |
879c2813 VJ |
469 | int err = -1; |
470 | int serrno = 0; | |
2289be19 AK |
471 | char *newpath; |
472 | V9fsString fullname; | |
faa44e3d | 473 | char buffer[PATH_MAX]; |
879c2813 | 474 | |
2289be19 AK |
475 | v9fs_string_init(&fullname); |
476 | v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name); | |
477 | newpath = fullname.data; | |
478 | ||
879c2813 | 479 | /* Determine the security model */ |
b97400ca | 480 | if (fs_ctx->export_flags & V9FS_SM_MAPPED) { |
879c2813 VJ |
481 | int fd; |
482 | ssize_t oldpath_size, write_size; | |
faa44e3d | 483 | fd = open(rpath(fs_ctx, newpath, buffer), O_CREAT|O_EXCL|O_RDWR, |
879c2813 VJ |
484 | SM_LOCAL_MODE_BITS); |
485 | if (fd == -1) { | |
2289be19 AK |
486 | err = fd; |
487 | goto out; | |
879c2813 VJ |
488 | } |
489 | /* Write the oldpath (target) to the file. */ | |
f35bde2f | 490 | oldpath_size = strlen(oldpath); |
879c2813 VJ |
491 | do { |
492 | write_size = write(fd, (void *)oldpath, oldpath_size); | |
493 | } while (write_size == -1 && errno == EINTR); | |
494 | ||
495 | if (write_size != oldpath_size) { | |
496 | serrno = errno; | |
497 | close(fd); | |
498 | err = -1; | |
499 | goto err_end; | |
500 | } | |
501 | close(fd); | |
502 | /* Set cleint credentials in symlink's xattr */ | |
503 | credp->fc_mode = credp->fc_mode|S_IFLNK; | |
faa44e3d | 504 | err = local_set_xattr(rpath(fs_ctx, newpath, buffer), credp); |
879c2813 VJ |
505 | if (err == -1) { |
506 | serrno = errno; | |
507 | goto err_end; | |
508 | } | |
b97400ca AK |
509 | } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) || |
510 | (fs_ctx->export_flags & V9FS_SM_NONE)) { | |
faa44e3d | 511 | err = symlink(oldpath, rpath(fs_ctx, newpath, buffer)); |
879c2813 | 512 | if (err) { |
2289be19 | 513 | goto out; |
879c2813 | 514 | } |
faa44e3d | 515 | err = lchown(rpath(fs_ctx, newpath, buffer), credp->fc_uid, |
2289be19 | 516 | credp->fc_gid); |
879c2813 | 517 | if (err == -1) { |
12848bfc AK |
518 | /* |
519 | * If we fail to change ownership and if we are | |
520 | * using security model none. Ignore the error | |
521 | */ | |
b97400ca | 522 | if ((fs_ctx->export_flags & V9FS_SEC_MASK) != V9FS_SM_NONE) { |
12848bfc AK |
523 | serrno = errno; |
524 | goto err_end; | |
525 | } else | |
526 | err = 0; | |
879c2813 VJ |
527 | } |
528 | } | |
2289be19 | 529 | goto out; |
879c2813 VJ |
530 | |
531 | err_end: | |
faa44e3d | 532 | remove(rpath(fs_ctx, newpath, buffer)); |
879c2813 | 533 | errno = serrno; |
2289be19 AK |
534 | out: |
535 | v9fs_string_free(&fullname); | |
879c2813 | 536 | return err; |
c494dd6f AL |
537 | } |
538 | ||
2289be19 AK |
539 | static int local_link(FsContext *ctx, V9fsPath *oldpath, |
540 | V9fsPath *dirpath, const char *name) | |
c494dd6f | 541 | { |
2289be19 AK |
542 | int ret; |
543 | V9fsString newpath; | |
faa44e3d | 544 | char buffer[PATH_MAX], buffer1[PATH_MAX]; |
c494dd6f | 545 | |
2289be19 AK |
546 | v9fs_string_init(&newpath); |
547 | v9fs_string_sprintf(&newpath, "%s/%s", dirpath->data, name); | |
548 | ||
549 | ret = link(rpath(ctx, oldpath->data, buffer), | |
550 | rpath(ctx, newpath.data, buffer1)); | |
551 | v9fs_string_free(&newpath); | |
552 | return ret; | |
c494dd6f AL |
553 | } |
554 | ||
2289be19 | 555 | static int local_truncate(FsContext *ctx, V9fsPath *fs_path, off_t size) |
8cf89e00 | 556 | { |
faa44e3d | 557 | char buffer[PATH_MAX]; |
2289be19 AK |
558 | char *path = fs_path->data; |
559 | ||
faa44e3d | 560 | return truncate(rpath(ctx, path, buffer), size); |
8cf89e00 AL |
561 | } |
562 | ||
563 | static int local_rename(FsContext *ctx, const char *oldpath, | |
564 | const char *newpath) | |
565 | { | |
faa44e3d | 566 | char buffer[PATH_MAX], buffer1[PATH_MAX]; |
8cf89e00 | 567 | |
faa44e3d | 568 | return rename(rpath(ctx, oldpath, buffer), rpath(ctx, newpath, buffer1)); |
8cf89e00 AL |
569 | } |
570 | ||
2289be19 | 571 | static int local_chown(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp) |
8cf89e00 | 572 | { |
faa44e3d | 573 | char buffer[PATH_MAX]; |
2289be19 AK |
574 | char *path = fs_path->data; |
575 | ||
c79ce737 | 576 | if ((credp->fc_uid == -1 && credp->fc_gid == -1) || |
17b1971f AK |
577 | (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) || |
578 | (fs_ctx->export_flags & V9FS_SM_NONE)) { | |
579 | return lchown(rpath(fs_ctx, path, buffer), | |
580 | credp->fc_uid, credp->fc_gid); | |
b97400ca | 581 | } else if (fs_ctx->export_flags & V9FS_SM_MAPPED) { |
faa44e3d | 582 | return local_set_xattr(rpath(fs_ctx, path, buffer), credp); |
f7613bee VJ |
583 | } |
584 | return -1; | |
8cf89e00 AL |
585 | } |
586 | ||
2289be19 | 587 | static int local_utimensat(FsContext *s, V9fsPath *fs_path, |
38671423 | 588 | const struct timespec *buf) |
8cf89e00 | 589 | { |
faa44e3d | 590 | char buffer[PATH_MAX]; |
2289be19 AK |
591 | char *path = fs_path->data; |
592 | ||
ae0f940e | 593 | return qemu_utimens(rpath(s, path, buffer), buf); |
8cf89e00 AL |
594 | } |
595 | ||
5bae1900 AL |
596 | static int local_remove(FsContext *ctx, const char *path) |
597 | { | |
faa44e3d VJ |
598 | char buffer[PATH_MAX]; |
599 | return remove(rpath(ctx, path, buffer)); | |
5bae1900 AL |
600 | } |
601 | ||
8b888272 AK |
602 | static int local_fsync(FsContext *ctx, int fid_type, |
603 | V9fsFidOpenState *fs, int datasync) | |
8cf89e00 | 604 | { |
8b888272 AK |
605 | int fd; |
606 | ||
607 | if (fid_type == P9_FID_DIR) { | |
608 | fd = dirfd(fs->dir); | |
609 | } else { | |
610 | fd = fs->fd; | |
611 | } | |
612 | ||
49594973 | 613 | if (datasync) { |
8b888272 | 614 | return qemu_fdatasync(fd); |
49594973 | 615 | } else { |
8b888272 | 616 | return fsync(fd); |
49594973 | 617 | } |
8cf89e00 AL |
618 | } |
619 | ||
2289be19 | 620 | static int local_statfs(FsContext *s, V9fsPath *fs_path, struct statfs *stbuf) |
be940c87 | 621 | { |
faa44e3d | 622 | char buffer[PATH_MAX]; |
2289be19 AK |
623 | char *path = fs_path->data; |
624 | ||
625 | return statfs(rpath(s, path, buffer), stbuf); | |
be940c87 MK |
626 | } |
627 | ||
2289be19 | 628 | static ssize_t local_lgetxattr(FsContext *ctx, V9fsPath *fs_path, |
fa32ef88 AK |
629 | const char *name, void *value, size_t size) |
630 | { | |
2289be19 AK |
631 | char *path = fs_path->data; |
632 | ||
fc22118d | 633 | return v9fs_get_xattr(ctx, path, name, value, size); |
fa32ef88 AK |
634 | } |
635 | ||
2289be19 | 636 | static ssize_t local_llistxattr(FsContext *ctx, V9fsPath *fs_path, |
fa32ef88 AK |
637 | void *value, size_t size) |
638 | { | |
2289be19 AK |
639 | char *path = fs_path->data; |
640 | ||
fc22118d | 641 | return v9fs_list_xattr(ctx, path, value, size); |
fa32ef88 AK |
642 | } |
643 | ||
2289be19 | 644 | static int local_lsetxattr(FsContext *ctx, V9fsPath *fs_path, const char *name, |
10b468bd AK |
645 | void *value, size_t size, int flags) |
646 | { | |
2289be19 AK |
647 | char *path = fs_path->data; |
648 | ||
fc22118d | 649 | return v9fs_set_xattr(ctx, path, name, value, size, flags); |
10b468bd AK |
650 | } |
651 | ||
2289be19 AK |
652 | static int local_lremovexattr(FsContext *ctx, V9fsPath *fs_path, |
653 | const char *name) | |
9ed3ef26 | 654 | { |
2289be19 AK |
655 | char *path = fs_path->data; |
656 | ||
fc22118d | 657 | return v9fs_remove_xattr(ctx, path, name); |
9ed3ef26 AK |
658 | } |
659 | ||
2289be19 AK |
660 | static int local_name_to_path(FsContext *ctx, V9fsPath *dir_path, |
661 | const char *name, V9fsPath *target) | |
662 | { | |
663 | if (dir_path) { | |
664 | v9fs_string_sprintf((V9fsString *)target, "%s/%s", | |
665 | dir_path->data, name); | |
666 | } else { | |
667 | v9fs_string_sprintf((V9fsString *)target, "%s", name); | |
668 | } | |
669 | /* Bump the size for including terminating NULL */ | |
670 | target->size++; | |
671 | return 0; | |
672 | } | |
673 | ||
674 | static int local_renameat(FsContext *ctx, V9fsPath *olddir, | |
675 | const char *old_name, V9fsPath *newdir, | |
676 | const char *new_name) | |
677 | { | |
678 | int ret; | |
679 | V9fsString old_full_name, new_full_name; | |
680 | ||
681 | v9fs_string_init(&old_full_name); | |
682 | v9fs_string_init(&new_full_name); | |
683 | ||
684 | v9fs_string_sprintf(&old_full_name, "%s/%s", olddir->data, old_name); | |
685 | v9fs_string_sprintf(&new_full_name, "%s/%s", newdir->data, new_name); | |
686 | ||
687 | ret = local_rename(ctx, old_full_name.data, new_full_name.data); | |
688 | v9fs_string_free(&old_full_name); | |
689 | v9fs_string_free(&new_full_name); | |
690 | return ret; | |
691 | } | |
692 | ||
693 | static int local_unlinkat(FsContext *ctx, V9fsPath *dir, | |
694 | const char *name, int flags) | |
695 | { | |
696 | int ret; | |
697 | V9fsString fullname; | |
698 | char buffer[PATH_MAX]; | |
699 | v9fs_string_init(&fullname); | |
700 | ||
701 | v9fs_string_sprintf(&fullname, "%s/%s", dir->data, name); | |
702 | ret = remove(rpath(ctx, fullname.data, buffer)); | |
703 | v9fs_string_free(&fullname); | |
704 | ||
705 | return ret; | |
706 | } | |
9ed3ef26 | 707 | |
e06a765e HPB |
708 | static int local_ioc_getversion(FsContext *ctx, V9fsPath *path, |
709 | mode_t st_mode, uint64_t *st_gen) | |
710 | { | |
cc720ddb | 711 | int err; |
ae0f940e | 712 | #ifdef FS_IOC_GETVERSION |
cc720ddb AK |
713 | V9fsFidOpenState fid_open; |
714 | ||
e06a765e HPB |
715 | /* |
716 | * Do not try to open special files like device nodes, fifos etc | |
717 | * We can get fd for regular files and directories only | |
718 | */ | |
719 | if (!S_ISREG(st_mode) && !S_ISDIR(st_mode)) { | |
720 | return 0; | |
721 | } | |
cc720ddb AK |
722 | err = local_open(ctx, path, O_RDONLY, &fid_open); |
723 | if (err < 0) { | |
724 | return err; | |
e06a765e | 725 | } |
cc720ddb AK |
726 | err = ioctl(fid_open.fd, FS_IOC_GETVERSION, st_gen); |
727 | local_close(ctx, &fid_open); | |
ae0f940e PB |
728 | #else |
729 | err = -ENOTTY; | |
730 | #endif | |
e06a765e HPB |
731 | return err; |
732 | } | |
733 | ||
0174fe73 AK |
734 | static int local_init(FsContext *ctx) |
735 | { | |
2507718b | 736 | int err = 0; |
e06a765e HPB |
737 | struct statfs stbuf; |
738 | ||
c98f1d4a | 739 | ctx->export_flags |= V9FS_PATHNAME_FSCONTEXT; |
2507718b AK |
740 | #ifdef FS_IOC_GETVERSION |
741 | /* | |
742 | * use ioc_getversion only if the iocl is definied | |
743 | */ | |
e06a765e HPB |
744 | err = statfs(ctx->fs_root, &stbuf); |
745 | if (!err) { | |
746 | switch (stbuf.f_type) { | |
747 | case EXT2_SUPER_MAGIC: | |
748 | case BTRFS_SUPER_MAGIC: | |
749 | case REISERFS_SUPER_MAGIC: | |
750 | case XFS_SUPER_MAGIC: | |
751 | ctx->exops.get_st_gen = local_ioc_getversion; | |
752 | break; | |
753 | } | |
754 | } | |
2507718b | 755 | #endif |
e06a765e | 756 | return err; |
0174fe73 AK |
757 | } |
758 | ||
99519f0a AK |
759 | static int local_parse_opts(QemuOpts *opts, struct FsDriverEntry *fse) |
760 | { | |
761 | const char *sec_model = qemu_opt_get(opts, "security_model"); | |
762 | const char *path = qemu_opt_get(opts, "path"); | |
763 | ||
764 | if (!sec_model) { | |
765 | fprintf(stderr, "security model not specified, " | |
766 | "local fs needs security model\nvalid options are:" | |
767 | "\tsecurity_model=[passthrough|mapped|none]\n"); | |
768 | return -1; | |
769 | } | |
770 | ||
771 | if (!strcmp(sec_model, "passthrough")) { | |
772 | fse->export_flags |= V9FS_SM_PASSTHROUGH; | |
773 | } else if (!strcmp(sec_model, "mapped")) { | |
774 | fse->export_flags |= V9FS_SM_MAPPED; | |
775 | } else if (!strcmp(sec_model, "none")) { | |
776 | fse->export_flags |= V9FS_SM_NONE; | |
777 | } else { | |
778 | fprintf(stderr, "Invalid security model %s specified, valid options are" | |
779 | "\n\t [passthrough|mapped|none]\n", sec_model); | |
780 | return -1; | |
781 | } | |
782 | ||
783 | if (!path) { | |
784 | fprintf(stderr, "fsdev: No path specified.\n"); | |
785 | return -1; | |
786 | } | |
787 | fse->path = g_strdup(path); | |
788 | ||
789 | return 0; | |
790 | } | |
791 | ||
9f107513 | 792 | FileOperations local_ops = { |
99519f0a | 793 | .parse_opts = local_parse_opts, |
0174fe73 | 794 | .init = local_init, |
131dcb25 | 795 | .lstat = local_lstat, |
131dcb25 AL |
796 | .readlink = local_readlink, |
797 | .close = local_close, | |
798 | .closedir = local_closedir, | |
a6568fe2 AL |
799 | .open = local_open, |
800 | .opendir = local_opendir, | |
a9231555 AL |
801 | .rewinddir = local_rewinddir, |
802 | .telldir = local_telldir, | |
5f524c1e | 803 | .readdir_r = local_readdir_r, |
a9231555 | 804 | .seekdir = local_seekdir, |
56d15a53 SG |
805 | .preadv = local_preadv, |
806 | .pwritev = local_pwritev, | |
c494dd6f AL |
807 | .chmod = local_chmod, |
808 | .mknod = local_mknod, | |
c494dd6f AL |
809 | .mkdir = local_mkdir, |
810 | .fstat = local_fstat, | |
811 | .open2 = local_open2, | |
812 | .symlink = local_symlink, | |
813 | .link = local_link, | |
8cf89e00 AL |
814 | .truncate = local_truncate, |
815 | .rename = local_rename, | |
816 | .chown = local_chown, | |
74bc02b2 | 817 | .utimensat = local_utimensat, |
5bae1900 | 818 | .remove = local_remove, |
8cf89e00 | 819 | .fsync = local_fsync, |
be940c87 | 820 | .statfs = local_statfs, |
fa32ef88 AK |
821 | .lgetxattr = local_lgetxattr, |
822 | .llistxattr = local_llistxattr, | |
10b468bd | 823 | .lsetxattr = local_lsetxattr, |
9ed3ef26 | 824 | .lremovexattr = local_lremovexattr, |
2289be19 AK |
825 | .name_to_path = local_name_to_path, |
826 | .renameat = local_renameat, | |
827 | .unlinkat = local_unlinkat, | |
9f107513 | 828 | }; |