diff options
author | J. Bruce Fields <bfields@redhat.com> | 2019-04-09 15:56:57 -0400 |
---|---|---|
committer | J. Bruce Fields <bfields@redhat.com> | 2019-07-03 17:52:50 -0400 |
commit | 97ad4031e29521894fc28765f14247e79b0ef263 (patch) | |
tree | daec545c5697807abc3b7182f15b2c8086fd71a2 | |
parent | bf5ed3e3bb84c39d70ad10b2f8e47ec62f4c63b1 (diff) |
nfsd4: add a client info file
Add a new nfsd/clients/#/info file with some basic information about
each NFSv4 client.
Signed-off-by: J. Bruce Fields <bfields@redhat.com>
-rw-r--r-- | fs/nfsd/nfs4state.c | 38 | ||||
-rw-r--r-- | fs/nfsd/nfsctl.c | 114 | ||||
-rw-r--r-- | fs/nfsd/nfsd.h | 4 |
3 files changed, 148 insertions, 8 deletions
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 8f9747d84525..a6c722dc7e5e 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c | |||
@@ -2214,6 +2214,41 @@ find_stateid_by_type(struct nfs4_client *cl, stateid_t *t, char typemask) | |||
2214 | return s; | 2214 | return s; |
2215 | } | 2215 | } |
2216 | 2216 | ||
2217 | static int client_info_show(struct seq_file *m, void *v) | ||
2218 | { | ||
2219 | struct inode *inode = m->private; | ||
2220 | struct nfsdfs_client *nc; | ||
2221 | struct nfs4_client *clp; | ||
2222 | u64 clid; | ||
2223 | |||
2224 | nc = get_nfsdfs_client(inode); | ||
2225 | if (!nc) | ||
2226 | return -ENXIO; | ||
2227 | clp = container_of(nc, struct nfs4_client, cl_nfsdfs); | ||
2228 | memcpy(&clid, &clp->cl_clientid, sizeof(clid)); | ||
2229 | seq_printf(m, "clientid: 0x%llx\n", clid); | ||
2230 | drop_client(clp); | ||
2231 | |||
2232 | return 0; | ||
2233 | } | ||
2234 | |||
2235 | static int client_info_open(struct inode *inode, struct file *file) | ||
2236 | { | ||
2237 | return single_open(file, client_info_show, inode); | ||
2238 | } | ||
2239 | |||
2240 | static const struct file_operations client_info_fops = { | ||
2241 | .open = client_info_open, | ||
2242 | .read = seq_read, | ||
2243 | .llseek = seq_lseek, | ||
2244 | .release = single_release, | ||
2245 | }; | ||
2246 | |||
2247 | static const struct tree_descr client_files[] = { | ||
2248 | [0] = {"info", &client_info_fops, S_IRUSR}, | ||
2249 | [1] = {""}, | ||
2250 | }; | ||
2251 | |||
2217 | static struct nfs4_client *create_client(struct xdr_netobj name, | 2252 | static struct nfs4_client *create_client(struct xdr_netobj name, |
2218 | struct svc_rqst *rqstp, nfs4_verifier *verf) | 2253 | struct svc_rqst *rqstp, nfs4_verifier *verf) |
2219 | { | 2254 | { |
@@ -2242,7 +2277,8 @@ static struct nfs4_client *create_client(struct xdr_netobj name, | |||
2242 | clp->cl_cb_session = NULL; | 2277 | clp->cl_cb_session = NULL; |
2243 | clp->net = net; | 2278 | clp->net = net; |
2244 | clp->cl_nfsd_dentry = nfsd_client_mkdir(nn, &clp->cl_nfsdfs, | 2279 | clp->cl_nfsd_dentry = nfsd_client_mkdir(nn, &clp->cl_nfsdfs, |
2245 | clp->cl_clientid.cl_id - nn->clientid_base); | 2280 | clp->cl_clientid.cl_id - nn->clientid_base, |
2281 | client_files); | ||
2246 | if (!clp->cl_nfsd_dentry) { | 2282 | if (!clp->cl_nfsd_dentry) { |
2247 | free_client(clp); | 2283 | free_client(clp); |
2248 | return NULL; | 2284 | return NULL; |
diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index 599d600f0658..4683ba5c69c7 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c | |||
@@ -1208,14 +1208,116 @@ out_err: | |||
1208 | goto out; | 1208 | goto out; |
1209 | } | 1209 | } |
1210 | 1210 | ||
1211 | static void clear_ncl(struct inode *inode) | ||
1212 | { | ||
1213 | struct nfsdfs_client *ncl = inode->i_private; | ||
1214 | |||
1215 | inode->i_private = NULL; | ||
1216 | synchronize_rcu(); | ||
1217 | kref_put(&ncl->cl_ref, ncl->cl_release); | ||
1218 | } | ||
1219 | |||
1220 | |||
1221 | struct nfsdfs_client *__get_nfsdfs_client(struct inode *inode) | ||
1222 | { | ||
1223 | struct nfsdfs_client *nc = inode->i_private; | ||
1224 | |||
1225 | if (nc) | ||
1226 | kref_get(&nc->cl_ref); | ||
1227 | return nc; | ||
1228 | } | ||
1229 | |||
1230 | struct nfsdfs_client *get_nfsdfs_client(struct inode *inode) | ||
1231 | { | ||
1232 | struct nfsdfs_client *nc; | ||
1233 | |||
1234 | rcu_read_lock(); | ||
1235 | nc = __get_nfsdfs_client(inode); | ||
1236 | rcu_read_unlock(); | ||
1237 | return nc; | ||
1238 | } | ||
1239 | /* from __rpc_unlink */ | ||
1240 | static void nfsdfs_remove_file(struct inode *dir, struct dentry *dentry) | ||
1241 | { | ||
1242 | int ret; | ||
1243 | |||
1244 | clear_ncl(d_inode(dentry)); | ||
1245 | dget(dentry); | ||
1246 | ret = simple_unlink(dir, dentry); | ||
1247 | d_delete(dentry); | ||
1248 | dput(dentry); | ||
1249 | WARN_ON_ONCE(ret); | ||
1250 | } | ||
1251 | |||
1252 | static void nfsdfs_remove_files(struct dentry *root) | ||
1253 | { | ||
1254 | struct dentry *dentry, *tmp; | ||
1255 | |||
1256 | list_for_each_entry_safe(dentry, tmp, &root->d_subdirs, d_child) { | ||
1257 | if (!simple_positive(dentry)) { | ||
1258 | WARN_ON_ONCE(1); /* I think this can't happen? */ | ||
1259 | continue; | ||
1260 | } | ||
1261 | nfsdfs_remove_file(d_inode(root), dentry); | ||
1262 | } | ||
1263 | } | ||
1264 | |||
1265 | /* XXX: cut'n'paste from simple_fill_super; figure out if we could share | ||
1266 | * code instead. */ | ||
1267 | static int nfsdfs_create_files(struct dentry *root, | ||
1268 | const struct tree_descr *files) | ||
1269 | { | ||
1270 | struct inode *dir = d_inode(root); | ||
1271 | struct inode *inode; | ||
1272 | struct dentry *dentry; | ||
1273 | int i; | ||
1274 | |||
1275 | inode_lock(dir); | ||
1276 | for (i = 0; files->name && files->name[0]; i++, files++) { | ||
1277 | if (!files->name) | ||
1278 | continue; | ||
1279 | dentry = d_alloc_name(root, files->name); | ||
1280 | if (!dentry) | ||
1281 | goto out; | ||
1282 | inode = nfsd_get_inode(d_inode(root)->i_sb, | ||
1283 | S_IFREG | files->mode); | ||
1284 | if (!inode) { | ||
1285 | dput(dentry); | ||
1286 | goto out; | ||
1287 | } | ||
1288 | inode->i_fop = files->ops; | ||
1289 | inode->i_private = __get_nfsdfs_client(dir); | ||
1290 | d_add(dentry, inode); | ||
1291 | fsnotify_create(dir, dentry); | ||
1292 | } | ||
1293 | inode_unlock(dir); | ||
1294 | return 0; | ||
1295 | out: | ||
1296 | nfsdfs_remove_files(root); | ||
1297 | inode_unlock(dir); | ||
1298 | return -ENOMEM; | ||
1299 | } | ||
1300 | |||
1211 | /* on success, returns positive number unique to that client. */ | 1301 | /* on success, returns positive number unique to that client. */ |
1212 | struct dentry *nfsd_client_mkdir(struct nfsd_net *nn, struct nfsdfs_client *ncl, u32 id) | 1302 | struct dentry *nfsd_client_mkdir(struct nfsd_net *nn, |
1303 | struct nfsdfs_client *ncl, u32 id, | ||
1304 | const struct tree_descr *files) | ||
1213 | { | 1305 | { |
1306 | struct dentry *dentry; | ||
1214 | char name[11]; | 1307 | char name[11]; |
1308 | int ret; | ||
1215 | 1309 | ||
1216 | sprintf(name, "%u", id); | 1310 | sprintf(name, "%u", id); |
1217 | 1311 | ||
1218 | return nfsd_mkdir(nn->nfsd_client_dir, ncl, name); | 1312 | dentry = nfsd_mkdir(nn->nfsd_client_dir, ncl, name); |
1313 | if (IS_ERR(dentry)) /* XXX: tossing errors? */ | ||
1314 | return NULL; | ||
1315 | ret = nfsdfs_create_files(dentry, files); | ||
1316 | if (ret) { | ||
1317 | nfsd_client_rmdir(dentry); | ||
1318 | return NULL; | ||
1319 | } | ||
1320 | return dentry; | ||
1219 | } | 1321 | } |
1220 | 1322 | ||
1221 | /* Taken from __rpc_rmdir: */ | 1323 | /* Taken from __rpc_rmdir: */ |
@@ -1223,16 +1325,16 @@ void nfsd_client_rmdir(struct dentry *dentry) | |||
1223 | { | 1325 | { |
1224 | struct inode *dir = d_inode(dentry->d_parent); | 1326 | struct inode *dir = d_inode(dentry->d_parent); |
1225 | struct inode *inode = d_inode(dentry); | 1327 | struct inode *inode = d_inode(dentry); |
1226 | struct nfsdfs_client *ncl = inode->i_private; | ||
1227 | int ret; | 1328 | int ret; |
1228 | 1329 | ||
1229 | inode->i_private = NULL; | 1330 | inode_lock(dir); |
1230 | synchronize_rcu(); | 1331 | nfsdfs_remove_files(dentry); |
1231 | kref_put(&ncl->cl_ref, ncl->cl_release); | 1332 | clear_ncl(inode); |
1232 | dget(dentry); | 1333 | dget(dentry); |
1233 | ret = simple_rmdir(dir, dentry); | 1334 | ret = simple_rmdir(dir, dentry); |
1234 | WARN_ON_ONCE(ret); | 1335 | WARN_ON_ONCE(ret); |
1235 | d_delete(dentry); | 1336 | d_delete(dentry); |
1337 | inode_unlock(dir); | ||
1236 | } | 1338 | } |
1237 | 1339 | ||
1238 | static int nfsd_fill_super(struct super_block * sb, void * data, int silent) | 1340 | static int nfsd_fill_super(struct super_block * sb, void * data, int silent) |
diff --git a/fs/nfsd/nfsd.h b/fs/nfsd/nfsd.h index 85525dcbf77d..af2947551e9c 100644 --- a/fs/nfsd/nfsd.h +++ b/fs/nfsd/nfsd.h | |||
@@ -92,7 +92,9 @@ struct nfsdfs_client { | |||
92 | void (*cl_release)(struct kref *kref); | 92 | void (*cl_release)(struct kref *kref); |
93 | }; | 93 | }; |
94 | 94 | ||
95 | struct dentry *nfsd_client_mkdir(struct nfsd_net *nn, struct nfsdfs_client *ncl, u32 id); | 95 | struct nfsdfs_client *get_nfsdfs_client(struct inode *); |
96 | struct dentry *nfsd_client_mkdir(struct nfsd_net *nn, | ||
97 | struct nfsdfs_client *ncl, u32 id, const struct tree_descr *); | ||
96 | void nfsd_client_rmdir(struct dentry *dentry); | 98 | void nfsd_client_rmdir(struct dentry *dentry); |
97 | 99 | ||
98 | #if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL) | 100 | #if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL) |