2010-04-29 16:14:44 +04:00
|
|
|
/*
|
|
|
|
* Virtio 9p backend
|
|
|
|
*
|
|
|
|
* Copyright IBM, Corp. 2010
|
|
|
|
*
|
|
|
|
* Authors:
|
|
|
|
* Anthony Liguori <aliguori@us.ibm.com>
|
|
|
|
*
|
|
|
|
* This work is licensed under the terms of the GNU GPL, version 2. See
|
|
|
|
* the COPYING file in the top-level directory.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2011-06-01 11:05:14 +04:00
|
|
|
#include "hw/virtio.h"
|
|
|
|
#include "hw/pc.h"
|
2010-04-29 16:14:44 +04:00
|
|
|
#include "qemu_socket.h"
|
2011-06-01 11:05:14 +04:00
|
|
|
#include "hw/virtio-pci.h"
|
2010-04-29 16:14:44 +04:00
|
|
|
#include "virtio-9p.h"
|
|
|
|
#include "fsdev/qemu-fsdev.h"
|
|
|
|
#include "virtio-9p-debug.h"
|
2010-10-18 13:58:16 +04:00
|
|
|
#include "virtio-9p-xattr.h"
|
2011-05-19 01:18:05 +04:00
|
|
|
#include "virtio-9p-coth.h"
|
2010-04-29 16:14:44 +04:00
|
|
|
|
|
|
|
int debug_9p_pdu;
|
|
|
|
|
2010-06-02 00:30:51 +04:00
|
|
|
enum {
|
|
|
|
Oread = 0x00,
|
|
|
|
Owrite = 0x01,
|
|
|
|
Ordwr = 0x02,
|
|
|
|
Oexec = 0x03,
|
|
|
|
Oexcl = 0x04,
|
|
|
|
Otrunc = 0x10,
|
|
|
|
Orexec = 0x20,
|
|
|
|
Orclose = 0x40,
|
|
|
|
Oappend = 0x80,
|
|
|
|
};
|
|
|
|
|
|
|
|
static int omode_to_uflags(int8_t mode)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
switch (mode & 3) {
|
|
|
|
case Oread:
|
|
|
|
ret = O_RDONLY;
|
|
|
|
break;
|
|
|
|
case Ordwr:
|
|
|
|
ret = O_RDWR;
|
|
|
|
break;
|
|
|
|
case Owrite:
|
|
|
|
ret = O_WRONLY;
|
|
|
|
break;
|
|
|
|
case Oexec:
|
|
|
|
ret = O_RDONLY;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mode & Otrunc) {
|
|
|
|
ret |= O_TRUNC;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mode & Oappend) {
|
|
|
|
ret |= O_APPEND;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mode & Oexcl) {
|
|
|
|
ret |= O_EXCL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2010-06-15 00:34:41 +04:00
|
|
|
void cred_init(FsCred *credp)
|
2010-04-29 16:14:47 +04:00
|
|
|
{
|
2010-06-15 00:34:41 +04:00
|
|
|
credp->fc_uid = -1;
|
|
|
|
credp->fc_gid = -1;
|
|
|
|
credp->fc_mode = -1;
|
|
|
|
credp->fc_rdev = -1;
|
2010-04-29 16:14:47 +04:00
|
|
|
}
|
|
|
|
|
2010-06-15 00:34:41 +04:00
|
|
|
static int v9fs_do_lstat(V9fsState *s, V9fsString *path, struct stat *stbuf)
|
2010-04-29 16:14:47 +04:00
|
|
|
{
|
2010-06-15 00:34:41 +04:00
|
|
|
return s->ops->lstat(&s->ctx, path->data, stbuf);
|
2010-04-29 16:14:47 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static int v9fs_do_close(V9fsState *s, int fd)
|
|
|
|
{
|
|
|
|
return s->ops->close(&s->ctx, fd);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int v9fs_do_closedir(V9fsState *s, DIR *dir)
|
|
|
|
{
|
|
|
|
return s->ops->closedir(&s->ctx, dir);
|
|
|
|
}
|
|
|
|
|
2010-04-29 16:14:55 +04:00
|
|
|
static int v9fs_do_open(V9fsState *s, V9fsString *path, int flags)
|
|
|
|
{
|
|
|
|
return s->ops->open(&s->ctx, path->data, flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
static DIR *v9fs_do_opendir(V9fsState *s, V9fsString *path)
|
|
|
|
{
|
|
|
|
return s->ops->opendir(&s->ctx, path->data);
|
|
|
|
}
|
|
|
|
|
2010-04-29 16:14:56 +04:00
|
|
|
static void v9fs_do_rewinddir(V9fsState *s, DIR *dir)
|
|
|
|
{
|
|
|
|
return s->ops->rewinddir(&s->ctx, dir);
|
|
|
|
}
|
|
|
|
|
|
|
|
static off_t v9fs_do_telldir(V9fsState *s, DIR *dir)
|
|
|
|
{
|
|
|
|
return s->ops->telldir(&s->ctx, dir);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct dirent *v9fs_do_readdir(V9fsState *s, DIR *dir)
|
|
|
|
{
|
|
|
|
return s->ops->readdir(&s->ctx, dir);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void v9fs_do_seekdir(V9fsState *s, DIR *dir, off_t off)
|
|
|
|
{
|
|
|
|
return s->ops->seekdir(&s->ctx, dir, off);
|
|
|
|
}
|
|
|
|
|
2010-10-08 10:00:16 +04:00
|
|
|
static int v9fs_do_preadv(V9fsState *s, int fd, const struct iovec *iov,
|
|
|
|
int iovcnt, int64_t offset)
|
2010-04-29 16:14:56 +04:00
|
|
|
{
|
2010-10-08 10:00:16 +04:00
|
|
|
return s->ops->preadv(&s->ctx, fd, iov, iovcnt, offset);
|
2010-04-29 16:14:56 +04:00
|
|
|
}
|
|
|
|
|
2010-10-08 10:00:16 +04:00
|
|
|
static int v9fs_do_pwritev(V9fsState *s, int fd, const struct iovec *iov,
|
|
|
|
int iovcnt, int64_t offset)
|
2010-04-29 16:14:56 +04:00
|
|
|
{
|
2010-10-08 10:00:16 +04:00
|
|
|
return s->ops->pwritev(&s->ctx, fd, iov, iovcnt, offset);
|
2010-04-29 16:14:58 +04:00
|
|
|
}
|
|
|
|
|
2010-04-29 16:14:59 +04:00
|
|
|
static int v9fs_do_chmod(V9fsState *s, V9fsString *path, mode_t mode)
|
|
|
|
{
|
2010-06-15 00:34:42 +04:00
|
|
|
FsCred cred;
|
|
|
|
cred_init(&cred);
|
|
|
|
cred.fc_mode = mode;
|
|
|
|
return s->ops->chmod(&s->ctx, path->data, &cred);
|
2010-04-29 16:14:59 +04:00
|
|
|
}
|
|
|
|
|
2010-06-22 10:54:09 +04:00
|
|
|
static int v9fs_do_mknod(V9fsState *s, char *name,
|
|
|
|
mode_t mode, dev_t dev, uid_t uid, gid_t gid)
|
2010-04-29 16:14:59 +04:00
|
|
|
{
|
2010-06-15 00:34:48 +04:00
|
|
|
FsCred cred;
|
|
|
|
cred_init(&cred);
|
2010-06-22 10:54:09 +04:00
|
|
|
cred.fc_uid = uid;
|
|
|
|
cred.fc_gid = gid;
|
2010-06-15 00:34:48 +04:00
|
|
|
cred.fc_mode = mode;
|
|
|
|
cred.fc_rdev = dev;
|
2010-06-22 10:54:09 +04:00
|
|
|
return s->ops->mknod(&s->ctx, name, &cred);
|
2010-04-29 16:14:59 +04:00
|
|
|
}
|
|
|
|
|
2010-06-22 10:55:22 +04:00
|
|
|
static int v9fs_do_mkdir(V9fsState *s, char *name, mode_t mode,
|
|
|
|
uid_t uid, gid_t gid)
|
2010-04-29 16:14:59 +04:00
|
|
|
{
|
2010-06-15 00:34:46 +04:00
|
|
|
FsCred cred;
|
|
|
|
|
|
|
|
cred_init(&cred);
|
2010-06-22 10:55:22 +04:00
|
|
|
cred.fc_uid = uid;
|
|
|
|
cred.fc_gid = gid;
|
|
|
|
cred.fc_mode = mode;
|
2010-06-15 00:34:46 +04:00
|
|
|
|
2010-06-22 10:55:22 +04:00
|
|
|
return s->ops->mkdir(&s->ctx, name, &cred);
|
2010-04-29 16:14:59 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static int v9fs_do_fstat(V9fsState *s, int fd, struct stat *stbuf)
|
|
|
|
{
|
|
|
|
return s->ops->fstat(&s->ctx, fd, stbuf);
|
|
|
|
}
|
|
|
|
|
2010-06-18 05:27:24 +04:00
|
|
|
static int v9fs_do_open2(V9fsState *s, char *fullname, uid_t uid, gid_t gid,
|
|
|
|
int flags, int mode)
|
2010-04-29 16:14:59 +04:00
|
|
|
{
|
2010-06-15 00:34:45 +04:00
|
|
|
FsCred cred;
|
|
|
|
|
|
|
|
cred_init(&cred);
|
2010-06-18 05:27:24 +04:00
|
|
|
cred.fc_uid = uid;
|
|
|
|
cred.fc_gid = gid;
|
|
|
|
cred.fc_mode = mode & 07777;
|
2010-06-15 00:34:45 +04:00
|
|
|
|
2010-06-18 05:27:24 +04:00
|
|
|
return s->ops->open2(&s->ctx, fullname, flags, &cred);
|
2010-04-29 16:14:59 +04:00
|
|
|
}
|
|
|
|
|
2010-06-10 01:02:08 +04:00
|
|
|
static int v9fs_do_symlink(V9fsState *s, V9fsFidState *fidp,
|
|
|
|
const char *oldpath, const char *newpath, gid_t gid)
|
2010-04-29 16:14:59 +04:00
|
|
|
{
|
2010-06-15 00:34:47 +04:00
|
|
|
FsCred cred;
|
|
|
|
cred_init(&cred);
|
2010-06-10 01:02:08 +04:00
|
|
|
cred.fc_uid = fidp->uid;
|
|
|
|
cred.fc_gid = gid;
|
|
|
|
cred.fc_mode = 0777;
|
2010-06-15 00:34:47 +04:00
|
|
|
|
2010-06-10 01:02:08 +04:00
|
|
|
return s->ops->symlink(&s->ctx, oldpath, newpath, &cred);
|
2010-04-29 16:14:59 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static int v9fs_do_link(V9fsState *s, V9fsString *oldpath, V9fsString *newpath)
|
|
|
|
{
|
|
|
|
return s->ops->link(&s->ctx, oldpath->data, newpath->data);
|
|
|
|
}
|
|
|
|
|
2010-04-29 16:15:00 +04:00
|
|
|
static int v9fs_do_truncate(V9fsState *s, V9fsString *path, off_t size)
|
|
|
|
{
|
|
|
|
return s->ops->truncate(&s->ctx, path->data, size);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int v9fs_do_rename(V9fsState *s, V9fsString *oldpath,
|
|
|
|
V9fsString *newpath)
|
|
|
|
{
|
|
|
|
return s->ops->rename(&s->ctx, oldpath->data, newpath->data);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int v9fs_do_chown(V9fsState *s, V9fsString *path, uid_t uid, gid_t gid)
|
|
|
|
{
|
2010-06-15 00:34:43 +04:00
|
|
|
FsCred cred;
|
|
|
|
cred_init(&cred);
|
|
|
|
cred.fc_uid = uid;
|
|
|
|
cred.fc_gid = gid;
|
|
|
|
|
|
|
|
return s->ops->chown(&s->ctx, path->data, &cred);
|
2010-04-29 16:15:00 +04:00
|
|
|
}
|
|
|
|
|
2010-06-09 17:44:38 +04:00
|
|
|
static int v9fs_do_utimensat(V9fsState *s, V9fsString *path,
|
|
|
|
const struct timespec times[2])
|
2010-04-29 16:15:00 +04:00
|
|
|
{
|
2010-06-09 17:44:38 +04:00
|
|
|
return s->ops->utimensat(&s->ctx, path->data, times);
|
2010-04-29 16:15:00 +04:00
|
|
|
}
|
|
|
|
|
2010-04-29 16:15:01 +04:00
|
|
|
static int v9fs_do_remove(V9fsState *s, V9fsString *path)
|
|
|
|
{
|
|
|
|
return s->ops->remove(&s->ctx, path->data);
|
|
|
|
}
|
|
|
|
|
2010-10-22 21:08:45 +04:00
|
|
|
static int v9fs_do_fsync(V9fsState *s, int fd, int datasync)
|
2010-04-29 16:15:00 +04:00
|
|
|
{
|
2010-10-22 21:08:45 +04:00
|
|
|
return s->ops->fsync(&s->ctx, fd, datasync);
|
2010-04-29 16:15:00 +04:00
|
|
|
}
|
|
|
|
|
2010-06-09 17:44:28 +04:00
|
|
|
static int v9fs_do_statfs(V9fsState *s, V9fsString *path, struct statfs *stbuf)
|
|
|
|
{
|
|
|
|
return s->ops->statfs(&s->ctx, path->data, stbuf);
|
|
|
|
}
|
|
|
|
|
2010-09-02 09:39:06 +04:00
|
|
|
static ssize_t v9fs_do_lgetxattr(V9fsState *s, V9fsString *path,
|
|
|
|
V9fsString *xattr_name,
|
|
|
|
void *value, size_t size)
|
|
|
|
{
|
|
|
|
return s->ops->lgetxattr(&s->ctx, path->data,
|
|
|
|
xattr_name->data, value, size);
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t v9fs_do_llistxattr(V9fsState *s, V9fsString *path,
|
|
|
|
void *value, size_t size)
|
|
|
|
{
|
|
|
|
return s->ops->llistxattr(&s->ctx, path->data,
|
|
|
|
value, size);
|
|
|
|
}
|
|
|
|
|
2010-09-02 09:39:07 +04:00
|
|
|
static int v9fs_do_lsetxattr(V9fsState *s, V9fsString *path,
|
|
|
|
V9fsString *xattr_name,
|
|
|
|
void *value, size_t size, int flags)
|
|
|
|
{
|
|
|
|
return s->ops->lsetxattr(&s->ctx, path->data,
|
|
|
|
xattr_name->data, value, size, flags);
|
|
|
|
}
|
|
|
|
|
2010-08-26 09:45:23 +04:00
|
|
|
static int v9fs_do_lremovexattr(V9fsState *s, V9fsString *path,
|
|
|
|
V9fsString *xattr_name)
|
|
|
|
{
|
|
|
|
return s->ops->lremovexattr(&s->ctx, path->data,
|
|
|
|
xattr_name->data);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-04-29 16:14:46 +04:00
|
|
|
static void v9fs_string_init(V9fsString *str)
|
|
|
|
{
|
|
|
|
str->data = NULL;
|
|
|
|
str->size = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void v9fs_string_free(V9fsString *str)
|
|
|
|
{
|
|
|
|
qemu_free(str->data);
|
|
|
|
str->data = NULL;
|
|
|
|
str->size = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void v9fs_string_null(V9fsString *str)
|
|
|
|
{
|
|
|
|
v9fs_string_free(str);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int number_to_string(void *arg, char type)
|
|
|
|
{
|
|
|
|
unsigned int ret = 0;
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
case 'u': {
|
|
|
|
unsigned int num = *(unsigned int *)arg;
|
|
|
|
|
|
|
|
do {
|
|
|
|
ret++;
|
|
|
|
num = num/10;
|
|
|
|
} while (num);
|
|
|
|
break;
|
|
|
|
}
|
2010-10-07 04:09:42 +04:00
|
|
|
case 'U': {
|
|
|
|
unsigned long num = *(unsigned long *)arg;
|
|
|
|
do {
|
|
|
|
ret++;
|
|
|
|
num = num/10;
|
|
|
|
} while (num);
|
|
|
|
break;
|
|
|
|
}
|
2010-04-29 16:14:46 +04:00
|
|
|
default:
|
|
|
|
printf("Number_to_string: Unknown number format\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2010-09-27 20:45:47 +04:00
|
|
|
static int GCC_FMT_ATTR(2, 0)
|
|
|
|
v9fs_string_alloc_printf(char **strp, const char *fmt, va_list ap)
|
2010-04-29 16:14:46 +04:00
|
|
|
{
|
|
|
|
va_list ap2;
|
|
|
|
char *iter = (char *)fmt;
|
|
|
|
int len = 0;
|
|
|
|
int nr_args = 0;
|
|
|
|
char *arg_char_ptr;
|
|
|
|
unsigned int arg_uint;
|
2010-10-07 04:09:42 +04:00
|
|
|
unsigned long arg_ulong;
|
2010-04-29 16:14:46 +04:00
|
|
|
|
|
|
|
/* Find the number of %'s that denotes an argument */
|
|
|
|
for (iter = strstr(iter, "%"); iter; iter = strstr(iter, "%")) {
|
|
|
|
nr_args++;
|
|
|
|
iter++;
|
|
|
|
}
|
|
|
|
|
|
|
|
len = strlen(fmt) - 2*nr_args;
|
|
|
|
|
|
|
|
if (!nr_args) {
|
|
|
|
goto alloc_print;
|
|
|
|
}
|
|
|
|
|
|
|
|
va_copy(ap2, ap);
|
|
|
|
|
|
|
|
iter = (char *)fmt;
|
|
|
|
|
|
|
|
/* Now parse the format string */
|
|
|
|
for (iter = strstr(iter, "%"); iter; iter = strstr(iter, "%")) {
|
|
|
|
iter++;
|
|
|
|
switch (*iter) {
|
|
|
|
case 'u':
|
|
|
|
arg_uint = va_arg(ap2, unsigned int);
|
|
|
|
len += number_to_string((void *)&arg_uint, 'u');
|
|
|
|
break;
|
2010-10-07 04:09:42 +04:00
|
|
|
case 'l':
|
|
|
|
if (*++iter == 'u') {
|
|
|
|
arg_ulong = va_arg(ap2, unsigned long);
|
|
|
|
len += number_to_string((void *)&arg_ulong, 'U');
|
|
|
|
} else {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
break;
|
2010-04-29 16:14:46 +04:00
|
|
|
case 's':
|
|
|
|
arg_char_ptr = va_arg(ap2, char *);
|
|
|
|
len += strlen(arg_char_ptr);
|
|
|
|
break;
|
|
|
|
case 'c':
|
|
|
|
len += 1;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
fprintf(stderr,
|
|
|
|
"v9fs_string_alloc_printf:Incorrect format %c", *iter);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
iter++;
|
|
|
|
}
|
|
|
|
|
|
|
|
alloc_print:
|
|
|
|
*strp = qemu_malloc((len + 1) * sizeof(**strp));
|
|
|
|
|
|
|
|
return vsprintf(*strp, fmt, ap);
|
|
|
|
}
|
|
|
|
|
2010-09-27 20:45:47 +04:00
|
|
|
static void GCC_FMT_ATTR(2, 3)
|
|
|
|
v9fs_string_sprintf(V9fsString *str, const char *fmt, ...)
|
2010-04-29 16:14:46 +04:00
|
|
|
{
|
|
|
|
va_list ap;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
v9fs_string_free(str);
|
|
|
|
|
|
|
|
va_start(ap, fmt);
|
|
|
|
err = v9fs_string_alloc_printf(&str->data, fmt, ap);
|
|
|
|
BUG_ON(err == -1);
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
str->size = err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void v9fs_string_copy(V9fsString *lhs, V9fsString *rhs)
|
|
|
|
{
|
|
|
|
v9fs_string_free(lhs);
|
|
|
|
v9fs_string_sprintf(lhs, "%s", rhs->data);
|
|
|
|
}
|
|
|
|
|
2011-06-01 11:05:11 +04:00
|
|
|
/*
|
|
|
|
* Return TRUE if s1 is an ancestor of s2.
|
|
|
|
*
|
|
|
|
* E.g. "a/b" is an ancestor of "a/b/c" but not of "a/bc/d".
|
|
|
|
* As a special case, We treat s1 as ancestor of s2 if they are same!
|
|
|
|
*/
|
|
|
|
static int v9fs_path_is_ancestor(V9fsString *s1, V9fsString *s2)
|
|
|
|
{
|
|
|
|
if (!strncmp(s1->data, s2->data, s1->size)) {
|
|
|
|
if (s2->data[s1->size] == '\0' || s2->data[s1->size] == '/') {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-04-29 16:14:46 +04:00
|
|
|
static size_t v9fs_string_size(V9fsString *str)
|
|
|
|
{
|
|
|
|
return str->size;
|
|
|
|
}
|
|
|
|
|
2010-04-29 16:14:48 +04:00
|
|
|
static V9fsFidState *lookup_fid(V9fsState *s, int32_t fid)
|
|
|
|
{
|
|
|
|
V9fsFidState *f;
|
|
|
|
|
|
|
|
for (f = s->fid_list; f; f = f->next) {
|
|
|
|
if (f->fid == fid) {
|
|
|
|
return f;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static V9fsFidState *alloc_fid(V9fsState *s, int32_t fid)
|
|
|
|
{
|
|
|
|
V9fsFidState *f;
|
|
|
|
|
|
|
|
f = lookup_fid(s, fid);
|
|
|
|
if (f) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
f = qemu_mallocz(sizeof(V9fsFidState));
|
|
|
|
|
|
|
|
f->fid = fid;
|
2010-09-02 09:39:06 +04:00
|
|
|
f->fid_type = P9_FID_NONE;
|
2010-04-29 16:14:48 +04:00
|
|
|
|
|
|
|
f->next = s->fid_list;
|
|
|
|
s->fid_list = f;
|
|
|
|
|
|
|
|
return f;
|
|
|
|
}
|
|
|
|
|
2010-09-02 09:39:07 +04:00
|
|
|
static int v9fs_xattr_fid_clunk(V9fsState *s, V9fsFidState *fidp)
|
|
|
|
{
|
|
|
|
int retval = 0;
|
|
|
|
|
|
|
|
if (fidp->fs.xattr.copied_len == -1) {
|
|
|
|
/* getxattr/listxattr fid */
|
|
|
|
goto free_value;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* if this is fid for setxattr. clunk should
|
|
|
|
* result in setxattr localcall
|
|
|
|
*/
|
|
|
|
if (fidp->fs.xattr.len != fidp->fs.xattr.copied_len) {
|
|
|
|
/* clunk after partial write */
|
|
|
|
retval = -EINVAL;
|
|
|
|
goto free_out;
|
|
|
|
}
|
2010-08-26 09:45:23 +04:00
|
|
|
if (fidp->fs.xattr.len) {
|
|
|
|
retval = v9fs_do_lsetxattr(s, &fidp->path, &fidp->fs.xattr.name,
|
|
|
|
fidp->fs.xattr.value,
|
|
|
|
fidp->fs.xattr.len,
|
|
|
|
fidp->fs.xattr.flags);
|
|
|
|
} else {
|
|
|
|
retval = v9fs_do_lremovexattr(s, &fidp->path, &fidp->fs.xattr.name);
|
|
|
|
}
|
2010-09-02 09:39:07 +04:00
|
|
|
free_out:
|
|
|
|
v9fs_string_free(&fidp->fs.xattr.name);
|
|
|
|
free_value:
|
|
|
|
if (fidp->fs.xattr.value) {
|
|
|
|
qemu_free(fidp->fs.xattr.value);
|
|
|
|
}
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2010-04-29 16:14:48 +04:00
|
|
|
static int free_fid(V9fsState *s, int32_t fid)
|
|
|
|
{
|
2010-09-02 09:39:07 +04:00
|
|
|
int retval = 0;
|
2010-04-29 16:14:48 +04:00
|
|
|
V9fsFidState **fidpp, *fidp;
|
|
|
|
|
|
|
|
for (fidpp = &s->fid_list; *fidpp; fidpp = &(*fidpp)->next) {
|
|
|
|
if ((*fidpp)->fid == fid) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*fidpp == NULL) {
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
|
|
|
|
fidp = *fidpp;
|
|
|
|
*fidpp = fidp->next;
|
|
|
|
|
2010-09-02 09:39:06 +04:00
|
|
|
if (fidp->fid_type == P9_FID_FILE) {
|
|
|
|
v9fs_do_close(s, fidp->fs.fd);
|
|
|
|
} else if (fidp->fid_type == P9_FID_DIR) {
|
|
|
|
v9fs_do_closedir(s, fidp->fs.dir);
|
|
|
|
} else if (fidp->fid_type == P9_FID_XATTR) {
|
2010-09-02 09:39:07 +04:00
|
|
|
retval = v9fs_xattr_fid_clunk(s, fidp);
|
2010-04-29 16:14:48 +04:00
|
|
|
}
|
|
|
|
v9fs_string_free(&fidp->path);
|
|
|
|
qemu_free(fidp);
|
|
|
|
|
2010-09-02 09:39:07 +04:00
|
|
|
return retval;
|
2010-04-29 16:14:48 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
#define P9_QID_TYPE_DIR 0x80
|
|
|
|
#define P9_QID_TYPE_SYMLINK 0x02
|
|
|
|
|
|
|
|
#define P9_STAT_MODE_DIR 0x80000000
|
|
|
|
#define P9_STAT_MODE_APPEND 0x40000000
|
|
|
|
#define P9_STAT_MODE_EXCL 0x20000000
|
|
|
|
#define P9_STAT_MODE_MOUNT 0x10000000
|
|
|
|
#define P9_STAT_MODE_AUTH 0x08000000
|
|
|
|
#define P9_STAT_MODE_TMP 0x04000000
|
|
|
|
#define P9_STAT_MODE_SYMLINK 0x02000000
|
|
|
|
#define P9_STAT_MODE_LINK 0x01000000
|
|
|
|
#define P9_STAT_MODE_DEVICE 0x00800000
|
|
|
|
#define P9_STAT_MODE_NAMED_PIPE 0x00200000
|
|
|
|
#define P9_STAT_MODE_SOCKET 0x00100000
|
|
|
|
#define P9_STAT_MODE_SETUID 0x00080000
|
|
|
|
#define P9_STAT_MODE_SETGID 0x00040000
|
|
|
|
#define P9_STAT_MODE_SETVTX 0x00010000
|
|
|
|
|
|
|
|
#define P9_STAT_MODE_TYPE_BITS (P9_STAT_MODE_DIR | \
|
|
|
|
P9_STAT_MODE_SYMLINK | \
|
|
|
|
P9_STAT_MODE_LINK | \
|
|
|
|
P9_STAT_MODE_DEVICE | \
|
|
|
|
P9_STAT_MODE_NAMED_PIPE | \
|
|
|
|
P9_STAT_MODE_SOCKET)
|
|
|
|
|
|
|
|
/* This is the algorithm from ufs in spfs */
|
|
|
|
static void stat_to_qid(const struct stat *stbuf, V9fsQID *qidp)
|
|
|
|
{
|
|
|
|
size_t size;
|
|
|
|
|
|
|
|
size = MIN(sizeof(stbuf->st_ino), sizeof(qidp->path));
|
|
|
|
memcpy(&qidp->path, &stbuf->st_ino, size);
|
|
|
|
qidp->version = stbuf->st_mtime ^ (stbuf->st_size << 8);
|
|
|
|
qidp->type = 0;
|
|
|
|
if (S_ISDIR(stbuf->st_mode)) {
|
|
|
|
qidp->type |= P9_QID_TYPE_DIR;
|
|
|
|
}
|
|
|
|
if (S_ISLNK(stbuf->st_mode)) {
|
|
|
|
qidp->type |= P9_QID_TYPE_SYMLINK;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int fid_to_qid(V9fsState *s, V9fsFidState *fidp, V9fsQID *qidp)
|
|
|
|
{
|
|
|
|
struct stat stbuf;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
err = v9fs_do_lstat(s, &fidp->path, &stbuf);
|
|
|
|
if (err) {
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
stat_to_qid(&stbuf, qidp);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-04-29 16:14:44 +04:00
|
|
|
static V9fsPDU *alloc_pdu(V9fsState *s)
|
|
|
|
{
|
|
|
|
V9fsPDU *pdu = NULL;
|
|
|
|
|
|
|
|
if (!QLIST_EMPTY(&s->free_list)) {
|
|
|
|
pdu = QLIST_FIRST(&s->free_list);
|
|
|
|
QLIST_REMOVE(pdu, next);
|
|
|
|
}
|
|
|
|
return pdu;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void free_pdu(V9fsState *s, V9fsPDU *pdu)
|
|
|
|
{
|
|
|
|
if (pdu) {
|
2011-04-27 10:55:46 +04:00
|
|
|
if (debug_9p_pdu) {
|
|
|
|
pprint_pdu(pdu);
|
|
|
|
}
|
|
|
|
QLIST_INSERT_HEAD(&s->free_list, pdu, next);
|
2010-04-29 16:14:44 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t pdu_packunpack(void *addr, struct iovec *sg, int sg_count,
|
|
|
|
size_t offset, size_t size, int pack)
|
|
|
|
{
|
|
|
|
int i = 0;
|
|
|
|
size_t copied = 0;
|
|
|
|
|
|
|
|
for (i = 0; size && i < sg_count; i++) {
|
|
|
|
size_t len;
|
|
|
|
if (offset >= sg[i].iov_len) {
|
|
|
|
/* skip this sg */
|
|
|
|
offset -= sg[i].iov_len;
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
len = MIN(sg[i].iov_len - offset, size);
|
|
|
|
if (pack) {
|
|
|
|
memcpy(sg[i].iov_base + offset, addr, len);
|
|
|
|
} else {
|
|
|
|
memcpy(addr, sg[i].iov_base + offset, len);
|
|
|
|
}
|
|
|
|
size -= len;
|
|
|
|
copied += len;
|
|
|
|
addr += len;
|
|
|
|
if (size) {
|
|
|
|
offset = 0;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return copied;
|
|
|
|
}
|
|
|
|
|
2010-04-29 16:14:45 +04:00
|
|
|
static size_t pdu_unpack(void *dst, V9fsPDU *pdu, size_t offset, size_t size)
|
|
|
|
{
|
|
|
|
return pdu_packunpack(dst, pdu->elem.out_sg, pdu->elem.out_num,
|
|
|
|
offset, size, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static size_t pdu_pack(V9fsPDU *pdu, size_t offset, const void *src,
|
|
|
|
size_t size)
|
|
|
|
{
|
|
|
|
return pdu_packunpack((void *)src, pdu->elem.in_sg, pdu->elem.in_num,
|
|
|
|
offset, size, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int pdu_copy_sg(V9fsPDU *pdu, size_t offset, int rx, struct iovec *sg)
|
|
|
|
{
|
|
|
|
size_t pos = 0;
|
|
|
|
int i, j;
|
|
|
|
struct iovec *src_sg;
|
|
|
|
unsigned int num;
|
|
|
|
|
|
|
|
if (rx) {
|
|
|
|
src_sg = pdu->elem.in_sg;
|
|
|
|
num = pdu->elem.in_num;
|
|
|
|
} else {
|
|
|
|
src_sg = pdu->elem.out_sg;
|
|
|
|
num = pdu->elem.out_num;
|
|
|
|
}
|
|
|
|
|
|
|
|
j = 0;
|
|
|
|
for (i = 0; i < num; i++) {
|
|
|
|
if (offset <= pos) {
|
|
|
|
sg[j].iov_base = src_sg[i].iov_base;
|
|
|
|
sg[j].iov_len = src_sg[i].iov_len;
|
|
|
|
j++;
|
|
|
|
} else if (offset < (src_sg[i].iov_len + pos)) {
|
|
|
|
sg[j].iov_base = src_sg[i].iov_base;
|
|
|
|
sg[j].iov_len = src_sg[i].iov_len;
|
|
|
|
sg[j].iov_base += (offset - pos);
|
|
|
|
sg[j].iov_len -= (offset - pos);
|
|
|
|
j++;
|
|
|
|
}
|
|
|
|
pos += src_sg[i].iov_len;
|
|
|
|
}
|
|
|
|
|
|
|
|
return j;
|
|
|
|
}
|
|
|
|
|
|
|
|
static size_t pdu_unmarshal(V9fsPDU *pdu, size_t offset, const char *fmt, ...)
|
|
|
|
{
|
|
|
|
size_t old_offset = offset;
|
|
|
|
va_list ap;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
va_start(ap, fmt);
|
|
|
|
for (i = 0; fmt[i]; i++) {
|
|
|
|
switch (fmt[i]) {
|
|
|
|
case 'b': {
|
|
|
|
uint8_t *valp = va_arg(ap, uint8_t *);
|
|
|
|
offset += pdu_unpack(valp, pdu, offset, sizeof(*valp));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 'w': {
|
|
|
|
uint16_t val, *valp;
|
|
|
|
valp = va_arg(ap, uint16_t *);
|
|
|
|
offset += pdu_unpack(&val, pdu, offset, sizeof(val));
|
2011-04-07 07:02:03 +04:00
|
|
|
*valp = le16_to_cpu(val);
|
2010-04-29 16:14:45 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 'd': {
|
|
|
|
uint32_t val, *valp;
|
|
|
|
valp = va_arg(ap, uint32_t *);
|
|
|
|
offset += pdu_unpack(&val, pdu, offset, sizeof(val));
|
2011-04-07 07:02:03 +04:00
|
|
|
*valp = le32_to_cpu(val);
|
2010-04-29 16:14:45 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 'q': {
|
|
|
|
uint64_t val, *valp;
|
|
|
|
valp = va_arg(ap, uint64_t *);
|
|
|
|
offset += pdu_unpack(&val, pdu, offset, sizeof(val));
|
2011-04-07 07:02:03 +04:00
|
|
|
*valp = le64_to_cpu(val);
|
2010-04-29 16:14:45 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 'v': {
|
|
|
|
struct iovec *iov = va_arg(ap, struct iovec *);
|
|
|
|
int *iovcnt = va_arg(ap, int *);
|
|
|
|
*iovcnt = pdu_copy_sg(pdu, offset, 0, iov);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 's': {
|
|
|
|
V9fsString *str = va_arg(ap, V9fsString *);
|
|
|
|
offset += pdu_unmarshal(pdu, offset, "w", &str->size);
|
|
|
|
/* FIXME: sanity check str->size */
|
|
|
|
str->data = qemu_malloc(str->size + 1);
|
|
|
|
offset += pdu_unpack(str->data, pdu, offset, str->size);
|
|
|
|
str->data[str->size] = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 'Q': {
|
|
|
|
V9fsQID *qidp = va_arg(ap, V9fsQID *);
|
|
|
|
offset += pdu_unmarshal(pdu, offset, "bdq",
|
|
|
|
&qidp->type, &qidp->version, &qidp->path);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 'S': {
|
|
|
|
V9fsStat *statp = va_arg(ap, V9fsStat *);
|
|
|
|
offset += pdu_unmarshal(pdu, offset, "wwdQdddqsssssddd",
|
|
|
|
&statp->size, &statp->type, &statp->dev,
|
|
|
|
&statp->qid, &statp->mode, &statp->atime,
|
|
|
|
&statp->mtime, &statp->length,
|
|
|
|
&statp->name, &statp->uid, &statp->gid,
|
|
|
|
&statp->muid, &statp->extension,
|
|
|
|
&statp->n_uid, &statp->n_gid,
|
|
|
|
&statp->n_muid);
|
|
|
|
break;
|
|
|
|
}
|
virtio-9p: Implement server side of setattr for 9P2000.L protocol.
SYNOPSIS
size[4] Tsetattr tag[2] attr[n]
size[4] Rsetattr tag[2]
DESCRIPTION
The setattr command changes some of the file status information.
attr resembles the iattr structure used in Linux kernel. It
specifies which status parameter is to be changed and to what
value. It is laid out as follows:
valid[4]
specifies which status information is to be changed. Possible
values are:
ATTR_MODE (1 << 0)
ATTR_UID (1 << 1)
ATTR_GID (1 << 2)
ATTR_SIZE (1 << 3)
ATTR_ATIME (1 << 4)
ATTR_MTIME (1 << 5)
ATTR_CTIME (1 << 5)
ATTR_ATIME_SET (1 << 7)
ATTR_MTIME_SET (1 << 8)
The last two bits represent whether the time information
is being sent by the client's user space. In the absense
of these bits the server always uses server's time.
mode[4]
File permission bits
uid[4]
Owner id of file
gid[4]
Group id of the file
size[8]
File size
atime_sec[8]
Time of last file access, seconds
atime_nsec[8]
Time of last file access, nanoseconds
mtime_sec[8]
Time of last file modification, seconds
mtime_nsec[8]
Time of last file modification, nanoseconds
Explanation of the patches:
--------------------------
*) The kernel just copies relevent contents of iattr structure to p9_iattr_dotl
structure and passes it down to the client. The only check it has is calling
inode_change_ok()
*) The p9_iattr_dotl structure does not have ctime and ia_file parameters because
I don't think these are needed in our case. The client user space can request
updating just ctime by calling chown(fd, -1, -1). This is handled on server
side without a need for putting ctime on the wire.
*) The server currently supports changing mode, time, ownership and size of the
file.
*) 9P RFC says "Either all the changes in wstat request happen, or none of them
does: if the request succeeds, all changes were made; if it fails, none were."
I have not done anything to implement this specifically because I don't see
a reason.
[jvrao@linux.vnet.ibm.com: Parts of code for handling chown(-1,-1)
Signed-off-by: Sripathi Kodi <sripathik@in.ibm.com>
Signed-off-by: Venkateswararao Jujjuri <jvrao@linux.vnet.ibm.com>
2010-06-17 16:48:47 +04:00
|
|
|
case 'I': {
|
|
|
|
V9fsIattr *iattr = va_arg(ap, V9fsIattr *);
|
|
|
|
offset += pdu_unmarshal(pdu, offset, "ddddqqqqq",
|
|
|
|
&iattr->valid, &iattr->mode,
|
|
|
|
&iattr->uid, &iattr->gid, &iattr->size,
|
|
|
|
&iattr->atime_sec, &iattr->atime_nsec,
|
|
|
|
&iattr->mtime_sec, &iattr->mtime_nsec);
|
|
|
|
break;
|
|
|
|
}
|
2010-04-29 16:14:45 +04:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
return offset - old_offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
static size_t pdu_marshal(V9fsPDU *pdu, size_t offset, const char *fmt, ...)
|
|
|
|
{
|
|
|
|
size_t old_offset = offset;
|
|
|
|
va_list ap;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
va_start(ap, fmt);
|
|
|
|
for (i = 0; fmt[i]; i++) {
|
|
|
|
switch (fmt[i]) {
|
|
|
|
case 'b': {
|
|
|
|
uint8_t val = va_arg(ap, int);
|
|
|
|
offset += pdu_pack(pdu, offset, &val, sizeof(val));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 'w': {
|
|
|
|
uint16_t val;
|
|
|
|
cpu_to_le16w(&val, va_arg(ap, int));
|
|
|
|
offset += pdu_pack(pdu, offset, &val, sizeof(val));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 'd': {
|
|
|
|
uint32_t val;
|
|
|
|
cpu_to_le32w(&val, va_arg(ap, uint32_t));
|
|
|
|
offset += pdu_pack(pdu, offset, &val, sizeof(val));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 'q': {
|
|
|
|
uint64_t val;
|
|
|
|
cpu_to_le64w(&val, va_arg(ap, uint64_t));
|
|
|
|
offset += pdu_pack(pdu, offset, &val, sizeof(val));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 'v': {
|
|
|
|
struct iovec *iov = va_arg(ap, struct iovec *);
|
|
|
|
int *iovcnt = va_arg(ap, int *);
|
|
|
|
*iovcnt = pdu_copy_sg(pdu, offset, 1, iov);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 's': {
|
|
|
|
V9fsString *str = va_arg(ap, V9fsString *);
|
|
|
|
offset += pdu_marshal(pdu, offset, "w", str->size);
|
|
|
|
offset += pdu_pack(pdu, offset, str->data, str->size);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 'Q': {
|
|
|
|
V9fsQID *qidp = va_arg(ap, V9fsQID *);
|
|
|
|
offset += pdu_marshal(pdu, offset, "bdq",
|
|
|
|
qidp->type, qidp->version, qidp->path);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 'S': {
|
|
|
|
V9fsStat *statp = va_arg(ap, V9fsStat *);
|
|
|
|
offset += pdu_marshal(pdu, offset, "wwdQdddqsssssddd",
|
|
|
|
statp->size, statp->type, statp->dev,
|
|
|
|
&statp->qid, statp->mode, statp->atime,
|
|
|
|
statp->mtime, statp->length, &statp->name,
|
|
|
|
&statp->uid, &statp->gid, &statp->muid,
|
|
|
|
&statp->extension, statp->n_uid,
|
|
|
|
statp->n_gid, statp->n_muid);
|
|
|
|
break;
|
|
|
|
}
|
virtio-9p: getattr server implementation for 9P2000.L protocol.
SYNOPSIS
size[4] Tgetattr tag[2] fid[4] request_mask[8]
size[4] Rgetattr tag[2] lstat[n]
DESCRIPTION
The getattr transaction inquires about the file identified by fid.
request_mask is a bit mask that specifies which fields of the
stat structure is the client interested in.
The reply will contain a machine-independent directory entry,
laid out as follows:
st_result_mask[8]
Bit mask that indicates which fields in the stat structure
have been populated by the server
qid.type[1]
the type of the file (directory, etc.), represented as a bit
vector corresponding to the high 8 bits of the file's mode
word.
qid.vers[4]
version number for given path
qid.path[8]
the file server's unique identification for the file
st_mode[4]
Permission and flags
st_uid[4]
User id of owner
st_gid[4]
Group ID of owner
st_nlink[8]
Number of hard links
st_rdev[8]
Device ID (if special file)
st_size[8]
Size, in bytes
st_blksize[8]
Block size for file system IO
st_blocks[8]
Number of file system blocks allocated
st_atime_sec[8]
Time of last access, seconds
st_atime_nsec[8]
Time of last access, nanoseconds
st_mtime_sec[8]
Time of last modification, seconds
st_mtime_nsec[8]
Time of last modification, nanoseconds
st_ctime_sec[8]
Time of last status change, seconds
st_ctime_nsec[8]
Time of last status change, nanoseconds
st_btime_sec[8]
Time of creation (birth) of file, seconds
st_btime_nsec[8]
Time of creation (birth) of file, nanoseconds
st_gen[8]
Inode generation
st_data_version[8]
Data version number
request_mask and result_mask bit masks contain the following bits
#define P9_STATS_MODE 0x00000001ULL
#define P9_STATS_NLINK 0x00000002ULL
#define P9_STATS_UID 0x00000004ULL
#define P9_STATS_GID 0x00000008ULL
#define P9_STATS_RDEV 0x00000010ULL
#define P9_STATS_ATIME 0x00000020ULL
#define P9_STATS_MTIME 0x00000040ULL
#define P9_STATS_CTIME 0x00000080ULL
#define P9_STATS_INO 0x00000100ULL
#define P9_STATS_SIZE 0x00000200ULL
#define P9_STATS_BLOCKS 0x00000400ULL
#define P9_STATS_BTIME 0x00000800ULL
#define P9_STATS_GEN 0x00001000ULL
#define P9_STATS_DATA_VERSION 0x00002000ULL
#define P9_STATS_BASIC 0x000007ffULL
#define P9_STATS_ALL 0x00003fffULL
This patch implements the client side of getattr implementation for 9P2000.L.
It introduces a new structure p9_stat_dotl for getting Linux stat information
along with QID. The data layout is similar to stat structure in Linux user
space with the following major differences:
inode (st_ino) is not part of data. Instead qid is.
device (st_dev) is not part of data because this doesn't make sense on the
client.
All time variables are 64 bit wide on the wire. The kernel seems to use
32 bit variables for these variables. However, some of the architectures
have used 64 bit variables and glibc exposes 64 bit variables to user
space on some architectures. Hence to be on the safer side we have made
these 64 bit in the protocol. Refer to the comments in
include/asm-generic/stat.h
There are some additional fields: st_btime_sec, st_btime_nsec, st_gen,
st_data_version apart from the bitmask, st_result_mask. The bit mask
is filled by the server to indicate which stat fields have been
populated by the server. Currently there is no clean way for the
server to obtain these additional fields, so it sends back just the
basic fields.
Signed-off-by: M. Mohan Kumar <mohan@in.ibm.com>
Signed-off-by: Sripathi Kodi <sripathik@in.ibm.com>
2010-07-20 10:14:41 +04:00
|
|
|
case 'A': {
|
|
|
|
V9fsStatDotl *statp = va_arg(ap, V9fsStatDotl *);
|
|
|
|
offset += pdu_marshal(pdu, offset, "qQdddqqqqqqqqqqqqqqq",
|
|
|
|
statp->st_result_mask,
|
|
|
|
&statp->qid, statp->st_mode,
|
|
|
|
statp->st_uid, statp->st_gid,
|
|
|
|
statp->st_nlink, statp->st_rdev,
|
|
|
|
statp->st_size, statp->st_blksize, statp->st_blocks,
|
|
|
|
statp->st_atime_sec, statp->st_atime_nsec,
|
|
|
|
statp->st_mtime_sec, statp->st_mtime_nsec,
|
|
|
|
statp->st_ctime_sec, statp->st_ctime_nsec,
|
|
|
|
statp->st_btime_sec, statp->st_btime_nsec,
|
|
|
|
statp->st_gen, statp->st_data_version);
|
|
|
|
break;
|
|
|
|
}
|
2010-04-29 16:14:45 +04:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
return offset - old_offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void complete_pdu(V9fsState *s, V9fsPDU *pdu, ssize_t len)
|
|
|
|
{
|
|
|
|
int8_t id = pdu->id + 1; /* Response */
|
|
|
|
|
|
|
|
if (len < 0) {
|
|
|
|
int err = -len;
|
2010-07-28 12:40:22 +04:00
|
|
|
len = 7;
|
2010-04-29 16:14:45 +04:00
|
|
|
|
2010-07-28 12:40:22 +04:00
|
|
|
if (s->proto_version != V9FS_PROTO_2000L) {
|
|
|
|
V9fsString str;
|
|
|
|
|
|
|
|
str.data = strerror(err);
|
|
|
|
str.size = strlen(str.data);
|
|
|
|
|
|
|
|
len += pdu_marshal(pdu, len, "s", &str);
|
|
|
|
id = P9_RERROR;
|
|
|
|
}
|
2010-04-29 16:14:45 +04:00
|
|
|
|
2010-07-28 12:25:05 +04:00
|
|
|
len += pdu_marshal(pdu, len, "d", err);
|
2010-04-29 16:14:45 +04:00
|
|
|
|
2010-07-28 12:40:22 +04:00
|
|
|
if (s->proto_version == V9FS_PROTO_2000L) {
|
|
|
|
id = P9_RLERROR;
|
|
|
|
}
|
2010-04-29 16:14:45 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* fill out the header */
|
|
|
|
pdu_marshal(pdu, 0, "dbw", (int32_t)len, id, pdu->tag);
|
|
|
|
|
|
|
|
/* keep these in sync */
|
|
|
|
pdu->size = len;
|
|
|
|
pdu->id = id;
|
|
|
|
|
|
|
|
/* push onto queue and notify */
|
|
|
|
virtqueue_push(s->vq, &pdu->elem, len);
|
|
|
|
|
|
|
|
/* FIXME: we should batch these completions */
|
|
|
|
virtio_notify(&s->vdev, s->vq);
|
|
|
|
|
|
|
|
free_pdu(s, pdu);
|
|
|
|
}
|
|
|
|
|
2010-04-29 16:14:49 +04:00
|
|
|
static mode_t v9mode_to_mode(uint32_t mode, V9fsString *extension)
|
|
|
|
{
|
|
|
|
mode_t ret;
|
|
|
|
|
|
|
|
ret = mode & 0777;
|
|
|
|
if (mode & P9_STAT_MODE_DIR) {
|
|
|
|
ret |= S_IFDIR;
|
|
|
|
}
|
|
|
|
|
2010-07-28 12:25:05 +04:00
|
|
|
if (mode & P9_STAT_MODE_SYMLINK) {
|
|
|
|
ret |= S_IFLNK;
|
|
|
|
}
|
|
|
|
if (mode & P9_STAT_MODE_SOCKET) {
|
|
|
|
ret |= S_IFSOCK;
|
|
|
|
}
|
|
|
|
if (mode & P9_STAT_MODE_NAMED_PIPE) {
|
|
|
|
ret |= S_IFIFO;
|
|
|
|
}
|
|
|
|
if (mode & P9_STAT_MODE_DEVICE) {
|
|
|
|
if (extension && extension->data[0] == 'c') {
|
|
|
|
ret |= S_IFCHR;
|
|
|
|
} else {
|
|
|
|
ret |= S_IFBLK;
|
2010-04-29 16:14:49 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(ret&~0777)) {
|
|
|
|
ret |= S_IFREG;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mode & P9_STAT_MODE_SETUID) {
|
|
|
|
ret |= S_ISUID;
|
|
|
|
}
|
|
|
|
if (mode & P9_STAT_MODE_SETGID) {
|
|
|
|
ret |= S_ISGID;
|
|
|
|
}
|
|
|
|
if (mode & P9_STAT_MODE_SETVTX) {
|
|
|
|
ret |= S_ISVTX;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int donttouch_stat(V9fsStat *stat)
|
|
|
|
{
|
|
|
|
if (stat->type == -1 &&
|
|
|
|
stat->dev == -1 &&
|
|
|
|
stat->qid.type == -1 &&
|
|
|
|
stat->qid.version == -1 &&
|
|
|
|
stat->qid.path == -1 &&
|
|
|
|
stat->mode == -1 &&
|
|
|
|
stat->atime == -1 &&
|
|
|
|
stat->mtime == -1 &&
|
|
|
|
stat->length == -1 &&
|
|
|
|
!stat->name.size &&
|
|
|
|
!stat->uid.size &&
|
|
|
|
!stat->gid.size &&
|
|
|
|
!stat->muid.size &&
|
|
|
|
stat->n_uid == -1 &&
|
|
|
|
stat->n_gid == -1 &&
|
|
|
|
stat->n_muid == -1) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void v9fs_stat_free(V9fsStat *stat)
|
|
|
|
{
|
|
|
|
v9fs_string_free(&stat->name);
|
|
|
|
v9fs_string_free(&stat->uid);
|
|
|
|
v9fs_string_free(&stat->gid);
|
|
|
|
v9fs_string_free(&stat->muid);
|
|
|
|
v9fs_string_free(&stat->extension);
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint32_t stat_to_v9mode(const struct stat *stbuf)
|
|
|
|
{
|
|
|
|
uint32_t mode;
|
|
|
|
|
|
|
|
mode = stbuf->st_mode & 0777;
|
|
|
|
if (S_ISDIR(stbuf->st_mode)) {
|
|
|
|
mode |= P9_STAT_MODE_DIR;
|
|
|
|
}
|
|
|
|
|
2010-07-28 12:25:05 +04:00
|
|
|
if (S_ISLNK(stbuf->st_mode)) {
|
|
|
|
mode |= P9_STAT_MODE_SYMLINK;
|
|
|
|
}
|
2010-04-29 16:14:49 +04:00
|
|
|
|
2010-07-28 12:25:05 +04:00
|
|
|
if (S_ISSOCK(stbuf->st_mode)) {
|
|
|
|
mode |= P9_STAT_MODE_SOCKET;
|
|
|
|
}
|
2010-04-29 16:14:49 +04:00
|
|
|
|
2010-07-28 12:25:05 +04:00
|
|
|
if (S_ISFIFO(stbuf->st_mode)) {
|
|
|
|
mode |= P9_STAT_MODE_NAMED_PIPE;
|
|
|
|
}
|
2010-04-29 16:14:49 +04:00
|
|
|
|
2010-07-28 12:25:05 +04:00
|
|
|
if (S_ISBLK(stbuf->st_mode) || S_ISCHR(stbuf->st_mode)) {
|
|
|
|
mode |= P9_STAT_MODE_DEVICE;
|
|
|
|
}
|
2010-04-29 16:14:49 +04:00
|
|
|
|
2010-07-28 12:25:05 +04:00
|
|
|
if (stbuf->st_mode & S_ISUID) {
|
|
|
|
mode |= P9_STAT_MODE_SETUID;
|
|
|
|
}
|
2010-04-29 16:14:49 +04:00
|
|
|
|
2010-07-28 12:25:05 +04:00
|
|
|
if (stbuf->st_mode & S_ISGID) {
|
|
|
|
mode |= P9_STAT_MODE_SETGID;
|
|
|
|
}
|
2010-04-29 16:14:49 +04:00
|
|
|
|
2010-07-28 12:25:05 +04:00
|
|
|
if (stbuf->st_mode & S_ISVTX) {
|
|
|
|
mode |= P9_STAT_MODE_SETVTX;
|
2010-04-29 16:14:49 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
return mode;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int stat_to_v9stat(V9fsState *s, V9fsString *name,
|
|
|
|
const struct stat *stbuf,
|
|
|
|
V9fsStat *v9stat)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
const char *str;
|
|
|
|
|
|
|
|
memset(v9stat, 0, sizeof(*v9stat));
|
|
|
|
|
|
|
|
stat_to_qid(stbuf, &v9stat->qid);
|
|
|
|
v9stat->mode = stat_to_v9mode(stbuf);
|
|
|
|
v9stat->atime = stbuf->st_atime;
|
|
|
|
v9stat->mtime = stbuf->st_mtime;
|
|
|
|
v9stat->length = stbuf->st_size;
|
|
|
|
|
|
|
|
v9fs_string_null(&v9stat->uid);
|
|
|
|
v9fs_string_null(&v9stat->gid);
|
|
|
|
v9fs_string_null(&v9stat->muid);
|
|
|
|
|
2010-07-28 12:25:05 +04:00
|
|
|
v9stat->n_uid = stbuf->st_uid;
|
|
|
|
v9stat->n_gid = stbuf->st_gid;
|
|
|
|
v9stat->n_muid = 0;
|
2010-04-29 16:14:49 +04:00
|
|
|
|
2010-07-28 12:25:05 +04:00
|
|
|
v9fs_string_null(&v9stat->extension);
|
2010-04-29 16:14:49 +04:00
|
|
|
|
2010-07-28 12:25:05 +04:00
|
|
|
if (v9stat->mode & P9_STAT_MODE_SYMLINK) {
|
2011-08-08 22:06:41 +04:00
|
|
|
err = v9fs_co_readlink(s, name, &v9stat->extension);
|
|
|
|
if (err < 0) {
|
2010-07-28 12:25:05 +04:00
|
|
|
return err;
|
2010-04-29 16:14:49 +04:00
|
|
|
}
|
2010-07-28 12:25:05 +04:00
|
|
|
} else if (v9stat->mode & P9_STAT_MODE_DEVICE) {
|
|
|
|
v9fs_string_sprintf(&v9stat->extension, "%c %u %u",
|
|
|
|
S_ISCHR(stbuf->st_mode) ? 'c' : 'b',
|
|
|
|
major(stbuf->st_rdev), minor(stbuf->st_rdev));
|
|
|
|
} else if (S_ISDIR(stbuf->st_mode) || S_ISREG(stbuf->st_mode)) {
|
2010-09-27 20:45:47 +04:00
|
|
|
v9fs_string_sprintf(&v9stat->extension, "%s %lu",
|
|
|
|
"HARDLINKCOUNT", (unsigned long)stbuf->st_nlink);
|
2010-04-29 16:14:49 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
str = strrchr(name->data, '/');
|
|
|
|
if (str) {
|
|
|
|
str += 1;
|
|
|
|
} else {
|
|
|
|
str = name->data;
|
|
|
|
}
|
|
|
|
|
|
|
|
v9fs_string_sprintf(&v9stat->name, "%s", str);
|
|
|
|
|
|
|
|
v9stat->size = 61 +
|
|
|
|
v9fs_string_size(&v9stat->name) +
|
|
|
|
v9fs_string_size(&v9stat->uid) +
|
|
|
|
v9fs_string_size(&v9stat->gid) +
|
|
|
|
v9fs_string_size(&v9stat->muid) +
|
|
|
|
v9fs_string_size(&v9stat->extension);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
virtio-9p: getattr server implementation for 9P2000.L protocol.
SYNOPSIS
size[4] Tgetattr tag[2] fid[4] request_mask[8]
size[4] Rgetattr tag[2] lstat[n]
DESCRIPTION
The getattr transaction inquires about the file identified by fid.
request_mask is a bit mask that specifies which fields of the
stat structure is the client interested in.
The reply will contain a machine-independent directory entry,
laid out as follows:
st_result_mask[8]
Bit mask that indicates which fields in the stat structure
have been populated by the server
qid.type[1]
the type of the file (directory, etc.), represented as a bit
vector corresponding to the high 8 bits of the file's mode
word.
qid.vers[4]
version number for given path
qid.path[8]
the file server's unique identification for the file
st_mode[4]
Permission and flags
st_uid[4]
User id of owner
st_gid[4]
Group ID of owner
st_nlink[8]
Number of hard links
st_rdev[8]
Device ID (if special file)
st_size[8]
Size, in bytes
st_blksize[8]
Block size for file system IO
st_blocks[8]
Number of file system blocks allocated
st_atime_sec[8]
Time of last access, seconds
st_atime_nsec[8]
Time of last access, nanoseconds
st_mtime_sec[8]
Time of last modification, seconds
st_mtime_nsec[8]
Time of last modification, nanoseconds
st_ctime_sec[8]
Time of last status change, seconds
st_ctime_nsec[8]
Time of last status change, nanoseconds
st_btime_sec[8]
Time of creation (birth) of file, seconds
st_btime_nsec[8]
Time of creation (birth) of file, nanoseconds
st_gen[8]
Inode generation
st_data_version[8]
Data version number
request_mask and result_mask bit masks contain the following bits
#define P9_STATS_MODE 0x00000001ULL
#define P9_STATS_NLINK 0x00000002ULL
#define P9_STATS_UID 0x00000004ULL
#define P9_STATS_GID 0x00000008ULL
#define P9_STATS_RDEV 0x00000010ULL
#define P9_STATS_ATIME 0x00000020ULL
#define P9_STATS_MTIME 0x00000040ULL
#define P9_STATS_CTIME 0x00000080ULL
#define P9_STATS_INO 0x00000100ULL
#define P9_STATS_SIZE 0x00000200ULL
#define P9_STATS_BLOCKS 0x00000400ULL
#define P9_STATS_BTIME 0x00000800ULL
#define P9_STATS_GEN 0x00001000ULL
#define P9_STATS_DATA_VERSION 0x00002000ULL
#define P9_STATS_BASIC 0x000007ffULL
#define P9_STATS_ALL 0x00003fffULL
This patch implements the client side of getattr implementation for 9P2000.L.
It introduces a new structure p9_stat_dotl for getting Linux stat information
along with QID. The data layout is similar to stat structure in Linux user
space with the following major differences:
inode (st_ino) is not part of data. Instead qid is.
device (st_dev) is not part of data because this doesn't make sense on the
client.
All time variables are 64 bit wide on the wire. The kernel seems to use
32 bit variables for these variables. However, some of the architectures
have used 64 bit variables and glibc exposes 64 bit variables to user
space on some architectures. Hence to be on the safer side we have made
these 64 bit in the protocol. Refer to the comments in
include/asm-generic/stat.h
There are some additional fields: st_btime_sec, st_btime_nsec, st_gen,
st_data_version apart from the bitmask, st_result_mask. The bit mask
is filled by the server to indicate which stat fields have been
populated by the server. Currently there is no clean way for the
server to obtain these additional fields, so it sends back just the
basic fields.
Signed-off-by: M. Mohan Kumar <mohan@in.ibm.com>
Signed-off-by: Sripathi Kodi <sripathik@in.ibm.com>
2010-07-20 10:14:41 +04:00
|
|
|
#define P9_STATS_MODE 0x00000001ULL
|
|
|
|
#define P9_STATS_NLINK 0x00000002ULL
|
|
|
|
#define P9_STATS_UID 0x00000004ULL
|
|
|
|
#define P9_STATS_GID 0x00000008ULL
|
|
|
|
#define P9_STATS_RDEV 0x00000010ULL
|
|
|
|
#define P9_STATS_ATIME 0x00000020ULL
|
|
|
|
#define P9_STATS_MTIME 0x00000040ULL
|
|
|
|
#define P9_STATS_CTIME 0x00000080ULL
|
|
|
|
#define P9_STATS_INO 0x00000100ULL
|
|
|
|
#define P9_STATS_SIZE 0x00000200ULL
|
|
|
|
#define P9_STATS_BLOCKS 0x00000400ULL
|
|
|
|
|
|
|
|
#define P9_STATS_BTIME 0x00000800ULL
|
|
|
|
#define P9_STATS_GEN 0x00001000ULL
|
|
|
|
#define P9_STATS_DATA_VERSION 0x00002000ULL
|
|
|
|
|
|
|
|
#define P9_STATS_BASIC 0x000007ffULL /* Mask for fields up to BLOCKS */
|
|
|
|
#define P9_STATS_ALL 0x00003fffULL /* Mask for All fields above */
|
|
|
|
|
|
|
|
|
|
|
|
static void stat_to_v9stat_dotl(V9fsState *s, const struct stat *stbuf,
|
|
|
|
V9fsStatDotl *v9lstat)
|
|
|
|
{
|
|
|
|
memset(v9lstat, 0, sizeof(*v9lstat));
|
|
|
|
|
|
|
|
v9lstat->st_mode = stbuf->st_mode;
|
|
|
|
v9lstat->st_nlink = stbuf->st_nlink;
|
|
|
|
v9lstat->st_uid = stbuf->st_uid;
|
|
|
|
v9lstat->st_gid = stbuf->st_gid;
|
|
|
|
v9lstat->st_rdev = stbuf->st_rdev;
|
|
|
|
v9lstat->st_size = stbuf->st_size;
|
|
|
|
v9lstat->st_blksize = stbuf->st_blksize;
|
|
|
|
v9lstat->st_blocks = stbuf->st_blocks;
|
|
|
|
v9lstat->st_atime_sec = stbuf->st_atime;
|
|
|
|
v9lstat->st_atime_nsec = stbuf->st_atim.tv_nsec;
|
|
|
|
v9lstat->st_mtime_sec = stbuf->st_mtime;
|
|
|
|
v9lstat->st_mtime_nsec = stbuf->st_mtim.tv_nsec;
|
|
|
|
v9lstat->st_ctime_sec = stbuf->st_ctime;
|
|
|
|
v9lstat->st_ctime_nsec = stbuf->st_ctim.tv_nsec;
|
|
|
|
/* Currently we only support BASIC fields in stat */
|
|
|
|
v9lstat->st_result_mask = P9_STATS_BASIC;
|
|
|
|
|
|
|
|
stat_to_qid(stbuf, &v9lstat->qid);
|
|
|
|
}
|
|
|
|
|
2010-04-29 16:14:50 +04:00
|
|
|
static struct iovec *adjust_sg(struct iovec *sg, int len, int *iovcnt)
|
|
|
|
{
|
|
|
|
while (len && *iovcnt) {
|
|
|
|
if (len < sg->iov_len) {
|
|
|
|
sg->iov_len -= len;
|
|
|
|
sg->iov_base += len;
|
|
|
|
len = 0;
|
|
|
|
} else {
|
|
|
|
len -= sg->iov_len;
|
|
|
|
sg++;
|
|
|
|
*iovcnt -= 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return sg;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct iovec *cap_sg(struct iovec *sg, int cap, int *cnt)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
int total = 0;
|
|
|
|
|
|
|
|
for (i = 0; i < *cnt; i++) {
|
|
|
|
if ((total + sg[i].iov_len) > cap) {
|
|
|
|
sg[i].iov_len -= ((total + sg[i].iov_len) - cap);
|
|
|
|
i++;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
total += sg[i].iov_len;
|
|
|
|
}
|
|
|
|
|
|
|
|
*cnt = i;
|
|
|
|
|
|
|
|
return sg;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void print_sg(struct iovec *sg, int cnt)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
printf("sg[%d]: {", cnt);
|
|
|
|
for (i = 0; i < cnt; i++) {
|
|
|
|
if (i) {
|
|
|
|
printf(", ");
|
|
|
|
}
|
|
|
|
printf("(%p, %zd)", sg[i].iov_base, sg[i].iov_len);
|
|
|
|
}
|
|
|
|
printf("}\n");
|
|
|
|
}
|
|
|
|
|
2010-04-29 16:15:00 +04:00
|
|
|
static void v9fs_fix_path(V9fsString *dst, V9fsString *src, int len)
|
|
|
|
{
|
|
|
|
V9fsString str;
|
|
|
|
v9fs_string_init(&str);
|
|
|
|
v9fs_string_copy(&str, dst);
|
|
|
|
v9fs_string_sprintf(dst, "%s%s", src->data, str.data+len);
|
|
|
|
v9fs_string_free(&str);
|
|
|
|
}
|
|
|
|
|
2011-05-19 01:18:05 +04:00
|
|
|
static void v9fs_version(void *opaque)
|
2010-04-29 16:14:44 +04:00
|
|
|
{
|
2011-05-19 01:18:05 +04:00
|
|
|
V9fsPDU *pdu = opaque;
|
|
|
|
V9fsState *s = pdu->s;
|
2010-04-29 16:14:51 +04:00
|
|
|
V9fsString version;
|
|
|
|
size_t offset = 7;
|
|
|
|
|
2010-06-09 17:44:28 +04:00
|
|
|
pdu_unmarshal(pdu, offset, "ds", &s->msize, &version);
|
2010-04-29 16:14:51 +04:00
|
|
|
|
2010-05-27 12:27:29 +04:00
|
|
|
if (!strcmp(version.data, "9P2000.u")) {
|
|
|
|
s->proto_version = V9FS_PROTO_2000U;
|
|
|
|
} else if (!strcmp(version.data, "9P2000.L")) {
|
|
|
|
s->proto_version = V9FS_PROTO_2000L;
|
|
|
|
} else {
|
2010-04-29 16:14:51 +04:00
|
|
|
v9fs_string_sprintf(&version, "unknown");
|
2010-04-29 16:14:44 +04:00
|
|
|
}
|
2010-04-29 16:14:51 +04:00
|
|
|
|
2010-06-09 17:44:28 +04:00
|
|
|
offset += pdu_marshal(pdu, offset, "ds", s->msize, &version);
|
2010-04-29 16:14:51 +04:00
|
|
|
complete_pdu(s, pdu, offset);
|
|
|
|
|
|
|
|
v9fs_string_free(&version);
|
2011-05-19 01:18:05 +04:00
|
|
|
return;
|
2010-04-29 16:14:44 +04:00
|
|
|
}
|
|
|
|
|
2011-05-19 01:18:05 +04:00
|
|
|
static void v9fs_attach(void *opaque)
|
2010-04-29 16:14:44 +04:00
|
|
|
{
|
2011-05-19 01:18:05 +04:00
|
|
|
V9fsPDU *pdu = opaque;
|
|
|
|
V9fsState *s = pdu->s;
|
2010-04-29 16:14:52 +04:00
|
|
|
int32_t fid, afid, n_uname;
|
|
|
|
V9fsString uname, aname;
|
|
|
|
V9fsFidState *fidp;
|
|
|
|
V9fsQID qid;
|
|
|
|
size_t offset = 7;
|
|
|
|
ssize_t err;
|
|
|
|
|
|
|
|
pdu_unmarshal(pdu, offset, "ddssd", &fid, &afid, &uname, &aname, &n_uname);
|
|
|
|
|
|
|
|
fidp = alloc_fid(s, fid);
|
|
|
|
if (fidp == NULL) {
|
|
|
|
err = -EINVAL;
|
|
|
|
goto out;
|
2010-04-29 16:14:44 +04:00
|
|
|
}
|
2010-04-29 16:14:52 +04:00
|
|
|
|
|
|
|
fidp->uid = n_uname;
|
|
|
|
|
|
|
|
v9fs_string_sprintf(&fidp->path, "%s", "/");
|
|
|
|
err = fid_to_qid(s, fidp, &qid);
|
|
|
|
if (err) {
|
|
|
|
err = -EINVAL;
|
|
|
|
free_fid(s, fid);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
offset += pdu_marshal(pdu, offset, "Q", &qid);
|
|
|
|
|
|
|
|
err = offset;
|
|
|
|
out:
|
|
|
|
complete_pdu(s, pdu, err);
|
|
|
|
v9fs_string_free(&uname);
|
|
|
|
v9fs_string_free(&aname);
|
2010-04-29 16:14:44 +04:00
|
|
|
}
|
|
|
|
|
2010-04-29 16:14:53 +04:00
|
|
|
static void v9fs_stat_post_lstat(V9fsState *s, V9fsStatState *vs, int err)
|
|
|
|
{
|
|
|
|
if (err == -1) {
|
|
|
|
err = -errno;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = stat_to_v9stat(s, &vs->fidp->path, &vs->stbuf, &vs->v9stat);
|
|
|
|
if (err) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
vs->offset += pdu_marshal(vs->pdu, vs->offset, "wS", 0, &vs->v9stat);
|
|
|
|
err = vs->offset;
|
|
|
|
|
|
|
|
out:
|
|
|
|
complete_pdu(s, vs->pdu, err);
|
|
|
|
v9fs_stat_free(&vs->v9stat);
|
|
|
|
qemu_free(vs);
|
|
|
|
}
|
|
|
|
|
2011-05-19 01:18:05 +04:00
|
|
|
static void v9fs_stat(void *opaque)
|
2010-04-29 16:14:44 +04:00
|
|
|
{
|
2011-05-19 01:18:05 +04:00
|
|
|
V9fsPDU *pdu = opaque;
|
|
|
|
V9fsState *s = pdu->s;
|
2010-04-29 16:14:53 +04:00
|
|
|
int32_t fid;
|
|
|
|
V9fsStatState *vs;
|
|
|
|
ssize_t err = 0;
|
|
|
|
|
|
|
|
vs = qemu_malloc(sizeof(*vs));
|
|
|
|
vs->pdu = pdu;
|
|
|
|
vs->offset = 7;
|
|
|
|
|
|
|
|
memset(&vs->v9stat, 0, sizeof(vs->v9stat));
|
|
|
|
|
|
|
|
pdu_unmarshal(vs->pdu, vs->offset, "d", &fid);
|
|
|
|
|
|
|
|
vs->fidp = lookup_fid(s, fid);
|
|
|
|
if (vs->fidp == NULL) {
|
|
|
|
err = -ENOENT;
|
|
|
|
goto out;
|
2010-04-29 16:14:44 +04:00
|
|
|
}
|
2010-04-29 16:14:53 +04:00
|
|
|
|
|
|
|
err = v9fs_do_lstat(s, &vs->fidp->path, &vs->stbuf);
|
|
|
|
v9fs_stat_post_lstat(s, vs, err);
|
|
|
|
return;
|
|
|
|
|
|
|
|
out:
|
|
|
|
complete_pdu(s, vs->pdu, err);
|
|
|
|
v9fs_stat_free(&vs->v9stat);
|
|
|
|
qemu_free(vs);
|
2010-04-29 16:14:44 +04:00
|
|
|
}
|
|
|
|
|
virtio-9p: getattr server implementation for 9P2000.L protocol.
SYNOPSIS
size[4] Tgetattr tag[2] fid[4] request_mask[8]
size[4] Rgetattr tag[2] lstat[n]
DESCRIPTION
The getattr transaction inquires about the file identified by fid.
request_mask is a bit mask that specifies which fields of the
stat structure is the client interested in.
The reply will contain a machine-independent directory entry,
laid out as follows:
st_result_mask[8]
Bit mask that indicates which fields in the stat structure
have been populated by the server
qid.type[1]
the type of the file (directory, etc.), represented as a bit
vector corresponding to the high 8 bits of the file's mode
word.
qid.vers[4]
version number for given path
qid.path[8]
the file server's unique identification for the file
st_mode[4]
Permission and flags
st_uid[4]
User id of owner
st_gid[4]
Group ID of owner
st_nlink[8]
Number of hard links
st_rdev[8]
Device ID (if special file)
st_size[8]
Size, in bytes
st_blksize[8]
Block size for file system IO
st_blocks[8]
Number of file system blocks allocated
st_atime_sec[8]
Time of last access, seconds
st_atime_nsec[8]
Time of last access, nanoseconds
st_mtime_sec[8]
Time of last modification, seconds
st_mtime_nsec[8]
Time of last modification, nanoseconds
st_ctime_sec[8]
Time of last status change, seconds
st_ctime_nsec[8]
Time of last status change, nanoseconds
st_btime_sec[8]
Time of creation (birth) of file, seconds
st_btime_nsec[8]
Time of creation (birth) of file, nanoseconds
st_gen[8]
Inode generation
st_data_version[8]
Data version number
request_mask and result_mask bit masks contain the following bits
#define P9_STATS_MODE 0x00000001ULL
#define P9_STATS_NLINK 0x00000002ULL
#define P9_STATS_UID 0x00000004ULL
#define P9_STATS_GID 0x00000008ULL
#define P9_STATS_RDEV 0x00000010ULL
#define P9_STATS_ATIME 0x00000020ULL
#define P9_STATS_MTIME 0x00000040ULL
#define P9_STATS_CTIME 0x00000080ULL
#define P9_STATS_INO 0x00000100ULL
#define P9_STATS_SIZE 0x00000200ULL
#define P9_STATS_BLOCKS 0x00000400ULL
#define P9_STATS_BTIME 0x00000800ULL
#define P9_STATS_GEN 0x00001000ULL
#define P9_STATS_DATA_VERSION 0x00002000ULL
#define P9_STATS_BASIC 0x000007ffULL
#define P9_STATS_ALL 0x00003fffULL
This patch implements the client side of getattr implementation for 9P2000.L.
It introduces a new structure p9_stat_dotl for getting Linux stat information
along with QID. The data layout is similar to stat structure in Linux user
space with the following major differences:
inode (st_ino) is not part of data. Instead qid is.
device (st_dev) is not part of data because this doesn't make sense on the
client.
All time variables are 64 bit wide on the wire. The kernel seems to use
32 bit variables for these variables. However, some of the architectures
have used 64 bit variables and glibc exposes 64 bit variables to user
space on some architectures. Hence to be on the safer side we have made
these 64 bit in the protocol. Refer to the comments in
include/asm-generic/stat.h
There are some additional fields: st_btime_sec, st_btime_nsec, st_gen,
st_data_version apart from the bitmask, st_result_mask. The bit mask
is filled by the server to indicate which stat fields have been
populated by the server. Currently there is no clean way for the
server to obtain these additional fields, so it sends back just the
basic fields.
Signed-off-by: M. Mohan Kumar <mohan@in.ibm.com>
Signed-off-by: Sripathi Kodi <sripathik@in.ibm.com>
2010-07-20 10:14:41 +04:00
|
|
|
static void v9fs_getattr_post_lstat(V9fsState *s, V9fsStatStateDotl *vs,
|
|
|
|
int err)
|
|
|
|
{
|
|
|
|
if (err == -1) {
|
|
|
|
err = -errno;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
stat_to_v9stat_dotl(s, &vs->stbuf, &vs->v9stat_dotl);
|
|
|
|
vs->offset += pdu_marshal(vs->pdu, vs->offset, "A", &vs->v9stat_dotl);
|
|
|
|
err = vs->offset;
|
|
|
|
|
|
|
|
out:
|
|
|
|
complete_pdu(s, vs->pdu, err);
|
|
|
|
qemu_free(vs);
|
|
|
|
}
|
|
|
|
|
2011-05-19 01:18:05 +04:00
|
|
|
static void v9fs_getattr(void *opaque)
|
virtio-9p: getattr server implementation for 9P2000.L protocol.
SYNOPSIS
size[4] Tgetattr tag[2] fid[4] request_mask[8]
size[4] Rgetattr tag[2] lstat[n]
DESCRIPTION
The getattr transaction inquires about the file identified by fid.
request_mask is a bit mask that specifies which fields of the
stat structure is the client interested in.
The reply will contain a machine-independent directory entry,
laid out as follows:
st_result_mask[8]
Bit mask that indicates which fields in the stat structure
have been populated by the server
qid.type[1]
the type of the file (directory, etc.), represented as a bit
vector corresponding to the high 8 bits of the file's mode
word.
qid.vers[4]
version number for given path
qid.path[8]
the file server's unique identification for the file
st_mode[4]
Permission and flags
st_uid[4]
User id of owner
st_gid[4]
Group ID of owner
st_nlink[8]
Number of hard links
st_rdev[8]
Device ID (if special file)
st_size[8]
Size, in bytes
st_blksize[8]
Block size for file system IO
st_blocks[8]
Number of file system blocks allocated
st_atime_sec[8]
Time of last access, seconds
st_atime_nsec[8]
Time of last access, nanoseconds
st_mtime_sec[8]
Time of last modification, seconds
st_mtime_nsec[8]
Time of last modification, nanoseconds
st_ctime_sec[8]
Time of last status change, seconds
st_ctime_nsec[8]
Time of last status change, nanoseconds
st_btime_sec[8]
Time of creation (birth) of file, seconds
st_btime_nsec[8]
Time of creation (birth) of file, nanoseconds
st_gen[8]
Inode generation
st_data_version[8]
Data version number
request_mask and result_mask bit masks contain the following bits
#define P9_STATS_MODE 0x00000001ULL
#define P9_STATS_NLINK 0x00000002ULL
#define P9_STATS_UID 0x00000004ULL
#define P9_STATS_GID 0x00000008ULL
#define P9_STATS_RDEV 0x00000010ULL
#define P9_STATS_ATIME 0x00000020ULL
#define P9_STATS_MTIME 0x00000040ULL
#define P9_STATS_CTIME 0x00000080ULL
#define P9_STATS_INO 0x00000100ULL
#define P9_STATS_SIZE 0x00000200ULL
#define P9_STATS_BLOCKS 0x00000400ULL
#define P9_STATS_BTIME 0x00000800ULL
#define P9_STATS_GEN 0x00001000ULL
#define P9_STATS_DATA_VERSION 0x00002000ULL
#define P9_STATS_BASIC 0x000007ffULL
#define P9_STATS_ALL 0x00003fffULL
This patch implements the client side of getattr implementation for 9P2000.L.
It introduces a new structure p9_stat_dotl for getting Linux stat information
along with QID. The data layout is similar to stat structure in Linux user
space with the following major differences:
inode (st_ino) is not part of data. Instead qid is.
device (st_dev) is not part of data because this doesn't make sense on the
client.
All time variables are 64 bit wide on the wire. The kernel seems to use
32 bit variables for these variables. However, some of the architectures
have used 64 bit variables and glibc exposes 64 bit variables to user
space on some architectures. Hence to be on the safer side we have made
these 64 bit in the protocol. Refer to the comments in
include/asm-generic/stat.h
There are some additional fields: st_btime_sec, st_btime_nsec, st_gen,
st_data_version apart from the bitmask, st_result_mask. The bit mask
is filled by the server to indicate which stat fields have been
populated by the server. Currently there is no clean way for the
server to obtain these additional fields, so it sends back just the
basic fields.
Signed-off-by: M. Mohan Kumar <mohan@in.ibm.com>
Signed-off-by: Sripathi Kodi <sripathik@in.ibm.com>
2010-07-20 10:14:41 +04:00
|
|
|
{
|
2011-05-19 01:18:05 +04:00
|
|
|
V9fsPDU *pdu = opaque;
|
|
|
|
V9fsState *s = pdu->s;
|
virtio-9p: getattr server implementation for 9P2000.L protocol.
SYNOPSIS
size[4] Tgetattr tag[2] fid[4] request_mask[8]
size[4] Rgetattr tag[2] lstat[n]
DESCRIPTION
The getattr transaction inquires about the file identified by fid.
request_mask is a bit mask that specifies which fields of the
stat structure is the client interested in.
The reply will contain a machine-independent directory entry,
laid out as follows:
st_result_mask[8]
Bit mask that indicates which fields in the stat structure
have been populated by the server
qid.type[1]
the type of the file (directory, etc.), represented as a bit
vector corresponding to the high 8 bits of the file's mode
word.
qid.vers[4]
version number for given path
qid.path[8]
the file server's unique identification for the file
st_mode[4]
Permission and flags
st_uid[4]
User id of owner
st_gid[4]
Group ID of owner
st_nlink[8]
Number of hard links
st_rdev[8]
Device ID (if special file)
st_size[8]
Size, in bytes
st_blksize[8]
Block size for file system IO
st_blocks[8]
Number of file system blocks allocated
st_atime_sec[8]
Time of last access, seconds
st_atime_nsec[8]
Time of last access, nanoseconds
st_mtime_sec[8]
Time of last modification, seconds
st_mtime_nsec[8]
Time of last modification, nanoseconds
st_ctime_sec[8]
Time of last status change, seconds
st_ctime_nsec[8]
Time of last status change, nanoseconds
st_btime_sec[8]
Time of creation (birth) of file, seconds
st_btime_nsec[8]
Time of creation (birth) of file, nanoseconds
st_gen[8]
Inode generation
st_data_version[8]
Data version number
request_mask and result_mask bit masks contain the following bits
#define P9_STATS_MODE 0x00000001ULL
#define P9_STATS_NLINK 0x00000002ULL
#define P9_STATS_UID 0x00000004ULL
#define P9_STATS_GID 0x00000008ULL
#define P9_STATS_RDEV 0x00000010ULL
#define P9_STATS_ATIME 0x00000020ULL
#define P9_STATS_MTIME 0x00000040ULL
#define P9_STATS_CTIME 0x00000080ULL
#define P9_STATS_INO 0x00000100ULL
#define P9_STATS_SIZE 0x00000200ULL
#define P9_STATS_BLOCKS 0x00000400ULL
#define P9_STATS_BTIME 0x00000800ULL
#define P9_STATS_GEN 0x00001000ULL
#define P9_STATS_DATA_VERSION 0x00002000ULL
#define P9_STATS_BASIC 0x000007ffULL
#define P9_STATS_ALL 0x00003fffULL
This patch implements the client side of getattr implementation for 9P2000.L.
It introduces a new structure p9_stat_dotl for getting Linux stat information
along with QID. The data layout is similar to stat structure in Linux user
space with the following major differences:
inode (st_ino) is not part of data. Instead qid is.
device (st_dev) is not part of data because this doesn't make sense on the
client.
All time variables are 64 bit wide on the wire. The kernel seems to use
32 bit variables for these variables. However, some of the architectures
have used 64 bit variables and glibc exposes 64 bit variables to user
space on some architectures. Hence to be on the safer side we have made
these 64 bit in the protocol. Refer to the comments in
include/asm-generic/stat.h
There are some additional fields: st_btime_sec, st_btime_nsec, st_gen,
st_data_version apart from the bitmask, st_result_mask. The bit mask
is filled by the server to indicate which stat fields have been
populated by the server. Currently there is no clean way for the
server to obtain these additional fields, so it sends back just the
basic fields.
Signed-off-by: M. Mohan Kumar <mohan@in.ibm.com>
Signed-off-by: Sripathi Kodi <sripathik@in.ibm.com>
2010-07-20 10:14:41 +04:00
|
|
|
int32_t fid;
|
|
|
|
V9fsStatStateDotl *vs;
|
|
|
|
ssize_t err = 0;
|
|
|
|
V9fsFidState *fidp;
|
|
|
|
uint64_t request_mask;
|
|
|
|
|
|
|
|
vs = qemu_malloc(sizeof(*vs));
|
|
|
|
vs->pdu = pdu;
|
|
|
|
vs->offset = 7;
|
|
|
|
|
|
|
|
memset(&vs->v9stat_dotl, 0, sizeof(vs->v9stat_dotl));
|
|
|
|
|
|
|
|
pdu_unmarshal(vs->pdu, vs->offset, "dq", &fid, &request_mask);
|
|
|
|
|
|
|
|
fidp = lookup_fid(s, fid);
|
|
|
|
if (fidp == NULL) {
|
|
|
|
err = -ENOENT;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Currently we only support BASIC fields in stat, so there is no
|
|
|
|
* need to look at request_mask.
|
|
|
|
*/
|
|
|
|
err = v9fs_do_lstat(s, &fidp->path, &vs->stbuf);
|
|
|
|
v9fs_getattr_post_lstat(s, vs, err);
|
|
|
|
return;
|
|
|
|
|
|
|
|
out:
|
|
|
|
complete_pdu(s, vs->pdu, err);
|
|
|
|
qemu_free(vs);
|
|
|
|
}
|
|
|
|
|
virtio-9p: Implement server side of setattr for 9P2000.L protocol.
SYNOPSIS
size[4] Tsetattr tag[2] attr[n]
size[4] Rsetattr tag[2]
DESCRIPTION
The setattr command changes some of the file status information.
attr resembles the iattr structure used in Linux kernel. It
specifies which status parameter is to be changed and to what
value. It is laid out as follows:
valid[4]
specifies which status information is to be changed. Possible
values are:
ATTR_MODE (1 << 0)
ATTR_UID (1 << 1)
ATTR_GID (1 << 2)
ATTR_SIZE (1 << 3)
ATTR_ATIME (1 << 4)
ATTR_MTIME (1 << 5)
ATTR_CTIME (1 << 5)
ATTR_ATIME_SET (1 << 7)
ATTR_MTIME_SET (1 << 8)
The last two bits represent whether the time information
is being sent by the client's user space. In the absense
of these bits the server always uses server's time.
mode[4]
File permission bits
uid[4]
Owner id of file
gid[4]
Group id of the file
size[8]
File size
atime_sec[8]
Time of last file access, seconds
atime_nsec[8]
Time of last file access, nanoseconds
mtime_sec[8]
Time of last file modification, seconds
mtime_nsec[8]
Time of last file modification, nanoseconds
Explanation of the patches:
--------------------------
*) The kernel just copies relevent contents of iattr structure to p9_iattr_dotl
structure and passes it down to the client. The only check it has is calling
inode_change_ok()
*) The p9_iattr_dotl structure does not have ctime and ia_file parameters because
I don't think these are needed in our case. The client user space can request
updating just ctime by calling chown(fd, -1, -1). This is handled on server
side without a need for putting ctime on the wire.
*) The server currently supports changing mode, time, ownership and size of the
file.
*) 9P RFC says "Either all the changes in wstat request happen, or none of them
does: if the request succeeds, all changes were made; if it fails, none were."
I have not done anything to implement this specifically because I don't see
a reason.
[jvrao@linux.vnet.ibm.com: Parts of code for handling chown(-1,-1)
Signed-off-by: Sripathi Kodi <sripathik@in.ibm.com>
Signed-off-by: Venkateswararao Jujjuri <jvrao@linux.vnet.ibm.com>
2010-06-17 16:48:47 +04:00
|
|
|
/* From Linux kernel code */
|
|
|
|
#define ATTR_MODE (1 << 0)
|
|
|
|
#define ATTR_UID (1 << 1)
|
|
|
|
#define ATTR_GID (1 << 2)
|
|
|
|
#define ATTR_SIZE (1 << 3)
|
|
|
|
#define ATTR_ATIME (1 << 4)
|
|
|
|
#define ATTR_MTIME (1 << 5)
|
|
|
|
#define ATTR_CTIME (1 << 6)
|
|
|
|
#define ATTR_MASK 127
|
|
|
|
#define ATTR_ATIME_SET (1 << 7)
|
|
|
|
#define ATTR_MTIME_SET (1 << 8)
|
|
|
|
|
|
|
|
static void v9fs_setattr_post_truncate(V9fsState *s, V9fsSetattrState *vs,
|
|
|
|
int err)
|
|
|
|
{
|
|
|
|
if (err == -1) {
|
|
|
|
err = -errno;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
err = vs->offset;
|
|
|
|
|
|
|
|
out:
|
|
|
|
complete_pdu(s, vs->pdu, err);
|
|
|
|
qemu_free(vs);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void v9fs_setattr_post_chown(V9fsState *s, V9fsSetattrState *vs, int err)
|
|
|
|
{
|
|
|
|
if (err == -1) {
|
|
|
|
err = -errno;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (vs->v9iattr.valid & (ATTR_SIZE)) {
|
|
|
|
err = v9fs_do_truncate(s, &vs->fidp->path, vs->v9iattr.size);
|
|
|
|
}
|
|
|
|
v9fs_setattr_post_truncate(s, vs, err);
|
|
|
|
return;
|
|
|
|
|
|
|
|
out:
|
|
|
|
complete_pdu(s, vs->pdu, err);
|
|
|
|
qemu_free(vs);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void v9fs_setattr_post_utimensat(V9fsState *s, V9fsSetattrState *vs,
|
|
|
|
int err)
|
|
|
|
{
|
|
|
|
if (err == -1) {
|
|
|
|
err = -errno;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If the only valid entry in iattr is ctime we can call
|
|
|
|
* chown(-1,-1) to update the ctime of the file
|
|
|
|
*/
|
|
|
|
if ((vs->v9iattr.valid & (ATTR_UID | ATTR_GID)) ||
|
|
|
|
((vs->v9iattr.valid & ATTR_CTIME)
|
|
|
|
&& !((vs->v9iattr.valid & ATTR_MASK) & ~ATTR_CTIME))) {
|
|
|
|
if (!(vs->v9iattr.valid & ATTR_UID)) {
|
|
|
|
vs->v9iattr.uid = -1;
|
|
|
|
}
|
|
|
|
if (!(vs->v9iattr.valid & ATTR_GID)) {
|
|
|
|
vs->v9iattr.gid = -1;
|
|
|
|
}
|
|
|
|
err = v9fs_do_chown(s, &vs->fidp->path, vs->v9iattr.uid,
|
|
|
|
vs->v9iattr.gid);
|
|
|
|
}
|
|
|
|
v9fs_setattr_post_chown(s, vs, err);
|
|
|
|
return;
|
|
|
|
|
|
|
|
out:
|
|
|
|
complete_pdu(s, vs->pdu, err);
|
|
|
|
qemu_free(vs);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void v9fs_setattr_post_chmod(V9fsState *s, V9fsSetattrState *vs, int err)
|
|
|
|
{
|
|
|
|
if (err == -1) {
|
|
|
|
err = -errno;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (vs->v9iattr.valid & (ATTR_ATIME | ATTR_MTIME)) {
|
|
|
|
struct timespec times[2];
|
|
|
|
if (vs->v9iattr.valid & ATTR_ATIME) {
|
|
|
|
if (vs->v9iattr.valid & ATTR_ATIME_SET) {
|
|
|
|
times[0].tv_sec = vs->v9iattr.atime_sec;
|
|
|
|
times[0].tv_nsec = vs->v9iattr.atime_nsec;
|
|
|
|
} else {
|
|
|
|
times[0].tv_nsec = UTIME_NOW;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
times[0].tv_nsec = UTIME_OMIT;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (vs->v9iattr.valid & ATTR_MTIME) {
|
|
|
|
if (vs->v9iattr.valid & ATTR_MTIME_SET) {
|
|
|
|
times[1].tv_sec = vs->v9iattr.mtime_sec;
|
|
|
|
times[1].tv_nsec = vs->v9iattr.mtime_nsec;
|
|
|
|
} else {
|
|
|
|
times[1].tv_nsec = UTIME_NOW;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
times[1].tv_nsec = UTIME_OMIT;
|
|
|
|
}
|
|
|
|
err = v9fs_do_utimensat(s, &vs->fidp->path, times);
|
|
|
|
}
|
|
|
|
v9fs_setattr_post_utimensat(s, vs, err);
|
|
|
|
return;
|
|
|
|
|
|
|
|
out:
|
|
|
|
complete_pdu(s, vs->pdu, err);
|
|
|
|
qemu_free(vs);
|
|
|
|
}
|
|
|
|
|
2011-05-19 01:18:05 +04:00
|
|
|
static void v9fs_setattr(void *opaque)
|
virtio-9p: Implement server side of setattr for 9P2000.L protocol.
SYNOPSIS
size[4] Tsetattr tag[2] attr[n]
size[4] Rsetattr tag[2]
DESCRIPTION
The setattr command changes some of the file status information.
attr resembles the iattr structure used in Linux kernel. It
specifies which status parameter is to be changed and to what
value. It is laid out as follows:
valid[4]
specifies which status information is to be changed. Possible
values are:
ATTR_MODE (1 << 0)
ATTR_UID (1 << 1)
ATTR_GID (1 << 2)
ATTR_SIZE (1 << 3)
ATTR_ATIME (1 << 4)
ATTR_MTIME (1 << 5)
ATTR_CTIME (1 << 5)
ATTR_ATIME_SET (1 << 7)
ATTR_MTIME_SET (1 << 8)
The last two bits represent whether the time information
is being sent by the client's user space. In the absense
of these bits the server always uses server's time.
mode[4]
File permission bits
uid[4]
Owner id of file
gid[4]
Group id of the file
size[8]
File size
atime_sec[8]
Time of last file access, seconds
atime_nsec[8]
Time of last file access, nanoseconds
mtime_sec[8]
Time of last file modification, seconds
mtime_nsec[8]
Time of last file modification, nanoseconds
Explanation of the patches:
--------------------------
*) The kernel just copies relevent contents of iattr structure to p9_iattr_dotl
structure and passes it down to the client. The only check it has is calling
inode_change_ok()
*) The p9_iattr_dotl structure does not have ctime and ia_file parameters because
I don't think these are needed in our case. The client user space can request
updating just ctime by calling chown(fd, -1, -1). This is handled on server
side without a need for putting ctime on the wire.
*) The server currently supports changing mode, time, ownership and size of the
file.
*) 9P RFC says "Either all the changes in wstat request happen, or none of them
does: if the request succeeds, all changes were made; if it fails, none were."
I have not done anything to implement this specifically because I don't see
a reason.
[jvrao@linux.vnet.ibm.com: Parts of code for handling chown(-1,-1)
Signed-off-by: Sripathi Kodi <sripathik@in.ibm.com>
Signed-off-by: Venkateswararao Jujjuri <jvrao@linux.vnet.ibm.com>
2010-06-17 16:48:47 +04:00
|
|
|
{
|
2011-05-19 01:18:05 +04:00
|
|
|
V9fsPDU *pdu = opaque;
|
|
|
|
V9fsState *s = pdu->s;
|
virtio-9p: Implement server side of setattr for 9P2000.L protocol.
SYNOPSIS
size[4] Tsetattr tag[2] attr[n]
size[4] Rsetattr tag[2]
DESCRIPTION
The setattr command changes some of the file status information.
attr resembles the iattr structure used in Linux kernel. It
specifies which status parameter is to be changed and to what
value. It is laid out as follows:
valid[4]
specifies which status information is to be changed. Possible
values are:
ATTR_MODE (1 << 0)
ATTR_UID (1 << 1)
ATTR_GID (1 << 2)
ATTR_SIZE (1 << 3)
ATTR_ATIME (1 << 4)
ATTR_MTIME (1 << 5)
ATTR_CTIME (1 << 5)
ATTR_ATIME_SET (1 << 7)
ATTR_MTIME_SET (1 << 8)
The last two bits represent whether the time information
is being sent by the client's user space. In the absense
of these bits the server always uses server's time.
mode[4]
File permission bits
uid[4]
Owner id of file
gid[4]
Group id of the file
size[8]
File size
atime_sec[8]
Time of last file access, seconds
atime_nsec[8]
Time of last file access, nanoseconds
mtime_sec[8]
Time of last file modification, seconds
mtime_nsec[8]
Time of last file modification, nanoseconds
Explanation of the patches:
--------------------------
*) The kernel just copies relevent contents of iattr structure to p9_iattr_dotl
structure and passes it down to the client. The only check it has is calling
inode_change_ok()
*) The p9_iattr_dotl structure does not have ctime and ia_file parameters because
I don't think these are needed in our case. The client user space can request
updating just ctime by calling chown(fd, -1, -1). This is handled on server
side without a need for putting ctime on the wire.
*) The server currently supports changing mode, time, ownership and size of the
file.
*) 9P RFC says "Either all the changes in wstat request happen, or none of them
does: if the request succeeds, all changes were made; if it fails, none were."
I have not done anything to implement this specifically because I don't see
a reason.
[jvrao@linux.vnet.ibm.com: Parts of code for handling chown(-1,-1)
Signed-off-by: Sripathi Kodi <sripathik@in.ibm.com>
Signed-off-by: Venkateswararao Jujjuri <jvrao@linux.vnet.ibm.com>
2010-06-17 16:48:47 +04:00
|
|
|
int32_t fid;
|
|
|
|
V9fsSetattrState *vs;
|
|
|
|
int err = 0;
|
|
|
|
|
|
|
|
vs = qemu_malloc(sizeof(*vs));
|
|
|
|
vs->pdu = pdu;
|
|
|
|
vs->offset = 7;
|
|
|
|
|
|
|
|
pdu_unmarshal(pdu, vs->offset, "dI", &fid, &vs->v9iattr);
|
|
|
|
|
|
|
|
vs->fidp = lookup_fid(s, fid);
|
|
|
|
if (vs->fidp == NULL) {
|
|
|
|
err = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (vs->v9iattr.valid & ATTR_MODE) {
|
|
|
|
err = v9fs_do_chmod(s, &vs->fidp->path, vs->v9iattr.mode);
|
|
|
|
}
|
|
|
|
|
|
|
|
v9fs_setattr_post_chmod(s, vs, err);
|
|
|
|
return;
|
|
|
|
|
|
|
|
out:
|
|
|
|
complete_pdu(s, vs->pdu, err);
|
|
|
|
qemu_free(vs);
|
|
|
|
}
|
|
|
|
|
2010-04-29 16:14:54 +04:00
|
|
|
static void v9fs_walk_complete(V9fsState *s, V9fsWalkState *vs, int err)
|
|
|
|
{
|
|
|
|
complete_pdu(s, vs->pdu, err);
|
|
|
|
|
2011-04-14 13:24:40 +04:00
|
|
|
if (vs->nwnames && vs->nwnames <= P9_MAXWELEM) {
|
2010-04-29 16:14:54 +04:00
|
|
|
for (vs->name_idx = 0; vs->name_idx < vs->nwnames; vs->name_idx++) {
|
|
|
|
v9fs_string_free(&vs->wnames[vs->name_idx]);
|
|
|
|
}
|
|
|
|
|
|
|
|
qemu_free(vs->wnames);
|
|
|
|
qemu_free(vs->qids);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void v9fs_walk_marshal(V9fsWalkState *vs)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
vs->offset = 7;
|
|
|
|
vs->offset += pdu_marshal(vs->pdu, vs->offset, "w", vs->nwnames);
|
|
|
|
|
|
|
|
for (i = 0; i < vs->nwnames; i++) {
|
|
|
|
vs->offset += pdu_marshal(vs->pdu, vs->offset, "Q", &vs->qids[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void v9fs_walk_post_newfid_lstat(V9fsState *s, V9fsWalkState *vs,
|
|
|
|
int err)
|
|
|
|
{
|
|
|
|
if (err == -1) {
|
|
|
|
free_fid(s, vs->newfidp->fid);
|
|
|
|
v9fs_string_free(&vs->path);
|
|
|
|
err = -ENOENT;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
stat_to_qid(&vs->stbuf, &vs->qids[vs->name_idx]);
|
|
|
|
|
|
|
|
vs->name_idx++;
|
|
|
|
if (vs->name_idx < vs->nwnames) {
|
|
|
|
v9fs_string_sprintf(&vs->path, "%s/%s", vs->newfidp->path.data,
|
|
|
|
vs->wnames[vs->name_idx].data);
|
|
|
|
v9fs_string_copy(&vs->newfidp->path, &vs->path);
|
|
|
|
|
|
|
|
err = v9fs_do_lstat(s, &vs->newfidp->path, &vs->stbuf);
|
|
|
|
v9fs_walk_post_newfid_lstat(s, vs, err);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
v9fs_string_free(&vs->path);
|
|
|
|
v9fs_walk_marshal(vs);
|
|
|
|
err = vs->offset;
|
|
|
|
out:
|
|
|
|
v9fs_walk_complete(s, vs, err);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void v9fs_walk_post_oldfid_lstat(V9fsState *s, V9fsWalkState *vs,
|
|
|
|
int err)
|
|
|
|
{
|
|
|
|
if (err == -1) {
|
|
|
|
v9fs_string_free(&vs->path);
|
|
|
|
err = -ENOENT;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
stat_to_qid(&vs->stbuf, &vs->qids[vs->name_idx]);
|
|
|
|
vs->name_idx++;
|
|
|
|
if (vs->name_idx < vs->nwnames) {
|
|
|
|
|
|
|
|
v9fs_string_sprintf(&vs->path, "%s/%s",
|
|
|
|
vs->fidp->path.data, vs->wnames[vs->name_idx].data);
|
|
|
|
v9fs_string_copy(&vs->fidp->path, &vs->path);
|
|
|
|
|
|
|
|
err = v9fs_do_lstat(s, &vs->fidp->path, &vs->stbuf);
|
|
|
|
v9fs_walk_post_oldfid_lstat(s, vs, err);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
v9fs_string_free(&vs->path);
|
|
|
|
v9fs_walk_marshal(vs);
|
|
|
|
err = vs->offset;
|
|
|
|
out:
|
|
|
|
v9fs_walk_complete(s, vs, err);
|
|
|
|
}
|
|
|
|
|
2011-05-19 01:18:05 +04:00
|
|
|
static void v9fs_walk(void *opaque)
|
2010-04-29 16:14:44 +04:00
|
|
|
{
|
2011-05-19 01:18:05 +04:00
|
|
|
V9fsPDU *pdu = opaque;
|
|
|
|
V9fsState *s = pdu->s;
|
2010-04-29 16:14:54 +04:00
|
|
|
int32_t fid, newfid;
|
|
|
|
V9fsWalkState *vs;
|
|
|
|
int err = 0;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
vs = qemu_malloc(sizeof(*vs));
|
|
|
|
vs->pdu = pdu;
|
|
|
|
vs->wnames = NULL;
|
|
|
|
vs->qids = NULL;
|
|
|
|
vs->offset = 7;
|
|
|
|
|
|
|
|
vs->offset += pdu_unmarshal(vs->pdu, vs->offset, "ddw", &fid,
|
|
|
|
&newfid, &vs->nwnames);
|
|
|
|
|
2011-04-14 13:24:40 +04:00
|
|
|
if (vs->nwnames && vs->nwnames <= P9_MAXWELEM) {
|
2010-04-29 16:14:54 +04:00
|
|
|
vs->wnames = qemu_mallocz(sizeof(vs->wnames[0]) * vs->nwnames);
|
|
|
|
|
|
|
|
vs->qids = qemu_mallocz(sizeof(vs->qids[0]) * vs->nwnames);
|
|
|
|
|
|
|
|
for (i = 0; i < vs->nwnames; i++) {
|
|
|
|
vs->offset += pdu_unmarshal(vs->pdu, vs->offset, "s",
|
|
|
|
&vs->wnames[i]);
|
|
|
|
}
|
2011-04-14 13:24:40 +04:00
|
|
|
} else if (vs->nwnames > P9_MAXWELEM) {
|
|
|
|
err = -EINVAL;
|
|
|
|
goto out;
|
2010-04-29 16:14:54 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
vs->fidp = lookup_fid(s, fid);
|
|
|
|
if (vs->fidp == NULL) {
|
|
|
|
err = -ENOENT;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* FIXME: is this really valid? */
|
|
|
|
if (fid == newfid) {
|
|
|
|
|
2010-09-02 09:39:06 +04:00
|
|
|
BUG_ON(vs->fidp->fid_type != P9_FID_NONE);
|
2010-04-29 16:14:54 +04:00
|
|
|
v9fs_string_init(&vs->path);
|
|
|
|
vs->name_idx = 0;
|
|
|
|
|
|
|
|
if (vs->name_idx < vs->nwnames) {
|
|
|
|
v9fs_string_sprintf(&vs->path, "%s/%s",
|
|
|
|
vs->fidp->path.data, vs->wnames[vs->name_idx].data);
|
|
|
|
v9fs_string_copy(&vs->fidp->path, &vs->path);
|
|
|
|
|
|
|
|
err = v9fs_do_lstat(s, &vs->fidp->path, &vs->stbuf);
|
|
|
|
v9fs_walk_post_oldfid_lstat(s, vs, err);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
vs->newfidp = alloc_fid(s, newfid);
|
|
|
|
if (vs->newfidp == NULL) {
|
|
|
|
err = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
vs->newfidp->uid = vs->fidp->uid;
|
|
|
|
v9fs_string_init(&vs->path);
|
|
|
|
vs->name_idx = 0;
|
|
|
|
v9fs_string_copy(&vs->newfidp->path, &vs->fidp->path);
|
|
|
|
|
|
|
|
if (vs->name_idx < vs->nwnames) {
|
|
|
|
v9fs_string_sprintf(&vs->path, "%s/%s", vs->newfidp->path.data,
|
|
|
|
vs->wnames[vs->name_idx].data);
|
|
|
|
v9fs_string_copy(&vs->newfidp->path, &vs->path);
|
|
|
|
|
|
|
|
err = v9fs_do_lstat(s, &vs->newfidp->path, &vs->stbuf);
|
|
|
|
v9fs_walk_post_newfid_lstat(s, vs, err);
|
|
|
|
return;
|
|
|
|
}
|
2010-04-29 16:14:44 +04:00
|
|
|
}
|
2010-04-29 16:14:54 +04:00
|
|
|
|
|
|
|
v9fs_walk_marshal(vs);
|
|
|
|
err = vs->offset;
|
|
|
|
out:
|
|
|
|
v9fs_walk_complete(s, vs, err);
|
2010-04-29 16:14:44 +04:00
|
|
|
}
|
|
|
|
|
2010-06-09 17:44:28 +04:00
|
|
|
static int32_t get_iounit(V9fsState *s, V9fsString *name)
|
|
|
|
{
|
|
|
|
struct statfs stbuf;
|
|
|
|
int32_t iounit = 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* iounit should be multiples of f_bsize (host filesystem block size
|
|
|
|
* and as well as less than (client msize - P9_IOHDRSZ))
|
|
|
|
*/
|
|
|
|
if (!v9fs_do_statfs(s, name, &stbuf)) {
|
|
|
|
iounit = stbuf.f_bsize;
|
|
|
|
iounit *= (s->msize - P9_IOHDRSZ)/stbuf.f_bsize;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!iounit) {
|
|
|
|
iounit = s->msize - P9_IOHDRSZ;
|
|
|
|
}
|
|
|
|
return iounit;
|
|
|
|
}
|
|
|
|
|
2010-04-29 16:14:55 +04:00
|
|
|
static void v9fs_open_post_opendir(V9fsState *s, V9fsOpenState *vs, int err)
|
|
|
|
{
|
2010-09-02 09:39:06 +04:00
|
|
|
if (vs->fidp->fs.dir == NULL) {
|
2010-04-29 16:14:55 +04:00
|
|
|
err = -errno;
|
|
|
|
goto out;
|
|
|
|
}
|
2010-09-02 09:39:06 +04:00
|
|
|
vs->fidp->fid_type = P9_FID_DIR;
|
2010-04-29 16:14:55 +04:00
|
|
|
vs->offset += pdu_marshal(vs->pdu, vs->offset, "Qd", &vs->qid, 0);
|
|
|
|
err = vs->offset;
|
|
|
|
out:
|
|
|
|
complete_pdu(s, vs->pdu, err);
|
|
|
|
qemu_free(vs);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2010-06-09 17:44:28 +04:00
|
|
|
static void v9fs_open_post_getiounit(V9fsState *s, V9fsOpenState *vs)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
vs->offset += pdu_marshal(vs->pdu, vs->offset, "Qd", &vs->qid, vs->iounit);
|
|
|
|
err = vs->offset;
|
|
|
|
complete_pdu(s, vs->pdu, err);
|
|
|
|
qemu_free(vs);
|
|
|
|
}
|
|
|
|
|
2010-04-29 16:14:55 +04:00
|
|
|
static void v9fs_open_post_open(V9fsState *s, V9fsOpenState *vs, int err)
|
|
|
|
{
|
2010-09-02 09:39:06 +04:00
|
|
|
if (vs->fidp->fs.fd == -1) {
|
2010-04-29 16:14:55 +04:00
|
|
|
err = -errno;
|
|
|
|
goto out;
|
|
|
|
}
|
2010-09-02 09:39:06 +04:00
|
|
|
vs->fidp->fid_type = P9_FID_FILE;
|
2010-06-09 17:44:28 +04:00
|
|
|
vs->iounit = get_iounit(s, &vs->fidp->path);
|
|
|
|
v9fs_open_post_getiounit(s, vs);
|
|
|
|
return;
|
2010-04-29 16:14:55 +04:00
|
|
|
out:
|
|
|
|
complete_pdu(s, vs->pdu, err);
|
|
|
|
qemu_free(vs);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void v9fs_open_post_lstat(V9fsState *s, V9fsOpenState *vs, int err)
|
|
|
|
{
|
2010-06-22 18:17:04 +04:00
|
|
|
int flags;
|
|
|
|
|
2010-04-29 16:14:55 +04:00
|
|
|
if (err) {
|
|
|
|
err = -errno;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
stat_to_qid(&vs->stbuf, &vs->qid);
|
|
|
|
|
|
|
|
if (S_ISDIR(vs->stbuf.st_mode)) {
|
2010-09-02 09:39:06 +04:00
|
|
|
vs->fidp->fs.dir = v9fs_do_opendir(s, &vs->fidp->path);
|
2010-04-29 16:14:55 +04:00
|
|
|
v9fs_open_post_opendir(s, vs, err);
|
|
|
|
} else {
|
2010-06-22 18:17:04 +04:00
|
|
|
if (s->proto_version == V9FS_PROTO_2000L) {
|
|
|
|
flags = vs->mode;
|
virtio-9p: Change handling of flags in open() path for 9P2000.L
This patch applies on top of 9P2000.L patches that we have on the list.
I took a look at how 9P server is handling open() flags in 9P2000.L path.
I think we can do away with the valid_flags() function and simplify the
code. The reasoning is as follows:
O_NOCTTY: (If the file is a terminal, don't make it the controlling
terminal of the process even though the process does not have a controlling
terminal) By the time the control reaches 9P client it is clear that what
we have is not a terminal device. Hence it does not matter what we do with
this flag. In any case 9P server can filter this flag out before making the
syscall.
O_NONBLOCK: (Don't block if i) Can't read/write to the file ii) Can't get
locks) This has an impact on FIFOs, but also on file locks. Hence we can
pass it down to the system call.
O_ASYNC: From the manpage:
O_ASYNC
Enable signal-driven I/O: generate a signal (SIGIO by default, but
this can be changed via fcntl(2)) when input or output becomes pos-
sible on this file descriptor. This feature is only available for
terminals, pseudo-terminals, sockets, and (since Linux 2.6) pipes
and FIFOs. See fcntl(2) for further details.
Again, this does not make any impact on regular files handled by 9P. Also,
we don't want 9P server to receive SIGIO. Hence I think 9P server can
filter this flag out before making the syscall.
O_CLOEXEC: This flag makes sense only on the client. If guest user space
sets this flag the guest VFS will take care of calling close() on the fd if
an exec() happens. Hence 9P client need not be bothered with this flag.
Also I think QEMU will not do an exec, but if it does, it makes sense to
close these fds. Hence we can pass this flag down to the syscall.
O_CREAT: Since we are in open() path it means we have confirmed that the file
exists. Hence there is no need to pass O_CREAT flag down to the system. In fact
on some versions of glibc this causes problems, because we pass O_CREAT flag,
but don't have permission bits. Hence we can just mask this flag out.
So in summary:
Mask out:
O_NOCTTY
O_ASYNC
O_CREAT
Pass-through:
O_NONBLOCK
O_CLOEXEC
Signed-off-by: Sripathi Kodi <sripathik@in.ibm.com>
Signed-off-by: Venkateswararao Jujjuri <jvrao@linux.vnet.ibm.com>
2010-08-20 14:47:47 +04:00
|
|
|
flags &= ~(O_NOCTTY | O_ASYNC | O_CREAT);
|
2010-09-19 21:47:11 +04:00
|
|
|
/* Ignore direct disk access hint until the server supports it. */
|
|
|
|
flags &= ~O_DIRECT;
|
2010-06-22 18:17:04 +04:00
|
|
|
} else {
|
|
|
|
flags = omode_to_uflags(vs->mode);
|
|
|
|
}
|
2010-09-02 09:39:06 +04:00
|
|
|
vs->fidp->fs.fd = v9fs_do_open(s, &vs->fidp->path, flags);
|
2010-04-29 16:14:55 +04:00
|
|
|
v9fs_open_post_open(s, vs, err);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
out:
|
|
|
|
complete_pdu(s, vs->pdu, err);
|
|
|
|
qemu_free(vs);
|
2010-04-29 16:14:44 +04:00
|
|
|
}
|
|
|
|
|
2011-05-19 01:18:05 +04:00
|
|
|
static void v9fs_open(void *opaque)
|
2010-04-29 16:14:55 +04:00
|
|
|
{
|
2011-05-19 01:18:05 +04:00
|
|
|
V9fsPDU *pdu = opaque;
|
|
|
|
V9fsState *s = pdu->s;
|
2010-04-29 16:14:55 +04:00
|
|
|
int32_t fid;
|
|
|
|
V9fsOpenState *vs;
|
|
|
|
ssize_t err = 0;
|
|
|
|
|
|
|
|
vs = qemu_malloc(sizeof(*vs));
|
|
|
|
vs->pdu = pdu;
|
|
|
|
vs->offset = 7;
|
2010-06-22 18:17:04 +04:00
|
|
|
vs->mode = 0;
|
2010-04-29 16:14:55 +04:00
|
|
|
|
2010-06-22 18:17:04 +04:00
|
|
|
if (s->proto_version == V9FS_PROTO_2000L) {
|
|
|
|
pdu_unmarshal(vs->pdu, vs->offset, "dd", &fid, &vs->mode);
|
|
|
|
} else {
|
|
|
|
pdu_unmarshal(vs->pdu, vs->offset, "db", &fid, &vs->mode);
|
|
|
|
}
|
2010-04-29 16:14:55 +04:00
|
|
|
|
|
|
|
vs->fidp = lookup_fid(s, fid);
|
|
|
|
if (vs->fidp == NULL) {
|
|
|
|
err = -ENOENT;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2010-09-02 09:39:06 +04:00
|
|
|
BUG_ON(vs->fidp->fid_type != P9_FID_NONE);
|
2010-04-29 16:14:55 +04:00
|
|
|
|
|
|
|
err = v9fs_do_lstat(s, &vs->fidp->path, &vs->stbuf);
|
|
|
|
|
|
|
|
v9fs_open_post_lstat(s, vs, err);
|
|
|
|
return;
|
|
|
|
out:
|
|
|
|
complete_pdu(s, pdu, err);
|
|
|
|
qemu_free(vs);
|
|
|
|
}
|
|
|
|
|
2010-06-18 05:27:24 +04:00
|
|
|
static void v9fs_post_lcreate(V9fsState *s, V9fsLcreateState *vs, int err)
|
|
|
|
{
|
|
|
|
if (err == 0) {
|
|
|
|
v9fs_string_copy(&vs->fidp->path, &vs->fullname);
|
|
|
|
stat_to_qid(&vs->stbuf, &vs->qid);
|
|
|
|
vs->offset += pdu_marshal(vs->pdu, vs->offset, "Qd", &vs->qid,
|
2011-02-01 11:51:41 +03:00
|
|
|
vs->iounit);
|
2010-06-18 05:27:24 +04:00
|
|
|
err = vs->offset;
|
|
|
|
} else {
|
2010-09-02 09:39:06 +04:00
|
|
|
vs->fidp->fid_type = P9_FID_NONE;
|
2010-06-18 05:27:24 +04:00
|
|
|
err = -errno;
|
2010-09-20 21:54:29 +04:00
|
|
|
if (vs->fidp->fs.fd > 0) {
|
|
|
|
close(vs->fidp->fs.fd);
|
|
|
|
}
|
2010-06-18 05:27:24 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
complete_pdu(s, vs->pdu, err);
|
|
|
|
v9fs_string_free(&vs->name);
|
|
|
|
v9fs_string_free(&vs->fullname);
|
|
|
|
qemu_free(vs);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void v9fs_lcreate_post_get_iounit(V9fsState *s, V9fsLcreateState *vs,
|
|
|
|
int err)
|
|
|
|
{
|
|
|
|
if (err) {
|
|
|
|
err = -errno;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
err = v9fs_do_lstat(s, &vs->fullname, &vs->stbuf);
|
|
|
|
|
|
|
|
out:
|
|
|
|
v9fs_post_lcreate(s, vs, err);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void v9fs_lcreate_post_do_open2(V9fsState *s, V9fsLcreateState *vs,
|
|
|
|
int err)
|
|
|
|
{
|
2010-09-02 09:39:06 +04:00
|
|
|
if (vs->fidp->fs.fd == -1) {
|
2010-06-18 05:27:24 +04:00
|
|
|
err = -errno;
|
|
|
|
goto out;
|
|
|
|
}
|
2010-09-02 09:39:06 +04:00
|
|
|
vs->fidp->fid_type = P9_FID_FILE;
|
2010-06-18 05:27:24 +04:00
|
|
|
vs->iounit = get_iounit(s, &vs->fullname);
|
|
|
|
v9fs_lcreate_post_get_iounit(s, vs, err);
|
|
|
|
return;
|
|
|
|
|
|
|
|
out:
|
|
|
|
v9fs_post_lcreate(s, vs, err);
|
|
|
|
}
|
|
|
|
|
2011-05-19 01:18:05 +04:00
|
|
|
static void v9fs_lcreate(void *opaque)
|
2010-06-18 05:27:24 +04:00
|
|
|
{
|
2011-05-19 01:18:05 +04:00
|
|
|
V9fsPDU *pdu = opaque;
|
|
|
|
V9fsState *s = pdu->s;
|
2010-06-18 05:27:24 +04:00
|
|
|
int32_t dfid, flags, mode;
|
|
|
|
gid_t gid;
|
|
|
|
V9fsLcreateState *vs;
|
|
|
|
ssize_t err = 0;
|
|
|
|
|
|
|
|
vs = qemu_malloc(sizeof(*vs));
|
|
|
|
vs->pdu = pdu;
|
|
|
|
vs->offset = 7;
|
|
|
|
|
|
|
|
v9fs_string_init(&vs->fullname);
|
|
|
|
|
|
|
|
pdu_unmarshal(vs->pdu, vs->offset, "dsddd", &dfid, &vs->name, &flags,
|
|
|
|
&mode, &gid);
|
|
|
|
|
|
|
|
vs->fidp = lookup_fid(s, dfid);
|
|
|
|
if (vs->fidp == NULL) {
|
|
|
|
err = -ENOENT;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
v9fs_string_sprintf(&vs->fullname, "%s/%s", vs->fidp->path.data,
|
|
|
|
vs->name.data);
|
|
|
|
|
2010-09-19 21:47:11 +04:00
|
|
|
/* Ignore direct disk access hint until the server supports it. */
|
|
|
|
flags &= ~O_DIRECT;
|
|
|
|
|
2010-09-02 09:39:06 +04:00
|
|
|
vs->fidp->fs.fd = v9fs_do_open2(s, vs->fullname.data, vs->fidp->uid,
|
2010-06-18 05:27:24 +04:00
|
|
|
gid, flags, mode);
|
|
|
|
v9fs_lcreate_post_do_open2(s, vs, err);
|
|
|
|
return;
|
|
|
|
|
|
|
|
out:
|
|
|
|
complete_pdu(s, vs->pdu, err);
|
|
|
|
v9fs_string_free(&vs->name);
|
|
|
|
qemu_free(vs);
|
|
|
|
}
|
|
|
|
|
2010-09-23 04:18:33 +04:00
|
|
|
static void v9fs_post_do_fsync(V9fsState *s, V9fsPDU *pdu, int err)
|
|
|
|
{
|
|
|
|
if (err == -1) {
|
|
|
|
err = -errno;
|
|
|
|
}
|
|
|
|
complete_pdu(s, pdu, err);
|
|
|
|
}
|
|
|
|
|
2011-05-19 01:18:05 +04:00
|
|
|
static void v9fs_fsync(void *opaque)
|
2010-09-23 04:18:33 +04:00
|
|
|
{
|
2011-05-19 01:18:05 +04:00
|
|
|
V9fsPDU *pdu = opaque;
|
|
|
|
V9fsState *s = pdu->s;
|
2010-09-23 04:18:33 +04:00
|
|
|
int32_t fid;
|
|
|
|
size_t offset = 7;
|
|
|
|
V9fsFidState *fidp;
|
2010-10-22 21:08:45 +04:00
|
|
|
int datasync;
|
2010-09-23 04:18:33 +04:00
|
|
|
int err;
|
|
|
|
|
2010-10-22 21:08:45 +04:00
|
|
|
pdu_unmarshal(pdu, offset, "dd", &fid, &datasync);
|
2010-09-23 04:18:33 +04:00
|
|
|
fidp = lookup_fid(s, fid);
|
|
|
|
if (fidp == NULL) {
|
|
|
|
err = -ENOENT;
|
|
|
|
v9fs_post_do_fsync(s, pdu, err);
|
|
|
|
return;
|
|
|
|
}
|
2010-10-22 21:08:45 +04:00
|
|
|
err = v9fs_do_fsync(s, fidp->fs.fd, datasync);
|
2010-09-23 04:18:33 +04:00
|
|
|
v9fs_post_do_fsync(s, pdu, err);
|
|
|
|
}
|
|
|
|
|
2011-05-19 01:18:05 +04:00
|
|
|
static void v9fs_clunk(void *opaque)
|
2010-04-29 16:14:55 +04:00
|
|
|
{
|
2011-05-19 01:18:05 +04:00
|
|
|
V9fsPDU *pdu = opaque;
|
|
|
|
V9fsState *s = pdu->s;
|
2010-04-29 16:14:57 +04:00
|
|
|
int32_t fid;
|
|
|
|
size_t offset = 7;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
pdu_unmarshal(pdu, offset, "d", &fid);
|
|
|
|
|
|
|
|
err = free_fid(s, fid);
|
|
|
|
if (err < 0) {
|
|
|
|
goto out;
|
2010-04-29 16:14:55 +04:00
|
|
|
}
|
2010-04-29 16:14:57 +04:00
|
|
|
|
|
|
|
offset = 7;
|
|
|
|
err = offset;
|
|
|
|
out:
|
|
|
|
complete_pdu(s, pdu, err);
|
2010-04-29 16:14:44 +04:00
|
|
|
}
|
|
|
|
|
2010-04-29 16:14:56 +04:00
|
|
|
static void v9fs_read_post_readdir(V9fsState *, V9fsReadState *, ssize_t);
|
|
|
|
|
|
|
|
static void v9fs_read_post_seekdir(V9fsState *s, V9fsReadState *vs, ssize_t err)
|
|
|
|
{
|
|
|
|
if (err) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
vs->offset += pdu_marshal(vs->pdu, vs->offset, "d", vs->count);
|
|
|
|
vs->offset += vs->count;
|
|
|
|
err = vs->offset;
|
|
|
|
out:
|
|
|
|
complete_pdu(s, vs->pdu, err);
|
2011-08-08 22:06:41 +04:00
|
|
|
v9fs_stat_free(&vs->v9stat);
|
|
|
|
v9fs_string_free(&vs->name);
|
2010-04-29 16:14:56 +04:00
|
|
|
qemu_free(vs);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void v9fs_read_post_dir_lstat(V9fsState *s, V9fsReadState *vs,
|
|
|
|
ssize_t err)
|
|
|
|
{
|
|
|
|
if (err) {
|
|
|
|
err = -errno;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
err = stat_to_v9stat(s, &vs->name, &vs->stbuf, &vs->v9stat);
|
|
|
|
if (err) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
vs->len = pdu_marshal(vs->pdu, vs->offset + 4 + vs->count, "S",
|
|
|
|
&vs->v9stat);
|
|
|
|
if ((vs->len != (vs->v9stat.size + 2)) ||
|
|
|
|
((vs->count + vs->len) > vs->max_count)) {
|
2010-09-02 09:39:06 +04:00
|
|
|
v9fs_do_seekdir(s, vs->fidp->fs.dir, vs->dir_pos);
|
2010-04-29 16:14:56 +04:00
|
|
|
v9fs_read_post_seekdir(s, vs, err);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
vs->count += vs->len;
|
|
|
|
v9fs_stat_free(&vs->v9stat);
|
|
|
|
v9fs_string_free(&vs->name);
|
|
|
|
vs->dir_pos = vs->dent->d_off;
|
2010-09-02 09:39:06 +04:00
|
|
|
vs->dent = v9fs_do_readdir(s, vs->fidp->fs.dir);
|
2010-04-29 16:14:56 +04:00
|
|
|
v9fs_read_post_readdir(s, vs, err);
|
|
|
|
return;
|
|
|
|
out:
|
2010-09-02 09:39:06 +04:00
|
|
|
v9fs_do_seekdir(s, vs->fidp->fs.dir, vs->dir_pos);
|
2010-04-29 16:14:56 +04:00
|
|
|
v9fs_read_post_seekdir(s, vs, err);
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static void v9fs_read_post_readdir(V9fsState *s, V9fsReadState *vs, ssize_t err)
|
|
|
|
{
|
|
|
|
if (vs->dent) {
|
|
|
|
memset(&vs->v9stat, 0, sizeof(vs->v9stat));
|
|
|
|
v9fs_string_init(&vs->name);
|
|
|
|
v9fs_string_sprintf(&vs->name, "%s/%s", vs->fidp->path.data,
|
|
|
|
vs->dent->d_name);
|
|
|
|
err = v9fs_do_lstat(s, &vs->name, &vs->stbuf);
|
|
|
|
v9fs_read_post_dir_lstat(s, vs, err);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
vs->offset += pdu_marshal(vs->pdu, vs->offset, "d", vs->count);
|
|
|
|
vs->offset += vs->count;
|
|
|
|
err = vs->offset;
|
|
|
|
complete_pdu(s, vs->pdu, err);
|
|
|
|
qemu_free(vs);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void v9fs_read_post_telldir(V9fsState *s, V9fsReadState *vs, ssize_t err)
|
|
|
|
{
|
2010-09-02 09:39:06 +04:00
|
|
|
vs->dent = v9fs_do_readdir(s, vs->fidp->fs.dir);
|
2010-04-29 16:14:56 +04:00
|
|
|
v9fs_read_post_readdir(s, vs, err);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void v9fs_read_post_rewinddir(V9fsState *s, V9fsReadState *vs,
|
|
|
|
ssize_t err)
|
|
|
|
{
|
2010-09-02 09:39:06 +04:00
|
|
|
vs->dir_pos = v9fs_do_telldir(s, vs->fidp->fs.dir);
|
2010-04-29 16:14:56 +04:00
|
|
|
v9fs_read_post_telldir(s, vs, err);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2010-10-08 10:00:16 +04:00
|
|
|
static void v9fs_read_post_preadv(V9fsState *s, V9fsReadState *vs, ssize_t err)
|
2010-04-29 16:14:56 +04:00
|
|
|
{
|
|
|
|
if (err < 0) {
|
|
|
|
/* IO error return the error */
|
|
|
|
err = -errno;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
vs->total += vs->len;
|
|
|
|
vs->sg = adjust_sg(vs->sg, vs->len, &vs->cnt);
|
|
|
|
if (vs->total < vs->count && vs->len > 0) {
|
|
|
|
do {
|
|
|
|
if (0) {
|
|
|
|
print_sg(vs->sg, vs->cnt);
|
|
|
|
}
|
2010-10-08 10:00:16 +04:00
|
|
|
vs->len = v9fs_do_preadv(s, vs->fidp->fs.fd, vs->sg, vs->cnt,
|
|
|
|
vs->off);
|
|
|
|
if (vs->len > 0) {
|
|
|
|
vs->off += vs->len;
|
|
|
|
}
|
2010-04-29 16:14:56 +04:00
|
|
|
} while (vs->len == -1 && errno == EINTR);
|
|
|
|
if (vs->len == -1) {
|
|
|
|
err = -errno;
|
|
|
|
}
|
2010-10-08 10:00:16 +04:00
|
|
|
v9fs_read_post_preadv(s, vs, err);
|
2010-04-29 16:14:56 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
vs->offset += pdu_marshal(vs->pdu, vs->offset, "d", vs->total);
|
|
|
|
vs->offset += vs->count;
|
|
|
|
err = vs->offset;
|
|
|
|
|
|
|
|
out:
|
|
|
|
complete_pdu(s, vs->pdu, err);
|
|
|
|
qemu_free(vs);
|
|
|
|
}
|
|
|
|
|
2010-09-02 09:39:06 +04:00
|
|
|
static void v9fs_xattr_read(V9fsState *s, V9fsReadState *vs)
|
|
|
|
{
|
|
|
|
ssize_t err = 0;
|
|
|
|
int read_count;
|
|
|
|
int64_t xattr_len;
|
|
|
|
|
|
|
|
xattr_len = vs->fidp->fs.xattr.len;
|
|
|
|
read_count = xattr_len - vs->off;
|
|
|
|
if (read_count > vs->count) {
|
|
|
|
read_count = vs->count;
|
|
|
|
} else if (read_count < 0) {
|
|
|
|
/*
|
|
|
|
* read beyond XATTR value
|
|
|
|
*/
|
|
|
|
read_count = 0;
|
|
|
|
}
|
|
|
|
vs->offset += pdu_marshal(vs->pdu, vs->offset, "d", read_count);
|
|
|
|
vs->offset += pdu_pack(vs->pdu, vs->offset,
|
|
|
|
((char *)vs->fidp->fs.xattr.value) + vs->off,
|
|
|
|
read_count);
|
|
|
|
err = vs->offset;
|
|
|
|
complete_pdu(s, vs->pdu, err);
|
|
|
|
qemu_free(vs);
|
|
|
|
}
|
|
|
|
|
2011-05-19 01:18:05 +04:00
|
|
|
static void v9fs_read(void *opaque)
|
2010-04-29 16:14:44 +04:00
|
|
|
{
|
2011-05-19 01:18:05 +04:00
|
|
|
V9fsPDU *pdu = opaque;
|
|
|
|
V9fsState *s = pdu->s;
|
2010-04-29 16:14:56 +04:00
|
|
|
int32_t fid;
|
|
|
|
V9fsReadState *vs;
|
|
|
|
ssize_t err = 0;
|
|
|
|
|
|
|
|
vs = qemu_malloc(sizeof(*vs));
|
|
|
|
vs->pdu = pdu;
|
|
|
|
vs->offset = 7;
|
|
|
|
vs->total = 0;
|
|
|
|
vs->len = 0;
|
|
|
|
vs->count = 0;
|
|
|
|
|
|
|
|
pdu_unmarshal(vs->pdu, vs->offset, "dqd", &fid, &vs->off, &vs->count);
|
|
|
|
|
|
|
|
vs->fidp = lookup_fid(s, fid);
|
|
|
|
if (vs->fidp == NULL) {
|
|
|
|
err = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2010-09-02 09:39:06 +04:00
|
|
|
if (vs->fidp->fid_type == P9_FID_DIR) {
|
2010-04-29 16:14:56 +04:00
|
|
|
vs->max_count = vs->count;
|
|
|
|
vs->count = 0;
|
|
|
|
if (vs->off == 0) {
|
2010-09-02 09:39:06 +04:00
|
|
|
v9fs_do_rewinddir(s, vs->fidp->fs.dir);
|
2010-04-29 16:14:56 +04:00
|
|
|
}
|
|
|
|
v9fs_read_post_rewinddir(s, vs, err);
|
|
|
|
return;
|
2010-09-02 09:39:06 +04:00
|
|
|
} else if (vs->fidp->fid_type == P9_FID_FILE) {
|
2010-04-29 16:14:56 +04:00
|
|
|
vs->sg = vs->iov;
|
|
|
|
pdu_marshal(vs->pdu, vs->offset + 4, "v", vs->sg, &vs->cnt);
|
2010-10-08 10:00:16 +04:00
|
|
|
vs->sg = cap_sg(vs->sg, vs->count, &vs->cnt);
|
|
|
|
if (vs->total <= vs->count) {
|
|
|
|
vs->len = v9fs_do_preadv(s, vs->fidp->fs.fd, vs->sg, vs->cnt,
|
|
|
|
vs->off);
|
|
|
|
if (vs->len > 0) {
|
|
|
|
vs->off += vs->len;
|
|
|
|
}
|
|
|
|
err = vs->len;
|
|
|
|
v9fs_read_post_preadv(s, vs, err);
|
|
|
|
}
|
2010-04-29 16:14:56 +04:00
|
|
|
return;
|
2010-09-02 09:39:06 +04:00
|
|
|
} else if (vs->fidp->fid_type == P9_FID_XATTR) {
|
|
|
|
v9fs_xattr_read(s, vs);
|
|
|
|
return;
|
2010-04-29 16:14:56 +04:00
|
|
|
} else {
|
|
|
|
err = -EINVAL;
|
2010-04-29 16:14:44 +04:00
|
|
|
}
|
2010-04-29 16:14:56 +04:00
|
|
|
out:
|
|
|
|
complete_pdu(s, pdu, err);
|
|
|
|
qemu_free(vs);
|
2010-04-29 16:14:44 +04:00
|
|
|
}
|
|
|
|
|
2010-06-09 13:27:57 +04:00
|
|
|
typedef struct V9fsReadDirState {
|
|
|
|
V9fsPDU *pdu;
|
|
|
|
V9fsFidState *fidp;
|
|
|
|
V9fsQID qid;
|
|
|
|
off_t saved_dir_pos;
|
|
|
|
struct dirent *dent;
|
|
|
|
int32_t count;
|
|
|
|
int32_t max_count;
|
|
|
|
size_t offset;
|
|
|
|
int64_t initial_offset;
|
|
|
|
V9fsString name;
|
|
|
|
} V9fsReadDirState;
|
|
|
|
|
|
|
|
static void v9fs_readdir_post_seekdir(V9fsState *s, V9fsReadDirState *vs)
|
|
|
|
{
|
|
|
|
vs->offset += pdu_marshal(vs->pdu, vs->offset, "d", vs->count);
|
|
|
|
vs->offset += vs->count;
|
|
|
|
complete_pdu(s, vs->pdu, vs->offset);
|
|
|
|
qemu_free(vs);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Size of each dirent on the wire: size of qid (13) + size of offset (8)
|
|
|
|
* size of type (1) + size of name.size (2) + strlen(name.data)
|
|
|
|
*/
|
|
|
|
#define V9_READDIR_DATA_SZ (24 + strlen(vs->name.data))
|
|
|
|
|
|
|
|
static void v9fs_readdir_post_readdir(V9fsState *s, V9fsReadDirState *vs)
|
|
|
|
{
|
|
|
|
int len;
|
|
|
|
size_t size;
|
|
|
|
|
|
|
|
if (vs->dent) {
|
|
|
|
v9fs_string_init(&vs->name);
|
|
|
|
v9fs_string_sprintf(&vs->name, "%s", vs->dent->d_name);
|
|
|
|
|
|
|
|
if ((vs->count + V9_READDIR_DATA_SZ) > vs->max_count) {
|
|
|
|
/* Ran out of buffer. Set dir back to old position and return */
|
2010-09-02 09:39:06 +04:00
|
|
|
v9fs_do_seekdir(s, vs->fidp->fs.dir, vs->saved_dir_pos);
|
2010-06-09 13:27:57 +04:00
|
|
|
v9fs_readdir_post_seekdir(s, vs);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Fill up just the path field of qid because the client uses
|
|
|
|
* only that. To fill the entire qid structure we will have
|
|
|
|
* to stat each dirent found, which is expensive
|
|
|
|
*/
|
|
|
|
size = MIN(sizeof(vs->dent->d_ino), sizeof(vs->qid.path));
|
|
|
|
memcpy(&vs->qid.path, &vs->dent->d_ino, size);
|
|
|
|
/* Fill the other fields with dummy values */
|
|
|
|
vs->qid.type = 0;
|
|
|
|
vs->qid.version = 0;
|
|
|
|
|
|
|
|
len = pdu_marshal(vs->pdu, vs->offset+4+vs->count, "Qqbs",
|
|
|
|
&vs->qid, vs->dent->d_off,
|
|
|
|
vs->dent->d_type, &vs->name);
|
|
|
|
vs->count += len;
|
|
|
|
v9fs_string_free(&vs->name);
|
|
|
|
vs->saved_dir_pos = vs->dent->d_off;
|
2010-09-02 09:39:06 +04:00
|
|
|
vs->dent = v9fs_do_readdir(s, vs->fidp->fs.dir);
|
2010-06-09 13:27:57 +04:00
|
|
|
v9fs_readdir_post_readdir(s, vs);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
vs->offset += pdu_marshal(vs->pdu, vs->offset, "d", vs->count);
|
|
|
|
vs->offset += vs->count;
|
|
|
|
complete_pdu(s, vs->pdu, vs->offset);
|
|
|
|
qemu_free(vs);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void v9fs_readdir_post_telldir(V9fsState *s, V9fsReadDirState *vs)
|
|
|
|
{
|
2010-09-02 09:39:06 +04:00
|
|
|
vs->dent = v9fs_do_readdir(s, vs->fidp->fs.dir);
|
2010-06-09 13:27:57 +04:00
|
|
|
v9fs_readdir_post_readdir(s, vs);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void v9fs_readdir_post_setdir(V9fsState *s, V9fsReadDirState *vs)
|
|
|
|
{
|
2010-09-02 09:39:06 +04:00
|
|
|
vs->saved_dir_pos = v9fs_do_telldir(s, vs->fidp->fs.dir);
|
2010-06-09 13:27:57 +04:00
|
|
|
v9fs_readdir_post_telldir(s, vs);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-05-19 01:18:05 +04:00
|
|
|
static void v9fs_readdir(void *opaque)
|
2010-06-09 13:27:57 +04:00
|
|
|
{
|
2011-05-19 01:18:05 +04:00
|
|
|
V9fsPDU *pdu = opaque;
|
|
|
|
V9fsState *s = pdu->s;
|
2010-06-09 13:27:57 +04:00
|
|
|
int32_t fid;
|
|
|
|
V9fsReadDirState *vs;
|
|
|
|
ssize_t err = 0;
|
|
|
|
size_t offset = 7;
|
|
|
|
|
|
|
|
vs = qemu_malloc(sizeof(*vs));
|
|
|
|
vs->pdu = pdu;
|
|
|
|
vs->offset = 7;
|
|
|
|
vs->count = 0;
|
|
|
|
|
|
|
|
pdu_unmarshal(vs->pdu, offset, "dqd", &fid, &vs->initial_offset,
|
|
|
|
&vs->max_count);
|
|
|
|
|
|
|
|
vs->fidp = lookup_fid(s, fid);
|
2010-09-02 09:39:06 +04:00
|
|
|
if (vs->fidp == NULL || !(vs->fidp->fs.dir)) {
|
2010-06-09 13:27:57 +04:00
|
|
|
err = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (vs->initial_offset == 0) {
|
2010-09-02 09:39:06 +04:00
|
|
|
v9fs_do_rewinddir(s, vs->fidp->fs.dir);
|
2010-06-09 13:27:57 +04:00
|
|
|
} else {
|
2010-09-02 09:39:06 +04:00
|
|
|
v9fs_do_seekdir(s, vs->fidp->fs.dir, vs->initial_offset);
|
2010-06-09 13:27:57 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
v9fs_readdir_post_setdir(s, vs);
|
|
|
|
return;
|
|
|
|
|
|
|
|
out:
|
|
|
|
complete_pdu(s, pdu, err);
|
|
|
|
qemu_free(vs);
|
|
|
|
}
|
|
|
|
|
2010-10-08 10:00:16 +04:00
|
|
|
static void v9fs_write_post_pwritev(V9fsState *s, V9fsWriteState *vs,
|
2010-04-29 16:14:58 +04:00
|
|
|
ssize_t err)
|
|
|
|
{
|
|
|
|
if (err < 0) {
|
|
|
|
/* IO error return the error */
|
|
|
|
err = -errno;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
vs->total += vs->len;
|
|
|
|
vs->sg = adjust_sg(vs->sg, vs->len, &vs->cnt);
|
|
|
|
if (vs->total < vs->count && vs->len > 0) {
|
|
|
|
do {
|
|
|
|
if (0) {
|
|
|
|
print_sg(vs->sg, vs->cnt);
|
|
|
|
}
|
2010-10-08 10:00:16 +04:00
|
|
|
vs->len = v9fs_do_pwritev(s, vs->fidp->fs.fd, vs->sg, vs->cnt,
|
|
|
|
vs->off);
|
|
|
|
if (vs->len > 0) {
|
|
|
|
vs->off += vs->len;
|
|
|
|
}
|
2010-04-29 16:14:58 +04:00
|
|
|
} while (vs->len == -1 && errno == EINTR);
|
|
|
|
if (vs->len == -1) {
|
|
|
|
err = -errno;
|
|
|
|
}
|
2010-10-08 10:00:16 +04:00
|
|
|
v9fs_write_post_pwritev(s, vs, err);
|
2010-04-29 16:14:58 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
vs->offset += pdu_marshal(vs->pdu, vs->offset, "d", vs->total);
|
|
|
|
err = vs->offset;
|
|
|
|
out:
|
|
|
|
complete_pdu(s, vs->pdu, err);
|
|
|
|
qemu_free(vs);
|
|
|
|
}
|
|
|
|
|
2010-09-02 09:39:07 +04:00
|
|
|
static void v9fs_xattr_write(V9fsState *s, V9fsWriteState *vs)
|
|
|
|
{
|
|
|
|
int i, to_copy;
|
|
|
|
ssize_t err = 0;
|
|
|
|
int write_count;
|
|
|
|
int64_t xattr_len;
|
|
|
|
|
|
|
|
xattr_len = vs->fidp->fs.xattr.len;
|
|
|
|
write_count = xattr_len - vs->off;
|
|
|
|
if (write_count > vs->count) {
|
|
|
|
write_count = vs->count;
|
|
|
|
} else if (write_count < 0) {
|
|
|
|
/*
|
|
|
|
* write beyond XATTR value len specified in
|
|
|
|
* xattrcreate
|
|
|
|
*/
|
|
|
|
err = -ENOSPC;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
vs->offset += pdu_marshal(vs->pdu, vs->offset, "d", write_count);
|
|
|
|
err = vs->offset;
|
|
|
|
vs->fidp->fs.xattr.copied_len += write_count;
|
|
|
|
/*
|
|
|
|
* Now copy the content from sg list
|
|
|
|
*/
|
|
|
|
for (i = 0; i < vs->cnt; i++) {
|
|
|
|
if (write_count > vs->sg[i].iov_len) {
|
|
|
|
to_copy = vs->sg[i].iov_len;
|
|
|
|
} else {
|
|
|
|
to_copy = write_count;
|
|
|
|
}
|
|
|
|
memcpy((char *)vs->fidp->fs.xattr.value + vs->off,
|
|
|
|
vs->sg[i].iov_base, to_copy);
|
|
|
|
/* updating vs->off since we are not using below */
|
|
|
|
vs->off += to_copy;
|
|
|
|
write_count -= to_copy;
|
|
|
|
}
|
|
|
|
out:
|
|
|
|
complete_pdu(s, vs->pdu, err);
|
|
|
|
qemu_free(vs);
|
|
|
|
}
|
|
|
|
|
2011-05-19 01:18:05 +04:00
|
|
|
static void v9fs_write(void *opaque)
|
2010-04-29 16:14:44 +04:00
|
|
|
{
|
2011-05-19 01:18:05 +04:00
|
|
|
V9fsPDU *pdu = opaque;
|
|
|
|
V9fsState *s = pdu->s;
|
2010-04-29 16:14:58 +04:00
|
|
|
int32_t fid;
|
|
|
|
V9fsWriteState *vs;
|
|
|
|
ssize_t err;
|
|
|
|
|
|
|
|
vs = qemu_malloc(sizeof(*vs));
|
|
|
|
|
|
|
|
vs->pdu = pdu;
|
|
|
|
vs->offset = 7;
|
|
|
|
vs->sg = vs->iov;
|
|
|
|
vs->total = 0;
|
|
|
|
vs->len = 0;
|
|
|
|
|
|
|
|
pdu_unmarshal(vs->pdu, vs->offset, "dqdv", &fid, &vs->off, &vs->count,
|
2010-09-02 09:39:07 +04:00
|
|
|
vs->sg, &vs->cnt);
|
2010-04-29 16:14:58 +04:00
|
|
|
|
|
|
|
vs->fidp = lookup_fid(s, fid);
|
|
|
|
if (vs->fidp == NULL) {
|
|
|
|
err = -EINVAL;
|
|
|
|
goto out;
|
2010-04-29 16:14:44 +04:00
|
|
|
}
|
2010-04-29 16:14:58 +04:00
|
|
|
|
2010-09-02 09:39:07 +04:00
|
|
|
if (vs->fidp->fid_type == P9_FID_FILE) {
|
|
|
|
if (vs->fidp->fs.fd == -1) {
|
|
|
|
err = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
} else if (vs->fidp->fid_type == P9_FID_XATTR) {
|
|
|
|
/*
|
|
|
|
* setxattr operation
|
|
|
|
*/
|
|
|
|
v9fs_xattr_write(s, vs);
|
|
|
|
return;
|
|
|
|
} else {
|
2010-04-29 16:14:58 +04:00
|
|
|
err = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
2010-10-08 10:00:16 +04:00
|
|
|
vs->sg = cap_sg(vs->sg, vs->count, &vs->cnt);
|
|
|
|
if (vs->total <= vs->count) {
|
|
|
|
vs->len = v9fs_do_pwritev(s, vs->fidp->fs.fd, vs->sg, vs->cnt, vs->off);
|
|
|
|
if (vs->len > 0) {
|
|
|
|
vs->off += vs->len;
|
|
|
|
}
|
|
|
|
err = vs->len;
|
|
|
|
v9fs_write_post_pwritev(s, vs, err);
|
|
|
|
}
|
2010-04-29 16:14:58 +04:00
|
|
|
return;
|
|
|
|
out:
|
|
|
|
complete_pdu(s, vs->pdu, err);
|
|
|
|
qemu_free(vs);
|
2010-04-29 16:14:44 +04:00
|
|
|
}
|
|
|
|
|
2010-06-09 17:44:28 +04:00
|
|
|
static void v9fs_create_post_getiounit(V9fsState *s, V9fsCreateState *vs)
|
2010-04-29 16:14:59 +04:00
|
|
|
{
|
2010-06-09 17:44:28 +04:00
|
|
|
int err;
|
|
|
|
v9fs_string_copy(&vs->fidp->path, &vs->fullname);
|
|
|
|
stat_to_qid(&vs->stbuf, &vs->qid);
|
2010-04-29 16:14:59 +04:00
|
|
|
|
2010-06-09 17:44:28 +04:00
|
|
|
vs->offset += pdu_marshal(vs->pdu, vs->offset, "Qd", &vs->qid, vs->iounit);
|
|
|
|
err = vs->offset;
|
2010-04-29 16:14:59 +04:00
|
|
|
|
2010-06-09 17:44:28 +04:00
|
|
|
complete_pdu(s, vs->pdu, err);
|
|
|
|
v9fs_string_free(&vs->name);
|
|
|
|
v9fs_string_free(&vs->extension);
|
|
|
|
v9fs_string_free(&vs->fullname);
|
|
|
|
qemu_free(vs);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void v9fs_post_create(V9fsState *s, V9fsCreateState *vs, int err)
|
|
|
|
{
|
|
|
|
if (err == 0) {
|
|
|
|
vs->iounit = get_iounit(s, &vs->fidp->path);
|
|
|
|
v9fs_create_post_getiounit(s, vs);
|
|
|
|
return;
|
2010-04-29 16:14:59 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
complete_pdu(s, vs->pdu, err);
|
|
|
|
v9fs_string_free(&vs->name);
|
|
|
|
v9fs_string_free(&vs->extension);
|
|
|
|
v9fs_string_free(&vs->fullname);
|
|
|
|
qemu_free(vs);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void v9fs_create_post_perms(V9fsState *s, V9fsCreateState *vs, int err)
|
|
|
|
{
|
|
|
|
if (err) {
|
|
|
|
err = -errno;
|
|
|
|
}
|
|
|
|
v9fs_post_create(s, vs, err);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void v9fs_create_post_opendir(V9fsState *s, V9fsCreateState *vs,
|
|
|
|
int err)
|
|
|
|
{
|
2010-09-02 09:39:06 +04:00
|
|
|
if (!vs->fidp->fs.dir) {
|
2010-04-29 16:14:59 +04:00
|
|
|
err = -errno;
|
|
|
|
}
|
2010-09-02 09:39:06 +04:00
|
|
|
vs->fidp->fid_type = P9_FID_DIR;
|
2010-04-29 16:14:59 +04:00
|
|
|
v9fs_post_create(s, vs, err);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void v9fs_create_post_dir_lstat(V9fsState *s, V9fsCreateState *vs,
|
|
|
|
int err)
|
|
|
|
{
|
|
|
|
if (err) {
|
|
|
|
err = -errno;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2010-09-02 09:39:06 +04:00
|
|
|
vs->fidp->fs.dir = v9fs_do_opendir(s, &vs->fullname);
|
2010-04-29 16:14:59 +04:00
|
|
|
v9fs_create_post_opendir(s, vs, err);
|
|
|
|
return;
|
|
|
|
|
|
|
|
out:
|
|
|
|
v9fs_post_create(s, vs, err);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void v9fs_create_post_mkdir(V9fsState *s, V9fsCreateState *vs, int err)
|
|
|
|
{
|
|
|
|
if (err) {
|
|
|
|
err = -errno;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = v9fs_do_lstat(s, &vs->fullname, &vs->stbuf);
|
|
|
|
v9fs_create_post_dir_lstat(s, vs, err);
|
|
|
|
return;
|
|
|
|
|
|
|
|
out:
|
|
|
|
v9fs_post_create(s, vs, err);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void v9fs_create_post_fstat(V9fsState *s, V9fsCreateState *vs, int err)
|
|
|
|
{
|
|
|
|
if (err) {
|
2010-09-02 09:39:06 +04:00
|
|
|
vs->fidp->fid_type = P9_FID_NONE;
|
|
|
|
close(vs->fidp->fs.fd);
|
2010-04-29 16:14:59 +04:00
|
|
|
err = -errno;
|
|
|
|
}
|
|
|
|
v9fs_post_create(s, vs, err);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void v9fs_create_post_open2(V9fsState *s, V9fsCreateState *vs, int err)
|
|
|
|
{
|
2010-09-02 09:39:06 +04:00
|
|
|
if (vs->fidp->fs.fd == -1) {
|
2010-04-29 16:14:59 +04:00
|
|
|
err = -errno;
|
|
|
|
goto out;
|
|
|
|
}
|
2010-09-02 09:39:06 +04:00
|
|
|
vs->fidp->fid_type = P9_FID_FILE;
|
|
|
|
err = v9fs_do_fstat(s, vs->fidp->fs.fd, &vs->stbuf);
|
2010-04-29 16:14:59 +04:00
|
|
|
v9fs_create_post_fstat(s, vs, err);
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
out:
|
|
|
|
v9fs_post_create(s, vs, err);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static void v9fs_create_post_lstat(V9fsState *s, V9fsCreateState *vs, int err)
|
|
|
|
{
|
|
|
|
|
|
|
|
if (err == 0 || errno != ENOENT) {
|
|
|
|
err = -errno;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (vs->perm & P9_STAT_MODE_DIR) {
|
2010-06-22 10:55:22 +04:00
|
|
|
err = v9fs_do_mkdir(s, vs->fullname.data, vs->perm & 0777,
|
|
|
|
vs->fidp->uid, -1);
|
2010-04-29 16:14:59 +04:00
|
|
|
v9fs_create_post_mkdir(s, vs, err);
|
|
|
|
} else if (vs->perm & P9_STAT_MODE_SYMLINK) {
|
2010-06-10 01:02:08 +04:00
|
|
|
err = v9fs_do_symlink(s, vs->fidp, vs->extension.data,
|
|
|
|
vs->fullname.data, -1);
|
2010-04-29 16:14:59 +04:00
|
|
|
v9fs_create_post_perms(s, vs, err);
|
|
|
|
} else if (vs->perm & P9_STAT_MODE_LINK) {
|
|
|
|
int32_t nfid = atoi(vs->extension.data);
|
|
|
|
V9fsFidState *nfidp = lookup_fid(s, nfid);
|
|
|
|
if (nfidp == NULL) {
|
|
|
|
err = -errno;
|
|
|
|
v9fs_post_create(s, vs, err);
|
|
|
|
}
|
|
|
|
err = v9fs_do_link(s, &nfidp->path, &vs->fullname);
|
|
|
|
v9fs_create_post_perms(s, vs, err);
|
|
|
|
} else if (vs->perm & P9_STAT_MODE_DEVICE) {
|
|
|
|
char ctype;
|
|
|
|
uint32_t major, minor;
|
|
|
|
mode_t nmode = 0;
|
|
|
|
|
|
|
|
if (sscanf(vs->extension.data, "%c %u %u", &ctype, &major,
|
|
|
|
&minor) != 3) {
|
|
|
|
err = -errno;
|
|
|
|
v9fs_post_create(s, vs, err);
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (ctype) {
|
|
|
|
case 'c':
|
|
|
|
nmode = S_IFCHR;
|
|
|
|
break;
|
|
|
|
case 'b':
|
|
|
|
nmode = S_IFBLK;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
err = -EIO;
|
|
|
|
v9fs_post_create(s, vs, err);
|
|
|
|
}
|
|
|
|
|
|
|
|
nmode |= vs->perm & 0777;
|
2010-06-22 10:54:09 +04:00
|
|
|
err = v9fs_do_mknod(s, vs->fullname.data, nmode,
|
|
|
|
makedev(major, minor), vs->fidp->uid, -1);
|
2010-04-29 16:14:59 +04:00
|
|
|
v9fs_create_post_perms(s, vs, err);
|
|
|
|
} else if (vs->perm & P9_STAT_MODE_NAMED_PIPE) {
|
2010-06-22 10:54:09 +04:00
|
|
|
err = v9fs_do_mknod(s, vs->fullname.data, S_IFIFO | (vs->perm & 0777),
|
|
|
|
0, vs->fidp->uid, -1);
|
2010-04-29 16:14:59 +04:00
|
|
|
v9fs_post_create(s, vs, err);
|
|
|
|
} else if (vs->perm & P9_STAT_MODE_SOCKET) {
|
2010-06-22 10:54:09 +04:00
|
|
|
err = v9fs_do_mknod(s, vs->fullname.data, S_IFSOCK | (vs->perm & 0777),
|
|
|
|
0, vs->fidp->uid, -1);
|
2010-06-15 00:34:49 +04:00
|
|
|
v9fs_post_create(s, vs, err);
|
2010-04-29 16:14:59 +04:00
|
|
|
} else {
|
2010-09-02 09:39:06 +04:00
|
|
|
vs->fidp->fs.fd = v9fs_do_open2(s, vs->fullname.data, vs->fidp->uid,
|
2010-06-18 05:27:24 +04:00
|
|
|
-1, omode_to_uflags(vs->mode)|O_CREAT, vs->perm);
|
|
|
|
|
2010-04-29 16:14:59 +04:00
|
|
|
v9fs_create_post_open2(s, vs, err);
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
out:
|
|
|
|
v9fs_post_create(s, vs, err);
|
|
|
|
}
|
|
|
|
|
2011-05-19 01:18:05 +04:00
|
|
|
static void v9fs_create(void *opaque)
|
2010-04-29 16:14:44 +04:00
|
|
|
{
|
2011-05-19 01:18:05 +04:00
|
|
|
V9fsPDU *pdu = opaque;
|
|
|
|
V9fsState *s = pdu->s;
|
2010-04-29 16:14:59 +04:00
|
|
|
int32_t fid;
|
|
|
|
V9fsCreateState *vs;
|
|
|
|
int err = 0;
|
|
|
|
|
|
|
|
vs = qemu_malloc(sizeof(*vs));
|
|
|
|
vs->pdu = pdu;
|
|
|
|
vs->offset = 7;
|
|
|
|
|
|
|
|
v9fs_string_init(&vs->fullname);
|
|
|
|
|
|
|
|
pdu_unmarshal(vs->pdu, vs->offset, "dsdbs", &fid, &vs->name,
|
|
|
|
&vs->perm, &vs->mode, &vs->extension);
|
|
|
|
|
|
|
|
vs->fidp = lookup_fid(s, fid);
|
|
|
|
if (vs->fidp == NULL) {
|
|
|
|
err = -EINVAL;
|
|
|
|
goto out;
|
2010-04-29 16:14:44 +04:00
|
|
|
}
|
2010-04-29 16:14:59 +04:00
|
|
|
|
|
|
|
v9fs_string_sprintf(&vs->fullname, "%s/%s", vs->fidp->path.data,
|
|
|
|
vs->name.data);
|
|
|
|
|
|
|
|
err = v9fs_do_lstat(s, &vs->fullname, &vs->stbuf);
|
|
|
|
v9fs_create_post_lstat(s, vs, err);
|
|
|
|
return;
|
|
|
|
|
|
|
|
out:
|
|
|
|
complete_pdu(s, vs->pdu, err);
|
|
|
|
v9fs_string_free(&vs->name);
|
|
|
|
v9fs_string_free(&vs->extension);
|
|
|
|
qemu_free(vs);
|
2010-04-29 16:14:44 +04:00
|
|
|
}
|
|
|
|
|
2010-06-10 01:02:08 +04:00
|
|
|
static void v9fs_post_symlink(V9fsState *s, V9fsSymlinkState *vs, int err)
|
|
|
|
{
|
|
|
|
if (err == 0) {
|
|
|
|
stat_to_qid(&vs->stbuf, &vs->qid);
|
|
|
|
vs->offset += pdu_marshal(vs->pdu, vs->offset, "Q", &vs->qid);
|
|
|
|
err = vs->offset;
|
|
|
|
} else {
|
|
|
|
err = -errno;
|
|
|
|
}
|
|
|
|
complete_pdu(s, vs->pdu, err);
|
|
|
|
v9fs_string_free(&vs->name);
|
|
|
|
v9fs_string_free(&vs->symname);
|
|
|
|
v9fs_string_free(&vs->fullname);
|
|
|
|
qemu_free(vs);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void v9fs_symlink_post_do_symlink(V9fsState *s, V9fsSymlinkState *vs,
|
|
|
|
int err)
|
|
|
|
{
|
|
|
|
if (err) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
err = v9fs_do_lstat(s, &vs->fullname, &vs->stbuf);
|
|
|
|
out:
|
|
|
|
v9fs_post_symlink(s, vs, err);
|
|
|
|
}
|
|
|
|
|
2011-05-19 01:18:05 +04:00
|
|
|
static void v9fs_symlink(void *opaque)
|
2010-06-10 01:02:08 +04:00
|
|
|
{
|
2011-05-19 01:18:05 +04:00
|
|
|
V9fsPDU *pdu = opaque;
|
|
|
|
V9fsState *s = pdu->s;
|
2010-06-10 01:02:08 +04:00
|
|
|
int32_t dfid;
|
|
|
|
V9fsSymlinkState *vs;
|
|
|
|
int err = 0;
|
|
|
|
gid_t gid;
|
|
|
|
|
|
|
|
vs = qemu_malloc(sizeof(*vs));
|
|
|
|
vs->pdu = pdu;
|
|
|
|
vs->offset = 7;
|
|
|
|
|
|
|
|
v9fs_string_init(&vs->fullname);
|
|
|
|
|
|
|
|
pdu_unmarshal(vs->pdu, vs->offset, "dssd", &dfid, &vs->name,
|
|
|
|
&vs->symname, &gid);
|
|
|
|
|
|
|
|
vs->dfidp = lookup_fid(s, dfid);
|
|
|
|
if (vs->dfidp == NULL) {
|
|
|
|
err = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
v9fs_string_sprintf(&vs->fullname, "%s/%s", vs->dfidp->path.data,
|
|
|
|
vs->name.data);
|
|
|
|
err = v9fs_do_symlink(s, vs->dfidp, vs->symname.data,
|
|
|
|
vs->fullname.data, gid);
|
|
|
|
v9fs_symlink_post_do_symlink(s, vs, err);
|
|
|
|
return;
|
|
|
|
|
|
|
|
out:
|
|
|
|
complete_pdu(s, vs->pdu, err);
|
|
|
|
v9fs_string_free(&vs->name);
|
|
|
|
v9fs_string_free(&vs->symname);
|
|
|
|
qemu_free(vs);
|
|
|
|
}
|
|
|
|
|
2011-05-19 01:18:05 +04:00
|
|
|
static void v9fs_flush(void *opaque)
|
2010-04-29 16:14:44 +04:00
|
|
|
{
|
2011-05-19 01:18:05 +04:00
|
|
|
V9fsPDU *pdu = opaque;
|
|
|
|
V9fsState *s = pdu->s;
|
2010-04-29 16:15:02 +04:00
|
|
|
/* A nop call with no return */
|
|
|
|
complete_pdu(s, pdu, 7);
|
2011-05-19 01:18:05 +04:00
|
|
|
return;
|
2010-04-29 16:14:44 +04:00
|
|
|
}
|
|
|
|
|
2011-05-19 01:18:05 +04:00
|
|
|
static void v9fs_link(void *opaque)
|
2010-06-09 22:21:15 +04:00
|
|
|
{
|
2011-05-19 01:18:05 +04:00
|
|
|
V9fsPDU *pdu = opaque;
|
|
|
|
V9fsState *s = pdu->s;
|
2010-06-09 22:21:15 +04:00
|
|
|
int32_t dfid, oldfid;
|
|
|
|
V9fsFidState *dfidp, *oldfidp;
|
|
|
|
V9fsString name, fullname;
|
|
|
|
size_t offset = 7;
|
|
|
|
int err = 0;
|
|
|
|
|
|
|
|
v9fs_string_init(&fullname);
|
|
|
|
|
|
|
|
pdu_unmarshal(pdu, offset, "dds", &dfid, &oldfid, &name);
|
|
|
|
|
|
|
|
dfidp = lookup_fid(s, dfid);
|
|
|
|
if (dfidp == NULL) {
|
|
|
|
err = -errno;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
oldfidp = lookup_fid(s, oldfid);
|
|
|
|
if (oldfidp == NULL) {
|
|
|
|
err = -errno;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
v9fs_string_sprintf(&fullname, "%s/%s", dfidp->path.data, name.data);
|
|
|
|
err = offset;
|
|
|
|
err = v9fs_do_link(s, &oldfidp->path, &fullname);
|
|
|
|
if (err) {
|
|
|
|
err = -errno;
|
|
|
|
}
|
|
|
|
v9fs_string_free(&fullname);
|
|
|
|
|
|
|
|
out:
|
|
|
|
v9fs_string_free(&name);
|
|
|
|
complete_pdu(s, pdu, err);
|
|
|
|
}
|
|
|
|
|
2010-04-29 16:15:01 +04:00
|
|
|
static void v9fs_remove_post_remove(V9fsState *s, V9fsRemoveState *vs,
|
|
|
|
int err)
|
|
|
|
{
|
|
|
|
if (err < 0) {
|
2010-06-09 13:03:22 +04:00
|
|
|
err = -errno;
|
|
|
|
} else {
|
|
|
|
err = vs->offset;
|
2010-04-29 16:15:01 +04:00
|
|
|
}
|
|
|
|
|
2010-06-09 13:03:22 +04:00
|
|
|
/* For TREMOVE we need to clunk the fid even on failed remove */
|
|
|
|
free_fid(s, vs->fidp->fid);
|
|
|
|
|
2010-04-29 16:15:01 +04:00
|
|
|
complete_pdu(s, vs->pdu, err);
|
|
|
|
qemu_free(vs);
|
|
|
|
}
|
|
|
|
|
2011-05-19 01:18:05 +04:00
|
|
|
static void v9fs_remove(void *opaque)
|
2010-04-29 16:14:44 +04:00
|
|
|
{
|
2011-05-19 01:18:05 +04:00
|
|
|
V9fsPDU *pdu = opaque;
|
|
|
|
V9fsState *s = pdu->s;
|
2010-04-29 16:15:01 +04:00
|
|
|
int32_t fid;
|
|
|
|
V9fsRemoveState *vs;
|
|
|
|
int err = 0;
|
|
|
|
|
|
|
|
vs = qemu_malloc(sizeof(*vs));
|
|
|
|
vs->pdu = pdu;
|
|
|
|
vs->offset = 7;
|
|
|
|
|
|
|
|
pdu_unmarshal(vs->pdu, vs->offset, "d", &fid);
|
|
|
|
|
|
|
|
vs->fidp = lookup_fid(s, fid);
|
|
|
|
if (vs->fidp == NULL) {
|
|
|
|
err = -EINVAL;
|
|
|
|
goto out;
|
2010-04-29 16:14:44 +04:00
|
|
|
}
|
2010-04-29 16:15:01 +04:00
|
|
|
|
|
|
|
err = v9fs_do_remove(s, &vs->fidp->path);
|
|
|
|
v9fs_remove_post_remove(s, vs, err);
|
|
|
|
return;
|
|
|
|
|
|
|
|
out:
|
|
|
|
complete_pdu(s, pdu, err);
|
|
|
|
qemu_free(vs);
|
2010-04-29 16:14:44 +04:00
|
|
|
}
|
|
|
|
|
2010-04-29 16:15:00 +04:00
|
|
|
static void v9fs_wstat_post_truncate(V9fsState *s, V9fsWstatState *vs, int err)
|
|
|
|
{
|
|
|
|
if (err < 0) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = vs->offset;
|
|
|
|
|
|
|
|
out:
|
|
|
|
v9fs_stat_free(&vs->v9stat);
|
|
|
|
complete_pdu(s, vs->pdu, err);
|
|
|
|
qemu_free(vs);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void v9fs_wstat_post_rename(V9fsState *s, V9fsWstatState *vs, int err)
|
|
|
|
{
|
|
|
|
if (err < 0) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if (vs->v9stat.length != -1) {
|
|
|
|
if (v9fs_do_truncate(s, &vs->fidp->path, vs->v9stat.length) < 0) {
|
|
|
|
err = -errno;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
v9fs_wstat_post_truncate(s, vs, err);
|
|
|
|
return;
|
|
|
|
|
|
|
|
out:
|
|
|
|
v9fs_stat_free(&vs->v9stat);
|
|
|
|
complete_pdu(s, vs->pdu, err);
|
|
|
|
qemu_free(vs);
|
|
|
|
}
|
|
|
|
|
2010-06-22 10:59:41 +04:00
|
|
|
static int v9fs_complete_rename(V9fsState *s, V9fsRenameState *vs)
|
2010-04-29 16:15:00 +04:00
|
|
|
{
|
2010-06-22 10:59:41 +04:00
|
|
|
int err = 0;
|
|
|
|
char *old_name, *new_name;
|
|
|
|
char *end;
|
2010-04-29 16:15:00 +04:00
|
|
|
|
2010-06-22 10:59:41 +04:00
|
|
|
if (vs->newdirfid != -1) {
|
|
|
|
V9fsFidState *dirfidp;
|
|
|
|
dirfidp = lookup_fid(s, vs->newdirfid);
|
|
|
|
|
|
|
|
if (dirfidp == NULL) {
|
|
|
|
err = -ENOENT;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2010-09-02 09:39:06 +04:00
|
|
|
BUG_ON(dirfidp->fid_type != P9_FID_NONE);
|
2010-04-29 16:15:00 +04:00
|
|
|
|
2010-06-22 10:59:41 +04:00
|
|
|
new_name = qemu_mallocz(dirfidp->path.size + vs->name.size + 2);
|
|
|
|
|
|
|
|
strcpy(new_name, dirfidp->path.data);
|
|
|
|
strcat(new_name, "/");
|
|
|
|
strcat(new_name + dirfidp->path.size, vs->name.data);
|
|
|
|
} else {
|
2010-04-29 16:15:00 +04:00
|
|
|
old_name = vs->fidp->path.data;
|
|
|
|
end = strrchr(old_name, '/');
|
|
|
|
if (end) {
|
|
|
|
end++;
|
|
|
|
} else {
|
|
|
|
end = old_name;
|
|
|
|
}
|
2010-06-22 10:59:41 +04:00
|
|
|
new_name = qemu_mallocz(end - old_name + vs->name.size + 1);
|
2010-04-29 16:15:00 +04:00
|
|
|
|
2010-06-22 10:59:41 +04:00
|
|
|
strncat(new_name, old_name, end - old_name);
|
|
|
|
strncat(new_name + (end - old_name), vs->name.data, vs->name.size);
|
|
|
|
}
|
2010-04-29 16:15:00 +04:00
|
|
|
|
2010-06-22 10:59:41 +04:00
|
|
|
v9fs_string_free(&vs->name);
|
|
|
|
vs->name.data = qemu_strdup(new_name);
|
|
|
|
vs->name.size = strlen(new_name);
|
2010-04-29 16:15:00 +04:00
|
|
|
|
2010-06-22 10:59:41 +04:00
|
|
|
if (strcmp(new_name, vs->fidp->path.data) != 0) {
|
|
|
|
if (v9fs_do_rename(s, &vs->fidp->path, &vs->name)) {
|
|
|
|
err = -errno;
|
|
|
|
} else {
|
|
|
|
V9fsFidState *fidp;
|
|
|
|
/*
|
|
|
|
* Fixup fid's pointing to the old name to
|
|
|
|
* start pointing to the new name
|
|
|
|
*/
|
|
|
|
for (fidp = s->fid_list; fidp; fidp = fidp->next) {
|
|
|
|
if (vs->fidp == fidp) {
|
|
|
|
/*
|
2011-06-01 11:05:11 +04:00
|
|
|
* we replace name of this fid towards the end so
|
|
|
|
* that our below v9fs_path_is_ancestor check will
|
|
|
|
* work
|
2010-06-22 10:59:41 +04:00
|
|
|
*/
|
|
|
|
continue;
|
|
|
|
}
|
2011-06-01 11:05:11 +04:00
|
|
|
if (v9fs_path_is_ancestor(&vs->fidp->path, &fidp->path)) {
|
2010-06-22 10:59:41 +04:00
|
|
|
/* replace the name */
|
|
|
|
v9fs_fix_path(&fidp->path, &vs->name,
|
|
|
|
strlen(vs->fidp->path.data));
|
2010-04-29 16:15:00 +04:00
|
|
|
}
|
|
|
|
}
|
2010-06-22 10:59:41 +04:00
|
|
|
v9fs_string_copy(&vs->fidp->path, &vs->name);
|
2010-04-29 16:15:00 +04:00
|
|
|
}
|
|
|
|
}
|
2010-06-22 10:59:41 +04:00
|
|
|
out:
|
|
|
|
v9fs_string_free(&vs->name);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void v9fs_rename_post_rename(V9fsState *s, V9fsRenameState *vs, int err)
|
|
|
|
{
|
|
|
|
complete_pdu(s, vs->pdu, err);
|
|
|
|
qemu_free(vs);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void v9fs_wstat_post_chown(V9fsState *s, V9fsWstatState *vs, int err)
|
|
|
|
{
|
|
|
|
if (err < 0) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (vs->v9stat.name.size != 0) {
|
|
|
|
V9fsRenameState *vr;
|
|
|
|
|
2010-09-02 09:39:07 +04:00
|
|
|
vr = qemu_mallocz(sizeof(V9fsRenameState));
|
2010-06-22 10:59:41 +04:00
|
|
|
vr->newdirfid = -1;
|
|
|
|
vr->pdu = vs->pdu;
|
|
|
|
vr->fidp = vs->fidp;
|
|
|
|
vr->offset = vs->offset;
|
|
|
|
vr->name.size = vs->v9stat.name.size;
|
|
|
|
vr->name.data = qemu_strdup(vs->v9stat.name.data);
|
|
|
|
|
|
|
|
err = v9fs_complete_rename(s, vr);
|
|
|
|
qemu_free(vr);
|
|
|
|
}
|
2010-04-29 16:15:00 +04:00
|
|
|
v9fs_wstat_post_rename(s, vs, err);
|
|
|
|
return;
|
|
|
|
|
|
|
|
out:
|
|
|
|
v9fs_stat_free(&vs->v9stat);
|
|
|
|
complete_pdu(s, vs->pdu, err);
|
|
|
|
qemu_free(vs);
|
|
|
|
}
|
|
|
|
|
2011-05-19 01:18:05 +04:00
|
|
|
static void v9fs_rename(void *opaque)
|
2010-06-22 10:59:41 +04:00
|
|
|
{
|
2011-05-19 01:18:05 +04:00
|
|
|
V9fsPDU *pdu = opaque;
|
|
|
|
V9fsState *s = pdu->s;
|
2010-06-22 10:59:41 +04:00
|
|
|
int32_t fid;
|
|
|
|
V9fsRenameState *vs;
|
|
|
|
ssize_t err = 0;
|
|
|
|
|
|
|
|
vs = qemu_malloc(sizeof(*vs));
|
|
|
|
vs->pdu = pdu;
|
|
|
|
vs->offset = 7;
|
|
|
|
|
|
|
|
pdu_unmarshal(vs->pdu, vs->offset, "dds", &fid, &vs->newdirfid, &vs->name);
|
|
|
|
|
|
|
|
vs->fidp = lookup_fid(s, fid);
|
|
|
|
if (vs->fidp == NULL) {
|
|
|
|
err = -ENOENT;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2010-09-02 09:39:06 +04:00
|
|
|
BUG_ON(vs->fidp->fid_type != P9_FID_NONE);
|
2010-06-22 10:59:41 +04:00
|
|
|
|
|
|
|
err = v9fs_complete_rename(s, vs);
|
|
|
|
v9fs_rename_post_rename(s, vs, err);
|
|
|
|
return;
|
|
|
|
out:
|
|
|
|
complete_pdu(s, vs->pdu, err);
|
|
|
|
qemu_free(vs);
|
|
|
|
}
|
|
|
|
|
2010-04-29 16:15:00 +04:00
|
|
|
static void v9fs_wstat_post_utime(V9fsState *s, V9fsWstatState *vs, int err)
|
|
|
|
{
|
|
|
|
if (err < 0) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2010-06-15 00:34:43 +04:00
|
|
|
if (vs->v9stat.n_gid != -1 || vs->v9stat.n_uid != -1) {
|
2010-04-29 16:15:00 +04:00
|
|
|
if (v9fs_do_chown(s, &vs->fidp->path, vs->v9stat.n_uid,
|
|
|
|
vs->v9stat.n_gid)) {
|
|
|
|
err = -errno;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
v9fs_wstat_post_chown(s, vs, err);
|
|
|
|
return;
|
|
|
|
|
|
|
|
out:
|
|
|
|
v9fs_stat_free(&vs->v9stat);
|
|
|
|
complete_pdu(s, vs->pdu, err);
|
|
|
|
qemu_free(vs);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void v9fs_wstat_post_chmod(V9fsState *s, V9fsWstatState *vs, int err)
|
|
|
|
{
|
|
|
|
if (err < 0) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2010-06-09 17:44:38 +04:00
|
|
|
if (vs->v9stat.mtime != -1 || vs->v9stat.atime != -1) {
|
2010-06-09 17:44:38 +04:00
|
|
|
struct timespec times[2];
|
|
|
|
if (vs->v9stat.atime != -1) {
|
|
|
|
times[0].tv_sec = vs->v9stat.atime;
|
|
|
|
times[0].tv_nsec = 0;
|
|
|
|
} else {
|
|
|
|
times[0].tv_nsec = UTIME_OMIT;
|
|
|
|
}
|
|
|
|
if (vs->v9stat.mtime != -1) {
|
|
|
|
times[1].tv_sec = vs->v9stat.mtime;
|
|
|
|
times[1].tv_nsec = 0;
|
|
|
|
} else {
|
|
|
|
times[1].tv_nsec = UTIME_OMIT;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (v9fs_do_utimensat(s, &vs->fidp->path, times)) {
|
2010-04-29 16:15:00 +04:00
|
|
|
err = -errno;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
v9fs_wstat_post_utime(s, vs, err);
|
|
|
|
return;
|
|
|
|
|
|
|
|
out:
|
|
|
|
v9fs_stat_free(&vs->v9stat);
|
|
|
|
complete_pdu(s, vs->pdu, err);
|
|
|
|
qemu_free(vs);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void v9fs_wstat_post_fsync(V9fsState *s, V9fsWstatState *vs, int err)
|
|
|
|
{
|
|
|
|
if (err == -1) {
|
|
|
|
err = -errno;
|
|
|
|
}
|
|
|
|
v9fs_stat_free(&vs->v9stat);
|
|
|
|
complete_pdu(s, vs->pdu, err);
|
|
|
|
qemu_free(vs);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void v9fs_wstat_post_lstat(V9fsState *s, V9fsWstatState *vs, int err)
|
|
|
|
{
|
|
|
|
uint32_t v9_mode;
|
|
|
|
|
|
|
|
if (err == -1) {
|
|
|
|
err = -errno;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
v9_mode = stat_to_v9mode(&vs->stbuf);
|
|
|
|
|
|
|
|
if ((vs->v9stat.mode & P9_STAT_MODE_TYPE_BITS) !=
|
|
|
|
(v9_mode & P9_STAT_MODE_TYPE_BITS)) {
|
|
|
|
/* Attempting to change the type */
|
|
|
|
err = -EIO;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (v9fs_do_chmod(s, &vs->fidp->path, v9mode_to_mode(vs->v9stat.mode,
|
|
|
|
&vs->v9stat.extension))) {
|
|
|
|
err = -errno;
|
|
|
|
}
|
|
|
|
v9fs_wstat_post_chmod(s, vs, err);
|
|
|
|
return;
|
|
|
|
|
|
|
|
out:
|
|
|
|
v9fs_stat_free(&vs->v9stat);
|
|
|
|
complete_pdu(s, vs->pdu, err);
|
|
|
|
qemu_free(vs);
|
|
|
|
}
|
|
|
|
|
2011-05-19 01:18:05 +04:00
|
|
|
static void v9fs_wstat(void *opaque)
|
2010-04-29 16:14:44 +04:00
|
|
|
{
|
2011-05-19 01:18:05 +04:00
|
|
|
V9fsPDU *pdu = opaque;
|
|
|
|
V9fsState *s = pdu->s;
|
2010-04-29 16:15:00 +04:00
|
|
|
int32_t fid;
|
|
|
|
V9fsWstatState *vs;
|
|
|
|
int err = 0;
|
|
|
|
|
|
|
|
vs = qemu_malloc(sizeof(*vs));
|
|
|
|
vs->pdu = pdu;
|
|
|
|
vs->offset = 7;
|
|
|
|
|
|
|
|
pdu_unmarshal(pdu, vs->offset, "dwS", &fid, &vs->unused, &vs->v9stat);
|
|
|
|
|
|
|
|
vs->fidp = lookup_fid(s, fid);
|
|
|
|
if (vs->fidp == NULL) {
|
|
|
|
err = -EINVAL;
|
|
|
|
goto out;
|
2010-04-29 16:14:44 +04:00
|
|
|
}
|
2010-04-29 16:15:00 +04:00
|
|
|
|
|
|
|
/* do we need to sync the file? */
|
|
|
|
if (donttouch_stat(&vs->v9stat)) {
|
2010-10-22 21:08:45 +04:00
|
|
|
err = v9fs_do_fsync(s, vs->fidp->fs.fd, 0);
|
2010-04-29 16:15:00 +04:00
|
|
|
v9fs_wstat_post_fsync(s, vs, err);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (vs->v9stat.mode != -1) {
|
|
|
|
err = v9fs_do_lstat(s, &vs->fidp->path, &vs->stbuf);
|
|
|
|
v9fs_wstat_post_lstat(s, vs, err);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
v9fs_wstat_post_chmod(s, vs, err);
|
|
|
|
return;
|
|
|
|
|
|
|
|
out:
|
|
|
|
v9fs_stat_free(&vs->v9stat);
|
|
|
|
complete_pdu(s, vs->pdu, err);
|
|
|
|
qemu_free(vs);
|
2010-04-29 16:14:44 +04:00
|
|
|
}
|
|
|
|
|
2010-05-10 10:41:03 +04:00
|
|
|
static void v9fs_statfs_post_statfs(V9fsState *s, V9fsStatfsState *vs, int err)
|
|
|
|
{
|
2010-06-09 17:44:28 +04:00
|
|
|
int32_t bsize_factor;
|
|
|
|
|
2010-05-10 10:41:03 +04:00
|
|
|
if (err) {
|
|
|
|
err = -errno;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2010-06-09 17:44:28 +04:00
|
|
|
/*
|
|
|
|
* compute bsize factor based on host file system block size
|
|
|
|
* and client msize
|
|
|
|
*/
|
|
|
|
bsize_factor = (s->msize - P9_IOHDRSZ)/vs->stbuf.f_bsize;
|
|
|
|
if (!bsize_factor) {
|
|
|
|
bsize_factor = 1;
|
|
|
|
}
|
2010-05-10 10:41:03 +04:00
|
|
|
vs->v9statfs.f_type = vs->stbuf.f_type;
|
|
|
|
vs->v9statfs.f_bsize = vs->stbuf.f_bsize;
|
2010-06-09 17:44:28 +04:00
|
|
|
vs->v9statfs.f_bsize *= bsize_factor;
|
|
|
|
/*
|
|
|
|
* f_bsize is adjusted(multiplied) by bsize factor, so we need to
|
|
|
|
* adjust(divide) the number of blocks, free blocks and available
|
|
|
|
* blocks by bsize factor
|
|
|
|
*/
|
|
|
|
vs->v9statfs.f_blocks = vs->stbuf.f_blocks/bsize_factor;
|
|
|
|
vs->v9statfs.f_bfree = vs->stbuf.f_bfree/bsize_factor;
|
|
|
|
vs->v9statfs.f_bavail = vs->stbuf.f_bavail/bsize_factor;
|
2010-05-10 10:41:03 +04:00
|
|
|
vs->v9statfs.f_files = vs->stbuf.f_files;
|
|
|
|
vs->v9statfs.f_ffree = vs->stbuf.f_ffree;
|
|
|
|
vs->v9statfs.fsid_val = (unsigned int) vs->stbuf.f_fsid.__val[0] |
|
|
|
|
(unsigned long long)vs->stbuf.f_fsid.__val[1] << 32;
|
|
|
|
vs->v9statfs.f_namelen = vs->stbuf.f_namelen;
|
|
|
|
|
|
|
|
vs->offset += pdu_marshal(vs->pdu, vs->offset, "ddqqqqqqd",
|
|
|
|
vs->v9statfs.f_type, vs->v9statfs.f_bsize, vs->v9statfs.f_blocks,
|
|
|
|
vs->v9statfs.f_bfree, vs->v9statfs.f_bavail, vs->v9statfs.f_files,
|
|
|
|
vs->v9statfs.f_ffree, vs->v9statfs.fsid_val,
|
|
|
|
vs->v9statfs.f_namelen);
|
|
|
|
|
|
|
|
out:
|
|
|
|
complete_pdu(s, vs->pdu, vs->offset);
|
|
|
|
qemu_free(vs);
|
|
|
|
}
|
|
|
|
|
2011-05-19 01:18:05 +04:00
|
|
|
static void v9fs_statfs(void *opaque)
|
2010-05-10 10:41:03 +04:00
|
|
|
{
|
2011-05-19 01:18:05 +04:00
|
|
|
V9fsPDU *pdu = opaque;
|
|
|
|
V9fsState *s = pdu->s;
|
2010-05-10 10:41:03 +04:00
|
|
|
V9fsStatfsState *vs;
|
|
|
|
ssize_t err = 0;
|
|
|
|
|
|
|
|
vs = qemu_malloc(sizeof(*vs));
|
|
|
|
vs->pdu = pdu;
|
|
|
|
vs->offset = 7;
|
|
|
|
|
|
|
|
memset(&vs->v9statfs, 0, sizeof(vs->v9statfs));
|
|
|
|
|
|
|
|
pdu_unmarshal(vs->pdu, vs->offset, "d", &vs->fid);
|
|
|
|
|
|
|
|
vs->fidp = lookup_fid(s, vs->fid);
|
|
|
|
if (vs->fidp == NULL) {
|
|
|
|
err = -ENOENT;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = v9fs_do_statfs(s, &vs->fidp->path, &vs->stbuf);
|
|
|
|
v9fs_statfs_post_statfs(s, vs, err);
|
|
|
|
return;
|
|
|
|
|
|
|
|
out:
|
|
|
|
complete_pdu(s, vs->pdu, err);
|
|
|
|
qemu_free(vs);
|
2011-05-19 01:18:05 +04:00
|
|
|
return;
|
2010-05-10 10:41:03 +04:00
|
|
|
}
|
|
|
|
|
2010-06-22 10:54:09 +04:00
|
|
|
static void v9fs_mknod_post_lstat(V9fsState *s, V9fsMkState *vs, int err)
|
|
|
|
{
|
|
|
|
if (err == -1) {
|
|
|
|
err = -errno;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
stat_to_qid(&vs->stbuf, &vs->qid);
|
|
|
|
vs->offset += pdu_marshal(vs->pdu, vs->offset, "Q", &vs->qid);
|
|
|
|
err = vs->offset;
|
|
|
|
out:
|
|
|
|
complete_pdu(s, vs->pdu, err);
|
|
|
|
v9fs_string_free(&vs->fullname);
|
|
|
|
v9fs_string_free(&vs->name);
|
|
|
|
qemu_free(vs);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void v9fs_mknod_post_mknod(V9fsState *s, V9fsMkState *vs, int err)
|
|
|
|
{
|
|
|
|
if (err == -1) {
|
|
|
|
err = -errno;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = v9fs_do_lstat(s, &vs->fullname, &vs->stbuf);
|
|
|
|
v9fs_mknod_post_lstat(s, vs, err);
|
|
|
|
return;
|
|
|
|
out:
|
|
|
|
complete_pdu(s, vs->pdu, err);
|
|
|
|
v9fs_string_free(&vs->fullname);
|
|
|
|
v9fs_string_free(&vs->name);
|
|
|
|
qemu_free(vs);
|
|
|
|
}
|
|
|
|
|
2011-05-19 01:18:05 +04:00
|
|
|
static void v9fs_mknod(void *opaque)
|
2010-06-22 10:54:09 +04:00
|
|
|
{
|
2011-05-19 01:18:05 +04:00
|
|
|
V9fsPDU *pdu = opaque;
|
|
|
|
V9fsState *s = pdu->s;
|
2010-06-22 10:54:09 +04:00
|
|
|
int32_t fid;
|
|
|
|
V9fsMkState *vs;
|
|
|
|
int err = 0;
|
|
|
|
V9fsFidState *fidp;
|
|
|
|
gid_t gid;
|
|
|
|
int mode;
|
|
|
|
int major, minor;
|
|
|
|
|
|
|
|
vs = qemu_malloc(sizeof(*vs));
|
|
|
|
vs->pdu = pdu;
|
|
|
|
vs->offset = 7;
|
|
|
|
|
|
|
|
v9fs_string_init(&vs->fullname);
|
|
|
|
pdu_unmarshal(vs->pdu, vs->offset, "dsdddd", &fid, &vs->name, &mode,
|
|
|
|
&major, &minor, &gid);
|
|
|
|
|
|
|
|
fidp = lookup_fid(s, fid);
|
|
|
|
if (fidp == NULL) {
|
|
|
|
err = -ENOENT;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
v9fs_string_sprintf(&vs->fullname, "%s/%s", fidp->path.data, vs->name.data);
|
|
|
|
err = v9fs_do_mknod(s, vs->fullname.data, mode, makedev(major, minor),
|
|
|
|
fidp->uid, gid);
|
|
|
|
v9fs_mknod_post_mknod(s, vs, err);
|
|
|
|
return;
|
|
|
|
|
|
|
|
out:
|
|
|
|
complete_pdu(s, vs->pdu, err);
|
|
|
|
v9fs_string_free(&vs->fullname);
|
|
|
|
v9fs_string_free(&vs->name);
|
|
|
|
qemu_free(vs);
|
|
|
|
}
|
|
|
|
|
[virto-9p] Implement TLOCK
Synopsis
size[4] TLock tag[2] fid[4] flock[n]
size[4] RLock tag[2] status[1]
Description
Tlock is used to acquire/release byte range posix locks on a file
identified by given fid. The reply contains status of the lock request
flock structure:
type[1] - Type of lock: F_RDLCK, F_WRLCK, F_UNLCK
flags[4] - Flags could be either of
P9_LOCK_FLAGS_BLOCK(1) - Blocked lock request, if there is a
conflicting lock exists, wait for that lock to be released.
P9_LOCK_FLAGS_RECLAIM(2) - Reclaim lock request, used when client is
trying to reclaim a lock after a server restrart (due to crash)
start[8] - Starting offset for lock
length[8] - Number of bytes to lock
If length is 0, lock all bytes starting at the location 'start'
through to the end of file
pid[4] - PID of the process that wants to take lock
client_id[4] - Unique client id
status[1] - Status of the lock request, can be
P9_LOCK_SUCCESS(0), P9_LOCK_BLOCKED(1), P9_LOCK_ERROR(2) or
P9_LOCK_GRACE(3)
P9_LOCK_SUCCESS - Request was successful
P9_LOCK_BLOCKED - A conflicting lock is held by another process
P9_LOCK_ERROR - Error while processing the lock request
P9_LOCK_GRACE - Server is in grace period, it can't accept new lock
requests in this period (except locks with
P9_LOCK_FLAGS_RECLAIM flag set)
Signed-off-by: M. Mohan Kumar <mohan@in.ibm.com>
Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
Signed-off-by: Venkateswararao Jujjuri <jvrao@linux.vnet.ibm.com>
2010-09-08 00:49:32 +04:00
|
|
|
/*
|
|
|
|
* Implement posix byte range locking code
|
|
|
|
* Server side handling of locking code is very simple, because 9p server in
|
|
|
|
* QEMU can handle only one client. And most of the lock handling
|
|
|
|
* (like conflict, merging) etc is done by the VFS layer itself, so no need to
|
|
|
|
* do any thing in * qemu 9p server side lock code path.
|
|
|
|
* So when a TLOCK request comes, always return success
|
|
|
|
*/
|
|
|
|
|
2011-05-19 01:18:05 +04:00
|
|
|
static void v9fs_lock(void *opaque)
|
[virto-9p] Implement TLOCK
Synopsis
size[4] TLock tag[2] fid[4] flock[n]
size[4] RLock tag[2] status[1]
Description
Tlock is used to acquire/release byte range posix locks on a file
identified by given fid. The reply contains status of the lock request
flock structure:
type[1] - Type of lock: F_RDLCK, F_WRLCK, F_UNLCK
flags[4] - Flags could be either of
P9_LOCK_FLAGS_BLOCK(1) - Blocked lock request, if there is a
conflicting lock exists, wait for that lock to be released.
P9_LOCK_FLAGS_RECLAIM(2) - Reclaim lock request, used when client is
trying to reclaim a lock after a server restrart (due to crash)
start[8] - Starting offset for lock
length[8] - Number of bytes to lock
If length is 0, lock all bytes starting at the location 'start'
through to the end of file
pid[4] - PID of the process that wants to take lock
client_id[4] - Unique client id
status[1] - Status of the lock request, can be
P9_LOCK_SUCCESS(0), P9_LOCK_BLOCKED(1), P9_LOCK_ERROR(2) or
P9_LOCK_GRACE(3)
P9_LOCK_SUCCESS - Request was successful
P9_LOCK_BLOCKED - A conflicting lock is held by another process
P9_LOCK_ERROR - Error while processing the lock request
P9_LOCK_GRACE - Server is in grace period, it can't accept new lock
requests in this period (except locks with
P9_LOCK_FLAGS_RECLAIM flag set)
Signed-off-by: M. Mohan Kumar <mohan@in.ibm.com>
Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
Signed-off-by: Venkateswararao Jujjuri <jvrao@linux.vnet.ibm.com>
2010-09-08 00:49:32 +04:00
|
|
|
{
|
2011-05-19 01:18:05 +04:00
|
|
|
V9fsPDU *pdu = opaque;
|
|
|
|
V9fsState *s = pdu->s;
|
[virto-9p] Implement TLOCK
Synopsis
size[4] TLock tag[2] fid[4] flock[n]
size[4] RLock tag[2] status[1]
Description
Tlock is used to acquire/release byte range posix locks on a file
identified by given fid. The reply contains status of the lock request
flock structure:
type[1] - Type of lock: F_RDLCK, F_WRLCK, F_UNLCK
flags[4] - Flags could be either of
P9_LOCK_FLAGS_BLOCK(1) - Blocked lock request, if there is a
conflicting lock exists, wait for that lock to be released.
P9_LOCK_FLAGS_RECLAIM(2) - Reclaim lock request, used when client is
trying to reclaim a lock after a server restrart (due to crash)
start[8] - Starting offset for lock
length[8] - Number of bytes to lock
If length is 0, lock all bytes starting at the location 'start'
through to the end of file
pid[4] - PID of the process that wants to take lock
client_id[4] - Unique client id
status[1] - Status of the lock request, can be
P9_LOCK_SUCCESS(0), P9_LOCK_BLOCKED(1), P9_LOCK_ERROR(2) or
P9_LOCK_GRACE(3)
P9_LOCK_SUCCESS - Request was successful
P9_LOCK_BLOCKED - A conflicting lock is held by another process
P9_LOCK_ERROR - Error while processing the lock request
P9_LOCK_GRACE - Server is in grace period, it can't accept new lock
requests in this period (except locks with
P9_LOCK_FLAGS_RECLAIM flag set)
Signed-off-by: M. Mohan Kumar <mohan@in.ibm.com>
Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
Signed-off-by: Venkateswararao Jujjuri <jvrao@linux.vnet.ibm.com>
2010-09-08 00:49:32 +04:00
|
|
|
int32_t fid, err = 0;
|
|
|
|
V9fsLockState *vs;
|
|
|
|
|
|
|
|
vs = qemu_mallocz(sizeof(*vs));
|
|
|
|
vs->pdu = pdu;
|
|
|
|
vs->offset = 7;
|
|
|
|
|
|
|
|
vs->flock = qemu_malloc(sizeof(*vs->flock));
|
|
|
|
pdu_unmarshal(vs->pdu, vs->offset, "dbdqqds", &fid, &vs->flock->type,
|
|
|
|
&vs->flock->flags, &vs->flock->start, &vs->flock->length,
|
|
|
|
&vs->flock->proc_id, &vs->flock->client_id);
|
|
|
|
|
|
|
|
vs->status = P9_LOCK_ERROR;
|
|
|
|
|
|
|
|
/* We support only block flag now (that too ignored currently) */
|
|
|
|
if (vs->flock->flags & ~P9_LOCK_FLAGS_BLOCK) {
|
|
|
|
err = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
vs->fidp = lookup_fid(s, fid);
|
|
|
|
if (vs->fidp == NULL) {
|
|
|
|
err = -ENOENT;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = v9fs_do_fstat(s, vs->fidp->fs.fd, &vs->stbuf);
|
|
|
|
if (err < 0) {
|
|
|
|
err = -errno;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
vs->status = P9_LOCK_SUCCESS;
|
|
|
|
out:
|
|
|
|
vs->offset += pdu_marshal(vs->pdu, vs->offset, "b", vs->status);
|
|
|
|
complete_pdu(s, vs->pdu, err);
|
|
|
|
qemu_free(vs->flock);
|
|
|
|
qemu_free(vs);
|
|
|
|
}
|
|
|
|
|
2010-09-08 01:06:52 +04:00
|
|
|
/*
|
|
|
|
* When a TGETLOCK request comes, always return success because all lock
|
|
|
|
* handling is done by client's VFS layer.
|
|
|
|
*/
|
|
|
|
|
2011-05-19 01:18:05 +04:00
|
|
|
static void v9fs_getlock(void *opaque)
|
2010-09-08 01:06:52 +04:00
|
|
|
{
|
2011-05-19 01:18:05 +04:00
|
|
|
V9fsPDU *pdu = opaque;
|
|
|
|
V9fsState *s = pdu->s;
|
2010-09-08 01:06:52 +04:00
|
|
|
int32_t fid, err = 0;
|
|
|
|
V9fsGetlockState *vs;
|
|
|
|
|
|
|
|
vs = qemu_mallocz(sizeof(*vs));
|
|
|
|
vs->pdu = pdu;
|
|
|
|
vs->offset = 7;
|
|
|
|
|
|
|
|
vs->glock = qemu_malloc(sizeof(*vs->glock));
|
|
|
|
pdu_unmarshal(vs->pdu, vs->offset, "dbqqds", &fid, &vs->glock->type,
|
|
|
|
&vs->glock->start, &vs->glock->length, &vs->glock->proc_id,
|
|
|
|
&vs->glock->client_id);
|
|
|
|
|
|
|
|
vs->fidp = lookup_fid(s, fid);
|
|
|
|
if (vs->fidp == NULL) {
|
|
|
|
err = -ENOENT;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = v9fs_do_fstat(s, vs->fidp->fs.fd, &vs->stbuf);
|
|
|
|
if (err < 0) {
|
|
|
|
err = -errno;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
vs->glock->type = F_UNLCK;
|
|
|
|
vs->offset += pdu_marshal(vs->pdu, vs->offset, "bqqds", vs->glock->type,
|
|
|
|
vs->glock->start, vs->glock->length, vs->glock->proc_id,
|
|
|
|
&vs->glock->client_id);
|
|
|
|
out:
|
|
|
|
complete_pdu(s, vs->pdu, err);
|
|
|
|
qemu_free(vs->glock);
|
|
|
|
qemu_free(vs);
|
|
|
|
}
|
|
|
|
|
2010-06-22 10:55:22 +04:00
|
|
|
static void v9fs_mkdir_post_lstat(V9fsState *s, V9fsMkState *vs, int err)
|
|
|
|
{
|
|
|
|
if (err == -1) {
|
|
|
|
err = -errno;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
stat_to_qid(&vs->stbuf, &vs->qid);
|
|
|
|
vs->offset += pdu_marshal(vs->pdu, vs->offset, "Q", &vs->qid);
|
|
|
|
err = vs->offset;
|
|
|
|
out:
|
|
|
|
complete_pdu(s, vs->pdu, err);
|
|
|
|
v9fs_string_free(&vs->fullname);
|
|
|
|
v9fs_string_free(&vs->name);
|
|
|
|
qemu_free(vs);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void v9fs_mkdir_post_mkdir(V9fsState *s, V9fsMkState *vs, int err)
|
|
|
|
{
|
|
|
|
if (err == -1) {
|
|
|
|
err = -errno;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = v9fs_do_lstat(s, &vs->fullname, &vs->stbuf);
|
|
|
|
v9fs_mkdir_post_lstat(s, vs, err);
|
|
|
|
return;
|
|
|
|
out:
|
|
|
|
complete_pdu(s, vs->pdu, err);
|
|
|
|
v9fs_string_free(&vs->fullname);
|
|
|
|
v9fs_string_free(&vs->name);
|
|
|
|
qemu_free(vs);
|
|
|
|
}
|
|
|
|
|
2011-05-19 01:18:05 +04:00
|
|
|
static void v9fs_mkdir(void *opaque)
|
2010-06-22 10:55:22 +04:00
|
|
|
{
|
2011-05-19 01:18:05 +04:00
|
|
|
V9fsPDU *pdu = opaque;
|
|
|
|
V9fsState *s = pdu->s;
|
2010-06-22 10:55:22 +04:00
|
|
|
int32_t fid;
|
|
|
|
V9fsMkState *vs;
|
|
|
|
int err = 0;
|
|
|
|
V9fsFidState *fidp;
|
|
|
|
gid_t gid;
|
|
|
|
int mode;
|
|
|
|
|
|
|
|
vs = qemu_malloc(sizeof(*vs));
|
|
|
|
vs->pdu = pdu;
|
|
|
|
vs->offset = 7;
|
|
|
|
|
|
|
|
v9fs_string_init(&vs->fullname);
|
|
|
|
pdu_unmarshal(vs->pdu, vs->offset, "dsdd", &fid, &vs->name, &mode,
|
|
|
|
&gid);
|
|
|
|
|
|
|
|
fidp = lookup_fid(s, fid);
|
|
|
|
if (fidp == NULL) {
|
|
|
|
err = -ENOENT;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
v9fs_string_sprintf(&vs->fullname, "%s/%s", fidp->path.data, vs->name.data);
|
|
|
|
err = v9fs_do_mkdir(s, vs->fullname.data, mode, fidp->uid, gid);
|
|
|
|
v9fs_mkdir_post_mkdir(s, vs, err);
|
|
|
|
return;
|
|
|
|
|
|
|
|
out:
|
|
|
|
complete_pdu(s, vs->pdu, err);
|
|
|
|
v9fs_string_free(&vs->fullname);
|
|
|
|
v9fs_string_free(&vs->name);
|
|
|
|
qemu_free(vs);
|
|
|
|
}
|
|
|
|
|
2010-09-02 09:39:06 +04:00
|
|
|
static void v9fs_post_xattr_getvalue(V9fsState *s, V9fsXattrState *vs, int err)
|
|
|
|
{
|
|
|
|
|
|
|
|
if (err < 0) {
|
|
|
|
err = -errno;
|
|
|
|
free_fid(s, vs->xattr_fidp->fid);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
vs->offset += pdu_marshal(vs->pdu, vs->offset, "q", vs->size);
|
|
|
|
err = vs->offset;
|
|
|
|
out:
|
|
|
|
complete_pdu(s, vs->pdu, err);
|
|
|
|
v9fs_string_free(&vs->name);
|
|
|
|
qemu_free(vs);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void v9fs_post_xattr_check(V9fsState *s, V9fsXattrState *vs, ssize_t err)
|
|
|
|
{
|
|
|
|
if (err < 0) {
|
|
|
|
err = -errno;
|
|
|
|
free_fid(s, vs->xattr_fidp->fid);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Read the xattr value
|
|
|
|
*/
|
|
|
|
vs->xattr_fidp->fs.xattr.len = vs->size;
|
|
|
|
vs->xattr_fidp->fid_type = P9_FID_XATTR;
|
|
|
|
vs->xattr_fidp->fs.xattr.copied_len = -1;
|
|
|
|
if (vs->size) {
|
|
|
|
vs->xattr_fidp->fs.xattr.value = qemu_malloc(vs->size);
|
|
|
|
err = v9fs_do_lgetxattr(s, &vs->xattr_fidp->path,
|
|
|
|
&vs->name, vs->xattr_fidp->fs.xattr.value,
|
|
|
|
vs->xattr_fidp->fs.xattr.len);
|
|
|
|
}
|
|
|
|
v9fs_post_xattr_getvalue(s, vs, err);
|
|
|
|
return;
|
|
|
|
out:
|
|
|
|
complete_pdu(s, vs->pdu, err);
|
|
|
|
v9fs_string_free(&vs->name);
|
|
|
|
qemu_free(vs);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void v9fs_post_lxattr_getvalue(V9fsState *s,
|
|
|
|
V9fsXattrState *vs, int err)
|
|
|
|
{
|
|
|
|
if (err < 0) {
|
|
|
|
err = -errno;
|
|
|
|
free_fid(s, vs->xattr_fidp->fid);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
vs->offset += pdu_marshal(vs->pdu, vs->offset, "q", vs->size);
|
|
|
|
err = vs->offset;
|
|
|
|
out:
|
|
|
|
complete_pdu(s, vs->pdu, err);
|
|
|
|
v9fs_string_free(&vs->name);
|
|
|
|
qemu_free(vs);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void v9fs_post_lxattr_check(V9fsState *s,
|
|
|
|
V9fsXattrState *vs, ssize_t err)
|
|
|
|
{
|
|
|
|
if (err < 0) {
|
|
|
|
err = -errno;
|
|
|
|
free_fid(s, vs->xattr_fidp->fid);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Read the xattr value
|
|
|
|
*/
|
|
|
|
vs->xattr_fidp->fs.xattr.len = vs->size;
|
|
|
|
vs->xattr_fidp->fid_type = P9_FID_XATTR;
|
|
|
|
vs->xattr_fidp->fs.xattr.copied_len = -1;
|
|
|
|
if (vs->size) {
|
|
|
|
vs->xattr_fidp->fs.xattr.value = qemu_malloc(vs->size);
|
|
|
|
err = v9fs_do_llistxattr(s, &vs->xattr_fidp->path,
|
|
|
|
vs->xattr_fidp->fs.xattr.value,
|
|
|
|
vs->xattr_fidp->fs.xattr.len);
|
|
|
|
}
|
|
|
|
v9fs_post_lxattr_getvalue(s, vs, err);
|
|
|
|
return;
|
|
|
|
out:
|
|
|
|
complete_pdu(s, vs->pdu, err);
|
|
|
|
v9fs_string_free(&vs->name);
|
|
|
|
qemu_free(vs);
|
|
|
|
}
|
|
|
|
|
2011-05-19 01:18:05 +04:00
|
|
|
static void v9fs_xattrwalk(void *opaque)
|
2010-09-02 09:39:06 +04:00
|
|
|
{
|
2011-05-19 01:18:05 +04:00
|
|
|
V9fsPDU *pdu = opaque;
|
|
|
|
V9fsState *s = pdu->s;
|
2010-09-02 09:39:06 +04:00
|
|
|
ssize_t err = 0;
|
|
|
|
V9fsXattrState *vs;
|
|
|
|
int32_t fid, newfid;
|
|
|
|
|
|
|
|
vs = qemu_malloc(sizeof(*vs));
|
|
|
|
vs->pdu = pdu;
|
|
|
|
vs->offset = 7;
|
|
|
|
|
|
|
|
pdu_unmarshal(vs->pdu, vs->offset, "dds", &fid, &newfid, &vs->name);
|
|
|
|
vs->file_fidp = lookup_fid(s, fid);
|
|
|
|
if (vs->file_fidp == NULL) {
|
|
|
|
err = -ENOENT;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
vs->xattr_fidp = alloc_fid(s, newfid);
|
|
|
|
if (vs->xattr_fidp == NULL) {
|
|
|
|
err = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
v9fs_string_copy(&vs->xattr_fidp->path, &vs->file_fidp->path);
|
|
|
|
if (vs->name.data[0] == 0) {
|
|
|
|
/*
|
|
|
|
* listxattr request. Get the size first
|
|
|
|
*/
|
|
|
|
vs->size = v9fs_do_llistxattr(s, &vs->xattr_fidp->path,
|
|
|
|
NULL, 0);
|
|
|
|
if (vs->size < 0) {
|
|
|
|
err = vs->size;
|
|
|
|
}
|
|
|
|
v9fs_post_lxattr_check(s, vs, err);
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* specific xattr fid. We check for xattr
|
|
|
|
* presence also collect the xattr size
|
|
|
|
*/
|
|
|
|
vs->size = v9fs_do_lgetxattr(s, &vs->xattr_fidp->path,
|
|
|
|
&vs->name, NULL, 0);
|
|
|
|
if (vs->size < 0) {
|
|
|
|
err = vs->size;
|
|
|
|
}
|
|
|
|
v9fs_post_xattr_check(s, vs, err);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
out:
|
|
|
|
complete_pdu(s, vs->pdu, err);
|
|
|
|
v9fs_string_free(&vs->name);
|
|
|
|
qemu_free(vs);
|
|
|
|
}
|
|
|
|
|
2011-05-19 01:18:05 +04:00
|
|
|
static void v9fs_xattrcreate(void *opaque)
|
2010-09-02 09:39:07 +04:00
|
|
|
{
|
2011-05-19 01:18:05 +04:00
|
|
|
V9fsPDU *pdu = opaque;
|
|
|
|
V9fsState *s = pdu->s;
|
2010-09-02 09:39:07 +04:00
|
|
|
int flags;
|
|
|
|
int32_t fid;
|
|
|
|
ssize_t err = 0;
|
|
|
|
V9fsXattrState *vs;
|
|
|
|
|
|
|
|
vs = qemu_malloc(sizeof(*vs));
|
|
|
|
vs->pdu = pdu;
|
|
|
|
vs->offset = 7;
|
|
|
|
|
|
|
|
pdu_unmarshal(vs->pdu, vs->offset, "dsqd",
|
|
|
|
&fid, &vs->name, &vs->size, &flags);
|
|
|
|
|
|
|
|
vs->file_fidp = lookup_fid(s, fid);
|
|
|
|
if (vs->file_fidp == NULL) {
|
|
|
|
err = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Make the file fid point to xattr */
|
|
|
|
vs->xattr_fidp = vs->file_fidp;
|
|
|
|
vs->xattr_fidp->fid_type = P9_FID_XATTR;
|
|
|
|
vs->xattr_fidp->fs.xattr.copied_len = 0;
|
|
|
|
vs->xattr_fidp->fs.xattr.len = vs->size;
|
|
|
|
vs->xattr_fidp->fs.xattr.flags = flags;
|
|
|
|
v9fs_string_init(&vs->xattr_fidp->fs.xattr.name);
|
|
|
|
v9fs_string_copy(&vs->xattr_fidp->fs.xattr.name, &vs->name);
|
2010-08-26 09:45:23 +04:00
|
|
|
if (vs->size)
|
|
|
|
vs->xattr_fidp->fs.xattr.value = qemu_malloc(vs->size);
|
|
|
|
else
|
|
|
|
vs->xattr_fidp->fs.xattr.value = NULL;
|
2010-09-02 09:39:07 +04:00
|
|
|
|
|
|
|
out:
|
|
|
|
complete_pdu(s, vs->pdu, err);
|
|
|
|
v9fs_string_free(&vs->name);
|
|
|
|
qemu_free(vs);
|
|
|
|
}
|
2010-09-02 09:39:06 +04:00
|
|
|
|
2011-05-19 01:18:05 +04:00
|
|
|
static void v9fs_readlink(void *opaque)
|
2010-09-14 13:38:25 +04:00
|
|
|
{
|
2011-05-19 01:18:05 +04:00
|
|
|
V9fsPDU *pdu = opaque;
|
2011-08-08 22:06:41 +04:00
|
|
|
size_t offset = 7;
|
|
|
|
V9fsString target;
|
2010-09-14 13:38:25 +04:00
|
|
|
int32_t fid;
|
|
|
|
int err = 0;
|
|
|
|
V9fsFidState *fidp;
|
|
|
|
|
2011-08-08 22:06:41 +04:00
|
|
|
pdu_unmarshal(pdu, offset, "d", &fid);
|
|
|
|
fidp = lookup_fid(pdu->s, fid);
|
2010-09-14 13:38:25 +04:00
|
|
|
if (fidp == NULL) {
|
|
|
|
err = -ENOENT;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2011-08-08 22:06:41 +04:00
|
|
|
v9fs_string_init(&target);
|
|
|
|
err = v9fs_co_readlink(pdu->s, &fidp->path, &target);
|
|
|
|
if (err < 0) {
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
offset += pdu_marshal(pdu, offset, "s", &target);
|
|
|
|
err = offset;
|
|
|
|
v9fs_string_free(&target);
|
2010-09-14 13:38:25 +04:00
|
|
|
out:
|
2011-08-08 22:06:41 +04:00
|
|
|
complete_pdu(pdu->s, pdu, err);
|
2010-09-14 13:38:25 +04:00
|
|
|
}
|
|
|
|
|
2011-05-19 01:18:05 +04:00
|
|
|
static CoroutineEntry *pdu_co_handlers[] = {
|
2010-06-09 13:27:57 +04:00
|
|
|
[P9_TREADDIR] = v9fs_readdir,
|
2010-05-10 10:41:03 +04:00
|
|
|
[P9_TSTATFS] = v9fs_statfs,
|
virtio-9p: getattr server implementation for 9P2000.L protocol.
SYNOPSIS
size[4] Tgetattr tag[2] fid[4] request_mask[8]
size[4] Rgetattr tag[2] lstat[n]
DESCRIPTION
The getattr transaction inquires about the file identified by fid.
request_mask is a bit mask that specifies which fields of the
stat structure is the client interested in.
The reply will contain a machine-independent directory entry,
laid out as follows:
st_result_mask[8]
Bit mask that indicates which fields in the stat structure
have been populated by the server
qid.type[1]
the type of the file (directory, etc.), represented as a bit
vector corresponding to the high 8 bits of the file's mode
word.
qid.vers[4]
version number for given path
qid.path[8]
the file server's unique identification for the file
st_mode[4]
Permission and flags
st_uid[4]
User id of owner
st_gid[4]
Group ID of owner
st_nlink[8]
Number of hard links
st_rdev[8]
Device ID (if special file)
st_size[8]
Size, in bytes
st_blksize[8]
Block size for file system IO
st_blocks[8]
Number of file system blocks allocated
st_atime_sec[8]
Time of last access, seconds
st_atime_nsec[8]
Time of last access, nanoseconds
st_mtime_sec[8]
Time of last modification, seconds
st_mtime_nsec[8]
Time of last modification, nanoseconds
st_ctime_sec[8]
Time of last status change, seconds
st_ctime_nsec[8]
Time of last status change, nanoseconds
st_btime_sec[8]
Time of creation (birth) of file, seconds
st_btime_nsec[8]
Time of creation (birth) of file, nanoseconds
st_gen[8]
Inode generation
st_data_version[8]
Data version number
request_mask and result_mask bit masks contain the following bits
#define P9_STATS_MODE 0x00000001ULL
#define P9_STATS_NLINK 0x00000002ULL
#define P9_STATS_UID 0x00000004ULL
#define P9_STATS_GID 0x00000008ULL
#define P9_STATS_RDEV 0x00000010ULL
#define P9_STATS_ATIME 0x00000020ULL
#define P9_STATS_MTIME 0x00000040ULL
#define P9_STATS_CTIME 0x00000080ULL
#define P9_STATS_INO 0x00000100ULL
#define P9_STATS_SIZE 0x00000200ULL
#define P9_STATS_BLOCKS 0x00000400ULL
#define P9_STATS_BTIME 0x00000800ULL
#define P9_STATS_GEN 0x00001000ULL
#define P9_STATS_DATA_VERSION 0x00002000ULL
#define P9_STATS_BASIC 0x000007ffULL
#define P9_STATS_ALL 0x00003fffULL
This patch implements the client side of getattr implementation for 9P2000.L.
It introduces a new structure p9_stat_dotl for getting Linux stat information
along with QID. The data layout is similar to stat structure in Linux user
space with the following major differences:
inode (st_ino) is not part of data. Instead qid is.
device (st_dev) is not part of data because this doesn't make sense on the
client.
All time variables are 64 bit wide on the wire. The kernel seems to use
32 bit variables for these variables. However, some of the architectures
have used 64 bit variables and glibc exposes 64 bit variables to user
space on some architectures. Hence to be on the safer side we have made
these 64 bit in the protocol. Refer to the comments in
include/asm-generic/stat.h
There are some additional fields: st_btime_sec, st_btime_nsec, st_gen,
st_data_version apart from the bitmask, st_result_mask. The bit mask
is filled by the server to indicate which stat fields have been
populated by the server. Currently there is no clean way for the
server to obtain these additional fields, so it sends back just the
basic fields.
Signed-off-by: M. Mohan Kumar <mohan@in.ibm.com>
Signed-off-by: Sripathi Kodi <sripathik@in.ibm.com>
2010-07-20 10:14:41 +04:00
|
|
|
[P9_TGETATTR] = v9fs_getattr,
|
virtio-9p: Implement server side of setattr for 9P2000.L protocol.
SYNOPSIS
size[4] Tsetattr tag[2] attr[n]
size[4] Rsetattr tag[2]
DESCRIPTION
The setattr command changes some of the file status information.
attr resembles the iattr structure used in Linux kernel. It
specifies which status parameter is to be changed and to what
value. It is laid out as follows:
valid[4]
specifies which status information is to be changed. Possible
values are:
ATTR_MODE (1 << 0)
ATTR_UID (1 << 1)
ATTR_GID (1 << 2)
ATTR_SIZE (1 << 3)
ATTR_ATIME (1 << 4)
ATTR_MTIME (1 << 5)
ATTR_CTIME (1 << 5)
ATTR_ATIME_SET (1 << 7)
ATTR_MTIME_SET (1 << 8)
The last two bits represent whether the time information
is being sent by the client's user space. In the absense
of these bits the server always uses server's time.
mode[4]
File permission bits
uid[4]
Owner id of file
gid[4]
Group id of the file
size[8]
File size
atime_sec[8]
Time of last file access, seconds
atime_nsec[8]
Time of last file access, nanoseconds
mtime_sec[8]
Time of last file modification, seconds
mtime_nsec[8]
Time of last file modification, nanoseconds
Explanation of the patches:
--------------------------
*) The kernel just copies relevent contents of iattr structure to p9_iattr_dotl
structure and passes it down to the client. The only check it has is calling
inode_change_ok()
*) The p9_iattr_dotl structure does not have ctime and ia_file parameters because
I don't think these are needed in our case. The client user space can request
updating just ctime by calling chown(fd, -1, -1). This is handled on server
side without a need for putting ctime on the wire.
*) The server currently supports changing mode, time, ownership and size of the
file.
*) 9P RFC says "Either all the changes in wstat request happen, or none of them
does: if the request succeeds, all changes were made; if it fails, none were."
I have not done anything to implement this specifically because I don't see
a reason.
[jvrao@linux.vnet.ibm.com: Parts of code for handling chown(-1,-1)
Signed-off-by: Sripathi Kodi <sripathik@in.ibm.com>
Signed-off-by: Venkateswararao Jujjuri <jvrao@linux.vnet.ibm.com>
2010-06-17 16:48:47 +04:00
|
|
|
[P9_TSETATTR] = v9fs_setattr,
|
2010-09-02 09:39:06 +04:00
|
|
|
[P9_TXATTRWALK] = v9fs_xattrwalk,
|
2010-09-02 09:39:07 +04:00
|
|
|
[P9_TXATTRCREATE] = v9fs_xattrcreate,
|
2010-06-22 10:54:09 +04:00
|
|
|
[P9_TMKNOD] = v9fs_mknod,
|
2010-06-22 10:59:41 +04:00
|
|
|
[P9_TRENAME] = v9fs_rename,
|
[virto-9p] Implement TLOCK
Synopsis
size[4] TLock tag[2] fid[4] flock[n]
size[4] RLock tag[2] status[1]
Description
Tlock is used to acquire/release byte range posix locks on a file
identified by given fid. The reply contains status of the lock request
flock structure:
type[1] - Type of lock: F_RDLCK, F_WRLCK, F_UNLCK
flags[4] - Flags could be either of
P9_LOCK_FLAGS_BLOCK(1) - Blocked lock request, if there is a
conflicting lock exists, wait for that lock to be released.
P9_LOCK_FLAGS_RECLAIM(2) - Reclaim lock request, used when client is
trying to reclaim a lock after a server restrart (due to crash)
start[8] - Starting offset for lock
length[8] - Number of bytes to lock
If length is 0, lock all bytes starting at the location 'start'
through to the end of file
pid[4] - PID of the process that wants to take lock
client_id[4] - Unique client id
status[1] - Status of the lock request, can be
P9_LOCK_SUCCESS(0), P9_LOCK_BLOCKED(1), P9_LOCK_ERROR(2) or
P9_LOCK_GRACE(3)
P9_LOCK_SUCCESS - Request was successful
P9_LOCK_BLOCKED - A conflicting lock is held by another process
P9_LOCK_ERROR - Error while processing the lock request
P9_LOCK_GRACE - Server is in grace period, it can't accept new lock
requests in this period (except locks with
P9_LOCK_FLAGS_RECLAIM flag set)
Signed-off-by: M. Mohan Kumar <mohan@in.ibm.com>
Signed-off-by: Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
Signed-off-by: Venkateswararao Jujjuri <jvrao@linux.vnet.ibm.com>
2010-09-08 00:49:32 +04:00
|
|
|
[P9_TLOCK] = v9fs_lock,
|
2010-09-08 01:06:52 +04:00
|
|
|
[P9_TGETLOCK] = v9fs_getlock,
|
2010-09-14 13:38:25 +04:00
|
|
|
[P9_TREADLINK] = v9fs_readlink,
|
2010-06-22 10:55:22 +04:00
|
|
|
[P9_TMKDIR] = v9fs_mkdir,
|
2010-04-29 16:14:44 +04:00
|
|
|
[P9_TVERSION] = v9fs_version,
|
2010-06-22 18:17:04 +04:00
|
|
|
[P9_TLOPEN] = v9fs_open,
|
2010-04-29 16:14:44 +04:00
|
|
|
[P9_TATTACH] = v9fs_attach,
|
|
|
|
[P9_TSTAT] = v9fs_stat,
|
|
|
|
[P9_TWALK] = v9fs_walk,
|
|
|
|
[P9_TCLUNK] = v9fs_clunk,
|
2010-09-23 04:18:33 +04:00
|
|
|
[P9_TFSYNC] = v9fs_fsync,
|
2010-04-29 16:14:44 +04:00
|
|
|
[P9_TOPEN] = v9fs_open,
|
|
|
|
[P9_TREAD] = v9fs_read,
|
|
|
|
#if 0
|
|
|
|
[P9_TAUTH] = v9fs_auth,
|
|
|
|
#endif
|
|
|
|
[P9_TFLUSH] = v9fs_flush,
|
2010-06-09 22:21:15 +04:00
|
|
|
[P9_TLINK] = v9fs_link,
|
2010-06-10 01:02:08 +04:00
|
|
|
[P9_TSYMLINK] = v9fs_symlink,
|
2010-04-29 16:14:44 +04:00
|
|
|
[P9_TCREATE] = v9fs_create,
|
2010-06-18 05:27:24 +04:00
|
|
|
[P9_TLCREATE] = v9fs_lcreate,
|
2010-04-29 16:14:44 +04:00
|
|
|
[P9_TWRITE] = v9fs_write,
|
|
|
|
[P9_TWSTAT] = v9fs_wstat,
|
|
|
|
[P9_TREMOVE] = v9fs_remove,
|
|
|
|
};
|
|
|
|
|
2011-05-19 01:18:05 +04:00
|
|
|
static void v9fs_op_not_supp(void *opaque)
|
2011-06-01 11:05:14 +04:00
|
|
|
{
|
2011-05-19 01:18:05 +04:00
|
|
|
V9fsPDU *pdu = opaque;
|
|
|
|
complete_pdu(pdu->s, pdu, -EOPNOTSUPP);
|
2011-06-01 11:05:14 +04:00
|
|
|
}
|
|
|
|
|
2010-04-29 16:14:44 +04:00
|
|
|
static void submit_pdu(V9fsState *s, V9fsPDU *pdu)
|
|
|
|
{
|
2011-05-19 01:18:05 +04:00
|
|
|
Coroutine *co;
|
|
|
|
CoroutineEntry *handler;
|
2010-04-29 16:14:44 +04:00
|
|
|
|
|
|
|
if (debug_9p_pdu) {
|
|
|
|
pprint_pdu(pdu);
|
|
|
|
}
|
2011-05-19 01:18:05 +04:00
|
|
|
if (pdu->id >= ARRAY_SIZE(pdu_co_handlers) ||
|
|
|
|
(pdu_co_handlers[pdu->id] == NULL)) {
|
2011-06-01 11:05:14 +04:00
|
|
|
handler = v9fs_op_not_supp;
|
|
|
|
} else {
|
2011-05-19 01:18:05 +04:00
|
|
|
handler = pdu_co_handlers[pdu->id];
|
2011-06-01 11:05:14 +04:00
|
|
|
}
|
2011-05-19 01:18:05 +04:00
|
|
|
co = qemu_coroutine_create(handler);
|
|
|
|
qemu_coroutine_enter(co, pdu);
|
2010-04-29 16:14:44 +04:00
|
|
|
}
|
|
|
|
|
2011-06-01 11:05:13 +04:00
|
|
|
void handle_9p_output(VirtIODevice *vdev, VirtQueue *vq)
|
2010-04-29 16:14:44 +04:00
|
|
|
{
|
|
|
|
V9fsState *s = (V9fsState *)vdev;
|
|
|
|
V9fsPDU *pdu;
|
|
|
|
ssize_t len;
|
|
|
|
|
|
|
|
while ((pdu = alloc_pdu(s)) &&
|
|
|
|
(len = virtqueue_pop(vq, &pdu->elem)) != 0) {
|
|
|
|
uint8_t *ptr;
|
2011-05-19 01:18:05 +04:00
|
|
|
pdu->s = s;
|
2010-04-29 16:14:44 +04:00
|
|
|
BUG_ON(pdu->elem.out_num == 0 || pdu->elem.in_num == 0);
|
|
|
|
BUG_ON(pdu->elem.out_sg[0].iov_len < 7);
|
|
|
|
|
|
|
|
ptr = pdu->elem.out_sg[0].iov_base;
|
|
|
|
|
|
|
|
memcpy(&pdu->size, ptr, 4);
|
|
|
|
pdu->id = ptr[4];
|
|
|
|
memcpy(&pdu->tag, ptr + 5, 2);
|
|
|
|
submit_pdu(s, pdu);
|
|
|
|
}
|
|
|
|
free_pdu(s, pdu);
|
|
|
|
}
|