Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * linux/fs/lockd/svcsubs.c
0004  *
0005  * Various support routines for the NLM server.
0006  *
0007  * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
0008  */
0009 
0010 #include <linux/types.h>
0011 #include <linux/string.h>
0012 #include <linux/time.h>
0013 #include <linux/in.h>
0014 #include <linux/slab.h>
0015 #include <linux/mutex.h>
0016 #include <linux/sunrpc/svc.h>
0017 #include <linux/sunrpc/addr.h>
0018 #include <linux/lockd/lockd.h>
0019 #include <linux/lockd/share.h>
0020 #include <linux/module.h>
0021 #include <linux/mount.h>
0022 #include <uapi/linux/nfs2.h>
0023 
0024 #define NLMDBG_FACILITY     NLMDBG_SVCSUBS
0025 
0026 
0027 /*
0028  * Global file hash table
0029  */
0030 #define FILE_HASH_BITS      7
0031 #define FILE_NRHASH     (1<<FILE_HASH_BITS)
0032 static struct hlist_head    nlm_files[FILE_NRHASH];
0033 static DEFINE_MUTEX(nlm_file_mutex);
0034 
0035 #ifdef CONFIG_SUNRPC_DEBUG
0036 static inline void nlm_debug_print_fh(char *msg, struct nfs_fh *f)
0037 {
0038     u32 *fhp = (u32*)f->data;
0039 
0040     /* print the first 32 bytes of the fh */
0041     dprintk("lockd: %s (%08x %08x %08x %08x %08x %08x %08x %08x)\n",
0042         msg, fhp[0], fhp[1], fhp[2], fhp[3],
0043         fhp[4], fhp[5], fhp[6], fhp[7]);
0044 }
0045 
0046 static inline void nlm_debug_print_file(char *msg, struct nlm_file *file)
0047 {
0048     struct inode *inode = nlmsvc_file_inode(file);
0049 
0050     dprintk("lockd: %s %s/%ld\n",
0051         msg, inode->i_sb->s_id, inode->i_ino);
0052 }
0053 #else
0054 static inline void nlm_debug_print_fh(char *msg, struct nfs_fh *f)
0055 {
0056     return;
0057 }
0058 
0059 static inline void nlm_debug_print_file(char *msg, struct nlm_file *file)
0060 {
0061     return;
0062 }
0063 #endif
0064 
0065 static inline unsigned int file_hash(struct nfs_fh *f)
0066 {
0067     unsigned int tmp=0;
0068     int i;
0069     for (i=0; i<NFS2_FHSIZE;i++)
0070         tmp += f->data[i];
0071     return tmp & (FILE_NRHASH - 1);
0072 }
0073 
0074 int lock_to_openmode(struct file_lock *lock)
0075 {
0076     return (lock->fl_type == F_WRLCK) ? O_WRONLY : O_RDONLY;
0077 }
0078 
0079 /*
0080  * Open the file. Note that if we're reexporting, for example,
0081  * this could block the lockd thread for a while.
0082  *
0083  * We have to make sure we have the right credential to open
0084  * the file.
0085  */
0086 static __be32 nlm_do_fopen(struct svc_rqst *rqstp,
0087                struct nlm_file *file, int mode)
0088 {
0089     struct file **fp = &file->f_file[mode];
0090     __be32  nfserr;
0091 
0092     if (*fp)
0093         return 0;
0094     nfserr = nlmsvc_ops->fopen(rqstp, &file->f_handle, fp, mode);
0095     if (nfserr)
0096         dprintk("lockd: open failed (error %d)\n", nfserr);
0097     return nfserr;
0098 }
0099 
0100 /*
0101  * Lookup file info. If it doesn't exist, create a file info struct
0102  * and open a (VFS) file for the given inode.
0103  */
0104 __be32
0105 nlm_lookup_file(struct svc_rqst *rqstp, struct nlm_file **result,
0106                     struct nlm_lock *lock)
0107 {
0108     struct nlm_file *file;
0109     unsigned int    hash;
0110     __be32      nfserr;
0111     int     mode;
0112 
0113     nlm_debug_print_fh("nlm_lookup_file", &lock->fh);
0114 
0115     hash = file_hash(&lock->fh);
0116     mode = lock_to_openmode(&lock->fl);
0117 
0118     /* Lock file table */
0119     mutex_lock(&nlm_file_mutex);
0120 
0121     hlist_for_each_entry(file, &nlm_files[hash], f_list)
0122         if (!nfs_compare_fh(&file->f_handle, &lock->fh)) {
0123             mutex_lock(&file->f_mutex);
0124             nfserr = nlm_do_fopen(rqstp, file, mode);
0125             mutex_unlock(&file->f_mutex);
0126             goto found;
0127         }
0128     nlm_debug_print_fh("creating file for", &lock->fh);
0129 
0130     nfserr = nlm_lck_denied_nolocks;
0131     file = kzalloc(sizeof(*file), GFP_KERNEL);
0132     if (!file)
0133         goto out_free;
0134 
0135     memcpy(&file->f_handle, &lock->fh, sizeof(struct nfs_fh));
0136     mutex_init(&file->f_mutex);
0137     INIT_HLIST_NODE(&file->f_list);
0138     INIT_LIST_HEAD(&file->f_blocks);
0139 
0140     nfserr = nlm_do_fopen(rqstp, file, mode);
0141     if (nfserr)
0142         goto out_unlock;
0143 
0144     hlist_add_head(&file->f_list, &nlm_files[hash]);
0145 
0146 found:
0147     dprintk("lockd: found file %p (count %d)\n", file, file->f_count);
0148     *result = file;
0149     file->f_count++;
0150 
0151 out_unlock:
0152     mutex_unlock(&nlm_file_mutex);
0153     return nfserr;
0154 
0155 out_free:
0156     kfree(file);
0157     goto out_unlock;
0158 }
0159 
0160 /*
0161  * Delete a file after having released all locks, blocks and shares
0162  */
0163 static inline void
0164 nlm_delete_file(struct nlm_file *file)
0165 {
0166     nlm_debug_print_file("closing file", file);
0167     if (!hlist_unhashed(&file->f_list)) {
0168         hlist_del(&file->f_list);
0169         if (file->f_file[O_RDONLY])
0170             nlmsvc_ops->fclose(file->f_file[O_RDONLY]);
0171         if (file->f_file[O_WRONLY])
0172             nlmsvc_ops->fclose(file->f_file[O_WRONLY]);
0173         kfree(file);
0174     } else {
0175         printk(KERN_WARNING "lockd: attempt to release unknown file!\n");
0176     }
0177 }
0178 
0179 static int nlm_unlock_files(struct nlm_file *file, fl_owner_t owner)
0180 {
0181     struct file_lock lock;
0182 
0183     locks_init_lock(&lock);
0184     lock.fl_type  = F_UNLCK;
0185     lock.fl_start = 0;
0186     lock.fl_end   = OFFSET_MAX;
0187     lock.fl_owner = owner;
0188     if (file->f_file[O_RDONLY] &&
0189         vfs_lock_file(file->f_file[O_RDONLY], F_SETLK, &lock, NULL))
0190         goto out_err;
0191     if (file->f_file[O_WRONLY] &&
0192         vfs_lock_file(file->f_file[O_WRONLY], F_SETLK, &lock, NULL))
0193         goto out_err;
0194     return 0;
0195 out_err:
0196     pr_warn("lockd: unlock failure in %s:%d\n", __FILE__, __LINE__);
0197     return 1;
0198 }
0199 
0200 /*
0201  * Loop over all locks on the given file and perform the specified
0202  * action.
0203  */
0204 static int
0205 nlm_traverse_locks(struct nlm_host *host, struct nlm_file *file,
0206             nlm_host_match_fn_t match)
0207 {
0208     struct inode     *inode = nlmsvc_file_inode(file);
0209     struct file_lock *fl;
0210     struct file_lock_context *flctx = inode->i_flctx;
0211     struct nlm_host  *lockhost;
0212 
0213     if (!flctx || list_empty_careful(&flctx->flc_posix))
0214         return 0;
0215 again:
0216     file->f_locks = 0;
0217     spin_lock(&flctx->flc_lock);
0218     list_for_each_entry(fl, &flctx->flc_posix, fl_list) {
0219         if (fl->fl_lmops != &nlmsvc_lock_operations)
0220             continue;
0221 
0222         /* update current lock count */
0223         file->f_locks++;
0224 
0225         lockhost = ((struct nlm_lockowner *)fl->fl_owner)->host;
0226         if (match(lockhost, host)) {
0227 
0228             spin_unlock(&flctx->flc_lock);
0229             if (nlm_unlock_files(file, fl->fl_owner))
0230                 return 1;
0231             goto again;
0232         }
0233     }
0234     spin_unlock(&flctx->flc_lock);
0235 
0236     return 0;
0237 }
0238 
0239 static int
0240 nlmsvc_always_match(void *dummy1, struct nlm_host *dummy2)
0241 {
0242     return 1;
0243 }
0244 
0245 /*
0246  * Inspect a single file
0247  */
0248 static inline int
0249 nlm_inspect_file(struct nlm_host *host, struct nlm_file *file, nlm_host_match_fn_t match)
0250 {
0251     nlmsvc_traverse_blocks(host, file, match);
0252     nlmsvc_traverse_shares(host, file, match);
0253     return nlm_traverse_locks(host, file, match);
0254 }
0255 
0256 /*
0257  * Quick check whether there are still any locks, blocks or
0258  * shares on a given file.
0259  */
0260 static inline int
0261 nlm_file_inuse(struct nlm_file *file)
0262 {
0263     struct inode     *inode = nlmsvc_file_inode(file);
0264     struct file_lock *fl;
0265     struct file_lock_context *flctx = inode->i_flctx;
0266 
0267     if (file->f_count || !list_empty(&file->f_blocks) || file->f_shares)
0268         return 1;
0269 
0270     if (flctx && !list_empty_careful(&flctx->flc_posix)) {
0271         spin_lock(&flctx->flc_lock);
0272         list_for_each_entry(fl, &flctx->flc_posix, fl_list) {
0273             if (fl->fl_lmops == &nlmsvc_lock_operations) {
0274                 spin_unlock(&flctx->flc_lock);
0275                 return 1;
0276             }
0277         }
0278         spin_unlock(&flctx->flc_lock);
0279     }
0280     file->f_locks = 0;
0281     return 0;
0282 }
0283 
0284 static void nlm_close_files(struct nlm_file *file)
0285 {
0286     if (file->f_file[O_RDONLY])
0287         nlmsvc_ops->fclose(file->f_file[O_RDONLY]);
0288     if (file->f_file[O_WRONLY])
0289         nlmsvc_ops->fclose(file->f_file[O_WRONLY]);
0290 }
0291 
0292 /*
0293  * Loop over all files in the file table.
0294  */
0295 static int
0296 nlm_traverse_files(void *data, nlm_host_match_fn_t match,
0297         int (*is_failover_file)(void *data, struct nlm_file *file))
0298 {
0299     struct hlist_node *next;
0300     struct nlm_file *file;
0301     int i, ret = 0;
0302 
0303     mutex_lock(&nlm_file_mutex);
0304     for (i = 0; i < FILE_NRHASH; i++) {
0305         hlist_for_each_entry_safe(file, next, &nlm_files[i], f_list) {
0306             if (is_failover_file && !is_failover_file(data, file))
0307                 continue;
0308             file->f_count++;
0309             mutex_unlock(&nlm_file_mutex);
0310 
0311             /* Traverse locks, blocks and shares of this file
0312              * and update file->f_locks count */
0313             if (nlm_inspect_file(data, file, match))
0314                 ret = 1;
0315 
0316             mutex_lock(&nlm_file_mutex);
0317             file->f_count--;
0318             /* No more references to this file. Let go of it. */
0319             if (list_empty(&file->f_blocks) && !file->f_locks
0320              && !file->f_shares && !file->f_count) {
0321                 hlist_del(&file->f_list);
0322                 nlm_close_files(file);
0323                 kfree(file);
0324             }
0325         }
0326     }
0327     mutex_unlock(&nlm_file_mutex);
0328     return ret;
0329 }
0330 
0331 /*
0332  * Release file. If there are no more remote locks on this file,
0333  * close it and free the handle.
0334  *
0335  * Note that we can't do proper reference counting without major
0336  * contortions because the code in fs/locks.c creates, deletes and
0337  * splits locks without notification. Our only way is to walk the
0338  * entire lock list each time we remove a lock.
0339  */
0340 void
0341 nlm_release_file(struct nlm_file *file)
0342 {
0343     dprintk("lockd: nlm_release_file(%p, ct = %d)\n",
0344                 file, file->f_count);
0345 
0346     /* Lock file table */
0347     mutex_lock(&nlm_file_mutex);
0348 
0349     /* If there are no more locks etc, delete the file */
0350     if (--file->f_count == 0 && !nlm_file_inuse(file))
0351         nlm_delete_file(file);
0352 
0353     mutex_unlock(&nlm_file_mutex);
0354 }
0355 
0356 /*
0357  * Helpers function for resource traversal
0358  *
0359  * nlmsvc_mark_host:
0360  *  used by the garbage collector; simply sets h_inuse only for those
0361  *  hosts, which passed network check.
0362  *  Always returns 0.
0363  *
0364  * nlmsvc_same_host:
0365  *  returns 1 iff the two hosts match. Used to release
0366  *  all resources bound to a specific host.
0367  *
0368  * nlmsvc_is_client:
0369  *  returns 1 iff the host is a client.
0370  *  Used by nlmsvc_invalidate_all
0371  */
0372 
0373 static int
0374 nlmsvc_mark_host(void *data, struct nlm_host *hint)
0375 {
0376     struct nlm_host *host = data;
0377 
0378     if ((hint->net == NULL) ||
0379         (host->net == hint->net))
0380         host->h_inuse = 1;
0381     return 0;
0382 }
0383 
0384 static int
0385 nlmsvc_same_host(void *data, struct nlm_host *other)
0386 {
0387     struct nlm_host *host = data;
0388 
0389     return host == other;
0390 }
0391 
0392 static int
0393 nlmsvc_is_client(void *data, struct nlm_host *dummy)
0394 {
0395     struct nlm_host *host = data;
0396 
0397     if (host->h_server) {
0398         /* we are destroying locks even though the client
0399          * hasn't asked us too, so don't unmonitor the
0400          * client
0401          */
0402         if (host->h_nsmhandle)
0403             host->h_nsmhandle->sm_sticky = 1;
0404         return 1;
0405     } else
0406         return 0;
0407 }
0408 
0409 /*
0410  * Mark all hosts that still hold resources
0411  */
0412 void
0413 nlmsvc_mark_resources(struct net *net)
0414 {
0415     struct nlm_host hint;
0416 
0417     dprintk("lockd: %s for net %x\n", __func__, net ? net->ns.inum : 0);
0418     hint.net = net;
0419     nlm_traverse_files(&hint, nlmsvc_mark_host, NULL);
0420 }
0421 
0422 /*
0423  * Release all resources held by the given client
0424  */
0425 void
0426 nlmsvc_free_host_resources(struct nlm_host *host)
0427 {
0428     dprintk("lockd: nlmsvc_free_host_resources\n");
0429 
0430     if (nlm_traverse_files(host, nlmsvc_same_host, NULL)) {
0431         printk(KERN_WARNING
0432             "lockd: couldn't remove all locks held by %s\n",
0433             host->h_name);
0434         BUG();
0435     }
0436 }
0437 
0438 /**
0439  * nlmsvc_invalidate_all - remove all locks held for clients
0440  *
0441  * Release all locks held by NFS clients.
0442  *
0443  */
0444 void
0445 nlmsvc_invalidate_all(void)
0446 {
0447     /*
0448      * Previously, the code would call
0449      * nlmsvc_free_host_resources for each client in
0450      * turn, which is about as inefficient as it gets.
0451      * Now we just do it once in nlm_traverse_files.
0452      */
0453     nlm_traverse_files(NULL, nlmsvc_is_client, NULL);
0454 }
0455 
0456 
0457 static int
0458 nlmsvc_match_sb(void *datap, struct nlm_file *file)
0459 {
0460     struct super_block *sb = datap;
0461 
0462     return sb == nlmsvc_file_inode(file)->i_sb;
0463 }
0464 
0465 /**
0466  * nlmsvc_unlock_all_by_sb - release locks held on this file system
0467  * @sb: super block
0468  *
0469  * Release all locks held by clients accessing this file system.
0470  */
0471 int
0472 nlmsvc_unlock_all_by_sb(struct super_block *sb)
0473 {
0474     int ret;
0475 
0476     ret = nlm_traverse_files(sb, nlmsvc_always_match, nlmsvc_match_sb);
0477     return ret ? -EIO : 0;
0478 }
0479 EXPORT_SYMBOL_GPL(nlmsvc_unlock_all_by_sb);
0480 
0481 static int
0482 nlmsvc_match_ip(void *datap, struct nlm_host *host)
0483 {
0484     return rpc_cmp_addr(nlm_srcaddr(host), datap);
0485 }
0486 
0487 /**
0488  * nlmsvc_unlock_all_by_ip - release local locks by IP address
0489  * @server_addr: server's IP address as seen by clients
0490  *
0491  * Release all locks held by clients accessing this host
0492  * via the passed in IP address.
0493  */
0494 int
0495 nlmsvc_unlock_all_by_ip(struct sockaddr *server_addr)
0496 {
0497     int ret;
0498 
0499     ret = nlm_traverse_files(server_addr, nlmsvc_match_ip, NULL);
0500     return ret ? -EIO : 0;
0501 }
0502 EXPORT_SYMBOL_GPL(nlmsvc_unlock_all_by_ip);