diff options
author | Eric W. Biederman <ebiederm@xmission.com> | 2014-07-31 07:35:20 -0400 |
---|---|---|
committer | Eric W. Biederman <ebiederm@xmission.com> | 2014-08-04 12:28:32 -0400 |
commit | 65b38851a17472d31fec9019fc3a55b0802dab88 (patch) | |
tree | 4b5830300b5cbd722f5b1680b96aaa5e72c0d638 /fs/nfs/client.c | |
parent | db181ce011e3c033328608299cd6fac06ea50130 (diff) |
NFS: Fix /proc/fs/nfsfs/servers and /proc/fs/nfsfs/volumes
The usage of pid_ns->child_reaper->nsproxy->net_ns in
nfs_server_list_open and nfs_client_list_open is not safe.
/proc for a pid namespace can remain mounted after the all of the
process in that pid namespace have exited. There are also times
before the initial process in a pid namespace has started or after the
initial process in a pid namespace has exited where
pid_ns->child_reaper can be NULL or stale. Making the idiom
pid_ns->child_reaper->nsproxy a double whammy of problems.
Luckily all that needs to happen is to move /proc/fs/nfsfs/servers and
/proc/fs/nfsfs/volumes under /proc/net to /proc/net/nfsfs/servers and
/proc/net/nfsfs/volumes and add a symlink from the original location,
and to use seq_open_net as it has been designed.
Cc: stable@vger.kernel.org
Cc: Trond Myklebust <trond.myklebust@primarydata.com>
Cc: Stanislav Kinsbursky <skinsbursky@parallels.com>
Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com>
Diffstat (limited to 'fs/nfs/client.c')
-rw-r--r-- | fs/nfs/client.c | 95 |
1 files changed, 55 insertions, 40 deletions
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; |