diff options
| -rw-r--r-- | Documentation/filesystems/proc.txt | 38 | ||||
| -rw-r--r-- | fs/dcache.c | 114 | ||||
| -rw-r--r-- | fs/namespace.c | 265 | ||||
| -rw-r--r-- | fs/pnode.c | 56 | ||||
| -rw-r--r-- | fs/pnode.h | 1 | ||||
| -rw-r--r-- | fs/proc/base.c | 121 | ||||
| -rw-r--r-- | fs/seq_file.c | 95 | ||||
| -rw-r--r-- | fs/xattr.c | 1 | ||||
| -rw-r--r-- | include/linux/dcache.h | 2 | ||||
| -rw-r--r-- | include/linux/mnt_namespace.h | 12 | ||||
| -rw-r--r-- | include/linux/mount.h | 2 | ||||
| -rw-r--r-- | include/linux/seq_file.h | 4 |
12 files changed, 573 insertions, 138 deletions
diff --git a/Documentation/filesystems/proc.txt b/Documentation/filesystems/proc.txt index 518ebe609e2b..2a99116edc47 100644 --- a/Documentation/filesystems/proc.txt +++ b/Documentation/filesystems/proc.txt | |||
| @@ -43,6 +43,7 @@ Table of Contents | |||
| 43 | 2.13 /proc/<pid>/oom_score - Display current oom-killer score | 43 | 2.13 /proc/<pid>/oom_score - Display current oom-killer score |
| 44 | 2.14 /proc/<pid>/io - Display the IO accounting fields | 44 | 2.14 /proc/<pid>/io - Display the IO accounting fields |
| 45 | 2.15 /proc/<pid>/coredump_filter - Core dump filtering settings | 45 | 2.15 /proc/<pid>/coredump_filter - Core dump filtering settings |
| 46 | 2.16 /proc/<pid>/mountinfo - Information about mounts | ||
| 46 | 47 | ||
| 47 | ------------------------------------------------------------------------------ | 48 | ------------------------------------------------------------------------------ |
| 48 | Preface | 49 | Preface |
| @@ -2348,4 +2349,41 @@ For example: | |||
| 2348 | $ echo 0x7 > /proc/self/coredump_filter | 2349 | $ echo 0x7 > /proc/self/coredump_filter |
| 2349 | $ ./some_program | 2350 | $ ./some_program |
| 2350 | 2351 | ||
| 2352 | 2.16 /proc/<pid>/mountinfo - Information about mounts | ||
| 2353 | -------------------------------------------------------- | ||
| 2354 | |||
| 2355 | This file contains lines of the form: | ||
| 2356 | |||
| 2357 | 36 35 98:0 /mnt1 /mnt2 rw,noatime master:1 - ext3 /dev/root rw,errors=continue | ||
| 2358 | (1)(2)(3) (4) (5) (6) (7) (8) (9) (10) (11) | ||
| 2359 | |||
| 2360 | (1) mount ID: unique identifier of the mount (may be reused after umount) | ||
| 2361 | (2) parent ID: ID of parent (or of self for the top of the mount tree) | ||
| 2362 | (3) major:minor: value of st_dev for files on filesystem | ||
| 2363 | (4) root: root of the mount within the filesystem | ||
| 2364 | (5) mount point: mount point relative to the process's root | ||
| 2365 | (6) mount options: per mount options | ||
| 2366 | (7) optional fields: zero or more fields of the form "tag[:value]" | ||
| 2367 | (8) separator: marks the end of the optional fields | ||
| 2368 | (9) filesystem type: name of filesystem of the form "type[.subtype]" | ||
| 2369 | (10) mount source: filesystem specific information or "none" | ||
| 2370 | (11) super options: per super block options | ||
| 2371 | |||
| 2372 | Parsers should ignore all unrecognised optional fields. Currently the | ||
| 2373 | possible optional fields are: | ||
| 2374 | |||
| 2375 | shared:X mount is shared in peer group X | ||
| 2376 | master:X mount is slave to peer group X | ||
| 2377 | propagate_from:X mount is slave and receives propagation from peer group X (*) | ||
| 2378 | unbindable mount is unbindable | ||
| 2379 | |||
| 2380 | (*) X is the closest dominant peer group under the process's root. If | ||
| 2381 | X is the immediate master of the mount, or if there's no dominant peer | ||
| 2382 | group under the same root, then only the "master:X" field is present | ||
| 2383 | and not the "propagate_from:X" field. | ||
| 2384 | |||
| 2385 | For more information on mount propagation see: | ||
| 2386 | |||
| 2387 | Documentation/filesystems/sharedsubtree.txt | ||
| 2388 | |||
| 2351 | ------------------------------------------------------------------------------ | 2389 | ------------------------------------------------------------------------------ |
diff --git a/fs/dcache.c b/fs/dcache.c index 43455776711e..3ee588d5f585 100644 --- a/fs/dcache.c +++ b/fs/dcache.c | |||
| @@ -1746,12 +1746,21 @@ shouldnt_be_hashed: | |||
| 1746 | goto shouldnt_be_hashed; | 1746 | goto shouldnt_be_hashed; |
| 1747 | } | 1747 | } |
| 1748 | 1748 | ||
| 1749 | static int prepend(char **buffer, int *buflen, const char *str, | ||
| 1750 | int namelen) | ||
| 1751 | { | ||
| 1752 | *buflen -= namelen; | ||
| 1753 | if (*buflen < 0) | ||
| 1754 | return -ENAMETOOLONG; | ||
| 1755 | *buffer -= namelen; | ||
| 1756 | memcpy(*buffer, str, namelen); | ||
| 1757 | return 0; | ||
| 1758 | } | ||
| 1759 | |||
| 1749 | /** | 1760 | /** |
| 1750 | * d_path - return the path of a dentry | 1761 | * d_path - return the path of a dentry |
| 1751 | * @dentry: dentry to report | 1762 | * @path: the dentry/vfsmount to report |
| 1752 | * @vfsmnt: vfsmnt to which the dentry belongs | 1763 | * @root: root vfsmnt/dentry (may be modified by this function) |
| 1753 | * @root: root dentry | ||
| 1754 | * @rootmnt: vfsmnt to which the root dentry belongs | ||
| 1755 | * @buffer: buffer to return value in | 1764 | * @buffer: buffer to return value in |
| 1756 | * @buflen: buffer length | 1765 | * @buflen: buffer length |
| 1757 | * | 1766 | * |
| @@ -1761,23 +1770,22 @@ shouldnt_be_hashed: | |||
| 1761 | * Returns the buffer or an error code if the path was too long. | 1770 | * Returns the buffer or an error code if the path was too long. |
| 1762 | * | 1771 | * |
| 1763 | * "buflen" should be positive. Caller holds the dcache_lock. | 1772 | * "buflen" should be positive. Caller holds the dcache_lock. |
| 1773 | * | ||
| 1774 | * If path is not reachable from the supplied root, then the value of | ||
| 1775 | * root is changed (without modifying refcounts). | ||
| 1764 | */ | 1776 | */ |
| 1765 | static char *__d_path(struct dentry *dentry, struct vfsmount *vfsmnt, | 1777 | char *__d_path(const struct path *path, struct path *root, |
| 1766 | struct path *root, char *buffer, int buflen) | 1778 | char *buffer, int buflen) |
| 1767 | { | 1779 | { |
| 1780 | struct dentry *dentry = path->dentry; | ||
| 1781 | struct vfsmount *vfsmnt = path->mnt; | ||
| 1768 | char * end = buffer+buflen; | 1782 | char * end = buffer+buflen; |
| 1769 | char * retval; | 1783 | char * retval; |
| 1770 | int namelen; | 1784 | |
| 1771 | 1785 | prepend(&end, &buflen, "\0", 1); | |
| 1772 | *--end = '\0'; | 1786 | if (!IS_ROOT(dentry) && d_unhashed(dentry) && |
| 1773 | buflen--; | 1787 | (prepend(&end, &buflen, " (deleted)", 10) != 0)) |
| 1774 | if (!IS_ROOT(dentry) && d_unhashed(dentry)) { | ||
| 1775 | buflen -= 10; | ||
| 1776 | end -= 10; | ||
| 1777 | if (buflen < 0) | ||
| 1778 | goto Elong; | 1788 | goto Elong; |
| 1779 | memcpy(end, " (deleted)", 10); | ||
| 1780 | } | ||
| 1781 | 1789 | ||
| 1782 | if (buflen < 1) | 1790 | if (buflen < 1) |
| 1783 | goto Elong; | 1791 | goto Elong; |
| @@ -1804,13 +1812,10 @@ static char *__d_path(struct dentry *dentry, struct vfsmount *vfsmnt, | |||
| 1804 | } | 1812 | } |
| 1805 | parent = dentry->d_parent; | 1813 | parent = dentry->d_parent; |
| 1806 | prefetch(parent); | 1814 | prefetch(parent); |
| 1807 | namelen = dentry->d_name.len; | 1815 | if ((prepend(&end, &buflen, dentry->d_name.name, |
| 1808 | buflen -= namelen + 1; | 1816 | dentry->d_name.len) != 0) || |
| 1809 | if (buflen < 0) | 1817 | (prepend(&end, &buflen, "/", 1) != 0)) |
| 1810 | goto Elong; | 1818 | goto Elong; |
| 1811 | end -= namelen; | ||
| 1812 | memcpy(end, dentry->d_name.name, namelen); | ||
| 1813 | *--end = '/'; | ||
| 1814 | retval = end; | 1819 | retval = end; |
| 1815 | dentry = parent; | 1820 | dentry = parent; |
| 1816 | } | 1821 | } |
| @@ -1818,12 +1823,12 @@ static char *__d_path(struct dentry *dentry, struct vfsmount *vfsmnt, | |||
| 1818 | return retval; | 1823 | return retval; |
| 1819 | 1824 | ||
| 1820 | global_root: | 1825 | global_root: |
| 1821 | namelen = dentry->d_name.len; | 1826 | retval += 1; /* hit the slash */ |
| 1822 | buflen -= namelen; | 1827 | if (prepend(&retval, &buflen, dentry->d_name.name, |
| 1823 | if (buflen < 0) | 1828 | dentry->d_name.len) != 0) |
| 1824 | goto Elong; | 1829 | goto Elong; |
| 1825 | retval -= namelen-1; /* hit the slash */ | 1830 | root->mnt = vfsmnt; |
| 1826 | memcpy(retval, dentry->d_name.name, namelen); | 1831 | root->dentry = dentry; |
| 1827 | return retval; | 1832 | return retval; |
| 1828 | Elong: | 1833 | Elong: |
| 1829 | return ERR_PTR(-ENAMETOOLONG); | 1834 | return ERR_PTR(-ENAMETOOLONG); |
| @@ -1846,6 +1851,7 @@ char *d_path(struct path *path, char *buf, int buflen) | |||
| 1846 | { | 1851 | { |
| 1847 | char *res; | 1852 | char *res; |
| 1848 | struct path root; | 1853 | struct path root; |
| 1854 | struct path tmp; | ||
| 1849 | 1855 | ||
| 1850 | /* | 1856 | /* |
| 1851 | * We have various synthetic filesystems that never get mounted. On | 1857 | * We have various synthetic filesystems that never get mounted. On |
| @@ -1859,10 +1865,11 @@ char *d_path(struct path *path, char *buf, int buflen) | |||
| 1859 | 1865 | ||
| 1860 | read_lock(¤t->fs->lock); | 1866 | read_lock(¤t->fs->lock); |
| 1861 | root = current->fs->root; | 1867 | root = current->fs->root; |
| 1862 | path_get(¤t->fs->root); | 1868 | path_get(&root); |
| 1863 | read_unlock(¤t->fs->lock); | 1869 | read_unlock(¤t->fs->lock); |
| 1864 | spin_lock(&dcache_lock); | 1870 | spin_lock(&dcache_lock); |
| 1865 | res = __d_path(path->dentry, path->mnt, &root, buf, buflen); | 1871 | tmp = root; |
| 1872 | res = __d_path(path, &tmp, buf, buflen); | ||
| 1866 | spin_unlock(&dcache_lock); | 1873 | spin_unlock(&dcache_lock); |
| 1867 | path_put(&root); | 1874 | path_put(&root); |
| 1868 | return res; | 1875 | return res; |
| @@ -1890,6 +1897,48 @@ char *dynamic_dname(struct dentry *dentry, char *buffer, int buflen, | |||
| 1890 | } | 1897 | } |
| 1891 | 1898 | ||
| 1892 | /* | 1899 | /* |
| 1900 | * Write full pathname from the root of the filesystem into the buffer. | ||
| 1901 | */ | ||
| 1902 | char *dentry_path(struct dentry *dentry, char *buf, int buflen) | ||
| 1903 | { | ||
| 1904 | char *end = buf + buflen; | ||
| 1905 | char *retval; | ||
| 1906 | |||
| 1907 | spin_lock(&dcache_lock); | ||
| 1908 | prepend(&end, &buflen, "\0", 1); | ||
| 1909 | if (!IS_ROOT(dentry) && d_unhashed(dentry) && | ||
| 1910 | (prepend(&end, &buflen, "//deleted", 9) != 0)) | ||
| 1911 | goto Elong; | ||
| 1912 | if (buflen < 1) | ||
| 1913 | goto Elong; | ||
| 1914 | /* Get '/' right */ | ||
| 1915 | retval = end-1; | ||
| 1916 | *retval = '/'; | ||
| 1917 | |||
| 1918 | for (;;) { | ||
| 1919 | struct dentry *parent; | ||
| 1920 | if (IS_ROOT(dentry)) | ||
| 1921 | break; | ||
| 1922 | |||
| 1923 | parent = dentry->d_parent; | ||
| 1924 | prefetch(parent); | ||
| 1925 | |||
| 1926 | if ((prepend(&end, &buflen, dentry->d_name.name, | ||
| 1927 | dentry->d_name.len) != 0) || | ||
| 1928 | (prepend(&end, &buflen, "/", 1) != 0)) | ||
| 1929 | goto Elong; | ||
| 1930 | |||
| 1931 | retval = end; | ||
| 1932 | dentry = parent; | ||
| 1933 | } | ||
| 1934 | spin_unlock(&dcache_lock); | ||
| 1935 | return retval; | ||
| 1936 | Elong: | ||
| 1937 | spin_unlock(&dcache_lock); | ||
| 1938 | return ERR_PTR(-ENAMETOOLONG); | ||
| 1939 | } | ||
| 1940 | |||
| 1941 | /* | ||
| 1893 | * NOTE! The user-level library version returns a | 1942 | * NOTE! The user-level library version returns a |
| 1894 | * character pointer. The kernel system call just | 1943 | * character pointer. The kernel system call just |
| 1895 | * returns the length of the buffer filled (which | 1944 | * returns the length of the buffer filled (which |
| @@ -1918,9 +1967,9 @@ asmlinkage long sys_getcwd(char __user *buf, unsigned long size) | |||
| 1918 | 1967 | ||
| 1919 | read_lock(¤t->fs->lock); | 1968 | read_lock(¤t->fs->lock); |
| 1920 | pwd = current->fs->pwd; | 1969 | pwd = current->fs->pwd; |
| 1921 | path_get(¤t->fs->pwd); | 1970 | path_get(&pwd); |
| 1922 | root = current->fs->root; | 1971 | root = current->fs->root; |
| 1923 | path_get(¤t->fs->root); | 1972 | path_get(&root); |
| 1924 | read_unlock(¤t->fs->lock); | 1973 | read_unlock(¤t->fs->lock); |
| 1925 | 1974 | ||
| 1926 | error = -ENOENT; | 1975 | error = -ENOENT; |
| @@ -1928,9 +1977,10 @@ asmlinkage long sys_getcwd(char __user *buf, unsigned long size) | |||
| 1928 | spin_lock(&dcache_lock); | 1977 | spin_lock(&dcache_lock); |
| 1929 | if (pwd.dentry->d_parent == pwd.dentry || !d_unhashed(pwd.dentry)) { | 1978 | if (pwd.dentry->d_parent == pwd.dentry || !d_unhashed(pwd.dentry)) { |
| 1930 | unsigned long len; | 1979 | unsigned long len; |
| 1980 | struct path tmp = root; | ||
| 1931 | char * cwd; | 1981 | char * cwd; |
| 1932 | 1982 | ||
| 1933 | cwd = __d_path(pwd.dentry, pwd.mnt, &root, page, PAGE_SIZE); | 1983 | cwd = __d_path(&pwd, &tmp, page, PAGE_SIZE); |
| 1934 | spin_unlock(&dcache_lock); | 1984 | spin_unlock(&dcache_lock); |
| 1935 | 1985 | ||
| 1936 | error = PTR_ERR(cwd); | 1986 | error = PTR_ERR(cwd); |
diff --git a/fs/namespace.c b/fs/namespace.c index 1bf302d0478b..0505fb61aa74 100644 --- a/fs/namespace.c +++ b/fs/namespace.c | |||
| @@ -27,6 +27,7 @@ | |||
| 27 | #include <linux/mount.h> | 27 | #include <linux/mount.h> |
| 28 | #include <linux/ramfs.h> | 28 | #include <linux/ramfs.h> |
| 29 | #include <linux/log2.h> | 29 | #include <linux/log2.h> |
| 30 | #include <linux/idr.h> | ||
| 30 | #include <asm/uaccess.h> | 31 | #include <asm/uaccess.h> |
| 31 | #include <asm/unistd.h> | 32 | #include <asm/unistd.h> |
| 32 | #include "pnode.h" | 33 | #include "pnode.h" |
| @@ -39,6 +40,8 @@ | |||
| 39 | __cacheline_aligned_in_smp DEFINE_SPINLOCK(vfsmount_lock); | 40 | __cacheline_aligned_in_smp DEFINE_SPINLOCK(vfsmount_lock); |
| 40 | 41 | ||
| 41 | static int event; | 42 | static int event; |
| 43 | static DEFINE_IDA(mnt_id_ida); | ||
| 44 | static DEFINE_IDA(mnt_group_ida); | ||
| 42 | 45 | ||
| 43 | static struct list_head *mount_hashtable __read_mostly; | 46 | static struct list_head *mount_hashtable __read_mostly; |
| 44 | static struct kmem_cache *mnt_cache __read_mostly; | 47 | static struct kmem_cache *mnt_cache __read_mostly; |
| @@ -58,10 +61,63 @@ static inline unsigned long hash(struct vfsmount *mnt, struct dentry *dentry) | |||
| 58 | 61 | ||
| 59 | #define MNT_WRITER_UNDERFLOW_LIMIT -(1<<16) | 62 | #define MNT_WRITER_UNDERFLOW_LIMIT -(1<<16) |
| 60 | 63 | ||
| 64 | /* allocation is serialized by namespace_sem */ | ||
| 65 | static int mnt_alloc_id(struct vfsmount *mnt) | ||
| 66 | { | ||
| 67 | int res; | ||
| 68 | |||
| 69 | retry: | ||
| 70 | ida_pre_get(&mnt_id_ida, GFP_KERNEL); | ||
| 71 | spin_lock(&vfsmount_lock); | ||
| 72 | res = ida_get_new(&mnt_id_ida, &mnt->mnt_id); | ||
| 73 | spin_unlock(&vfsmount_lock); | ||
| 74 | if (res == -EAGAIN) | ||
| 75 | goto retry; | ||
| 76 | |||
| 77 | return res; | ||
| 78 | } | ||
| 79 | |||
| 80 | static void mnt_free_id(struct vfsmount *mnt) | ||
| 81 | { | ||
| 82 | spin_lock(&vfsmount_lock); | ||
| 83 | ida_remove(&mnt_id_ida, mnt->mnt_id); | ||
| 84 | spin_unlock(&vfsmount_lock); | ||
| 85 | } | ||
| 86 | |||
| 87 | /* | ||
| 88 | * Allocate a new peer group ID | ||
| 89 | * | ||
| 90 | * mnt_group_ida is protected by namespace_sem | ||
| 91 | */ | ||
| 92 | static int mnt_alloc_group_id(struct vfsmount *mnt) | ||
| 93 | { | ||
| 94 | if (!ida_pre_get(&mnt_group_ida, GFP_KERNEL)) | ||
| 95 | return -ENOMEM; | ||
| 96 | |||
| 97 | return ida_get_new_above(&mnt_group_ida, 1, &mnt->mnt_group_id); | ||
| 98 | } | ||
| 99 | |||
| 100 | /* | ||
| 101 | * Release a peer group ID | ||
| 102 | */ | ||
| 103 | void mnt_release_group_id(struct vfsmount *mnt) | ||
| 104 | { | ||
| 105 | ida_remove(&mnt_group_ida, mnt->mnt_group_id); | ||
| 106 | mnt->mnt_group_id = 0; | ||
| 107 | } | ||
| 108 | |||
| 61 | struct vfsmount *alloc_vfsmnt(const char *name) | 109 | struct vfsmount *alloc_vfsmnt(const char *name) |
| 62 | { | 110 | { |
| 63 | struct vfsmount *mnt = kmem_cache_zalloc(mnt_cache, GFP_KERNEL); | 111 | struct vfsmount *mnt = kmem_cache_zalloc(mnt_cache, GFP_KERNEL); |
| 64 | if (mnt) { | 112 | if (mnt) { |
| 113 | int err; | ||
| 114 | |||
| 115 | err = mnt_alloc_id(mnt); | ||
| 116 | if (err) { | ||
| 117 | kmem_cache_free(mnt_cache, mnt); | ||
| 118 | return NULL; | ||
| 119 | } | ||
| 120 | |||
| 65 | atomic_set(&mnt->mnt_count, 1); | 121 | atomic_set(&mnt->mnt_count, 1); |
| 66 | INIT_LIST_HEAD(&mnt->mnt_hash); | 122 | INIT_LIST_HEAD(&mnt->mnt_hash); |
| 67 | INIT_LIST_HEAD(&mnt->mnt_child); | 123 | INIT_LIST_HEAD(&mnt->mnt_child); |
| @@ -353,6 +409,7 @@ EXPORT_SYMBOL(simple_set_mnt); | |||
| 353 | void free_vfsmnt(struct vfsmount *mnt) | 409 | void free_vfsmnt(struct vfsmount *mnt) |
| 354 | { | 410 | { |
| 355 | kfree(mnt->mnt_devname); | 411 | kfree(mnt->mnt_devname); |
| 412 | mnt_free_id(mnt); | ||
| 356 | kmem_cache_free(mnt_cache, mnt); | 413 | kmem_cache_free(mnt_cache, mnt); |
| 357 | } | 414 | } |
| 358 | 415 | ||
| @@ -499,6 +556,17 @@ static struct vfsmount *clone_mnt(struct vfsmount *old, struct dentry *root, | |||
| 499 | struct vfsmount *mnt = alloc_vfsmnt(old->mnt_devname); | 556 | struct vfsmount *mnt = alloc_vfsmnt(old->mnt_devname); |
| 500 | 557 | ||
| 501 | if (mnt) { | 558 | if (mnt) { |
| 559 | if (flag & (CL_SLAVE | CL_PRIVATE)) | ||
| 560 | mnt->mnt_group_id = 0; /* not a peer of original */ | ||
| 561 | else | ||
| 562 | mnt->mnt_group_id = old->mnt_group_id; | ||
| 563 | |||
| 564 | if ((flag & CL_MAKE_SHARED) && !mnt->mnt_group_id) { | ||
| 565 | int err = mnt_alloc_group_id(mnt); | ||
| 566 | if (err) | ||
| 567 | goto out_free; | ||
| 568 | } | ||
| 569 | |||
| 502 | mnt->mnt_flags = old->mnt_flags; | 570 | mnt->mnt_flags = old->mnt_flags; |
| 503 | atomic_inc(&sb->s_active); | 571 | atomic_inc(&sb->s_active); |
| 504 | mnt->mnt_sb = sb; | 572 | mnt->mnt_sb = sb; |
| @@ -528,6 +596,10 @@ static struct vfsmount *clone_mnt(struct vfsmount *old, struct dentry *root, | |||
| 528 | } | 596 | } |
| 529 | } | 597 | } |
| 530 | return mnt; | 598 | return mnt; |
| 599 | |||
| 600 | out_free: | ||
| 601 | free_vfsmnt(mnt); | ||
| 602 | return NULL; | ||
| 531 | } | 603 | } |
| 532 | 604 | ||
| 533 | static inline void __mntput(struct vfsmount *mnt) | 605 | static inline void __mntput(struct vfsmount *mnt) |
| @@ -652,20 +724,21 @@ void save_mount_options(struct super_block *sb, char *options) | |||
| 652 | } | 724 | } |
| 653 | EXPORT_SYMBOL(save_mount_options); | 725 | EXPORT_SYMBOL(save_mount_options); |
| 654 | 726 | ||
| 727 | #ifdef CONFIG_PROC_FS | ||
| 655 | /* iterator */ | 728 | /* iterator */ |
| 656 | static void *m_start(struct seq_file *m, loff_t *pos) | 729 | static void *m_start(struct seq_file *m, loff_t *pos) |
| 657 | { | 730 | { |
| 658 | struct mnt_namespace *n = m->private; | 731 | struct proc_mounts *p = m->private; |
| 659 | 732 | ||
| 660 | down_read(&namespace_sem); | 733 | down_read(&namespace_sem); |
| 661 | return seq_list_start(&n->list, *pos); | 734 | return seq_list_start(&p->ns->list, *pos); |
| 662 | } | 735 | } |
| 663 | 736 | ||
| 664 | static void *m_next(struct seq_file *m, void *v, loff_t *pos) | 737 | static void *m_next(struct seq_file *m, void *v, loff_t *pos) |
| 665 | { | 738 | { |
| 666 | struct mnt_namespace *n = m->private; | 739 | struct proc_mounts *p = m->private; |
| 667 | 740 | ||
| 668 | return seq_list_next(v, &n->list, pos); | 741 | return seq_list_next(v, &p->ns->list, pos); |
| 669 | } | 742 | } |
| 670 | 743 | ||
| 671 | static void m_stop(struct seq_file *m, void *v) | 744 | static void m_stop(struct seq_file *m, void *v) |
| @@ -673,20 +746,30 @@ static void m_stop(struct seq_file *m, void *v) | |||
| 673 | up_read(&namespace_sem); | 746 | up_read(&namespace_sem); |
| 674 | } | 747 | } |
| 675 | 748 | ||
| 676 | static int show_vfsmnt(struct seq_file *m, void *v) | 749 | struct proc_fs_info { |
| 750 | int flag; | ||
| 751 | const char *str; | ||
| 752 | }; | ||
| 753 | |||
| 754 | static void show_sb_opts(struct seq_file *m, struct super_block *sb) | ||
| 677 | { | 755 | { |
| 678 | struct vfsmount *mnt = list_entry(v, struct vfsmount, mnt_list); | 756 | static const struct proc_fs_info fs_info[] = { |
| 679 | int err = 0; | ||
| 680 | static struct proc_fs_info { | ||
| 681 | int flag; | ||
| 682 | char *str; | ||
| 683 | } fs_info[] = { | ||
| 684 | { MS_SYNCHRONOUS, ",sync" }, | 757 | { MS_SYNCHRONOUS, ",sync" }, |
| 685 | { MS_DIRSYNC, ",dirsync" }, | 758 | { MS_DIRSYNC, ",dirsync" }, |
| 686 | { MS_MANDLOCK, ",mand" }, | 759 | { MS_MANDLOCK, ",mand" }, |
| 687 | { 0, NULL } | 760 | { 0, NULL } |
| 688 | }; | 761 | }; |
| 689 | static struct proc_fs_info mnt_info[] = { | 762 | const struct proc_fs_info *fs_infop; |
| 763 | |||
| 764 | for (fs_infop = fs_info; fs_infop->flag; fs_infop++) { | ||
| 765 | if (sb->s_flags & fs_infop->flag) | ||
| 766 | seq_puts(m, fs_infop->str); | ||
| 767 | } | ||
| 768 | } | ||
| 769 | |||
| 770 | static void show_mnt_opts(struct seq_file *m, struct vfsmount *mnt) | ||
| 771 | { | ||
| 772 | static const struct proc_fs_info mnt_info[] = { | ||
| 690 | { MNT_NOSUID, ",nosuid" }, | 773 | { MNT_NOSUID, ",nosuid" }, |
| 691 | { MNT_NODEV, ",nodev" }, | 774 | { MNT_NODEV, ",nodev" }, |
| 692 | { MNT_NOEXEC, ",noexec" }, | 775 | { MNT_NOEXEC, ",noexec" }, |
| @@ -695,40 +778,108 @@ static int show_vfsmnt(struct seq_file *m, void *v) | |||
| 695 | { MNT_RELATIME, ",relatime" }, | 778 | { MNT_RELATIME, ",relatime" }, |
| 696 | { 0, NULL } | 779 | { 0, NULL } |
| 697 | }; | 780 | }; |
| 698 | struct proc_fs_info *fs_infop; | 781 | const struct proc_fs_info *fs_infop; |
| 782 | |||
| 783 | for (fs_infop = mnt_info; fs_infop->flag; fs_infop++) { | ||
| 784 | if (mnt->mnt_flags & fs_infop->flag) | ||
| 785 | seq_puts(m, fs_infop->str); | ||
| 786 | } | ||
| 787 | } | ||
| 788 | |||
| 789 | static void show_type(struct seq_file *m, struct super_block *sb) | ||
| 790 | { | ||
| 791 | mangle(m, sb->s_type->name); | ||
| 792 | if (sb->s_subtype && sb->s_subtype[0]) { | ||
| 793 | seq_putc(m, '.'); | ||
| 794 | mangle(m, sb->s_subtype); | ||
| 795 | } | ||
| 796 | } | ||
| 797 | |||
| 798 | static int show_vfsmnt(struct seq_file *m, void *v) | ||
| 799 | { | ||
| 800 | struct vfsmount *mnt = list_entry(v, struct vfsmount, mnt_list); | ||
| 801 | int err = 0; | ||
| 699 | struct path mnt_path = { .dentry = mnt->mnt_root, .mnt = mnt }; | 802 | struct path mnt_path = { .dentry = mnt->mnt_root, .mnt = mnt }; |
| 700 | 803 | ||
| 701 | mangle(m, mnt->mnt_devname ? mnt->mnt_devname : "none"); | 804 | mangle(m, mnt->mnt_devname ? mnt->mnt_devname : "none"); |
| 702 | seq_putc(m, ' '); | 805 | seq_putc(m, ' '); |
| 703 | seq_path(m, &mnt_path, " \t\n\\"); | 806 | seq_path(m, &mnt_path, " \t\n\\"); |
| 704 | seq_putc(m, ' '); | 807 | seq_putc(m, ' '); |
| 705 | mangle(m, mnt->mnt_sb->s_type->name); | 808 | show_type(m, mnt->mnt_sb); |
| 706 | if (mnt->mnt_sb->s_subtype && mnt->mnt_sb->s_subtype[0]) { | ||
| 707 | seq_putc(m, '.'); | ||
| 708 | mangle(m, mnt->mnt_sb->s_subtype); | ||
| 709 | } | ||
| 710 | seq_puts(m, __mnt_is_readonly(mnt) ? " ro" : " rw"); | 809 | seq_puts(m, __mnt_is_readonly(mnt) ? " ro" : " rw"); |
| 711 | for (fs_infop = fs_info; fs_infop->flag; fs_infop++) { | 810 | show_sb_opts(m, mnt->mnt_sb); |
| 712 | if (mnt->mnt_sb->s_flags & fs_infop->flag) | 811 | show_mnt_opts(m, mnt); |
| 713 | seq_puts(m, fs_infop->str); | ||
| 714 | } | ||
| 715 | for (fs_infop = mnt_info; fs_infop->flag; fs_infop++) { | ||
| 716 | if (mnt->mnt_flags & fs_infop->flag) | ||
| 717 | seq_puts(m, fs_infop->str); | ||
| 718 | } | ||
| 719 | if (mnt->mnt_sb->s_op->show_options) | 812 | if (mnt->mnt_sb->s_op->show_options) |
| 720 | err = mnt->mnt_sb->s_op->show_options(m, mnt); | 813 | err = mnt->mnt_sb->s_op->show_options(m, mnt); |
| 721 | seq_puts(m, " 0 0\n"); | 814 | seq_puts(m, " 0 0\n"); |
| 722 | return err; | 815 | return err; |
| 723 | } | 816 | } |
| 724 | 817 | ||
| 725 | struct seq_operations mounts_op = { | 818 | const struct seq_operations mounts_op = { |
| 726 | .start = m_start, | 819 | .start = m_start, |
| 727 | .next = m_next, | 820 | .next = m_next, |
| 728 | .stop = m_stop, | 821 | .stop = m_stop, |
| 729 | .show = show_vfsmnt | 822 | .show = show_vfsmnt |
| 730 | }; | 823 | }; |
| 731 | 824 | ||
| 825 | static int show_mountinfo(struct seq_file *m, void *v) | ||
| 826 | { | ||
| 827 | struct proc_mounts *p = m->private; | ||
| 828 | struct vfsmount *mnt = list_entry(v, struct vfsmount, mnt_list); | ||
| 829 | struct super_block *sb = mnt->mnt_sb; | ||
| 830 | struct path mnt_path = { .dentry = mnt->mnt_root, .mnt = mnt }; | ||
| 831 | struct path root = p->root; | ||
| 832 | int err = 0; | ||
| 833 | |||
| 834 | seq_printf(m, "%i %i %u:%u ", mnt->mnt_id, mnt->mnt_parent->mnt_id, | ||
| 835 | MAJOR(sb->s_dev), MINOR(sb->s_dev)); | ||
| 836 | seq_dentry(m, mnt->mnt_root, " \t\n\\"); | ||
| 837 | seq_putc(m, ' '); | ||
| 838 | seq_path_root(m, &mnt_path, &root, " \t\n\\"); | ||
| 839 | if (root.mnt != p->root.mnt || root.dentry != p->root.dentry) { | ||
| 840 | /* | ||
| 841 | * Mountpoint is outside root, discard that one. Ugly, | ||
| 842 | * but less so than trying to do that in iterator in a | ||
| 843 | * race-free way (due to renames). | ||
| 844 | */ | ||
| 845 | return SEQ_SKIP; | ||
| 846 | } | ||
| 847 | seq_puts(m, mnt->mnt_flags & MNT_READONLY ? " ro" : " rw"); | ||
| 848 | show_mnt_opts(m, mnt); | ||
| 849 | |||
| 850 | /* Tagged fields ("foo:X" or "bar") */ | ||
| 851 | if (IS_MNT_SHARED(mnt)) | ||
| 852 | seq_printf(m, " shared:%i", mnt->mnt_group_id); | ||
| 853 | if (IS_MNT_SLAVE(mnt)) { | ||
| 854 | int master = mnt->mnt_master->mnt_group_id; | ||
| 855 | int dom = get_dominating_id(mnt, &p->root); | ||
| 856 | seq_printf(m, " master:%i", master); | ||
| 857 | if (dom && dom != master) | ||
| 858 | seq_printf(m, " propagate_from:%i", dom); | ||
| 859 | } | ||
| 860 | if (IS_MNT_UNBINDABLE(mnt)) | ||
| 861 | seq_puts(m, " unbindable"); | ||
| 862 | |||
| 863 | /* Filesystem specific data */ | ||
| 864 | seq_puts(m, " - "); | ||
| 865 | show_type(m, sb); | ||
| 866 | seq_putc(m, ' '); | ||
| 867 | mangle(m, mnt->mnt_devname ? mnt->mnt_devname : "none"); | ||
| 868 | seq_puts(m, sb->s_flags & MS_RDONLY ? " ro" : " rw"); | ||
| 869 | show_sb_opts(m, sb); | ||
| 870 | if (sb->s_op->show_options) | ||
| 871 | err = sb->s_op->show_options(m, mnt); | ||
| 872 | seq_putc(m, '\n'); | ||
| 873 | return err; | ||
| 874 | } | ||
| 875 | |||
| 876 | const struct seq_operations mountinfo_op = { | ||
| 877 | .start = m_start, | ||
| 878 | .next = m_next, | ||
| 879 | .stop = m_stop, | ||
| 880 | .show = show_mountinfo, | ||
| 881 | }; | ||
| 882 | |||
| 732 | static int show_vfsstat(struct seq_file *m, void *v) | 883 | static int show_vfsstat(struct seq_file *m, void *v) |
| 733 | { | 884 | { |
| 734 | struct vfsmount *mnt = list_entry(v, struct vfsmount, mnt_list); | 885 | struct vfsmount *mnt = list_entry(v, struct vfsmount, mnt_list); |
| @@ -749,7 +900,7 @@ static int show_vfsstat(struct seq_file *m, void *v) | |||
| 749 | 900 | ||
| 750 | /* file system type */ | 901 | /* file system type */ |
| 751 | seq_puts(m, "with fstype "); | 902 | seq_puts(m, "with fstype "); |
| 752 | mangle(m, mnt->mnt_sb->s_type->name); | 903 | show_type(m, mnt->mnt_sb); |
| 753 | 904 | ||
| 754 | /* optional statistics */ | 905 | /* optional statistics */ |
| 755 | if (mnt->mnt_sb->s_op->show_stats) { | 906 | if (mnt->mnt_sb->s_op->show_stats) { |
| @@ -761,12 +912,13 @@ static int show_vfsstat(struct seq_file *m, void *v) | |||
| 761 | return err; | 912 | return err; |
| 762 | } | 913 | } |
| 763 | 914 | ||
| 764 | struct seq_operations mountstats_op = { | 915 | const struct seq_operations mountstats_op = { |
| 765 | .start = m_start, | 916 | .start = m_start, |
| 766 | .next = m_next, | 917 | .next = m_next, |
| 767 | .stop = m_stop, | 918 | .stop = m_stop, |
| 768 | .show = show_vfsstat, | 919 | .show = show_vfsstat, |
| 769 | }; | 920 | }; |
| 921 | #endif /* CONFIG_PROC_FS */ | ||
| 770 | 922 | ||
| 771 | /** | 923 | /** |
| 772 | * may_umount_tree - check if a mount tree is busy | 924 | * may_umount_tree - check if a mount tree is busy |
| @@ -1108,6 +1260,33 @@ void drop_collected_mounts(struct vfsmount *mnt) | |||
| 1108 | release_mounts(&umount_list); | 1260 | release_mounts(&umount_list); |
| 1109 | } | 1261 | } |
| 1110 | 1262 | ||
| 1263 | static void cleanup_group_ids(struct vfsmount *mnt, struct vfsmount *end) | ||
| 1264 | { | ||
| 1265 | struct vfsmount *p; | ||
| 1266 | |||
| 1267 | for (p = mnt; p != end; p = next_mnt(p, mnt)) { | ||
| 1268 | if (p->mnt_group_id && !IS_MNT_SHARED(p)) | ||
| 1269 | mnt_release_group_id(p); | ||
| 1270 | } | ||
| 1271 | } | ||
| 1272 | |||
| 1273 | static int invent_group_ids(struct vfsmount *mnt, bool recurse) | ||
| 1274 | { | ||
| 1275 | struct vfsmount *p; | ||
| 1276 | |||
| 1277 | for (p = mnt; p; p = recurse ? next_mnt(p, mnt) : NULL) { | ||
| 1278 | if (!p->mnt_group_id && !IS_MNT_SHARED(p)) { | ||
| 1279 | int err = mnt_alloc_group_id(p); | ||
| 1280 | if (err) { | ||
| 1281 | cleanup_group_ids(mnt, p); | ||
| 1282 | return err; | ||
| 1283 | } | ||
| 1284 | } | ||
| 1285 | } | ||
| 1286 | |||
| 1287 | return 0; | ||
| 1288 | } | ||
| 1289 | |||
| 1111 | /* | 1290 | /* |
| 1112 | * @source_mnt : mount tree to be attached | 1291 | * @source_mnt : mount tree to be attached |
| 1113 | * @nd : place the mount tree @source_mnt is attached | 1292 | * @nd : place the mount tree @source_mnt is attached |
| @@ -1178,9 +1357,16 @@ static int attach_recursive_mnt(struct vfsmount *source_mnt, | |||
| 1178 | struct vfsmount *dest_mnt = path->mnt; | 1357 | struct vfsmount *dest_mnt = path->mnt; |
| 1179 | struct dentry *dest_dentry = path->dentry; | 1358 | struct dentry *dest_dentry = path->dentry; |
| 1180 | struct vfsmount *child, *p; | 1359 | struct vfsmount *child, *p; |
| 1360 | int err; | ||
| 1181 | 1361 | ||
| 1182 | if (propagate_mnt(dest_mnt, dest_dentry, source_mnt, &tree_list)) | 1362 | if (IS_MNT_SHARED(dest_mnt)) { |
| 1183 | return -EINVAL; | 1363 | err = invent_group_ids(source_mnt, true); |
| 1364 | if (err) | ||
| 1365 | goto out; | ||
| 1366 | } | ||
| 1367 | err = propagate_mnt(dest_mnt, dest_dentry, source_mnt, &tree_list); | ||
| 1368 | if (err) | ||
| 1369 | goto out_cleanup_ids; | ||
| 1184 | 1370 | ||
| 1185 | if (IS_MNT_SHARED(dest_mnt)) { | 1371 | if (IS_MNT_SHARED(dest_mnt)) { |
| 1186 | for (p = source_mnt; p; p = next_mnt(p, source_mnt)) | 1372 | for (p = source_mnt; p; p = next_mnt(p, source_mnt)) |
| @@ -1203,6 +1389,12 @@ static int attach_recursive_mnt(struct vfsmount *source_mnt, | |||
| 1203 | } | 1389 | } |
| 1204 | spin_unlock(&vfsmount_lock); | 1390 | spin_unlock(&vfsmount_lock); |
| 1205 | return 0; | 1391 | return 0; |
| 1392 | |||
| 1393 | out_cleanup_ids: | ||
| 1394 | if (IS_MNT_SHARED(dest_mnt)) | ||
| 1395 | cleanup_group_ids(source_mnt, NULL); | ||
| 1396 | out: | ||
| 1397 | return err; | ||
| 1206 | } | 1398 | } |
| 1207 | 1399 | ||
| 1208 | static int graft_tree(struct vfsmount *mnt, struct path *path) | 1400 | static int graft_tree(struct vfsmount *mnt, struct path *path) |
| @@ -1243,6 +1435,7 @@ static noinline int do_change_type(struct nameidata *nd, int flag) | |||
| 1243 | struct vfsmount *m, *mnt = nd->path.mnt; | 1435 | struct vfsmount *m, *mnt = nd->path.mnt; |
| 1244 | int recurse = flag & MS_REC; | 1436 | int recurse = flag & MS_REC; |
| 1245 | int type = flag & ~MS_REC; | 1437 | int type = flag & ~MS_REC; |
| 1438 | int err = 0; | ||
| 1246 | 1439 | ||
| 1247 | if (!capable(CAP_SYS_ADMIN)) | 1440 | if (!capable(CAP_SYS_ADMIN)) |
| 1248 | return -EPERM; | 1441 | return -EPERM; |
| @@ -1251,12 +1444,20 @@ static noinline int do_change_type(struct nameidata *nd, int flag) | |||
| 1251 | return -EINVAL; | 1444 | return -EINVAL; |
| 1252 | 1445 | ||
| 1253 | down_write(&namespace_sem); | 1446 | down_write(&namespace_sem); |
| 1447 | if (type == MS_SHARED) { | ||
| 1448 | err = invent_group_ids(mnt, recurse); | ||
| 1449 | if (err) | ||
| 1450 | goto out_unlock; | ||
| 1451 | } | ||
| 1452 | |||
| 1254 | spin_lock(&vfsmount_lock); | 1453 | spin_lock(&vfsmount_lock); |
| 1255 | for (m = mnt; m; m = (recurse ? next_mnt(m, mnt) : NULL)) | 1454 | for (m = mnt; m; m = (recurse ? next_mnt(m, mnt) : NULL)) |
| 1256 | change_mnt_propagation(m, type); | 1455 | change_mnt_propagation(m, type); |
| 1257 | spin_unlock(&vfsmount_lock); | 1456 | spin_unlock(&vfsmount_lock); |
| 1457 | |||
| 1458 | out_unlock: | ||
| 1258 | up_write(&namespace_sem); | 1459 | up_write(&namespace_sem); |
| 1259 | return 0; | 1460 | return err; |
| 1260 | } | 1461 | } |
| 1261 | 1462 | ||
| 1262 | /* | 1463 | /* |
diff --git a/fs/pnode.c b/fs/pnode.c index f968e35d9785..8d5f392ec3d3 100644 --- a/fs/pnode.c +++ b/fs/pnode.c | |||
| @@ -28,6 +28,57 @@ static inline struct vfsmount *next_slave(struct vfsmount *p) | |||
| 28 | return list_entry(p->mnt_slave.next, struct vfsmount, mnt_slave); | 28 | return list_entry(p->mnt_slave.next, struct vfsmount, mnt_slave); |
| 29 | } | 29 | } |
| 30 | 30 | ||
| 31 | /* | ||
| 32 | * Return true if path is reachable from root | ||
| 33 | * | ||
| 34 | * namespace_sem is held, and mnt is attached | ||
| 35 | */ | ||
| 36 | static bool is_path_reachable(struct vfsmount *mnt, struct dentry *dentry, | ||
| 37 | const struct path *root) | ||
| 38 | { | ||
| 39 | while (mnt != root->mnt && mnt->mnt_parent != mnt) { | ||
| 40 | dentry = mnt->mnt_mountpoint; | ||
| 41 | mnt = mnt->mnt_parent; | ||
| 42 | } | ||
| 43 | return mnt == root->mnt && is_subdir(dentry, root->dentry); | ||
| 44 | } | ||
| 45 | |||
| 46 | static struct vfsmount *get_peer_under_root(struct vfsmount *mnt, | ||
| 47 | struct mnt_namespace *ns, | ||
| 48 | const struct path *root) | ||
| 49 | { | ||
| 50 | struct vfsmount *m = mnt; | ||
| 51 | |||
| 52 | do { | ||
| 53 | /* Check the namespace first for optimization */ | ||
| 54 | if (m->mnt_ns == ns && is_path_reachable(m, m->mnt_root, root)) | ||
| 55 | return m; | ||
| 56 | |||
| 57 | m = next_peer(m); | ||
| 58 | } while (m != mnt); | ||
| 59 | |||
| 60 | return NULL; | ||
| 61 | } | ||
| 62 | |||
| 63 | /* | ||
| 64 | * Get ID of closest dominating peer group having a representative | ||
| 65 | * under the given root. | ||
| 66 | * | ||
| 67 | * Caller must hold namespace_sem | ||
| 68 | */ | ||
| 69 | int get_dominating_id(struct vfsmount *mnt, const struct path *root) | ||
| 70 | { | ||
| 71 | struct vfsmount *m; | ||
| 72 | |||
| 73 | for (m = mnt->mnt_master; m != NULL; m = m->mnt_master) { | ||
| 74 | struct vfsmount *d = get_peer_under_root(m, mnt->mnt_ns, root); | ||
| 75 | if (d) | ||
| 76 | return d->mnt_group_id; | ||
| 77 | } | ||
| 78 | |||
| 79 | return 0; | ||
| 80 | } | ||
| 81 | |||
| 31 | static int do_make_slave(struct vfsmount *mnt) | 82 | static int do_make_slave(struct vfsmount *mnt) |
| 32 | { | 83 | { |
| 33 | struct vfsmount *peer_mnt = mnt, *master = mnt->mnt_master; | 84 | struct vfsmount *peer_mnt = mnt, *master = mnt->mnt_master; |
| @@ -46,7 +97,11 @@ static int do_make_slave(struct vfsmount *mnt) | |||
| 46 | if (peer_mnt == mnt) | 97 | if (peer_mnt == mnt) |
| 47 | peer_mnt = NULL; | 98 | peer_mnt = NULL; |
| 48 | } | 99 | } |
| 100 | if (IS_MNT_SHARED(mnt) && list_empty(&mnt->mnt_share)) | ||
| 101 | mnt_release_group_id(mnt); | ||
| 102 | |||
| 49 | list_del_init(&mnt->mnt_share); | 103 | list_del_init(&mnt->mnt_share); |
| 104 | mnt->mnt_group_id = 0; | ||
| 50 | 105 | ||
| 51 | if (peer_mnt) | 106 | if (peer_mnt) |
| 52 | master = peer_mnt; | 107 | master = peer_mnt; |
| @@ -68,7 +123,6 @@ static int do_make_slave(struct vfsmount *mnt) | |||
| 68 | } | 123 | } |
| 69 | mnt->mnt_master = master; | 124 | mnt->mnt_master = master; |
| 70 | CLEAR_MNT_SHARED(mnt); | 125 | CLEAR_MNT_SHARED(mnt); |
| 71 | INIT_LIST_HEAD(&mnt->mnt_slave_list); | ||
| 72 | return 0; | 126 | return 0; |
| 73 | } | 127 | } |
| 74 | 128 | ||
diff --git a/fs/pnode.h b/fs/pnode.h index 973c3f825e7d..958665d662af 100644 --- a/fs/pnode.h +++ b/fs/pnode.h | |||
| @@ -36,4 +36,5 @@ int propagate_mnt(struct vfsmount *, struct dentry *, struct vfsmount *, | |||
| 36 | int propagate_umount(struct list_head *); | 36 | int propagate_umount(struct list_head *); |
| 37 | int propagate_mount_busy(struct vfsmount *, int); | 37 | int propagate_mount_busy(struct vfsmount *, int); |
| 38 | void mnt_release_group_id(struct vfsmount *); | 38 | void mnt_release_group_id(struct vfsmount *); |
| 39 | int get_dominating_id(struct vfsmount *mnt, const struct path *root); | ||
| 39 | #endif /* _LINUX_PNODE_H */ | 40 | #endif /* _LINUX_PNODE_H */ |
diff --git a/fs/proc/base.c b/fs/proc/base.c index 7313c62e3e9d..c5e412a00b17 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c | |||
| @@ -502,17 +502,14 @@ static const struct inode_operations proc_def_inode_operations = { | |||
| 502 | .setattr = proc_setattr, | 502 | .setattr = proc_setattr, |
| 503 | }; | 503 | }; |
| 504 | 504 | ||
| 505 | extern const struct seq_operations mounts_op; | 505 | static int mounts_open_common(struct inode *inode, struct file *file, |
| 506 | struct proc_mounts { | 506 | const struct seq_operations *op) |
| 507 | struct seq_file m; | ||
| 508 | int event; | ||
| 509 | }; | ||
| 510 | |||
| 511 | static int mounts_open(struct inode *inode, struct file *file) | ||
| 512 | { | 507 | { |
| 513 | struct task_struct *task = get_proc_task(inode); | 508 | struct task_struct *task = get_proc_task(inode); |
| 514 | struct nsproxy *nsp; | 509 | struct nsproxy *nsp; |
| 515 | struct mnt_namespace *ns = NULL; | 510 | struct mnt_namespace *ns = NULL; |
| 511 | struct fs_struct *fs = NULL; | ||
| 512 | struct path root; | ||
| 516 | struct proc_mounts *p; | 513 | struct proc_mounts *p; |
| 517 | int ret = -EINVAL; | 514 | int ret = -EINVAL; |
| 518 | 515 | ||
| @@ -525,40 +522,61 @@ static int mounts_open(struct inode *inode, struct file *file) | |||
| 525 | get_mnt_ns(ns); | 522 | get_mnt_ns(ns); |
| 526 | } | 523 | } |
| 527 | rcu_read_unlock(); | 524 | rcu_read_unlock(); |
| 528 | 525 | if (ns) | |
| 526 | fs = get_fs_struct(task); | ||
| 529 | put_task_struct(task); | 527 | put_task_struct(task); |
| 530 | } | 528 | } |
| 531 | 529 | ||
| 532 | if (ns) { | 530 | if (!ns) |
| 533 | ret = -ENOMEM; | 531 | goto err; |
| 534 | p = kmalloc(sizeof(struct proc_mounts), GFP_KERNEL); | 532 | if (!fs) |
| 535 | if (p) { | 533 | goto err_put_ns; |
| 536 | file->private_data = &p->m; | 534 | |
| 537 | ret = seq_open(file, &mounts_op); | 535 | read_lock(&fs->lock); |
| 538 | if (!ret) { | 536 | root = fs->root; |
| 539 | p->m.private = ns; | 537 | path_get(&root); |
| 540 | p->event = ns->event; | 538 | read_unlock(&fs->lock); |
| 541 | return 0; | 539 | put_fs_struct(fs); |
| 542 | } | 540 | |
| 543 | kfree(p); | 541 | ret = -ENOMEM; |
| 544 | } | 542 | p = kmalloc(sizeof(struct proc_mounts), GFP_KERNEL); |
| 545 | put_mnt_ns(ns); | 543 | if (!p) |
| 546 | } | 544 | goto err_put_path; |
| 545 | |||
| 546 | file->private_data = &p->m; | ||
| 547 | ret = seq_open(file, op); | ||
| 548 | if (ret) | ||
| 549 | goto err_free; | ||
| 550 | |||
| 551 | p->m.private = p; | ||
| 552 | p->ns = ns; | ||
| 553 | p->root = root; | ||
| 554 | p->event = ns->event; | ||
| 555 | |||
| 556 | return 0; | ||
| 557 | |||
| 558 | err_free: | ||
| 559 | kfree(p); | ||
| 560 | err_put_path: | ||
| 561 | path_put(&root); | ||
| 562 | err_put_ns: | ||
| 563 | put_mnt_ns(ns); | ||
| 564 | err: | ||
| 547 | return ret; | 565 | return ret; |
| 548 | } | 566 | } |
| 549 | 567 | ||
| 550 | static int mounts_release(struct inode *inode, struct file *file) | 568 | static int mounts_release(struct inode *inode, struct file *file) |
| 551 | { | 569 | { |
| 552 | struct seq_file *m = file->private_data; | 570 | struct proc_mounts *p = file->private_data; |
| 553 | struct mnt_namespace *ns = m->private; | 571 | path_put(&p->root); |
| 554 | put_mnt_ns(ns); | 572 | put_mnt_ns(p->ns); |
| 555 | return seq_release(inode, file); | 573 | return seq_release(inode, file); |
| 556 | } | 574 | } |
| 557 | 575 | ||
| 558 | static unsigned mounts_poll(struct file *file, poll_table *wait) | 576 | static unsigned mounts_poll(struct file *file, poll_table *wait) |
| 559 | { | 577 | { |
| 560 | struct proc_mounts *p = file->private_data; | 578 | struct proc_mounts *p = file->private_data; |
| 561 | struct mnt_namespace *ns = p->m.private; | 579 | struct mnt_namespace *ns = p->ns; |
| 562 | unsigned res = 0; | 580 | unsigned res = 0; |
| 563 | 581 | ||
| 564 | poll_wait(file, &ns->poll, wait); | 582 | poll_wait(file, &ns->poll, wait); |
| @@ -573,6 +591,11 @@ static unsigned mounts_poll(struct file *file, poll_table *wait) | |||
| 573 | return res; | 591 | return res; |
| 574 | } | 592 | } |
| 575 | 593 | ||
| 594 | static int mounts_open(struct inode *inode, struct file *file) | ||
| 595 | { | ||
| 596 | return mounts_open_common(inode, file, &mounts_op); | ||
| 597 | } | ||
| 598 | |||
| 576 | static const struct file_operations proc_mounts_operations = { | 599 | static const struct file_operations proc_mounts_operations = { |
| 577 | .open = mounts_open, | 600 | .open = mounts_open, |
| 578 | .read = seq_read, | 601 | .read = seq_read, |
| @@ -581,38 +604,22 @@ static const struct file_operations proc_mounts_operations = { | |||
| 581 | .poll = mounts_poll, | 604 | .poll = mounts_poll, |
| 582 | }; | 605 | }; |
| 583 | 606 | ||
| 584 | extern const struct seq_operations mountstats_op; | 607 | static int mountinfo_open(struct inode *inode, struct file *file) |
| 585 | static int mountstats_open(struct inode *inode, struct file *file) | ||
| 586 | { | 608 | { |
| 587 | int ret = seq_open(file, &mountstats_op); | 609 | return mounts_open_common(inode, file, &mountinfo_op); |
| 588 | 610 | } | |
| 589 | if (!ret) { | ||
| 590 | struct seq_file *m = file->private_data; | ||
| 591 | struct nsproxy *nsp; | ||
| 592 | struct mnt_namespace *mnt_ns = NULL; | ||
| 593 | struct task_struct *task = get_proc_task(inode); | ||
| 594 | |||
| 595 | if (task) { | ||
| 596 | rcu_read_lock(); | ||
| 597 | nsp = task_nsproxy(task); | ||
| 598 | if (nsp) { | ||
| 599 | mnt_ns = nsp->mnt_ns; | ||
| 600 | if (mnt_ns) | ||
| 601 | get_mnt_ns(mnt_ns); | ||
| 602 | } | ||
| 603 | rcu_read_unlock(); | ||
| 604 | 611 | ||
| 605 | put_task_struct(task); | 612 | static const struct file_operations proc_mountinfo_operations = { |
| 606 | } | 613 | .open = mountinfo_open, |
| 614 | .read = seq_read, | ||
| 615 | .llseek = seq_lseek, | ||
| 616 | .release = mounts_release, | ||
| 617 | .poll = mounts_poll, | ||
| 618 | }; | ||
| 607 | 619 | ||
| 608 | if (mnt_ns) | 620 | static int mountstats_open(struct inode *inode, struct file *file) |
| 609 | m->private = mnt_ns; | 621 | { |
| 610 | else { | 622 | return mounts_open_common(inode, file, &mountstats_op); |
| 611 | seq_release(inode, file); | ||
| 612 | ret = -EINVAL; | ||
| 613 | } | ||
| 614 | } | ||
| 615 | return ret; | ||
| 616 | } | 623 | } |
| 617 | 624 | ||
| 618 | static const struct file_operations proc_mountstats_operations = { | 625 | static const struct file_operations proc_mountstats_operations = { |
| @@ -2309,6 +2316,7 @@ static const struct pid_entry tgid_base_stuff[] = { | |||
| 2309 | LNK("root", root), | 2316 | LNK("root", root), |
| 2310 | LNK("exe", exe), | 2317 | LNK("exe", exe), |
| 2311 | REG("mounts", S_IRUGO, mounts), | 2318 | REG("mounts", S_IRUGO, mounts), |
| 2319 | REG("mountinfo", S_IRUGO, mountinfo), | ||
| 2312 | REG("mountstats", S_IRUSR, mountstats), | 2320 | REG("mountstats", S_IRUSR, mountstats), |
| 2313 | #ifdef CONFIG_PROC_PAGE_MONITOR | 2321 | #ifdef CONFIG_PROC_PAGE_MONITOR |
| 2314 | REG("clear_refs", S_IWUSR, clear_refs), | 2322 | REG("clear_refs", S_IWUSR, clear_refs), |
| @@ -2641,6 +2649,7 @@ static const struct pid_entry tid_base_stuff[] = { | |||
| 2641 | LNK("root", root), | 2649 | LNK("root", root), |
| 2642 | LNK("exe", exe), | 2650 | LNK("exe", exe), |
| 2643 | REG("mounts", S_IRUGO, mounts), | 2651 | REG("mounts", S_IRUGO, mounts), |
| 2652 | REG("mountinfo", S_IRUGO, mountinfo), | ||
| 2644 | #ifdef CONFIG_PROC_PAGE_MONITOR | 2653 | #ifdef CONFIG_PROC_PAGE_MONITOR |
| 2645 | REG("clear_refs", S_IWUSR, clear_refs), | 2654 | REG("clear_refs", S_IWUSR, clear_refs), |
| 2646 | REG("smaps", S_IRUGO, smaps), | 2655 | REG("smaps", S_IRUGO, smaps), |
diff --git a/fs/seq_file.c b/fs/seq_file.c index cdfd996ca6ef..3f54dbd6c49b 100644 --- a/fs/seq_file.c +++ b/fs/seq_file.c | |||
| @@ -350,28 +350,40 @@ int seq_printf(struct seq_file *m, const char *f, ...) | |||
| 350 | } | 350 | } |
| 351 | EXPORT_SYMBOL(seq_printf); | 351 | EXPORT_SYMBOL(seq_printf); |
| 352 | 352 | ||
| 353 | static char *mangle_path(char *s, char *p, char *esc) | ||
| 354 | { | ||
| 355 | while (s <= p) { | ||
| 356 | char c = *p++; | ||
| 357 | if (!c) { | ||
| 358 | return s; | ||
| 359 | } else if (!strchr(esc, c)) { | ||
| 360 | *s++ = c; | ||
| 361 | } else if (s + 4 > p) { | ||
| 362 | break; | ||
| 363 | } else { | ||
| 364 | *s++ = '\\'; | ||
| 365 | *s++ = '0' + ((c & 0300) >> 6); | ||
| 366 | *s++ = '0' + ((c & 070) >> 3); | ||
| 367 | *s++ = '0' + (c & 07); | ||
| 368 | } | ||
| 369 | } | ||
| 370 | return NULL; | ||
| 371 | } | ||
| 372 | |||
| 373 | /* | ||
| 374 | * return the absolute path of 'dentry' residing in mount 'mnt'. | ||
| 375 | */ | ||
| 353 | int seq_path(struct seq_file *m, struct path *path, char *esc) | 376 | int seq_path(struct seq_file *m, struct path *path, char *esc) |
| 354 | { | 377 | { |
| 355 | if (m->count < m->size) { | 378 | if (m->count < m->size) { |
| 356 | char *s = m->buf + m->count; | 379 | char *s = m->buf + m->count; |
| 357 | char *p = d_path(path, s, m->size - m->count); | 380 | char *p = d_path(path, s, m->size - m->count); |
| 358 | if (!IS_ERR(p)) { | 381 | if (!IS_ERR(p)) { |
| 359 | while (s <= p) { | 382 | s = mangle_path(s, p, esc); |
| 360 | char c = *p++; | 383 | if (s) { |
| 361 | if (!c) { | 384 | p = m->buf + m->count; |
| 362 | p = m->buf + m->count; | 385 | m->count = s - m->buf; |
| 363 | m->count = s - m->buf; | 386 | return s - p; |
| 364 | return s - p; | ||
| 365 | } else if (!strchr(esc, c)) { | ||
| 366 | *s++ = c; | ||
| 367 | } else if (s + 4 > p) { | ||
| 368 | break; | ||
| 369 | } else { | ||
| 370 | *s++ = '\\'; | ||
| 371 | *s++ = '0' + ((c & 0300) >> 6); | ||
| 372 | *s++ = '0' + ((c & 070) >> 3); | ||
| 373 | *s++ = '0' + (c & 07); | ||
| 374 | } | ||
| 375 | } | 387 | } |
| 376 | } | 388 | } |
| 377 | } | 389 | } |
| @@ -380,6 +392,57 @@ int seq_path(struct seq_file *m, struct path *path, char *esc) | |||
| 380 | } | 392 | } |
| 381 | EXPORT_SYMBOL(seq_path); | 393 | EXPORT_SYMBOL(seq_path); |
| 382 | 394 | ||
| 395 | /* | ||
| 396 | * Same as seq_path, but relative to supplied root. | ||
| 397 | * | ||
| 398 | * root may be changed, see __d_path(). | ||
| 399 | */ | ||
| 400 | int seq_path_root(struct seq_file *m, struct path *path, struct path *root, | ||
| 401 | char *esc) | ||
| 402 | { | ||
| 403 | int err = -ENAMETOOLONG; | ||
| 404 | if (m->count < m->size) { | ||
| 405 | char *s = m->buf + m->count; | ||
| 406 | char *p; | ||
| 407 | |||
| 408 | spin_lock(&dcache_lock); | ||
| 409 | p = __d_path(path, root, s, m->size - m->count); | ||
| 410 | spin_unlock(&dcache_lock); | ||
| 411 | err = PTR_ERR(p); | ||
| 412 | if (!IS_ERR(p)) { | ||
| 413 | s = mangle_path(s, p, esc); | ||
| 414 | if (s) { | ||
| 415 | p = m->buf + m->count; | ||
| 416 | m->count = s - m->buf; | ||
| 417 | return 0; | ||
| 418 | } | ||
| 419 | } | ||
| 420 | } | ||
| 421 | m->count = m->size; | ||
| 422 | return err; | ||
| 423 | } | ||
| 424 | |||
| 425 | /* | ||
| 426 | * returns the path of the 'dentry' from the root of its filesystem. | ||
| 427 | */ | ||
| 428 | int seq_dentry(struct seq_file *m, struct dentry *dentry, char *esc) | ||
| 429 | { | ||
| 430 | if (m->count < m->size) { | ||
| 431 | char *s = m->buf + m->count; | ||
| 432 | char *p = dentry_path(dentry, s, m->size - m->count); | ||
| 433 | if (!IS_ERR(p)) { | ||
| 434 | s = mangle_path(s, p, esc); | ||
| 435 | if (s) { | ||
| 436 | p = m->buf + m->count; | ||
| 437 | m->count = s - m->buf; | ||
| 438 | return s - p; | ||
| 439 | } | ||
| 440 | } | ||
| 441 | } | ||
| 442 | m->count = m->size; | ||
| 443 | return -1; | ||
| 444 | } | ||
| 445 | |||
| 383 | static void *single_start(struct seq_file *p, loff_t *pos) | 446 | static void *single_start(struct seq_file *p, loff_t *pos) |
| 384 | { | 447 | { |
| 385 | return NULL + (*pos == 0); | 448 | return NULL + (*pos == 0); |
diff --git a/fs/xattr.c b/fs/xattr.c index f7062da505d4..89a942f07e1b 100644 --- a/fs/xattr.c +++ b/fs/xattr.c | |||
| @@ -307,7 +307,6 @@ sys_fsetxattr(int fd, char __user *name, void __user *value, | |||
| 307 | error = setxattr(dentry, name, value, size, flags); | 307 | error = setxattr(dentry, name, value, size, flags); |
| 308 | mnt_drop_write(f->f_path.mnt); | 308 | mnt_drop_write(f->f_path.mnt); |
| 309 | } | 309 | } |
| 310 | out_fput: | ||
| 311 | fput(f); | 310 | fput(f); |
| 312 | return error; | 311 | return error; |
| 313 | } | 312 | } |
diff --git a/include/linux/dcache.h b/include/linux/dcache.h index fabd16d03a27..cfb1627ac51c 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h | |||
| @@ -301,7 +301,9 @@ extern int d_validate(struct dentry *, struct dentry *); | |||
| 301 | */ | 301 | */ |
| 302 | extern char *dynamic_dname(struct dentry *, char *, int, const char *, ...); | 302 | extern char *dynamic_dname(struct dentry *, char *, int, const char *, ...); |
| 303 | 303 | ||
| 304 | extern char *__d_path(const struct path *path, struct path *root, char *, int); | ||
| 304 | extern char *d_path(struct path *, char *, int); | 305 | extern char *d_path(struct path *, char *, int); |
| 306 | extern char *dentry_path(struct dentry *, char *, int); | ||
| 305 | 307 | ||
| 306 | /* Allocation counts.. */ | 308 | /* Allocation counts.. */ |
| 307 | 309 | ||
diff --git a/include/linux/mnt_namespace.h b/include/linux/mnt_namespace.h index 8eed44f8ca73..830bbcd449d6 100644 --- a/include/linux/mnt_namespace.h +++ b/include/linux/mnt_namespace.h | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #include <linux/mount.h> | 5 | #include <linux/mount.h> |
| 6 | #include <linux/sched.h> | 6 | #include <linux/sched.h> |
| 7 | #include <linux/nsproxy.h> | 7 | #include <linux/nsproxy.h> |
| 8 | #include <linux/seq_file.h> | ||
| 8 | 9 | ||
| 9 | struct mnt_namespace { | 10 | struct mnt_namespace { |
| 10 | atomic_t count; | 11 | atomic_t count; |
| @@ -14,6 +15,13 @@ struct mnt_namespace { | |||
| 14 | int event; | 15 | int event; |
| 15 | }; | 16 | }; |
| 16 | 17 | ||
| 18 | struct proc_mounts { | ||
| 19 | struct seq_file m; /* must be the first element */ | ||
| 20 | struct mnt_namespace *ns; | ||
| 21 | struct path root; | ||
| 22 | int event; | ||
| 23 | }; | ||
| 24 | |||
| 17 | extern struct mnt_namespace *copy_mnt_ns(unsigned long, struct mnt_namespace *, | 25 | extern struct mnt_namespace *copy_mnt_ns(unsigned long, struct mnt_namespace *, |
| 18 | struct fs_struct *); | 26 | struct fs_struct *); |
| 19 | extern void __put_mnt_ns(struct mnt_namespace *ns); | 27 | extern void __put_mnt_ns(struct mnt_namespace *ns); |
| @@ -37,5 +45,9 @@ static inline void get_mnt_ns(struct mnt_namespace *ns) | |||
| 37 | atomic_inc(&ns->count); | 45 | atomic_inc(&ns->count); |
| 38 | } | 46 | } |
| 39 | 47 | ||
| 48 | extern const struct seq_operations mounts_op; | ||
| 49 | extern const struct seq_operations mountinfo_op; | ||
| 50 | extern const struct seq_operations mountstats_op; | ||
| 51 | |||
| 40 | #endif | 52 | #endif |
| 41 | #endif | 53 | #endif |
diff --git a/include/linux/mount.h b/include/linux/mount.h index 87b24cea1863..b4836d58f428 100644 --- a/include/linux/mount.h +++ b/include/linux/mount.h | |||
| @@ -56,6 +56,8 @@ struct vfsmount { | |||
| 56 | struct list_head mnt_slave; /* slave list entry */ | 56 | struct list_head mnt_slave; /* slave list entry */ |
| 57 | struct vfsmount *mnt_master; /* slave is on master->mnt_slave_list */ | 57 | struct vfsmount *mnt_master; /* slave is on master->mnt_slave_list */ |
| 58 | struct mnt_namespace *mnt_ns; /* containing namespace */ | 58 | struct mnt_namespace *mnt_ns; /* containing namespace */ |
| 59 | int mnt_id; /* mount identifier */ | ||
| 60 | int mnt_group_id; /* peer group identifier */ | ||
| 59 | /* | 61 | /* |
| 60 | * We put mnt_count & mnt_expiry_mark at the end of struct vfsmount | 62 | * We put mnt_count & mnt_expiry_mark at the end of struct vfsmount |
| 61 | * to let these frequently modified fields in a separate cache line | 63 | * to let these frequently modified fields in a separate cache line |
diff --git a/include/linux/seq_file.h b/include/linux/seq_file.h index d65796dc26d9..5b5369c3c209 100644 --- a/include/linux/seq_file.h +++ b/include/linux/seq_file.h | |||
| @@ -10,6 +10,7 @@ struct seq_operations; | |||
| 10 | struct file; | 10 | struct file; |
| 11 | struct path; | 11 | struct path; |
| 12 | struct inode; | 12 | struct inode; |
| 13 | struct dentry; | ||
| 13 | 14 | ||
| 14 | struct seq_file { | 15 | struct seq_file { |
| 15 | char *buf; | 16 | char *buf; |
| @@ -44,6 +45,9 @@ int seq_printf(struct seq_file *, const char *, ...) | |||
| 44 | __attribute__ ((format (printf,2,3))); | 45 | __attribute__ ((format (printf,2,3))); |
| 45 | 46 | ||
| 46 | int seq_path(struct seq_file *, struct path *, char *); | 47 | int seq_path(struct seq_file *, struct path *, char *); |
| 48 | int seq_dentry(struct seq_file *, struct dentry *, char *); | ||
| 49 | int seq_path_root(struct seq_file *m, struct path *path, struct path *root, | ||
| 50 | char *esc); | ||
| 47 | 51 | ||
| 48 | int single_open(struct file *, int (*)(struct seq_file *, void *), void *); | 52 | int single_open(struct file *, int (*)(struct seq_file *, void *), void *); |
| 49 | int single_release(struct inode *, struct file *); | 53 | int single_release(struct inode *, struct file *); |
