Skip to content

Commit

Permalink
vfs: check i_nlink limits in vfs_{mkdir,rename_dir,link}
Browse files Browse the repository at this point in the history
New field of struct super_block - ->s_max_links.  Maximal allowed
value of ->i_nlink or 0; in the latter case all checks still need
to be done in ->link/->mkdir/->rename instances.  Note that this
limit applies both to directoris and to non-directories.

Signed-off-by: Al Viro <[email protected]>
  • Loading branch information
Al Viro committed Mar 21, 2012
1 parent c16fa4f commit 8de5277
Show file tree
Hide file tree
Showing 26 changed files with 46 additions and 149 deletions.
13 changes: 1 addition & 12 deletions fs/exofs/namei.c
Original file line number Diff line number Diff line change
Expand Up @@ -143,9 +143,6 @@ static int exofs_link(struct dentry *old_dentry, struct inode *dir,
{
struct inode *inode = old_dentry->d_inode;

if (inode->i_nlink >= EXOFS_LINK_MAX)
return -EMLINK;

inode->i_ctime = CURRENT_TIME;
inode_inc_link_count(inode);
ihold(inode);
Expand All @@ -156,10 +153,7 @@ static int exofs_link(struct dentry *old_dentry, struct inode *dir,
static int exofs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
{
struct inode *inode;
int err = -EMLINK;

if (dir->i_nlink >= EXOFS_LINK_MAX)
goto out;
int err;

inode_inc_link_count(dir);

Expand Down Expand Up @@ -275,11 +269,6 @@ static int exofs_rename(struct inode *old_dir, struct dentry *old_dentry,
if (err)
goto out_dir;
} else {
if (dir_de) {
err = -EMLINK;
if (new_dir->i_nlink >= EXOFS_LINK_MAX)
goto out_dir;
}
err = exofs_add_link(new_dentry, old_inode);
if (err)
goto out_dir;
Expand Down
1 change: 1 addition & 0 deletions fs/exofs/super.c
Original file line number Diff line number Diff line change
Expand Up @@ -754,6 +754,7 @@ static int exofs_fill_super(struct super_block *sb, void *data, int silent)
sb->s_blocksize = EXOFS_BLKSIZE;
sb->s_blocksize_bits = EXOFS_BLKSHIFT;
sb->s_maxbytes = MAX_LFS_FILESIZE;
sb->s_max_links = EXOFS_LINK_MAX;
atomic_set(&sbi->s_curr_pending, 0);
sb->s_bdev = NULL;
sb->s_dev = 0;
Expand Down
13 changes: 1 addition & 12 deletions fs/ext2/namei.c
Original file line number Diff line number Diff line change
Expand Up @@ -195,9 +195,6 @@ static int ext2_link (struct dentry * old_dentry, struct inode * dir,
struct inode *inode = old_dentry->d_inode;
int err;

if (inode->i_nlink >= EXT2_LINK_MAX)
return -EMLINK;

dquot_initialize(dir);

inode->i_ctime = CURRENT_TIME_SEC;
Expand All @@ -217,10 +214,7 @@ static int ext2_link (struct dentry * old_dentry, struct inode * dir,
static int ext2_mkdir(struct inode * dir, struct dentry * dentry, umode_t mode)
{
struct inode * inode;
int err = -EMLINK;

if (dir->i_nlink >= EXT2_LINK_MAX)
goto out;
int err;

dquot_initialize(dir);

Expand Down Expand Up @@ -346,11 +340,6 @@ static int ext2_rename (struct inode * old_dir, struct dentry * old_dentry,
drop_nlink(new_inode);
inode_dec_link_count(new_inode);
} else {
if (dir_de) {
err = -EMLINK;
if (new_dir->i_nlink >= EXT2_LINK_MAX)
goto out_dir;
}
err = ext2_add_link(new_dentry, old_inode);
if (err)
goto out_dir;
Expand Down
1 change: 1 addition & 0 deletions fs/ext2/super.c
Original file line number Diff line number Diff line change
Expand Up @@ -919,6 +919,7 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent)
}

sb->s_maxbytes = ext2_max_size(sb->s_blocksize_bits);
sb->s_max_links = EXT2_LINK_MAX;

if (le32_to_cpu(es->s_rev_level) == EXT2_GOOD_OLD_REV) {
sbi->s_inode_size = EXT2_GOOD_OLD_INODE_SIZE;
Expand Down
13 changes: 0 additions & 13 deletions fs/jfs/namei.c
Original file line number Diff line number Diff line change
Expand Up @@ -220,12 +220,6 @@ static int jfs_mkdir(struct inode *dip, struct dentry *dentry, umode_t mode)

dquot_initialize(dip);

/* link count overflow on parent directory ? */
if (dip->i_nlink == JFS_LINK_MAX) {
rc = -EMLINK;
goto out1;
}

/*
* search parent directory for entry/freespace
* (dtSearch() returns parent directory page pinned)
Expand Down Expand Up @@ -806,9 +800,6 @@ static int jfs_link(struct dentry *old_dentry,
jfs_info("jfs_link: %s %s", old_dentry->d_name.name,
dentry->d_name.name);

if (ip->i_nlink == JFS_LINK_MAX)
return -EMLINK;

dquot_initialize(dir);

tid = txBegin(ip->i_sb, 0);
Expand Down Expand Up @@ -1138,10 +1129,6 @@ static int jfs_rename(struct inode *old_dir, struct dentry *old_dentry,
rc = -ENOTEMPTY;
goto out3;
}
} else if ((new_dir != old_dir) &&
(new_dir->i_nlink == JFS_LINK_MAX)) {
rc = -EMLINK;
goto out3;
}
} else if (new_ip) {
IWRITE_LOCK(new_ip, RDWRLOCK_NORMAL);
Expand Down
1 change: 1 addition & 0 deletions fs/jfs/super.c
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,7 @@ static int jfs_fill_super(struct super_block *sb, void *data, int silent)
return -ENOMEM;

sb->s_fs_info = sbi;
sb->s_max_links = JFS_LINK_MAX;
sbi->sb = sb;
sbi->uid = sbi->gid = sbi->umask = -1;

Expand Down
3 changes: 0 additions & 3 deletions fs/logfs/dir.c
Original file line number Diff line number Diff line change
Expand Up @@ -558,9 +558,6 @@ static int logfs_link(struct dentry *old_dentry, struct inode *dir,
{
struct inode *inode = old_dentry->d_inode;

if (inode->i_nlink >= LOGFS_LINK_MAX)
return -EMLINK;

inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME;
ihold(inode);
inc_nlink(inode);
Expand Down
1 change: 1 addition & 0 deletions fs/logfs/super.c
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,7 @@ static struct dentry *logfs_get_sb_device(struct logfs_super *super,
* the filesystem incompatible with 32bit systems.
*/
sb->s_maxbytes = (1ull << 43) - 1;
sb->s_max_links = LOGFS_LINK_MAX;
sb->s_op = &logfs_super_operations;
sb->s_flags = flags | MS_NOATIME;

Expand Down
10 changes: 5 additions & 5 deletions fs/minix/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -190,24 +190,24 @@ static int minix_fill_super(struct super_block *s, void *data, int silent)
sbi->s_version = MINIX_V1;
sbi->s_dirsize = 16;
sbi->s_namelen = 14;
sbi->s_link_max = MINIX_LINK_MAX;
s->s_max_links = MINIX_LINK_MAX;
} else if (s->s_magic == MINIX_SUPER_MAGIC2) {
sbi->s_version = MINIX_V1;
sbi->s_dirsize = 32;
sbi->s_namelen = 30;
sbi->s_link_max = MINIX_LINK_MAX;
s->s_max_links = MINIX_LINK_MAX;
} else if (s->s_magic == MINIX2_SUPER_MAGIC) {
sbi->s_version = MINIX_V2;
sbi->s_nzones = ms->s_zones;
sbi->s_dirsize = 16;
sbi->s_namelen = 14;
sbi->s_link_max = MINIX2_LINK_MAX;
s->s_max_links = MINIX2_LINK_MAX;
} else if (s->s_magic == MINIX2_SUPER_MAGIC2) {
sbi->s_version = MINIX_V2;
sbi->s_nzones = ms->s_zones;
sbi->s_dirsize = 32;
sbi->s_namelen = 30;
sbi->s_link_max = MINIX2_LINK_MAX;
s->s_max_links = MINIX2_LINK_MAX;
} else if ( *(__u16 *)(bh->b_data + 24) == MINIX3_SUPER_MAGIC) {
m3s = (struct minix3_super_block *) bh->b_data;
s->s_magic = m3s->s_magic;
Expand All @@ -221,9 +221,9 @@ static int minix_fill_super(struct super_block *s, void *data, int silent)
sbi->s_dirsize = 64;
sbi->s_namelen = 60;
sbi->s_version = MINIX_V3;
sbi->s_link_max = MINIX2_LINK_MAX;
sbi->s_mount_state = MINIX_VALID_FS;
sb_set_blocksize(s, m3s->s_blocksize);
s->s_max_links = MINIX2_LINK_MAX;
} else
goto out_no_fs;

Expand Down
1 change: 0 additions & 1 deletion fs/minix/minix.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ struct minix_sb_info {
unsigned long s_max_size;
int s_dirsize;
int s_namelen;
int s_link_max;
struct buffer_head ** s_imap;
struct buffer_head ** s_zmap;
struct buffer_head * s_sbh;
Expand Down
14 changes: 1 addition & 13 deletions fs/minix/namei.c
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,6 @@ static int minix_link(struct dentry * old_dentry, struct inode * dir,
{
struct inode *inode = old_dentry->d_inode;

if (inode->i_nlink >= minix_sb(inode->i_sb)->s_link_max)
return -EMLINK;

inode->i_ctime = CURRENT_TIME_SEC;
inode_inc_link_count(inode);
ihold(inode);
Expand All @@ -106,10 +103,7 @@ static int minix_link(struct dentry * old_dentry, struct inode * dir,
static int minix_mkdir(struct inode * dir, struct dentry *dentry, umode_t mode)
{
struct inode * inode;
int err = -EMLINK;

if (dir->i_nlink >= minix_sb(dir->i_sb)->s_link_max)
goto out;
int err;

inode_inc_link_count(dir);

Expand Down Expand Up @@ -181,7 +175,6 @@ static int minix_rmdir(struct inode * dir, struct dentry *dentry)
static int minix_rename(struct inode * old_dir, struct dentry *old_dentry,
struct inode * new_dir, struct dentry *new_dentry)
{
struct minix_sb_info * info = minix_sb(old_dir->i_sb);
struct inode * old_inode = old_dentry->d_inode;
struct inode * new_inode = new_dentry->d_inode;
struct page * dir_page = NULL;
Expand Down Expand Up @@ -219,11 +212,6 @@ static int minix_rename(struct inode * old_dir, struct dentry *old_dentry,
drop_nlink(new_inode);
inode_dec_link_count(new_inode);
} else {
if (dir_de) {
err = -EMLINK;
if (new_dir->i_nlink >= info->s_link_max)
goto out_dir;
}
err = minix_add_link(new_dentry, old_inode);
if (err)
goto out_dir;
Expand Down
13 changes: 13 additions & 0 deletions fs/namei.c
Original file line number Diff line number Diff line change
Expand Up @@ -2569,6 +2569,7 @@ SYSCALL_DEFINE3(mknod, const char __user *, filename, umode_t, mode, unsigned, d
int vfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
{
int error = may_create(dir, dentry);
unsigned max_links = dir->i_sb->s_max_links;

if (error)
return error;
Expand All @@ -2581,6 +2582,9 @@ int vfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
if (error)
return error;

if (max_links && dir->i_nlink >= max_links)
return -EMLINK;

error = dir->i_op->mkdir(dir, dentry, mode);
if (!error)
fsnotify_mkdir(dir, dentry);
Expand Down Expand Up @@ -2911,6 +2915,7 @@ SYSCALL_DEFINE2(symlink, const char __user *, oldname, const char __user *, newn
int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry)
{
struct inode *inode = old_dentry->d_inode;
unsigned max_links = dir->i_sb->s_max_links;
int error;

if (!inode)
Expand Down Expand Up @@ -2941,6 +2946,8 @@ int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_de
/* Make sure we don't allow creating hardlink to an unlinked file */
if (inode->i_nlink == 0)
error = -ENOENT;
else if (max_links && inode->i_nlink >= max_links)
error = -EMLINK;
else
error = dir->i_op->link(old_dentry, dir, new_dentry);
mutex_unlock(&inode->i_mutex);
Expand Down Expand Up @@ -3050,6 +3057,7 @@ static int vfs_rename_dir(struct inode *old_dir, struct dentry *old_dentry,
{
int error = 0;
struct inode *target = new_dentry->d_inode;
unsigned max_links = new_dir->i_sb->s_max_links;

/*
* If we are going to change the parent - check write permissions,
Expand All @@ -3073,6 +3081,11 @@ static int vfs_rename_dir(struct inode *old_dir, struct dentry *old_dentry,
if (d_mountpoint(old_dentry) || d_mountpoint(new_dentry))
goto out;

error = -EMLINK;
if (max_links && !target && new_dir != old_dir &&
new_dir->i_nlink >= max_links)
goto out;

if (target)
shrink_dcache_parent(new_dentry);
error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry);
Expand Down
11 changes: 0 additions & 11 deletions fs/nilfs2/namei.c
Original file line number Diff line number Diff line change
Expand Up @@ -193,9 +193,6 @@ static int nilfs_link(struct dentry *old_dentry, struct inode *dir,
struct nilfs_transaction_info ti;
int err;

if (inode->i_nlink >= NILFS_LINK_MAX)
return -EMLINK;

err = nilfs_transaction_begin(dir->i_sb, &ti, 1);
if (err)
return err;
Expand All @@ -219,9 +216,6 @@ static int nilfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
struct nilfs_transaction_info ti;
int err;

if (dir->i_nlink >= NILFS_LINK_MAX)
return -EMLINK;

err = nilfs_transaction_begin(dir->i_sb, &ti, 1);
if (err)
return err;
Expand Down Expand Up @@ -400,11 +394,6 @@ static int nilfs_rename(struct inode *old_dir, struct dentry *old_dentry,
drop_nlink(new_inode);
nilfs_mark_inode_dirty(new_inode);
} else {
if (dir_de) {
err = -EMLINK;
if (new_dir->i_nlink >= NILFS_LINK_MAX)
goto out_dir;
}
err = nilfs_add_link(new_dentry, old_inode);
if (err)
goto out_dir;
Expand Down
1 change: 1 addition & 0 deletions fs/nilfs2/super.c
Original file line number Diff line number Diff line change
Expand Up @@ -1059,6 +1059,7 @@ nilfs_fill_super(struct super_block *sb, void *data, int silent)
sb->s_export_op = &nilfs_export_ops;
sb->s_root = NULL;
sb->s_time_gran = 1;
sb->s_max_links = NILFS_LINK_MAX;

bdi = sb->s_bdev->bd_inode->i_mapping->backing_dev_info;
sb->s_bdi = bdi ? : &default_backing_dev_info;
Expand Down
12 changes: 1 addition & 11 deletions fs/sysv/namei.c
Original file line number Diff line number Diff line change
Expand Up @@ -121,9 +121,6 @@ static int sysv_link(struct dentry * old_dentry, struct inode * dir,
{
struct inode *inode = old_dentry->d_inode;

if (inode->i_nlink >= SYSV_SB(inode->i_sb)->s_link_max)
return -EMLINK;

inode->i_ctime = CURRENT_TIME_SEC;
inode_inc_link_count(inode);
ihold(inode);
Expand All @@ -134,10 +131,8 @@ static int sysv_link(struct dentry * old_dentry, struct inode * dir,
static int sysv_mkdir(struct inode * dir, struct dentry *dentry, umode_t mode)
{
struct inode * inode;
int err = -EMLINK;
int err;

if (dir->i_nlink >= SYSV_SB(dir->i_sb)->s_link_max)
goto out;
inode_inc_link_count(dir);

inode = sysv_new_inode(dir, S_IFDIR|mode);
Expand Down Expand Up @@ -251,11 +246,6 @@ static int sysv_rename(struct inode * old_dir, struct dentry * old_dentry,
drop_nlink(new_inode);
inode_dec_link_count(new_inode);
} else {
if (dir_de) {
err = -EMLINK;
if (new_dir->i_nlink >= SYSV_SB(new_dir->i_sb)->s_link_max)
goto out_dir;
}
err = sysv_add_link(new_dentry, old_inode);
if (err)
goto out_dir;
Expand Down
Loading

0 comments on commit 8de5277

Please sign in to comment.