Statistics
| Branch: | Revision:

root / hw / virtio-9p-local.c @ 879c2813

History | View | Annotate | Download (11.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
#include "virtio.h"
14
#include "virtio-9p.h"
15
#include <arpa/inet.h>
16
#include <pwd.h>
17
#include <grp.h>
18
#include <sys/socket.h>
19
#include <sys/un.h>
20
#include <attr/xattr.h>
21

    
22
static const char *rpath(FsContext *ctx, const char *path)
23
{
24
    /* FIXME: so wrong... */
25
    static char buffer[4096];
26
    snprintf(buffer, sizeof(buffer), "%s/%s", ctx->fs_root, path);
27
    return buffer;
28
}
29

    
30

    
31
static int local_lstat(FsContext *fs_ctx, const char *path, struct stat *stbuf)
32
{
33
    int err;
34
    err =  lstat(rpath(fs_ctx, path), stbuf);
35
    if (err) {
36
        return err;
37
    }
38
    if (fs_ctx->fs_sm == SM_MAPPED) {
39
        /* Actual credentials are part of extended attrs */
40
        uid_t tmp_uid;
41
        gid_t tmp_gid;
42
        mode_t tmp_mode;
43
        dev_t tmp_dev;
44
        if (getxattr(rpath(fs_ctx, path), "user.virtfs.uid", &tmp_uid,
45
                    sizeof(uid_t)) > 0) {
46
            stbuf->st_uid = tmp_uid;
47
        }
48
        if (getxattr(rpath(fs_ctx, path), "user.virtfs.gid", &tmp_gid,
49
                    sizeof(gid_t)) > 0) {
50
            stbuf->st_gid = tmp_gid;
51
        }
52
        if (getxattr(rpath(fs_ctx, path), "user.virtfs.mode", &tmp_mode,
53
                    sizeof(mode_t)) > 0) {
54
            stbuf->st_mode = tmp_mode;
55
        }
56
        if (getxattr(rpath(fs_ctx, path), "user.virtfs.rdev", &tmp_dev,
57
                        sizeof(dev_t)) > 0) {
58
                stbuf->st_rdev = tmp_dev;
59
        }
60
    }
61
    return err;
62
}
63

    
64
static int local_set_xattr(const char *path, FsCred *credp)
65
{
66
    int err;
67
    if (credp->fc_uid != -1) {
68
        err = setxattr(path, "user.virtfs.uid", &credp->fc_uid, sizeof(uid_t),
69
                0);
70
        if (err) {
71
            return err;
72
        }
73
    }
74
    if (credp->fc_gid != -1) {
75
        err = setxattr(path, "user.virtfs.gid", &credp->fc_gid, sizeof(gid_t),
76
                0);
77
        if (err) {
78
            return err;
79
        }
80
    }
81
    if (credp->fc_mode != -1) {
82
        err = setxattr(path, "user.virtfs.mode", &credp->fc_mode,
83
                sizeof(mode_t), 0);
84
        if (err) {
85
            return err;
86
        }
87
    }
88
    if (credp->fc_rdev != -1) {
89
        err = setxattr(path, "user.virtfs.rdev", &credp->fc_rdev,
90
                sizeof(dev_t), 0);
91
        if (err) {
92
            return err;
93
        }
94
    }
95
    return 0;
96
}
97

    
98
static int local_post_create_passthrough(FsContext *fs_ctx, const char *path,
99
        FsCred *credp)
100
{
101
    if (chmod(rpath(fs_ctx, path), credp->fc_mode & 07777) < 0) {
102
        return -1;
103
    }
104
    if (chown(rpath(fs_ctx, path), credp->fc_uid, credp->fc_gid) < 0) {
105
        return -1;
106
    }
107
    return 0;
108
}
109

    
110
static ssize_t local_readlink(FsContext *fs_ctx, const char *path,
111
        char *buf, size_t bufsz)
112
{
113
    ssize_t tsize = -1;
114
    if (fs_ctx->fs_sm == SM_MAPPED) {
115
        int fd;
116
        fd = open(rpath(fs_ctx, path), O_RDONLY);
117
        if (fd == -1) {
118
            return -1;
119
        }
120
        do {
121
            tsize = read(fd, (void *)buf, bufsz);
122
        } while (tsize == -1 && errno == EINTR);
123
        close(fd);
124
        return tsize;
125
    } else if (fs_ctx->fs_sm == SM_PASSTHROUGH) {
126
        tsize = readlink(rpath(fs_ctx, path), buf, bufsz);
127
    }
128
    return tsize;
129
}
130

    
131
static int local_close(FsContext *ctx, int fd)
132
{
133
    return close(fd);
134
}
135

    
136
static int local_closedir(FsContext *ctx, DIR *dir)
137
{
138
    return closedir(dir);
139
}
140

    
141
static int local_open(FsContext *ctx, const char *path, int flags)
142
{
143
    return open(rpath(ctx, path), flags);
144
}
145

    
146
static DIR *local_opendir(FsContext *ctx, const char *path)
147
{
148
    return opendir(rpath(ctx, path));
149
}
150

    
151
static void local_rewinddir(FsContext *ctx, DIR *dir)
152
{
153
    return rewinddir(dir);
154
}
155

    
156
static off_t local_telldir(FsContext *ctx, DIR *dir)
157
{
158
    return telldir(dir);
159
}
160

    
161
static struct dirent *local_readdir(FsContext *ctx, DIR *dir)
162
{
163
    return readdir(dir);
164
}
165

    
166
static void local_seekdir(FsContext *ctx, DIR *dir, off_t off)
167
{
168
    return seekdir(dir, off);
169
}
170

    
171
static ssize_t local_readv(FsContext *ctx, int fd, const struct iovec *iov,
172
                            int iovcnt)
173
{
174
    return readv(fd, iov, iovcnt);
175
}
176

    
177
static off_t local_lseek(FsContext *ctx, int fd, off_t offset, int whence)
178
{
179
    return lseek(fd, offset, whence);
180
}
181

    
182
static ssize_t local_writev(FsContext *ctx, int fd, const struct iovec *iov,
183
                            int iovcnt)
184
{
185
    return writev(fd, iov, iovcnt);
186
}
187

    
188
static int local_chmod(FsContext *fs_ctx, const char *path, FsCred *credp)
189
{
190
    if (fs_ctx->fs_sm == SM_MAPPED) {
191
        return local_set_xattr(rpath(fs_ctx, path), credp);
192
    } else if (fs_ctx->fs_sm == SM_PASSTHROUGH) {
193
        return chmod(rpath(fs_ctx, path), credp->fc_mode);
194
    }
195
    return -1;
196
}
197

    
198
static int local_mknod(FsContext *ctx, const char *path, mode_t mode, dev_t dev)
199
{
200
    return mknod(rpath(ctx, path), mode, dev);
201
}
202

    
203
static int local_mksock(FsContext *ctx2, const char *path)
204
{
205
    struct sockaddr_un addr;
206
    int s;
207

    
208
    addr.sun_family = AF_UNIX;
209
    snprintf(addr.sun_path, 108, "%s", rpath(ctx2, path));
210

    
211
    s = socket(PF_UNIX, SOCK_STREAM, 0);
212
    if (s == -1) {
213
        return -1;
214
    }
215

    
216
    if (bind(s, (struct sockaddr *)&addr, sizeof(addr))) {
217
        close(s);
218
        return -1;
219
    }
220

    
221
    close(s);
222
    return 0;
223
}
224

    
225
static int local_mkdir(FsContext *fs_ctx, const char *path, FsCred *credp)
226
{
227
    int err = -1;
228
    int serrno = 0;
229

    
230
    /* Determine the security model */
231
    if (fs_ctx->fs_sm == SM_MAPPED) {
232
        err = mkdir(rpath(fs_ctx, path), SM_LOCAL_DIR_MODE_BITS);
233
        if (err == -1) {
234
            return err;
235
        }
236
        credp->fc_mode = credp->fc_mode|S_IFDIR;
237
        err = local_set_xattr(rpath(fs_ctx, path), credp);
238
        if (err == -1) {
239
            serrno = errno;
240
            goto err_end;
241
        }
242
    } else if (fs_ctx->fs_sm == SM_PASSTHROUGH) {
243
        err = mkdir(rpath(fs_ctx, path), credp->fc_mode);
244
        if (err == -1) {
245
            return err;
246
        }
247
        err = local_post_create_passthrough(fs_ctx, path, credp);
248
        if (err == -1) {
249
            serrno = errno;
250
            goto err_end;
251
        }
252
    }
253
    return err;
254

    
255
err_end:
256
    remove(rpath(fs_ctx, path));
257
    errno = serrno;
258
    return err;
259
}
260

    
261
static int local_fstat(FsContext *fs_ctx, int fd, struct stat *stbuf)
262
{
263
    int err;
264
    err = fstat(fd, stbuf);
265
    if (err) {
266
        return err;
267
    }
268
    if (fs_ctx->fs_sm == SM_MAPPED) {
269
        /* Actual credentials are part of extended attrs */
270
        uid_t tmp_uid;
271
        gid_t tmp_gid;
272
        mode_t tmp_mode;
273
        dev_t tmp_dev;
274

    
275
        if (fgetxattr(fd, "user.virtfs.uid", &tmp_uid, sizeof(uid_t)) > 0) {
276
            stbuf->st_uid = tmp_uid;
277
        }
278
        if (fgetxattr(fd, "user.virtfs.gid", &tmp_gid, sizeof(gid_t)) > 0) {
279
            stbuf->st_gid = tmp_gid;
280
        }
281
        if (fgetxattr(fd, "user.virtfs.mode", &tmp_mode, sizeof(mode_t)) > 0) {
282
            stbuf->st_mode = tmp_mode;
283
        }
284
        if (fgetxattr(fd, "user.virtfs.rdev", &tmp_dev, sizeof(dev_t)) > 0) {
285
                stbuf->st_rdev = tmp_dev;
286
        }
287
    }
288
    return err;
289
}
290

    
291
static int local_open2(FsContext *fs_ctx, const char *path, int flags,
292
        FsCred *credp)
293
{
294
    int fd = -1;
295
    int err = -1;
296
    int serrno = 0;
297

    
298
    /* Determine the security model */
299
    if (fs_ctx->fs_sm == SM_MAPPED) {
300
        fd = open(rpath(fs_ctx, path), flags, SM_LOCAL_MODE_BITS);
301
        if (fd == -1) {
302
            return fd;
303
        }
304
        credp->fc_mode = credp->fc_mode|S_IFREG;
305
        /* Set cleint credentials in xattr */
306
        err = local_set_xattr(rpath(fs_ctx, path), credp);
307
        if (err == -1) {
308
            serrno = errno;
309
            goto err_end;
310
        }
311
    } else if (fs_ctx->fs_sm == SM_PASSTHROUGH) {
312
        fd = open(rpath(fs_ctx, path), flags, credp->fc_mode);
313
        if (fd == -1) {
314
            return fd;
315
        }
316
        err = local_post_create_passthrough(fs_ctx, path, credp);
317
        if (err == -1) {
318
            serrno = errno;
319
            goto err_end;
320
        }
321
    }
322
    return fd;
323

    
324
err_end:
325
    close(fd);
326
    remove(rpath(fs_ctx, path));
327
    errno = serrno;
328
    return err;
329
}
330

    
331

    
332
static int local_symlink(FsContext *fs_ctx, const char *oldpath,
333
        const char *newpath, FsCred *credp)
334
{
335
    int err = -1;
336
    int serrno = 0;
337

    
338
    /* Determine the security model */
339
    if (fs_ctx->fs_sm == SM_MAPPED) {
340
        int fd;
341
        ssize_t oldpath_size, write_size;
342
        fd = open(rpath(fs_ctx, newpath), O_CREAT|O_EXCL|O_RDWR,
343
                SM_LOCAL_MODE_BITS);
344
        if (fd == -1) {
345
            return fd;
346
        }
347
        /* Write the oldpath (target) to the file. */
348
        oldpath_size = strlen(oldpath) + 1;
349
        do {
350
            write_size = write(fd, (void *)oldpath, oldpath_size);
351
        } while (write_size == -1 && errno == EINTR);
352

    
353
        if (write_size != oldpath_size) {
354
            serrno = errno;
355
            close(fd);
356
            err = -1;
357
            goto err_end;
358
        }
359
        close(fd);
360
        /* Set cleint credentials in symlink's xattr */
361
        credp->fc_mode = credp->fc_mode|S_IFLNK;
362
        err = local_set_xattr(rpath(fs_ctx, newpath), credp);
363
        if (err == -1) {
364
            serrno = errno;
365
            goto err_end;
366
        }
367
    } else if (fs_ctx->fs_sm == SM_PASSTHROUGH) {
368
        err = symlink(oldpath, rpath(fs_ctx, newpath));
369
        if (err) {
370
            return err;
371
        }
372
        err = lchown(rpath(fs_ctx, newpath), credp->fc_uid, credp->fc_gid);
373
        if (err == -1) {
374
            serrno = errno;
375
            goto err_end;
376
        }
377
    }
378
    return err;
379

    
380
err_end:
381
    remove(rpath(fs_ctx, newpath));
382
    errno = serrno;
383
    return err;
384
}
385

    
386
static int local_link(FsContext *ctx, const char *oldpath, const char *newpath)
387
{
388
    char *tmp = qemu_strdup(rpath(ctx, oldpath));
389
    int err, serrno = 0;
390

    
391
    if (tmp == NULL) {
392
        return -ENOMEM;
393
    }
394

    
395
    err = link(tmp, rpath(ctx, newpath));
396
    if (err == -1) {
397
        serrno = errno;
398
    }
399

    
400
    qemu_free(tmp);
401

    
402
    if (err == -1) {
403
        errno = serrno;
404
    }
405

    
406
    return err;
407
}
408

    
409
static int local_truncate(FsContext *ctx, const char *path, off_t size)
410
{
411
    return truncate(rpath(ctx, path), size);
412
}
413

    
414
static int local_rename(FsContext *ctx, const char *oldpath,
415
                        const char *newpath)
416
{
417
    char *tmp;
418
    int err;
419

    
420
    tmp = qemu_strdup(rpath(ctx, oldpath));
421
    if (tmp == NULL) {
422
        return -1;
423
    }
424

    
425
    err = rename(tmp, rpath(ctx, newpath));
426
    if (err == -1) {
427
        int serrno = errno;
428
        qemu_free(tmp);
429
        errno = serrno;
430
    } else {
431
        qemu_free(tmp);
432
    }
433

    
434
    return err;
435

    
436
}
437

    
438
static int local_chown(FsContext *fs_ctx, const char *path, FsCred *credp)
439
{
440
    if (fs_ctx->fs_sm == SM_MAPPED) {
441
        return local_set_xattr(rpath(fs_ctx, path), credp);
442
    } else if (fs_ctx->fs_sm == SM_PASSTHROUGH) {
443
        return lchown(rpath(fs_ctx, path), credp->fc_uid, credp->fc_gid);
444
    }
445
    return -1;
446
}
447

    
448
static int local_utime(FsContext *ctx, const char *path,
449
                        const struct utimbuf *buf)
450
{
451
    return utime(rpath(ctx, path), buf);
452
}
453

    
454
static int local_remove(FsContext *ctx, const char *path)
455
{
456
    return remove(rpath(ctx, path));
457
}
458

    
459
static int local_fsync(FsContext *ctx, int fd)
460
{
461
    return fsync(fd);
462
}
463

    
464
FileOperations local_ops = {
465
    .lstat = local_lstat,
466
    .readlink = local_readlink,
467
    .close = local_close,
468
    .closedir = local_closedir,
469
    .open = local_open,
470
    .opendir = local_opendir,
471
    .rewinddir = local_rewinddir,
472
    .telldir = local_telldir,
473
    .readdir = local_readdir,
474
    .seekdir = local_seekdir,
475
    .readv = local_readv,
476
    .lseek = local_lseek,
477
    .writev = local_writev,
478
    .chmod = local_chmod,
479
    .mknod = local_mknod,
480
    .mksock = local_mksock,
481
    .mkdir = local_mkdir,
482
    .fstat = local_fstat,
483
    .open2 = local_open2,
484
    .symlink = local_symlink,
485
    .link = local_link,
486
    .truncate = local_truncate,
487
    .rename = local_rename,
488
    .chown = local_chown,
489
    .utime = local_utime,
490
    .remove = local_remove,
491
    .fsync = local_fsync,
492
};