/* FUSE: Filesystem in Userspace Copyright (C) 2001-2005 Miklos Szeredi This program can be distributed under the terms of the GNU LGPL. See the file COPYING.LIB. */ #ifndef _FUSE_LOWLEVEL_H_ #define _FUSE_LOWLEVEL_H_ /* ----------------------------------------------------------- * * Low level API * * ----------------------------------------------------------- */ #include "fuse_common.h" #include #include #include #include #include #ifdef __cplusplus extern "C" { #endif /** The node ID of the root inode */ #define FUSE_ROOT_ID 1 /** Inode number type */ typedef unsigned long fuse_ino_t; /** Request pointer type */ typedef struct fuse_req *fuse_req_t; /** * Session * * This provides hooks for processing requests, and exiting */ struct fuse_session; /** * Channel * * A communication channel, providing hooks for sending and receiving * messages */ struct fuse_chan; /** Directory entry parameters supplied to fuse_reply_entry() */ struct fuse_entry_param { /** Unique inode number */ fuse_ino_t ino; /** The ino/generation pair should be unique for the filesystem's lifetime */ unsigned long generation; /** Inode attributes */ struct stat attr; /** Validity timeout (in seconds) for the attributes */ double attr_timeout; /** Validity timeout (in seconds) for the name */ double entry_timeout; }; /** Additional context associated with requests */ struct fuse_ctx { /** User ID of the calling process */ uid_t uid; /** Group ID of the calling process */ gid_t gid; /** Thread ID of the calling process */ pid_t pid; }; /* 'to_set' flags in setattr */ #define FUSE_SET_ATTR_MODE (1 << 0) #define FUSE_SET_ATTR_UID (1 << 1) #define FUSE_SET_ATTR_GID (1 << 2) #define FUSE_SET_ATTR_SIZE (1 << 3) #define FUSE_SET_ATTR_ATIME (1 << 4) #define FUSE_SET_ATTR_MTIME (1 << 5) #define FUSE_SET_ATTR_CTIME (1 << 6) /** Low level filesystem operations */ struct fuse_lowlevel_ops { /** Initialize filesystem * * Called before any other filesystem method * * 'userdata' from fuse_lowlevel_new() is passed to this function * * There's no reply to this function */ void (*init) (void *); /** Clean up filesystem * * Called on filesystem exit * * 'userdata' from fuse_lowlevel_new() is passed to this function * * There's no reply to this function */ void (*destroy)(void *); /** Look up a directory entry by name * * Valid replies: * fuse_reply_entry() * fuse_reply_err() */ void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name); /** * Forget about an inode * * The nlookup parameter indicates the number of lookups * previously performed on this inode. * * If the filesystem implements inode lifetimes, it is recommended * that inodes acquire a single reference on each lookup, and lose * nlookup references on each forget. * * The filesystem may ignore forget calls, if the inodes don't * need to have a limited lifetime. * * On unmount it is not guaranteed, that all referenced inodes * will receive a forget message. * * Valid replies: * fuse_reply_none() */ void (*forget) (fuse_req_t req, fuse_ino_t ino, unsigned long nlookup); /** Get file attributes * * Valid replies: * fuse_reply_attr() * fuse_reply_err() */ void (*getattr)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi); /** Set file attributes * * Valid replies: * fuse_reply_attr() * fuse_reply_err() */ void (*setattr)(fuse_req_t req, fuse_ino_t ino, struct stat *attr, int to_set, struct fuse_file_info *fi); /** Read symbolic link * * Valid replies: * fuse_reply_readlink * fuse_reply_err */ void (*readlink)(fuse_req_t req, fuse_ino_t ino); /** Create file node * * Create a regular file, character device, block device, fifo or * socket node. * * Valid replies: * fuse_reply_entry * fuse_reply_err */ void (*mknod) (fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode, dev_t rdev); /** Create a directory * * Valid replies: * fuse_reply_entry * fuse_reply_err */ void (*mkdir) (fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode); /** Remove a file * * Valid replies: * fuse_reply_err */ void (*unlink) (fuse_req_t req, fuse_ino_t parent, const char *name); /** Remove a directory * * Valid replies: * fuse_reply_err */ void (*rmdir) (fuse_req_t req, fuse_ino_t parent, const char *name); /** Create a symbolic link * * Valid replies: * fuse_reply_entry * fuse_reply_err */ void (*symlink)(fuse_req_t req, const char *link, fuse_ino_t parent, const char *name); /** Rename a file * * Valid replies: * fuse_reply_err */ void (*rename) (fuse_req_t req, fuse_ino_t parent, const char *name, fuse_ino_t newparent, const char *newname); /** Create a hard link * * Valid replies: * fuse_reply_entry * fuse_reply_err */ void (*link) (fuse_req_t req, fuse_ino_t ino, fuse_ino_t newparent, const char *newname); /** Open a file * * Open flags (with the exception of O_CREAT, O_EXCL, O_NOCTTY and * O_TRUNC) are available in fi->flags. * * Filesystem may store an arbitrary file handle (pointer, index, * etc) in fi->fh, and use this in other all other file operations * (read, write, flush, release, fsync). * * Filesystem may also implement stateless file I/O and not store * anything in fi->fh. * * There are also some flags (direct_io, keep_cache) which the * filesystem may set in fi, to change the way the file is opened. * See fuse_file_info structure in for more details. * * Valid replies: * fuse_reply_open * fuse_reply_err */ void (*open) (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi); /** Read data * * Read should send exactly the number of bytes requested except * on EOF or error, otherwise the rest of the data will be * substituted with zeroes. An exception to this is when the file * has been opened in 'direct_io' mode, in which case the return * value of the read system call will reflect the return value of * this operation. * * fi->fh will contain the value set by the open method, or will * be undefined if the open method didn't set any value. * * Valid replies: * fuse_reply_buf * fuse_reply_err */ void (*read) (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi); /** Write data * * Write should return exactly the number of bytes requested * except on error. An exception to this is when the file has * been opened in 'direct_io' mode, in which case the return value * of the write system call will reflect the return value of this * operation. * * fi->fh will contain the value set by the open method, or will * be undefined if the open method didn't set any value. * * Valid replies: * fuse_reply_write * fuse_reply_err */ void (*write) (fuse_req_t req, fuse_ino_t ino, const char *buf, size_t size, off_t off, struct fuse_file_info *fi); /** Flush method * * This is called on each close() of the opened file. * * Since file descriptors can be duplicated (dup, dup2, fork), for * one open call there may be many flush calls. * * fi->fh will contain the value set by the open method, or will * be undefined if the open method didn't set any value. * * NOTE: the name of the method is misleading, since (unlike * fsync) the filesystem is not forced to flush pending writes. * One reason to flush data, is if the filesystem wants to return * write errors. * * Valid replies: * fuse_reply_err */ void (*flush) (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi); /** Release an open file * * Release is called when there are no more references to an open * file: all file descriptors are closed and all memory mappings * are unmapped. * * For every open call there will be exactly one release call. * * The filesystem may reply with an error, but error values are * not returned to close() or munmap() which triggered the * release. * * fi->fh will contain the value set by the open method, or will * be undefined if the open method didn't set any value. * fi->flags will contain the same flags as for open. * * Valid replies: * fuse_reply_err */ void (*release)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi); /** Synchronize file contents * * If the datasync parameter is non-zero, then only the user data * should be flushed, not the meta data. * * Valid replies: * fuse_reply_err */ void (*fsync) (fuse_req_t req, fuse_ino_t ino, int datasync, struct fuse_file_info *fi); /** Open a directory * * Filesystem may store an arbitrary file handle (pointer, index, * etc) in fi->fh, and use this in other all other directory * stream operations (readdir, releasedir, fsyncdir). * * Filesystem may also implement stateless directory I/O and not * store anything in fi->fh, though that makes it impossible to * implement standard conforming directory stream operations in * case the contents of the directory can change between opendir * and releasedir. * * Valid replies: * fuse_reply_open * fuse_reply_err */ void (*opendir)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi); /** Read directory * * Send a buffer filled using fuse_add_dirent(), with size not * exceeding the requested size. Send an empty buffer on end of * stream. * * fi->fh will contain the value set by the opendir method, or * will be undefined if the opendir method didn't set any value. * * Valid replies: * fuse_reply_buf * fuse_reply_err */ void (*readdir)(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi); /** Release an open directory * * For every opendir call there will be exactly one releasedir * call. * * Any errors sent by releasedir will be ignored. * * fi->fh will contain the value set by the opendir method, or * will be undefined if the opendir method didn't set any value. * * Valid replies: * fuse_reply_err */ void (*releasedir)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi); /** Synchronize directory contents * * If the datasync parameter is non-zero, then only the directory * contents should be flushed, not the meta data. * * fi->fh will contain the value set by the opendir method, or * will be undefined if the opendir method didn't set any value. * * Valid replies: * fuse_reply_err */ void (*fsyncdir)(fuse_req_t req, fuse_ino_t ino, int datasync, struct fuse_file_info *fi); /** Get file system statistics * * Valid replies: * fuse_reply_statfs * fuse_reply_err */ void (*statfs) (fuse_req_t req); /** Set an extended attribute * * Valid replies: * fuse_reply_err */ void (*setxattr)(fuse_req_t req, fuse_ino_t ino, const char *name, const char *value, size_t size, int flags); /** Get an extended attribute * * If size is zero, the size of the value should be sent with * fuse_reply_xattr. * * If the size is non-zero, and the value fits in the buffer, the * value should be sent with fuse_reply_buf. * * If the size is too small for the value, the ERANGE error should * be sent. * * Valid replies: * fuse_reply_buf * fuse_reply_xattr * fuse_reply_err */ void (*getxattr)(fuse_req_t req, fuse_ino_t ino, const char *name, size_t size); /** List extended attribute names * * If size is zero, the total size of the attribute list should be * sent with fuse_reply_xattr. * * If the size is non-zero, and the null charater separated * attribute list fits in the buffer, the list should be sent with * fuse_reply_buf. * * If the size is too small for the list, the ERANGE error should * be sent. * * Valid replies: * fuse_reply_buf * fuse_reply_xattr * fuse_reply_err */ void (*listxattr)(fuse_req_t req, fuse_ino_t ino, size_t size); /** Remove an extended attribute * * Valid replies: * fuse_reply_err */ void (*removexattr)(fuse_req_t req, fuse_ino_t ino, const char *name); }; /** Reply with an error code or success (zero) * * all except forget may send an error * * unlink, rmdir, rename, flush, release, fsync, fsyncdir, setxattr * and removexattr may send a succes code */ int fuse_reply_err(fuse_req_t req, int err); /* forget */ int fuse_reply_none(fuse_req_t req); /* lookup, mknod, mkdir, symlink, link */ int fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e); /* getattr, setattr */ int fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout); /* readlink */ int fuse_reply_readlink(fuse_req_t req, const char *link); /* open, opendir */ int fuse_reply_open(fuse_req_t req, const struct fuse_file_info *fi); /* write */ int fuse_reply_write(fuse_req_t req, size_t count); /* read, readdir, getxattr, listxattr */ int fuse_reply_buf(fuse_req_t req, const char *buf, size_t size); /* statfs */ int fuse_reply_statfs(fuse_req_t req, const struct statfs *stbuf); /* getxattr, listxattr */ int fuse_reply_xattr(fuse_req_t req, size_t count); /* ------------------------------------------ */ /* return the size of a directory entry */ size_t fuse_dirent_size(size_t namelen); /* add a directory entry to the buffer */ char *fuse_add_dirent(char *buf, const char *name, const struct stat *stbuf, off_t off); /* ------------------------------------------ */ void *fuse_req_userdata(fuse_req_t req); const struct fuse_ctx *fuse_req_ctx(fuse_req_t req); /* ------------------------------------------ */ int fuse_lowlevel_is_lib_option(const char *opt); struct fuse_session *fuse_lowlevel_new(const char *opts, const struct fuse_lowlevel_ops *op, size_t op_size, void *userdata); struct fuse_chan *fuse_kern_chan_new(int fd); /* ------------------------------------------ */ struct fuse_session_ops { void (*process) (void *data, const char *buf, size_t len, struct fuse_chan *ch); void (*exit) (void *data, int val); int (*exited) (void *data); void (*destroy) (void *data); }; struct fuse_session *fuse_session_new(struct fuse_session_ops *op, void *data); void fuse_session_add_chan(struct fuse_session *se, struct fuse_chan *ch); struct fuse_chan *fuse_session_next_chan(struct fuse_session *se, struct fuse_chan *ch); void fuse_session_process(struct fuse_session *se, const char *buf, size_t len, struct fuse_chan *ch); void fuse_session_destroy(struct fuse_session *se); void fuse_session_exit(struct fuse_session *se); void fuse_session_reset(struct fuse_session *se); int fuse_session_exited(struct fuse_session *se); int fuse_session_loop(struct fuse_session *se); int fuse_session_loop_mt(struct fuse_session *se); /* ------------------------------------------ */ struct fuse_chan_ops { int (*receive)(struct fuse_chan *ch, char *buf, size_t size); int (*send)(struct fuse_chan *ch, const struct iovec iov[], size_t count); void (*destroy)(struct fuse_chan *ch); }; struct fuse_chan *fuse_chan_new(struct fuse_chan_ops *op, int fd, size_t bufsize, void *data); int fuse_chan_fd(struct fuse_chan *ch); size_t fuse_chan_bufsize(struct fuse_chan *ch); void *fuse_chan_data(struct fuse_chan *ch); struct fuse_session *fuse_chan_session(struct fuse_chan *ch); int fuse_chan_receive(struct fuse_chan *ch, char *buf, size_t size); int fuse_chan_send(struct fuse_chan *ch, const struct iovec iov[], size_t count); void fuse_chan_destroy(struct fuse_chan *ch); /* ------------------------------------------ */ #ifdef __cplusplus } #endif #endif /* _FUSE_LOWLEVEL_H_ */