Statistics
| Branch: | Revision:

root / hw / 9pfs / virtio-9p-local.c @ cc720ddb

History | View | Annotate | Download (20.9 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 <attr/xattr.h>
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
41

    
42
static int local_lstat(FsContext *fs_ctx, V9fsPath *fs_path, struct stat *stbuf)
43
{
44
    int err;
45
    char buffer[PATH_MAX];
46
    char *path = fs_path->data;
47

    
48
    err =  lstat(rpath(fs_ctx, path, buffer), stbuf);
49
    if (err) {
50
        return err;
51
    }
52
    if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
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;
58
        if (getxattr(rpath(fs_ctx, path, buffer), "user.virtfs.uid", &tmp_uid,
59
                    sizeof(uid_t)) > 0) {
60
            stbuf->st_uid = tmp_uid;
61
        }
62
        if (getxattr(rpath(fs_ctx, path, buffer), "user.virtfs.gid", &tmp_gid,
63
                    sizeof(gid_t)) > 0) {
64
            stbuf->st_gid = tmp_gid;
65
        }
66
        if (getxattr(rpath(fs_ctx, path, buffer), "user.virtfs.mode",
67
                    &tmp_mode, sizeof(mode_t)) > 0) {
68
            stbuf->st_mode = tmp_mode;
69
        }
70
        if (getxattr(rpath(fs_ctx, path, buffer), "user.virtfs.rdev", &tmp_dev,
71
                        sizeof(dev_t)) > 0) {
72
                stbuf->st_rdev = tmp_dev;
73
        }
74
    }
75
    return err;
76
}
77

    
78
static int local_set_xattr(const char *path, FsCred *credp)
79
{
80
    int err;
81

    
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
        }
88
    }
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
        }
95
    }
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
        }
102
    }
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
        }
109
    }
110
    return 0;
111
}
112

    
113
static int local_post_create_passthrough(FsContext *fs_ctx, const char *path,
114
                                         FsCred *credp)
115
{
116
    char buffer[PATH_MAX];
117

    
118
    if (chmod(rpath(fs_ctx, path, buffer), credp->fc_mode & 07777) < 0) {
119
        return -1;
120
    }
121
    if (lchown(rpath(fs_ctx, path, buffer), credp->fc_uid,
122
                credp->fc_gid) < 0) {
123
        /*
124
         * If we fail to change ownership and if we are
125
         * using security model none. Ignore the error
126
         */
127
        if ((fs_ctx->export_flags & V9FS_SEC_MASK) != V9FS_SM_NONE) {
128
            return -1;
129
        }
130
    }
131
    return 0;
132
}
133

    
134
static ssize_t local_readlink(FsContext *fs_ctx, V9fsPath *fs_path,
135
                              char *buf, size_t bufsz)
136
{
137
    ssize_t tsize = -1;
138
    char buffer[PATH_MAX];
139
    char *path = fs_path->data;
140

    
141
    if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
142
        int fd;
143
        fd = open(rpath(fs_ctx, path, buffer), O_RDONLY);
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;
152
    } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
153
               (fs_ctx->export_flags & V9FS_SM_NONE)) {
154
        tsize = readlink(rpath(fs_ctx, path, buffer), buf, bufsz);
155
    }
156
    return tsize;
157
}
158

    
159
static int local_close(FsContext *ctx, V9fsFidOpenState *fs)
160
{
161
    return close(fs->fd);
162
}
163

    
164
static int local_closedir(FsContext *ctx, V9fsFidOpenState *fs)
165
{
166
    return closedir(fs->dir);
167
}
168

    
169
static int local_open(FsContext *ctx, V9fsPath *fs_path,
170
                      int flags, V9fsFidOpenState *fs)
171
{
172
    char buffer[PATH_MAX];
173
    char *path = fs_path->data;
174

    
175
    fs->fd = open(rpath(ctx, path, buffer), flags);
176
    return fs->fd;
177
}
178

    
179
static int local_opendir(FsContext *ctx,
180
                         V9fsPath *fs_path, V9fsFidOpenState *fs)
