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/*
  17 * NULL call.
  18 */
  19static __be32
  20nfsd3_proc_null(struct svc_rqst *rqstp)
  21{
  22        return rpc_success;
  23}
  24
  25/*
  26 * Get the Access and/or Default ACL of a file.
  27 */
  28static __be32 nfsd3_proc_getacl(struct svc_rqst *rqstp)
  29{
  30        struct nfsd3_getaclargs *argp = rqstp->rq_argp;
  31        struct nfsd3_getaclres *resp = rqstp->rq_resp;
  32        struct posix_acl *acl;
  33        struct inode *inode;
  34        svc_fh *fh;
  35
  36        fh = fh_copy(&resp->fh, &argp->fh);
  37        resp->status = fh_verify(rqstp, &resp->fh, 0, NFSD_MAY_NOP);
  38        if (resp->status != nfs_ok)
  39                goto out;
  40
  41        inode = d_inode(fh->fh_dentry);
  42
  43        if (argp->mask & ~NFS_ACL_MASK) {
  44                resp->status = nfserr_inval;
  45                goto out;
  46        }
  47        resp->mask = argp->mask;
  48
  49        if (resp->mask & (NFS_ACL|NFS_ACLCNT)) {
  50                acl = get_acl(inode, ACL_TYPE_ACCESS);
  51                if (acl == NULL) {
  52                        /* Solaris returns the inode's minimum ACL. */
  53                        acl = posix_acl_from_mode(inode->i_mode, GFP_KERNEL);
  54                }
  55                if (IS_ERR(acl)) {
  56                        resp->status = nfserrno(PTR_ERR(acl));
  57                        goto fail;
  58                }
  59                resp->acl_access = acl;
  60        }
  61        if (resp->mask & (NFS_DFACL|NFS_DFACLCNT)) {
  62                /* Check how Solaris handles requests for the Default ACL
  63                   of a non-directory! */
  64                acl = get_acl(inode, ACL_TYPE_DEFAULT);
  65                if (IS_ERR(acl)) {
  66                        resp->status = nfserrno(PTR_ERR(acl));
  67                        goto fail;
  68                }
  69                resp->acl_default = acl;
  70        }
  71
  72        /* resp->acl_{access,default} are released in nfs3svc_release_getacl. */
  73out:
  74        return rpc_success;
  75
  76fail:
  77        posix_acl_release(resp->acl_access);
  78        posix_acl_release(resp->acl_default);
  79        goto out;
  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        int error;
  92
  93        fh = fh_copy(&resp->fh, &argp->fh);
  94        resp->status = fh_verify(rqstp, &resp->fh, 0, NFSD_MAY_SATTR);
  95        if (resp->status != nfs_ok)
  96                goto out;
  97
  98        inode = d_inode(fh->fh_dentry);
  99
 100        error = fh_want_write(fh);
 101        if (error)
 102                goto out_errno;
 103
 104        fh_lock(fh);
 105
 106        error = set_posix_acl(inode, ACL_TYPE_ACCESS, argp->acl_access);
 107        if (error)
 108                goto out_drop_lock;
 109        error = set_posix_acl(inode, ACL_TYPE_DEFAULT, argp->acl_default);
 110
 111out_drop_lock:
 112        fh_unlock(fh);
 113        fh_drop_write(fh);
 114out_errno:
 115        resp->status = nfserrno(error);
 116out:
 117        /* argp->acl_{access,default} may have been allocated in
 118           nfs3svc_decode_setaclargs. */
 119        posix_acl_release(argp->acl_access);
 120        posix_acl_release(argp->acl_default);
 121        return rpc_success;
 122}
 123
 124/*
 125 * XDR decode functions
 126 */
 127static int nfs3svc_decode_getaclargs(struct svc_rqst *rqstp, __be32 *p)
 128{
 129        struct nfsd3_getaclargs *args = rqstp->rq_argp;
 130
 131        p = nfs3svc_decode_fh(p, &args->fh);
 132        if (!p)
 133                return 0;
 134        args->mask = ntohl(*p); p++;
 135
 136        return xdr_argsize_check(rqstp, p);
 137}
 138
 139
 140static int nfs3svc_decode_setaclargs(struct svc_rqst *rqstp, __be32 *p)
 141{
 142        struct nfsd3_setaclargs *args = rqstp->rq_argp;
 143        struct kvec *head = rqstp->rq_arg.head;
 144        unsigned int base;
 145        int n;
 146
 147        p = nfs3svc_decode_fh(p, &args->fh);
 148        if (!p)
 149                return 0;
 150        args->mask = ntohl(*p++);
 151        if (args->mask & ~NFS_ACL_MASK ||
 152            !xdr_argsize_check(rqstp, p))
 153                return 0;
 154
 155        base = (char *)p - (char *)head->iov_base;
 156        n = nfsacl_decode(&rqstp->rq_arg, base, NULL,
 157                          (args->mask & NFS_ACL) ?
 158                          &args->acl_access : NULL);
 159        if (n > 0)
 160                n = nfsacl_decode(&rqstp->rq_arg, base + n, NULL,
 161                                  (args->mask & NFS_DFACL) ?
 162                                  &args->acl_default : NULL);
 163        return (n > 0);
 164}
 165
 166/*
 167 * XDR encode functions
 168 */
 169
 170/* GETACL */
 171static int nfs3svc_encode_getaclres(struct svc_rqst *rqstp, __be32 *p)
 172{
 173        struct nfsd3_getaclres *resp = rqstp->rq_resp;
 174        struct dentry *dentry = resp->fh.fh_dentry;
 175
 176        *p++ = resp->status;
 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++ = resp->status;
 222        p = nfs3svc_encode_post_op_attr(rqstp, p, &resp->fh);
 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
 238struct nfsd3_voidargs { int dummy; };
 239
 240#define ST 1            /* status*/
 241#define AT 21           /* attributes */
 242#define pAT (1+AT)      /* post attributes - conditional */
 243#define ACL (1+NFS_ACL_MAX_ENTRIES*3)  /* Access Control List */
 244
 245static const struct svc_procedure nfsd_acl_procedures3[3] = {
 246        [ACLPROC3_NULL] = {
 247                .pc_func = nfsd3_proc_null,
 248                .pc_decode = nfs3svc_decode_voidarg,
 249                .pc_encode = nfs3svc_encode_voidres,
 250                .pc_argsize = sizeof(struct nfsd3_voidargs),
 251                .pc_ressize = sizeof(struct nfsd3_voidargs),
 252                .pc_cachetype = RC_NOCACHE,
 253                .pc_xdrressize = ST,
 254        },
 255        [ACLPROC3_GETACL] = {
 256                .pc_func = nfsd3_proc_getacl,
 257                .pc_decode = nfs3svc_decode_getaclargs,
 258                .pc_encode = nfs3svc_encode_getaclres,
 259                .pc_release = nfs3svc_release_getacl,
 260                .pc_argsize = sizeof(struct nfsd3_getaclargs),
 261                .pc_ressize = sizeof(struct nfsd3_getaclres),
 262                .pc_cachetype = RC_NOCACHE,
 263                .pc_xdrressize = ST+1+2*(1+ACL),
 264        },
 265        [ACLPROC3_SETACL] = {
 266                .pc_func = nfsd3_proc_setacl,
 267                .pc_decode = nfs3svc_decode_setaclargs,
 268                .pc_encode = nfs3svc_encode_setaclres,
 269                .pc_release = nfs3svc_release_fhandle,
 270                .pc_argsize = sizeof(struct nfsd3_setaclargs),
 271                .pc_ressize = sizeof(struct nfsd3_attrstat),
 272                .pc_cachetype = RC_NOCACHE,
 273                .pc_xdrressize = ST+pAT,
 274        },
 275};
 276
 277static unsigned int nfsd_acl_count3[ARRAY_SIZE(nfsd_acl_procedures3)];
 278const struct svc_version nfsd_acl_version3 = {
 279        .vs_vers        = 3,
 280        .vs_nproc       = 3,
 281        .vs_proc        = nfsd_acl_procedures3,
 282        .vs_count       = nfsd_acl_count3,
 283        .vs_dispatch    = nfsd_dispatch,
 284        .vs_xdrsize     = NFS3_SVC_XDRSIZE,
 285};
 286
 287