diff --git a/ChangeLog b/ChangeLog index 925dbc6..faf3559 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2005-08-23 Miklos Szeredi + + * lib: add userspace side of create() method for experimentation + 2005-08-19 Miklos Szeredi * lib: always refresh directory contents after rewinddir() to diff --git a/example/fusexmp_fh.c b/example/fusexmp_fh.c index 432ff6f..a1d2283 100644 --- a/example/fusexmp_fh.c +++ b/example/fusexmp_fh.c @@ -217,6 +217,28 @@ static int xmp_open(const char *path, struct fuse_file_info *fi) return 0; } +static int xmp_create(const char *path, mode_t mode, struct fuse_file_info *fi) +{ + int fd; + struct stat stbuf; + + fd = open(path, fi->flags | O_NOFOLLOW, mode); + if(fd == -1) + return -errno; + + if (fstat(fd, &stbuf) == -1) { + close(fd); + return -EIO; + } + if (!S_ISREG(stbuf.st_mode)) { + close(fd); + return -EISDIR; + } + + fi->fh = fd; + return 0; +} + static int xmp_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi) { @@ -338,6 +360,7 @@ static struct fuse_operations xmp_oper = { .statfs = xmp_statfs, .release = xmp_release, .fsync = xmp_fsync, + .create = xmp_create, #ifdef HAVE_SETXATTR .setxattr = xmp_setxattr, .getxattr = xmp_getxattr, diff --git a/include/fuse.h b/include/fuse.h index 4bb215e..6251158 100644 --- a/include/fuse.h +++ b/include/fuse.h @@ -311,6 +311,8 @@ struct fuse_operations { * Introduced in version 2.4 */ int (*access) (const char *, int); + + int (*create) (const char *, mode_t, struct fuse_file_info *); }; /** Extra context that may be needed by some filesystems diff --git a/include/fuse_lowlevel.h b/include/fuse_lowlevel.h index ffeb6f9..a8e3e3e 100644 --- a/include/fuse_lowlevel.h +++ b/include/fuse_lowlevel.h @@ -95,6 +95,8 @@ struct fuse_lowlevel_ops { fuse_ino_t newparent, const char *newname); void (*link) (fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent, const char *newname); + void (*create) (fuse_req_t req, fuse_ino_t parent, const char *name, + mode_t mode, struct fuse_file_info *fi); void (*open) (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi); void (*read) (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi); @@ -132,9 +134,13 @@ int fuse_reply_err(fuse_req_t req, int err); /* forget */ int fuse_reply_none(fuse_req_t req); -/* lookup, mknod, mkdir, symlink, link */ +/* lookup, create, mknod, mkdir, symlink, link */ int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e); +/* create */ +int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e, + const struct fuse_file_info *fi); + /* getattr, setattr */ int fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout); diff --git a/kernel/fuse_kernel.h b/kernel/fuse_kernel.h index 2c2903e..aa6b298 100644 --- a/kernel/fuse_kernel.h +++ b/kernel/fuse_kernel.h @@ -106,7 +106,8 @@ enum fuse_opcode { FUSE_GETLK = 31, FUSE_SETLK = 32, FUSE_SETLKW = 33, - FUSE_ACCESS = 34 + FUSE_ACCESS = 34, + FUSE_CREATE = 35 }; /* Conservative buffer size for the client */ @@ -164,7 +165,7 @@ struct fuse_setattr_in { struct fuse_open_in { __u32 flags; - __u32 padding; + __u32 mode; }; struct fuse_open_out { diff --git a/lib/fuse.c b/lib/fuse.c index f9b1543..237dee1 100644 --- a/lib/fuse.c +++ b/lib/fuse.c @@ -975,6 +975,71 @@ static void fuse_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent, reply_entry(req, &e, err); } +static void fuse_create(fuse_req_t req, fuse_ino_t parent, const char *name, + mode_t mode, struct fuse_file_info *fi) +{ + struct fuse *f = req_fuse_prepare(req); + struct fuse_entry_param e; + char *path; + int opened = 0; + int err; + + err = -ENOENT; + pthread_rwlock_rdlock(&f->tree_lock); + path = get_path_name(f, parent, name); + if (path != NULL) { + err = -ENOSYS; + if (f->op.create && f->op.getattr) { + int oerr = f->op.create(path, mode, fi); + if (!oerr) + opened = 1; + + if (f->flags & FUSE_DEBUG) { + if (opened) + printf("CREATE[%lu] flags: 0x%x %s\n", fi->fh, fi->flags, path); + else + printf("LOOKUP(CREATE) %s\n", path); + fflush(stdout); + } + + err = lookup_path(f, parent, name, path, &e); + if (err) { + if (f->op.release && opened) + f->op.release(path, fi); + } else if (opened != (S_ISREG(e.attr.st_mode) != 0)) { + err = oerr ? oerr : -EIO; + if (f->op.release && opened) + f->op.release(path, fi); + forget_node(f, e.ino, 1); + } + } + } + + if (!err) { + if (f->flags & FUSE_DIRECT_IO) + fi->direct_io = 1; + if (f->flags & FUSE_KERNEL_CACHE) + fi->keep_cache = 1; + + pthread_mutex_lock(&f->lock); + if (fuse_reply_create(req, &e, fi) == -ENOENT) { + /* The open syscall was interrupted, so it must be cancelled */ + if(f->op.release && opened) + f->op.release(path, fi); + forget_node(f, e.ino, 1); + } else { + struct node *node = get_node(f, e.ino); + node->open_count ++; + } + pthread_mutex_unlock(&f->lock); + } else + reply_err(req, err); + + if (path) + free(path); + pthread_rwlock_unlock(&f->tree_lock); +} + static void fuse_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) { @@ -1618,6 +1683,7 @@ static struct fuse_lowlevel_ops fuse_path_ops = { .symlink = fuse_symlink, .rename = fuse_rename, .link = fuse_link, + .create = fuse_create, .open = fuse_open, .read = fuse_read, .write = fuse_write, diff --git a/lib/fuse_lowlevel.c b/lib/fuse_lowlevel.c index bcd48c5..893f723 100644 --- a/lib/fuse_lowlevel.c +++ b/lib/fuse_lowlevel.c @@ -73,6 +73,7 @@ static const char *opname(enum fuse_opcode opcode) case FUSE_SETLK: return "SETLK"; case FUSE_SETLKW: return "SETLKW"; case FUSE_ACCESS: return "ACCESS"; + case FUSE_CREATE: return "CREATE"; default: return "???"; } } @@ -258,19 +259,48 @@ static unsigned int calc_timeout_nsec(double t) return (unsigned int) (f * 1.0e9); } +static void fill_entry(struct fuse_entry_out *arg, + const struct fuse_entry_param *e) +{ + arg->nodeid = e->ino; + arg->generation = e->generation; + arg->entry_valid = calc_timeout_sec(e->entry_timeout); + arg->entry_valid_nsec = calc_timeout_nsec(e->entry_timeout); + arg->attr_valid = calc_timeout_sec(e->attr_timeout); + arg->attr_valid_nsec = calc_timeout_nsec(e->attr_timeout); + convert_stat(&e->attr, &arg->attr); +} + +static void fill_open(struct fuse_open_out *arg, + const struct fuse_file_info *f) +{ + arg->fh = f->fh; + if (f->direct_io) + arg->open_flags |= FOPEN_DIRECT_IO; + if (f->keep_cache) + arg->open_flags |= FOPEN_KEEP_CACHE; +} + int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e) { struct fuse_entry_out arg; memset(&arg, 0, sizeof(arg)); - arg.nodeid = e->ino; - arg.generation = e->generation; - arg.entry_valid = calc_timeout_sec(e->entry_timeout); - arg.entry_valid_nsec = calc_timeout_nsec(e->entry_timeout); - arg.attr_valid = calc_timeout_sec(e->attr_timeout); - arg.attr_valid_nsec = calc_timeout_nsec(e->attr_timeout); - convert_stat(&e->attr, &arg.attr); + fill_entry(&arg, e); + return send_reply_ok(req, &arg, sizeof(arg)); +} +int fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e, + const struct fuse_file_info *f) +{ + struct { + struct fuse_entry_out e; + struct fuse_open_out o; + } arg; + + memset(&arg, 0, sizeof(arg)); + fill_entry(&arg.e, e); + fill_open(&arg.o, f); return send_reply_ok(req, &arg, sizeof(arg)); } @@ -297,12 +327,7 @@ int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *f) struct fuse_open_out arg; memset(&arg, 0, sizeof(arg)); - arg.fh = f->fh; - if (f->direct_io) - arg.open_flags |= FOPEN_DIRECT_IO; - if (f->keep_cache) - arg.open_flags |= FOPEN_KEEP_CACHE; - + fill_open(&arg, f); return send_reply_ok(req, &arg, sizeof(arg)); } @@ -466,6 +491,20 @@ static void do_link(fuse_req_t req, fuse_ino_t nodeid, fuse_reply_err(req, ENOSYS); } +static void do_create(fuse_req_t req, fuse_ino_t nodeid, + struct fuse_open_in *arg) +{ + if (req->f->op.create) { + struct fuse_file_info fi; + + memset(&fi, 0, sizeof(fi)); + fi.flags = arg->flags; + + req->f->op.create(req, nodeid, PARAM(arg), arg->mode, &fi); + } else + fuse_reply_err(req, ENOSYS); +} + static void do_open(fuse_req_t req, fuse_ino_t nodeid, struct fuse_open_in *arg) { @@ -886,6 +925,10 @@ static void fuse_ll_process(void *data, const char *buf, size_t len, do_access(req, in->nodeid, (struct fuse_access_in *) inarg); break; + case FUSE_CREATE: + do_create(req, in->nodeid, (struct fuse_open_in *) inarg); + break; + default: fuse_reply_err(req, ENOSYS); }