root/fs/lockd/svcsubs.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. nlm_debug_print_fh
  2. nlm_debug_print_file
  3. nlm_debug_print_fh
  4. nlm_debug_print_file
  5. file_hash
  6. nlm_lookup_file
  7. nlm_delete_file
  8. nlm_traverse_locks
  9. nlmsvc_always_match
  10. nlm_inspect_file
  11. nlm_file_inuse
  12. nlm_traverse_files
  13. nlm_release_file
  14. nlmsvc_mark_host
  15. nlmsvc_same_host
  16. nlmsvc_is_client
  17. nlmsvc_mark_resources
  18. nlmsvc_free_host_resources
  19. nlmsvc_invalidate_all
  20. nlmsvc_match_sb
  21. nlmsvc_unlock_all_by_sb
  22. nlmsvc_match_ip
  23. nlmsvc_unlock_all_by_ip

   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
  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 
  46 static inline void nlm_debug_print_file(char *msg, struct nlm_file *file)
  47 {
  48         struct inode *inode = locks_inode(file->f_file);
  49 
  50         dprintk("lockd: %s %s/%ld\n",
  51                 msg, inode->i_sb->s_id, inode->i_ino);
  52 }
  53 #else
  54 static inline void nlm_debug_print_fh(char *msg, struct nfs_fh *f)
  55 {
  56         return;
  57 }
  58 
  59 static inline void nlm_debug_print_file(char *msg, struct nlm_file *file)
  60 {
  61         return;
  62 }
  63 #endif
  64 
  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 
  74 /*
  75  * Lookup file info. If it doesn't exist, create a file info struct
  76  * and open a (VFS) file for the given inode.
  77  *
  78  * FIXME:
  79  * Note that we open the file O_RDONLY even when creating write locks.
  80  * This is not quite right, but for now, we assume the client performs
  81  * the proper R/W checking.
  82  */
  83 __be32
  84 nlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result,
  85                                         struct nfs_fh *f)
  86 {
  87         struct nlm_file *file;
  88         unsigned int    hash;
  89         __be32          nfserr;
  90 
  91         nlm_debug_print_fh("nlm_lookup_file", f);
  92 
  93         hash = file_hash(f);
  94 
  95         /* Lock file table */
  96         mutex_lock(&nlm_file_mutex);
  97 
  98         hlist_for_each_entry(file, &nlm_files[hash], f_list)
  99                 if (!nfs_compare_fh(&file->f_handle, f))
 100                         goto found;
 101 
 102         nlm_debug_print_fh("creating file for", f);
 103 
 104         nfserr = nlm_lck_denied_nolocks;
 105         file = kzalloc(sizeof(*file), GFP_KERNEL);
 106         if (!file)
 107                 goto out_unlock;
 108 
 109         memcpy(&file->f_handle, f, sizeof(struct nfs_fh));
 110         mutex_init(&file->f_mutex);
 111         INIT_HLIST_NODE(&file->f_list);
 112         INIT_LIST_HEAD(&file->f_blocks);
 113 
 114         /* Open the file. Note that this must not sleep for too long, else
 115          * we would lock up lockd:-) So no NFS re-exports, folks.
 116          *
 117          * We have to make sure we have the right credential to open
 118          * the file.
 119          */
 120         if ((nfserr = nlmsvc_ops->fopen(rqstp, f, &file->f_file)) != 0) {
 121                 dprintk("lockd: open failed (error %d)\n", nfserr);
 122                 goto out_free;
 123         }
 124 
 125         hlist_add_head(&file->f_list, &nlm_files[hash]);
 126 
 127 found:
 128         dprintk("lockd: found file %p (count %d)\n", file, file->f_count);
 129         *result = file;
 130         file->f_count++;
 131         nfserr = 0;
 132 
 133 out_unlock:
 134         mutex_unlock(&nlm_file_mutex);
 135         return nfserr;
 136 
 137 out_free:
 138         kfree(file);
 139         goto out_unlock;
 140 }
 141 
 142 /*
 143  * Delete a file after having released all locks, blocks and shares
 144  */
 145 static inline void
 146 nlm_delete_file(struct nlm_file *file)
 147 {
 148         nlm_debug_print_file("closing file", file);
 149         if (!hlist_unhashed(&file->f_list)) {
 150                 hlist_del(&file->f_list);
 151                 nlmsvc_ops->fclose(file->f_file);
 152                 kfree(file);
 153         } else {
 154                 printk(KERN_WARNING "lockd: attempt to release unknown file!\n");
 155         }
 156 }
 157 
 158 /*
 159  * Loop over all locks on the given file and perform the specified
 160  * action.
 161  */
 162 static int
 163 nlm_traverse_locks(struct nlm_host *host, struct nlm_file *file,
 164                         nlm_host_match_fn_t match)
 165 {
 166         struct inode     *inode = nlmsvc_file_inode(file);
 167         struct file_lock *fl;
 168         struct file_lock_context *flctx = inode->i_flctx;
 169         struct nlm_host  *lockhost;
 170 
 171         if (!flctx || list_empty_careful(&flctx->flc_posix))
 172                 return 0;
 173 again:
 174         file->f_locks = 0;
 175         spin_lock(&flctx->flc_lock);
 176         list_for_each_entry(fl, &flctx->flc_posix, fl_list) {
 177                 if (fl->fl_lmops != &nlmsvc_lock_operations)
 178                         continue;
 179 
 180                 /* update current lock count */
 181                 file->f_locks++;
 182 
 183                 lockhost = ((struct nlm_lockowner *)fl->fl_owner)->host;
 184                 if (match(lockhost, host)) {
 185                         struct file_lock lock = *fl;
 186 
 187                         spin_unlock(&flctx->flc_lock);
 188                         lock.fl_type  = F_UNLCK;
 189                         lock.fl_start = 0;
 190                         lock.fl_end   = OFFSET_MAX;
 191                         if (vfs_lock_file(file->f_file, F_SETLK, &lock, NULL) < 0) {
 192                                 printk("lockd: unlock failure in %s:%d\n",
 193                                                 __FILE__, __LINE__);
 194                                 return 1;
 195                         }
 196                         goto again;
 197                 }
 198         }
 199         spin_unlock(&flctx->flc_lock);
 200 
 201         return 0;
 202 }
 203 
 204 static int
 205 nlmsvc_always_match(void *dummy1, struct nlm_host *dummy2)
 206 {
 207         return 1;
 208 }
 209 
 210 /*
 211  * Inspect a single file
 212  */
 213 static inline int
 214 nlm_inspect_file(struct nlm_host *host, struct nlm_file *file, nlm_host_match_fn_t match)
 215 {
 216         nlmsvc_traverse_blocks(host, file, match);
 217         nlmsvc_traverse_shares(host, file, match);
 218         return nlm_traverse_locks(host, file, match);
 219 }
 220 
 221 /*
 222  * Quick check whether there are still any locks, blocks or
 223  * shares on a given file.
 224  */
 225 static inline int
 226 nlm_file_inuse(struct nlm_file *file)
 227 {
 228         struct inode     *inode = nlmsvc_file_inode(file);
 229         struct file_lock *fl;
 230         struct file_lock_context *flctx = inode->i_flctx;
 231 
 232         if (file->f_count || !list_empty(&file->f_blocks) || file->f_shares)
 233                 return 1;
 234 
 235         if (flctx && !list_empty_careful(&flctx->flc_posix)) {
 236                 spin_lock(&flctx->flc_lock);
 237                 list_for_each_entry(fl, &flctx->flc_posix, fl_list) {
 238                         if (fl->fl_lmops == &nlmsvc_lock_operations) {
 239                                 spin_unlock(&flctx->flc_lock);
 240                                 return 1;
 241                         }
 242                 }
 243                 spin_unlock(&flctx->flc_lock);
 244         }
 245         file->f_locks = 0;
 246         return 0;
 247 }
 248 
 249 /*
 250  * Loop over all files in the file table.
 251  */
 252 static int
 253 nlm_traverse_files(void *data, nlm_host_match_fn_t match,
 254                 int (*is_failover_file)(void *data, struct nlm_file *file))
 255 {
 256         struct hlist_node *next;
 257         struct nlm_file *file;
 258         int i, ret = 0;
 259 
 260         mutex_lock(&nlm_file_mutex);
 261         for (i = 0; i < FILE_NRHASH; i++) {
 262                 hlist_for_each_entry_safe(file, next, &nlm_files[i], f_list) {
 263                         if (is_failover_file && !is_failover_file(data, file))
 264                                 continue;
 265                         file->f_count++;
 266                         mutex_unlock(&nlm_file_mutex);
 267 
 268                         /* Traverse locks, blocks and shares of this file
 269                          * and update file->f_locks count */
 270                         if (nlm_inspect_file(data, file, match))
 271                                 ret = 1;
 272 
 273                         mutex_lock(&nlm_file_mutex);
 274                         file->f_count--;
 275                         /* No more references to this file. Let go of it. */
 276                         if (list_empty(&file->f_blocks) && !file->f_locks
 277                          && !file->f_shares && !file->f_count) {
 278                                 hlist_del(&file->f_list);
 279                                 nlmsvc_ops->fclose(file->f_file);
 280                                 kfree(file);
 281                         }
 282                 }
 283         }
 284         mutex_unlock(&nlm_file_mutex);
 285         return ret;
 286 }
 287 
 288 /*
 289  * Release file. If there are no more remote locks on this file,
 290  * close it and free the handle.
 291  *
 292  * Note that we can't do proper reference counting without major
 293  * contortions because the code in fs/locks.c creates, deletes and
 294  * splits locks without notification. Our only way is to walk the
 295  * entire lock list each time we remove a lock.
 296  */
 297 void
 298 nlm_release_file(struct nlm_file *file)
 299 {
 300         dprintk("lockd: nlm_release_file(%p, ct = %d)\n",
 301                                 file, file->f_count);
 302 
 303         /* Lock file table */
 304         mutex_lock(&nlm_file_mutex);
 305 
 306         /* If there are no more locks etc, delete the file */
 307         if (--file->f_count == 0 && !nlm_file_inuse(file))
 308                 nlm_delete_file(file);
 309 
 310         mutex_unlock(&nlm_file_mutex);
 311 }
 312 
 313 /*
 314  * Helpers function for resource traversal
 315  *
 316  * nlmsvc_mark_host:
 317  *      used by the garbage collector; simply sets h_inuse only for those
 318  *      hosts, which passed network check.
 319  *      Always returns 0.
 320  *
 321  * nlmsvc_same_host:
 322  *      returns 1 iff the two hosts match. Used to release
 323  *      all resources bound to a specific host.
 324  *
 325  * nlmsvc_is_client:
 326  *      returns 1 iff the host is a client.
 327  *      Used by nlmsvc_invalidate_all
 328  */
 329 
 330 static int
 331 nlmsvc_mark_host(void *data, struct nlm_host *hint)
 332 {
 333         struct nlm_host *host = data;
 334 
 335         if ((hint->net == NULL) ||
 336             (host->net == hint->net))
 337                 host->h_inuse = 1;
 338         return 0;
 339 }
 340 
 341 static int
 342 nlmsvc_same_host(void *data, struct nlm_host *other)
 343 {
 344         struct nlm_host *host = data;
 345 
 346         return host == other;
 347 }
 348 
 349 static int
 350 nlmsvc_is_client(void *data, struct nlm_host *dummy)
 351 {
 352         struct nlm_host *host = data;
 353 
 354         if (host->h_server) {
 355                 /* we are destroying locks even though the client
 356                  * hasn't asked us too, so don't unmonitor the
 357                  * client
 358                  */
 359                 if (host->h_nsmhandle)
 360                         host->h_nsmhandle->sm_sticky = 1;
 361                 return 1;
 362         } else
 363                 return 0;
 364 }
 365 
 366 /*
 367  * Mark all hosts that still hold resources
 368  */
 369 void
 370 nlmsvc_mark_resources(struct net *net)
 371 {
 372         struct nlm_host hint;
 373 
 374         dprintk("lockd: %s for net %x\n", __func__, net ? net->ns.inum : 0);
 375         hint.net = net;
 376         nlm_traverse_files(&hint, nlmsvc_mark_host, NULL);
 377 }
 378 
 379 /*
 380  * Release all resources held by the given client
 381  */
 382 void
 383 nlmsvc_free_host_resources(struct nlm_host *host)
 384 {
 385         dprintk("lockd: nlmsvc_free_host_resources\n");
 386 
 387         if (nlm_traverse_files(host, nlmsvc_same_host, NULL)) {
 388                 printk(KERN_WARNING
 389                         "lockd: couldn't remove all locks held by %s\n",
 390                         host->h_name);
 391                 BUG();
 392         }
 393 }
 394 
 395 /**
 396  * nlmsvc_invalidate_all - remove all locks held for clients
 397  *
 398  * Release all locks held by NFS clients.
 399  *
 400  */
 401 void
 402 nlmsvc_invalidate_all(void)
 403 {
 404         /*
 405          * Previously, the code would call
 406          * nlmsvc_free_host_resources for each client in
 407          * turn, which is about as inefficient as it gets.
 408          * Now we just do it once in nlm_traverse_files.
 409          */
 410         nlm_traverse_files(NULL, nlmsvc_is_client, NULL);
 411 }
 412 
 413 static int
 414 nlmsvc_match_sb(void *datap, struct nlm_file *file)
 415 {
 416         struct super_block *sb = datap;
 417 
 418         return sb == locks_inode(file->f_file)->i_sb;
 419 }
 420 
 421 /**
 422  * nlmsvc_unlock_all_by_sb - release locks held on this file system
 423  * @sb: super block
 424  *
 425  * Release all locks held by clients accessing this file system.
 426  */
 427 int
 428 nlmsvc_unlock_all_by_sb(struct super_block *sb)
 429 {
 430         int ret;
 431 
 432         ret = nlm_traverse_files(sb, nlmsvc_always_match, nlmsvc_match_sb);
 433         return ret ? -EIO : 0;
 434 }
 435 EXPORT_SYMBOL_GPL(nlmsvc_unlock_all_by_sb);
 436 
 437 static int
 438 nlmsvc_match_ip(void *datap, struct nlm_host *host)
 439 {
 440         return rpc_cmp_addr(nlm_srcaddr(host), datap);
 441 }
 442 
 443 /**
 444  * nlmsvc_unlock_all_by_ip - release local locks by IP address
 445  * @server_addr: server's IP address as seen by clients
 446  *
 447  * Release all locks held by clients accessing this host
 448  * via the passed in IP address.
 449  */
 450 int
 451 nlmsvc_unlock_all_by_ip(struct sockaddr *server_addr)
 452 {
 453         int ret;
 454 
 455         ret = nlm_traverse_files(server_addr, nlmsvc_match_ip, NULL);
 456         return ret ? -EIO : 0;
 457 }
 458 EXPORT_SYMBOL_GPL(nlmsvc_unlock_all_by_ip);

/* [<][>][^][v][top][bottom][index][help] */