LCOV - code coverage report
Current view: top level - fs/lockd - svcsubs.c (source / functions) Hit Total Coverage
Test: gcov_data_raspi2_qemu_modules_combined.info Lines: 0 116 0.0 %
Date: 2020-09-30 20:25:01 Functions: 0 18 0.0 %
Branches: 0 82 0.0 %

           Branch data     Line data    Source code
       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         [ #  # ]:          0 :         for (i=0; i<NFS2_FHSIZE;i++)
      70                 :          0 :                 tmp += f->data[i];
      71                 :          0 :         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                 :          0 : 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                 :          0 :         mutex_lock(&nlm_file_mutex);
      97                 :            : 
      98   [ #  #  #  #  :          0 :         hlist_for_each_entry(file, &nlm_files[hash], f_list)
                   #  # ]
      99         [ #  # ]:          0 :                 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                 :          0 :         file = kzalloc(sizeof(*file), GFP_KERNEL);
     106         [ #  # ]:          0 :         if (!file)
     107                 :            :                 goto out_unlock;
     108                 :            : 
     109                 :          0 :         memcpy(&file->f_handle, f, sizeof(struct nfs_fh));
     110                 :          0 :         mutex_init(&file->f_mutex);
     111                 :            :         INIT_HLIST_NODE(&file->f_list);
     112                 :          0 :         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         [ #  # ]:          0 :         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                 :          0 :         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                 :          0 :         *result = file;
     130                 :          0 :         file->f_count++;
     131                 :            :         nfserr = 0;
     132                 :            : 
     133                 :            : out_unlock:
     134                 :          0 :         mutex_unlock(&nlm_file_mutex);
     135                 :          0 :         return nfserr;
     136                 :            : 
     137                 :            : out_free:
     138                 :          0 :         kfree(file);
     139                 :          0 :         goto out_unlock;
     140                 :            : }
     141                 :            : 
     142                 :            : /*
     143                 :            :  * Delete a file after having released all locks, blocks and shares
     144                 :            :  */
     145                 :            : static inline void
     146                 :          0 : nlm_delete_file(struct nlm_file *file)
     147                 :            : {
     148                 :            :         nlm_debug_print_file("closing file", file);
     149         [ #  # ]:          0 :         if (!hlist_unhashed(&file->f_list)) {
     150                 :            :                 hlist_del(&file->f_list);
     151                 :          0 :                 nlmsvc_ops->fclose(file->f_file);
     152                 :          0 :                 kfree(file);
     153                 :            :         } else {
     154                 :          0 :                 printk(KERN_WARNING "lockd: attempt to release unknown file!\n");
     155                 :            :         }
     156                 :          0 : }
     157                 :            : 
     158                 :            : /*
     159                 :            :  * Loop over all locks on the given file and perform the specified
     160                 :            :  * action.
     161                 :            :  */
     162                 :            : static int
     163                 :          0 : 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                 :          0 :         struct file_lock_context *flctx = inode->i_flctx;
     169                 :            :         struct nlm_host  *lockhost;
     170                 :            : 
     171   [ #  #  #  # ]:          0 :         if (!flctx || list_empty_careful(&flctx->flc_posix))
     172                 :            :                 return 0;
     173                 :            : again:
     174                 :          0 :         file->f_locks = 0;
     175                 :            :         spin_lock(&flctx->flc_lock);
     176         [ #  # ]:          0 :         list_for_each_entry(fl, &flctx->flc_posix, fl_list) {
     177         [ #  # ]:          0 :                 if (fl->fl_lmops != &nlmsvc_lock_operations)
     178                 :          0 :                         continue;
     179                 :            : 
     180                 :            :                 /* update current lock count */
     181                 :          0 :                 file->f_locks++;
     182                 :            : 
     183                 :          0 :                 lockhost = ((struct nlm_lockowner *)fl->fl_owner)->host;
     184         [ #  # ]:          0 :                 if (match(lockhost, host)) {
     185                 :          0 :                         struct file_lock lock = *fl;
     186                 :            : 
     187                 :            :                         spin_unlock(&flctx->flc_lock);
     188                 :          0 :                         lock.fl_type  = F_UNLCK;
     189                 :          0 :                         lock.fl_start = 0;
     190                 :          0 :                         lock.fl_end   = OFFSET_MAX;
     191         [ #  # ]:          0 :                         if (vfs_lock_file(file->f_file, F_SETLK, &lock, NULL) < 0) {
     192                 :          0 :                                 printk("lockd: unlock failure in %s:%d\n",
     193                 :            :                                                 __FILE__, __LINE__);
     194                 :          0 :                                 return 1;
     195                 :            :                         }
     196                 :          0 :                         goto again;
     197                 :            :                 }
     198                 :            :         }
     199                 :            :         spin_unlock(&flctx->flc_lock);
     200                 :            : 
     201                 :          0 :         return 0;
     202                 :            : }
     203                 :            : 
     204                 :            : static int
     205                 :          0 : nlmsvc_always_match(void *dummy1, struct nlm_host *dummy2)
     206                 :            : {
     207                 :          0 :         return 1;
     208                 :            : }
     209                 :            : 
     210                 :            : /*
     211                 :            :  * Inspect a single file
     212                 :            :  */
     213                 :            : static inline int
     214                 :          0 : nlm_inspect_file(struct nlm_host *host, struct nlm_file *file, nlm_host_match_fn_t match)
     215                 :            : {
     216                 :          0 :         nlmsvc_traverse_blocks(host, file, match);
     217                 :          0 :         nlmsvc_traverse_shares(host, file, match);
     218                 :          0 :         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                 :          0 : nlm_file_inuse(struct nlm_file *file)
     227                 :            : {
     228                 :            :         struct inode     *inode = nlmsvc_file_inode(file);
     229                 :            :         struct file_lock *fl;
     230                 :          0 :         struct file_lock_context *flctx = inode->i_flctx;
     231                 :            : 
     232   [ #  #  #  #  :          0 :         if (file->f_count || !list_empty(&file->f_blocks) || file->f_shares)
                   #  # ]
     233                 :            :                 return 1;
     234                 :            : 
     235   [ #  #  #  # ]:          0 :         if (flctx && !list_empty_careful(&flctx->flc_posix)) {
     236                 :            :                 spin_lock(&flctx->flc_lock);
     237         [ #  # ]:          0 :                 list_for_each_entry(fl, &flctx->flc_posix, fl_list) {
     238         [ #  # ]:          0 :                         if (fl->fl_lmops == &nlmsvc_lock_operations) {
     239                 :            :                                 spin_unlock(&flctx->flc_lock);
     240                 :          0 :                                 return 1;
     241                 :            :                         }
     242                 :            :                 }
     243                 :            :                 spin_unlock(&flctx->flc_lock);
     244                 :            :         }
     245                 :          0 :         file->f_locks = 0;
     246                 :          0 :         return 0;
     247                 :            : }
     248                 :            : 
     249                 :            : /*
     250                 :            :  * Loop over all files in the file table.
     251                 :            :  */
     252                 :            : static int
     253                 :          0 : 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                 :          0 :         mutex_lock(&nlm_file_mutex);
     261         [ #  # ]:          0 :         for (i = 0; i < FILE_NRHASH; i++) {
     262   [ #  #  #  #  :          0 :                 hlist_for_each_entry_safe(file, next, &nlm_files[i], f_list) {
                   #  # ]
     263   [ #  #  #  # ]:          0 :                         if (is_failover_file && !is_failover_file(data, file))
     264                 :          0 :                                 continue;
     265                 :          0 :                         file->f_count++;
     266                 :          0 :                         mutex_unlock(&nlm_file_mutex);
     267                 :            : 
     268                 :            :                         /* Traverse locks, blocks and shares of this file
     269                 :            :                          * and update file->f_locks count */
     270         [ #  # ]:          0 :                         if (nlm_inspect_file(data, file, match))
     271                 :            :                                 ret = 1;
     272                 :            : 
     273                 :          0 :                         mutex_lock(&nlm_file_mutex);
     274                 :          0 :                         file->f_count--;
     275                 :            :                         /* No more references to this file. Let go of it. */
     276   [ #  #  #  # ]:          0 :                         if (list_empty(&file->f_blocks) && !file->f_locks
     277   [ #  #  #  # ]:          0 :                          && !file->f_shares && !file->f_count) {
     278                 :            :                                 hlist_del(&file->f_list);
     279                 :          0 :                                 nlmsvc_ops->fclose(file->f_file);
     280                 :          0 :                                 kfree(file);
     281                 :            :                         }
     282                 :            :                 }
     283                 :            :         }
     284                 :          0 :         mutex_unlock(&nlm_file_mutex);
     285                 :          0 :         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                 :          0 : 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                 :          0 :         mutex_lock(&nlm_file_mutex);
     305                 :            : 
     306                 :            :         /* If there are no more locks etc, delete the file */
     307   [ #  #  #  # ]:          0 :         if (--file->f_count == 0 && !nlm_file_inuse(file))
     308                 :          0 :                 nlm_delete_file(file);
     309                 :            : 
     310                 :          0 :         mutex_unlock(&nlm_file_mutex);
     311                 :          0 : }
     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                 :          0 : nlmsvc_mark_host(void *data, struct nlm_host *hint)
     332                 :            : {
     333                 :            :         struct nlm_host *host = data;
     334                 :            : 
     335   [ #  #  #  # ]:          0 :         if ((hint->net == NULL) ||
     336                 :          0 :             (host->net == hint->net))
     337                 :          0 :                 host->h_inuse = 1;
     338                 :          0 :         return 0;
     339                 :            : }
     340                 :            : 
     341                 :            : static int
     342                 :          0 : nlmsvc_same_host(void *data, struct nlm_host *other)
     343                 :            : {
     344                 :            :         struct nlm_host *host = data;
     345                 :            : 
     346                 :          0 :         return host == other;
     347                 :            : }
     348                 :            : 
     349                 :            : static int
     350                 :          0 : nlmsvc_is_client(void *data, struct nlm_host *dummy)
     351                 :            : {
     352                 :            :         struct nlm_host *host = data;
     353                 :            : 
     354         [ #  # ]:          0 :         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         [ #  # ]:          0 :                 if (host->h_nsmhandle)
     360                 :          0 :                         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                 :          0 : 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                 :          0 :         hint.net = net;
     376                 :          0 :         nlm_traverse_files(&hint, nlmsvc_mark_host, NULL);
     377                 :          0 : }
     378                 :            : 
     379                 :            : /*
     380                 :            :  * Release all resources held by the given client
     381                 :            :  */
     382                 :            : void
     383                 :          0 : nlmsvc_free_host_resources(struct nlm_host *host)
     384                 :            : {
     385                 :            :         dprintk("lockd: nlmsvc_free_host_resources\n");
     386                 :            : 
     387         [ #  # ]:          0 :         if (nlm_traverse_files(host, nlmsvc_same_host, NULL)) {
     388                 :          0 :                 printk(KERN_WARNING
     389                 :            :                         "lockd: couldn't remove all locks held by %s\n",
     390                 :            :                         host->h_name);
     391                 :          0 :                 BUG();
     392                 :            :         }
     393                 :          0 : }
     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                 :          0 : 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                 :          0 :         nlm_traverse_files(NULL, nlmsvc_is_client, NULL);
     411                 :          0 : }
     412                 :            : 
     413                 :            : static int
     414                 :          0 : nlmsvc_match_sb(void *datap, struct nlm_file *file)
     415                 :            : {
     416                 :            :         struct super_block *sb = datap;
     417                 :            : 
     418                 :          0 :         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                 :          0 : nlmsvc_unlock_all_by_sb(struct super_block *sb)
     429                 :            : {
     430                 :            :         int ret;
     431                 :            : 
     432                 :          0 :         ret = nlm_traverse_files(sb, nlmsvc_always_match, nlmsvc_match_sb);
     433         [ #  # ]:          0 :         return ret ? -EIO : 0;
     434                 :            : }
     435                 :            : EXPORT_SYMBOL_GPL(nlmsvc_unlock_all_by_sb);
     436                 :            : 
     437                 :            : static int
     438                 :          0 : nlmsvc_match_ip(void *datap, struct nlm_host *host)
     439                 :            : {
     440                 :          0 :         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                 :          0 : nlmsvc_unlock_all_by_ip(struct sockaddr *server_addr)
     452                 :            : {
     453                 :            :         int ret;
     454                 :            : 
     455                 :          0 :         ret = nlm_traverse_files(server_addr, nlmsvc_match_ip, NULL);
     456         [ #  # ]:          0 :         return ret ? -EIO : 0;
     457                 :            : }
     458                 :            : EXPORT_SYMBOL_GPL(nlmsvc_unlock_all_by_ip);

Generated by: LCOV version 1.14