linux/drivers/staging/lustre/lustre/obdclass/acl.c
<<
>>
Prefs
   1/*
   2 * GPL HEADER START
   3 *
   4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License version 2 only,
   8 * as published by the Free Software Foundation.
   9 *
  10 * This program is distributed in the hope that it will be useful, but
  11 * WITHOUT ANY WARRANTY; without even the implied warranty of
  12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  13 * General Public License version 2 for more details (a copy is included
  14 * in the LICENSE file that accompanied this code).
  15 *
  16 * You should have received a copy of the GNU General Public License
  17 * version 2 along with this program; If not, see
  18 * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
  19 *
  20 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
  21 * CA 95054 USA or visit www.sun.com if you need additional information or
  22 * have any questions.
  23 *
  24 * GPL HEADER END
  25 */
  26/*
  27 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
  28 * Use is subject to license terms.
  29 *
  30 * Copyright (c) 2012, Intel Corporation.
  31 */
  32/*
  33 * This file is part of Lustre, http://www.lustre.org/
  34 * Lustre is a trademark of Sun Microsystems, Inc.
  35 *
  36 * lustre/obdclass/acl.c
  37 *
  38 * Lustre Access Control List.
  39 *
  40 * Author: Fan Yong <fanyong@clusterfs.com>
  41 */
  42
  43#define DEBUG_SUBSYSTEM S_SEC
  44#include <lu_object.h>
  45#include <lustre_acl.h>
  46#include <lustre_eacl.h>
  47#include <obd_support.h>
  48
  49#ifdef CONFIG_FS_POSIX_ACL
  50
  51#define CFS_ACL_XATTR_VERSION POSIX_ACL_XATTR_VERSION
  52
  53enum {
  54        ES_UNK  = 0,    /* unknown stat */
  55        ES_UNC  = 1,    /* ACL entry is not changed */
  56        ES_MOD  = 2,    /* ACL entry is modified */
  57        ES_ADD  = 3,    /* ACL entry is added */
  58        ES_DEL  = 4     /* ACL entry is deleted */
  59};
  60
  61static inline void lustre_ext_acl_le_to_cpu(ext_acl_xattr_entry *d,
  62                                            ext_acl_xattr_entry *s)
  63{
  64        d->e_tag        = le16_to_cpu(s->e_tag);
  65        d->e_perm       = le16_to_cpu(s->e_perm);
  66        d->e_id  = le32_to_cpu(s->e_id);
  67        d->e_stat       = le32_to_cpu(s->e_stat);
  68}
  69
  70static inline void lustre_ext_acl_cpu_to_le(ext_acl_xattr_entry *d,
  71                                            ext_acl_xattr_entry *s)
  72{
  73        d->e_tag        = cpu_to_le16(s->e_tag);
  74        d->e_perm       = cpu_to_le16(s->e_perm);
  75        d->e_id  = cpu_to_le32(s->e_id);
  76        d->e_stat       = cpu_to_le32(s->e_stat);
  77}
  78
  79static inline void lustre_posix_acl_le_to_cpu(posix_acl_xattr_entry *d,
  80                                              posix_acl_xattr_entry *s)
  81{
  82        d->e_tag        = le16_to_cpu(s->e_tag);
  83        d->e_perm       = le16_to_cpu(s->e_perm);
  84        d->e_id  = le32_to_cpu(s->e_id);
  85}
  86
  87static inline void lustre_posix_acl_cpu_to_le(posix_acl_xattr_entry *d,
  88                                              posix_acl_xattr_entry *s)
  89{
  90        d->e_tag        = cpu_to_le16(s->e_tag);
  91        d->e_perm       = cpu_to_le16(s->e_perm);
  92        d->e_id  = cpu_to_le32(s->e_id);
  93}
  94
  95
  96/* if "new_count == 0", then "new = {a_version, NULL}", NOT NULL. */
  97static int lustre_posix_acl_xattr_reduce_space(posix_acl_xattr_header **header,
  98                                               int old_count, int new_count)
  99{
 100        int old_size = CFS_ACL_XATTR_SIZE(old_count, posix_acl_xattr);
 101        int new_size = CFS_ACL_XATTR_SIZE(new_count, posix_acl_xattr);
 102        posix_acl_xattr_header *new;
 103
 104        if (unlikely(old_count <= new_count))
 105                return old_size;
 106
 107        OBD_ALLOC(new, new_size);
 108        if (unlikely(new == NULL))
 109                return -ENOMEM;
 110
 111        memcpy(new, *header, new_size);
 112        OBD_FREE(*header, old_size);
 113        *header = new;
 114        return new_size;
 115}
 116
 117/* if "new_count == 0", then "new = {0, NULL}", NOT NULL. */
 118static int lustre_ext_acl_xattr_reduce_space(ext_acl_xattr_header **header,
 119                                             int old_count)
 120{
 121        int ext_count = le32_to_cpu((*header)->a_count);
 122        int ext_size = CFS_ACL_XATTR_SIZE(ext_count, ext_acl_xattr);
 123        int old_size = CFS_ACL_XATTR_SIZE(old_count, ext_acl_xattr);
 124        ext_acl_xattr_header *new;
 125
 126        if (unlikely(old_count <= ext_count))
 127                return 0;
 128
 129        OBD_ALLOC(new, ext_size);
 130        if (unlikely(new == NULL))
 131                return -ENOMEM;
 132
 133        memcpy(new, *header, ext_size);
 134        OBD_FREE(*header, old_size);
 135        *header = new;
 136        return 0;
 137}
 138
 139/*
 140 * Generate new extended ACL based on the posix ACL.
 141 */
 142ext_acl_xattr_header *
 143lustre_posix_acl_xattr_2ext(posix_acl_xattr_header *header, int size)
 144{
 145        int count, i, esize;
 146        ext_acl_xattr_header *new;
 147        ENTRY;
 148
 149        if (unlikely(size < 0))
 150                RETURN(ERR_PTR(-EINVAL));
 151        else if (!size)
 152                count = 0;
 153        else
 154                count = CFS_ACL_XATTR_COUNT(size, posix_acl_xattr);
 155        esize = CFS_ACL_XATTR_SIZE(count, ext_acl_xattr);
 156        OBD_ALLOC(new, esize);
 157        if (unlikely(new == NULL))
 158                RETURN(ERR_PTR(-ENOMEM));
 159
 160        new->a_count = cpu_to_le32(count);
 161        for (i = 0; i < count; i++) {
 162                new->a_entries[i].e_tag  = header->a_entries[i].e_tag;
 163                new->a_entries[i].e_perm = header->a_entries[i].e_perm;
 164                new->a_entries[i].e_id   = header->a_entries[i].e_id;
 165                new->a_entries[i].e_stat = cpu_to_le32(ES_UNK);
 166        }
 167
 168        RETURN(new);
 169}
 170EXPORT_SYMBOL(lustre_posix_acl_xattr_2ext);
 171
 172/*
 173 * Filter out the "nobody" entries in the posix ACL.
 174 */
 175int lustre_posix_acl_xattr_filter(posix_acl_xattr_header *header, int size,
 176                                  posix_acl_xattr_header **out)
 177{
 178        int count, i, j, rc = 0;
 179        __u32 id;
 180        posix_acl_xattr_header *new;
 181        ENTRY;
 182
 183        if (unlikely(size < 0))
 184                RETURN(-EINVAL);
 185        else if (!size)
 186                RETURN(0);
 187
 188        OBD_ALLOC(new, size);
 189        if (unlikely(new == NULL))
 190                RETURN(-ENOMEM);
 191
 192        new->a_version = cpu_to_le32(CFS_ACL_XATTR_VERSION);
 193        count = CFS_ACL_XATTR_COUNT(size, posix_acl_xattr);
 194        for (i = 0, j = 0; i < count; i++) {
 195                id = le32_to_cpu(header->a_entries[i].e_id);
 196                switch (le16_to_cpu(header->a_entries[i].e_tag)) {
 197                case ACL_USER_OBJ:
 198                case ACL_GROUP_OBJ:
 199                case ACL_MASK:
 200                case ACL_OTHER:
 201                        if (id != ACL_UNDEFINED_ID)
 202                                GOTO(_out, rc = -EIO);
 203
 204                        memcpy(&new->a_entries[j++], &header->a_entries[i],
 205                               sizeof(posix_acl_xattr_entry));
 206                        break;
 207                case ACL_USER:
 208                        if (id != NOBODY_UID)
 209                                memcpy(&new->a_entries[j++],
 210                                       &header->a_entries[i],
 211                                       sizeof(posix_acl_xattr_entry));
 212                        break;
 213                case ACL_GROUP:
 214                        if (id != NOBODY_GID)
 215                                memcpy(&new->a_entries[j++],
 216                                       &header->a_entries[i],
 217                                       sizeof(posix_acl_xattr_entry));
 218                        break;
 219                default:
 220                        GOTO(_out, rc = -EIO);
 221                }
 222        }
 223
 224        /* free unused space. */
 225        rc = lustre_posix_acl_xattr_reduce_space(&new, count, j);
 226        if (rc >= 0) {
 227                size = rc;
 228                *out = new;
 229                rc = 0;
 230        }
 231        EXIT;
 232
 233_out:
 234        if (rc) {
 235                OBD_FREE(new, size);
 236                size = rc;
 237        }
 238        return size;
 239}
 240EXPORT_SYMBOL(lustre_posix_acl_xattr_filter);
 241
 242/*
 243 * Release the posix ACL space.
 244 */
 245void lustre_posix_acl_xattr_free(posix_acl_xattr_header *header, int size)
 246{
 247        OBD_FREE(header, size);
 248}
 249EXPORT_SYMBOL(lustre_posix_acl_xattr_free);
 250
 251/*
 252 * Release the extended ACL space.
 253 */
 254void lustre_ext_acl_xattr_free(ext_acl_xattr_header *header)
 255{
 256        OBD_FREE(header, CFS_ACL_XATTR_SIZE(le32_to_cpu(header->a_count), \
 257                                            ext_acl_xattr));
 258}
 259EXPORT_SYMBOL(lustre_ext_acl_xattr_free);
 260
 261static ext_acl_xattr_entry *
 262lustre_ext_acl_xattr_search(ext_acl_xattr_header *header,
 263                            posix_acl_xattr_entry *entry, int *pos)
 264{
 265        int once, start, end, i, j, count = le32_to_cpu(header->a_count);
 266
 267        once = 0;
 268        start = *pos;
 269        end = count;
 270
 271again:
 272        for (i = start; i < end; i++) {
 273                if (header->a_entries[i].e_tag == entry->e_tag &&
 274                    header->a_entries[i].e_id == entry->e_id) {
 275                        j = i;
 276                        if (++i >= count)
 277                                i = 0;
 278                        *pos = i;
 279                        return &header->a_entries[j];
 280                }
 281        }
 282
 283        if (!once) {
 284                once = 1;
 285                start = 0;
 286                end = *pos;
 287                goto again;
 288        }
 289
 290        return NULL;
 291}
 292
 293/*
 294 * Merge the posix ACL and the extended ACL into new posix ACL.
 295 */
 296int lustre_acl_xattr_merge2posix(posix_acl_xattr_header *posix_header, int size,
 297                                 ext_acl_xattr_header *ext_header,
 298                                 posix_acl_xattr_header **out)
 299{
 300        int posix_count, posix_size, i, j;
 301        int ext_count = le32_to_cpu(ext_header->a_count), pos = 0, rc = 0;
 302        posix_acl_xattr_entry pe = {ACL_MASK, 0, ACL_UNDEFINED_ID};
 303        posix_acl_xattr_header *new;
 304        ext_acl_xattr_entry *ee, ae;
 305        ENTRY;
 306
 307        lustre_posix_acl_cpu_to_le(&pe, &pe);
 308        ee = lustre_ext_acl_xattr_search(ext_header, &pe, &pos);
 309        if (ee == NULL || le32_to_cpu(ee->e_stat) == ES_DEL) {
 310                /* there are only base ACL entries at most. */
 311                posix_count = 3;
 312                posix_size = CFS_ACL_XATTR_SIZE(posix_count, posix_acl_xattr);
 313                OBD_ALLOC(new, posix_size);
 314                if (unlikely(new == NULL))
 315                        RETURN(-ENOMEM);
 316
 317                new->a_version = cpu_to_le32(CFS_ACL_XATTR_VERSION);
 318                for (i = 0, j = 0; i < ext_count; i++) {
 319                        lustre_ext_acl_le_to_cpu(&ae,
 320                                                 &ext_header->a_entries[i]);
 321                        switch (ae.e_tag) {
 322                        case ACL_USER_OBJ:
 323                        case ACL_GROUP_OBJ:
 324                        case ACL_OTHER:
 325                                if (ae.e_id != ACL_UNDEFINED_ID)
 326                                        GOTO(_out, rc = -EIO);
 327
 328                                if (ae.e_stat != ES_DEL) {
 329                                        new->a_entries[j].e_tag =
 330                                                ext_header->a_entries[i].e_tag;
 331                                        new->a_entries[j].e_perm =
 332                                                ext_header->a_entries[i].e_perm;
 333                                        new->a_entries[j++].e_id =
 334                                                ext_header->a_entries[i].e_id;
 335                                }
 336                                break;
 337                        case ACL_MASK:
 338                        case ACL_USER:
 339                        case ACL_GROUP:
 340                                if (ae.e_stat == ES_DEL)
 341                                        break;
 342                        default:
 343                                GOTO(_out, rc = -EIO);
 344                        }
 345                }
 346        } else {
 347                /* maybe there are valid ACL_USER or ACL_GROUP entries in the
 348                 * original server-side ACL, they are regarded as ES_UNC stat.*/
 349                int ori_posix_count;
 350
 351                if (unlikely(size < 0))
 352                        RETURN(-EINVAL);
 353                else if (!size)
 354                        ori_posix_count = 0;
 355                else
 356                        ori_posix_count =
 357                                CFS_ACL_XATTR_COUNT(size, posix_acl_xattr);
 358                posix_count = ori_posix_count + ext_count;
 359                posix_size =
 360                        CFS_ACL_XATTR_SIZE(posix_count, posix_acl_xattr);
 361                OBD_ALLOC(new, posix_size);
 362                if (unlikely(new == NULL))
 363                        RETURN(-ENOMEM);
 364
 365                new->a_version = cpu_to_le32(CFS_ACL_XATTR_VERSION);
 366                /* 1. process the unchanged ACL entries
 367                 *    in the original server-side ACL. */
 368                pos = 0;
 369                for (i = 0, j = 0; i < ori_posix_count; i++) {
 370                        ee = lustre_ext_acl_xattr_search(ext_header,
 371                                        &posix_header->a_entries[i], &pos);
 372                        if (ee == NULL)
 373                                memcpy(&new->a_entries[j++],
 374                                       &posix_header->a_entries[i],
 375                                       sizeof(posix_acl_xattr_entry));
 376                }
 377
 378                /* 2. process the non-deleted entries
 379                 *    from client-side extended ACL. */
 380                for (i = 0; i < ext_count; i++) {
 381                        if (le16_to_cpu(ext_header->a_entries[i].e_stat) !=
 382                            ES_DEL) {
 383                                new->a_entries[j].e_tag =
 384                                                ext_header->a_entries[i].e_tag;
 385                                new->a_entries[j].e_perm =
 386                                                ext_header->a_entries[i].e_perm;
 387                                new->a_entries[j++].e_id =
 388                                                ext_header->a_entries[i].e_id;
 389                        }
 390                }
 391        }
 392
 393        /* free unused space. */
 394        rc = lustre_posix_acl_xattr_reduce_space(&new, posix_count, j);
 395        if (rc >= 0) {
 396                posix_size = rc;
 397                *out = new;
 398                rc = 0;
 399        }
 400        EXIT;
 401
 402_out:
 403        if (rc) {
 404                OBD_FREE(new, posix_size);
 405                posix_size = rc;
 406        }
 407        return posix_size;
 408}
 409EXPORT_SYMBOL(lustre_acl_xattr_merge2posix);
 410
 411/*
 412 * Merge the posix ACL and the extended ACL into new extended ACL.
 413 */
 414ext_acl_xattr_header *
 415lustre_acl_xattr_merge2ext(posix_acl_xattr_header *posix_header, int size,
 416                           ext_acl_xattr_header *ext_header)
 417{
 418        int ori_ext_count, posix_count, ext_count, ext_size;
 419        int i, j, pos = 0, rc = 0;
 420        posix_acl_xattr_entry pae;
 421        ext_acl_xattr_header *new;
 422        ext_acl_xattr_entry *ee, eae;
 423        ENTRY;
 424
 425        if (unlikely(size < 0))
 426                RETURN(ERR_PTR(-EINVAL));
 427        else if (!size)
 428                posix_count = 0;
 429        else
 430                posix_count = CFS_ACL_XATTR_COUNT(size, posix_acl_xattr);
 431        ori_ext_count = le32_to_cpu(ext_header->a_count);
 432        ext_count = posix_count + ori_ext_count;
 433        ext_size = CFS_ACL_XATTR_SIZE(ext_count, ext_acl_xattr);
 434
 435        OBD_ALLOC(new, ext_size);
 436        if (unlikely(new == NULL))
 437                RETURN(ERR_PTR(-ENOMEM));
 438
 439        for (i = 0, j = 0; i < posix_count; i++) {
 440                lustre_posix_acl_le_to_cpu(&pae, &posix_header->a_entries[i]);
 441                switch (pae.e_tag) {
 442                case ACL_USER_OBJ:
 443                case ACL_GROUP_OBJ:
 444                case ACL_MASK:
 445                case ACL_OTHER:
 446                        if (pae.e_id != ACL_UNDEFINED_ID)
 447                                GOTO(out, rc = -EIO);
 448                case ACL_USER:
 449                        /* ignore "nobody" entry. */
 450                        if (pae.e_id == NOBODY_UID)
 451                                break;
 452
 453                        new->a_entries[j].e_tag =
 454                                        posix_header->a_entries[i].e_tag;
 455                        new->a_entries[j].e_perm =
 456                                        posix_header->a_entries[i].e_perm;
 457                        new->a_entries[j].e_id =
 458                                        posix_header->a_entries[i].e_id;
 459                        ee = lustre_ext_acl_xattr_search(ext_header,
 460                                        &posix_header->a_entries[i], &pos);
 461                        if (ee) {
 462                                if (posix_header->a_entries[i].e_perm !=
 463                                                                ee->e_perm)
 464                                        /* entry modified. */
 465                                        ee->e_stat =
 466                                        new->a_entries[j++].e_stat =
 467                                                        cpu_to_le32(ES_MOD);
 468                                else
 469                                        /* entry unchanged. */
 470                                        ee->e_stat =
 471                                        new->a_entries[j++].e_stat =
 472                                                        cpu_to_le32(ES_UNC);
 473                        } else {
 474                                /* new entry. */
 475                                new->a_entries[j++].e_stat =
 476                                                        cpu_to_le32(ES_ADD);
 477                        }
 478                        break;
 479                case ACL_GROUP:
 480                        /* ignore "nobody" entry. */
 481                        if (pae.e_id == NOBODY_GID)
 482                                break;
 483                        new->a_entries[j].e_tag =
 484                                        posix_header->a_entries[i].e_tag;
 485                        new->a_entries[j].e_perm =
 486                                        posix_header->a_entries[i].e_perm;
 487                        new->a_entries[j].e_id =
 488                                        posix_header->a_entries[i].e_id;
 489                        ee = lustre_ext_acl_xattr_search(ext_header,
 490                                        &posix_header->a_entries[i], &pos);
 491                        if (ee) {
 492                                if (posix_header->a_entries[i].e_perm !=
 493                                                                ee->e_perm)
 494                                        /* entry modified. */
 495                                        ee->e_stat =
 496                                        new->a_entries[j++].e_stat =
 497                                                        cpu_to_le32(ES_MOD);
 498                                else
 499                                        /* entry unchanged. */
 500                                        ee->e_stat =
 501                                        new->a_entries[j++].e_stat =
 502                                                        cpu_to_le32(ES_UNC);
 503                        } else {
 504                                /* new entry. */
 505                                new->a_entries[j++].e_stat =
 506                                                        cpu_to_le32(ES_ADD);
 507                        }
 508                        break;
 509                default:
 510                        GOTO(out, rc = -EIO);
 511                }
 512        }
 513
 514        /* process deleted entries. */
 515        for (i = 0; i < ori_ext_count; i++) {
 516                lustre_ext_acl_le_to_cpu(&eae, &ext_header->a_entries[i]);
 517                if (eae.e_stat == ES_UNK) {
 518                        /* ignore "nobody" entry. */
 519                        if ((eae.e_tag == ACL_USER && eae.e_id == NOBODY_UID) ||
 520                            (eae.e_tag == ACL_GROUP && eae.e_id == NOBODY_GID))
 521                                continue;
 522
 523                        new->a_entries[j].e_tag =
 524                                                ext_header->a_entries[i].e_tag;
 525                        new->a_entries[j].e_perm =
 526                                                ext_header->a_entries[i].e_perm;
 527                        new->a_entries[j].e_id = ext_header->a_entries[i].e_id;
 528                        new->a_entries[j++].e_stat = cpu_to_le32(ES_DEL);
 529                }
 530        }
 531
 532        new->a_count = cpu_to_le32(j);
 533        /* free unused space. */
 534        rc = lustre_ext_acl_xattr_reduce_space(&new, ext_count);
 535        EXIT;
 536
 537out:
 538        if (rc) {
 539                OBD_FREE(new, ext_size);
 540                new = ERR_PTR(rc);
 541        }
 542        return new;
 543}
 544EXPORT_SYMBOL(lustre_acl_xattr_merge2ext);
 545
 546#endif
 547