X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=blobdiff_plain;ds=inline;f=fs%2Ffuse%2Fdir.c;h=f127625543b4d557e713ce66629633183b102560;hb=92a8780e1136c5ca0c7ed940000d399943d1576e;hp=8adc1eed164b52d745278781f3cae1e6ab05da24;hpb=b6aeadeda22a9aa322fdfcd3f4c69ccf0da5cbdd;p=linux-2.6 diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 8adc1eed16..f127625543 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -418,7 +418,8 @@ static int fuse_revalidate(struct dentry *entry) struct fuse_conn *fc = get_fuse_conn(inode); if (get_node_id(inode) == FUSE_ROOT_ID) { - if (current->fsuid != fc->user_id) + if (!(fc->flags & FUSE_ALLOW_OTHER) && + current->fsuid != fc->user_id) return -EACCES; } else if (time_before_eq(jiffies, fi->i_time)) return 0; @@ -430,9 +431,31 @@ static int fuse_permission(struct inode *inode, int mask, struct nameidata *nd) { struct fuse_conn *fc = get_fuse_conn(inode); - if (current->fsuid != fc->user_id) + if (!(fc->flags & FUSE_ALLOW_OTHER) && current->fsuid != fc->user_id) return -EACCES; - else { + else if (fc->flags & FUSE_DEFAULT_PERMISSIONS) { + int err = generic_permission(inode, mask, NULL); + + /* If permission is denied, try to refresh file + attributes. This is also needed, because the root + node will at first have no permissions */ + if (err == -EACCES) { + err = fuse_do_getattr(inode); + if (!err) + err = generic_permission(inode, mask, NULL); + } + + /* FIXME: Need some mechanism to revoke permissions: + currently if the filesystem suddenly changes the + file mode, we will not be informed about it, and + continue to allow access to the file/directory. + + This is actually not so grave, since the user can + simply keep access to the file/directory anyway by + keeping it open... */ + + return err; + } else { int mode = inode->i_mode; if ((mask & MAY_WRITE) && IS_RDONLY(inode) && (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode))) @@ -636,6 +659,12 @@ static int fuse_setattr(struct dentry *entry, struct iattr *attr) int err; int is_truncate = 0; + if (fc->flags & FUSE_DEFAULT_PERMISSIONS) { + err = inode_change_ok(inode, attr); + if (err) + return err; + } + if (attr->ia_valid & ATTR_SIZE) { unsigned long limit; is_truncate = 1; @@ -715,6 +744,177 @@ static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry, return d_splice_alias(inode, entry); } +static int fuse_setxattr(struct dentry *entry, const char *name, + const void *value, size_t size, int flags) +{ + struct inode *inode = entry->d_inode; + struct fuse_conn *fc = get_fuse_conn(inode); + struct fuse_req *req; + struct fuse_setxattr_in inarg; + int err; + + if (size > FUSE_XATTR_SIZE_MAX) + return -E2BIG; + + if (fc->no_setxattr) + return -EOPNOTSUPP; + + req = fuse_get_request(fc); + if (!req) + return -ERESTARTNOINTR; + + memset(&inarg, 0, sizeof(inarg)); + inarg.size = size; + inarg.flags = flags; + req->in.h.opcode = FUSE_SETXATTR; + req->in.h.nodeid = get_node_id(inode); + req->inode = inode; + req->in.numargs = 3; + req->in.args[0].size = sizeof(inarg); + req->in.args[0].value = &inarg; + req->in.args[1].size = strlen(name) + 1; + req->in.args[1].value = name; + req->in.args[2].size = size; + req->in.args[2].value = value; + request_send(fc, req); + err = req->out.h.error; + fuse_put_request(fc, req); + if (err == -ENOSYS) { + fc->no_setxattr = 1; + err = -EOPNOTSUPP; + } + return err; +} + +static ssize_t fuse_getxattr(struct dentry *entry, const char *name, + void *value, size_t size) +{ + struct inode *inode = entry->d_inode; + struct fuse_conn *fc = get_fuse_conn(inode); + struct fuse_req *req; + struct fuse_getxattr_in inarg; + struct fuse_getxattr_out outarg; + ssize_t ret; + + if (fc->no_getxattr) + return -EOPNOTSUPP; + + req = fuse_get_request(fc); + if (!req) + return -ERESTARTNOINTR; + + memset(&inarg, 0, sizeof(inarg)); + inarg.size = size; + req->in.h.opcode = FUSE_GETXATTR; + req->in.h.nodeid = get_node_id(inode); + req->inode = inode; + req->in.numargs = 2; + req->in.args[0].size = sizeof(inarg); + req->in.args[0].value = &inarg; + req->in.args[1].size = strlen(name) + 1; + req->in.args[1].value = name; + /* This is really two different operations rolled into one */ + req->out.numargs = 1; + if (size) { + req->out.argvar = 1; + req->out.args[0].size = size; + req->out.args[0].value = value; + } else { + req->out.args[0].size = sizeof(outarg); + req->out.args[0].value = &outarg; + } + request_send(fc, req); + ret = req->out.h.error; + if (!ret) + ret = size ? req->out.args[0].size : outarg.size; + else { + if (ret == -ENOSYS) { + fc->no_getxattr = 1; + ret = -EOPNOTSUPP; + } + } + fuse_put_request(fc, req); + return ret; +} + +static ssize_t fuse_listxattr(struct dentry *entry, char *list, size_t size) +{ + struct inode *inode = entry->d_inode; + struct fuse_conn *fc = get_fuse_conn(inode); + struct fuse_req *req; + struct fuse_getxattr_in inarg; + struct fuse_getxattr_out outarg; + ssize_t ret; + + if (fc->no_listxattr) + return -EOPNOTSUPP; + + req = fuse_get_request(fc); + if (!req) + return -ERESTARTNOINTR; + + memset(&inarg, 0, sizeof(inarg)); + inarg.size = size; + req->in.h.opcode = FUSE_LISTXATTR; + req->in.h.nodeid = get_node_id(inode); + req->inode = inode; + req->in.numargs = 1; + req->in.args[0].size = sizeof(inarg); + req->in.args[0].value = &inarg; + /* This is really two different operations rolled into one */ + req->out.numargs = 1; + if (size) { + req->out.argvar = 1; + req->out.args[0].size = size; + req->out.args[0].value = list; + } else { + req->out.args[0].size = sizeof(outarg); + req->out.args[0].value = &outarg; + } + request_send(fc, req); + ret = req->out.h.error; + if (!ret) + ret = size ? req->out.args[0].size : outarg.size; + else { + if (ret == -ENOSYS) { + fc->no_listxattr = 1; + ret = -EOPNOTSUPP; + } + } + fuse_put_request(fc, req); + return ret; +} + +static int fuse_removexattr(struct dentry *entry, const char *name) +{ + struct inode *inode = entry->d_inode; + struct fuse_conn *fc = get_fuse_conn(inode); + struct fuse_req *req; + int err; + + if (fc->no_removexattr) + return -EOPNOTSUPP; + + req = fuse_get_request(fc); + if (!req) + return -ERESTARTNOINTR; + + req->in.h.opcode = FUSE_REMOVEXATTR; + req->in.h.nodeid = get_node_id(inode); + req->inode = inode; + req->in.numargs = 1; + req->in.args[0].size = strlen(name) + 1; + req->in.args[0].value = name; + request_send(fc, req); + err = req->out.h.error; + fuse_put_request(fc, req); + if (err == -ENOSYS) { + fc->no_removexattr = 1; + err = -EOPNOTSUPP; + } + return err; +} + static struct inode_operations fuse_dir_inode_operations = { .lookup = fuse_lookup, .mkdir = fuse_mkdir, @@ -728,6 +928,10 @@ static struct inode_operations fuse_dir_inode_operations = { .mknod = fuse_mknod, .permission = fuse_permission, .getattr = fuse_getattr, + .setxattr = fuse_setxattr, + .getxattr = fuse_getxattr, + .listxattr = fuse_listxattr, + .removexattr = fuse_removexattr, }; static struct file_operations fuse_dir_operations = { @@ -742,6 +946,10 @@ static struct inode_operations fuse_common_inode_operations = { .setattr = fuse_setattr, .permission = fuse_permission, .getattr = fuse_getattr, + .setxattr = fuse_setxattr, + .getxattr = fuse_getxattr, + .listxattr = fuse_listxattr, + .removexattr = fuse_removexattr, }; static struct inode_operations fuse_symlink_inode_operations = { @@ -750,6 +958,10 @@ static struct inode_operations fuse_symlink_inode_operations = { .put_link = fuse_put_link, .readlink = generic_readlink, .getattr = fuse_getattr, + .setxattr = fuse_setxattr, + .getxattr = fuse_getxattr, + .listxattr = fuse_listxattr, + .removexattr = fuse_removexattr, }; void fuse_init_common(struct inode *inode)