181
{
182
    char buffer[PATH_MAX];
183
    char *path = fs_path->data;
184

    
185
    fs->dir = opendir(rpath(ctx, path, buffer));
186
    if (!fs->dir) {
187
        return -1;
188
    }
189
    return 0;
190
}
191

    
192
static void local_rewinddir(FsContext *ctx, V9fsFidOpenState *fs)
193
{
194
    return rewinddir(fs->dir);
195
}
196

    
197
static off_t local_telldir(FsContext *ctx, V9fsFidOpenState *fs)
198
{
199
    return telldir(fs->dir);
200
}
201

    
202
static int local_readdir_r(FsContext *ctx, V9fsFidOpenState *fs,
203
                           struct dirent *entry,
204
                           struct dirent **result)
205
{
206
    return readdir_r(fs->dir, entry, result);
207
}
208

    
209
static void local_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off)
210
{
211
    return seekdir(fs->dir, off);
212
}
213

    
214
static ssize_t local_preadv(FsContext *ctx, V9fsFidOpenState *fs,
215
                            const struct iovec *iov,
216
                            int iovcnt, off_t offset)
217
{
218
#ifdef CONFIG_PREADV
219
    return preadv(fs->fd, iov, iovcnt, offset);
220
#else
221
    int err = lseek(fs->fd, offset, SEEK_SET);
222
    if (err == -1) {
223
        return err;
224
    } else {
225
        return readv(fs->fd, iov, iovcnt);
226
    }
227
#endif
228
}
229

    
230
static ssize_t local_pwritev(FsContext *ctx, V9fsFidOpenState *fs,
231
                             const struct iovec *iov,
232
                             int iovcnt, off_t offset)
233
{
234
    ssize_t ret
235
;
236
#ifdef CONFIG_PREADV
237
    ret = pwritev(fs->fd, iov, iovcnt, offset);
238
#else
239
    int err = lseek(fs->fd, offset, SEEK_SET);
240
    if (err == -1) {
241
        return err;
242
    } else {
243
        ret = writev(fs->fd, iov, iovcnt);
244
    }
245
#endif
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
         */
253
        sync_file_range(fs->fd, offset, ret,
254
                        SYNC_FILE_RANGE_WAIT_BEFORE | SYNC_FILE_RANGE_WRITE);
255
    }
256
#endif
257
    return ret;
258
}
259

    
260
static int local_chmod(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp)
261
{
262
    char buffer[PATH_MAX];
263
    char *path = fs_path->data;
264

    
265
    if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
266
        return local_set_xattr(rpath(fs_ctx, path, buffer), credp);
267
    } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
268
               (fs_ctx->export_flags & V9FS_SM_NONE)) {
269
        return chmod(rpath(fs_ctx, path, buffer), credp->fc_mode);
270
    }
271
    return -1;
272
}
273

    
274
static int local_mknod(FsContext *fs_ctx, V9fsPath *dir_path,
275
                       const char *name, FsCred *credp)
276
{
277
    char *path;
278
    int err = -1;
279
    int serrno = 0;
280
    V9fsString fullname;
281
    char buffer[PATH_MAX];
282

    
283
    v9fs_string_init(&fullname);
284
    v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
285
    path = fullname.data;
286

    
287
    /* Determine the security model */
288
    if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
289
        err = mknod(rpath(fs_ctx, path, buffer),
290
                SM_LOCAL_MODE_BITS|S_IFREG, 0);
291
        if (err == -1) {
292
            goto out;
293
        }
294
        err = local_set_xattr(rpath(fs_ctx, path, buffer), credp);
295
        if (err == -1) {
296
            serrno = errno;
297
            goto err_end;
298
        }
299
    } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
300
               (fs_ctx->export_flags & V9FS_SM_NONE)) {
301
        err = mknod(rpath(fs_ctx, path, buffer), credp->fc_mode,
302
                credp->fc_rdev);
303
        if (err == -1) {
304
            goto out;
305
        }
306
        err = local_post_create_passthrough(fs_ctx, path, credp);
307
        if (err == -1) {
308
            serrno = errno;
309
            goto err_end;
310
        }
311
    }
312
    goto out;
313

    
314
err_end:
315
    remove(rpath(fs_ctx, path, buffer));
316
    errno = serrno;
317
out:
318
    v9fs_string_free(&fullname);
319
    return err;
