symlinkの階層


[root@localhost dir1]# cat mksymlink1.c
#include <stdio.h>
#include <stddef.h>

void    main()
{
       int     i;
       char    buff[30];

       system("echo \"12345\" > a0");
       for (i = 1; i < 42; i++) {
               sprintf(buff, "ln -s a%d a%d", i - 1, i);
               system(buff);
       }
}
[root@localhost dir1]# ./a.out
[root@localhost dir1]# cat a0
12345
[root@localhost dir1]# cat a40
12345
[root@localhost dir1]# cat a41
cat: a41: シンボリックリンクの階層が多すぎます

[root@localhost dir1]# cat mksymlink2.c
#include <stdio.h>
#include <stddef.h>

void    main(int argc, char* argv[])
{
       int     i;
       char    path[32], buff[100];

       for (i = 1; i < 50; i++) {
               sprintf(buff, "mkdir a%d", i);
               system(buff);
       }

       getcwd(path, 32);
       printf("%s\n", path);
       for (i = 1; i < 50; i++) {
               sprintf(buff, "ln -s %s/a%d a%d", path, i, i - 1);
               system(buff);
       }
}

[root@localhost dir1]# ls  a0/a2/a3/a4/a5/a6/a7/a8/a9/a10/a11/a12/a13/a14/a15/a16/a17/a18/a19/a20/a21/a22/a23/a24/a25/a26/a27/a28/a29/a30/a31/a32/a33/a34/a35/a36/a37/a38/a39/a40
a41

[root@localhost dir1]# ls  a0/a2/a3/a4/a5/a6/a7/a8/a9/a10/a11/a12/a13/a14/a15/a16/a17/a18/a19/a20/a21/a22/a23/a24/a25/a26/a27/a28/a29/a30/a31/a32/a33/a34/a35/a36/a37/a38/a39/a40/a41
ls:  jja0/a2/a3/a4/a5/a6/a7/a8/a9/a10/a11/a12/a13/a14/a15/a16/a17/a18/a19/a20/a21/a22/a23/a24/a25/a26/a27/a28/a29/a30/a31/a32/a33/a34/a35/a36/a37/a38/a39/a40/a41  にアクセスできません: シンボリックリンクの階層が多すぎます

シンボリックリンクは inode_operationsの.symlinkコールバックがコールされ、掛かるinodeのデータブロックに設定されているリンクpathが走査されます。path走査毎にcurrent->total_link_count++とし、 current->total_link_count>= 40でエラーです。.symlinkコールバックが定義されていないファイルシステム(sysfs/procfs等)のfast_symlinkでは、シンボリックリンクpathはstruct nameidata nd.saved_names[MAX_NESTED_LINKS + 1]に設定され、走査時current->link_count++で、current->link_count>MAX_NESTED_LINKS(8)でエラーとなります。
struct nameidata {
       struct path     path;
       struct qstr     last;
       struct path     root;
       struct inode    *inode; /* path.dentry.d_inode */
       unsigned int    flags;
       unsigned        seq;
       int             last_type;
       unsigned        depth;
       char *saved_names[MAX_NESTED_LINKS + 1];

       /* Intent data */
       union {
               struct open_intent open;
       } intent;
};

static int link_path_walk(const char *name, struct nameidata *nd)
{
       struct path next;
       int err;
       
       while (*name=='/')
               name++;
       if (!*name)
               return 0;

       for(;;) {
               struct qstr this;
               long len;
               int type;

               err = may_lookup(nd);
               if (err)
                       break;

               len = hash_name(name, &this.hash);
               this.name = name;
               this.len = len;

               type = LAST_NORM;
               if (name[0] == '.') switch (len) {
                       case 2:
                               if (name[1] == '.') {
                                       type = LAST_DOTDOT;
                                       nd->flags |= LOOKUP_JUMPED;
                               }
                               break;
                       case 1:
                               type = LAST_DOT;
               }
               if (likely(type == LAST_NORM)) {
                       struct dentry *parent = nd->path.dentry;
                       nd->flags &= ~LOOKUP_JUMPED;
                       if (unlikely(parent->d_flags & DCACHE_OP_HASH)) {
                               err = parent->d_op->d_hash(parent, nd->inode,
                                                          &this);
                               if (err < 0)
                                       break;
                       }
               }

               if (!name[len])
                       goto last_component;

               do {
                       len++;
               } while (unlikely(name[len] == '/'));
               if (!name[len])
                       goto last_component;
               name += len;

               err = walk_component(nd, &next, &this, type, LOOKUP_FOLLOW);
               if (err < 0)
                       return err;

               if (err) {
                       err = nested_symlink(&next, nd);
                       if (err)
                               return err;
               }
               if (can_lookup(nd->inode))
                       continue;
               err = -ENOTDIR; 
               break;
               /* here ends the main loop */

last_component:
               nd->last = this;
               nd->last_type = type;
               return 0;
       }
       terminate_walk(nd);
       return err;
}

