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