passthrough_ll: add some of the missing operations

New operations added:

 - mkdir
 - mknod
 - symlink
 - link
 - unlink
 - rmdir
 - rename
 - setattr
 - fsyncdir
 - flush
 - fsync
 - statfs
 - fallocate

Caveats:

 - The utimes(2) family of syscalls will fail on symlinks on 4.18 and
   earlier kernels.  Hoping to add support to later kernels.

 - The link(2) and linkat(2) system calls will fail on symlinks unless running
   with privileges (CAP_DAC_READ_SEARCH).

Signed-off-by: Vivek Goyal <vgoyal@redhat.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
This commit is contained in:
Miklos Szeredi
2018-06-19 14:33:21 -04:00
committed by Nikolaus Rath
parent 610125f3c1
commit 4a92a82f2e
+324 -1
View File
@@ -143,6 +143,99 @@ static void lo_getattr(fuse_req_t req, fuse_ino_t ino,
fuse_reply_attr(req, &buf, 1.0);
}
static int utimensat_empty_nofollow(struct lo_inode *inode,
const struct timespec *tv)
{
int res;
char procname[64];
if (inode->is_symlink) {
res = utimensat(inode->fd, "", tv,
AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
if (res == -1 && errno == EINVAL) {
/* Sorry, no race free way to set times on symlink. */
errno = EPERM;
}
return res;
}
sprintf(procname, "/proc/self/fd/%i", inode->fd);
return utimensat(AT_FDCWD, procname, tv, 0);
}
static void lo_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr,
int valid, struct fuse_file_info *fi)
{
int saverr;
char procname[64];
struct lo_inode *inode = lo_inode(req, ino);
int ifd = inode->fd;
int res;
if (valid & FUSE_SET_ATTR_MODE) {
if (fi) {
res = fchmod(fi->fh, attr->st_mode);
} else {
sprintf(procname, "/proc/self/fd/%i", ifd);
res = chmod(procname, attr->st_mode);
}
if (res == -1)
goto out_err;
}
if (valid & (FUSE_SET_ATTR_UID | FUSE_SET_ATTR_GID)) {
uid_t uid = (valid & FUSE_SET_ATTR_UID) ?
attr->st_uid : (uid_t) -1;
gid_t gid = (valid & FUSE_SET_ATTR_GID) ?
attr->st_gid : (gid_t) -1;
res = fchownat(ifd, "", uid, gid,
AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
if (res == -1)
goto out_err;
}
if (valid & FUSE_SET_ATTR_SIZE) {
if (fi) {
res = ftruncate(fi->fh, attr->st_size);
} else {
sprintf(procname, "/proc/self/fd/%i", ifd);
res = truncate(procname, attr->st_size);
}
if (res == -1)
goto out_err;
}
if (valid & (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME)) {
struct timespec tv[2];
tv[0].tv_sec = 0;
tv[1].tv_sec = 0;
tv[0].tv_nsec = UTIME_OMIT;
tv[1].tv_nsec = UTIME_OMIT;
if (valid & FUSE_SET_ATTR_ATIME_NOW)
tv[0].tv_nsec = UTIME_NOW;
else if (valid & FUSE_SET_ATTR_ATIME)
tv[0] = attr->st_atim;
if (valid & FUSE_SET_ATTR_MTIME_NOW)
tv[1].tv_nsec = UTIME_NOW;
else if (valid & FUSE_SET_ATTR_MTIME)
tv[1] = attr->st_mtim;
if (fi)
res = futimens(fi->fh, tv);
else
res = utimensat_empty_nofollow(inode, tv);
if (res == -1)
goto out_err;
}
return lo_getattr(req, ino, fi);
out_err:
saverr = errno;
fuse_reply_err(req, saverr);
}
static struct lo_inode *lo_find(struct lo_data *lo, struct stat *st)
{
struct lo_inode *p;
@@ -240,6 +333,162 @@ static void lo_lookup(fuse_req_t req, fuse_ino_t parent, const char *name)
fuse_reply_entry(req, &e);
}
static void lo_mknod_symlink(fuse_req_t req, fuse_ino_t parent,
const char *name, mode_t mode, dev_t rdev,
const char *link)
{
int newfd = -1;
int res;
int saverr;
struct lo_inode *inode;
struct lo_inode *dir = lo_inode(req, parent);
struct fuse_entry_param e;
saverr = ENOMEM;
inode = calloc(1, sizeof(struct lo_inode));
if (!inode)
goto out;
if (S_ISDIR(mode))
res = mkdirat(dir->fd, name, mode);
else if (S_ISLNK(mode))
res = symlinkat(link, dir->fd, name);
else
res = mknodat(dir->fd, name, mode, rdev);
saverr = errno;
if (res == -1)
goto out;
saverr = lo_do_lookup(req, parent, name, &e);
if (saverr)
goto out;
if (lo_debug(req))
fprintf(stderr, " %lli/%s -> %lli\n",
(unsigned long long) parent, name, (unsigned long long) e.ino);
fuse_reply_entry(req, &e);
return;
out:
if (newfd != -1)
close(newfd);
free(inode);
fuse_reply_err(req, saverr);
}
static void lo_mknod(fuse_req_t req, fuse_ino_t parent,
const char *name, mode_t mode, dev_t rdev)
{
lo_mknod_symlink(req, parent, name, mode, rdev, NULL);
}
static void lo_mkdir(fuse_req_t req, fuse_ino_t parent, const char *name,
mode_t mode)
{
lo_mknod_symlink(req, parent, name, S_IFDIR | mode, 0, NULL);
}
static void lo_symlink(fuse_req_t req, const char *link,
fuse_ino_t parent, const char *name)
{
lo_mknod_symlink(req, parent, name, S_IFLNK, 0, link);
}
static int linkat_empty_nofollow(struct lo_inode *inode, int dfd,
const char *name)
{
int res;
char procname[64];
if (inode->is_symlink) {
res = linkat(inode->fd, "", dfd, name, AT_EMPTY_PATH);
if (res == -1 && (errno == ENOENT || errno == EINVAL)) {
/* Sorry, no race free way to hard-link a symlink. */
errno = EPERM;
}
return res;
}
sprintf(procname, "/proc/self/fd/%i", inode->fd);
return linkat(AT_FDCWD, procname, dfd, name, AT_SYMLINK_FOLLOW);
}
static void lo_link(fuse_req_t req, fuse_ino_t ino, fuse_ino_t parent,
const char *name)
{
int res;
struct lo_data *lo = lo_data(req);
struct lo_inode *inode = lo_inode(req, ino);
struct fuse_entry_param e;
int saverr;
memset(&e, 0, sizeof(struct fuse_entry_param));
e.attr_timeout = 1.0;
e.entry_timeout = 1.0;
res = linkat_empty_nofollow(inode, lo_fd(req, parent), name);
if (res == -1)
goto out_err;
res = fstatat(inode->fd, "", &e.attr, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
if (res == -1)
goto out_err;
pthread_mutex_lock(&lo->mutex);
inode->refcount++;
pthread_mutex_unlock(&lo->mutex);
e.ino = (uintptr_t) inode;
if (lo_debug(req))
fprintf(stderr, " %lli/%s -> %lli\n",
(unsigned long long) parent, name,
(unsigned long long) e.ino);
fuse_reply_entry(req, &e);
return;
out_err:
saverr = errno;
fuse_reply_err(req, saverr);
}
static void lo_rmdir(fuse_req_t req, fuse_ino_t parent, const char *name)
{
int res;
res = unlinkat(lo_fd(req, parent), name, AT_REMOVEDIR);
fuse_reply_err(req, res == -1 ? errno : 0);
}
static void lo_rename(fuse_req_t req, fuse_ino_t parent, const char *name,
fuse_ino_t newparent, const char *newname,
unsigned int flags)
{
int res;
if (flags) {
fuse_reply_err(req, EINVAL);
return;
}
res = renameat(lo_fd(req, parent), name,
lo_fd(req, newparent), newname);
fuse_reply_err(req, res == -1 ? errno : 0);
}
static void lo_unlink(fuse_req_t req, fuse_ino_t parent, const char *name)
{
int res;
res = unlinkat(lo_fd(req, parent), name, 0);
fuse_reply_err(req, res == -1 ? errno : 0);
}
static void unref_inode(struct lo_data *lo, struct lo_inode *inode, uint64_t n)
{
if (!inode)
@@ -478,6 +727,19 @@ static void lo_create(fuse_req_t req, fuse_ino_t parent, const char *name,
fuse_reply_create(req, &e, fi);
}
static void lo_fsyncdir(fuse_req_t req, fuse_ino_t ino, int datasync,
struct fuse_file_info *fi)
{
int res;
int fd = dirfd(lo_dirp(fi)->dp);
(void) ino;
if (datasync)
res = fdatasync(fd);
else
res = fsync(fd);
fuse_reply_err(req, res == -1 ? errno : 0);
}
static void lo_open(fuse_req_t req, fuse_ino_t ino,
struct fuse_file_info *fi)
{
@@ -522,6 +784,26 @@ static void lo_release(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi
fuse_reply_err(req, 0);
}
static void lo_flush(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi)
{
int res;
(void) ino;
res = close(dup(fi->fh));
fuse_reply_err(req, res == -1 ? errno : 0);
}
static void lo_fsync(fuse_req_t req, fuse_ino_t ino, int datasync,
struct fuse_file_info *fi)
{
int res;
(void) ino;
if (datasync)
res = fdatasync(fi->fh);
else
res = fsync(fi->fh);
fuse_reply_err(req, res == -1 ? errno : 0);
}
static void lo_read(fuse_req_t req, fuse_ino_t ino, size_t size,
off_t offset, struct fuse_file_info *fi)
{
@@ -561,21 +843,62 @@ static void lo_write_buf(fuse_req_t req, fuse_ino_t ino,
fuse_reply_write(req, (size_t) res);
}
static void lo_statfs(fuse_req_t req, fuse_ino_t ino)
{
int res;
struct statvfs stbuf;
res = fstatvfs(lo_fd(req, ino), &stbuf);
if (res == -1)
fuse_reply_err(req, errno);
else
fuse_reply_statfs(req, &stbuf);
}
static void lo_fallocate(fuse_req_t req, fuse_ino_t ino, int mode,
off_t offset, off_t length, struct fuse_file_info *fi)
{
int err;
(void) ino;
if (mode) {
fuse_reply_err(req, EOPNOTSUPP);
return;
}
err = posix_fallocate(fi->fh, offset, length);
fuse_reply_err(req, err);
}
static struct fuse_lowlevel_ops lo_oper = {
.init = lo_init,
.lookup = lo_lookup,
.mkdir = lo_mkdir,
.mknod = lo_mknod,
.symlink = lo_symlink,
.link = lo_link,
.unlink = lo_unlink,
.rmdir = lo_rmdir,
.rename = lo_rename,
.forget = lo_forget,
.getattr = lo_getattr,
.setattr = lo_setattr,
.readlink = lo_readlink,
.opendir = lo_opendir,
.readdir = lo_readdir,
.readdirplus = lo_readdirplus,
.releasedir = lo_releasedir,
.fsyncdir = lo_fsyncdir,
.create = lo_create,
.open = lo_open,
.release = lo_release,
.flush = lo_flush,
.fsync = lo_fsync,
.read = lo_read,
.write_buf = lo_write_buf
.write_buf = lo_write_buf,
.statfs = lo_statfs,
.fallocate = lo_fallocate,
};
int main(int argc, char *argv[])