]>
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> | |
758e8e38 | 22 | #include <attr/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 | ||
cc720ddb AK |
369 | static int local_fstat(FsContext *fs_ctx, |
370 | V9fsFidOpenState *fs, struct stat *stbuf) | |
c494dd6f | 371 | { |
1237ad76 | 372 | int err; |
cc720ddb | 373 | err = fstat(fs->fd, stbuf); |
1237ad76 VJ |
374 | if (err) { |
375 | return err; | |
376 | } | |
b97400ca | 377 | if (fs_ctx->export_flags & V9FS_SM_MAPPED) { |
1237ad76 VJ |
378 | /* Actual credentials are part of extended attrs */ |
379 | uid_t tmp_uid; | |
380 | gid_t tmp_gid; | |
381 | mode_t tmp_mode; | |
382 | dev_t tmp_dev; | |
383 | ||
cc720ddb AK |
384 | if (fgetxattr(fs->fd, "user.virtfs.uid", |
385 | &tmp_uid, sizeof(uid_t)) > 0) { | |
1237ad76 VJ |
386 | stbuf->st_uid = tmp_uid; |
387 | } | |
cc720ddb AK |
388 | if (fgetxattr(fs->fd, "user.virtfs.gid", |
389 | &tmp_gid, sizeof(gid_t)) > 0) { | |
1237ad76 VJ |
390 | stbuf->st_gid = tmp_gid; |
391 | } | |
cc720ddb AK |
392 | if (fgetxattr(fs->fd, "user.virtfs.mode", |
393 | &tmp_mode, sizeof(mode_t)) > 0) { | |
1237ad76 VJ |
394 | stbuf->st_mode = tmp_mode; |
395 | } | |
cc720ddb AK |
396 | if (fgetxattr(fs->fd, "user.virtfs.rdev", |
397 | &tmp_dev, sizeof(dev_t)) > 0) { | |
1237ad76 VJ |
398 | stbuf->st_rdev = tmp_dev; |
399 | } | |
400 | } | |
401 | return err; | |
c494dd6f AL |
402 | } |
403 | ||
2289be19 | 404 | static int local_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name, |
cc720ddb | 405 | int flags, FsCred *credp, V9fsFidOpenState *fs) |
c494dd6f | 406 | { |
2289be19 | 407 | char *path; |
4750a96f VJ |
408 | int fd = -1; |
409 | int err = -1; | |
410 | int serrno = 0; | |
2289be19 | 411 | V9fsString fullname; |
faa44e3d | 412 | char buffer[PATH_MAX]; |
4750a96f | 413 | |
2289be19 AK |
414 | v9fs_string_init(&fullname); |
415 | v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name); | |
416 | path = fullname.data; | |
417 | ||
4750a96f | 418 | /* Determine the security model */ |
b97400ca | 419 | if (fs_ctx->export_flags & V9FS_SM_MAPPED) { |
faa44e3d | 420 | fd = open(rpath(fs_ctx, path, buffer), flags, SM_LOCAL_MODE_BITS); |
4750a96f | 421 | if (fd == -1) { |
2289be19 AK |
422 | err = fd; |
423 | goto out; | |
4750a96f VJ |
424 | } |
425 | credp->fc_mode = credp->fc_mode|S_IFREG; | |
426 | /* Set cleint credentials in xattr */ | |
faa44e3d | 427 | err = local_set_xattr(rpath(fs_ctx, path, buffer), credp); |
4750a96f VJ |
428 | if (err == -1) { |
429 | serrno = errno; | |
430 | goto err_end; | |
431 | } | |
b97400ca AK |
432 | } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) || |
433 | (fs_ctx->export_flags & V9FS_SM_NONE)) { | |
faa44e3d | 434 | fd = open(rpath(fs_ctx, path, buffer), flags, credp->fc_mode); |
4750a96f | 435 | if (fd == -1) { |
2289be19 AK |
436 | err = fd; |
437 | goto out; | |
4750a96f VJ |
438 | } |
439 | err = local_post_create_passthrough(fs_ctx, path, credp); | |
440 | if (err == -1) { | |
441 | serrno = errno; | |
442 | goto err_end; | |
443 | } | |
444 | } | |
2289be19 | 445 | err = fd; |
cc720ddb | 446 | fs->fd = fd; |
2289be19 | 447 | goto out; |
4750a96f VJ |
448 | |
449 | err_end: | |
450 | close(fd); | |
faa44e3d | 451 | remove(rpath(fs_ctx, path, buffer)); |
4750a96f | 452 | errno = serrno; |
2289be19 AK |
453 | out: |
454 | v9fs_string_free(&fullname); | |
4750a96f | 455 | return err; |
c494dd6f AL |
456 | } |
457 | ||
758e8e38 | 458 | |
879c2813 | 459 | static int local_symlink(FsContext *fs_ctx, const char *oldpath, |
2289be19 | 460 | V9fsPath *dir_path, const char *name, FsCred *credp) |
c494dd6f | 461 | { |
879c2813 VJ |
462 | int err = -1; |
463 | int serrno = 0; | |
2289be19 AK |
464 | char *newpath; |
465 | V9fsString fullname; | |
faa44e3d | 466 | char buffer[PATH_MAX]; |
879c2813 | 467 | |
2289be19 AK |
468 | v9fs_string_init(&fullname); |
469 | v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name); | |
470 | newpath = fullname.data; | |
471 | ||
879c2813 | 472 | /* Determine the security model */ |
b97400ca | 473 | if (fs_ctx->export_flags & V9FS_SM_MAPPED) { |
879c2813 VJ |
474 | int fd; |
475 | ssize_t oldpath_size, write_size; | |
faa44e3d | 476 | fd = open(rpath(fs_ctx, newpath, buffer), O_CREAT|O_EXCL|O_RDWR, |
879c2813 VJ |
477 | SM_LOCAL_MODE_BITS); |
478 | if (fd == -1) { | |
2289be19 AK |
479 | err = fd; |
480 | goto out; | |
879c2813 VJ |
481 | } |
482 | /* Write the oldpath (target) to the file. */ | |
f35bde2f | 483 | oldpath_size = strlen(oldpath); |
879c2813 VJ |
484 | do { |
485 | write_size = write(fd, (void *)oldpath, oldpath_size); | |
486 | } while (write_size == -1 && errno == EINTR); | |
487 | ||
488 | if (write_size != oldpath_size) { | |
489 | serrno = errno; | |
490 | close(fd); | |
491 | err = -1; | |
492 | goto err_end; | |
493 | } | |
494 | close(fd); | |
495 | /* Set cleint credentials in symlink's xattr */ | |
496 | credp->fc_mode = credp->fc_mode|S_IFLNK; | |
faa44e3d | 497 | err = local_set_xattr(rpath(fs_ctx, newpath, buffer), credp); |
879c2813 VJ |
498 | if (err == -1) { |
499 | serrno = errno; | |
500 | goto err_end; | |
501 | } | |
b97400ca AK |
502 | } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) || |
503 | (fs_ctx->export_flags & V9FS_SM_NONE)) { | |
faa44e3d | 504 | err = symlink(oldpath, rpath(fs_ctx, newpath, buffer)); |
879c2813 | 505 | if (err) { |
2289be19 | 506 | goto out; |
879c2813 | 507 | } |
faa44e3d | 508 | err = lchown(rpath(fs_ctx, newpath, buffer), credp->fc_uid, |
2289be19 | 509 | credp->fc_gid); |
879c2813 | 510 | if (err == -1) { |
12848bfc AK |
511 | /* |
512 | * If we fail to change ownership and if we are | |
513 | * using security model none. Ignore the error | |
514 | */ | |
b97400ca | 515 | if ((fs_ctx->export_flags & V9FS_SEC_MASK) != V9FS_SM_NONE) { |
12848bfc AK |
516 | serrno = errno; |
517 | goto err_end; | |
518 | } else | |
519 | err = 0; | |
879c2813 VJ |
520 | } |
521 | } | |
2289be19 | 522 | goto out; |
879c2813 VJ |
523 | |
524 | err_end: | |
faa44e3d | 525 | remove(rpath(fs_ctx, newpath, buffer)); |
879c2813 | 526 | errno = serrno; |
2289be19 AK |
527 | out: |
528 | v9fs_string_free(&fullname); | |
879c2813 | 529 | return err; |
c494dd6f AL |
530 | } |
531 | ||
2289be19 AK |
532 | static int local_link(FsContext *ctx, V9fsPath *oldpath, |
533 | V9fsPath *dirpath, const char *name) | |
c494dd6f | 534 | { |
2289be19 AK |
535 | int ret; |
536 | V9fsString newpath; | |
faa44e3d | 537 | char buffer[PATH_MAX], buffer1[PATH_MAX]; |
c494dd6f | 538 | |
2289be19 AK |
539 | v9fs_string_init(&newpath); |
540 | v9fs_string_sprintf(&newpath, "%s/%s", dirpath->data, name); | |
541 | ||
542 | ret = link(rpath(ctx, oldpath->data, buffer), | |
543 | rpath(ctx, newpath.data, buffer1)); | |
544 | v9fs_string_free(&newpath); | |
545 | return ret; | |
c494dd6f AL |
546 | } |
547 | ||
2289be19 | 548 | static int local_truncate(FsContext *ctx, V9fsPath *fs_path, off_t size) |
8cf89e00 | 549 | { |
faa44e3d | 550 | char buffer[PATH_MAX]; |
2289be19 AK |
551 | char *path = fs_path->data; |
552 | ||
faa44e3d | 553 | return truncate(rpath(ctx, path, buffer), size); |
8cf89e00 AL |
554 | } |
555 | ||
556 | static int local_rename(FsContext *ctx, const char *oldpath, | |
557 | const char *newpath) | |
558 | { | |
faa44e3d | 559 | char buffer[PATH_MAX], buffer1[PATH_MAX]; |
8cf89e00 | 560 | |
faa44e3d | 561 | return rename(rpath(ctx, oldpath, buffer), rpath(ctx, newpath, buffer1)); |
8cf89e00 AL |
562 | } |
563 | ||
2289be19 | 564 | static int local_chown(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp) |
8cf89e00 | 565 | { |
faa44e3d | 566 | char buffer[PATH_MAX]; |
2289be19 AK |
567 | char *path = fs_path->data; |
568 | ||
c79ce737 | 569 | if ((credp->fc_uid == -1 && credp->fc_gid == -1) || |
17b1971f AK |
570 | (fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) || |
571 | (fs_ctx->export_flags & V9FS_SM_NONE)) { | |
572 | return lchown(rpath(fs_ctx, path, buffer), | |
573 | credp->fc_uid, credp->fc_gid); | |
b97400ca | 574 | } else if (fs_ctx->export_flags & V9FS_SM_MAPPED) { |
faa44e3d | 575 | return local_set_xattr(rpath(fs_ctx, path, buffer), credp); |
f7613bee VJ |
576 | } |
577 | return -1; | |
8cf89e00 AL |
578 | } |
579 | ||
2289be19 | 580 | static int local_utimensat(FsContext *s, V9fsPath *fs_path, |
38671423 | 581 | const struct timespec *buf) |
8cf89e00 | 582 | { |
faa44e3d | 583 | char buffer[PATH_MAX]; |
2289be19 AK |
584 | char *path = fs_path->data; |
585 | ||
faa44e3d | 586 | return qemu_utimensat(AT_FDCWD, rpath(s, path, buffer), buf, |
2289be19 | 587 | AT_SYMLINK_NOFOLLOW); |
8cf89e00 AL |
588 | } |
589 | ||
5bae1900 AL |
590 | static int local_remove(FsContext *ctx, const char *path) |
591 | { | |
faa44e3d VJ |
592 | char buffer[PATH_MAX]; |
593 | return remove(rpath(ctx, path, buffer)); | |
5bae1900 AL |
594 | } |
595 | ||
cc720ddb | 596 | static int local_fsync(FsContext *ctx, V9fsFidOpenState *fs, int datasync) |
8cf89e00 | 597 | { |
49594973 | 598 | if (datasync) { |
cc720ddb | 599 | return qemu_fdatasync(fs->fd); |
49594973 | 600 | } else { |
cc720ddb | 601 | return fsync(fs->fd); |
49594973 | 602 | } |
8cf89e00 AL |
603 | } |
604 | ||
2289be19 | 605 | static int local_statfs(FsContext *s, V9fsPath *fs_path, struct statfs *stbuf) |
be940c87 | 606 | { |
faa44e3d | 607 | char buffer[PATH_MAX]; |
2289be19 AK |
608 | char *path = fs_path->data; |
609 | ||
610 | return statfs(rpath(s, path, buffer), stbuf); | |
be940c87 MK |
611 | } |
612 | ||
2289be19 | 613 | static ssize_t local_lgetxattr(FsContext *ctx, V9fsPath *fs_path, |
fa32ef88 AK |
614 | const char *name, void *value, size_t size) |
615 | { | |
2289be19 AK |
616 | char *path = fs_path->data; |
617 | ||
fc22118d | 618 | return v9fs_get_xattr(ctx, path, name, value, size); |
fa32ef88 AK |
619 | } |
620 | ||
2289be19 | 621 | static ssize_t local_llistxattr(FsContext *ctx, V9fsPath *fs_path, |
fa32ef88 AK |
622 | void *value, size_t size) |
623 | { | |
2289be19 AK |
624 | char *path = fs_path->data; |
625 | ||
fc22118d | 626 | return v9fs_list_xattr(ctx, path, value, size); |
fa32ef88 AK |
627 | } |
628 | ||
2289be19 | 629 | static int local_lsetxattr(FsContext *ctx, V9fsPath *fs_path, const char *name, |
10b468bd AK |
630 | void *value, size_t size, int flags) |
631 | { | |
2289be19 AK |
632 | char *path = fs_path->data; |
633 | ||
fc22118d | 634 | return v9fs_set_xattr(ctx, path, name, value, size, flags); |
10b468bd AK |
635 | } |
636 | ||
2289be19 AK |
637 | static int local_lremovexattr(FsContext *ctx, V9fsPath *fs_path, |
638 | const char *name) | |
9ed3ef26 | 639 | { |
2289be19 AK |
640 | char *path = fs_path->data; |
641 | ||
fc22118d | 642 | return v9fs_remove_xattr(ctx, path, name); |
9ed3ef26 AK |
643 | } |
644 | ||
2289be19 AK |
645 | static int local_name_to_path(FsContext *ctx, V9fsPath *dir_path, |
646 | const char *name, V9fsPath *target) | |
647 | { | |
648 | if (dir_path) { | |
649 | v9fs_string_sprintf((V9fsString *)target, "%s/%s", | |
650 | dir_path->data, name); | |
651 | } else { | |
652 | v9fs_string_sprintf((V9fsString *)target, "%s", name); | |
653 | } | |
654 | /* Bump the size for including terminating NULL */ | |
655 | target->size++; | |
656 | return 0; | |
657 | } | |
658 | ||
659 | static int local_renameat(FsContext *ctx, V9fsPath *olddir, | |
660 | const char *old_name, V9fsPath *newdir, | |
661 | const char *new_name) | |
662 | { | |
663 | int ret; | |
664 | V9fsString old_full_name, new_full_name; | |
665 | ||
666 | v9fs_string_init(&old_full_name); | |
667 | v9fs_string_init(&new_full_name); | |
668 | ||
669 | v9fs_string_sprintf(&old_full_name, "%s/%s", olddir->data, old_name); | |
670 | v9fs_string_sprintf(&new_full_name, "%s/%s", newdir->data, new_name); | |
671 | ||
672 | ret = local_rename(ctx, old_full_name.data, new_full_name.data); | |
673 | v9fs_string_free(&old_full_name); | |
674 | v9fs_string_free(&new_full_name); | |
675 | return ret; | |
676 | } | |
677 | ||
678 | static int local_unlinkat(FsContext *ctx, V9fsPath *dir, | |
679 | const char *name, int flags) | |
680 | { | |
681 | int ret; | |
682 | V9fsString fullname; | |
683 | char buffer[PATH_MAX]; | |
684 | v9fs_string_init(&fullname); | |
685 | ||
686 | v9fs_string_sprintf(&fullname, "%s/%s", dir->data, name); | |
687 | ret = remove(rpath(ctx, fullname.data, buffer)); | |
688 | v9fs_string_free(&fullname); | |
689 | ||
690 | return ret; | |
691 | } | |
9ed3ef26 | 692 | |
e06a765e HPB |
693 | static int local_ioc_getversion(FsContext *ctx, V9fsPath *path, |
694 | mode_t st_mode, uint64_t *st_gen) | |
695 | { | |
cc720ddb AK |
696 | int err; |
697 | V9fsFidOpenState fid_open; | |
698 | ||
e06a765e HPB |
699 | /* |
700 | * Do not try to open special files like device nodes, fifos etc | |
701 | * We can get fd for regular files and directories only | |
702 | */ | |
703 | if (!S_ISREG(st_mode) && !S_ISDIR(st_mode)) { | |
704 | return 0; | |
705 | } | |
cc720ddb AK |
706 | err = local_open(ctx, path, O_RDONLY, &fid_open); |
707 | if (err < 0) { | |
708 | return err; | |
e06a765e | 709 | } |
cc720ddb AK |
710 | err = ioctl(fid_open.fd, FS_IOC_GETVERSION, st_gen); |
711 | local_close(ctx, &fid_open); | |
e06a765e HPB |
712 | return err; |
713 | } | |
714 | ||
0174fe73 AK |
715 | static int local_init(FsContext *ctx) |
716 | { | |
e06a765e HPB |
717 | int err; |
718 | struct statfs stbuf; | |
719 | ||
c98f1d4a | 720 | ctx->export_flags |= V9FS_PATHNAME_FSCONTEXT; |
e06a765e HPB |
721 | err = statfs(ctx->fs_root, &stbuf); |
722 | if (!err) { | |
723 | switch (stbuf.f_type) { | |
724 | case EXT2_SUPER_MAGIC: | |
725 | case BTRFS_SUPER_MAGIC: | |
726 | case REISERFS_SUPER_MAGIC: | |
727 | case XFS_SUPER_MAGIC: | |
728 | ctx->exops.get_st_gen = local_ioc_getversion; | |
729 | break; | |
730 | } | |
731 | } | |
732 | return err; | |
0174fe73 AK |
733 | } |
734 | ||
9f107513 | 735 | FileOperations local_ops = { |
0174fe73 | 736 | .init = local_init, |
131dcb25 | 737 | .lstat = local_lstat, |
131dcb25 AL |
738 | .readlink = local_readlink, |
739 | .close = local_close, | |
740 | .closedir = local_closedir, | |
a6568fe2 AL |
741 | .open = local_open, |
742 | .opendir = local_opendir, | |
a9231555 AL |
743 | .rewinddir = local_rewinddir, |
744 | .telldir = local_telldir, | |
5f524c1e | 745 | .readdir_r = local_readdir_r, |
a9231555 | 746 | .seekdir = local_seekdir, |
56d15a53 SG |
747 | .preadv = local_preadv, |
748 | .pwritev = local_pwritev, | |
c494dd6f AL |
749 | .chmod = local_chmod, |
750 | .mknod = local_mknod, | |
c494dd6f AL |
751 | .mkdir = local_mkdir, |
752 | .fstat = local_fstat, | |
753 | .open2 = local_open2, | |
754 | .symlink = local_symlink, | |
755 | .link = local_link, | |
8cf89e00 AL |
756 | .truncate = local_truncate, |
757 | .rename = local_rename, | |
758 | .chown = local_chown, | |
74bc02b2 | 759 | .utimensat = local_utimensat, |
5bae1900 | 760 | .remove = local_remove, |
8cf89e00 | 761 | .fsync = local_fsync, |
be940c87 | 762 | .statfs = local_statfs, |
fa32ef88 AK |
763 | .lgetxattr = local_lgetxattr, |
764 | .llistxattr = local_llistxattr, | |
10b468bd | 765 | .lsetxattr = local_lsetxattr, |
9ed3ef26 | 766 | .lremovexattr = local_lremovexattr, |
2289be19 AK |
767 | .name_to_path = local_name_to_path, |
768 | .renameat = local_renameat, | |
769 | .unlinkat = local_unlinkat, | |
9f107513 | 770 | }; |