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