enum { MAX_NESTED_LINKS = 8 };

static inline int nested_symlink(struct path *path, struct nameidata *nd)
{
       int res;

       if (unlikely(current->link_count >= MAX_NESTED_LINKS)) {
               path_put_conditional(path, nd);
               path_put(&nd->path);
               return -ELOOP;
       }
       BUG_ON(nd->depth >= MAX_NESTED_LINKS);

       nd->depth++;
       current->link_count++;

       do {
               struct path link = *path;
               void *cookie;

               res = follow_link(&link, nd, &cookie);
               if (!res)
                       res = walk_component(nd, path, &nd->last,
                                            nd->last_type, LOOKUP_FOLLOW);
               put_link(nd, &link, cookie);
       } while (res > 0);

       current->link_count--;
       nd->depth--;
       return res;
}

static __always_inline int
follow_link(struct path *link, struct nameidata *nd, void **p)
{
       int error;
       struct dentry *dentry = link->dentry;

       BUG_ON(nd->flags & LOOKUP_RCU);

       if (link->mnt == nd->path.mnt)
               mntget(link->mnt);

       if (unlikely(current->total_link_count >= 40)) {
               *p = ERR_PTR(-ELOOP); /* no ->put_link(), please */
               path_put(&nd->path);
               return -ELOOP;
       }
       cond_resched();
       current->total_link_count++;

       touch_atime(link->mnt, dentry);
       nd_set_link(nd, NULL);

       error = security_inode_follow_link(link->dentry, nd);
       if (error) {
               *p = ERR_PTR(error); /* no ->put_link(), please */
               path_put(&nd->path);
               return error;
       }

       nd->last_type = LAST_BIND;
       *p = dentry->d_inode->i_op->follow_link(dentry, nd);
       error = PTR_ERR(*p);
       if (!IS_ERR(*p)) {
               char *s = nd_get_link(nd);
               error = 0;
               if (s)
                       error = __vfs_follow_link(nd, s);
               else if (nd->last_type == LAST_BIND) {
                       nd->flags |= LOOKUP_JUMPED;
                       nd->inode = nd->path.dentry->d_inode;
                       if (nd->inode->i_op->follow_link) {
                               /* stepped on a _really_ weird one */
                               path_put(&nd->path);
                               error = -ELOOP;
                       }
               }
       }
       return error;
}

備考

ディレクトリにハードクリンクできないのは、ファイル実装故でなく、パス走査での参照でない故、プロセスのrootディレクトリを超えての参照が可能となる運用上の理由からかと思います。

chrootで/lib等をlinkすると、chroot下のlib->/libですが、chroot後はlibは/libで、/lib->/libで多重階層エラーとなります。


最終更新 2017/01/25 18:28:33 - north
(2017/01/25 18:12:26 作成)


検索

アクセス数
3686196
最近のコメント
コアダンプファイル - sakaia
list_head構造体 - yocto_no_yomikata
勧告ロックと強制ロック - wataash
LKMからのファイル出力 - 重松 宏昌
kprobe - ななし
ksetの実装 - スーパーコピー
カーネルスレッドとは - ノース
カーネルスレッドとは - nbyst
asmlinkageってなに? - ノース
asmlinkageってなに? - よろしく
Adsense
広告情報が設定されていません。