linux/fs/nfs/nfs3acl.c
<<
>>
Prefs
   1#include <linux/fs.h>
   2#include <linux/nfs.h>
   3#include <linux/nfs3.h>
   4#include <linux/nfs_fs.h>
   5#include <linux/posix_acl_xattr.h>
   6#include <linux/nfsacl.h>
   7
   8#include "internal.h"
   9
  10#define NFSDBG_FACILITY NFSDBG_PROC
  11
  12ssize_t nfs3_listxattr(struct dentry *dentry, char *buffer, size_t size)
  13{
  14        struct inode *inode = dentry->d_inode;
  15        struct posix_acl *acl;
  16        int pos=0, len=0;
  17
  18#       define output(s) do {                                           \
  19                        if (pos + sizeof(s) <= size) {                  \
  20                                memcpy(buffer + pos, s, sizeof(s));     \
  21                                pos += sizeof(s);                       \
  22                        }                                               \
  23                        len += sizeof(s);                               \
  24                } while(0)
  25
  26        acl = nfs3_proc_getacl(inode, ACL_TYPE_ACCESS);
  27        if (IS_ERR(acl))
  28                return PTR_ERR(acl);
  29        if (acl) {
  30                output("system.posix_acl_access");
  31                posix_acl_release(acl);
  32        }
  33
  34        if (S_ISDIR(inode->i_mode)) {
  35                acl = nfs3_proc_getacl(inode, ACL_TYPE_DEFAULT);
  36                if (IS_ERR(acl))
  37                        return PTR_ERR(acl);
  38                if (acl) {
  39                        output("system.posix_acl_default");
  40                        posix_acl_release(acl);
  41                }
  42        }
  43
  44#       undef output
  45
  46        if (!buffer || len <= size)
  47                return len;
  48        return -ERANGE;
  49}
  50
  51ssize_t nfs3_getxattr(struct dentry *dentry, const char *name,
  52                void *buffer, size_t size)
  53{
  54        struct inode *inode = dentry->d_inode;
  55        struct posix_acl *acl;
  56        int type, error = 0;
  57
  58        if (strcmp(name, POSIX_ACL_XATTR_ACCESS) == 0)
  59                type = ACL_TYPE_ACCESS;
  60        else if (strcmp(name, POSIX_ACL_XATTR_DEFAULT) == 0)
  61                type = ACL_TYPE_DEFAULT;
  62        else
  63                return -EOPNOTSUPP;
  64
  65        acl = nfs3_proc_getacl(inode, type);
  66        if (IS_ERR(acl))
  67                return PTR_ERR(acl);
  68        else if (acl) {
  69                if (type == ACL_TYPE_ACCESS && acl->a_count == 0)
  70                        error = -ENODATA;
  71                else
  72                        error = posix_acl_to_xattr(acl, buffer, size);
  73                posix_acl_release(acl);
  74        } else
  75                error = -ENODATA;
  76
  77        return error;
  78}
  79
  80int nfs3_setxattr(struct dentry *dentry, const char *name,
  81             const void *value, size_t size, int flags)
  82{
  83        struct inode *inode = dentry->d_inode;
  84        struct posix_acl *acl;
  85        int type, error;
  86
  87        if (strcmp(name, POSIX_ACL_XATTR_ACCESS) == 0)
  88                type = ACL_TYPE_ACCESS;
  89        else if (strcmp(name, POSIX_ACL_XATTR_DEFAULT) == 0)
  90                type = ACL_TYPE_DEFAULT;
  91        else
  92                return -EOPNOTSUPP;
  93
  94        acl = posix_acl_from_xattr(value, size);
  95        if (IS_ERR(acl))
  96                return PTR_ERR(acl);
  97        error = nfs3_proc_setacl(inode, type, acl);
  98        posix_acl_release(acl);
  99
 100        return error;
 101}
 102
 103int nfs3_removexattr(struct dentry *dentry, const char *name)
 104{
 105        struct inode *inode = dentry->d_inode;
 106        int type;
 107
 108        if (strcmp(name, POSIX_ACL_XATTR_ACCESS) == 0)
 109                type = ACL_TYPE_ACCESS;
 110        else if (strcmp(name, POSIX_ACL_XATTR_DEFAULT) == 0)
 111                type = ACL_TYPE_DEFAULT;
 112        else
 113                return -EOPNOTSUPP;
 114
 115        return nfs3_proc_setacl(inode, type, NULL);
 116}
 117
 118static void __nfs3_forget_cached_acls(struct nfs_inode *nfsi)
 119{
 120        if (!IS_ERR(nfsi->acl_access)) {
 121                posix_acl_release(nfsi->acl_access);
 122                nfsi->acl_access = ERR_PTR(-EAGAIN);
 123        }
 124        if (!IS_ERR(nfsi->acl_default)) {
 125                posix_acl_release(nfsi->acl_default);
 126                nfsi->acl_default = ERR_PTR(-EAGAIN);
 127        }
 128}
 129
 130void nfs3_forget_cached_acls(struct inode *inode)
 131{
 132        dprintk("NFS: nfs3_forget_cached_acls(%s/%ld)\n", inode->i_sb->s_id,
 133                inode->i_ino);
 134        spin_lock(&inode->i_lock);
 135        __nfs3_forget_cached_acls(NFS_I(inode));
 136        spin_unlock(&inode->i_lock);
 137}
 138
 139static struct posix_acl *nfs3_get_cached_acl(struct inode *inode, int type)
 140{
 141        struct nfs_inode *nfsi = NFS_I(inode);
 142        struct posix_acl *acl = ERR_PTR(-EINVAL);
 143
 144        spin_lock(&inode->i_lock);
 145        switch(type) {
 146                case ACL_TYPE_ACCESS:
 147                        acl = nfsi->acl_access;
 148                        break;
 149
 150                case ACL_TYPE_DEFAULT:
 151                        acl = nfsi->acl_default;
 152                        break;
 153
 154                default:
 155                        goto out;
 156        }
 157        if (IS_ERR(acl))
 158                acl = ERR_PTR(-EAGAIN);
 159        else
 160                acl = posix_acl_dup(acl);
 161out:
 162        spin_unlock(&inode->i_lock);
 163        dprintk("NFS: nfs3_get_cached_acl(%s/%ld, %d) = %p\n", inode->i_sb->s_id,
 164                inode->i_ino, type, acl);
 165        return acl;
 166}
 167
 168static void nfs3_cache_acls(struct inode *inode, struct posix_acl *acl,
 169                    struct posix_acl *dfacl)
 170{
 171        struct nfs_inode *nfsi = NFS_I(inode);
 172
 173        dprintk("nfs3_cache_acls(%s/%ld, %p, %p)\n", inode->i_sb->s_id,
 174                inode->i_ino, acl, dfacl);
 175        spin_lock(&inode->i_lock);
 176        __nfs3_forget_cached_acls(NFS_I(inode));
 177        if (!IS_ERR(acl))
 178                nfsi->acl_access = posix_acl_dup(acl);
 179        if (!IS_ERR(dfacl))
 180                nfsi->acl_default = posix_acl_dup(dfacl);
 181        spin_unlock(&inode->i_lock);
 182}
 183
 184struct posix_acl *nfs3_proc_getacl(struct inode *inode, int type)
 185{
 186        struct nfs_server *server = NFS_SERVER(inode);
 187        struct nfs_fattr fattr;
 188        struct page *pages[NFSACL_MAXPAGES] = { };
 189        struct nfs3_getaclargs args = {
 190                .fh = NFS_FH(inode),
 191                /* The xdr layer may allocate pages here. */
 192                .pages = pages,
 193        };
 194        struct nfs3_getaclres res = {
 195                .fattr =        &fattr,
 196        };
 197        struct rpc_message msg = {
 198                .rpc_argp       = &args,
 199                .rpc_resp       = &res,
 200        };
 201        struct posix_acl *acl;
 202        int status, count;
 203
 204        if (!nfs_server_capable(inode, NFS_CAP_ACLS))
 205                return ERR_PTR(-EOPNOTSUPP);
 206
 207        status = nfs_revalidate_inode(server, inode);
 208        if (status < 0)
 209                return ERR_PTR(status);
 210        if (NFS_I(inode)->cache_validity & NFS_INO_INVALID_ACL)
 211                nfs_zap_acl_cache(inode);
 212        acl = nfs3_get_cached_acl(inode, type);
 213        if (acl != ERR_PTR(-EAGAIN))
 214                return acl;
 215        acl = NULL;
 216
 217        /*
 218         * Only get the access acl when explicitly requested: We don't
 219         * need it for access decisions, and only some applications use
 220         * it. Applications which request the access acl first are not
 221         * penalized from this optimization.
 222         */
 223        if (type == ACL_TYPE_ACCESS)
 224                args.mask |= NFS_ACLCNT|NFS_ACL;
 225        if (S_ISDIR(inode->i_mode))
 226                args.mask |= NFS_DFACLCNT|NFS_DFACL;
 227        if (args.mask == 0)
 228                return NULL;
 229
 230        dprintk("NFS call getacl\n");
 231        msg.rpc_proc = &server->client_acl->cl_procinfo[ACLPROC3_GETACL];
 232        status = rpc_call_sync(server->client_acl, &msg, 0);
 233        dprintk("NFS reply getacl: %d\n", status);
 234
 235        /* pages may have been allocated at the xdr layer. */
 236        for (count = 0; count < NFSACL_MAXPAGES && args.pages[count]; count++)
 237                __free_page(args.pages[count]);
 238
 239        switch (status) {
 240                case 0:
 241                        status = nfs_refresh_inode(inode, &fattr);
 242                        break;
 243                case -EPFNOSUPPORT:
 244                case -EPROTONOSUPPORT:
 245                        dprintk("NFS_V3_ACL extension not supported; disabling\n");
 246                        server->caps &= ~NFS_CAP_ACLS;
 247                case -ENOTSUPP:
 248                        status = -EOPNOTSUPP;
 249                default:
 250                        goto getout;
 251        }
 252        if ((args.mask & res.mask) != args.mask) {
 253                status = -EIO;
 254                goto getout;
 255        }
 256
 257        if (res.acl_access != NULL) {
 258                if (posix_acl_equiv_mode(res.acl_access, NULL) == 0) {
 259                        posix_acl_release(res.acl_access);
 260                        res.acl_access = NULL;
 261                }
 262        }
 263        nfs3_cache_acls(inode,
 264                (res.mask & NFS_ACL)   ? res.acl_access  : ERR_PTR(-EINVAL),
 265                (res.mask & NFS_DFACL) ? res.acl_default : ERR_PTR(-EINVAL));
 266
 267        switch(type) {
 268                case ACL_TYPE_ACCESS:
 269                        acl = res.acl_access;
 270                        res.acl_access = NULL;
 271                        break;
 272
 273                case ACL_TYPE_DEFAULT:
 274                        acl = res.acl_default;
 275                        res.acl_default = NULL;
 276        }
 277
 278getout:
 279        posix_acl_release(res.acl_access);
 280        posix_acl_release(res.acl_default);
 281
 282        if (status != 0) {
 283                posix_acl_release(acl);
 284                acl = ERR_PTR(status);
 285        }
 286        return acl;
 287}
 288
 289static int nfs3_proc_setacls(struct inode *inode, struct posix_acl *acl,
 290                  struct posix_acl *dfacl)
 291{
 292        struct nfs_server *server = NFS_SERVER(inode);
 293        struct nfs_fattr fattr;
 294        struct page *pages[NFSACL_MAXPAGES] = { };
 295        struct nfs3_setaclargs args = {
 296                .inode = inode,
 297                .mask = NFS_ACL,
 298                .acl_access = acl,
 299                .pages = pages,
 300        };
 301        struct rpc_message msg = {
 302                .rpc_argp       = &args,
 303                .rpc_resp       = &fattr,
 304        };
 305        int status, count;
 306
 307        status = -EOPNOTSUPP;
 308        if (!nfs_server_capable(inode, NFS_CAP_ACLS))
 309                goto out;
 310
 311        /* We are doing this here, because XDR marshalling can only
 312           return -ENOMEM. */
 313        status = -ENOSPC;
 314        if (acl != NULL && acl->a_count > NFS_ACL_MAX_ENTRIES)
 315                goto out;
 316        if (dfacl != NULL && dfacl->a_count > NFS_ACL_MAX_ENTRIES)
 317                goto out;
 318        if (S_ISDIR(inode->i_mode)) {
 319                args.mask |= NFS_DFACL;
 320                args.acl_default = dfacl;
 321        }
 322
 323        dprintk("NFS call setacl\n");
 324        msg.rpc_proc = &server->client_acl->cl_procinfo[ACLPROC3_SETACL];
 325        status = rpc_call_sync(server->client_acl, &msg, 0);
 326        nfs_access_zap_cache(inode);
 327        nfs_zap_acl_cache(inode);
 328        dprintk("NFS reply setacl: %d\n", status);
 329
 330        /* pages may have been allocated at the xdr layer. */
 331        for (count = 0; count < NFSACL_MAXPAGES && args.pages[count]; count++)
 332                __free_page(args.pages[count]);
 333
 334        switch (status) {
 335                case 0:
 336                        status = nfs_refresh_inode(inode, &fattr);
 337                        nfs3_cache_acls(inode, acl, dfacl);
 338                        break;
 339                case -EPFNOSUPPORT:
 340                case -EPROTONOSUPPORT:
 341                        dprintk("NFS_V3_ACL SETACL RPC not supported"
 342                                        "(will not retry)\n");
 343                        server->caps &= ~NFS_CAP_ACLS;
 344                case -ENOTSUPP:
 345                        status = -EOPNOTSUPP;
 346        }
 347out:
 348        return status;
 349}
 350
 351int nfs3_proc_setacl(struct inode *inode, int type, struct posix_acl *acl)
 352{
 353        struct posix_acl *alloc = NULL, *dfacl = NULL;
 354        int status;
 355
 356        if (S_ISDIR(inode->i_mode)) {
 357                switch(type) {
 358                        case ACL_TYPE_ACCESS:
 359                                alloc = dfacl = nfs3_proc_getacl(inode,
 360                                                ACL_TYPE_DEFAULT);
 361                                if (IS_ERR(alloc))
 362                                        goto fail;
 363                                break;
 364
 365                        case ACL_TYPE_DEFAULT:
 366                                dfacl = acl;
 367                                alloc = acl = nfs3_proc_getacl(inode,
 368                                                ACL_TYPE_ACCESS);
 369                                if (IS_ERR(alloc))
 370                                        goto fail;
 371                                break;
 372
 373                        default:
 374                                return -EINVAL;
 375                }
 376        } else if (type != ACL_TYPE_ACCESS)
 377                        return -EINVAL;
 378
 379        if (acl == NULL) {
 380                alloc = acl = posix_acl_from_mode(inode->i_mode, GFP_KERNEL);
 381                if (IS_ERR(alloc))
 382                        goto fail;
 383        }
 384        status = nfs3_proc_setacls(inode, acl, dfacl);
 385        posix_acl_release(alloc);
 386        return status;
 387
 388fail:
 389        return PTR_ERR(alloc);
 390}
 391
 392int nfs3_proc_set_default_acl(struct inode *dir, struct inode *inode,
 393                mode_t mode)
 394{
 395        struct posix_acl *dfacl, *acl;
 396        int error = 0;
 397
 398        dfacl = nfs3_proc_getacl(dir, ACL_TYPE_DEFAULT);
 399        if (IS_ERR(dfacl)) {
 400                error = PTR_ERR(dfacl);
 401                return (error == -EOPNOTSUPP) ? 0 : error;
 402        }
 403        if (!dfacl)
 404                return 0;
 405        acl = posix_acl_clone(dfacl, GFP_KERNEL);
 406        error = -ENOMEM;
 407        if (!acl)
 408                goto out_release_dfacl;
 409        error = posix_acl_create_masq(acl, &mode);
 410        if (error < 0)
 411                goto out_release_acl;
 412        error = nfs3_proc_setacls(inode, acl, S_ISDIR(inode->i_mode) ?
 413                                                      dfacl : NULL);
 414out_release_acl:
 415        posix_acl_release(acl);
 416out_release_dfacl:
 417        posix_acl_release(dfacl);
 418        return error;
 419}
 420
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.