1  // SPDX-License-Identifier: GPL-2.0-only
2  /*
3   * linux/fs/lockd/svcsubs.c
4   *
5   * Various support routines for the NLM server.
6   *
7   * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
8   */
9  
10  #include <linux/types.h>
11  #include <linux/string.h>
12  #include <linux/time.h>
13  #include <linux/in.h>
14  #include <linux/slab.h>
15  #include <linux/mutex.h>
16  #include <linux/sunrpc/svc.h>
17  #include <linux/sunrpc/addr.h>
18  #include <linux/lockd/lockd.h>
19  #include <linux/lockd/share.h>
20  #include <linux/module.h>
21  #include <linux/mount.h>
22  #include <uapi/linux/nfs2.h>
23  
24  #define NLMDBG_FACILITY		NLMDBG_SVCSUBS
25  
26  
27  /*
28   * Global file hash table
29   */
30  #define FILE_HASH_BITS		7
31  #define FILE_NRHASH		(1<<FILE_HASH_BITS)
32  static struct hlist_head	nlm_files[FILE_NRHASH];
33  static DEFINE_MUTEX(nlm_file_mutex);
34  
35  #ifdef CONFIG_SUNRPC_DEBUG
nlm_debug_print_fh(char * msg,struct nfs_fh * f)36  static inline void nlm_debug_print_fh(char *msg, struct nfs_fh *f)
37  {
38  	u32 *fhp = (u32*)f->data;
39  
40  	/* print the first 32 bytes of the fh */
41  	dprintk("lockd: %s (%08x %08x %08x %08x %08x %08x %08x %08x)\n",
42  		msg, fhp[0], fhp[1], fhp[2], fhp[3],
43  		fhp[4], fhp[5], fhp[6], fhp[7]);
44  }
45  
nlm_debug_print_file(char * msg,struct nlm_file * file)46  static inline void nlm_debug_print_file(char *msg, struct nlm_file *file)
47  {
48  	struct inode *inode = nlmsvc_file_inode(file);
49  
50  	dprintk("lockd: %s %s/%ld\n",
51  		msg, inode->i_sb->s_id, inode->i_ino);
52  }
53  #else
nlm_debug_print_fh(char * msg,struct nfs_fh * f)54  static inline void nlm_debug_print_fh(char *msg, struct nfs_fh *f)
55  {
56  	return;
57  }
58  
nlm_debug_print_file(char * msg,struct nlm_file * file)59  static inline void nlm_debug_print_file(char *msg, struct nlm_file *file)
60  {
61  	return;
62  }
63  #endif
64  
file_hash(struct nfs_fh * f)65  static inline unsigned int file_hash(struct nfs_fh *f)
66  {
67  	unsigned int tmp=0;
68  	int i;
69  	for (i=0; i<NFS2_FHSIZE;i++)
70  		tmp += f->data[i];
71  	return tmp & (FILE_NRHASH - 1);
72  }
73  
lock_to_openmode(struct file_lock * lock)74  int lock_to_openmode(struct file_lock *lock)
75  {
76  	return lock_is_write(lock) ? O_WRONLY : O_RDONLY;
77  }
78  
79  /*
80   * Open the file. Note that if we're reexporting, for example,
81   * this could block the lockd thread for a while.
82   *
83   * We have to make sure we have the right credential to open
84   * the file.
85   */
nlm_do_fopen(struct svc_rqst * rqstp,struct nlm_file * file,int mode)86  static __be32 nlm_do_fopen(struct svc_rqst *rqstp,
87  			   struct nlm_file *file, int mode)
88  {
89  	struct file **fp = &file->f_file[mode];
90  	__be32	nfserr;
91  
92  	if (*fp)
93  		return 0;
94  	nfserr = nlmsvc_ops->fopen(rqstp, &file->f_handle, fp, mode);
95  	if (nfserr)
96  		dprintk("lockd: open failed (error %d)\n", nfserr);
97  	return nfserr;
98  }
99  
100  /*
101   * Lookup file info. If it doesn't exist, create a file info struct
102   * and open a (VFS) file for the given inode.
103   */
104  __be32
nlm_lookup_file(struct svc_rqst * rqstp,struct nlm_file ** result,struct nlm_lock * lock)105  nlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result,
106  					struct nlm_lock *lock)
107  {
108  	struct nlm_file	*file;
109  	unsigned int	hash;
110  	__be32		nfserr;
111  	int		mode;
112  
113  	nlm_debug_print_fh("nlm_lookup_file", &lock->fh);
114  
115  	hash = file_hash(&lock->fh);
116  	mode = lock_to_openmode(&lock->fl);
117  
118  	/* Lock file table */
119  	mutex_lock(&nlm_file_mutex);
120  
121  	hlist_for_each_entry(file, &nlm_files[hash], f_list)
122  		if (!nfs_compare_fh(&file->f_handle, &lock->fh)) {
123  			mutex_lock(&file->f_mutex);
124  			nfserr = nlm_do_fopen(rqstp, file, mode);
125  			mutex_unlock(&file->f_mutex);
126  			goto found;
127  		}
128  	nlm_debug_print_fh("creating file for", &lock->fh);
129  
130  	nfserr = nlm_lck_denied_nolocks;
131  	file = kzalloc(sizeof(*file), GFP_KERNEL);
132  	if (!file)
133  		goto out_free;
134  
135  	memcpy(&file->f_handle, &lock->fh, sizeof(struct nfs_fh));
136  	mutex_init(&file->f_mutex);
137  	INIT_HLIST_NODE(&file->f_list);
138  	INIT_LIST_HEAD(&file->f_blocks);
139  
140  	nfserr = nlm_do_fopen(rqstp, file, mode);
141  	if (nfserr)
142  		goto out_unlock;
143  
144  	hlist_add_head(&file->f_list, &nlm_files[hash]);
145  
146  found:
147  	dprintk("lockd: found file %p (count %d)\n", file, file->f_count);
148  	*result = file;
149  	file->f_count++;
150  
151  out_unlock:
152  	mutex_unlock(&nlm_file_mutex);
153  	return nfserr;
154  
155  out_free:
156  	kfree(file);
157  	goto out_unlock;
158  }
159  
160  /*
161   * Delete a file after having released all locks, blocks and shares
162   */
163  static inline void
nlm_delete_file(struct nlm_file * file)164  nlm_delete_file(struct nlm_file *file)
165  {
166  	nlm_debug_print_file("closing file", file);
167  	if (!hlist_unhashed(&file->f_list)) {
168  		hlist_del(&file->f_list);
169  		if (file->f_file[O_RDONLY])
170  			nlmsvc_ops->fclose(file->f_file[O_RDONLY]);
171  		if (file->f_file[O_WRONLY])
172  			nlmsvc_ops->fclose(file->f_file[O_WRONLY]);
173  		kfree(file);
174  	} else {
175  		printk(KERN_WARNING "lockd: attempt to release unknown file!\n");
176  	}
177  }
178  
nlm_unlock_files(struct nlm_file * file,const struct file_lock * fl)179  static int nlm_unlock_files(struct nlm_file *file, const struct file_lock *fl)
180  {
181  	struct file_lock lock;
182  
183  	locks_init_lock(&lock);
184  	lock.c.flc_type  = F_UNLCK;
185  	lock.fl_start = 0;
186  	lock.fl_end   = OFFSET_MAX;
187  	lock.c.flc_owner = fl->c.flc_owner;
188  	lock.c.flc_pid   = fl->c.flc_pid;
189  	lock.c.flc_flags = FL_POSIX;
190  
191  	lock.c.flc_file = file->f_file[O_RDONLY];
192  	if (lock.c.flc_file && vfs_lock_file(lock.c.flc_file, F_SETLK, &lock, NULL))
193  		goto out_err;
194  	lock.c.flc_file = file->f_file[O_WRONLY];
195  	if (lock.c.flc_file && vfs_lock_file(lock.c.flc_file, F_SETLK, &lock, NULL))
196  		goto out_err;
197  	return 0;
198  out_err:
199  	pr_warn("lockd: unlock failure in %s:%d\n", __FILE__, __LINE__);
200  	return 1;
201  }
202  
203  /*
204   * Loop over all locks on the given file and perform the specified
205   * action.
206   */
207  static int
nlm_traverse_locks(struct nlm_host * host,struct nlm_file * file,nlm_host_match_fn_t match)208  nlm_traverse_locks(struct nlm_host *host, struct nlm_file *file,
209  			nlm_host_match_fn_t match)
210  {
211  	struct inode	 *inode = nlmsvc_file_inode(file);
212  	struct file_lock *fl;
213  	struct file_lock_context *flctx = locks_inode_context(inode);
214  	struct nlm_host	 *lockhost;
215  
216  	if (!flctx || list_empty_careful(&flctx->flc_posix))
217  		return 0;
218  again:
219  	file->f_locks = 0;
220  	spin_lock(&flctx->flc_lock);
221  	for_each_file_lock(fl, &flctx->flc_posix) {
222  		if (fl->fl_lmops != &nlmsvc_lock_operations)
223  			continue;
224  
225  		/* update current lock count */
226  		file->f_locks++;
227  
228  		lockhost = ((struct nlm_lockowner *) fl->c.flc_owner)->host;
229  		if (match(lockhost, host)) {
230  
231  			spin_unlock(&flctx->flc_lock);
232  			if (nlm_unlock_files(file, fl))
233  				return 1;
234  			goto again;
235  		}
236  	}
237  	spin_unlock(&flctx->flc_lock);
238  
239  	return 0;
240  }
241  
242  static int
nlmsvc_always_match(void * dummy1,struct nlm_host * dummy2)243  nlmsvc_always_match(void *dummy1, struct nlm_host *dummy2)
244  {
245  	return 1;
246  }
247  
248  /*
249   * Inspect a single file
250   */
251  static inline int
nlm_inspect_file(struct nlm_host * host,struct nlm_file * file,nlm_host_match_fn_t match)252  nlm_inspect_file(struct nlm_host *host, struct nlm_file *file, nlm_host_match_fn_t match)
253  {
254  	nlmsvc_traverse_blocks(host, file, match);
255  	nlmsvc_traverse_shares(host, file, match);
256  	return nlm_traverse_locks(host, file, match);
257  }
258  
259  /*
260   * Quick check whether there are still any locks, blocks or
261   * shares on a given file.
262   */
263  static inline int
nlm_file_inuse(struct nlm_file * file)264  nlm_file_inuse(struct nlm_file *file)
265  {
266  	struct inode	 *inode = nlmsvc_file_inode(file);
267  	struct file_lock *fl;
268  	struct file_lock_context *flctx = locks_inode_context(inode);
269  
270  	if (file->f_count || !list_empty(&file->f_blocks) || file->f_shares)
271  		return 1;
272  
273  	if (flctx && !list_empty_careful(&flctx->flc_posix)) {
274  		spin_lock(&flctx->flc_lock);
275  		for_each_file_lock(fl, &flctx->flc_posix) {
276  			if (fl->fl_lmops == &nlmsvc_lock_operations) {
277  				spin_unlock(&flctx->flc_lock);
278  				return 1;
279  			}
280  		}
281  		spin_unlock(&flctx->flc_lock);
282  	}
283  	file->f_locks = 0;
284  	return 0;
285  }
286  
nlm_close_files(struct nlm_file * file)287  static void nlm_close_files(struct nlm_file *file)
288  {
289  	if (file->f_file[O_RDONLY])
290  		nlmsvc_ops->fclose(file->f_file[O_RDONLY]);
291  	if (file->f_file[O_WRONLY])
292  		nlmsvc_ops->fclose(file->f_file[O_WRONLY]);
293  }
294  
295  /*
296   * Loop over all files in the file table.
297   */
298  static int
nlm_traverse_files(void * data,nlm_host_match_fn_t match,int (* is_failover_file)(void * data,struct nlm_file * file))299  nlm_traverse_files(void *data, nlm_host_match_fn_t match,
300  		int (*is_failover_file)(void *data, struct nlm_file *file))
301  {
302  	struct hlist_node *next;
303  	struct nlm_file	*file;
304  	int i, ret = 0;
305  
306  	mutex_lock(&nlm_file_mutex);
307  	for (i = 0; i < FILE_NRHASH; i++) {
308  		hlist_for_each_entry_safe(file, next, &nlm_files[i], f_list) {
309  			if (is_failover_file && !is_failover_file(data, file))
310  				continue;
311  			file->f_count++;
312  			mutex_unlock(&nlm_file_mutex);
313  
314  			/* Traverse locks, blocks and shares of this file
315  			 * and update file->f_locks count */
316  			if (nlm_inspect_file(data, file, match))
317  				ret = 1;
318  
319  			mutex_lock(&nlm_file_mutex);
320  			file->f_count--;
321  			/* No more references to this file. Let go of it. */
322  			if (list_empty(&file->f_blocks) && !file->f_locks
323  			 && !file->f_shares && !file->f_count) {
324  				hlist_del(&file->f_list);
325  				nlm_close_files(file);
326  				kfree(file);
327  			}
328  		}
329  	}
330  	mutex_unlock(&nlm_file_mutex);
331  	return ret;
332  }
333  
334  /*
335   * Release file. If there are no more remote locks on this file,
336   * close it and free the handle.
337   *
338   * Note that we can't do proper reference counting without major
339   * contortions because the code in fs/locks.c creates, deletes and
340   * splits locks without notification. Our only way is to walk the
341   * entire lock list each time we remove a lock.
342   */
343  void
nlm_release_file(struct nlm_file * file)344  nlm_release_file(struct nlm_file *file)
345  {
346  	dprintk("lockd: nlm_release_file(%p, ct = %d)\n",
347  				file, file->f_count);
348  
349  	/* Lock file table */
350  	mutex_lock(&nlm_file_mutex);
351  
352  	/* If there are no more locks etc, delete the file */
353  	if (--file->f_count == 0 && !nlm_file_inuse(file))
354  		nlm_delete_file(file);
355  
356  	mutex_unlock(&nlm_file_mutex);
357  }
358  
359  /*
360   * Helpers function for resource traversal
361   *
362   * nlmsvc_mark_host:
363   *	used by the garbage collector; simply sets h_inuse only for those
364   *	hosts, which passed network check.
365   *	Always returns 0.
366   *
367   * nlmsvc_same_host:
368   *	returns 1 iff the two hosts match. Used to release
369   *	all resources bound to a specific host.
370   *
371   * nlmsvc_is_client:
372   *	returns 1 iff the host is a client.
373   *	Used by nlmsvc_invalidate_all
374   */
375  
376  static int
nlmsvc_mark_host(void * data,struct nlm_host * hint)377  nlmsvc_mark_host(void *data, struct nlm_host *hint)
378  {
379  	struct nlm_host *host = data;
380  
381  	if ((hint->net == NULL) ||
382  	    (host->net == hint->net))
383  		host->h_inuse = 1;
384  	return 0;
385  }
386  
387  static int
nlmsvc_same_host(void * data,struct nlm_host * other)388  nlmsvc_same_host(void *data, struct nlm_host *other)
389  {
390  	struct nlm_host *host = data;
391  
392  	return host == other;
393  }
394  
395  static int
nlmsvc_is_client(void * data,struct nlm_host * dummy)396  nlmsvc_is_client(void *data, struct nlm_host *dummy)
397  {
398  	struct nlm_host *host = data;
399  
400  	if (host->h_server) {
401  		/* we are destroying locks even though the client
402  		 * hasn't asked us too, so don't unmonitor the
403  		 * client
404  		 */
405  		if (host->h_nsmhandle)
406  			host->h_nsmhandle->sm_sticky = 1;
407  		return 1;
408  	} else
409  		return 0;
410  }
411  
412  /*
413   * Mark all hosts that still hold resources
414   */
415  void
nlmsvc_mark_resources(struct net * net)416  nlmsvc_mark_resources(struct net *net)
417  {
418  	struct nlm_host hint;
419  
420  	dprintk("lockd: %s for net %x\n", __func__, net ? net->ns.inum : 0);
421  	hint.net = net;
422  	nlm_traverse_files(&hint, nlmsvc_mark_host, NULL);
423  }
424  
425  /*
426   * Release all resources held by the given client
427   */
428  void
nlmsvc_free_host_resources(struct nlm_host * host)429  nlmsvc_free_host_resources(struct nlm_host *host)
430  {
431  	dprintk("lockd: nlmsvc_free_host_resources\n");
432  
433  	if (nlm_traverse_files(host, nlmsvc_same_host, NULL)) {
434  		printk(KERN_WARNING
435  			"lockd: couldn't remove all locks held by %s\n",
436  			host->h_name);
437  		BUG();
438  	}
439  }
440  
441  /**
442   * nlmsvc_invalidate_all - remove all locks held for clients
443   *
444   * Release all locks held by NFS clients.
445   *
446   */
447  void
nlmsvc_invalidate_all(void)448  nlmsvc_invalidate_all(void)
449  {
450  	/*
451  	 * Previously, the code would call
452  	 * nlmsvc_free_host_resources for each client in
453  	 * turn, which is about as inefficient as it gets.
454  	 * Now we just do it once in nlm_traverse_files.
455  	 */
456  	nlm_traverse_files(NULL, nlmsvc_is_client, NULL);
457  }
458  
459  
460  static int
nlmsvc_match_sb(void * datap,struct nlm_file * file)461  nlmsvc_match_sb(void *datap, struct nlm_file *file)
462  {
463  	struct super_block *sb = datap;
464  
465  	return sb == nlmsvc_file_inode(file)->i_sb;
466  }
467  
468  /**
469   * nlmsvc_unlock_all_by_sb - release locks held on this file system
470   * @sb: super block
471   *
472   * Release all locks held by clients accessing this file system.
473   */
474  int
nlmsvc_unlock_all_by_sb(struct super_block * sb)475  nlmsvc_unlock_all_by_sb(struct super_block *sb)
476  {
477  	int ret;
478  
479  	ret = nlm_traverse_files(sb, nlmsvc_always_match, nlmsvc_match_sb);
480  	return ret ? -EIO : 0;
481  }
482  EXPORT_SYMBOL_GPL(nlmsvc_unlock_all_by_sb);
483  
484  static int
nlmsvc_match_ip(void * datap,struct nlm_host * host)485  nlmsvc_match_ip(void *datap, struct nlm_host *host)
486  {
487  	return rpc_cmp_addr(nlm_srcaddr(host), datap);
488  }
489  
490  /**
491   * nlmsvc_unlock_all_by_ip - release local locks by IP address
492   * @server_addr: server's IP address as seen by clients
493   *
494   * Release all locks held by clients accessing this host
495   * via the passed in IP address.
496   */
497  int
nlmsvc_unlock_all_by_ip(struct sockaddr * server_addr)498  nlmsvc_unlock_all_by_ip(struct sockaddr *server_addr)
499  {
500  	int ret;
501  
502  	ret = nlm_traverse_files(server_addr, nlmsvc_match_ip, NULL);
503  	return ret ? -EIO : 0;
504  }
505  EXPORT_SYMBOL_GPL(nlmsvc_unlock_all_by_ip);
506