From: Darrick J. Wong <djwong@xxxxxxxxxx> Try to implement append-only files correctly. Signed-off-by: "Darrick J. Wong" <djwong@xxxxxxxxxx> --- misc/fuse2fs.c | 50 +++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 39 insertions(+), 11 deletions(-) diff --git a/misc/fuse2fs.c b/misc/fuse2fs.c index 8567d2a8801bb6..52c24715fbc109 100644 --- a/misc/fuse2fs.c +++ b/misc/fuse2fs.c @@ -41,6 +41,7 @@ #include <inttypes.h> #include "ext2fs/ext2fs.h" #include "ext2fs/ext2_fs.h" +#include "ext2fs/ext2fsP.h" #if FUSE_VERSION >= FUSE_MAKE_VERSION(3, 0) # define FUSE_PLATFORM_OPTS "" #else @@ -507,20 +508,26 @@ static inline int want_check_owner(struct fuse2fs *ff, return !is_superuser(ff, ctxt); } +/* Test for append permission */ +#define A_OK 16 + static int check_iflags_access(struct fuse2fs *ff, ext2_ino_t ino, const struct ext2_inode *inode, int mask) { ext2_filsys fs = ff->fs; - /* no writing to read-only or broken fs */ - if ((mask & W_OK) && !fs_writeable(fs)) + EXT2FS_BUILD_BUG_ON((A_OK & (R_OK | W_OK | X_OK | F_OK)) != 0); + + /* no writing or metadata changes to read-only or broken fs */ + if ((mask & (W_OK | A_OK)) && !fs_writeable(fs)) return -EROFS; - dbg_printf(ff, "access ino=%d mask=e%s%s%s iflags=0x%x\n", + dbg_printf(ff, "access ino=%d mask=e%s%s%s%s iflags=0x%x\n", ino, (mask & R_OK ? "r" : ""), (mask & W_OK ? "w" : ""), (mask & X_OK ? "x" : ""), + (mask & A_OK ? "a" : ""), inode->i_flags); /* is immutable? */ @@ -528,6 +535,10 @@ static int check_iflags_access(struct fuse2fs *ff, ext2_ino_t ino, (inode->i_flags & EXT2_IMMUTABLE_FL)) return -EPERM; + /* is append-only? */ + if ((inode->i_flags & EXT2_APPEND_FL) && (mask & W_OK) && !(mask & A_OK)) + return -EPERM; + return 0; } @@ -541,7 +552,7 @@ static int check_inum_access(struct fuse2fs *ff, ext2_ino_t ino, int mask) int ret; /* no writing to read-only or broken fs */ - if ((mask & W_OK) && !fs_writeable(fs)) + if ((mask & (W_OK | A_OK)) && !fs_writeable(fs)) return -EROFS; err = ext2fs_read_inode(fs, ino, &inode); @@ -549,11 +560,12 @@ static int check_inum_access(struct fuse2fs *ff, ext2_ino_t ino, int mask) return translate_error(fs, ino, err); perms = inode.i_mode & 0777; - dbg_printf(ff, "access ino=%d mask=e%s%s%s perms=0%o iflags=0x%x " + dbg_printf(ff, "access ino=%d mask=e%s%s%s%s perms=0%o iflags=0x%x " "fuid=%d fgid=%d uid=%d gid=%d\n", ino, (mask & R_OK ? "r" : ""), (mask & W_OK ? "w" : ""), (mask & X_OK ? "x" : ""), + (mask & A_OK ? "a" : ""), perms, inode.i_flags, inode_uid(inode), inode_gid(inode), ctxt->uid, ctxt->gid); @@ -898,7 +910,7 @@ static int op_mknod(const char *path, mode_t mode, dev_t dev) goto out2; } - ret = check_inum_access(ff, parent, W_OK); + ret = check_inum_access(ff, parent, A_OK | W_OK); if (ret) goto out2; @@ -1029,7 +1041,7 @@ static int op_mkdir(const char *path, mode_t mode) goto out2; } - ret = check_inum_access(ff, parent, W_OK); + ret = check_inum_access(ff, parent, A_OK | W_OK); if (ret) goto out2; @@ -1432,7 +1444,7 @@ static int op_symlink(const char *src, const char *dest) goto out2; } - ret = check_inum_access(ff, parent, W_OK); + ret = check_inum_access(ff, parent, A_OK | W_OK); if (ret) goto out2; @@ -1807,7 +1819,7 @@ static int op_link(const char *src, const char *dest) goto out2; } - ret = check_inum_access(ff, parent, W_OK); + ret = check_inum_access(ff, parent, A_OK | W_OK); if (ret) goto out2; @@ -2128,6 +2140,15 @@ static int __op_open(struct fuse2fs *ff, const char *path, file->open_flags |= EXT2_FILE_WRITE; break; } + if (fp->flags & O_APPEND) { + /* the kernel doesn't allow truncation of an append-only file */ + if (fp->flags & O_TRUNC) { + ret = -EPERM; + goto out; + } + + check |= A_OK; + } detect_linux_executable_open(fp->flags, &check, &file->open_flags); @@ -2938,7 +2959,7 @@ static int op_create(const char *path, mode_t mode, struct fuse_file_info *fp) goto out2; } - ret = check_inum_access(ff, parent, W_OK); + ret = check_inum_access(ff, parent, A_OK | W_OK); if (ret) goto out2; @@ -3110,6 +3131,7 @@ static int op_utimens(const char *path, const struct timespec ctv[2] errcode_t err; ext2_ino_t ino; struct ext2_inode_large inode; + int access = W_OK; int ret = 0; FUSE2FS_CHECK_CONTEXT(ff); @@ -3125,7 +3147,13 @@ static int op_utimens(const char *path, const struct timespec ctv[2] (long long int)ctv[0].tv_sec, ctv[0].tv_nsec, (long long int)ctv[1].tv_sec, ctv[1].tv_nsec); - ret = check_inum_access(ff, ino, W_OK); + /* + * ext4 allows timestamp updates of append-only files but only if we're + * setting to current time + */ + if (ctv[0].tv_nsec == UTIME_NOW && ctv[1].tv_nsec == UTIME_NOW) + access |= A_OK; + ret = check_inum_access(ff, ino, access); if (ret) goto out;