Skip to content

Commit 5f8415d

Browse files
amir73ilMiklos Szeredi
authored andcommitted
ovl: persistent overlay inode nlink for indexed inodes
With inodes index enabled, an overlay inode nlink counts the union of upper and non-covered lower hardlinks. During the lifetime of a non-pure upper inode, the following nlink modifying operations can happen: 1. Lower hardlink copy up 2. Upper hardlink created, unlinked or renamed over 3. Lower hardlink whiteout or renamed over For the first, copy up case, the union nlink does not change, whether the operation succeeds or fails, but the upper inode nlink may change. Therefore, before copy up, we store the union nlink value relative to the lower inode nlink in the index inode xattr trusted.overlay.nlink. For the second, upper hardlink case, the union nlink should be incremented or decremented IFF the operation succeeds, aligned with nlink change of the upper inode. Therefore, before link/unlink/rename, we store the union nlink value relative to the upper inode nlink in the index inode. For the last, lower cover up case, we simplify things by preceding the whiteout or cover up with copy up. This makes sure that there is an index upper inode where the nlink xattr can be stored before the copied up upper entry is unlink. Return the overlay inode nlinks for indexed upper inodes on stat(2). Signed-off-by: Amir Goldstein <amir73il@gmail.com> Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
1 parent 59be097 commit 5f8415d

File tree

5 files changed

+204
-3
lines changed

5 files changed

+204
-3
lines changed

fs/overlayfs/copy_up.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,10 @@ static int ovl_link_up(struct dentry *parent, struct dentry *dentry)
323323
struct dentry *upperdir = ovl_dentry_upper(parent);
324324
struct inode *udir = d_inode(upperdir);
325325

326+
err = ovl_set_nlink_lower(dentry);
327+
if (err)
328+
return err;
329+
326330
inode_lock_nested(udir, I_MUTEX_PARENT);
327331
upper = lookup_one_len(dentry->d_name.name, upperdir,
328332
dentry->d_name.len);
@@ -335,6 +339,7 @@ static int ovl_link_up(struct dentry *parent, struct dentry *dentry)
335339
ovl_dentry_set_upper_alias(dentry);
336340
}
337341
inode_unlock(udir);
342+
ovl_set_nlink_upper(dentry);
338343

339344
return err;
340345
}