320
}
321

    
322
static int local_mkdir(FsContext *fs_ctx, V9fsPath *dir_path,
323
                       const char *name, FsCred *credp)
324
{
325
    char *path;
326
    int err = -1;
327
    int serrno = 0;
328
    V9fsString fullname;
329
    char buffer[PATH_MAX];
330

    
331
    v9fs_string_init(&fullname);
332
    v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
333
    path = fullname.data;
334

    
335
    /* Determine the security model */
336
    if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
337
        err = mkdir(rpath(fs_ctx, path, buffer), SM_LOCAL_DIR_MODE_BITS);
338
        if (err == -1) {
339
            goto out;
340
        }
341
        credp->fc_mode = credp->fc_mode|S_IFDIR;
342
        err = local_set_xattr(rpath(fs_ctx, path, buffer), credp);
343
        if (err == -1) {
344
            serrno = errno;
345
            goto err_end;
346
        }
347
    } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
348
               (fs_ctx->export_flags & V9FS_SM_NONE)) {
349
        err = mkdir(rpath(fs_ctx, path, buffer), credp->fc_mode);
350
        if (err == -1) {
351
            goto out;
352
        }
353
        err = local_post_create_passthrough(fs_ctx, path, credp);
354
        if (err == -1) {
355
            serrno = errno;
356
            goto err_end;
357
        }
358
    }
359
    goto out;
360

    
361
err_end:
362
    remove(rpath(fs_ctx, path, buffer));
363
    errno = serrno;
364
out:
365
    v9fs_string_free(&fullname);
366
    return err;
367
}
368

    
369
static int local_fstat(FsContext *fs_ctx,
370
                       V9fsFidOpenState *fs, struct stat *stbuf)
371
{
372
    int err;
373
    err = fstat(fs->fd, stbuf);
374
    if (err) {
375
        return err;
376
    }
377
    if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
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

    
384
        if (fgetxattr(fs->fd, "user.virtfs.uid",
385
                      &tmp_uid, sizeof(uid_t)) > 0) {
386
            stbuf->st_uid = tmp_uid;
387
        }
388
        if (fgetxattr(fs->fd, "user.virtfs.gid",
389
                      &tmp_gid, sizeof(gid_t)) > 0) {
390
            stbuf->st_gid = tmp_gid;
391
        }
392
        if (fgetxattr(fs->fd, "user.virtfs.mode",
393
                      &tmp_mode, sizeof(mode_t)) > 0) {
394
            stbuf->st_mode = tmp_mode;
395
        }
396
        if (fgetxattr(fs->fd, "user.virtfs.rdev",
397
                      &tmp_dev, sizeof(dev_t)) > 0) {
398
                stbuf->st_rdev = tmp_dev;
399
        }
400
    }
401
    return err;
402
}
403

    
404
static int local_open2(FsContext *fs_ctx, V9fsPath *dir_path, const char *name,
405
                       int flags, FsCred *credp, V9fsFidOpenState *fs)
406
{
407
    char *path;
408
    int fd = -1;
409
    int err = -1;
410
    int serrno = 0;
411
    V9fsString fullname;
412
    char buffer[PATH_MAX];
413

    
414
    v9fs_string_init(&fullname);
415
    v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
416
    path = fullname.data;
417

    
418
    /* Determine the security model */
419
    if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
420
        fd = open(rpath(fs_ctx, path, buffer), flags, SM_LOCAL_MODE_BITS);
421
        if (fd == -1) {
422
            err = fd;
423
            goto out;
424
        }
425
        credp->fc_mode = credp->fc_mode|S_IFREG;
426
        /* Set cleint credentials in xattr */
427
        err = local_set_xattr(rpath(fs_ctx, path, buffer), credp);
428
        if (err == -1) {
429
            serrno = errno;
430
            goto err_end;
431
        }
432
    } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
433
               (fs_ctx->export_flags & V9FS_SM_NONE)) {
434
        fd = open(rpath(fs_ctx, path, buffer), flags, credp->fc_mode);
435
        if (fd == -1) {
436
            err = fd;
437
            goto out;
438
        }
439
        err = local_post_create_passthrough(fs_ctx, path, credp);
440
        if (err == -1) {
441
            serrno = errno;
442
            goto err_end;
443
        }
444
    }
445
    err = fd;
446
    fs->fd = fd;
447
    goto out;
448

    
449
err_end:
450
    close(fd);
451
    remove(rpath(fs_ctx, path, buffer));
452
    errno = serrno;
453
out:
454
    v9fs_string_free(&fullname);
455
    return err;
456
}
457

    
458

    
459
static int local_symlink(FsContext *fs_ctx, const char *oldpath,
460
                         V9fsPath *dir_path, const char *name, FsCred *credp)
461
{
462
    int err = -1;
463
    int serrno = 0;
464
    char *newpath;
465
    V9fsString fullname;
466
    char buffer[PATH_MAX];
467

    
468
    v9fs_string_init(&fullname);
469
    v9fs_string_sprintf(&fullname, "%s/%s", dir_path->data, name);
470
    newpath = fullname.data;
471

    
472
    /* Determine the security model */
473
    if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
474
        int fd;
475
        ssize_t oldpath_size, write_size;
476
        fd = open(rpath(fs_ctx, newpath, buffer), O_CREAT|O_EXCL|O_RDWR,
477
                SM_LOCAL_MODE_BITS);
478
        if (fd == -1) {
479
            err = fd;
480
            goto out;
481
        }
482
        /* Write the oldpath (target) to the file. */
483
        oldpath_size = strlen(oldpath);
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;
497
        err = local_set_xattr(rpath(fs_ctx, newpath, buffer), credp);
498
        if (err == -1) {
499
            serrno = errno;
500
            goto err_end;
501
        }
502
    } else if ((fs_ctx->export_flags & V9FS_SM_PASSTHROUGH) ||
503
               (fs_ctx->export_flags & V9FS_SM_NONE)) {
504
        err = symlink(oldpath, rpath(fs_ctx, newpath, buffer));
505
        if (err) {
506
            goto out;
507
        }
508
        err = lchown(rpath(fs_ctx, newpath, buffer), credp->fc_uid,
509
                     credp->fc_gid);
510
        if (err == -1) {
511
            /*
512
             * If we fail to change ownership and if we are
513
             * using security model none. Ignore the error
514
             */
515
            if ((fs_ctx->export_flags & V9FS_SEC_MASK) != V9FS_SM_NONE) {
516
                serrno = errno;
517
                goto err_end;
518
            } else
519
                err = 0;
520
        }
521
    }
522
    goto out;
523

    
524
err_end:
525
    remove(rpath(fs_ctx, newpath, buffer));
526
    errno = serrno;
527
out:
528
    v9fs_string_free(&fullname);
529
    return err;
530
}
531

    
532
static int local_link(FsContext *ctx, V9fsPath *oldpath,
533
                      V9fsPath *dirpath, const char *name)
534
{
535
    int ret;
536
    V9fsString newpath;
537
    char buffer[PATH_MAX], buffer1[PATH_MAX];
538

    
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;
546
}
547

    
548
static int local_truncate(FsContext *ctx, V9fsPath *fs_path, off_t size)
549
{
550
    char buffer[PATH_MAX];
551
    char *path = fs_path->data;
552

    
553
    return truncate(rpath(ctx, path, buffer), size);
554
}
555

    
556
static int local_rename(FsContext *ctx, const char *oldpath,
557
                        const char *newpath)
558
{
559
    char buffer[PATH_MAX], buffer1[PATH_MAX];
560

    
561
    return rename(rpath(ctx, oldpath, buffer), rpath(ctx, newpath, buffer1));
562
}
563

    
564
static int local_chown(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp)
565
{
566
    char buffer[PATH_MAX];
567
    char *path = fs_path->data;
568

    
569
    if ((credp->fc_uid == -1 && credp->fc_gid == -1) ||
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);
574
    } else if (fs_ctx->export_flags & V9FS_SM_MAPPED) {
575
        return local_set_xattr(rpath(fs_ctx, path, buffer), credp);
576
    }
577
    return -1;
578
}
579

    
580
static int local_utimensat(FsContext *s, V9fsPath *fs_path,
581
                           const struct timespec *buf)
