linux/fs/nfsd/nfs3acl.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Process version 3 NFSACL requests.
   4 *
   5 * Copyright (C) 2002-2003 Andreas Gruenbacher <agruen@suse.de>
   6 */
   7
   8#include "nfsd.h"
   9/* FIXME: nfsacl.h is a broken header */
  10#include <linux/nfsacl.h>
  11#include <linux/gfp.h>
  12#include "cache.h"
  13#include "xdr3.h"
  14#include "vfs.h"
  15
  16#define RETURN_STATUS(st)       { resp->status = (st); return (st); }
  17
  18/*
  19 * NULL call.
  20 */
  21static __be32
  22nfsd3_proc_null(struct svc_rqst *rqstp)
  23{
  24        return nfs_ok;
  25}
  26
  27/*
  28 * Get the Access and/or Default ACL of a file.
  29 */
  30static __be32 nfsd3_proc_getacl(struct svc_rqst *rqstp)
  31{
  32        struct nfsd3_getaclargs *argp = rqstp->rq_argp;
  33        struct nfsd3_getaclres *resp = rqstp->rq_resp;
  34        struct posix_acl *acl;
  35        struct inode *inode;
  36        svc_fh *fh;
  37        __be32 nfserr = 0;
  38
  39        fh = fh_copy(&resp->fh, &argp->fh);
  40        nfserr = fh_verify(rqstp, &resp->fh, 0, NFSD_MAY_NOP);
  41        if (nfserr)
  42                RETURN_STATUS(nfserr);
  43
  44        inode = d_inode(fh->fh_dentry);
  45
  46        if (argp->mask & ~NFS_ACL_MASK)
  47                RETURN_STATUS(nfserr_inval);
  48        resp->mask = argp->mask;
  49
  50        if (resp->mask & (NFS_ACL|NFS_ACLCNT)) {
  51                acl = get_acl(inode, ACL_TYPE_ACCESS);
  52                if (acl == NULL) {
  53                        /* Solaris returns the inode's minimum ACL. */
  54                        acl = posix_acl_from_mode(inode->i_mode, GFP_KERNEL);
  55                }
  56                if (IS_ERR(acl)) {
  57                        nfserr = nfserrno(PTR_ERR(acl));
  58                        goto fail;
  59                }
  60                resp->acl_access = acl;
  61        }
  62        if (resp->mask & (NFS_DFACL|NFS_DFACLCNT)) {
  63                /* Check how Solaris handles requests for the Default ACL
  64                   of a non-directory! */
  65                acl = get_acl(inode, ACL_TYPE_DEFAULT);
  66                if (IS_ERR(acl)) {
  67                        nfserr = nfserrno(PTR_ERR(acl));
  68                        goto fail;
  69                }
  70                resp->acl_default = acl;
  71        }
  72
  73        /* resp->acl_{access,default} are released in nfs3svc_release_getacl. */
  74        RETURN_STATUS(0);
  75
  76fail:
  77        posix_acl_release(resp->acl_access);
  78        posix_acl_release(resp->acl_default);
  79        RETURN_STATUS(nfserr);
  80}
  81
  82/*
  83 * Set the Access and/or Default ACL of a file.
  84 */
  85static __be32 nfsd3_proc_setacl(struct svc_rqst *rqstp)
  86{
  87        struct nfsd3_setaclargs *argp = rqstp->rq_argp;
  88        struct nfsd3_attrstat *resp = rqstp->rq_resp;
  89        struct inode *inode;
  90        svc_fh *fh;
  91        __be32 nfserr = 0;
  92        int error;
  93
  94        fh = fh_copy(&resp->fh, &argp->fh);
  95        nfserr = fh_verify(rqstp, &resp->fh, 0, NFSD_MAY_SATTR);
  96        if (nfserr)
  97                goto out;
  98
  99        inode = d_inode(fh->fh_dentry);
 100
 101        error = fh_want_write(fh);
 102        if (error)
 103                goto out_errno;
 104
 105        fh_lock(fh);
 106
 107        error = set_posix_acl(inode, ACL_TYPE_ACCESS, argp->acl_access);
 108        if (error)
 109                goto out_drop_lock;
 110        error = set_posix_acl(inode, ACL_TYPE_DEFAULT, argp->acl_default);
 111
 112out_drop_lock:
 113        fh_unlock(fh);
 114        fh_drop_write(fh);
 115out_errno:
 116        nfserr = nfserrno(error);
 117out:
 118        /* argp->acl_{access,default} may have been allocated in
 119           nfs3svc_decode_setaclargs. */
 120        posix_acl_release(argp->acl_access);
 121        posix_acl_release(argp->acl_default);
 122        RETURN_STATUS(nfserr);
 123}
 124
 125/*
 126 * XDR decode functions
 127 */
 128static int nfs3svc_decode_getaclargs(struct svc_rqst *rqstp, __be32 *p)
 129{
 130        struct nfsd3_getaclargs *args = rqstp->rq_argp;
 131
 132        p = nfs3svc_decode_fh(p, &args->fh);
 133        if (!p)
 134                return 0;
 135        args->mask = ntohl(*p); p++;
 136
 137        return xdr_argsize_check(rqstp, p);
 138}
 139
 140
 141static int nfs3svc_decode_setaclargs(struct svc_rqst *rqstp, __be32 *p)
 142{
 143        struct nfsd3_setaclargs *args = rqstp->rq_argp;
 144        struct kvec *head = rqstp->rq_arg.head;
 145        unsigned int base;
 146        int n;
 147
 148        p = nfs3svc_decode_fh(p, &args->fh);
 149        if (!p)
 150                return 0;
 151        args->mask = ntohl(*p++);
 152        if (args->mask & ~NFS_ACL_MASK ||
 153            !xdr_argsize_check(rqstp, p))
 154                return 0;
 155
 156        base = (char *)p - (char *)head->iov_base;
 157        n = nfsacl_decode(&rqstp->rq_arg, base, NULL,
 158                          (args->mask & NFS_ACL) ?
 159                          &args->acl_access : NULL);
 160        if (n > 0)
 161                n = nfsacl_decode(&rqstp->rq_arg, base + n, NULL,
 162                                  (args->mask & NFS_DFACL) ?
 163                                  &args->acl_default : NULL);
 164        return (n > 0);
 165}
 166
 167/*
 168 * XDR encode functions
 169 */
 170
 171/* GETACL */
 172static int nfs3svc_encode_getaclres(struct svc_rqst *rqstp, __be32 *p)
 173{
 174        struct nfsd3_getaclres *resp = rqstp->rq_resp;
 175        struct dentry *dentry = resp->fh.fh_dentry;
 176
 177        p = nfs3svc_encode_post_op_attr(rqstp, p, &resp->fh);
 178        if (resp->status == 0 && dentry && d_really_is_positive(dentry)) {
 179                struct inode *inode = d_inode(dentry);
 180                struct kvec *head = rqstp->rq_res.head;
 181                unsigned int base;
 182                int n;
 183                int w;
 184
 185                *p++ = htonl(resp->mask);
 186                if (!xdr_ressize_check(rqstp, p))
 187                        return 0;
 188                base = (char *)p - (char *)head->iov_base;
 189
 190                rqstp->rq_res.page_len = w = nfsacl_size(
 191                        (resp->mask & NFS_ACL)   ? resp->acl_access  : NULL,
 192                        (resp->mask & NFS_DFACL) ? resp->acl_default : NULL);
 193                while (w > 0) {
 194                        if (!*(rqstp->rq_next_page++))
 195                                return 0;
 196                        w -= PAGE_SIZE;
 197                }
 198
 199                n = nfsacl_encode(&rqstp->rq_res, base, inode,
 200                                  resp->acl_access,
 201                                  resp->mask & NFS_ACL, 0);
 202                if (n > 0)
 203                        n = nfsacl_encode(&rqstp->rq_res, base + n, inode,
 204                                          resp->acl_default,
 205                                          resp->mask & NFS_DFACL,
 206                                          NFS_ACL_DEFAULT);
 207                if (n <= 0)
 208                        return 0;
 209        } else
 210                if (!xdr_ressize_check(rqstp, p))
 211                        return 0;
 212
 213        return 1;
 214}
 215
 216/* SETACL */
 217static int nfs3svc_encode_setaclres(struct svc_rqst *rqstp, __be32 *p)
 218{
 219        struct nfsd3_attrstat *resp = rqstp->rq_resp;
 220
 221        p = nfs3svc_encode_post_op_attr(rqstp, p, &resp->fh);
 222
 223        return xdr_ressize_check(rqstp, p);
 224}
 225
 226/*
 227 * XDR release functions
 228 */
 229static void nfs3svc_release_getacl(struct svc_rqst *rqstp)
 230{
 231        struct nfsd3_getaclres *resp = rqstp->rq_resp;
 232
 233        fh_put(&resp->fh);
 234        posix_acl_release(resp->acl_access);
 235        posix_acl_release(resp->acl_default);
 236}
 237
 238#define nfs3svc_decode_voidargs         NULL
 239#define nfs3svc_release_void            NULL
 240#define nfsd3_setaclres                 nfsd3_attrstat
 241#define nfsd3_voidres                   nfsd3_voidargs
 242struct nfsd3_voidargs { int dummy; };
 243
 244#define PROC(name, argt, rest, relt, cache, respsize)                   \
 245{                                                                       \
 246        .pc_func        = nfsd3_proc_##name,                            \
 247        .pc_decode      = nfs3svc_decode_##argt##args,                  \
 248        .pc_encode      = nfs3svc_encode_##rest##res,                   \
 249        .pc_release     = nfs3svc_release_##relt,                       \
 250        .pc_argsize     = sizeof(struct nfsd3_##argt##args),            \
 251        .pc_ressize     = sizeof(struct nfsd3_##rest##res),             \
 252        .pc_cachetype   = cache,                                        \
 253        .pc_xdrressize  = respsize,                                     \
 254}
 255
 256#define ST 1            /* status*/
 257#define AT 21           /* attributes */
 258#define pAT (1+AT)      /* post attributes - conditional */
 259#define ACL (1+NFS_ACL_MAX_ENTRIES*3)  /* Access Control List */
 260
 261static const struct svc_procedure nfsd_acl_procedures3[] = {
 262  PROC(null,    void,           void,           void,     RC_NOCACHE, ST),
 263  PROC(getacl,  getacl,         getacl,         getacl,   RC_NOCACHE, ST+1+2*(1+ACL)),
 264  PROC(setacl,  setacl,         setacl,         fhandle,  RC_NOCACHE, ST+pAT),
 265};
 266
 267static unsigned int nfsd_acl_count3[ARRAY_SIZE(nfsd_acl_procedures3)];
 268const struct svc_version nfsd_acl_version3 = {
 269        .vs_vers        = 3,
 270        .vs_nproc       = 3,
 271        .vs_proc        = nfsd_acl_procedures3,
 272        .vs_count       = nfsd_acl_count3,
 273        .vs_dispatch    = nfsd_dispatch,
 274        .vs_xdrsize     = NFS3_SVC_XDRSIZE,
 275};
 276
 277