* new fusermount flag '-z': lazy unmount, default is not lazy
+ * Extended attributes operations added (getxattr, setxattr,
+ listxattr, removexattr)
+
2004-03-25 Miklos Szeredi <mszeredi@inf.bme.hu>
* If filesystem doesn't define a statfs operation, then an
* an fdatasync() operation.
*/
struct fuse_operations {
- int (*getattr) (const char *, struct stat *);
- int (*readlink) (const char *, char *, size_t);
- int (*getdir) (const char *, fuse_dirh_t, fuse_dirfil_t);
- int (*mknod) (const char *, mode_t, dev_t);
- int (*mkdir) (const char *, mode_t);
- int (*unlink) (const char *);
- int (*rmdir) (const char *);
- int (*symlink) (const char *, const char *);
- int (*rename) (const char *, const char *);
- int (*link) (const char *, const char *);
- int (*chmod) (const char *, mode_t);
- int (*chown) (const char *, uid_t, gid_t);
- int (*truncate) (const char *, off_t);
- int (*utime) (const char *, struct utimbuf *);
- int (*open) (const char *, int);
- int (*read) (const char *, char *, size_t, off_t);
- int (*write) (const char *, const char *, size_t, off_t);
- int (*statfs) (const char *, struct statfs *);
- int (*release) (const char *, int);
- int (*fsync) (const char *, int);
+ int (*getattr) (const char *, struct stat *);
+ int (*readlink) (const char *, char *, size_t);
+ int (*getdir) (const char *, fuse_dirh_t, fuse_dirfil_t);
+ int (*mknod) (const char *, mode_t, dev_t);
+ int (*mkdir) (const char *, mode_t);
+ int (*unlink) (const char *);
+ int (*rmdir) (const char *);
+ int (*symlink) (const char *, const char *);
+ int (*rename) (const char *, const char *);
+ int (*link) (const char *, const char *);
+ int (*chmod) (const char *, mode_t);
+ int (*chown) (const char *, uid_t, gid_t);
+ int (*truncate) (const char *, off_t);
+ int (*utime) (const char *, struct utimbuf *);
+ int (*open) (const char *, int);
+ int (*read) (const char *, char *, size_t, off_t);
+ int (*write) (const char *, const char *, size_t, off_t);
+ int (*statfs) (const char *, struct statfs *);
+ int (*release) (const char *, int);
+ int (*fsync) (const char *, int);
+ int (*setxattr) (const char *, const char *, const char *, size_t, int);
+ int (*getxattr) (const char *, const char *, char *, size_t);
+ int (*listxattr) (const char *, char *, size_t);
+ int (*removexattr) (const char *, const char *);
};
/** Extra context that may be needed by some filesystems */
#define FATTR_CTIME (1 << 6)
enum fuse_opcode {
- FUSE_LOOKUP = 1,
- FUSE_FORGET = 2, /* no reply */
- FUSE_GETATTR = 3,
- FUSE_SETATTR = 4,
- FUSE_READLINK = 5,
- FUSE_SYMLINK = 6,
- FUSE_GETDIR = 7,
- FUSE_MKNOD = 8,
- FUSE_MKDIR = 9,
- FUSE_UNLINK = 10,
- FUSE_RMDIR = 11,
- FUSE_RENAME = 12,
- FUSE_LINK = 13,
- FUSE_OPEN = 14,
- FUSE_READ = 15,
- FUSE_WRITE = 16,
- FUSE_STATFS = 17,
- FUSE_RELEASE = 18, /* no reply */
- FUSE_INVALIDATE = 19, /* user initiated */
- FUSE_FSYNC = 20
+ FUSE_LOOKUP = 1,
+ FUSE_FORGET = 2, /* no reply */
+ FUSE_GETATTR = 3,
+ FUSE_SETATTR = 4,
+ FUSE_READLINK = 5,
+ FUSE_SYMLINK = 6,
+ FUSE_GETDIR = 7,
+ FUSE_MKNOD = 8,
+ FUSE_MKDIR = 9,
+ FUSE_UNLINK = 10,
+ FUSE_RMDIR = 11,
+ FUSE_RENAME = 12,
+ FUSE_LINK = 13,
+ FUSE_OPEN = 14,
+ FUSE_READ = 15,
+ FUSE_WRITE = 16,
+ FUSE_STATFS = 17,
+ FUSE_RELEASE = 18, /* no reply */
+ FUSE_INVALIDATE = 19, /* user initiated */
+ FUSE_FSYNC = 20,
+ FUSE_SETXATTR = 21,
+ FUSE_GETXATTR = 22,
+ FUSE_LISTXATTR = 23,
+ FUSE_REMOVEXATTR = 24,
};
/* Conservative buffer size for the client */
int datasync;
};
+struct fuse_setxattr_in {
+ unsigned int size;
+ unsigned int flags;
+};
+
+struct fuse_getlistxattr_in {
+ unsigned int size;
+};
+
struct fuse_in_header {
int unique;
enum fuse_opcode opcode;
{
return _fuse_dentry_revalidate(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 = INO_FC(inode);
+ struct fuse_in in = FUSE_IN_INIT;
+ struct fuse_out out = FUSE_OUT_INIT;
+ struct fuse_setxattr_in inarg;
+
+ memset(&inarg, 0, sizeof(inarg));
+ inarg.size = size;
+ inarg.flags = flags;
+
+ in.h.opcode = FUSE_SETXATTR;
+ in.h.ino = inode->i_ino;
+ in.numargs = 3;
+ in.args[0].size = sizeof(inarg);
+ in.args[0].value = &inarg;
+ in.args[1].size = strlen(name) + 1;
+ in.args[1].value = name;
+ in.args[2].size = size;
+ in.args[2].value = value;
+ request_send(fc, &in, &out);
+ return out.h.error;
+}
+
+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 = INO_FC(inode);
+ struct fuse_in in = FUSE_IN_INIT;
+ struct fuse_out out = FUSE_OUT_INIT;
+ struct fuse_getlistxattr_in inarg;
+
+ memset(&inarg, 0, sizeof(inarg));
+ inarg.size = size;
+
+ in.h.opcode = FUSE_GETXATTR;
+ in.h.ino = inode->i_ino;
+ in.numargs = 2;
+ in.args[0].size = sizeof(inarg);
+ in.args[0].value = &inarg;
+ in.args[1].size = strlen(name) + 1;
+ in.args[1].value = name;
+ out.argvar = 1;
+ out.numargs = 1;
+ out.args[0].size = size;
+ out.args[0].value = value;
+ request_send(fc, &in, &out);
+ if(!out.h.error)
+ return out.args[0].size;
+ else
+ return out.h.error;
+}
+
+static ssize_t fuse_listxattr(struct dentry *entry, char *list, size_t size)
+{
+ struct inode *inode = entry->d_inode;
+ struct fuse_conn *fc = INO_FC(inode);
+ struct fuse_in in = FUSE_IN_INIT;
+ struct fuse_out out = FUSE_OUT_INIT;
+ struct fuse_getlistxattr_in inarg;
+
+ memset(&inarg, 0, sizeof(inarg));
+ inarg.size = size;
+
+ in.h.opcode = FUSE_LISTXATTR;
+ in.h.ino = inode->i_ino;
+ in.numargs = 1;
+ in.args[0].size = sizeof(inarg);
+ in.args[0].value = &inarg;
+ out.argvar = 1;
+ out.numargs = 1;
+ out.args[0].size = size;
+ out.args[0].value = list;
+ request_send(fc, &in, &out);
+ if(!out.h.error)
+ return out.args[0].size;
+ else
+ return out.h.error;
+}
+
+static int fuse_removexattr(struct dentry *entry, const char *name)
+{
+ struct inode *inode = entry->d_inode;
+ struct fuse_conn *fc = INO_FC(inode);
+ struct fuse_in in = FUSE_IN_INIT;
+ struct fuse_out out = FUSE_OUT_INIT;
+
+ in.h.opcode = FUSE_REMOVEXATTR;
+ in.h.ino = inode->i_ino;
+ in.numargs = 1;
+ in.args[0].size = strlen(name) + 1;
+ in.args[0].value = name;
+ request_send(fc, &in, &out);
+ return out.h.error;
+
+}
+
#else /* KERNEL_2_6 */
#define fuse_create _fuse_create
.permission = fuse_permission,
#ifdef KERNEL_2_6
.getattr = fuse_getattr,
+ .setxattr = fuse_setxattr,
+ .getxattr = fuse_getxattr,
+ .listxattr = fuse_listxattr,
+ .removexattr = fuse_removexattr,
#else
.revalidate = fuse_revalidate,
#endif
.permission = fuse_permission,
#ifdef KERNEL_2_6
.getattr = fuse_getattr,
+ .setxattr = fuse_setxattr,
+ .getxattr = fuse_getxattr,
+ .listxattr = fuse_listxattr,
+ .removexattr = fuse_removexattr,
#else
.revalidate = fuse_revalidate,
#endif
.follow_link = fuse_follow_link,
#ifdef KERNEL_2_6
.getattr = fuse_getattr,
+ .setxattr = fuse_setxattr,
+ .getxattr = fuse_getxattr,
+ .listxattr = fuse_listxattr,
+ .removexattr = fuse_removexattr,
#else
.revalidate = fuse_revalidate,
#endif
static const char *opname(enum fuse_opcode opcode)
{
switch(opcode) {
- case FUSE_LOOKUP: return "LOOKUP";
- case FUSE_FORGET: return "FORGET";
- case FUSE_GETATTR: return "GETATTR";
- case FUSE_SETATTR: return "SETATTR";
- case FUSE_READLINK: return "READLINK";
- case FUSE_SYMLINK: return "SYMLINK";
- case FUSE_GETDIR: return "GETDIR";
- case FUSE_MKNOD: return "MKNOD";
- case FUSE_MKDIR: return "MKDIR";
- case FUSE_UNLINK: return "UNLINK";
- case FUSE_RMDIR: return "RMDIR";
- case FUSE_RENAME: return "RENAME";
- case FUSE_LINK: return "LINK";
- case FUSE_OPEN: return "OPEN";
- case FUSE_READ: return "READ";
- case FUSE_WRITE: return "WRITE";
- case FUSE_STATFS: return "STATFS";
- case FUSE_RELEASE: return "RELEASE";
- case FUSE_FSYNC: return "FSYNC";
+ case FUSE_LOOKUP: return "LOOKUP";
+ case FUSE_FORGET: return "FORGET";
+ case FUSE_GETATTR: return "GETATTR";
+ case FUSE_SETATTR: return "SETATTR";
+ case FUSE_READLINK: return "READLINK";
+ case FUSE_SYMLINK: return "SYMLINK";
+ case FUSE_GETDIR: return "GETDIR";
+ case FUSE_MKNOD: return "MKNOD";
+ case FUSE_MKDIR: return "MKDIR";
+ case FUSE_UNLINK: return "UNLINK";
+ case FUSE_RMDIR: return "RMDIR";
+ case FUSE_RENAME: return "RENAME";
+ case FUSE_LINK: return "LINK";
+ case FUSE_OPEN: return "OPEN";
+ case FUSE_READ: return "READ";
+ case FUSE_WRITE: return "WRITE";
+ case FUSE_STATFS: return "STATFS";
+ case FUSE_RELEASE: return "RELEASE";
+ case FUSE_FSYNC: return "FSYNC";
+ case FUSE_SETXATTR: return "SETXATTR";
+ case FUSE_GETXATTR: return "GETXATTR";
+ case FUSE_LISTXATTR: return "LISTXATTR";
+ case FUSE_REMOVEXATTR: return "REMOVEXATTR";
default: return "???";
}
}
send_reply(f, in, res, NULL, 0);
}
+static void do_setxattr(struct fuse *f, struct fuse_in_header *in,
+ struct fuse_setxattr_in *arg)
+{
+ int res;
+ char *path;
+ char *name = PARAM(arg);
+ unsigned char *value = name + strlen(name) + 1;
+
+ res = -ENOENT;
+ path = get_path(f, in->ino);
+ if (path != NULL) {
+ res = -EOPNOTSUPP; /* or is it ENOTSUPP ??? */
+ if (f->op.setxattr)
+ res = f->op.setxattr(path, name, value, arg->size, arg->flags);
+ free(path);
+ }
+ send_reply(f, in, res, NULL, 0);
+}
+
+static void do_getxattr(struct fuse *f, struct fuse_in_header *in,
+ struct fuse_getlistxattr_in *arg)
+{
+ int res;
+ char *path;
+ char *name = PARAM(arg);
+ char *outbuf = (char *) malloc(sizeof(struct fuse_out_header) + arg->size);
+ struct fuse_out_header *out = (struct fuse_out_header *) outbuf;
+ char *value = outbuf + sizeof(struct fuse_out_header);
+ size_t size;
+ size_t outsize;
+
+ res = -ENOENT;
+ path = get_path(f, in->ino);
+ if (path != NULL) {
+ res = -EOPNOTSUPP; /* or is it ENOTSUPP ??? */
+ if (f->op.getxattr)
+ res = f->op.getxattr(path, name, value, arg->size);
+ free(path);
+ }
+ size = 0;
+ if(res > 0) {
+ size = res;
+ res = 0;
+ }
+ memset(out, 0, sizeof(struct fuse_out_header));
+ out->unique = in->unique;
+ out->error = res;
+ outsize = sizeof(struct fuse_out_header) + size;
+
+ send_reply_raw(f, outbuf, outsize);
+ free(outbuf);
+}
+
+static void do_listxattr(struct fuse *f, struct fuse_in_header *in,
+ struct fuse_getlistxattr_in *arg)
+{
+ int res;
+ char *path;
+ char *outbuf = (char *) malloc(sizeof(struct fuse_out_header) + arg->size);
+ struct fuse_out_header *out = (struct fuse_out_header *) outbuf;
+ char *value = outbuf + sizeof(struct fuse_out_header);
+ size_t size;
+ size_t outsize;
+
+ res = -ENOENT;
+ path = get_path(f, in->ino);
+ if (path != NULL) {
+ res = -EOPNOTSUPP; /* or is it ENOTSUPP ??? */
+ if (f->op.listxattr)
+ res = f->op.listxattr(path, value, arg->size);
+ free(path);
+ }
+ size = 0;
+ if(res > 0) {
+ size = res;
+ res = 0;
+ }
+ memset(out, 0, sizeof(struct fuse_out_header));
+ out->unique = in->unique;
+ out->error = res;
+ outsize = sizeof(struct fuse_out_header) + size;
+
+ send_reply_raw(f, outbuf, outsize);
+ free(outbuf);
+}
+
+static void do_removexattr(struct fuse *f, struct fuse_in_header *in,
+ char *name)
+{
+ int res;
+ char *path;
+
+ res = -ENOENT;
+ path = get_path(f, in->ino);
+ if (path != NULL) {
+ res = -EOPNOTSUPP; /* or is it ENOTSUPP ??? */
+ if (f->op.removexattr)
+ res = f->op.removexattr(path, name);
+ free(path);
+ }
+ send_reply(f, in, res, NULL, 0);
+}
+
+
static void free_cmd(struct fuse_cmd *cmd)
{
free(cmd->buf);
do_fsync(f, in, (struct fuse_fsync_in *) inarg);
break;
+ case FUSE_SETXATTR:
+ do_setxattr(f, in, (struct fuse_setxattr_in *) inarg);
+ break;
+
+ case FUSE_GETXATTR:
+ do_getxattr(f, in, (struct fuse_getlistxattr_in *) inarg);
+ break;
+
+ case FUSE_LISTXATTR:
+ do_listxattr(f, in, (struct fuse_getlistxattr_in *) inarg);
+ break;
+
+ case FUSE_REMOVEXATTR:
+ do_removexattr(f, in, (char *) inarg);
+ break;
+
default:
send_reply(f, in, -ENOSYS, NULL, 0);
}
if(found) {
res = umount2(mnt, lazy ? 2 : 0);
if(res == -1) {
- fprintf(stderr, "%s: failed to unmount %s: %s\n", progname, mnt,
- strerror(errno));
+ if(!quiet)
+ fprintf(stderr, "%s: failed to unmount %s: %s\n",
+ progname, mnt,strerror(errno));
found = -1;
}
}
} else {
res = umount2(mnt, lazy ? 2 : 0);
if(res == -1) {
- fprintf(stderr, "%s: failed to unmount %s: %s\n", progname, mnt,
- strerror(errno));
+ if (!quiet)
+ fprintf(stderr, "%s: failed to unmount %s: %s\n",
+ progname, mnt, strerror(errno));
}
}
if(res == -1)