fs/overlayfs/dir.c

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -591,6 +591,7 @@ static int ovl_link(struct dentry *old, struct inode *newdir,
591591
struct dentry *new)
592592
{
593593
int err;
594+
bool locked = false;
594595
struct inode *inode;
595596

596597
err = ovl_want_write(old);
@@ -601,13 +602,18 @@ static int ovl_link(struct dentry *old, struct inode *newdir,
601602
if (err)
602603
goto out_drop_write;
603604

605+
err = ovl_nlink_start(old, &locked);
606+
if (err)
607+
goto out_drop_write;
608+
604609
inode = d_inode(old);
605610
ihold(inode);
606611

607612
err = ovl_create_or_link(new, inode, NULL, ovl_dentry_upper(old));
608613
if (err)
609614
iput(inode);
610615

616+
ovl_nlink_end(old, locked);
611617
out_drop_write:
612618
ovl_drop_write(old);
613619
out:
@@ -743,8 +749,8 @@ static int ovl_remove_upper(struct dentry *dentry, bool is_dir)
743749

744750
static int ovl_do_remove(struct dentry *dentry, bool is_dir)
745751
{
746-
enum ovl_path_type type;
747752
int err;
753+
bool locked = false;
748754
const struct cred *old_cred;
749755

750756
err = ovl_want_write(dentry);
@@ -755,7 +761,9 @@ static int ovl_do_remove(struct dentry *dentry, bool is_dir)
755761
if (err)
756762
goto out_drop_write;
757763

758-
type = ovl_path_type(dentry);
764+
err = ovl_nlink_start(dentry, &locked);
765+
if (err)
766+
goto out_drop_write;
759767

760768
old_cred = ovl_override_creds(dentry->d_sb);
761769
if (!ovl_lower_positive(dentry))
@@ -769,6 +777,7 @@ static int ovl_do_remove(struct dentry *dentry, bool is_dir)
769777
else
770778
drop_nlink(dentry->d_inode);
771779
}
780+
ovl_nlink_end(dentry, locked);
772781
out_drop_write:
773782
ovl_drop_write(dentry);
774783
out:
@@ -891,6 +900,7 @@ static int ovl_rename(struct inode *olddir, struct dentry *old,
891900
unsigned int flags)
892901
{
893902
int err;
903+
bool locked = false;
894904
struct dentry *old_upperdir;
895905
struct dentry *new_upperdir;
896906
struct dentry *olddentry;
@@ -934,6 +944,10 @@ static int ovl_rename(struct inode *olddir, struct dentry *old,
934944
err = ovl_copy_up(new);
935945
if (err)
936946
goto out_drop_write;
947+
} else {
948+
err = ovl_nlink_start(new, &locked);
949+
if (err)
950+
goto out_drop_write;
937951
}
938952

939953
old_cred = ovl_override_creds(old->d_sb);
@@ -1072,6 +1086,7 @@ static int ovl_rename(struct inode *olddir, struct dentry *old,
10721086
unlock_rename(new_upperdir, old_upperdir);
10731087
out_revert_creds:
10741088
revert_creds(old_cred);
1089+
ovl_nlink_end(new, locked);
10751090
out_drop_write:
10761091
ovl_drop_write(old);
10771092
out:

fs/overlayfs/inode.c

Lines changed: 111 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <linux/cred.h>
1313
#include <linux/xattr.h>
1414
#include <linux/posix_acl.h>
15+
#include <linux/ratelimit.h>
1516
#include "overlayfs.h"
1617

1718
int ovl_setattr(struct dentry *dentry, struct iattr *attr)
@@ -130,6 +131,15 @@ int ovl_getattr(const struct path *path, struct kstat *stat,
130131
if (is_dir && OVL_TYPE_MERGE(type))
131132
stat->nlink = 1;
132133

134+
/*
135+
* Return the overlay inode nlinks for indexed upper inodes.
136+
* Overlay inode nlink counts the union of the upper hardlinks
137+
* and non-covered lower hardlinks. It does not include the upper
138+
* index hardlink.
139+
*/
140+
if (!is_dir && ovl_test_flag(OVL_INDEX, d_inode(dentry)))
141+
stat->nlink = dentry->d_inode->i_nlink;
142+
133143
out:
134144
revert_creds(old_cred);
135145

@@ -442,6 +452,103 @@ static void ovl_fill_inode(struct inode *inode, umode_t mode, dev_t rdev)
442452
}
443453
}
444454

455+
/*
456+
* With inodes index enabled, an overlay inode nlink counts the union of upper
457+
* hardlinks and non-covered lower hardlinks. During the lifetime of a non-pure
458+
* upper inode, the following nlink modifying operations can happen:
459+
*
460+
* 1. Lower hardlink copy up
461+
* 2. Upper hardlink created, unlinked or renamed over
462+
* 3. Lower hardlink whiteout or renamed over
463+
*
464+
* For the first, copy up case, the union nlink does not change, whether the
465+
* operation succeeds or fails, but the upper inode nlink may change.
466+
* Therefore, before copy up, we store the union nlink value relative to the
467+
* lower inode nlink in the index inode xattr trusted.overlay.nlink.
468+
*
469+
* For the second, upper hardlink case, the union nlink should be incremented
470+
* or decremented IFF the operation succeeds, aligned with nlink change of the
471+
* upper inode. Therefore, before link/unlink/rename, we store the union nlink
472+
* value relative to the upper inode nlink in the index inode.
473+
*
474+
* For the last, lower cover up case, we simplify things by preceding the
475+
* whiteout or cover up with copy up. This makes sure that there is an index
476+
* upper inode where the nlink xattr can be stored before the copied up upper
477+
* entry is unlink.
478+
*/
479+
#define OVL_NLINK_ADD_UPPER (1 << 0)
480+
481+
/*
482+
* On-disk format for indexed nlink:
483+
*
484+
* nlink relative to the upper inode - "U[+-]NUM"
485+
* nlink relative to the lower inode - "L[+-]NUM"
486+
*/
487+
488+
static int ovl_set_nlink_common(struct dentry *dentry,
489+
struct dentry *realdentry, const char *format)
490+
{
491+
struct inode *inode = d_inode(dentry);
492+
struct inode *realinode = d_inode(realdentry);
493+
char buf[13];
494+
int len;
495+
496+
len = snprintf(buf, sizeof(buf), format,
497+
(int) (inode->i_nlink - realinode->i_nlink));
498+
499+
return ovl_do_setxattr(ovl_dentry_upper(dentry),
500+
OVL_XATTR_NLINK, buf, len, 0);
501+
}
502+
503+
int ovl_set_nlink_upper(struct dentry *dentry)
504+
{
505+
return ovl_set_nlink_common(dentry, ovl_dentry_upper(dentry), "U%+i");
506+
}
507+
508+
int ovl_set_nlink_lower(struct dentry *dentry)
509+
{
510+
return ovl_set_nlink_common(dentry, ovl_dentry_lower(dentry), "L%+i");
511+
}
512+
513+
static unsigned int ovl_get_nlink(struct dentry *lowerdentry,
514+
struct dentry *upperdentry,
515+
unsigned int fallback)
516+
{
517+
int nlink_diff;
518+
int nlink;
519+
char buf[13];
520+
int err;
521+
522+
if (!lowerdentry || !upperdentry || d_inode(lowerdentry)->i_nlink == 1)
523+
return fallback;
524+
525+
err = vfs_getxattr(upperdentry, OVL_XATTR_NLINK, &buf, sizeof(buf) - 1);
526+
if (err < 0)
527+
goto fail;
528+
529+
buf[err] = '\0';
530+
if ((buf[0] != 'L' && buf[0] != 'U') ||
531+
(buf[1] != '+' && buf[1] != '-'))
532+
goto fail;
533+
534+
err = kstrtoint(buf + 1, 10, &nlink_diff);
535+
if (err < 0)
536+
goto fail;
537+
538+
nlink = d_inode(buf[0] == 'L' ? lowerdentry : upperdentry)->i_nlink;
539+
nlink += nlink_diff;
540+
541+
if (nlink <= 0)
542+
goto fail;
543+
544+
return nlink;
545+
546+
fail:
547+
pr_warn_ratelimited("overlayfs: failed to get index nlink (%pd2, err=%i)\n",
548+
upperdentry, err);
549+
return fallback;
550+
}
551+
445552
struct inode *ovl_new_inode(struct super_block *sb, umode_t mode, dev_t rdev)
446553
{
447554
struct inode *inode;
@@ -495,6 +602,7 @@ struct inode *ovl_get_inode(struct dentry *dentry, struct dentry *upperdentry)
495602
if (!S_ISDIR(realinode->i_mode) &&
496603
(upperdentry || (lowerdentry && ovl_indexdir(dentry->d_sb)))) {
497604
struct inode *key = d_inode(lowerdentry ?: upperdentry);
605+
unsigned int nlink;
498606

499607
inode = iget5_locked(dentry->d_sb, (unsigned long) key,
500608
ovl_inode_test, ovl_inode_set, key);
@@ -515,7 +623,9 @@ struct inode *ovl_get_inode(struct dentry *dentry, struct dentry *upperdentry)
515623
goto out;
516624
}
517625

518-
set_nlink(inode, realinode->i_nlink);
626+
nlink = ovl_get_nlink(lowerdentry, upperdentry,
627+
realinode->i_nlink);
628+
set_nlink(inode, nlink);
519629
} else {
520630
inode = new_inode(dentry->d_sb);
521631
if (!inode)

fs/overlayfs/overlayfs.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ enum ovl_path_type {
2525
#define OVL_XATTR_REDIRECT OVL_XATTR_PREFIX "redirect"
2626
#define OVL_XATTR_ORIGIN OVL_XATTR_PREFIX "origin"
2727
#define OVL_XATTR_IMPURE OVL_XATTR_PREFIX "impure"
28+
#define OVL_XATTR_NLINK OVL_XATTR_PREFIX "nlink"
2829

2930
enum ovl_flag {
3031
OVL_IMPURE,
@@ -229,6 +230,8 @@ void ovl_set_flag(unsigned long flag, struct inode *inode);
229230
bool ovl_test_flag(unsigned long flag, struct inode *inode);
230231
bool ovl_inuse_trylock(struct dentry *dentry);
231232
void ovl_inuse_unlock(struct dentry *dentry);
233+
int ovl_nlink_start(struct dentry *dentry, bool *locked);
234+
void ovl_nlink_end(struct dentry *dentry, bool locked);
232235

233236
static inline bool ovl_is_impuredir(struct dentry *dentry)
234237
{
@@ -258,6 +261,8 @@ int ovl_indexdir_cleanup(struct dentry *dentry, struct vfsmount *mnt,
258261
struct path *lowerstack, unsigned int numlower);
259262

260263
/* inode.c */
264+
int ovl_set_nlink_upper(struct dentry *dentry);
265+
int ovl_set_nlink_lower(struct dentry *dentry);
261266
int ovl_setattr(struct dentry *dentry, struct iattr *attr);
262267
int ovl_getattr(const struct path *path, struct kstat *stat,
263268
u32 request_mask, unsigned int flags);

fs/overlayfs/util.c

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,3 +410,69 @@ void ovl_inuse_unlock(struct dentry *dentry)
410410
spin_unlock(&inode->i_lock);
411411
}
412412
}
413+
414+
/*
415+
* Operations that change overlay inode and upper inode nlink need to be
416+
* synchronized with copy up for persistent nlink accounting.
417+
*/
418+
int ovl_nlink_start(struct dentry *dentry, bool *locked)
419+
{
420+
struct ovl_inode *oi = OVL_I(d_inode(dentry));
421+
const struct cred *old_cred;
422+
int err;
423+
424+
if (!d_inode(dentry) || d_is_dir(dentry))
425+
return 0;
426+
427+
/*
428+
* With inodes index is enabled, we store the union overlay nlink
429+
* in an xattr on the index inode. When whiting out lower hardlinks
430+
* we need to decrement the overlay persistent nlink, but before the
431+
* first copy up, we have no upper index inode to store the xattr.
432+
*
433+
* As a workaround, before whiteout/rename over of a lower hardlink,
434+
* copy up to create the upper index. Creating the upper index will
435+
* initialize the overlay nlink, so it could be dropped if unlink
436+
* or rename succeeds.
437+
*
438+
* TODO: implement metadata only index copy up when called with
439+
* ovl_copy_up_flags(dentry, O_PATH).
440+
*/
441+
if (ovl_indexdir(dentry->d_sb) && !ovl_dentry_has_upper_alias(dentry) &&
442+
d_inode(ovl_dentry_lower(dentry))->i_nlink > 1) {
443+
err = ovl_copy_up(dentry);
444+
if (err)
445+
return err;
446+
}
447+
448+
err = mutex_lock_interruptible(&oi->lock);
449+
if (err)
450+
return err;
451+
452+
if (!ovl_test_flag(OVL_INDEX, d_inode(dentry)))
453+
goto out;
454+
455+
old_cred = ovl_override_creds(dentry->d_sb);
456+
/*
457+
* The overlay inode nlink should be incremented/decremented IFF the
458+
* upper operation succeeds, along with nlink change of upper inode.
459+
* Therefore, before link/unlink/rename, we store the union nlink
460+
* value relative to the upper inode nlink in an upper inode xattr.
461+
*/
462+
err = ovl_set_nlink_upper(dentry);
463+
revert_creds(old_cred);
464+
465+
out:
466+
if (err)
467+
mutex_unlock(&oi->lock);
468+
else
469+
*locked = true;
470+
471+
return err;
472+
}
473+
474+
void ovl_nlink_end(struct dentry *dentry, bool locked)
475+
{
476+
if (locked)
477+
mutex_unlock(&OVL_I(d_inode(dentry))->lock);
478+
}

0 commit comments

Comments
 (0)