]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - fs/namei.c
vfs: check i_nlink limits in vfs_{mkdir,rename_dir,link}
[karo-tx-linux.git] / fs / namei.c
index 71807dc7e402bf5b0b145510f670b726c01b255b..a0b82762e8fcc484972f1235dc877a2eede75c19 100644 (file)
@@ -1381,6 +1381,7 @@ unsigned int full_name_hash(const unsigned char *name, unsigned int len)
                hash = partial_name_hash(*name++, hash);
        return end_name_hash(hash);
 }
+EXPORT_SYMBOL(full_name_hash);
 
 /*
  * We know there's a real path component here of at least
@@ -2161,7 +2162,7 @@ static struct file *do_last(struct nameidata *nd, struct path *path,
                /* sayonara */
                error = complete_walk(nd);
                if (error)
-                       return ERR_PTR(-ECHILD);
+                       return ERR_PTR(error);
 
                error = -ENOTDIR;
                if (nd->flags & LOOKUP_DIRECTORY) {
@@ -2260,7 +2261,7 @@ static struct file *do_last(struct nameidata *nd, struct path *path,
        /* Why this, you ask?  _Now_ we might have grown LOOKUP_JUMPED... */
        error = complete_walk(nd);
        if (error)
-               goto exit;
+               return ERR_PTR(error);
        error = -EISDIR;
        if (S_ISDIR(nd->inode->i_mode))
                goto exit;
@@ -2568,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;
@@ -2580,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);
@@ -2910,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)
@@ -2940,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);
@@ -3049,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,
@@ -3072,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);