Commit f7f1bacc authored by J. R. Okajima's avatar J. R. Okajima
Browse files

aufs: copy-up 1/7, attributes



Copy the inode attributes between branches.
See also the document in this commit.
Signed-off-by: default avatarJ. R. Okajima <hooanon05g@gmail.com>
parent 9f923257
...@@ -178,3 +178,12 @@ simply renames the whiteouted name back and returns an error. If all are ...@@ -178,3 +178,12 @@ simply renames the whiteouted name back and returns an error. If all are
succeeded, aufs registers a function to remove the whiteouted unique succeeded, aufs registers a function to remove the whiteouted unique
temporary name completely and asynchronously to the system global temporary name completely and asynchronously to the system global
workqueue. workqueue.
Copy-up
----------------------------------------------------------------------
It is a well-known feature or concept.
When user modifies a file on a readonly branch, aufs operate "copy-up"
internally and makes change to the new file on the upper writable branch.
When the trigger systemcall does not update the timestamps of the parent
dir, aufs reverts it after copy-up.
...@@ -14,6 +14,7 @@ aufs-y := module.o sbinfo.o super.o branch.o xino.o sysaufs.o opts.o \ ...@@ -14,6 +14,7 @@ aufs-y := module.o sbinfo.o super.o branch.o xino.o sysaufs.o opts.o \
wkq.o vfsub.o dcsub.o \ wkq.o vfsub.o dcsub.o \
cpup.o whout.o \ cpup.o whout.o \
dinfo.o dentry.o \ dinfo.o dentry.o \
dir.o \
iinfo.o inode.o i_op.o iinfo.o inode.o i_op.o
# all are boolean # all are boolean
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include "cpup.h" #include "cpup.h"
#include "dcsub.h" #include "dcsub.h"
#include "dentry.h" #include "dentry.h"
#include "dir.h"
#include "fstype.h" #include "fstype.h"
#include "hbl.h" #include "hbl.h"
#include "inode.h" #include "inode.h"
......
...@@ -470,8 +470,11 @@ int au_br_add(struct super_block *sb, struct au_opt_add *add) ...@@ -470,8 +470,11 @@ int au_br_add(struct super_block *sb, struct au_opt_add *add)
au_br_do_add(sb, add_branch, add_bindex); au_br_do_add(sb, add_branch, add_bindex);
h_dentry = add->path.dentry; h_dentry = add->path.dentry;
if (!add_bindex) if (!add_bindex) {
au_cpup_attr_all(root_inode, /*force*/1);
sb->s_maxbytes = h_dentry->d_sb->s_maxbytes; sb->s_maxbytes = h_dentry->d_sb->s_maxbytes;
} else
au_add_nlink(root_inode, d_inode(h_dentry));
out: out:
return err; return err;
......
...@@ -7,8 +7,81 @@ ...@@ -7,8 +7,81 @@
* copy-up functions, see wbr_policy.c for copy-down * copy-up functions, see wbr_policy.c for copy-down
*/ */
#include <linux/fs_stack.h>
#include "aufs.h" #include "aufs.h"
void au_cpup_attr_flags(struct inode *dst, unsigned int iflags)
{
const unsigned int mask = S_DEAD | S_SWAPFILE | S_PRIVATE
| S_NOATIME | S_NOCMTIME | S_AUTOMOUNT;
BUILD_BUG_ON(sizeof(iflags) != sizeof(dst->i_flags));
dst->i_flags |= iflags & ~mask;
if (au_test_fs_notime(dst->i_sb))
dst->i_flags |= S_NOATIME | S_NOCMTIME;
}
void au_cpup_attr_timesizes(struct inode *inode)
{
struct inode *h_inode;
h_inode = au_h_iptr(inode, au_ibtop(inode));
fsstack_copy_attr_times(inode, h_inode);
fsstack_copy_inode_size(inode, h_inode);
}
void au_cpup_attr_nlink(struct inode *inode, int force)
{
struct inode *h_inode;
struct super_block *sb;
aufs_bindex_t bindex, bbot;
sb = inode->i_sb;
bindex = au_ibtop(inode);
h_inode = au_h_iptr(inode, bindex);
if (!force
&& !S_ISDIR(h_inode->i_mode)
&& au_opt_test(au_mntflags(sb), PLINK)
&& au_plink_test(inode))
return;
/*
* 0 can happen in revalidating.
* h_inode->i_mutex may not be held here, but it is harmless since once
* i_nlink reaches 0, it will never become positive except O_TMPFILE
* case.
* todo: O_TMPFILE+linkat(AT_SYMLINK_FOLLOW) bypassing aufs may cause
* the incorrect link count.
*/
set_nlink(inode, h_inode->i_nlink);
/*
* fewer nlink makes find(1) noisy, but larger nlink doesn't.
* it may includes whplink directory.
*/
if (S_ISDIR(h_inode->i_mode)) {
bbot = au_ibbot(inode);
for (bindex++; bindex <= bbot; bindex++) {
h_inode = au_h_iptr(inode, bindex);
if (h_inode)
au_add_nlink(inode, h_inode);
}
}
}
void au_cpup_attr_changeable(struct inode *inode)
{
struct inode *h_inode;
h_inode = au_h_iptr(inode, au_ibtop(inode));
inode->i_mode = h_inode->i_mode;
inode->i_uid = h_inode->i_uid;
inode->i_gid = h_inode->i_gid;
au_cpup_attr_timesizes(inode);
au_cpup_attr_flags(inode, h_inode->i_flags);
}
void au_cpup_igen(struct inode *inode, struct inode *h_inode) void au_cpup_igen(struct inode *inode, struct inode *h_inode)
{ {
struct au_iinfo *iinfo = au_ii(inode); struct au_iinfo *iinfo = au_ii(inode);
...@@ -18,3 +91,50 @@ void au_cpup_igen(struct inode *inode, struct inode *h_inode) ...@@ -18,3 +91,50 @@ void au_cpup_igen(struct inode *inode, struct inode *h_inode)
iinfo->ii_higen = h_inode->i_generation; iinfo->ii_higen = h_inode->i_generation;
iinfo->ii_hsb1 = h_inode->i_sb; iinfo->ii_hsb1 = h_inode->i_sb;
} }
void au_cpup_attr_all(struct inode *inode, int force)
{
struct inode *h_inode;
h_inode = au_h_iptr(inode, au_ibtop(inode));
au_cpup_attr_changeable(inode);
if (inode->i_nlink > 0)
au_cpup_attr_nlink(inode, force);
inode->i_rdev = h_inode->i_rdev;
inode->i_blkbits = h_inode->i_blkbits;
au_cpup_igen(inode, h_inode);
}
/* ---------------------------------------------------------------------- */
/* Note: dt_dentry and dt_h_dentry are not dget/dput-ed */
/* keep the timestamps of the parent dir when cpup */
void au_dtime_store(struct au_dtime *dt, struct dentry *dentry,
struct path *h_path)
{
struct inode *h_inode;
dt->dt_dentry = dentry;
dt->dt_h_path = *h_path;
h_inode = d_inode(h_path->dentry);
dt->dt_atime = h_inode->i_atime;
dt->dt_mtime = h_inode->i_mtime;
/* smp_mb(); */
}
void au_dtime_revert(struct au_dtime *dt)
{
struct iattr attr;
int err;
attr.ia_atime = dt->dt_atime;
attr.ia_mtime = dt->dt_mtime;
attr.ia_valid = ATTR_FORCE | ATTR_MTIME | ATTR_MTIME_SET
| ATTR_ATIME | ATTR_ATIME_SET;
/* no delegation since this is a directory */
err = vfsub_notify_change(&dt->dt_h_path, &attr, /*delegated*/NULL);
if (unlikely(err))
pr_warn("restoring timestamps failed(%d). ignored\n", err);
}
...@@ -12,9 +12,28 @@ ...@@ -12,9 +12,28 @@
#ifdef __KERNEL__ #ifdef __KERNEL__
#include <linux/path.h>
struct inode; struct inode;
void au_cpup_attr_flags(struct inode *dst, unsigned int iflags);
void au_cpup_attr_timesizes(struct inode *inode);
void au_cpup_attr_nlink(struct inode *inode, int force);
void au_cpup_attr_changeable(struct inode *inode);
void au_cpup_igen(struct inode *inode, struct inode *h_inode); void au_cpup_igen(struct inode *inode, struct inode *h_inode);
void au_cpup_attr_all(struct inode *inode, int force);
/* ---------------------------------------------------------------------- */
/* keep timestamps when copyup */
struct au_dtime {
struct dentry *dt_dentry;
struct path dt_h_path;
struct timespec64 dt_atime, dt_mtime;
};
void au_dtime_store(struct au_dtime *dt, struct dentry *dentry,
struct path *h_path);
void au_dtime_revert(struct au_dtime *dt);
#endif /* __KERNEL__ */ #endif /* __KERNEL__ */
#endif /* __AUFS_CPUP_H__ */ #endif /* __AUFS_CPUP_H__ */
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2005-2019 Junjiro R. Okajima
*/
/*
* directory operations
*/
#include "aufs.h"
void au_add_nlink(struct inode *dir, struct inode *h_dir)
{
unsigned int nlink;
AuDebugOn(!S_ISDIR(dir->i_mode) || !S_ISDIR(h_dir->i_mode));
nlink = dir->i_nlink;
nlink += h_dir->i_nlink - 2;
if (h_dir->i_nlink < 2)
nlink += 2;
smp_mb(); /* for i_nlink */
/* 0 can happen in revaliding */
set_nlink(dir, nlink);
}
void au_sub_nlink(struct inode *dir, struct inode *h_dir)
{
unsigned int nlink;
AuDebugOn(!S_ISDIR(dir->i_mode) || !S_ISDIR(h_dir->i_mode));
nlink = dir->i_nlink;
nlink -= h_dir->i_nlink - 2;
if (h_dir->i_nlink < 2)
nlink -= 2;
smp_mb(); /* for i_nlink */
/* nlink == 0 means the branch-fs is broken */
set_nlink(dir, nlink);
}
struct au_dir_ts_arg {
struct dentry *dentry;
aufs_bindex_t brid;
};
static void au_do_dir_ts(void *arg)
{
struct au_dir_ts_arg *a = arg;
struct au_dtime dt;
struct path h_path;
struct inode *dir, *h_dir;
struct super_block *sb;
struct au_branch *br;
struct au_hinode *hdir;
int err;
aufs_bindex_t btop, bindex;
sb = a->dentry->d_sb;
if (d_really_is_negative(a->dentry))
goto out;
/* no dir->i_mutex lock */
si_read_lock(sb, /*flags*/0); /* noflush */
di_write_lock(a->dentry, AuLsc_DI_CHILD);
dir = d_inode(a->dentry);
btop = au_ibtop(dir);
bindex = au_br_index(sb, a->brid);
if (bindex < btop)
goto out_unlock;
br = au_sbr(sb, bindex);
h_path.dentry = au_h_dptr(a->dentry, bindex);
if (!h_path.dentry)
goto out_unlock;
h_path.mnt = au_br_mnt(br);
au_dtime_store(&dt, a->dentry, &h_path);
br = au_sbr(sb, btop);
if (!au_br_writable(br->br_perm))
goto out_unlock;
h_path.dentry = au_h_dptr(a->dentry, btop);
h_path.mnt = au_br_mnt(br);
err = vfsub_mnt_want_write(h_path.mnt);
if (err)
goto out_unlock;
hdir = au_hi(dir, btop);
inode_lock_nested(hdir->hi_inode, AuLsc_I_PARENT);
h_dir = au_h_iptr(dir, btop);
if (h_dir->i_nlink
&& timespec64_compare(&h_dir->i_mtime, &dt.dt_mtime) < 0) {
dt.dt_h_path = h_path;
au_dtime_revert(&dt);
}
inode_unlock(hdir->hi_inode);
vfsub_mnt_drop_write(h_path.mnt);
au_cpup_attr_timesizes(dir);
out_unlock:
di_write_unlock(a->dentry);
si_read_unlock(sb);
out:
dput(a->dentry);
au_nwt_done(&au_sbi(sb)->si_nowait);
au_kfree_try_rcu(arg);
}
void au_dir_ts(struct inode *dir, aufs_bindex_t bindex)
{
int perm, wkq_err;
aufs_bindex_t btop;
struct au_dir_ts_arg *arg;
struct dentry *dentry;
struct super_block *sb;
IMustLock(dir);
dentry = d_find_any_alias(dir);
AuDebugOn(!dentry);
sb = dentry->d_sb;
btop = au_ibtop(dir);
if (btop == bindex) {
au_cpup_attr_timesizes(dir);
goto out;
}
perm = au_sbr_perm(sb, btop);
if (!au_br_writable(perm))
goto out;
arg = kmalloc(sizeof(*arg), GFP_NOFS);
if (!arg)
goto out;
arg->dentry = dget(dentry); /* will be dput-ted by au_do_dir_ts() */
arg->brid = au_sbr_id(sb, bindex);
wkq_err = au_wkq_nowait(au_do_dir_ts, arg, sb, /*flags*/0);
if (unlikely(wkq_err)) {
pr_err("wkq %d\n", wkq_err);
dput(dentry);
au_kfree_try_rcu(arg);
}
out:
dput(dentry);
}
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2005-2019 Junjiro R. Okajima
*/
/*
* directory operations
*/
#ifndef __AUFS_DIR_H__
#define __AUFS_DIR_H__
#ifdef __KERNEL__
#include <linux/fs.h>
/* ---------------------------------------------------------------------- */
/* dir.c */
void au_add_nlink(struct inode *dir, struct inode *h_dir);
void au_sub_nlink(struct inode *dir, struct inode *h_dir);
void au_dir_ts(struct inode *dir, aufs_bindex_t bsrc);
#endif /* __KERNEL__ */
#endif /* __AUFS_DIR_H__ */
...@@ -247,6 +247,16 @@ static inline int au_test_fs_no_limit_nlink(struct super_block *sb) ...@@ -247,6 +247,16 @@ static inline int au_test_fs_no_limit_nlink(struct super_block *sb)
|| au_test_ubifs(sb); || au_test_ubifs(sb);
} }
/*
* filesystems which sets S_NOATIME and S_NOCMTIME.
*/
static inline int au_test_fs_notime(struct super_block *sb)
{
return au_test_nfs(sb)
|| au_test_ubifs(sb)
;
}
/* ---------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- */
/* /*
......
...@@ -180,6 +180,67 @@ out: ...@@ -180,6 +180,67 @@ out:
/* ---------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- */
struct notify_change_args {
int *errp;
struct path *path;
struct iattr *ia;
struct inode **delegated_inode;
};
static void call_notify_change(void *args)
{
struct notify_change_args *a = args;
struct inode *h_inode;
h_inode = d_inode(a->path->dentry);
IMustLock(h_inode);
*a->errp = -EPERM;
if (!IS_IMMUTABLE(h_inode) && !IS_APPEND(h_inode)) {
lockdep_off();
*a->errp = notify_change(a->path->dentry, a->ia,
a->delegated_inode);
lockdep_on();
}
AuTraceErr(*a->errp);
}
int vfsub_notify_change(struct path *path, struct iattr *ia,
struct inode **delegated_inode)
{
int err;
struct notify_change_args args = {
.errp = &err,
.path = path,
.ia = ia,
.delegated_inode = delegated_inode
};
call_notify_change(&args);
return err;
}
int vfsub_sio_notify_change(struct path *path, struct iattr *ia,
struct inode **delegated_inode)
{
int err, wkq_err;
struct notify_change_args args = {
.errp = &err,
.path = path,
.ia = ia,
.delegated_inode = delegated_inode
};
wkq_err = au_wkq_wait(call_notify_change, &args);
if (unlikely(wkq_err))
err = wkq_err;
return err;
}
/* ---------------------------------------------------------------------- */
struct unlink_args { struct unlink_args {
int *errp; int *errp;
struct inode *dir; struct inode *dir;
......
...@@ -95,6 +95,10 @@ static inline loff_t vfsub_f_size_read(struct file *file) ...@@ -95,6 +95,10 @@ static inline loff_t vfsub_f_size_read(struct file *file)
/* ---------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- */
int vfsub_sio_notify_change(struct path *path, struct iattr *ia,
struct inode **delegated_inode);
int vfsub_notify_change(struct path *path, struct iattr *ia,
struct inode **delegated_inode);
int vfsub_unlink(struct inode *dir, struct path *path, int vfsub_unlink(struct inode *dir, struct path *path,
struct inode **delegated_inode, int force); struct inode **delegated_inode, int force);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment