diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2011-12-07 11:14:42 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-12-07 11:14:42 -0500 |
commit | 3172f8fe1ca1d432b196efad453c0ceb89302075 (patch) | |
tree | 0b9e8af7ea9ee9883ff1a834357694254200335a | |
parent | b835c0f47f725d864bf2545f10c733b754bb6d51 (diff) | |
parent | 02125a826459a6ad142f8d91c5b6357562f96615 (diff) |
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs:
fix apparmor dereferencing potentially freed dentry, sanitize __d_path() API
-rw-r--r-- | fs/dcache.c | 71 | ||||
-rw-r--r-- | fs/namespace.c | 20 | ||||
-rw-r--r-- | fs/seq_file.c | 6 | ||||
-rw-r--r-- | include/linux/dcache.h | 3 | ||||
-rw-r--r-- | include/linux/fs.h | 1 | ||||
-rw-r--r-- | security/apparmor/path.c | 65 | ||||
-rw-r--r-- | security/tomoyo/realpath.c | 3 |
7 files changed, 100 insertions, 69 deletions
diff --git a/fs/dcache.c b/fs/dcache.c index 10ba92def3f6..89509b5a090e 100644 --- a/fs/dcache.c +++ b/fs/dcache.c | |||
@@ -2439,16 +2439,14 @@ static int prepend_name(char **buffer, int *buflen, struct qstr *name) | |||
2439 | /** | 2439 | /** |
2440 | * prepend_path - Prepend path string to a buffer | 2440 | * prepend_path - Prepend path string to a buffer |
2441 | * @path: the dentry/vfsmount to report | 2441 | * @path: the dentry/vfsmount to report |
2442 | * @root: root vfsmnt/dentry (may be modified by this function) | 2442 | * @root: root vfsmnt/dentry |
2443 | * @buffer: pointer to the end of the buffer | 2443 | * @buffer: pointer to the end of the buffer |
2444 | * @buflen: pointer to buffer length | 2444 | * @buflen: pointer to buffer length |
2445 | * | 2445 | * |
2446 | * Caller holds the rename_lock. | 2446 | * Caller holds the rename_lock. |
2447 | * | ||
2448 | * If path is not reachable from the supplied root, then the value of | ||
2449 | * root is changed (without modifying refcounts). | ||
2450 | */ | 2447 | */ |
2451 | static int prepend_path(const struct path *path, struct path *root, | 2448 | static int prepend_path(const struct path *path, |
2449 | const struct path *root, | ||
2452 | char **buffer, int *buflen) | 2450 | char **buffer, int *buflen) |
2453 | { | 2451 | { |
2454 | struct dentry *dentry = path->dentry; | 2452 | struct dentry *dentry = path->dentry; |
@@ -2483,10 +2481,10 @@ static int prepend_path(const struct path *path, struct path *root, | |||
2483 | dentry = parent; | 2481 | dentry = parent; |
2484 | } | 2482 | } |
2485 | 2483 | ||
2486 | out: | ||
2487 | if (!error && !slash) | 2484 | if (!error && !slash) |
2488 | error = prepend(buffer, buflen, "/", 1); | 2485 | error = prepend(buffer, buflen, "/", 1); |
2489 | 2486 | ||
2487 | out: | ||
2490 | br_read_unlock(vfsmount_lock); | 2488 | br_read_unlock(vfsmount_lock); |
2491 | return error; | 2489 | return error; |
2492 | 2490 | ||
@@ -2500,15 +2498,17 @@ global_root: | |||
2500 | WARN(1, "Root dentry has weird name <%.*s>\n", | 2498 | WARN(1, "Root dentry has weird name <%.*s>\n", |
2501 | (int) dentry->d_name.len, dentry->d_name.name); | 2499 | (int) dentry->d_name.len, dentry->d_name.name); |
2502 | } | 2500 | } |
2503 | root->mnt = vfsmnt; | 2501 | if (!slash) |
2504 | root->dentry = dentry; | 2502 | error = prepend(buffer, buflen, "/", 1); |
2503 | if (!error) | ||
2504 | error = vfsmnt->mnt_ns ? 1 : 2; | ||
2505 | goto out; | 2505 | goto out; |
2506 | } | 2506 | } |
2507 | 2507 | ||
2508 | /** | 2508 | /** |
2509 | * __d_path - return the path of a dentry | 2509 | * __d_path - return the path of a dentry |
2510 | * @path: the dentry/vfsmount to report | 2510 | * @path: the dentry/vfsmount to report |
2511 | * @root: root vfsmnt/dentry (may be modified by this function) | 2511 | * @root: root vfsmnt/dentry |
2512 | * @buf: buffer to return value in | 2512 | * @buf: buffer to return value in |
2513 | * @buflen: buffer length | 2513 | * @buflen: buffer length |
2514 | * | 2514 | * |
@@ -2519,10 +2519,10 @@ global_root: | |||
2519 | * | 2519 | * |
2520 | * "buflen" should be positive. | 2520 | * "buflen" should be positive. |
2521 | * | 2521 | * |
2522 | * If path is not reachable from the supplied root, then the value of | 2522 | * If the path is not reachable from the supplied root, return %NULL. |
2523 | * root is changed (without modifying refcounts). | ||
2524 | */ | 2523 | */ |
2525 | char *__d_path(const struct path *path, struct path *root, | 2524 | char *__d_path(const struct path *path, |
2525 | const struct path *root, | ||
2526 | char *buf, int buflen) | 2526 | char *buf, int buflen) |
2527 | { | 2527 | { |
2528 | char *res = buf + buflen; | 2528 | char *res = buf + buflen; |
@@ -2533,7 +2533,28 @@ char *__d_path(const struct path *path, struct path *root, | |||
2533 | error = prepend_path(path, root, &res, &buflen); | 2533 | error = prepend_path(path, root, &res, &buflen); |
2534 | write_sequnlock(&rename_lock); | 2534 | write_sequnlock(&rename_lock); |
2535 | 2535 | ||
2536 | if (error) | 2536 | if (error < 0) |
2537 | return ERR_PTR(error); | ||
2538 | if (error > 0) | ||
2539 | return NULL; | ||
2540 | return res; | ||
2541 | } | ||
2542 | |||
2543 | char *d_absolute_path(const struct path *path, | ||
2544 | char *buf, int buflen) | ||
2545 | { | ||
2546 | struct path root = {}; | ||
2547 | char *res = buf + buflen; | ||
2548 | int error; | ||
2549 | |||
2550 | prepend(&res, &buflen, "\0", 1); | ||
2551 | write_seqlock(&rename_lock); | ||
2552 | error = prepend_path(path, &root, &res, &buflen); | ||
2553 | write_sequnlock(&rename_lock); | ||
2554 | |||
2555 | if (error > 1) | ||
2556 | error = -EINVAL; | ||
2557 | if (error < 0) | ||
2537 | return ERR_PTR(error); | 2558 | return ERR_PTR(error); |
2538 | return res; | 2559 | return res; |
2539 | } | 2560 | } |
@@ -2541,8 +2562,9 @@ char *__d_path(const struct path *path, struct path *root, | |||
2541 | /* | 2562 | /* |
2542 | * same as __d_path but appends "(deleted)" for unlinked files. | 2563 | * same as __d_path but appends "(deleted)" for unlinked files. |
2543 | */ | 2564 | */ |
2544 | static int path_with_deleted(const struct path *path, struct path *root, | 2565 | static int path_with_deleted(const struct path *path, |
2545 | char **buf, int *buflen) | 2566 | const struct path *root, |
2567 | char **buf, int *buflen) | ||
2546 | { | 2568 | { |
2547 | prepend(buf, buflen, "\0", 1); | 2569 | prepend(buf, buflen, "\0", 1); |
2548 | if (d_unlinked(path->dentry)) { | 2570 | if (d_unlinked(path->dentry)) { |
@@ -2579,7 +2601,6 @@ char *d_path(const struct path *path, char *buf, int buflen) | |||
2579 | { | 2601 | { |
2580 | char *res = buf + buflen; | 2602 | char *res = buf + buflen; |
2581 | struct path root; | 2603 | struct path root; |
2582 | struct path tmp; | ||
2583 | int error; | 2604 | int error; |
2584 | 2605 | ||
2585 | /* | 2606 | /* |
@@ -2594,9 +2615,8 @@ char *d_path(const struct path *path, char *buf, int buflen) | |||
2594 | 2615 | ||
2595 | get_fs_root(current->fs, &root); | 2616 | get_fs_root(current->fs, &root); |
2596 | write_seqlock(&rename_lock); | 2617 | write_seqlock(&rename_lock); |
2597 | tmp = root; | 2618 | error = path_with_deleted(path, &root, &res, &buflen); |
2598 | error = path_with_deleted(path, &tmp, &res, &buflen); | 2619 | if (error < 0) |
2599 | if (error) | ||
2600 | res = ERR_PTR(error); | 2620 | res = ERR_PTR(error); |
2601 | write_sequnlock(&rename_lock); | 2621 | write_sequnlock(&rename_lock); |
2602 | path_put(&root); | 2622 | path_put(&root); |
@@ -2617,7 +2637,6 @@ char *d_path_with_unreachable(const struct path *path, char *buf, int buflen) | |||
2617 | { | 2637 | { |
2618 | char *res = buf + buflen; | 2638 | char *res = buf + buflen; |
2619 | struct path root; | 2639 | struct path root; |
2620 | struct path tmp; | ||
2621 | int error; | 2640 | int error; |
2622 | 2641 | ||
2623 | if (path->dentry->d_op && path->dentry->d_op->d_dname) | 2642 | if (path->dentry->d_op && path->dentry->d_op->d_dname) |
@@ -2625,9 +2644,8 @@ char *d_path_with_unreachable(const struct path *path, char *buf, int buflen) | |||
2625 | 2644 | ||
2626 | get_fs_root(current->fs, &root); | 2645 | get_fs_root(current->fs, &root); |
2627 | write_seqlock(&rename_lock); | 2646 | write_seqlock(&rename_lock); |
2628 | tmp = root; | 2647 | error = path_with_deleted(path, &root, &res, &buflen); |
2629 | error = path_with_deleted(path, &tmp, &res, &buflen); | 2648 | if (error > 0) |
2630 | if (!error && !path_equal(&tmp, &root)) | ||
2631 | error = prepend_unreachable(&res, &buflen); | 2649 | error = prepend_unreachable(&res, &buflen); |
2632 | write_sequnlock(&rename_lock); | 2650 | write_sequnlock(&rename_lock); |
2633 | path_put(&root); | 2651 | path_put(&root); |
@@ -2758,19 +2776,18 @@ SYSCALL_DEFINE2(getcwd, char __user *, buf, unsigned long, size) | |||
2758 | write_seqlock(&rename_lock); | 2776 | write_seqlock(&rename_lock); |
2759 | if (!d_unlinked(pwd.dentry)) { | 2777 | if (!d_unlinked(pwd.dentry)) { |
2760 | unsigned long len; | 2778 | unsigned long len; |
2761 | struct path tmp = root; | ||
2762 | char *cwd = page + PAGE_SIZE; | 2779 | char *cwd = page + PAGE_SIZE; |
2763 | int buflen = PAGE_SIZE; | 2780 | int buflen = PAGE_SIZE; |
2764 | 2781 | ||
2765 | prepend(&cwd, &buflen, "\0", 1); | 2782 | prepend(&cwd, &buflen, "\0", 1); |
2766 | error = prepend_path(&pwd, &tmp, &cwd, &buflen); | 2783 | error = prepend_path(&pwd, &root, &cwd, &buflen); |
2767 | write_sequnlock(&rename_lock); | 2784 | write_sequnlock(&rename_lock); |
2768 | 2785 | ||
2769 | if (error) | 2786 | if (error < 0) |
2770 | goto out; | 2787 | goto out; |
2771 | 2788 | ||
2772 | /* Unreachable from current root */ | 2789 | /* Unreachable from current root */ |
2773 | if (!path_equal(&tmp, &root)) { | 2790 | if (error > 0) { |
2774 | error = prepend_unreachable(&cwd, &buflen); | 2791 | error = prepend_unreachable(&cwd, &buflen); |
2775 | if (error) | 2792 | if (error) |
2776 | goto out; | 2793 | goto out; |
diff --git a/fs/namespace.c b/fs/namespace.c index 6d3a1963879b..cfc6d4448aa5 100644 --- a/fs/namespace.c +++ b/fs/namespace.c | |||
@@ -1048,15 +1048,12 @@ static int show_mountinfo(struct seq_file *m, void *v) | |||
1048 | if (err) | 1048 | if (err) |
1049 | goto out; | 1049 | goto out; |
1050 | seq_putc(m, ' '); | 1050 | seq_putc(m, ' '); |
1051 | seq_path_root(m, &mnt_path, &root, " \t\n\\"); | 1051 | |
1052 | if (root.mnt != p->root.mnt || root.dentry != p->root.dentry) { | 1052 | /* mountpoints outside of chroot jail will give SEQ_SKIP on this */ |
1053 | /* | 1053 | err = seq_path_root(m, &mnt_path, &root, " \t\n\\"); |
1054 | * Mountpoint is outside root, discard that one. Ugly, | 1054 | if (err) |
1055 | * but less so than trying to do that in iterator in a | 1055 | goto out; |
1056 | * race-free way (due to renames). | 1056 | |
1057 | */ | ||
1058 | return SEQ_SKIP; | ||
1059 | } | ||
1060 | seq_puts(m, mnt->mnt_flags & MNT_READONLY ? " ro" : " rw"); | 1057 | seq_puts(m, mnt->mnt_flags & MNT_READONLY ? " ro" : " rw"); |
1061 | show_mnt_opts(m, mnt); | 1058 | show_mnt_opts(m, mnt); |
1062 | 1059 | ||
@@ -2776,3 +2773,8 @@ void kern_unmount(struct vfsmount *mnt) | |||
2776 | } | 2773 | } |
2777 | } | 2774 | } |
2778 | EXPORT_SYMBOL(kern_unmount); | 2775 | EXPORT_SYMBOL(kern_unmount); |
2776 | |||
2777 | bool our_mnt(struct vfsmount *mnt) | ||
2778 | { | ||
2779 | return check_mnt(mnt); | ||
2780 | } | ||
diff --git a/fs/seq_file.c b/fs/seq_file.c index 05d6b0e78c95..dba43c3ea3af 100644 --- a/fs/seq_file.c +++ b/fs/seq_file.c | |||
@@ -449,8 +449,6 @@ EXPORT_SYMBOL(seq_path); | |||
449 | 449 | ||
450 | /* | 450 | /* |
451 | * Same as seq_path, but relative to supplied root. | 451 | * Same as seq_path, but relative to supplied root. |
452 | * | ||
453 | * root may be changed, see __d_path(). | ||
454 | */ | 452 | */ |
455 | int seq_path_root(struct seq_file *m, struct path *path, struct path *root, | 453 | int seq_path_root(struct seq_file *m, struct path *path, struct path *root, |
456 | char *esc) | 454 | char *esc) |
@@ -463,6 +461,8 @@ int seq_path_root(struct seq_file *m, struct path *path, struct path *root, | |||
463 | char *p; | 461 | char *p; |
464 | 462 | ||
465 | p = __d_path(path, root, buf, size); | 463 | p = __d_path(path, root, buf, size); |
464 | if (!p) | ||
465 | return SEQ_SKIP; | ||
466 | res = PTR_ERR(p); | 466 | res = PTR_ERR(p); |
467 | if (!IS_ERR(p)) { | 467 | if (!IS_ERR(p)) { |
468 | char *end = mangle_path(buf, p, esc); | 468 | char *end = mangle_path(buf, p, esc); |
@@ -474,7 +474,7 @@ int seq_path_root(struct seq_file *m, struct path *path, struct path *root, | |||
474 | } | 474 | } |
475 | seq_commit(m, res); | 475 | seq_commit(m, res); |
476 | 476 | ||
477 | return res < 0 ? res : 0; | 477 | return res < 0 && res != -ENAMETOOLONG ? res : 0; |
478 | } | 478 | } |
479 | 479 | ||
480 | /* | 480 | /* |
diff --git a/include/linux/dcache.h b/include/linux/dcache.h index 4df926199369..ed9f74f6c519 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h | |||
@@ -339,7 +339,8 @@ extern int d_validate(struct dentry *, struct dentry *); | |||
339 | */ | 339 | */ |
340 | extern char *dynamic_dname(struct dentry *, char *, int, const char *, ...); | 340 | extern char *dynamic_dname(struct dentry *, char *, int, const char *, ...); |
341 | 341 | ||
342 | extern char *__d_path(const struct path *path, struct path *root, char *, int); | 342 | extern char *__d_path(const struct path *, const struct path *, char *, int); |
343 | extern char *d_absolute_path(const struct path *, char *, int); | ||
343 | extern char *d_path(const struct path *, char *, int); | 344 | extern char *d_path(const struct path *, char *, int); |
344 | extern char *d_path_with_unreachable(const struct path *, char *, int); | 345 | extern char *d_path_with_unreachable(const struct path *, char *, int); |
345 | extern char *dentry_path_raw(struct dentry *, char *, int); | 346 | extern char *dentry_path_raw(struct dentry *, char *, int); |
diff --git a/include/linux/fs.h b/include/linux/fs.h index e3130220ce3e..019dc558df1a 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h | |||
@@ -1942,6 +1942,7 @@ extern int fd_statfs(int, struct kstatfs *); | |||
1942 | extern int statfs_by_dentry(struct dentry *, struct kstatfs *); | 1942 | extern int statfs_by_dentry(struct dentry *, struct kstatfs *); |
1943 | extern int freeze_super(struct super_block *super); | 1943 | extern int freeze_super(struct super_block *super); |
1944 | extern int thaw_super(struct super_block *super); | 1944 | extern int thaw_super(struct super_block *super); |
1945 | extern bool our_mnt(struct vfsmount *mnt); | ||
1945 | 1946 | ||
1946 | extern int current_umask(void); | 1947 | extern int current_umask(void); |
1947 | 1948 | ||
diff --git a/security/apparmor/path.c b/security/apparmor/path.c index 36cc0cc39e78..b566eba4a65c 100644 --- a/security/apparmor/path.c +++ b/security/apparmor/path.c | |||
@@ -57,23 +57,44 @@ static int prepend(char **buffer, int buflen, const char *str, int namelen) | |||
57 | static int d_namespace_path(struct path *path, char *buf, int buflen, | 57 | static int d_namespace_path(struct path *path, char *buf, int buflen, |
58 | char **name, int flags) | 58 | char **name, int flags) |
59 | { | 59 | { |
60 | struct path root, tmp; | ||
61 | char *res; | 60 | char *res; |
62 | int connected, error = 0; | 61 | int error = 0; |
62 | int connected = 1; | ||
63 | |||
64 | if (path->mnt->mnt_flags & MNT_INTERNAL) { | ||
65 | /* it's not mounted anywhere */ | ||
66 | res = dentry_path(path->dentry, buf, buflen); | ||
67 | *name = res; | ||
68 | if (IS_ERR(res)) { | ||
69 | *name = buf; | ||
70 | return PTR_ERR(res); | ||
71 | } | ||
72 | if (path->dentry->d_sb->s_magic == PROC_SUPER_MAGIC && | ||
73 | strncmp(*name, "/sys/", 5) == 0) { | ||
74 | /* TODO: convert over to using a per namespace | ||
75 | * control instead of hard coded /proc | ||
76 | */ | ||
77 | return prepend(name, *name - buf, "/proc", 5); | ||
78 | } | ||
79 | return 0; | ||
80 | } | ||
63 | 81 | ||
64 | /* Get the root we want to resolve too, released below */ | 82 | /* resolve paths relative to chroot?*/ |
65 | if (flags & PATH_CHROOT_REL) { | 83 | if (flags & PATH_CHROOT_REL) { |
66 | /* resolve paths relative to chroot */ | 84 | struct path root; |
67 | get_fs_root(current->fs, &root); | 85 | get_fs_root(current->fs, &root); |
68 | } else { | 86 | res = __d_path(path, &root, buf, buflen); |
69 | /* resolve paths relative to namespace */ | 87 | if (res && !IS_ERR(res)) { |
70 | root.mnt = current->nsproxy->mnt_ns->root; | 88 | /* everything's fine */ |
71 | root.dentry = root.mnt->mnt_root; | 89 | *name = res; |
72 | path_get(&root); | 90 | path_put(&root); |
91 | goto ok; | ||
92 | } | ||
93 | path_put(&root); | ||
94 | connected = 0; | ||
73 | } | 95 | } |
74 | 96 | ||
75 | tmp = root; | 97 | res = d_absolute_path(path, buf, buflen); |
76 | res = __d_path(path, &tmp, buf, buflen); | ||
77 | 98 | ||
78 | *name = res; | 99 | *name = res; |
79 | /* handle error conditions - and still allow a partial path to | 100 | /* handle error conditions - and still allow a partial path to |
@@ -84,7 +105,10 @@ static int d_namespace_path(struct path *path, char *buf, int buflen, | |||
84 | *name = buf; | 105 | *name = buf; |
85 | goto out; | 106 | goto out; |
86 | } | 107 | } |
108 | if (!our_mnt(path->mnt)) | ||
109 | connected = 0; | ||
87 | 110 | ||
111 | ok: | ||
88 | /* Handle two cases: | 112 | /* Handle two cases: |
89 | * 1. A deleted dentry && profile is not allowing mediation of deleted | 113 | * 1. A deleted dentry && profile is not allowing mediation of deleted |
90 | * 2. On some filesystems, newly allocated dentries appear to the | 114 | * 2. On some filesystems, newly allocated dentries appear to the |
@@ -97,10 +121,7 @@ static int d_namespace_path(struct path *path, char *buf, int buflen, | |||
97 | goto out; | 121 | goto out; |
98 | } | 122 | } |
99 | 123 | ||
100 | /* Determine if the path is connected to the expected root */ | 124 | /* If the path is not connected to the expected root, |
101 | connected = tmp.dentry == root.dentry && tmp.mnt == root.mnt; | ||
102 | |||
103 | /* If the path is not connected, | ||
104 | * check if it is a sysctl and handle specially else remove any | 125 | * check if it is a sysctl and handle specially else remove any |
105 | * leading / that __d_path may have returned. | 126 | * leading / that __d_path may have returned. |
106 | * Unless | 127 | * Unless |
@@ -112,17 +133,9 @@ static int d_namespace_path(struct path *path, char *buf, int buflen, | |||
112 | * namespace root. | 133 | * namespace root. |
113 | */ | 134 | */ |
114 | if (!connected) { | 135 | if (!connected) { |
115 | /* is the disconnect path a sysctl? */ | 136 | if (!(flags & PATH_CONNECT_PATH) && |
116 | if (tmp.dentry->d_sb->s_magic == PROC_SUPER_MAGIC && | ||
117 | strncmp(*name, "/sys/", 5) == 0) { | ||
118 | /* TODO: convert over to using a per namespace | ||
119 | * control instead of hard coded /proc | ||
120 | */ | ||
121 | error = prepend(name, *name - buf, "/proc", 5); | ||
122 | } else if (!(flags & PATH_CONNECT_PATH) && | ||
123 | !(((flags & CHROOT_NSCONNECT) == CHROOT_NSCONNECT) && | 137 | !(((flags & CHROOT_NSCONNECT) == CHROOT_NSCONNECT) && |
124 | (tmp.mnt == current->nsproxy->mnt_ns->root && | 138 | our_mnt(path->mnt))) { |
125 | tmp.dentry == tmp.mnt->mnt_root))) { | ||
126 | /* disconnected path, don't return pathname starting | 139 | /* disconnected path, don't return pathname starting |
127 | * with '/' | 140 | * with '/' |
128 | */ | 141 | */ |
@@ -133,8 +146,6 @@ static int d_namespace_path(struct path *path, char *buf, int buflen, | |||
133 | } | 146 | } |
134 | 147 | ||
135 | out: | 148 | out: |
136 | path_put(&root); | ||
137 | |||
138 | return error; | 149 | return error; |
139 | } | 150 | } |
140 | 151 | ||
diff --git a/security/tomoyo/realpath.c b/security/tomoyo/realpath.c index 738bbdf8d4c7..36fa7c9bedc4 100644 --- a/security/tomoyo/realpath.c +++ b/security/tomoyo/realpath.c | |||
@@ -101,9 +101,8 @@ static char *tomoyo_get_absolute_path(struct path *path, char * const buffer, | |||
101 | { | 101 | { |
102 | char *pos = ERR_PTR(-ENOMEM); | 102 | char *pos = ERR_PTR(-ENOMEM); |
103 | if (buflen >= 256) { | 103 | if (buflen >= 256) { |
104 | struct path ns_root = { }; | ||
105 | /* go to whatever namespace root we are under */ | 104 | /* go to whatever namespace root we are under */ |
106 | pos = __d_path(path, &ns_root, buffer, buflen - 1); | 105 | pos = d_absolute_path(path, buffer, buflen - 1); |
107 | if (!IS_ERR(pos) && *pos == '/' && pos[1]) { | 106 | if (!IS_ERR(pos) && *pos == '/' && pos[1]) { |
108 | struct inode *inode = path->dentry->d_inode; | 107 | struct inode *inode = path->dentry->d_inode; |
109 | if (inode && S_ISDIR(inode->i_mode)) { | 108 | if (inode && S_ISDIR(inode->i_mode)) { |