diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-08-09 20:10:41 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-08-09 20:10:41 -0400 |
commit | 77e40aae766ccbbbb0324cb92ab22e6e998375d7 (patch) | |
tree | fb4e8e840aaeeaac62249d7585249c4634886baa /fs | |
parent | 96784de59fb35077c2bb33c39328992b836d87d3 (diff) | |
parent | 344470cac42e887e68cfb5bdfa6171baf27f1eb5 (diff) |
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ebiederm/user-namespace
Pull namespace updates from Eric Biederman:
"This is a bunch of small changes built against 3.16-rc6. The most
significant change for users is the first patch which makes setns
drmatically faster by removing unneded rcu handling.
The next chunk of changes are so that "mount -o remount,.." will not
allow the user namespace root to drop flags on a mount set by the
system wide root. Aks this forces read-only mounts to stay read-only,
no-dev mounts to stay no-dev, no-suid mounts to stay no-suid, no-exec
mounts to stay no exec and it prevents unprivileged users from messing
with a mounts atime settings. I have included my test case as the
last patch in this series so people performing backports can verify
this change works correctly.
The next change fixes a bug in NFS that was discovered while auditing
nsproxy users for the first optimization. Today you can oops the
kernel by reading /proc/fs/nfsfs/{servers,volumes} if you are clever
with pid namespaces. I rebased and fixed the build of the
!CONFIG_NFS_FS case yesterday when a build bot caught my typo. Given
that no one to my knowledge bases anything on my tree fixing the typo
in place seems more responsible that requiring a typo-fix to be
backported as well.
The last change is a small semantic cleanup introducing
/proc/thread-self and pointing /proc/mounts and /proc/net at it. This
prevents several kinds of problemantic corner cases. It is a
user-visible change so it has a minute chance of causing regressions
so the change to /proc/mounts and /proc/net are individual one line
commits that can be trivially reverted. Unfortunately I lost and
could not find the email of the original reporter so he is not
credited. From at least one perspective this change to /proc/net is a
refgression fix to allow pthread /proc/net uses that were broken by
the introduction of the network namespace"
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ebiederm/user-namespace:
proc: Point /proc/mounts at /proc/thread-self/mounts instead of /proc/self/mounts
proc: Point /proc/net at /proc/thread-self/net instead of /proc/self/net
proc: Implement /proc/thread-self to point at the directory of the current thread
proc: Have net show up under /proc/<tgid>/task/<tid>
NFS: Fix /proc/fs/nfsfs/servers and /proc/fs/nfsfs/volumes
mnt: Add tests for unprivileged remount cases that have found to be faulty
mnt: Change the default remount atime from relatime to the existing value
mnt: Correct permission checks in do_remount
mnt: Move the test for MNT_LOCK_READONLY from change_mount_flags into do_remount
mnt: Only change user settable mount flags in remount
namespaces: Use task_lock and not rcu to protect nsproxy
Diffstat (limited to 'fs')
-rw-r--r-- | fs/namespace.c | 65 | ||||
-rw-r--r-- | fs/nfs/client.c | 95 | ||||
-rw-r--r-- | fs/nfs/inode.c | 3 | ||||
-rw-r--r-- | fs/nfs/internal.h | 9 | ||||
-rw-r--r-- | fs/nfs/netns.h | 3 | ||||
-rw-r--r-- | fs/proc/Makefile | 1 | ||||
-rw-r--r-- | fs/proc/base.c | 18 | ||||
-rw-r--r-- | fs/proc/inode.c | 7 | ||||
-rw-r--r-- | fs/proc/internal.h | 6 | ||||
-rw-r--r-- | fs/proc/proc_net.c | 6 | ||||
-rw-r--r-- | fs/proc/root.c | 5 | ||||
-rw-r--r-- | fs/proc/thread_self.c | 85 | ||||
-rw-r--r-- | fs/proc_namespace.c | 8 |
13 files changed, 246 insertions, 65 deletions
diff --git a/fs/namespace.c b/fs/namespace.c index 2a1447c946e7..0acabea58319 100644 --- a/fs/namespace.c +++ b/fs/namespace.c | |||
@@ -890,8 +890,21 @@ static struct mount *clone_mnt(struct mount *old, struct dentry *root, | |||
890 | 890 | ||
891 | mnt->mnt.mnt_flags = old->mnt.mnt_flags & ~(MNT_WRITE_HOLD|MNT_MARKED); | 891 | mnt->mnt.mnt_flags = old->mnt.mnt_flags & ~(MNT_WRITE_HOLD|MNT_MARKED); |
892 | /* Don't allow unprivileged users to change mount flags */ | 892 | /* Don't allow unprivileged users to change mount flags */ |
893 | if ((flag & CL_UNPRIVILEGED) && (mnt->mnt.mnt_flags & MNT_READONLY)) | 893 | if (flag & CL_UNPRIVILEGED) { |
894 | mnt->mnt.mnt_flags |= MNT_LOCK_READONLY; | 894 | mnt->mnt.mnt_flags |= MNT_LOCK_ATIME; |
895 | |||
896 | if (mnt->mnt.mnt_flags & MNT_READONLY) | ||
897 | mnt->mnt.mnt_flags |= MNT_LOCK_READONLY; | ||
898 | |||
899 | if (mnt->mnt.mnt_flags & MNT_NODEV) | ||
900 | mnt->mnt.mnt_flags |= MNT_LOCK_NODEV; | ||
901 | |||
902 | if (mnt->mnt.mnt_flags & MNT_NOSUID) | ||
903 | mnt->mnt.mnt_flags |= MNT_LOCK_NOSUID; | ||
904 | |||
905 | if (mnt->mnt.mnt_flags & MNT_NOEXEC) | ||
906 | mnt->mnt.mnt_flags |= MNT_LOCK_NOEXEC; | ||
907 | } | ||
895 | 908 | ||
896 | /* Don't allow unprivileged users to reveal what is under a mount */ | 909 | /* Don't allow unprivileged users to reveal what is under a mount */ |
897 | if ((flag & CL_UNPRIVILEGED) && list_empty(&old->mnt_expire)) | 910 | if ((flag & CL_UNPRIVILEGED) && list_empty(&old->mnt_expire)) |
@@ -1896,9 +1909,6 @@ static int change_mount_flags(struct vfsmount *mnt, int ms_flags) | |||
1896 | if (readonly_request == __mnt_is_readonly(mnt)) | 1909 | if (readonly_request == __mnt_is_readonly(mnt)) |
1897 | return 0; | 1910 | return 0; |
1898 | 1911 | ||
1899 | if (mnt->mnt_flags & MNT_LOCK_READONLY) | ||
1900 | return -EPERM; | ||
1901 | |||
1902 | if (readonly_request) | 1912 | if (readonly_request) |
1903 | error = mnt_make_readonly(real_mount(mnt)); | 1913 | error = mnt_make_readonly(real_mount(mnt)); |
1904 | else | 1914 | else |
@@ -1924,6 +1934,33 @@ static int do_remount(struct path *path, int flags, int mnt_flags, | |||
1924 | if (path->dentry != path->mnt->mnt_root) | 1934 | if (path->dentry != path->mnt->mnt_root) |
1925 | return -EINVAL; | 1935 | return -EINVAL; |
1926 | 1936 | ||
1937 | /* Don't allow changing of locked mnt flags. | ||
1938 | * | ||
1939 | * No locks need to be held here while testing the various | ||
1940 | * MNT_LOCK flags because those flags can never be cleared | ||
1941 | * once they are set. | ||
1942 | */ | ||
1943 | if ((mnt->mnt.mnt_flags & MNT_LOCK_READONLY) && | ||
1944 | !(mnt_flags & MNT_READONLY)) { | ||
1945 | return -EPERM; | ||
1946 | } | ||
1947 | if ((mnt->mnt.mnt_flags & MNT_LOCK_NODEV) && | ||
1948 | !(mnt_flags & MNT_NODEV)) { | ||
1949 | return -EPERM; | ||
1950 | } | ||
1951 | if ((mnt->mnt.mnt_flags & MNT_LOCK_NOSUID) && | ||
1952 | !(mnt_flags & MNT_NOSUID)) { | ||
1953 | return -EPERM; | ||
1954 | } | ||
1955 | if ((mnt->mnt.mnt_flags & MNT_LOCK_NOEXEC) && | ||
1956 | !(mnt_flags & MNT_NOEXEC)) { | ||
1957 | return -EPERM; | ||
1958 | } | ||
1959 | if ((mnt->mnt.mnt_flags & MNT_LOCK_ATIME) && | ||
1960 | ((mnt->mnt.mnt_flags & MNT_ATIME_MASK) != (mnt_flags & MNT_ATIME_MASK))) { | ||
1961 | return -EPERM; | ||
1962 | } | ||
1963 | |||
1927 | err = security_sb_remount(sb, data); | 1964 | err = security_sb_remount(sb, data); |
1928 | if (err) | 1965 | if (err) |
1929 | return err; | 1966 | return err; |
@@ -1937,7 +1974,7 @@ static int do_remount(struct path *path, int flags, int mnt_flags, | |||
1937 | err = do_remount_sb(sb, flags, data, 0); | 1974 | err = do_remount_sb(sb, flags, data, 0); |
1938 | if (!err) { | 1975 | if (!err) { |
1939 | lock_mount_hash(); | 1976 | lock_mount_hash(); |
1940 | mnt_flags |= mnt->mnt.mnt_flags & MNT_PROPAGATION_MASK; | 1977 | mnt_flags |= mnt->mnt.mnt_flags & ~MNT_USER_SETTABLE_MASK; |
1941 | mnt->mnt.mnt_flags = mnt_flags; | 1978 | mnt->mnt.mnt_flags = mnt_flags; |
1942 | touch_mnt_namespace(mnt->mnt_ns); | 1979 | touch_mnt_namespace(mnt->mnt_ns); |
1943 | unlock_mount_hash(); | 1980 | unlock_mount_hash(); |
@@ -2122,7 +2159,7 @@ static int do_new_mount(struct path *path, const char *fstype, int flags, | |||
2122 | */ | 2159 | */ |
2123 | if (!(type->fs_flags & FS_USERNS_DEV_MOUNT)) { | 2160 | if (!(type->fs_flags & FS_USERNS_DEV_MOUNT)) { |
2124 | flags |= MS_NODEV; | 2161 | flags |= MS_NODEV; |
2125 | mnt_flags |= MNT_NODEV; | 2162 | mnt_flags |= MNT_NODEV | MNT_LOCK_NODEV; |
2126 | } | 2163 | } |
2127 | } | 2164 | } |
2128 | 2165 | ||
@@ -2436,6 +2473,14 @@ long do_mount(const char *dev_name, const char *dir_name, | |||
2436 | if (flags & MS_RDONLY) | 2473 | if (flags & MS_RDONLY) |
2437 | mnt_flags |= MNT_READONLY; | 2474 | mnt_flags |= MNT_READONLY; |
2438 | 2475 | ||
2476 | /* The default atime for remount is preservation */ | ||
2477 | if ((flags & MS_REMOUNT) && | ||
2478 | ((flags & (MS_NOATIME | MS_NODIRATIME | MS_RELATIME | | ||
2479 | MS_STRICTATIME)) == 0)) { | ||
2480 | mnt_flags &= ~MNT_ATIME_MASK; | ||
2481 | mnt_flags |= path.mnt->mnt_flags & MNT_ATIME_MASK; | ||
2482 | } | ||
2483 | |||
2439 | flags &= ~(MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_ACTIVE | MS_BORN | | 2484 | flags &= ~(MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_ACTIVE | MS_BORN | |
2440 | MS_NOATIME | MS_NODIRATIME | MS_RELATIME| MS_KERNMOUNT | | 2485 | MS_NOATIME | MS_NODIRATIME | MS_RELATIME| MS_KERNMOUNT | |
2441 | MS_STRICTATIME); | 2486 | MS_STRICTATIME); |
@@ -2972,13 +3017,13 @@ static void *mntns_get(struct task_struct *task) | |||
2972 | struct mnt_namespace *ns = NULL; | 3017 | struct mnt_namespace *ns = NULL; |
2973 | struct nsproxy *nsproxy; | 3018 | struct nsproxy *nsproxy; |
2974 | 3019 | ||
2975 | rcu_read_lock(); | 3020 | task_lock(task); |
2976 | nsproxy = task_nsproxy(task); | 3021 | nsproxy = task->nsproxy; |
2977 | if (nsproxy) { | 3022 | if (nsproxy) { |
2978 | ns = nsproxy->mnt_ns; | 3023 | ns = nsproxy->mnt_ns; |
2979 | get_mnt_ns(ns); | 3024 | get_mnt_ns(ns); |
2980 | } | 3025 | } |
2981 | rcu_read_unlock(); | 3026 | task_unlock(task); |
2982 | 3027 | ||
2983 | return ns; | 3028 | return ns; |
2984 | } | 3029 | } |
diff --git a/fs/nfs/client.c b/fs/nfs/client.c index 1d09289c8f0e..180d1ec9c32e 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c | |||
@@ -1205,7 +1205,7 @@ static const struct file_operations nfs_server_list_fops = { | |||
1205 | .open = nfs_server_list_open, | 1205 | .open = nfs_server_list_open, |
1206 | .read = seq_read, | 1206 | .read = seq_read, |
1207 | .llseek = seq_lseek, | 1207 | .llseek = seq_lseek, |
1208 | .release = seq_release, | 1208 | .release = seq_release_net, |
1209 | .owner = THIS_MODULE, | 1209 | .owner = THIS_MODULE, |
1210 | }; | 1210 | }; |
1211 | 1211 | ||
@@ -1226,7 +1226,7 @@ static const struct file_operations nfs_volume_list_fops = { | |||
1226 | .open = nfs_volume_list_open, | 1226 | .open = nfs_volume_list_open, |
1227 | .read = seq_read, | 1227 | .read = seq_read, |
1228 | .llseek = seq_lseek, | 1228 | .llseek = seq_lseek, |
1229 | .release = seq_release, | 1229 | .release = seq_release_net, |
1230 | .owner = THIS_MODULE, | 1230 | .owner = THIS_MODULE, |
1231 | }; | 1231 | }; |
1232 | 1232 | ||
@@ -1236,19 +1236,8 @@ static const struct file_operations nfs_volume_list_fops = { | |||
1236 | */ | 1236 | */ |
1237 | static int nfs_server_list_open(struct inode *inode, struct file *file) | 1237 | static int nfs_server_list_open(struct inode *inode, struct file *file) |
1238 | { | 1238 | { |
1239 | struct seq_file *m; | 1239 | return seq_open_net(inode, file, &nfs_server_list_ops, |
1240 | int ret; | 1240 | sizeof(struct seq_net_private)); |
1241 | struct pid_namespace *pid_ns = file->f_dentry->d_sb->s_fs_info; | ||
1242 | struct net *net = pid_ns->child_reaper->nsproxy->net_ns; | ||
1243 | |||
1244 | ret = seq_open(file, &nfs_server_list_ops); | ||
1245 | if (ret < 0) | ||
1246 | return ret; | ||
1247 | |||
1248 | m = file->private_data; | ||
1249 | m->private = net; | ||
1250 | |||
1251 | return 0; | ||
1252 | } | 1241 | } |
1253 | 1242 | ||
1254 | /* | 1243 | /* |
@@ -1256,7 +1245,7 @@ static int nfs_server_list_open(struct inode *inode, struct file *file) | |||
1256 | */ | 1245 | */ |
1257 | static void *nfs_server_list_start(struct seq_file *m, loff_t *_pos) | 1246 | static void *nfs_server_list_start(struct seq_file *m, loff_t *_pos) |
1258 | { | 1247 | { |
1259 | struct nfs_net *nn = net_generic(m->private, nfs_net_id); | 1248 | struct nfs_net *nn = net_generic(seq_file_net(m), nfs_net_id); |
1260 | 1249 | ||
1261 | /* lock the list against modification */ | 1250 | /* lock the list against modification */ |
1262 | spin_lock(&nn->nfs_client_lock); | 1251 | spin_lock(&nn->nfs_client_lock); |
@@ -1268,7 +1257,7 @@ static void *nfs_server_list_start(struct seq_file *m, loff_t *_pos) | |||
1268 | */ | 1257 | */ |
1269 | static void *nfs_server_list_next(struct seq_file *p, void *v, loff_t *pos) | 1258 | static void *nfs_server_list_next(struct seq_file *p, void *v, loff_t *pos) |
1270 | { | 1259 | { |
1271 | struct nfs_net *nn = net_generic(p->private, nfs_net_id); | 1260 | struct nfs_net *nn = net_generic(seq_file_net(p), nfs_net_id); |
1272 | 1261 | ||
1273 | return seq_list_next(v, &nn->nfs_client_list, pos); | 1262 | return seq_list_next(v, &nn->nfs_client_list, pos); |
1274 | } | 1263 | } |
@@ -1278,7 +1267,7 @@ static void *nfs_server_list_next(struct seq_file *p, void *v, loff_t *pos) | |||
1278 | */ | 1267 | */ |
1279 | static void nfs_server_list_stop(struct seq_file *p, void *v) | 1268 | static void nfs_server_list_stop(struct seq_file *p, void *v) |
1280 | { | 1269 | { |
1281 | struct nfs_net *nn = net_generic(p->private, nfs_net_id); | 1270 | struct nfs_net *nn = net_generic(seq_file_net(p), nfs_net_id); |
1282 | 1271 | ||
1283 | spin_unlock(&nn->nfs_client_lock); | 1272 | spin_unlock(&nn->nfs_client_lock); |
1284 | } | 1273 | } |
@@ -1289,7 +1278,7 @@ static void nfs_server_list_stop(struct seq_file *p, void *v) | |||
1289 | static int nfs_server_list_show(struct seq_file *m, void *v) | 1278 | static int nfs_server_list_show(struct seq_file *m, void *v) |
1290 | { | 1279 | { |
1291 | struct nfs_client *clp; | 1280 | struct nfs_client *clp; |
1292 | struct nfs_net *nn = net_generic(m->private, nfs_net_id); | 1281 | struct nfs_net *nn = net_generic(seq_file_net(m), nfs_net_id); |
1293 | 1282 | ||
1294 | /* display header on line 1 */ | 1283 | /* display header on line 1 */ |
1295 | if (v == &nn->nfs_client_list) { | 1284 | if (v == &nn->nfs_client_list) { |
@@ -1321,19 +1310,8 @@ static int nfs_server_list_show(struct seq_file *m, void *v) | |||
1321 | */ | 1310 | */ |
1322 | static int nfs_volume_list_open(struct inode *inode, struct file *file) | 1311 | static int nfs_volume_list_open(struct inode *inode, struct file *file) |
1323 | { | 1312 | { |
1324 | struct seq_file *m; | 1313 | return seq_open_net(inode, file, &nfs_server_list_ops, |
1325 | int ret; | 1314 | sizeof(struct seq_net_private)); |
1326 | struct pid_namespace *pid_ns = file->f_dentry->d_sb->s_fs_info; | ||
1327 | struct net *net = pid_ns->child_reaper->nsproxy->net_ns; | ||
1328 | |||
1329 | ret = seq_open(file, &nfs_volume_list_ops); | ||
1330 | if (ret < 0) | ||
1331 | return ret; | ||
1332 | |||
1333 | m = file->private_data; | ||
1334 | m->private = net; | ||
1335 | |||
1336 | return 0; | ||
1337 | } | 1315 | } |
1338 | 1316 | ||
1339 | /* | 1317 | /* |
@@ -1341,7 +1319,7 @@ static int nfs_volume_list_open(struct inode *inode, struct file *file) | |||
1341 | */ | 1319 | */ |
1342 | static void *nfs_volume_list_start(struct seq_file *m, loff_t *_pos) | 1320 | static void *nfs_volume_list_start(struct seq_file *m, loff_t *_pos) |
1343 | { | 1321 | { |
1344 | struct nfs_net *nn = net_generic(m->private, nfs_net_id); | 1322 | struct nfs_net *nn = net_generic(seq_file_net(m), nfs_net_id); |
1345 | 1323 | ||
1346 | /* lock the list against modification */ | 1324 | /* lock the list against modification */ |
1347 | spin_lock(&nn->nfs_client_lock); | 1325 | spin_lock(&nn->nfs_client_lock); |
@@ -1353,7 +1331,7 @@ static void *nfs_volume_list_start(struct seq_file *m, loff_t *_pos) | |||
1353 | */ | 1331 | */ |
1354 | static void *nfs_volume_list_next(struct seq_file *p, void *v, loff_t *pos) | 1332 | static void *nfs_volume_list_next(struct seq_file *p, void *v, loff_t *pos) |
1355 | { | 1333 | { |
1356 | struct nfs_net *nn = net_generic(p->private, nfs_net_id); | 1334 | struct nfs_net *nn = net_generic(seq_file_net(p), nfs_net_id); |
1357 | 1335 | ||
1358 | return seq_list_next(v, &nn->nfs_volume_list, pos); | 1336 | return seq_list_next(v, &nn->nfs_volume_list, pos); |
1359 | } | 1337 | } |
@@ -1363,7 +1341,7 @@ static void *nfs_volume_list_next(struct seq_file *p, void *v, loff_t *pos) | |||
1363 | */ | 1341 | */ |
1364 | static void nfs_volume_list_stop(struct seq_file *p, void *v) | 1342 | static void nfs_volume_list_stop(struct seq_file *p, void *v) |
1365 | { | 1343 | { |
1366 | struct nfs_net *nn = net_generic(p->private, nfs_net_id); | 1344 | struct nfs_net *nn = net_generic(seq_file_net(p), nfs_net_id); |
1367 | 1345 | ||
1368 | spin_unlock(&nn->nfs_client_lock); | 1346 | spin_unlock(&nn->nfs_client_lock); |
1369 | } | 1347 | } |
@@ -1376,7 +1354,7 @@ static int nfs_volume_list_show(struct seq_file *m, void *v) | |||
1376 | struct nfs_server *server; | 1354 | struct nfs_server *server; |
1377 | struct nfs_client *clp; | 1355 | struct nfs_client *clp; |
1378 | char dev[8], fsid[17]; | 1356 | char dev[8], fsid[17]; |
1379 | struct nfs_net *nn = net_generic(m->private, nfs_net_id); | 1357 | struct nfs_net *nn = net_generic(seq_file_net(m), nfs_net_id); |
1380 | 1358 | ||
1381 | /* display header on line 1 */ | 1359 | /* display header on line 1 */ |
1382 | if (v == &nn->nfs_volume_list) { | 1360 | if (v == &nn->nfs_volume_list) { |
@@ -1407,6 +1385,45 @@ static int nfs_volume_list_show(struct seq_file *m, void *v) | |||
1407 | return 0; | 1385 | return 0; |
1408 | } | 1386 | } |
1409 | 1387 | ||
1388 | int nfs_fs_proc_net_init(struct net *net) | ||
1389 | { | ||
1390 | struct nfs_net *nn = net_generic(net, nfs_net_id); | ||
1391 | struct proc_dir_entry *p; | ||
1392 | |||
1393 | nn->proc_nfsfs = proc_net_mkdir(net, "nfsfs", net->proc_net); | ||
1394 | if (!nn->proc_nfsfs) | ||
1395 | goto error_0; | ||
1396 | |||
1397 | /* a file of servers with which we're dealing */ | ||
1398 | p = proc_create("servers", S_IFREG|S_IRUGO, | ||
1399 | nn->proc_nfsfs, &nfs_server_list_fops); | ||
1400 | if (!p) | ||
1401 | goto error_1; | ||
1402 | |||
1403 | /* a file of volumes that we have mounted */ | ||
1404 | p = proc_create("volumes", S_IFREG|S_IRUGO, | ||
1405 | nn->proc_nfsfs, &nfs_volume_list_fops); | ||
1406 | if (!p) | ||
1407 | goto error_2; | ||
1408 | return 0; | ||
1409 | |||
1410 | error_2: | ||
1411 | remove_proc_entry("servers", nn->proc_nfsfs); | ||
1412 | error_1: | ||
1413 | remove_proc_entry("fs/nfsfs", NULL); | ||
1414 | error_0: | ||
1415 | return -ENOMEM; | ||
1416 | } | ||
1417 | |||
1418 | void nfs_fs_proc_net_exit(struct net *net) | ||
1419 | { | ||
1420 | struct nfs_net *nn = net_generic(net, nfs_net_id); | ||
1421 | |||
1422 | remove_proc_entry("volumes", nn->proc_nfsfs); | ||
1423 | remove_proc_entry("servers", nn->proc_nfsfs); | ||
1424 | remove_proc_entry("fs/nfsfs", NULL); | ||
1425 | } | ||
1426 | |||
1410 | /* | 1427 | /* |
1411 | * initialise the /proc/fs/nfsfs/ directory | 1428 | * initialise the /proc/fs/nfsfs/ directory |
1412 | */ | 1429 | */ |
@@ -1419,14 +1436,12 @@ int __init nfs_fs_proc_init(void) | |||
1419 | goto error_0; | 1436 | goto error_0; |
1420 | 1437 | ||
1421 | /* a file of servers with which we're dealing */ | 1438 | /* a file of servers with which we're dealing */ |
1422 | p = proc_create("servers", S_IFREG|S_IRUGO, | 1439 | p = proc_symlink("servers", proc_fs_nfs, "../../net/nfsfs/servers"); |
1423 | proc_fs_nfs, &nfs_server_list_fops); | ||
1424 | if (!p) | 1440 | if (!p) |
1425 | goto error_1; | 1441 | goto error_1; |
1426 | 1442 | ||
1427 | /* a file of volumes that we have mounted */ | 1443 | /* a file of volumes that we have mounted */ |
1428 | p = proc_create("volumes", S_IFREG|S_IRUGO, | 1444 | p = proc_symlink("volumes", proc_fs_nfs, "../../net/nfsfs/volumes"); |
1429 | proc_fs_nfs, &nfs_volume_list_fops); | ||
1430 | if (!p) | 1445 | if (!p) |
1431 | goto error_2; | 1446 | goto error_2; |
1432 | return 0; | 1447 | return 0; |
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index abd37a380535..68921b01b792 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c | |||
@@ -1840,11 +1840,12 @@ EXPORT_SYMBOL_GPL(nfs_net_id); | |||
1840 | static int nfs_net_init(struct net *net) | 1840 | static int nfs_net_init(struct net *net) |
1841 | { | 1841 | { |
1842 | nfs_clients_init(net); | 1842 | nfs_clients_init(net); |
1843 | return 0; | 1843 | return nfs_fs_proc_net_init(net); |
1844 | } | 1844 | } |
1845 | 1845 | ||
1846 | static void nfs_net_exit(struct net *net) | 1846 | static void nfs_net_exit(struct net *net) |
1847 | { | 1847 | { |
1848 | nfs_fs_proc_net_exit(net); | ||
1848 | nfs_cleanup_cb_ident_idr(net); | 1849 | nfs_cleanup_cb_ident_idr(net); |
1849 | } | 1850 | } |
1850 | 1851 | ||
diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index 617f36611d4a..e2a45ae5014e 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h | |||
@@ -195,7 +195,16 @@ extern struct rpc_clnt *nfs4_find_or_create_ds_client(struct nfs_client *, | |||
195 | #ifdef CONFIG_PROC_FS | 195 | #ifdef CONFIG_PROC_FS |
196 | extern int __init nfs_fs_proc_init(void); | 196 | extern int __init nfs_fs_proc_init(void); |
197 | extern void nfs_fs_proc_exit(void); | 197 | extern void nfs_fs_proc_exit(void); |
198 | extern int nfs_fs_proc_net_init(struct net *net); | ||
199 | extern void nfs_fs_proc_net_exit(struct net *net); | ||
198 | #else | 200 | #else |
201 | static inline int nfs_fs_proc_net_init(struct net *net) | ||
202 | { | ||
203 | return 0; | ||
204 | } | ||
205 | static inline void nfs_fs_proc_net_exit(struct net *net) | ||
206 | { | ||
207 | } | ||
199 | static inline int nfs_fs_proc_init(void) | 208 | static inline int nfs_fs_proc_init(void) |
200 | { | 209 | { |
201 | return 0; | 210 | return 0; |
diff --git a/fs/nfs/netns.h b/fs/nfs/netns.h index 8ee1fab83268..ef221fb8a183 100644 --- a/fs/nfs/netns.h +++ b/fs/nfs/netns.h | |||
@@ -29,6 +29,9 @@ struct nfs_net { | |||
29 | #endif | 29 | #endif |
30 | spinlock_t nfs_client_lock; | 30 | spinlock_t nfs_client_lock; |
31 | struct timespec boot_time; | 31 | struct timespec boot_time; |
32 | #ifdef CONFIG_PROC_FS | ||
33 | struct proc_dir_entry *proc_nfsfs; | ||
34 | #endif | ||
32 | }; | 35 | }; |
33 | 36 | ||
34 | extern int nfs_net_id; | 37 | extern int nfs_net_id; |
diff --git a/fs/proc/Makefile b/fs/proc/Makefile index 239493ec718e..7151ea428041 100644 --- a/fs/proc/Makefile +++ b/fs/proc/Makefile | |||
@@ -23,6 +23,7 @@ proc-y += version.o | |||
23 | proc-y += softirqs.o | 23 | proc-y += softirqs.o |
24 | proc-y += namespaces.o | 24 | proc-y += namespaces.o |
25 | proc-y += self.o | 25 | proc-y += self.o |
26 | proc-y += thread_self.o | ||
26 | proc-$(CONFIG_PROC_SYSCTL) += proc_sysctl.o | 27 | proc-$(CONFIG_PROC_SYSCTL) += proc_sysctl.o |
27 | proc-$(CONFIG_NET) += proc_net.o | 28 | proc-$(CONFIG_NET) += proc_net.o |
28 | proc-$(CONFIG_PROC_KCORE) += kcore.o | 29 | proc-$(CONFIG_PROC_KCORE) += kcore.o |
diff --git a/fs/proc/base.c b/fs/proc/base.c index 043c83cb51f9..baf852b648ad 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c | |||
@@ -2814,7 +2814,7 @@ retry: | |||
2814 | return iter; | 2814 | return iter; |
2815 | } | 2815 | } |
2816 | 2816 | ||
2817 | #define TGID_OFFSET (FIRST_PROCESS_ENTRY + 1) | 2817 | #define TGID_OFFSET (FIRST_PROCESS_ENTRY + 2) |
2818 | 2818 | ||
2819 | /* for the /proc/ directory itself, after non-process stuff has been done */ | 2819 | /* for the /proc/ directory itself, after non-process stuff has been done */ |
2820 | int proc_pid_readdir(struct file *file, struct dir_context *ctx) | 2820 | int proc_pid_readdir(struct file *file, struct dir_context *ctx) |
@@ -2826,14 +2826,19 @@ int proc_pid_readdir(struct file *file, struct dir_context *ctx) | |||
2826 | if (pos >= PID_MAX_LIMIT + TGID_OFFSET) | 2826 | if (pos >= PID_MAX_LIMIT + TGID_OFFSET) |
2827 | return 0; | 2827 | return 0; |
2828 | 2828 | ||
2829 | if (pos == TGID_OFFSET - 1) { | 2829 | if (pos == TGID_OFFSET - 2) { |
2830 | struct inode *inode = ns->proc_self->d_inode; | 2830 | struct inode *inode = ns->proc_self->d_inode; |
2831 | if (!dir_emit(ctx, "self", 4, inode->i_ino, DT_LNK)) | 2831 | if (!dir_emit(ctx, "self", 4, inode->i_ino, DT_LNK)) |
2832 | return 0; | 2832 | return 0; |
2833 | iter.tgid = 0; | 2833 | ctx->pos = pos = pos + 1; |
2834 | } else { | 2834 | } |
2835 | iter.tgid = pos - TGID_OFFSET; | 2835 | if (pos == TGID_OFFSET - 1) { |
2836 | struct inode *inode = ns->proc_thread_self->d_inode; | ||
2837 | if (!dir_emit(ctx, "thread-self", 11, inode->i_ino, DT_LNK)) | ||
2838 | return 0; | ||
2839 | ctx->pos = pos = pos + 1; | ||
2836 | } | 2840 | } |
2841 | iter.tgid = pos - TGID_OFFSET; | ||
2837 | iter.task = NULL; | 2842 | iter.task = NULL; |
2838 | for (iter = next_tgid(ns, iter); | 2843 | for (iter = next_tgid(ns, iter); |
2839 | iter.task; | 2844 | iter.task; |
@@ -2862,6 +2867,9 @@ static const struct pid_entry tid_base_stuff[] = { | |||
2862 | DIR("fd", S_IRUSR|S_IXUSR, proc_fd_inode_operations, proc_fd_operations), | 2867 | DIR("fd", S_IRUSR|S_IXUSR, proc_fd_inode_operations, proc_fd_operations), |
2863 | DIR("fdinfo", S_IRUSR|S_IXUSR, proc_fdinfo_inode_operations, proc_fdinfo_operations), | 2868 | DIR("fdinfo", S_IRUSR|S_IXUSR, proc_fdinfo_inode_operations, proc_fdinfo_operations), |
2864 | DIR("ns", S_IRUSR|S_IXUGO, proc_ns_dir_inode_operations, proc_ns_dir_operations), | 2869 | DIR("ns", S_IRUSR|S_IXUGO, proc_ns_dir_inode_operations, proc_ns_dir_operations), |
2870 | #ifdef CONFIG_NET | ||
2871 | DIR("net", S_IRUGO|S_IXUGO, proc_net_inode_operations, proc_net_operations), | ||
2872 | #endif | ||
2865 | REG("environ", S_IRUSR, proc_environ_operations), | 2873 | REG("environ", S_IRUSR, proc_environ_operations), |
2866 | ONE("auxv", S_IRUSR, proc_pid_auxv), | 2874 | ONE("auxv", S_IRUSR, proc_pid_auxv), |
2867 | ONE("status", S_IRUGO, proc_pid_status), | 2875 | ONE("status", S_IRUGO, proc_pid_status), |
diff --git a/fs/proc/inode.c b/fs/proc/inode.c index 0adbc02d60e3..333080d7a671 100644 --- a/fs/proc/inode.c +++ b/fs/proc/inode.c | |||
@@ -442,6 +442,7 @@ struct inode *proc_get_inode(struct super_block *sb, struct proc_dir_entry *de) | |||
442 | int proc_fill_super(struct super_block *s) | 442 | int proc_fill_super(struct super_block *s) |
443 | { | 443 | { |
444 | struct inode *root_inode; | 444 | struct inode *root_inode; |
445 | int ret; | ||
445 | 446 | ||
446 | s->s_flags |= MS_NODIRATIME | MS_NOSUID | MS_NOEXEC; | 447 | s->s_flags |= MS_NODIRATIME | MS_NOSUID | MS_NOEXEC; |
447 | s->s_blocksize = 1024; | 448 | s->s_blocksize = 1024; |
@@ -463,5 +464,9 @@ int proc_fill_super(struct super_block *s) | |||
463 | return -ENOMEM; | 464 | return -ENOMEM; |
464 | } | 465 | } |
465 | 466 | ||
466 | return proc_setup_self(s); | 467 | ret = proc_setup_self(s); |
468 | if (ret) { | ||
469 | return ret; | ||
470 | } | ||
471 | return proc_setup_thread_self(s); | ||
467 | } | 472 | } |
diff --git a/fs/proc/internal.h b/fs/proc/internal.h index a024cf7b260f..7da13e49128a 100644 --- a/fs/proc/internal.h +++ b/fs/proc/internal.h | |||
@@ -231,6 +231,12 @@ static inline int proc_net_init(void) { return 0; } | |||
231 | extern int proc_setup_self(struct super_block *); | 231 | extern int proc_setup_self(struct super_block *); |
232 | 232 | ||
233 | /* | 233 | /* |
234 | * proc_thread_self.c | ||
235 | */ | ||
236 | extern int proc_setup_thread_self(struct super_block *); | ||
237 | extern void proc_thread_self_init(void); | ||
238 | |||
239 | /* | ||
234 | * proc_sysctl.c | 240 | * proc_sysctl.c |
235 | */ | 241 | */ |
236 | #ifdef CONFIG_PROC_SYSCTL | 242 | #ifdef CONFIG_PROC_SYSCTL |
diff --git a/fs/proc/proc_net.c b/fs/proc/proc_net.c index 4677bb7dc7c2..39481028ec08 100644 --- a/fs/proc/proc_net.c +++ b/fs/proc/proc_net.c | |||
@@ -113,9 +113,11 @@ static struct net *get_proc_task_net(struct inode *dir) | |||
113 | rcu_read_lock(); | 113 | rcu_read_lock(); |
114 | task = pid_task(proc_pid(dir), PIDTYPE_PID); | 114 | task = pid_task(proc_pid(dir), PIDTYPE_PID); |
115 | if (task != NULL) { | 115 | if (task != NULL) { |
116 | ns = task_nsproxy(task); | 116 | task_lock(task); |
117 | ns = task->nsproxy; | ||
117 | if (ns != NULL) | 118 | if (ns != NULL) |
118 | net = get_net(ns->net_ns); | 119 | net = get_net(ns->net_ns); |
120 | task_unlock(task); | ||
119 | } | 121 | } |
120 | rcu_read_unlock(); | 122 | rcu_read_unlock(); |
121 | 123 | ||
@@ -224,7 +226,7 @@ static struct pernet_operations __net_initdata proc_net_ns_ops = { | |||
224 | 226 | ||
225 | int __init proc_net_init(void) | 227 | int __init proc_net_init(void) |
226 | { | 228 | { |
227 | proc_symlink("net", NULL, "self/net"); | 229 | proc_symlink("net", NULL, "thread-self/net"); |
228 | 230 | ||
229 | return register_pernet_subsys(&proc_net_ns_ops); | 231 | return register_pernet_subsys(&proc_net_ns_ops); |
230 | } | 232 | } |
diff --git a/fs/proc/root.c b/fs/proc/root.c index 574bafc41f0b..6296c7626963 100644 --- a/fs/proc/root.c +++ b/fs/proc/root.c | |||
@@ -149,6 +149,8 @@ static void proc_kill_sb(struct super_block *sb) | |||
149 | ns = (struct pid_namespace *)sb->s_fs_info; | 149 | ns = (struct pid_namespace *)sb->s_fs_info; |
150 | if (ns->proc_self) | 150 | if (ns->proc_self) |
151 | dput(ns->proc_self); | 151 | dput(ns->proc_self); |
152 | if (ns->proc_thread_self) | ||
153 | dput(ns->proc_thread_self); | ||
152 | kill_anon_super(sb); | 154 | kill_anon_super(sb); |
153 | put_pid_ns(ns); | 155 | put_pid_ns(ns); |
154 | } | 156 | } |
@@ -170,7 +172,8 @@ void __init proc_root_init(void) | |||
170 | return; | 172 | return; |
171 | 173 | ||
172 | proc_self_init(); | 174 | proc_self_init(); |
173 | proc_symlink("mounts", NULL, "self/mounts"); | 175 | proc_thread_self_init(); |
176 | proc_symlink("mounts", NULL, "thread-self/mounts"); | ||
174 | 177 | ||
175 | proc_net_init(); | 178 | proc_net_init(); |
176 | 179 | ||
diff --git a/fs/proc/thread_self.c b/fs/proc/thread_self.c new file mode 100644 index 000000000000..59075b509df3 --- /dev/null +++ b/fs/proc/thread_self.c | |||
@@ -0,0 +1,85 @@ | |||
1 | #include <linux/sched.h> | ||
2 | #include <linux/namei.h> | ||
3 | #include <linux/slab.h> | ||
4 | #include <linux/pid_namespace.h> | ||
5 | #include "internal.h" | ||
6 | |||
7 | /* | ||
8 | * /proc/thread_self: | ||
9 | */ | ||
10 | static int proc_thread_self_readlink(struct dentry *dentry, char __user *buffer, | ||
11 | int buflen) | ||
12 | { | ||
13 | struct pid_namespace *ns = dentry->d_sb->s_fs_info; | ||
14 | pid_t tgid = task_tgid_nr_ns(current, ns); | ||
15 | pid_t pid = task_pid_nr_ns(current, ns); | ||
16 | char tmp[PROC_NUMBUF + 6 + PROC_NUMBUF]; | ||
17 | if (!pid) | ||
18 | return -ENOENT; | ||
19 | sprintf(tmp, "%d/task/%d", tgid, pid); | ||
20 | return readlink_copy(buffer, buflen, tmp); | ||
21 | } | ||
22 | |||
23 | static void *proc_thread_self_follow_link(struct dentry *dentry, struct nameidata *nd) | ||
24 | { | ||
25 | struct pid_namespace *ns = dentry->d_sb->s_fs_info; | ||
26 | pid_t tgid = task_tgid_nr_ns(current, ns); | ||
27 | pid_t pid = task_pid_nr_ns(current, ns); | ||
28 | char *name = ERR_PTR(-ENOENT); | ||
29 | if (pid) { | ||
30 | name = kmalloc(PROC_NUMBUF + 6 + PROC_NUMBUF, GFP_KERNEL); | ||
31 | if (!name) | ||
32 | name = ERR_PTR(-ENOMEM); | ||
33 | else | ||
34 | sprintf(name, "%d/task/%d", tgid, pid); | ||
35 | } | ||
36 | nd_set_link(nd, name); | ||
37 | return NULL; | ||
38 | } | ||
39 | |||
40 | static const struct inode_operations proc_thread_self_inode_operations = { | ||
41 | .readlink = proc_thread_self_readlink, | ||
42 | .follow_link = proc_thread_self_follow_link, | ||
43 | .put_link = kfree_put_link, | ||
44 | }; | ||
45 | |||
46 | static unsigned thread_self_inum; | ||
47 | |||
48 | int proc_setup_thread_self(struct super_block *s) | ||
49 | { | ||
50 | struct inode *root_inode = s->s_root->d_inode; | ||
51 | struct pid_namespace *ns = s->s_fs_info; | ||
52 | struct dentry *thread_self; | ||
53 | |||
54 | mutex_lock(&root_inode->i_mutex); | ||
55 | thread_self = d_alloc_name(s->s_root, "thread-self"); | ||
56 | if (thread_self) { | ||
57 | struct inode *inode = new_inode_pseudo(s); | ||
58 | if (inode) { | ||
59 | inode->i_ino = thread_self_inum; | ||
60 | inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; | ||
61 | inode->i_mode = S_IFLNK | S_IRWXUGO; | ||
62 | inode->i_uid = GLOBAL_ROOT_UID; | ||
63 | inode->i_gid = GLOBAL_ROOT_GID; | ||
64 | inode->i_op = &proc_thread_self_inode_operations; | ||
65 | d_add(thread_self, inode); | ||
66 | } else { | ||
67 | dput(thread_self); | ||
68 | thread_self = ERR_PTR(-ENOMEM); | ||
69 | } | ||
70 | } else { | ||
71 | thread_self = ERR_PTR(-ENOMEM); | ||
72 | } | ||
73 | mutex_unlock(&root_inode->i_mutex); | ||
74 | if (IS_ERR(thread_self)) { | ||
75 | pr_err("proc_fill_super: can't allocate /proc/thread_self\n"); | ||
76 | return PTR_ERR(thread_self); | ||
77 | } | ||
78 | ns->proc_thread_self = thread_self; | ||
79 | return 0; | ||
80 | } | ||
81 | |||
82 | void __init proc_thread_self_init(void) | ||
83 | { | ||
84 | proc_alloc_inum(&thread_self_inum); | ||
85 | } | ||
diff --git a/fs/proc_namespace.c b/fs/proc_namespace.c index 1a81373947f3..73ca1740d839 100644 --- a/fs/proc_namespace.c +++ b/fs/proc_namespace.c | |||
@@ -232,17 +232,15 @@ static int mounts_open_common(struct inode *inode, struct file *file, | |||
232 | if (!task) | 232 | if (!task) |
233 | goto err; | 233 | goto err; |
234 | 234 | ||
235 | rcu_read_lock(); | 235 | task_lock(task); |
236 | nsp = task_nsproxy(task); | 236 | nsp = task->nsproxy; |
237 | if (!nsp || !nsp->mnt_ns) { | 237 | if (!nsp || !nsp->mnt_ns) { |
238 | rcu_read_unlock(); | 238 | task_unlock(task); |
239 | put_task_struct(task); | 239 | put_task_struct(task); |
240 | goto err; | 240 | goto err; |
241 | } | 241 | } |
242 | ns = nsp->mnt_ns; | 242 | ns = nsp->mnt_ns; |
243 | get_mnt_ns(ns); | 243 | get_mnt_ns(ns); |
244 | rcu_read_unlock(); | ||
245 | task_lock(task); | ||
246 | if (!task->fs) { | 244 | if (!task->fs) { |
247 | task_unlock(task); | 245 | task_unlock(task); |
248 | put_task_struct(task); | 246 | put_task_struct(task); |