@@ -850,10 +850,14 @@ static int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
int ovl_copy_up_flags(struct dentry *dentry, int flags)
{
- int err = 0;
- const struct cred *old_cred = ovl_override_creds(dentry->d_sb);
+ int err;
+ const struct cred *old_cred;
bool disconnected = (dentry->d_flags & DCACHE_DISCONNECTED);
+ err = ovl_override_creds(dentry->d_sb, &old_cred);
+ if (err)
+ return err;
+
/*
* With NFS export, copy up can get called for a disconnected non-dir.
* In this case, we will copy up lower inode to index dir without
@@ -886,7 +890,7 @@ int ovl_copy_up_flags(struct dentry *dentry, int flags)
dput(parent);
dput(next);
}
- revert_creds(old_cred);
+ ovl_revert_creds(dentry->d_sb, old_cred);
return err;
}
@@ -544,7 +544,9 @@ static int ovl_create_or_link(struct dentry *dentry, struct inode *inode,
if (err)
return err;
- old_cred = ovl_override_creds(dentry->d_sb);
+ err = ovl_override_creds(dentry->d_sb, &old_cred);
+ if (err)
+ return err;
/*
* When linking a file with copy up origin into a new parent, mark the
@@ -579,7 +581,7 @@ static int ovl_create_or_link(struct dentry *dentry, struct inode *inode,
err = ovl_create_over_whiteout(dentry, inode, attr);
}
out_revert_creds:
- revert_creds(old_cred);
+ ovl_revert_creds(dentry->d_sb, old_cred);
return err;
}
@@ -653,9 +655,12 @@ static int ovl_set_link_redirect(struct dentry *dentry)
const struct cred *old_cred;
int err;
- old_cred = ovl_override_creds(dentry->d_sb);
+ err = ovl_override_creds(dentry->d_sb, &old_cred);
+ if (err)
+ return err;
+
err = ovl_set_redirect(dentry, false);
- revert_creds(old_cred);
+ ovl_revert_creds(dentry->d_sb, old_cred);
return err;
}
@@ -846,12 +851,15 @@ static int ovl_do_remove(struct dentry *dentry, bool is_dir)
if (err)
goto out_drop_write;
- old_cred = ovl_override_creds(dentry->d_sb);
+ err = ovl_override_creds(dentry->d_sb, &old_cred);
+ if (err)
+ goto out_drop_write;
+
if (!lower_positive)
err = ovl_remove_upper(dentry, is_dir, &list);
else
err = ovl_remove_and_whiteout(dentry, &list);
- revert_creds(old_cred);
+ ovl_revert_creds(dentry->d_sb, old_cred);
if (!err) {
if (is_dir)
clear_nlink(dentry->d_inode);
@@ -1096,7 +1104,9 @@ static int ovl_rename(struct inode *olddir, struct dentry *old,
update_nlink = true;
}
- old_cred = ovl_override_creds(old->d_sb);
+ err = ovl_override_creds(old->d_sb, &old_cred);
+ if (err)
+ goto out_drop_write;
if (!list_empty(&list)) {
opaquedir = ovl_clear_empty(new, &list);
@@ -1221,7 +1231,7 @@ static int ovl_rename(struct inode *olddir, struct dentry *old,
out_unlock:
unlock_rename(new_upperdir, old_upperdir);
out_revert_creds:
- revert_creds(old_cred);
+ ovl_revert_creds(old->d_sb, old_cred);
if (update_nlink)
ovl_nlink_end(new);
out_drop_write:
@@ -28,11 +28,15 @@ static struct file *ovl_open_realfile(const struct file *file,
struct file *realfile;
const struct cred *old_cred;
int flags = file->f_flags | O_NOATIME | FMODE_NONOTIFY;
+ int err;
+
+ err = ovl_override_creds(inode->i_sb, &old_cred);
+ if (err)
+ return ERR_PTR(err);
- old_cred = ovl_override_creds(inode->i_sb);
realfile = open_with_fake_path(&file->f_path, flags, realinode,
current_cred());
- revert_creds(old_cred);
+ ovl_revert_creds(inode->i_sb, old_cred);
pr_debug("open(%p[%pD2/%c], 0%o) -> (%p, 0%o)\n",
file, file, ovl_whatisit(inode, realinode), file->f_flags,
@@ -174,9 +178,11 @@ static loff_t ovl_llseek(struct file *file, loff_t offset, int whence)
inode_lock(inode);
real.file->f_pos = file->f_pos;
- old_cred = ovl_override_creds(inode->i_sb);
- ret = vfs_llseek(real.file, offset, whence);
- revert_creds(old_cred);
+ ret = ovl_override_creds(inode->i_sb, &old_cred);
+ if (!ret) {
+ ret = vfs_llseek(real.file, offset, whence);
+ ovl_revert_creds(inode->i_sb, old_cred);
+ }
file->f_pos = real.file->f_pos;
inode_unlock(inode);
@@ -228,6 +234,7 @@ static rwf_t ovl_iocb_to_rwf(struct kiocb *iocb)
static ssize_t ovl_read_iter(struct kiocb *iocb, struct iov_iter *iter)
{
struct file *file = iocb->ki_filp;
+ struct inode *inode = file_inode(file);
struct fd real;
const struct cred *old_cred;
ssize_t ret;
@@ -239,13 +246,17 @@ static ssize_t ovl_read_iter(struct kiocb *iocb, struct iov_iter *iter)
if (ret)
return ret;
- old_cred = ovl_override_creds(file_inode(file)->i_sb);
+ ret = ovl_override_creds(inode->i_sb, &old_cred);
+ if (ret)
+ goto out_fdput;
+
ret = vfs_iter_read(real.file, iter, &iocb->ki_pos,
ovl_iocb_to_rwf(iocb));
- revert_creds(old_cred);
+ ovl_revert_creds(inode->i_sb, old_cred);
ovl_file_accessed(file);
+out_fdput:
fdput(real);
return ret;
@@ -273,16 +284,20 @@ static ssize_t ovl_write_iter(struct kiocb *iocb, struct iov_iter *iter)
if (ret)
goto out_unlock;
- old_cred = ovl_override_creds(file_inode(file)->i_sb);
+ ret = ovl_override_creds(inode->i_sb, &old_cred);
+ if (ret)
+ goto out_fdput;
+
file_start_write(real.file);
ret = vfs_iter_write(real.file, iter, &iocb->ki_pos,
ovl_iocb_to_rwf(iocb));
file_end_write(real.file);
- revert_creds(old_cred);
+ ovl_revert_creds(inode->i_sb, old_cred);
/* Update size */
ovl_copyattr(ovl_inode_real(inode), inode);
+out_fdput:
fdput(real);
out_unlock:
@@ -293,6 +308,7 @@ static ssize_t ovl_write_iter(struct kiocb *iocb, struct iov_iter *iter)
static int ovl_fsync(struct file *file, loff_t start, loff_t end, int datasync)
{
+ struct inode *inode = file_inode(file);
struct fd real;
const struct cred *old_cred;
int ret;
@@ -302,10 +318,12 @@ static int ovl_fsync(struct file *file, loff_t start, loff_t end, int datasync)
return ret;
/* Don't sync lower file for fear of receiving EROFS error */
- if (file_inode(real.file) == ovl_inode_upper(file_inode(file))) {
- old_cred = ovl_override_creds(file_inode(file)->i_sb);
- ret = vfs_fsync_range(real.file, start, end, datasync);
- revert_creds(old_cred);
+ if (file_inode(real.file) == ovl_inode_upper(inode)) {
+ ret = ovl_override_creds(inode->i_sb, &old_cred);
+ if (!ret) {
+ ret = vfs_fsync_range(real.file, start, end, datasync);
+ ovl_revert_creds(inode->i_sb, old_cred);
+ }
}
fdput(real);
@@ -316,6 +334,7 @@ static int ovl_fsync(struct file *file, loff_t start, loff_t end, int datasync)
static int ovl_mmap(struct file *file, struct vm_area_struct *vma)
{
struct file *realfile = file->private_data;
+ struct inode *inode = file_inode(file);
const struct cred *old_cred;
int ret;
@@ -327,24 +346,25 @@ static int ovl_mmap(struct file *file, struct vm_area_struct *vma)
vma->vm_file = get_file(realfile);
- old_cred = ovl_override_creds(file_inode(file)->i_sb);
- ret = call_mmap(vma->vm_file, vma);
- revert_creds(old_cred);
-
+ ret = ovl_override_creds(inode->i_sb, &old_cred);
+ if (!ret) {
+ ret = call_mmap(vma->vm_file, vma);
+ ovl_revert_creds(inode->i_sb, old_cred);
+ }
if (ret) {
/* Drop reference count from new vm_file value */
fput(realfile);
} else {
/* Drop reference count from previous vm_file value */
fput(file);
+ ovl_file_accessed(file);
}
- ovl_file_accessed(file);
-
return ret;
}
-static long ovl_fallocate(struct file *file, int mode, loff_t offset, loff_t len)
+static long ovl_fallocate(struct file *file, int mode, loff_t offset,
+ loff_t len)
{
struct inode *inode = file_inode(file);
struct fd real;
@@ -355,13 +375,17 @@ static long ovl_fallocate(struct file *file, int mode, loff_t offset, loff_t len
if (ret)
return ret;
- old_cred = ovl_override_creds(file_inode(file)->i_sb);
+ ret = ovl_override_creds(inode->i_sb, &old_cred);
+ if (ret)
+ goto out_fdput;
+
ret = vfs_fallocate(real.file, mode, offset, len);
- revert_creds(old_cred);
+ ovl_revert_creds(inode->i_sb, old_cred);
/* Update size */
ovl_copyattr(ovl_inode_real(inode), inode);
+out_fdput:
fdput(real);
return ret;
@@ -369,6 +393,7 @@ static long ovl_fallocate(struct file *file, int mode, loff_t offset, loff_t len
static int ovl_fadvise(struct file *file, loff_t offset, loff_t len, int advice)
{
+ struct inode *inode = file_inode(file);
struct fd real;
const struct cred *old_cred;
int ret;
@@ -377,9 +402,11 @@ static int ovl_fadvise(struct file *file, loff_t offset, loff_t len, int advice)
if (ret)
return ret;
- old_cred = ovl_override_creds(file_inode(file)->i_sb);
- ret = vfs_fadvise(real.file, offset, len, advice);
- revert_creds(old_cred);
+ ret = ovl_override_creds(inode->i_sb, &old_cred);
+ if (!ret) {
+ ret = vfs_fadvise(real.file, offset, len, advice);
+ ovl_revert_creds(inode->i_sb, old_cred);
+ }
fdput(real);
@@ -389,6 +416,7 @@ static int ovl_fadvise(struct file *file, loff_t offset, loff_t len, int advice)
static long ovl_real_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
+ struct inode *inode = file_inode(file);
struct fd real;
const struct cred *old_cred;
long ret;
@@ -397,9 +425,11 @@ static long ovl_real_ioctl(struct file *file, unsigned int cmd,
if (ret)
return ret;
- old_cred = ovl_override_creds(file_inode(file)->i_sb);
- ret = vfs_ioctl(real.file, cmd, arg);
- revert_creds(old_cred);
+ ret = ovl_override_creds(inode->i_sb, &old_cred);
+ if (!ret) {
+ ret = vfs_ioctl(real.file, cmd, arg);
+ ovl_revert_creds(inode->i_sb, old_cred);
+ }
fdput(real);
@@ -570,7 +600,10 @@ static loff_t ovl_copyfile(struct file *file_in, loff_t pos_in,
return ret;
}
- old_cred = ovl_override_creds(file_inode(file_out)->i_sb);
+ ret = ovl_override_creds(inode_out->i_sb, &old_cred);
+ if (ret)
+ goto out_fdput;
+
switch (op) {
case OVL_COPY:
ret = vfs_copy_file_range(real_in.file, pos_in,
@@ -588,11 +621,12 @@ static loff_t ovl_copyfile(struct file *file_in, loff_t pos_in,
flags);
break;
}
- revert_creds(old_cred);
+ ovl_revert_creds(inode_out->i_sb, old_cred);
/* Update size */
ovl_copyattr(ovl_inode_real(inode_out), inode_out);
+out_fdput:
fdput(real_in);
fdput(real_out);
@@ -59,9 +59,11 @@ int ovl_setattr(struct dentry *dentry, struct iattr *attr)
attr->ia_valid &= ~ATTR_MODE;
inode_lock(upperdentry->d_inode);
- old_cred = ovl_override_creds(dentry->d_sb);
- err = notify_change(upperdentry, attr, NULL);
- revert_creds(old_cred);
+ err = ovl_override_creds(dentry->d_sb, &old_cred);
+ if (!err) {
+ err = notify_change(upperdentry, attr, NULL);
+ ovl_revert_creds(dentry->d_sb, old_cred);
+ }
if (!err)
ovl_copyattr(upperdentry->d_inode, dentry->d_inode);
inode_unlock(upperdentry->d_inode);
@@ -154,7 +156,10 @@ int ovl_getattr(const struct path *path, struct kstat *stat,
metacopy_blocks = ovl_is_metacopy_dentry(dentry);
type = ovl_path_real(dentry, &realpath);
- old_cred = ovl_override_creds(dentry->d_sb);
+ err = ovl_override_creds(dentry->d_sb, &old_cred);
+ if (err)
+ return err;
+
err = vfs_getattr(&realpath, stat, request_mask, flags);
if (err)
goto out;
@@ -257,7 +262,7 @@ int ovl_getattr(const struct path *path, struct kstat *stat,
stat->nlink = dentry->d_inode->i_nlink;
out:
- revert_creds(old_cred);
+ ovl_revert_creds(dentry->d_sb, old_cred);
return err;
}
@@ -283,7 +288,10 @@ int ovl_permission(struct inode *inode, int mask)
if (err)
return err;
- old_cred = ovl_override_creds(inode->i_sb);
+ err = ovl_override_creds(inode->i_sb, &old_cred);
+ if (err)
+ return err;
+
if (!upperinode &&
!special_file(realinode->i_mode) && mask & MAY_WRITE) {
mask &= ~(MAY_WRITE | MAY_APPEND);
@@ -291,7 +299,7 @@ int ovl_permission(struct inode *inode, int mask)
mask |= MAY_READ;
}
err = inode_permission(realinode, mask);
- revert_creds(old_cred);
+ ovl_revert_creds(inode->i_sb, old_cred);
return err;
}
@@ -302,13 +310,17 @@ static const char *ovl_get_link(struct dentry *dentry,
{
const struct cred *old_cred;
const char *p;
+ int err;
if (!dentry)
return ERR_PTR(-ECHILD);
- old_cred = ovl_override_creds(dentry->d_sb);
+ err = ovl_override_creds(dentry->d_sb, &old_cred);
+ if (err)
+ return ERR_PTR(err);
+
p = vfs_get_link(ovl_dentry_real(dentry), done);
- revert_creds(old_cred);
+ ovl_revert_creds(dentry->d_sb, old_cred);
return p;
}
@@ -344,14 +356,17 @@ int ovl_xattr_set(struct dentry *dentry, struct inode *inode, const char *name,
realdentry = ovl_dentry_upper(dentry);
}
- old_cred = ovl_override_creds(dentry->d_sb);
+ err = ovl_override_creds(dentry->d_sb, &old_cred);
+ if (err)
+ goto out_drop_write;
+
if (value)
err = vfs_setxattr(realdentry, name, value, size, flags);
else {
WARN_ON(flags != XATTR_REPLACE);
err = vfs_removexattr(realdentry, name);
}
- revert_creds(old_cred);
+ ovl_revert_creds(dentry->d_sb, old_cred);
/* copy c/mtime */
ovl_copyattr(d_inode(realdentry), inode);
@@ -370,9 +385,12 @@ int ovl_xattr_get(struct dentry *dentry, struct inode *inode, const char *name,
struct dentry *realdentry =
ovl_i_dentry_upper(inode) ?: ovl_dentry_lower(dentry);
- old_cred = ovl_override_creds(dentry->d_sb);
+ res = ovl_override_creds(dentry->d_sb, &old_cred);
+ if (res)
+ return res;
+
res = vfs_getxattr(realdentry, name, value, size);
- revert_creds(old_cred);
+ ovl_revert_creds(dentry->d_sb, old_cred);
return res;
}
@@ -394,9 +412,13 @@ ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size)
char *s;
const struct cred *old_cred;
- old_cred = ovl_override_creds(dentry->d_sb);
+ res = ovl_override_creds(dentry->d_sb, &old_cred);
+ if (res)
+ return res;
+
res = vfs_listxattr(realdentry, list, size);
- revert_creds(old_cred);
+ ovl_revert_creds(dentry->d_sb, old_cred);
+
if (res <= 0 || size == 0)
return res;
@@ -429,9 +451,11 @@ struct posix_acl *ovl_get_acl(struct inode *inode, int type)
if (!IS_ENABLED(CONFIG_FS_POSIX_ACL) || !IS_POSIXACL(realinode))
return NULL;
- old_cred = ovl_override_creds(inode->i_sb);
+ if (ovl_override_creds(inode->i_sb, &old_cred))
+ return NULL;
+
acl = get_acl(realinode, type);
- revert_creds(old_cred);
+ ovl_revert_creds(inode->i_sb, old_cred);
return acl;
}
@@ -463,13 +487,15 @@ static int ovl_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
if (!realinode->i_op->fiemap)
return -EOPNOTSUPP;
- old_cred = ovl_override_creds(inode->i_sb);
+ err = ovl_override_creds(inode->i_sb, &old_cred);
+ if (err)
+ return err;
if (fieinfo->fi_flags & FIEMAP_FLAG_SYNC)
filemap_write_and_wait(realinode->i_mapping);
err = realinode->i_op->fiemap(realinode, fieinfo, start, len);
- revert_creds(old_cred);
+ ovl_revert_creds(inode->i_sb, old_cred);
return err;
}
@@ -836,7 +836,10 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
if (dentry->d_name.len > ofs->namelen)
return ERR_PTR(-ENAMETOOLONG);
- old_cred = ovl_override_creds(dentry->d_sb);
+ err = ovl_override_creds(dentry->d_sb, &old_cred);
+ if (err)
+ return ERR_PTR(err);
+
upperdir = ovl_dentry_upper(dentry->d_parent);
if (upperdir) {
err = ovl_lookup_layer(upperdir, &d, &upperdentry);
@@ -1074,7 +1077,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
goto out_free_oe;
}
- revert_creds(old_cred);
+ ovl_revert_creds(dentry->d_sb, old_cred);
if (origin_path) {
dput(origin_path->dentry);
kfree(origin_path);
@@ -1101,7 +1104,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
kfree(upperredirect);
out:
kfree(d.redirect);
- revert_creds(old_cred);
+ ovl_revert_creds(dentry->d_sb, old_cred);
return ERR_PTR(err);
}
@@ -1125,7 +1128,9 @@ bool ovl_lower_positive(struct dentry *dentry)
if (!ovl_dentry_upper(dentry))
return true;
- old_cred = ovl_override_creds(dentry->d_sb);
+ if (ovl_override_creds(dentry->d_sb, &old_cred))
+ return false;
+
/* Positive upper -> have to look up lower to see whether it exists */
for (i = 0; !done && !positive && i < poe->numlower; i++) {
struct dentry *this;
@@ -1155,7 +1160,7 @@ bool ovl_lower_positive(struct dentry *dentry)
dput(this);
}
}
- revert_creds(old_cred);
+ ovl_revert_creds(dentry->d_sb, old_cred);
return positive;
}
@@ -204,7 +204,8 @@ static inline bool ovl_open_flags_need_copy_up(int flags)
int ovl_want_write(struct dentry *dentry);
void ovl_drop_write(struct dentry *dentry);
struct dentry *ovl_workdir(struct dentry *dentry);
-const struct cred *ovl_override_creds(struct super_block *sb);
+int ovl_override_creds(struct super_block *sb, const struct cred **old_cred);
+void ovl_revert_creds(struct super_block *sb, const struct cred *old_cred);
struct super_block *ovl_same_sb(struct super_block *sb);
int ovl_can_decode_fh(struct super_block *sb);
struct dentry *ovl_indexdir(struct super_block *sb);
@@ -271,7 +271,9 @@ static int ovl_check_whiteouts(struct dentry *dir, struct ovl_readdir_data *rdd)
struct dentry *dentry;
const struct cred *old_cred;
- old_cred = ovl_override_creds(rdd->dentry->d_sb);
+ err = ovl_override_creds(rdd->dentry->d_sb, &old_cred);
+ if (err)
+ return err;
err = down_write_killable(&dir->d_inode->i_rwsem);
if (!err) {
@@ -286,7 +288,7 @@ static int ovl_check_whiteouts(struct dentry *dir, struct ovl_readdir_data *rdd)
}
inode_unlock(dir->d_inode);
}
- revert_creds(old_cred);
+ ovl_revert_creds(rdd->dentry->d_sb, old_cred);
return err;
}
@@ -920,9 +922,12 @@ int ovl_check_empty_dir(struct dentry *dentry, struct list_head *list)
struct rb_root root = RB_ROOT;
const struct cred *old_cred;
- old_cred = ovl_override_creds(dentry->d_sb);
+ err = ovl_override_creds(dentry->d_sb, &old_cred);
+ if (err)
+ return err;
+
err = ovl_dir_read_merged(dentry, list, &root);
- revert_creds(old_cred);
+ ovl_revert_creds(dentry->d_sb, old_cred);
if (err)
return err;
@@ -33,11 +33,17 @@ struct dentry *ovl_workdir(struct dentry *dentry)
return ofs->workdir;
}
-const struct cred *ovl_override_creds(struct super_block *sb)
+int ovl_override_creds(struct super_block *sb, const struct cred **old_cred)
{
struct ovl_fs *ofs = sb->s_fs_info;
- return override_creds(ofs->creator_cred);
+ *old_cred = override_creds(ofs->creator_cred);
+ return 0;
+}
+
+void ovl_revert_creds(struct super_block *sb, const struct cred *old_cred)
+{
+ revert_creds(old_cred);
}
struct super_block *ovl_same_sb(struct super_block *sb)
@@ -783,7 +789,10 @@ int ovl_nlink_start(struct dentry *dentry)
if (d_is_dir(dentry) || !ovl_test_flag(OVL_INDEX, inode))
goto out;
- old_cred = ovl_override_creds(dentry->d_sb);
+ err = ovl_override_creds(dentry->d_sb, &old_cred);
+ if (err)
+ goto out;
+
/*
* The overlay inode nlink should be incremented/decremented IFF the
* upper operation succeeds, along with nlink change of upper inode.
@@ -791,7 +800,7 @@ int ovl_nlink_start(struct dentry *dentry)
* value relative to the upper inode nlink in an upper inode xattr.
*/
err = ovl_set_nlink_upper(dentry);
- revert_creds(old_cred);
+ ovl_revert_creds(dentry->d_sb, old_cred);
out:
if (err)
@@ -806,10 +815,13 @@ void ovl_nlink_end(struct dentry *dentry)
if (ovl_test_flag(OVL_INDEX, inode) && inode->i_nlink == 0) {
const struct cred *old_cred;
+ int err;
- old_cred = ovl_override_creds(dentry->d_sb);
- ovl_cleanup_index(dentry);
- revert_creds(old_cred);
+ err = ovl_override_creds(dentry->d_sb, &old_cred);
+ if (!err) {
+ ovl_cleanup_index(dentry);
+ ovl_revert_creds(dentry->d_sb, old_cred);
+ }
}
ovl_inode_unlock(inode);
write access to underlying layers is surrounded with ovl_want_write()/ ovl_drop_write() pair and error may be returned when write access is denied. read/lookup access to underlying layers is also surrounded with ovl_override_creds()/revert_creds(), but access cannot be denied. Change all call sites to use ovl_override_creds()/ovl_revert_creds() pair and allow error to be returned when access to underlying layers is denied. This is a prep patch for adding SHUTDOWN ioctl support. Signed-off-by: Amir Goldstein <amir73il@gmail.com> --- fs/overlayfs/copy_up.c | 10 +++-- fs/overlayfs/dir.c | 26 +++++++---- fs/overlayfs/file.c | 94 +++++++++++++++++++++++++++------------- fs/overlayfs/inode.c | 64 +++++++++++++++++++-------- fs/overlayfs/namei.c | 15 ++++--- fs/overlayfs/overlayfs.h | 3 +- fs/overlayfs/readdir.c | 13 ++++-- fs/overlayfs/util.c | 26 ++++++++--- 8 files changed, 174 insertions(+), 77 deletions(-)