582
{
583
    char buffer[PATH_MAX];
584
    char *path = fs_path->data;
585

    
586
    return qemu_utimensat(AT_FDCWD, rpath(s, path, buffer), buf,
587
                          AT_SYMLINK_NOFOLLOW);
588
}
589

    
590
static int local_remove(FsContext *ctx, const char *path)
591
{
592
    char buffer[PATH_MAX];
593
    return remove(rpath(ctx, path, buffer));
594
}
595

    
596
static int local_fsync(FsContext *ctx, V9fsFidOpenState *fs, int datasync)
597
{
598
    if (datasync) {
599
        return qemu_fdatasync(fs->fd);
600
    } else {
601
        return fsync(fs->fd);
602
    }
603
}
604

    
605
static int local_statfs(FsContext *s, V9fsPath *fs_path, struct statfs *stbuf)
606
{
607
    char buffer[PATH_MAX];
608
    char *path = fs_path->data;
609

    
610
    return statfs(rpath(s, path, buffer), stbuf);
611
}
612

    
613
static ssize_t local_lgetxattr(FsContext *ctx, V9fsPath *fs_path,
614
                               const char *name, void *value, size_t size)
615
{
616
    char *path = fs_path->data;
617

    
618
    return v9fs_get_xattr(ctx, path, name, value, size);
619
}
620

    
621
static ssize_t local_llistxattr(FsContext *ctx, V9fsPath *fs_path,
622
                                void *value, size_t size)
623
{
624
    char *path = fs_path->data;
625

    
626
    return v9fs_list_xattr(ctx, path, value, size);
627
}
628

    
629
static int local_lsetxattr(FsContext *ctx, V9fsPath *fs_path, const char *name,
630
                           void *value, size_t size, int flags)
631
{
632
    char *path = fs_path->data;
633

    
634
    return v9fs_set_xattr(ctx, path, name, value, size, flags);
635
}
636

    
637
static int local_lremovexattr(FsContext *ctx, V9fsPath *fs_path,
638
                              const char *name)
639
{
640
    char *path = fs_path->data;
641

    
642
    return v9fs_remove_xattr(ctx, path, name);
643
}
644

    
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
}
692

    
693
static int local_ioc_getversion(FsContext *ctx, V9fsPath *path,
694
                                mode_t st_mode, uint64_t *st_gen)
695
{
696
    int err;
697
    V9fsFidOpenState fid_open;
698

    
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
    }
706
    err = local_open(ctx, path, O_RDONLY, &fid_open);
707
    if (err < 0) {
708
        return err;
709
    }
710
    err = ioctl(fid_open.fd, FS_IOC_GETVERSION, st_gen);
711
    local_close(ctx, &fid_open);
712
    return err;
713
}
714

    
715
static int local_init(FsContext *ctx)
716
{
717
    int err;
718
    struct statfs stbuf;
719

    
720
    ctx->export_flags |= V9FS_PATHNAME_FSCONTEXT;
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;
733
}
734

    
735
FileOperations local_ops = {
736
    .init  = local_init,
737
    .lstat = local_lstat,
738
    .readlink = local_readlink,
739
    .close = local_close,
740
    .closedir = local_closedir,
741
    .open = local_open,
742
    .opendir = local_opendir,
743
    .rewinddir = local_rewinddir,
744
    .telldir = local_telldir,
745
    .readdir_r = local_readdir_r,
746
    .seekdir = local_seekdir,
747
    .preadv = local_preadv,
748
    .pwritev = local_pwritev,
749
    .chmod = local_chmod,
750
    .mknod = local_mknod,
751
    .mkdir = local_mkdir,
752
    .fstat = local_fstat,
753
    .open2 = local_open2,
754
    .symlink = local_symlink,
755
    .link = local_link,
756
    .truncate = local_truncate,
757
    .rename = local_rename,
758
    .chown = local_chown,
759
    .utimensat = local_utimensat,
760
    .remove = local_remove,
761
    .fsync = local_fsync,
762
    .statfs = local_statfs,
763
    .lgetxattr = local_lgetxattr,
764
    .llistxattr = local_llistxattr,
765
    .lsetxattr = local_lsetxattr,
766
    .lremovexattr = local_lremovexattr,
767
    .name_to_path = local_name_to_path,
768
    .renameat  = local_renameat,
769
    .unlinkat = local_unlinkat,
770
};