162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * NFS server file handle treatment. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de> 662306a36Sopenharmony_ci * Portions Copyright (C) 1999 G. Allen Morris III <gam3@acm.org> 762306a36Sopenharmony_ci * Extensive rewrite by Neil Brown <neilb@cse.unsw.edu.au> Southern-Spring 1999 862306a36Sopenharmony_ci * ... and again Southern-Winter 2001 to support export_operations 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/exportfs.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/sunrpc/svcauth_gss.h> 1462306a36Sopenharmony_ci#include "nfsd.h" 1562306a36Sopenharmony_ci#include "vfs.h" 1662306a36Sopenharmony_ci#include "auth.h" 1762306a36Sopenharmony_ci#include "trace.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#define NFSDDBG_FACILITY NFSDDBG_FH 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci/* 2362306a36Sopenharmony_ci * our acceptability function. 2462306a36Sopenharmony_ci * if NOSUBTREECHECK, accept anything 2562306a36Sopenharmony_ci * if not, require that we can walk up to exp->ex_dentry 2662306a36Sopenharmony_ci * doing some checks on the 'x' bits 2762306a36Sopenharmony_ci */ 2862306a36Sopenharmony_cistatic int nfsd_acceptable(void *expv, struct dentry *dentry) 2962306a36Sopenharmony_ci{ 3062306a36Sopenharmony_ci struct svc_export *exp = expv; 3162306a36Sopenharmony_ci int rv; 3262306a36Sopenharmony_ci struct dentry *tdentry; 3362306a36Sopenharmony_ci struct dentry *parent; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci if (exp->ex_flags & NFSEXP_NOSUBTREECHECK) 3662306a36Sopenharmony_ci return 1; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci tdentry = dget(dentry); 3962306a36Sopenharmony_ci while (tdentry != exp->ex_path.dentry && !IS_ROOT(tdentry)) { 4062306a36Sopenharmony_ci /* make sure parents give x permission to user */ 4162306a36Sopenharmony_ci int err; 4262306a36Sopenharmony_ci parent = dget_parent(tdentry); 4362306a36Sopenharmony_ci err = inode_permission(&nop_mnt_idmap, 4462306a36Sopenharmony_ci d_inode(parent), MAY_EXEC); 4562306a36Sopenharmony_ci if (err < 0) { 4662306a36Sopenharmony_ci dput(parent); 4762306a36Sopenharmony_ci break; 4862306a36Sopenharmony_ci } 4962306a36Sopenharmony_ci dput(tdentry); 5062306a36Sopenharmony_ci tdentry = parent; 5162306a36Sopenharmony_ci } 5262306a36Sopenharmony_ci if (tdentry != exp->ex_path.dentry) 5362306a36Sopenharmony_ci dprintk("nfsd_acceptable failed at %p %pd\n", tdentry, tdentry); 5462306a36Sopenharmony_ci rv = (tdentry == exp->ex_path.dentry); 5562306a36Sopenharmony_ci dput(tdentry); 5662306a36Sopenharmony_ci return rv; 5762306a36Sopenharmony_ci} 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci/* Type check. The correct error return for type mismatches does not seem to be 6062306a36Sopenharmony_ci * generally agreed upon. SunOS seems to use EISDIR if file isn't S_IFREG; a 6162306a36Sopenharmony_ci * comment in the NFSv3 spec says this is incorrect (implementation notes for 6262306a36Sopenharmony_ci * the write call). 6362306a36Sopenharmony_ci */ 6462306a36Sopenharmony_cistatic inline __be32 6562306a36Sopenharmony_cinfsd_mode_check(struct svc_rqst *rqstp, struct dentry *dentry, 6662306a36Sopenharmony_ci umode_t requested) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci umode_t mode = d_inode(dentry)->i_mode & S_IFMT; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci if (requested == 0) /* the caller doesn't care */ 7162306a36Sopenharmony_ci return nfs_ok; 7262306a36Sopenharmony_ci if (mode == requested) { 7362306a36Sopenharmony_ci if (mode == S_IFDIR && !d_can_lookup(dentry)) { 7462306a36Sopenharmony_ci WARN_ON_ONCE(1); 7562306a36Sopenharmony_ci return nfserr_notdir; 7662306a36Sopenharmony_ci } 7762306a36Sopenharmony_ci return nfs_ok; 7862306a36Sopenharmony_ci } 7962306a36Sopenharmony_ci /* 8062306a36Sopenharmony_ci * v4 has an error more specific than err_notdir which we should 8162306a36Sopenharmony_ci * return in preference to err_notdir: 8262306a36Sopenharmony_ci */ 8362306a36Sopenharmony_ci if (rqstp->rq_vers == 4 && mode == S_IFLNK) 8462306a36Sopenharmony_ci return nfserr_symlink; 8562306a36Sopenharmony_ci if (requested == S_IFDIR) 8662306a36Sopenharmony_ci return nfserr_notdir; 8762306a36Sopenharmony_ci if (mode == S_IFDIR) 8862306a36Sopenharmony_ci return nfserr_isdir; 8962306a36Sopenharmony_ci return nfserr_inval; 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic bool nfsd_originating_port_ok(struct svc_rqst *rqstp, int flags) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci if (flags & NFSEXP_INSECURE_PORT) 9562306a36Sopenharmony_ci return true; 9662306a36Sopenharmony_ci /* We don't require gss requests to use low ports: */ 9762306a36Sopenharmony_ci if (rqstp->rq_cred.cr_flavor >= RPC_AUTH_GSS) 9862306a36Sopenharmony_ci return true; 9962306a36Sopenharmony_ci return test_bit(RQ_SECURE, &rqstp->rq_flags); 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic __be32 nfsd_setuser_and_check_port(struct svc_rqst *rqstp, 10362306a36Sopenharmony_ci struct svc_export *exp) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci int flags = nfsexp_flags(rqstp, exp); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci /* Check if the request originated from a secure port. */ 10862306a36Sopenharmony_ci if (!nfsd_originating_port_ok(rqstp, flags)) { 10962306a36Sopenharmony_ci RPC_IFDEBUG(char buf[RPC_MAX_ADDRBUFLEN]); 11062306a36Sopenharmony_ci dprintk("nfsd: request from insecure port %s!\n", 11162306a36Sopenharmony_ci svc_print_addr(rqstp, buf, sizeof(buf))); 11262306a36Sopenharmony_ci return nfserr_perm; 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci /* Set user creds for this exportpoint */ 11662306a36Sopenharmony_ci return nfserrno(nfsd_setuser(rqstp, exp)); 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic inline __be32 check_pseudo_root(struct svc_rqst *rqstp, 12062306a36Sopenharmony_ci struct dentry *dentry, struct svc_export *exp) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci if (!(exp->ex_flags & NFSEXP_V4ROOT)) 12362306a36Sopenharmony_ci return nfs_ok; 12462306a36Sopenharmony_ci /* 12562306a36Sopenharmony_ci * v2/v3 clients have no need for the V4ROOT export--they use 12662306a36Sopenharmony_ci * the mount protocl instead; also, further V4ROOT checks may be 12762306a36Sopenharmony_ci * in v4-specific code, in which case v2/v3 clients could bypass 12862306a36Sopenharmony_ci * them. 12962306a36Sopenharmony_ci */ 13062306a36Sopenharmony_ci if (!nfsd_v4client(rqstp)) 13162306a36Sopenharmony_ci return nfserr_stale; 13262306a36Sopenharmony_ci /* 13362306a36Sopenharmony_ci * We're exposing only the directories and symlinks that have to be 13462306a36Sopenharmony_ci * traversed on the way to real exports: 13562306a36Sopenharmony_ci */ 13662306a36Sopenharmony_ci if (unlikely(!d_is_dir(dentry) && 13762306a36Sopenharmony_ci !d_is_symlink(dentry))) 13862306a36Sopenharmony_ci return nfserr_stale; 13962306a36Sopenharmony_ci /* 14062306a36Sopenharmony_ci * A pseudoroot export gives permission to access only one 14162306a36Sopenharmony_ci * single directory; the kernel has to make another upcall 14262306a36Sopenharmony_ci * before granting access to anything else under it: 14362306a36Sopenharmony_ci */ 14462306a36Sopenharmony_ci if (unlikely(dentry != exp->ex_path.dentry)) 14562306a36Sopenharmony_ci return nfserr_stale; 14662306a36Sopenharmony_ci return nfs_ok; 14762306a36Sopenharmony_ci} 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci/* 15062306a36Sopenharmony_ci * Use the given filehandle to look up the corresponding export and 15162306a36Sopenharmony_ci * dentry. On success, the results are used to set fh_export and 15262306a36Sopenharmony_ci * fh_dentry. 15362306a36Sopenharmony_ci */ 15462306a36Sopenharmony_cistatic __be32 nfsd_set_fh_dentry(struct svc_rqst *rqstp, struct svc_fh *fhp) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci struct knfsd_fh *fh = &fhp->fh_handle; 15762306a36Sopenharmony_ci struct fid *fid = NULL; 15862306a36Sopenharmony_ci struct svc_export *exp; 15962306a36Sopenharmony_ci struct dentry *dentry; 16062306a36Sopenharmony_ci int fileid_type; 16162306a36Sopenharmony_ci int data_left = fh->fh_size/4; 16262306a36Sopenharmony_ci int len; 16362306a36Sopenharmony_ci __be32 error; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci error = nfserr_stale; 16662306a36Sopenharmony_ci if (rqstp->rq_vers > 2) 16762306a36Sopenharmony_ci error = nfserr_badhandle; 16862306a36Sopenharmony_ci if (rqstp->rq_vers == 4 && fh->fh_size == 0) 16962306a36Sopenharmony_ci return nfserr_nofilehandle; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci if (fh->fh_version != 1) 17262306a36Sopenharmony_ci return error; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci if (--data_left < 0) 17562306a36Sopenharmony_ci return error; 17662306a36Sopenharmony_ci if (fh->fh_auth_type != 0) 17762306a36Sopenharmony_ci return error; 17862306a36Sopenharmony_ci len = key_len(fh->fh_fsid_type) / 4; 17962306a36Sopenharmony_ci if (len == 0) 18062306a36Sopenharmony_ci return error; 18162306a36Sopenharmony_ci if (fh->fh_fsid_type == FSID_MAJOR_MINOR) { 18262306a36Sopenharmony_ci /* deprecated, convert to type 3 */ 18362306a36Sopenharmony_ci len = key_len(FSID_ENCODE_DEV)/4; 18462306a36Sopenharmony_ci fh->fh_fsid_type = FSID_ENCODE_DEV; 18562306a36Sopenharmony_ci /* 18662306a36Sopenharmony_ci * struct knfsd_fh uses host-endian fields, which are 18762306a36Sopenharmony_ci * sometimes used to hold net-endian values. This 18862306a36Sopenharmony_ci * confuses sparse, so we must use __force here to 18962306a36Sopenharmony_ci * keep it from complaining. 19062306a36Sopenharmony_ci */ 19162306a36Sopenharmony_ci fh->fh_fsid[0] = new_encode_dev(MKDEV(ntohl((__force __be32)fh->fh_fsid[0]), 19262306a36Sopenharmony_ci ntohl((__force __be32)fh->fh_fsid[1]))); 19362306a36Sopenharmony_ci fh->fh_fsid[1] = fh->fh_fsid[2]; 19462306a36Sopenharmony_ci } 19562306a36Sopenharmony_ci data_left -= len; 19662306a36Sopenharmony_ci if (data_left < 0) 19762306a36Sopenharmony_ci return error; 19862306a36Sopenharmony_ci exp = rqst_exp_find(rqstp, fh->fh_fsid_type, fh->fh_fsid); 19962306a36Sopenharmony_ci fid = (struct fid *)(fh->fh_fsid + len); 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci error = nfserr_stale; 20262306a36Sopenharmony_ci if (IS_ERR(exp)) { 20362306a36Sopenharmony_ci trace_nfsd_set_fh_dentry_badexport(rqstp, fhp, PTR_ERR(exp)); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci if (PTR_ERR(exp) == -ENOENT) 20662306a36Sopenharmony_ci return error; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci return nfserrno(PTR_ERR(exp)); 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci if (exp->ex_flags & NFSEXP_NOSUBTREECHECK) { 21262306a36Sopenharmony_ci /* Elevate privileges so that the lack of 'r' or 'x' 21362306a36Sopenharmony_ci * permission on some parent directory will 21462306a36Sopenharmony_ci * not stop exportfs_decode_fh from being able 21562306a36Sopenharmony_ci * to reconnect a directory into the dentry cache. 21662306a36Sopenharmony_ci * The same problem can affect "SUBTREECHECK" exports, 21762306a36Sopenharmony_ci * but as nfsd_acceptable depends on correct 21862306a36Sopenharmony_ci * access control settings being in effect, we cannot 21962306a36Sopenharmony_ci * fix that case easily. 22062306a36Sopenharmony_ci */ 22162306a36Sopenharmony_ci struct cred *new = prepare_creds(); 22262306a36Sopenharmony_ci if (!new) { 22362306a36Sopenharmony_ci error = nfserrno(-ENOMEM); 22462306a36Sopenharmony_ci goto out; 22562306a36Sopenharmony_ci } 22662306a36Sopenharmony_ci new->cap_effective = 22762306a36Sopenharmony_ci cap_raise_nfsd_set(new->cap_effective, 22862306a36Sopenharmony_ci new->cap_permitted); 22962306a36Sopenharmony_ci put_cred(override_creds(new)); 23062306a36Sopenharmony_ci put_cred(new); 23162306a36Sopenharmony_ci } else { 23262306a36Sopenharmony_ci error = nfsd_setuser_and_check_port(rqstp, exp); 23362306a36Sopenharmony_ci if (error) 23462306a36Sopenharmony_ci goto out; 23562306a36Sopenharmony_ci } 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci /* 23862306a36Sopenharmony_ci * Look up the dentry using the NFS file handle. 23962306a36Sopenharmony_ci */ 24062306a36Sopenharmony_ci error = nfserr_stale; 24162306a36Sopenharmony_ci if (rqstp->rq_vers > 2) 24262306a36Sopenharmony_ci error = nfserr_badhandle; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci fileid_type = fh->fh_fileid_type; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci if (fileid_type == FILEID_ROOT) 24762306a36Sopenharmony_ci dentry = dget(exp->ex_path.dentry); 24862306a36Sopenharmony_ci else { 24962306a36Sopenharmony_ci dentry = exportfs_decode_fh_raw(exp->ex_path.mnt, fid, 25062306a36Sopenharmony_ci data_left, fileid_type, 25162306a36Sopenharmony_ci nfsd_acceptable, exp); 25262306a36Sopenharmony_ci if (IS_ERR_OR_NULL(dentry)) { 25362306a36Sopenharmony_ci trace_nfsd_set_fh_dentry_badhandle(rqstp, fhp, 25462306a36Sopenharmony_ci dentry ? PTR_ERR(dentry) : -ESTALE); 25562306a36Sopenharmony_ci switch (PTR_ERR(dentry)) { 25662306a36Sopenharmony_ci case -ENOMEM: 25762306a36Sopenharmony_ci case -ETIMEDOUT: 25862306a36Sopenharmony_ci break; 25962306a36Sopenharmony_ci default: 26062306a36Sopenharmony_ci dentry = ERR_PTR(-ESTALE); 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci } 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci if (dentry == NULL) 26562306a36Sopenharmony_ci goto out; 26662306a36Sopenharmony_ci if (IS_ERR(dentry)) { 26762306a36Sopenharmony_ci if (PTR_ERR(dentry) != -EINVAL) 26862306a36Sopenharmony_ci error = nfserrno(PTR_ERR(dentry)); 26962306a36Sopenharmony_ci goto out; 27062306a36Sopenharmony_ci } 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci if (d_is_dir(dentry) && 27362306a36Sopenharmony_ci (dentry->d_flags & DCACHE_DISCONNECTED)) { 27462306a36Sopenharmony_ci printk("nfsd: find_fh_dentry returned a DISCONNECTED directory: %pd2\n", 27562306a36Sopenharmony_ci dentry); 27662306a36Sopenharmony_ci } 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci fhp->fh_dentry = dentry; 27962306a36Sopenharmony_ci fhp->fh_export = exp; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci switch (rqstp->rq_vers) { 28262306a36Sopenharmony_ci case 4: 28362306a36Sopenharmony_ci if (dentry->d_sb->s_export_op->flags & EXPORT_OP_NOATOMIC_ATTR) 28462306a36Sopenharmony_ci fhp->fh_no_atomic_attr = true; 28562306a36Sopenharmony_ci break; 28662306a36Sopenharmony_ci case 3: 28762306a36Sopenharmony_ci if (dentry->d_sb->s_export_op->flags & EXPORT_OP_NOWCC) 28862306a36Sopenharmony_ci fhp->fh_no_wcc = true; 28962306a36Sopenharmony_ci break; 29062306a36Sopenharmony_ci case 2: 29162306a36Sopenharmony_ci fhp->fh_no_wcc = true; 29262306a36Sopenharmony_ci } 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci return 0; 29562306a36Sopenharmony_ciout: 29662306a36Sopenharmony_ci exp_put(exp); 29762306a36Sopenharmony_ci return error; 29862306a36Sopenharmony_ci} 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci/** 30162306a36Sopenharmony_ci * fh_verify - filehandle lookup and access checking 30262306a36Sopenharmony_ci * @rqstp: pointer to current rpc request 30362306a36Sopenharmony_ci * @fhp: filehandle to be verified 30462306a36Sopenharmony_ci * @type: expected type of object pointed to by filehandle 30562306a36Sopenharmony_ci * @access: type of access needed to object 30662306a36Sopenharmony_ci * 30762306a36Sopenharmony_ci * Look up a dentry from the on-the-wire filehandle, check the client's 30862306a36Sopenharmony_ci * access to the export, and set the current task's credentials. 30962306a36Sopenharmony_ci * 31062306a36Sopenharmony_ci * Regardless of success or failure of fh_verify(), fh_put() should be 31162306a36Sopenharmony_ci * called on @fhp when the caller is finished with the filehandle. 31262306a36Sopenharmony_ci * 31362306a36Sopenharmony_ci * fh_verify() may be called multiple times on a given filehandle, for 31462306a36Sopenharmony_ci * example, when processing an NFSv4 compound. The first call will look 31562306a36Sopenharmony_ci * up a dentry using the on-the-wire filehandle. Subsequent calls will 31662306a36Sopenharmony_ci * skip the lookup and just perform the other checks and possibly change 31762306a36Sopenharmony_ci * the current task's credentials. 31862306a36Sopenharmony_ci * 31962306a36Sopenharmony_ci * @type specifies the type of object expected using one of the S_IF* 32062306a36Sopenharmony_ci * constants defined in include/linux/stat.h. The caller may use zero 32162306a36Sopenharmony_ci * to indicate that it doesn't care, or a negative integer to indicate 32262306a36Sopenharmony_ci * that it expects something not of the given type. 32362306a36Sopenharmony_ci * 32462306a36Sopenharmony_ci * @access is formed from the NFSD_MAY_* constants defined in 32562306a36Sopenharmony_ci * fs/nfsd/vfs.h. 32662306a36Sopenharmony_ci */ 32762306a36Sopenharmony_ci__be32 32862306a36Sopenharmony_cifh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, umode_t type, int access) 32962306a36Sopenharmony_ci{ 33062306a36Sopenharmony_ci struct svc_export *exp = NULL; 33162306a36Sopenharmony_ci struct dentry *dentry; 33262306a36Sopenharmony_ci __be32 error; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci if (!fhp->fh_dentry) { 33562306a36Sopenharmony_ci error = nfsd_set_fh_dentry(rqstp, fhp); 33662306a36Sopenharmony_ci if (error) 33762306a36Sopenharmony_ci goto out; 33862306a36Sopenharmony_ci } 33962306a36Sopenharmony_ci dentry = fhp->fh_dentry; 34062306a36Sopenharmony_ci exp = fhp->fh_export; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci trace_nfsd_fh_verify(rqstp, fhp, type, access); 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci /* 34562306a36Sopenharmony_ci * We still have to do all these permission checks, even when 34662306a36Sopenharmony_ci * fh_dentry is already set: 34762306a36Sopenharmony_ci * - fh_verify may be called multiple times with different 34862306a36Sopenharmony_ci * "access" arguments (e.g. nfsd_proc_create calls 34962306a36Sopenharmony_ci * fh_verify(...,NFSD_MAY_EXEC) first, then later (in 35062306a36Sopenharmony_ci * nfsd_create) calls fh_verify(...,NFSD_MAY_CREATE). 35162306a36Sopenharmony_ci * - in the NFSv4 case, the filehandle may have been filled 35262306a36Sopenharmony_ci * in by fh_compose, and given a dentry, but further 35362306a36Sopenharmony_ci * compound operations performed with that filehandle 35462306a36Sopenharmony_ci * still need permissions checks. In the worst case, a 35562306a36Sopenharmony_ci * mountpoint crossing may have changed the export 35662306a36Sopenharmony_ci * options, and we may now need to use a different uid 35762306a36Sopenharmony_ci * (for example, if different id-squashing options are in 35862306a36Sopenharmony_ci * effect on the new filesystem). 35962306a36Sopenharmony_ci */ 36062306a36Sopenharmony_ci error = check_pseudo_root(rqstp, dentry, exp); 36162306a36Sopenharmony_ci if (error) 36262306a36Sopenharmony_ci goto out; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci error = nfsd_setuser_and_check_port(rqstp, exp); 36562306a36Sopenharmony_ci if (error) 36662306a36Sopenharmony_ci goto out; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci error = nfsd_mode_check(rqstp, dentry, type); 36962306a36Sopenharmony_ci if (error) 37062306a36Sopenharmony_ci goto out; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci /* 37362306a36Sopenharmony_ci * pseudoflavor restrictions are not enforced on NLM, 37462306a36Sopenharmony_ci * which clients virtually always use auth_sys for, 37562306a36Sopenharmony_ci * even while using RPCSEC_GSS for NFS. 37662306a36Sopenharmony_ci */ 37762306a36Sopenharmony_ci if (access & NFSD_MAY_LOCK || access & NFSD_MAY_BYPASS_GSS) 37862306a36Sopenharmony_ci goto skip_pseudoflavor_check; 37962306a36Sopenharmony_ci /* 38062306a36Sopenharmony_ci * Clients may expect to be able to use auth_sys during mount, 38162306a36Sopenharmony_ci * even if they use gss for everything else; see section 2.3.2 38262306a36Sopenharmony_ci * of rfc 2623. 38362306a36Sopenharmony_ci */ 38462306a36Sopenharmony_ci if (access & NFSD_MAY_BYPASS_GSS_ON_ROOT 38562306a36Sopenharmony_ci && exp->ex_path.dentry == dentry) 38662306a36Sopenharmony_ci goto skip_pseudoflavor_check; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci error = check_nfsd_access(exp, rqstp); 38962306a36Sopenharmony_ci if (error) 39062306a36Sopenharmony_ci goto out; 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ciskip_pseudoflavor_check: 39362306a36Sopenharmony_ci /* Finally, check access permissions. */ 39462306a36Sopenharmony_ci error = nfsd_permission(rqstp, exp, dentry, access); 39562306a36Sopenharmony_ciout: 39662306a36Sopenharmony_ci trace_nfsd_fh_verify_err(rqstp, fhp, type, access, error); 39762306a36Sopenharmony_ci if (error == nfserr_stale) 39862306a36Sopenharmony_ci nfsd_stats_fh_stale_inc(exp); 39962306a36Sopenharmony_ci return error; 40062306a36Sopenharmony_ci} 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci/* 40462306a36Sopenharmony_ci * Compose a file handle for an NFS reply. 40562306a36Sopenharmony_ci * 40662306a36Sopenharmony_ci * Note that when first composed, the dentry may not yet have 40762306a36Sopenharmony_ci * an inode. In this case a call to fh_update should be made 40862306a36Sopenharmony_ci * before the fh goes out on the wire ... 40962306a36Sopenharmony_ci */ 41062306a36Sopenharmony_cistatic void _fh_update(struct svc_fh *fhp, struct svc_export *exp, 41162306a36Sopenharmony_ci struct dentry *dentry) 41262306a36Sopenharmony_ci{ 41362306a36Sopenharmony_ci if (dentry != exp->ex_path.dentry) { 41462306a36Sopenharmony_ci struct fid *fid = (struct fid *) 41562306a36Sopenharmony_ci (fhp->fh_handle.fh_fsid + fhp->fh_handle.fh_size/4 - 1); 41662306a36Sopenharmony_ci int maxsize = (fhp->fh_maxsize - fhp->fh_handle.fh_size)/4; 41762306a36Sopenharmony_ci int fh_flags = (exp->ex_flags & NFSEXP_NOSUBTREECHECK) ? 0 : 41862306a36Sopenharmony_ci EXPORT_FH_CONNECTABLE; 41962306a36Sopenharmony_ci int fileid_type = 42062306a36Sopenharmony_ci exportfs_encode_fh(dentry, fid, &maxsize, fh_flags); 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci fhp->fh_handle.fh_fileid_type = 42362306a36Sopenharmony_ci fileid_type > 0 ? fileid_type : FILEID_INVALID; 42462306a36Sopenharmony_ci fhp->fh_handle.fh_size += maxsize * 4; 42562306a36Sopenharmony_ci } else { 42662306a36Sopenharmony_ci fhp->fh_handle.fh_fileid_type = FILEID_ROOT; 42762306a36Sopenharmony_ci } 42862306a36Sopenharmony_ci} 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_cistatic bool is_root_export(struct svc_export *exp) 43162306a36Sopenharmony_ci{ 43262306a36Sopenharmony_ci return exp->ex_path.dentry == exp->ex_path.dentry->d_sb->s_root; 43362306a36Sopenharmony_ci} 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_cistatic struct super_block *exp_sb(struct svc_export *exp) 43662306a36Sopenharmony_ci{ 43762306a36Sopenharmony_ci return exp->ex_path.dentry->d_sb; 43862306a36Sopenharmony_ci} 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_cistatic bool fsid_type_ok_for_exp(u8 fsid_type, struct svc_export *exp) 44162306a36Sopenharmony_ci{ 44262306a36Sopenharmony_ci switch (fsid_type) { 44362306a36Sopenharmony_ci case FSID_DEV: 44462306a36Sopenharmony_ci if (!old_valid_dev(exp_sb(exp)->s_dev)) 44562306a36Sopenharmony_ci return false; 44662306a36Sopenharmony_ci fallthrough; 44762306a36Sopenharmony_ci case FSID_MAJOR_MINOR: 44862306a36Sopenharmony_ci case FSID_ENCODE_DEV: 44962306a36Sopenharmony_ci return exp_sb(exp)->s_type->fs_flags & FS_REQUIRES_DEV; 45062306a36Sopenharmony_ci case FSID_NUM: 45162306a36Sopenharmony_ci return exp->ex_flags & NFSEXP_FSID; 45262306a36Sopenharmony_ci case FSID_UUID8: 45362306a36Sopenharmony_ci case FSID_UUID16: 45462306a36Sopenharmony_ci if (!is_root_export(exp)) 45562306a36Sopenharmony_ci return false; 45662306a36Sopenharmony_ci fallthrough; 45762306a36Sopenharmony_ci case FSID_UUID4_INUM: 45862306a36Sopenharmony_ci case FSID_UUID16_INUM: 45962306a36Sopenharmony_ci return exp->ex_uuid != NULL; 46062306a36Sopenharmony_ci } 46162306a36Sopenharmony_ci return true; 46262306a36Sopenharmony_ci} 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_cistatic void set_version_and_fsid_type(struct svc_fh *fhp, struct svc_export *exp, struct svc_fh *ref_fh) 46662306a36Sopenharmony_ci{ 46762306a36Sopenharmony_ci u8 version; 46862306a36Sopenharmony_ci u8 fsid_type; 46962306a36Sopenharmony_ciretry: 47062306a36Sopenharmony_ci version = 1; 47162306a36Sopenharmony_ci if (ref_fh && ref_fh->fh_export == exp) { 47262306a36Sopenharmony_ci version = ref_fh->fh_handle.fh_version; 47362306a36Sopenharmony_ci fsid_type = ref_fh->fh_handle.fh_fsid_type; 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci ref_fh = NULL; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci switch (version) { 47862306a36Sopenharmony_ci case 0xca: 47962306a36Sopenharmony_ci fsid_type = FSID_DEV; 48062306a36Sopenharmony_ci break; 48162306a36Sopenharmony_ci case 1: 48262306a36Sopenharmony_ci break; 48362306a36Sopenharmony_ci default: 48462306a36Sopenharmony_ci goto retry; 48562306a36Sopenharmony_ci } 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci /* 48862306a36Sopenharmony_ci * As the fsid -> filesystem mapping was guided by 48962306a36Sopenharmony_ci * user-space, there is no guarantee that the filesystem 49062306a36Sopenharmony_ci * actually supports that fsid type. If it doesn't we 49162306a36Sopenharmony_ci * loop around again without ref_fh set. 49262306a36Sopenharmony_ci */ 49362306a36Sopenharmony_ci if (!fsid_type_ok_for_exp(fsid_type, exp)) 49462306a36Sopenharmony_ci goto retry; 49562306a36Sopenharmony_ci } else if (exp->ex_flags & NFSEXP_FSID) { 49662306a36Sopenharmony_ci fsid_type = FSID_NUM; 49762306a36Sopenharmony_ci } else if (exp->ex_uuid) { 49862306a36Sopenharmony_ci if (fhp->fh_maxsize >= 64) { 49962306a36Sopenharmony_ci if (is_root_export(exp)) 50062306a36Sopenharmony_ci fsid_type = FSID_UUID16; 50162306a36Sopenharmony_ci else 50262306a36Sopenharmony_ci fsid_type = FSID_UUID16_INUM; 50362306a36Sopenharmony_ci } else { 50462306a36Sopenharmony_ci if (is_root_export(exp)) 50562306a36Sopenharmony_ci fsid_type = FSID_UUID8; 50662306a36Sopenharmony_ci else 50762306a36Sopenharmony_ci fsid_type = FSID_UUID4_INUM; 50862306a36Sopenharmony_ci } 50962306a36Sopenharmony_ci } else if (!old_valid_dev(exp_sb(exp)->s_dev)) 51062306a36Sopenharmony_ci /* for newer device numbers, we must use a newer fsid format */ 51162306a36Sopenharmony_ci fsid_type = FSID_ENCODE_DEV; 51262306a36Sopenharmony_ci else 51362306a36Sopenharmony_ci fsid_type = FSID_DEV; 51462306a36Sopenharmony_ci fhp->fh_handle.fh_version = version; 51562306a36Sopenharmony_ci if (version) 51662306a36Sopenharmony_ci fhp->fh_handle.fh_fsid_type = fsid_type; 51762306a36Sopenharmony_ci} 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci__be32 52062306a36Sopenharmony_cifh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry, 52162306a36Sopenharmony_ci struct svc_fh *ref_fh) 52262306a36Sopenharmony_ci{ 52362306a36Sopenharmony_ci /* ref_fh is a reference file handle. 52462306a36Sopenharmony_ci * if it is non-null and for the same filesystem, then we should compose 52562306a36Sopenharmony_ci * a filehandle which is of the same version, where possible. 52662306a36Sopenharmony_ci */ 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci struct inode * inode = d_inode(dentry); 52962306a36Sopenharmony_ci dev_t ex_dev = exp_sb(exp)->s_dev; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci dprintk("nfsd: fh_compose(exp %02x:%02x/%ld %pd2, ino=%ld)\n", 53262306a36Sopenharmony_ci MAJOR(ex_dev), MINOR(ex_dev), 53362306a36Sopenharmony_ci (long) d_inode(exp->ex_path.dentry)->i_ino, 53462306a36Sopenharmony_ci dentry, 53562306a36Sopenharmony_ci (inode ? inode->i_ino : 0)); 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci /* Choose filehandle version and fsid type based on 53862306a36Sopenharmony_ci * the reference filehandle (if it is in the same export) 53962306a36Sopenharmony_ci * or the export options. 54062306a36Sopenharmony_ci */ 54162306a36Sopenharmony_ci set_version_and_fsid_type(fhp, exp, ref_fh); 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci /* If we have a ref_fh, then copy the fh_no_wcc setting from it. */ 54462306a36Sopenharmony_ci fhp->fh_no_wcc = ref_fh ? ref_fh->fh_no_wcc : false; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci if (ref_fh == fhp) 54762306a36Sopenharmony_ci fh_put(ref_fh); 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci if (fhp->fh_dentry) { 55062306a36Sopenharmony_ci printk(KERN_ERR "fh_compose: fh %pd2 not initialized!\n", 55162306a36Sopenharmony_ci dentry); 55262306a36Sopenharmony_ci } 55362306a36Sopenharmony_ci if (fhp->fh_maxsize < NFS_FHSIZE) 55462306a36Sopenharmony_ci printk(KERN_ERR "fh_compose: called with maxsize %d! %pd2\n", 55562306a36Sopenharmony_ci fhp->fh_maxsize, 55662306a36Sopenharmony_ci dentry); 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci fhp->fh_dentry = dget(dentry); /* our internal copy */ 55962306a36Sopenharmony_ci fhp->fh_export = exp_get(exp); 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci fhp->fh_handle.fh_size = 56262306a36Sopenharmony_ci key_len(fhp->fh_handle.fh_fsid_type) + 4; 56362306a36Sopenharmony_ci fhp->fh_handle.fh_auth_type = 0; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci mk_fsid(fhp->fh_handle.fh_fsid_type, 56662306a36Sopenharmony_ci fhp->fh_handle.fh_fsid, 56762306a36Sopenharmony_ci ex_dev, 56862306a36Sopenharmony_ci d_inode(exp->ex_path.dentry)->i_ino, 56962306a36Sopenharmony_ci exp->ex_fsid, exp->ex_uuid); 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci if (inode) 57262306a36Sopenharmony_ci _fh_update(fhp, exp, dentry); 57362306a36Sopenharmony_ci if (fhp->fh_handle.fh_fileid_type == FILEID_INVALID) { 57462306a36Sopenharmony_ci fh_put(fhp); 57562306a36Sopenharmony_ci return nfserr_opnotsupp; 57662306a36Sopenharmony_ci } 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci return 0; 57962306a36Sopenharmony_ci} 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci/* 58262306a36Sopenharmony_ci * Update file handle information after changing a dentry. 58362306a36Sopenharmony_ci * This is only called by nfsd_create, nfsd_create_v3 and nfsd_proc_create 58462306a36Sopenharmony_ci */ 58562306a36Sopenharmony_ci__be32 58662306a36Sopenharmony_cifh_update(struct svc_fh *fhp) 58762306a36Sopenharmony_ci{ 58862306a36Sopenharmony_ci struct dentry *dentry; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci if (!fhp->fh_dentry) 59162306a36Sopenharmony_ci goto out_bad; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci dentry = fhp->fh_dentry; 59462306a36Sopenharmony_ci if (d_really_is_negative(dentry)) 59562306a36Sopenharmony_ci goto out_negative; 59662306a36Sopenharmony_ci if (fhp->fh_handle.fh_fileid_type != FILEID_ROOT) 59762306a36Sopenharmony_ci return 0; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci _fh_update(fhp, fhp->fh_export, dentry); 60062306a36Sopenharmony_ci if (fhp->fh_handle.fh_fileid_type == FILEID_INVALID) 60162306a36Sopenharmony_ci return nfserr_opnotsupp; 60262306a36Sopenharmony_ci return 0; 60362306a36Sopenharmony_ciout_bad: 60462306a36Sopenharmony_ci printk(KERN_ERR "fh_update: fh not verified!\n"); 60562306a36Sopenharmony_ci return nfserr_serverfault; 60662306a36Sopenharmony_ciout_negative: 60762306a36Sopenharmony_ci printk(KERN_ERR "fh_update: %pd2 still negative!\n", 60862306a36Sopenharmony_ci dentry); 60962306a36Sopenharmony_ci return nfserr_serverfault; 61062306a36Sopenharmony_ci} 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci/** 61362306a36Sopenharmony_ci * fh_fill_pre_attrs - Fill in pre-op attributes 61462306a36Sopenharmony_ci * @fhp: file handle to be updated 61562306a36Sopenharmony_ci * 61662306a36Sopenharmony_ci */ 61762306a36Sopenharmony_ci__be32 __must_check fh_fill_pre_attrs(struct svc_fh *fhp) 61862306a36Sopenharmony_ci{ 61962306a36Sopenharmony_ci bool v4 = (fhp->fh_maxsize == NFS4_FHSIZE); 62062306a36Sopenharmony_ci struct inode *inode; 62162306a36Sopenharmony_ci struct kstat stat; 62262306a36Sopenharmony_ci __be32 err; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci if (fhp->fh_no_wcc || fhp->fh_pre_saved) 62562306a36Sopenharmony_ci return nfs_ok; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci inode = d_inode(fhp->fh_dentry); 62862306a36Sopenharmony_ci err = fh_getattr(fhp, &stat); 62962306a36Sopenharmony_ci if (err) 63062306a36Sopenharmony_ci return err; 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci if (v4) 63362306a36Sopenharmony_ci fhp->fh_pre_change = nfsd4_change_attribute(&stat, inode); 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci fhp->fh_pre_mtime = stat.mtime; 63662306a36Sopenharmony_ci fhp->fh_pre_ctime = stat.ctime; 63762306a36Sopenharmony_ci fhp->fh_pre_size = stat.size; 63862306a36Sopenharmony_ci fhp->fh_pre_saved = true; 63962306a36Sopenharmony_ci return nfs_ok; 64062306a36Sopenharmony_ci} 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci/** 64362306a36Sopenharmony_ci * fh_fill_post_attrs - Fill in post-op attributes 64462306a36Sopenharmony_ci * @fhp: file handle to be updated 64562306a36Sopenharmony_ci * 64662306a36Sopenharmony_ci */ 64762306a36Sopenharmony_ci__be32 fh_fill_post_attrs(struct svc_fh *fhp) 64862306a36Sopenharmony_ci{ 64962306a36Sopenharmony_ci bool v4 = (fhp->fh_maxsize == NFS4_FHSIZE); 65062306a36Sopenharmony_ci struct inode *inode = d_inode(fhp->fh_dentry); 65162306a36Sopenharmony_ci __be32 err; 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci if (fhp->fh_no_wcc) 65462306a36Sopenharmony_ci return nfs_ok; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci if (fhp->fh_post_saved) 65762306a36Sopenharmony_ci printk("nfsd: inode locked twice during operation.\n"); 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci err = fh_getattr(fhp, &fhp->fh_post_attr); 66062306a36Sopenharmony_ci if (err) 66162306a36Sopenharmony_ci return err; 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci fhp->fh_post_saved = true; 66462306a36Sopenharmony_ci if (v4) 66562306a36Sopenharmony_ci fhp->fh_post_change = 66662306a36Sopenharmony_ci nfsd4_change_attribute(&fhp->fh_post_attr, inode); 66762306a36Sopenharmony_ci return nfs_ok; 66862306a36Sopenharmony_ci} 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci/** 67162306a36Sopenharmony_ci * fh_fill_both_attrs - Fill pre-op and post-op attributes 67262306a36Sopenharmony_ci * @fhp: file handle to be updated 67362306a36Sopenharmony_ci * 67462306a36Sopenharmony_ci * This is used when the directory wasn't changed, but wcc attributes 67562306a36Sopenharmony_ci * are needed anyway. 67662306a36Sopenharmony_ci */ 67762306a36Sopenharmony_ci__be32 __must_check fh_fill_both_attrs(struct svc_fh *fhp) 67862306a36Sopenharmony_ci{ 67962306a36Sopenharmony_ci __be32 err; 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci err = fh_fill_post_attrs(fhp); 68262306a36Sopenharmony_ci if (err) 68362306a36Sopenharmony_ci return err; 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci fhp->fh_pre_change = fhp->fh_post_change; 68662306a36Sopenharmony_ci fhp->fh_pre_mtime = fhp->fh_post_attr.mtime; 68762306a36Sopenharmony_ci fhp->fh_pre_ctime = fhp->fh_post_attr.ctime; 68862306a36Sopenharmony_ci fhp->fh_pre_size = fhp->fh_post_attr.size; 68962306a36Sopenharmony_ci fhp->fh_pre_saved = true; 69062306a36Sopenharmony_ci return nfs_ok; 69162306a36Sopenharmony_ci} 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci/* 69462306a36Sopenharmony_ci * Release a file handle. 69562306a36Sopenharmony_ci */ 69662306a36Sopenharmony_civoid 69762306a36Sopenharmony_cifh_put(struct svc_fh *fhp) 69862306a36Sopenharmony_ci{ 69962306a36Sopenharmony_ci struct dentry * dentry = fhp->fh_dentry; 70062306a36Sopenharmony_ci struct svc_export * exp = fhp->fh_export; 70162306a36Sopenharmony_ci if (dentry) { 70262306a36Sopenharmony_ci fhp->fh_dentry = NULL; 70362306a36Sopenharmony_ci dput(dentry); 70462306a36Sopenharmony_ci fh_clear_pre_post_attrs(fhp); 70562306a36Sopenharmony_ci } 70662306a36Sopenharmony_ci fh_drop_write(fhp); 70762306a36Sopenharmony_ci if (exp) { 70862306a36Sopenharmony_ci exp_put(exp); 70962306a36Sopenharmony_ci fhp->fh_export = NULL; 71062306a36Sopenharmony_ci } 71162306a36Sopenharmony_ci fhp->fh_no_wcc = false; 71262306a36Sopenharmony_ci return; 71362306a36Sopenharmony_ci} 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci/* 71662306a36Sopenharmony_ci * Shorthand for dprintk()'s 71762306a36Sopenharmony_ci */ 71862306a36Sopenharmony_cichar * SVCFH_fmt(struct svc_fh *fhp) 71962306a36Sopenharmony_ci{ 72062306a36Sopenharmony_ci struct knfsd_fh *fh = &fhp->fh_handle; 72162306a36Sopenharmony_ci static char buf[2+1+1+64*3+1]; 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci if (fh->fh_size < 0 || fh->fh_size> 64) 72462306a36Sopenharmony_ci return "bad-fh"; 72562306a36Sopenharmony_ci sprintf(buf, "%d: %*ph", fh->fh_size, fh->fh_size, fh->fh_raw); 72662306a36Sopenharmony_ci return buf; 72762306a36Sopenharmony_ci} 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_cienum fsid_source fsid_source(const struct svc_fh *fhp) 73062306a36Sopenharmony_ci{ 73162306a36Sopenharmony_ci if (fhp->fh_handle.fh_version != 1) 73262306a36Sopenharmony_ci return FSIDSOURCE_DEV; 73362306a36Sopenharmony_ci switch(fhp->fh_handle.fh_fsid_type) { 73462306a36Sopenharmony_ci case FSID_DEV: 73562306a36Sopenharmony_ci case FSID_ENCODE_DEV: 73662306a36Sopenharmony_ci case FSID_MAJOR_MINOR: 73762306a36Sopenharmony_ci if (exp_sb(fhp->fh_export)->s_type->fs_flags & FS_REQUIRES_DEV) 73862306a36Sopenharmony_ci return FSIDSOURCE_DEV; 73962306a36Sopenharmony_ci break; 74062306a36Sopenharmony_ci case FSID_NUM: 74162306a36Sopenharmony_ci if (fhp->fh_export->ex_flags & NFSEXP_FSID) 74262306a36Sopenharmony_ci return FSIDSOURCE_FSID; 74362306a36Sopenharmony_ci break; 74462306a36Sopenharmony_ci default: 74562306a36Sopenharmony_ci break; 74662306a36Sopenharmony_ci } 74762306a36Sopenharmony_ci /* either a UUID type filehandle, or the filehandle doesn't 74862306a36Sopenharmony_ci * match the export. 74962306a36Sopenharmony_ci */ 75062306a36Sopenharmony_ci if (fhp->fh_export->ex_flags & NFSEXP_FSID) 75162306a36Sopenharmony_ci return FSIDSOURCE_FSID; 75262306a36Sopenharmony_ci if (fhp->fh_export->ex_uuid) 75362306a36Sopenharmony_ci return FSIDSOURCE_UUID; 75462306a36Sopenharmony_ci return FSIDSOURCE_DEV; 75562306a36Sopenharmony_ci} 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci/* 75862306a36Sopenharmony_ci * We could use i_version alone as the change attribute. However, i_version 75962306a36Sopenharmony_ci * can go backwards on a regular file after an unclean shutdown. On its own 76062306a36Sopenharmony_ci * that doesn't necessarily cause a problem, but if i_version goes backwards 76162306a36Sopenharmony_ci * and then is incremented again it could reuse a value that was previously 76262306a36Sopenharmony_ci * used before boot, and a client who queried the two values might incorrectly 76362306a36Sopenharmony_ci * assume nothing changed. 76462306a36Sopenharmony_ci * 76562306a36Sopenharmony_ci * By using both ctime and the i_version counter we guarantee that as long as 76662306a36Sopenharmony_ci * time doesn't go backwards we never reuse an old value. If the filesystem 76762306a36Sopenharmony_ci * advertises STATX_ATTR_CHANGE_MONOTONIC, then this mitigation is not 76862306a36Sopenharmony_ci * needed. 76962306a36Sopenharmony_ci * 77062306a36Sopenharmony_ci * We only need to do this for regular files as well. For directories, we 77162306a36Sopenharmony_ci * assume that the new change attr is always logged to stable storage in some 77262306a36Sopenharmony_ci * fashion before the results can be seen. 77362306a36Sopenharmony_ci */ 77462306a36Sopenharmony_ciu64 nfsd4_change_attribute(struct kstat *stat, struct inode *inode) 77562306a36Sopenharmony_ci{ 77662306a36Sopenharmony_ci u64 chattr; 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci if (stat->result_mask & STATX_CHANGE_COOKIE) { 77962306a36Sopenharmony_ci chattr = stat->change_cookie; 78062306a36Sopenharmony_ci if (S_ISREG(inode->i_mode) && 78162306a36Sopenharmony_ci !(stat->attributes & STATX_ATTR_CHANGE_MONOTONIC)) { 78262306a36Sopenharmony_ci chattr += (u64)stat->ctime.tv_sec << 30; 78362306a36Sopenharmony_ci chattr += stat->ctime.tv_nsec; 78462306a36Sopenharmony_ci } 78562306a36Sopenharmony_ci } else { 78662306a36Sopenharmony_ci chattr = time_to_chattr(&stat->ctime); 78762306a36Sopenharmony_ci } 78862306a36Sopenharmony_ci return chattr; 78962306a36Sopenharmony_ci} 790