root/fs/f2fs/acl.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. f2fs_acl_size
  2. f2fs_acl_count
  3. f2fs_acl_from_disk
  4. f2fs_acl_to_disk
  5. __f2fs_get_acl
  6. f2fs_get_acl
  7. __f2fs_set_acl
  8. f2fs_set_acl
  9. f2fs_acl_clone
  10. f2fs_acl_create_masq
  11. f2fs_acl_create
  12. f2fs_init_acl

   1 // SPDX-License-Identifier: GPL-2.0
   2 /*
   3  * fs/f2fs/acl.c
   4  *
   5  * Copyright (c) 2012 Samsung Electronics Co., Ltd.
   6  *             http://www.samsung.com/
   7  *
   8  * Portions of this code from linux/fs/ext2/acl.c
   9  *
  10  * Copyright (C) 2001-2003 Andreas Gruenbacher, <agruen@suse.de>
  11  */
  12 #include <linux/f2fs_fs.h>
  13 #include "f2fs.h"
  14 #include "xattr.h"
  15 #include "acl.h"
  16 
  17 static inline size_t f2fs_acl_size(int count)
  18 {
  19         if (count <= 4) {
  20                 return sizeof(struct f2fs_acl_header) +
  21                         count * sizeof(struct f2fs_acl_entry_short);
  22         } else {
  23                 return sizeof(struct f2fs_acl_header) +
  24                         4 * sizeof(struct f2fs_acl_entry_short) +
  25                         (count - 4) * sizeof(struct f2fs_acl_entry);
  26         }
  27 }
  28 
  29 static inline int f2fs_acl_count(size_t size)
  30 {
  31         ssize_t s;
  32         size -= sizeof(struct f2fs_acl_header);
  33         s = size - 4 * sizeof(struct f2fs_acl_entry_short);
  34         if (s < 0) {
  35                 if (size % sizeof(struct f2fs_acl_entry_short))
  36                         return -1;
  37                 return size / sizeof(struct f2fs_acl_entry_short);
  38         } else {
  39                 if (s % sizeof(struct f2fs_acl_entry))
  40                         return -1;
  41                 return s / sizeof(struct f2fs_acl_entry) + 4;
  42         }
  43 }
  44 
  45 static struct posix_acl *f2fs_acl_from_disk(const char *value, size_t size)
  46 {
  47         int i, count;
  48         struct posix_acl *acl;
  49         struct f2fs_acl_header *hdr = (struct f2fs_acl_header *)value;
  50         struct f2fs_acl_entry *entry = (struct f2fs_acl_entry *)(hdr + 1);
  51         const char *end = value + size;
  52 
  53         if (size < sizeof(struct f2fs_acl_header))
  54                 return ERR_PTR(-EINVAL);
  55 
  56         if (hdr->a_version != cpu_to_le32(F2FS_ACL_VERSION))
  57                 return ERR_PTR(-EINVAL);
  58 
  59         count = f2fs_acl_count(size);
  60         if (count < 0)
  61                 return ERR_PTR(-EINVAL);
  62         if (count == 0)
  63                 return NULL;
  64 
  65         acl = posix_acl_alloc(count, GFP_NOFS);
  66         if (!acl)
  67                 return ERR_PTR(-ENOMEM);
  68 
  69         for (i = 0; i < count; i++) {
  70 
  71                 if ((char *)entry > end)
  72                         goto fail;
  73 
  74                 acl->a_entries[i].e_tag  = le16_to_cpu(entry->e_tag);
  75                 acl->a_entries[i].e_perm = le16_to_cpu(entry->e_perm);
  76 
  77                 switch (acl->a_entries[i].e_tag) {
  78                 case ACL_USER_OBJ:
  79                 case ACL_GROUP_OBJ:
  80                 case ACL_MASK:
  81                 case ACL_OTHER:
  82                         entry = (struct f2fs_acl_entry *)((char *)entry +
  83                                         sizeof(struct f2fs_acl_entry_short));
  84                         break;
  85 
  86                 case ACL_USER:
  87                         acl->a_entries[i].e_uid =
  88                                 make_kuid(&init_user_ns,
  89                                                 le32_to_cpu(entry->e_id));
  90                         entry = (struct f2fs_acl_entry *)((char *)entry +
  91                                         sizeof(struct f2fs_acl_entry));
  92                         break;
  93                 case ACL_GROUP:
  94                         acl->a_entries[i].e_gid =
  95                                 make_kgid(&init_user_ns,
  96                                                 le32_to_cpu(entry->e_id));
  97                         entry = (struct f2fs_acl_entry *)((char *)entry +
  98                                         sizeof(struct f2fs_acl_entry));
  99                         break;
 100                 default:
 101                         goto fail;
 102                 }
 103         }
 104         if ((char *)entry != end)
 105                 goto fail;
 106         return acl;
 107 fail:
 108         posix_acl_release(acl);
 109         return ERR_PTR(-EINVAL);
 110 }
 111 
 112 static void *f2fs_acl_to_disk(struct f2fs_sb_info *sbi,
 113                                 const struct posix_acl *acl, size_t *size)
 114 {
 115         struct f2fs_acl_header *f2fs_acl;
 116         struct f2fs_acl_entry *entry;
 117         int i;
 118 
 119         f2fs_acl = f2fs_kmalloc(sbi, sizeof(struct f2fs_acl_header) +
 120                         acl->a_count * sizeof(struct f2fs_acl_entry),
 121                         GFP_NOFS);
 122         if (!f2fs_acl)
 123                 return ERR_PTR(-ENOMEM);
 124 
 125         f2fs_acl->a_version = cpu_to_le32(F2FS_ACL_VERSION);
 126         entry = (struct f2fs_acl_entry *)(f2fs_acl + 1);
 127 
 128         for (i = 0; i < acl->a_count; i++) {
 129 
 130                 entry->e_tag  = cpu_to_le16(acl->a_entries[i].e_tag);
 131                 entry->e_perm = cpu_to_le16(acl->a_entries[i].e_perm);
 132 
 133                 switch (acl->a_entries[i].e_tag) {
 134                 case ACL_USER:
 135                         entry->e_id = cpu_to_le32(
 136                                         from_kuid(&init_user_ns,
 137                                                 acl->a_entries[i].e_uid));
 138                         entry = (struct f2fs_acl_entry *)((char *)entry +
 139                                         sizeof(struct f2fs_acl_entry));
 140                         break;
 141                 case ACL_GROUP:
 142                         entry->e_id = cpu_to_le32(
 143                                         from_kgid(&init_user_ns,
 144                                                 acl->a_entries[i].e_gid));
 145                         entry = (struct f2fs_acl_entry *)((char *)entry +
 146                                         sizeof(struct f2fs_acl_entry));
 147                         break;
 148                 case ACL_USER_OBJ:
 149                 case ACL_GROUP_OBJ:
 150                 case ACL_MASK:
 151                 case ACL_OTHER:
 152                         entry = (struct f2fs_acl_entry *)((char *)entry +
 153                                         sizeof(struct f2fs_acl_entry_short));
 154                         break;
 155                 default:
 156                         goto fail;
 157                 }
 158         }
 159         *size = f2fs_acl_size(acl->a_count);
 160         return (void *)f2fs_acl;
 161 
 162 fail:
 163         kvfree(f2fs_acl);
 164         return ERR_PTR(-EINVAL);
 165 }
 166 
 167 static struct posix_acl *__f2fs_get_acl(struct inode *inode, int type,
 168                                                 struct page *dpage)
 169 {
 170         int name_index = F2FS_XATTR_INDEX_POSIX_ACL_DEFAULT;
 171         void *value = NULL;
 172         struct posix_acl *acl;
 173         int retval;
 174 
 175         if (type == ACL_TYPE_ACCESS)
 176                 name_index = F2FS_XATTR_INDEX_POSIX_ACL_ACCESS;
 177 
 178         retval = f2fs_getxattr(inode, name_index, "", NULL, 0, dpage);
 179         if (retval > 0) {
 180                 value = f2fs_kmalloc(F2FS_I_SB(inode), retval, GFP_F2FS_ZERO);
 181                 if (!value)
 182                         return ERR_PTR(-ENOMEM);
 183                 retval = f2fs_getxattr(inode, name_index, "", value,
 184                                                         retval, dpage);
 185         }
 186 
 187         if (retval > 0)
 188                 acl = f2fs_acl_from_disk(value, retval);
 189         else if (retval == -ENODATA)
 190                 acl = NULL;
 191         else
 192                 acl = ERR_PTR(retval);
 193         kvfree(value);
 194 
 195         return acl;
 196 }
 197 
 198 struct posix_acl *f2fs_get_acl(struct inode *inode, int type)
 199 {
 200         return __f2fs_get_acl(inode, type, NULL);
 201 }
 202 
 203 static int __f2fs_set_acl(struct inode *inode, int type,
 204                         struct posix_acl *acl, struct page *ipage)
 205 {
 206         int name_index;
 207         void *value = NULL;
 208         size_t size = 0;
 209         int error;
 210         umode_t mode = inode->i_mode;
 211 
 212         switch (type) {
 213         case ACL_TYPE_ACCESS:
 214                 name_index = F2FS_XATTR_INDEX_POSIX_ACL_ACCESS;
 215                 if (acl && !ipage) {
 216                         error = posix_acl_update_mode(inode, &mode, &acl);
 217                         if (error)
 218                                 return error;
 219                         set_acl_inode(inode, mode);
 220                 }
 221                 break;
 222 
 223         case ACL_TYPE_DEFAULT:
 224                 name_index = F2FS_XATTR_INDEX_POSIX_ACL_DEFAULT;
 225                 if (!S_ISDIR(inode->i_mode))
 226                         return acl ? -EACCES : 0;
 227                 break;
 228 
 229         default:
 230                 return -EINVAL;
 231         }
 232 
 233         if (acl) {
 234                 value = f2fs_acl_to_disk(F2FS_I_SB(inode), acl, &size);
 235                 if (IS_ERR(value)) {
 236                         clear_inode_flag(inode, FI_ACL_MODE);
 237                         return PTR_ERR(value);
 238                 }
 239         }
 240 
 241         error = f2fs_setxattr(inode, name_index, "", value, size, ipage, 0);
 242 
 243         kvfree(value);
 244         if (!error)
 245                 set_cached_acl(inode, type, acl);
 246 
 247         clear_inode_flag(inode, FI_ACL_MODE);
 248         return error;
 249 }
 250 
 251 int f2fs_set_acl(struct inode *inode, struct posix_acl *acl, int type)
 252 {
 253         if (unlikely(f2fs_cp_error(F2FS_I_SB(inode))))
 254                 return -EIO;
 255 
 256         return __f2fs_set_acl(inode, type, acl, NULL);
 257 }
 258 
 259 /*
 260  * Most part of f2fs_acl_clone, f2fs_acl_create_masq, f2fs_acl_create
 261  * are copied from posix_acl.c
 262  */
 263 static struct posix_acl *f2fs_acl_clone(const struct posix_acl *acl,
 264                                                         gfp_t flags)
 265 {
 266         struct posix_acl *clone = NULL;
 267 
 268         if (acl) {
 269                 int size = sizeof(struct posix_acl) + acl->a_count *
 270                                 sizeof(struct posix_acl_entry);
 271                 clone = kmemdup(acl, size, flags);
 272                 if (clone)
 273                         refcount_set(&clone->a_refcount, 1);
 274         }
 275         return clone;
 276 }
 277 
 278 static int f2fs_acl_create_masq(struct posix_acl *acl, umode_t *mode_p)
 279 {
 280         struct posix_acl_entry *pa, *pe;
 281         struct posix_acl_entry *group_obj = NULL, *mask_obj = NULL;
 282         umode_t mode = *mode_p;
 283         int not_equiv = 0;
 284 
 285         /* assert(atomic_read(acl->a_refcount) == 1); */
 286 
 287         FOREACH_ACL_ENTRY(pa, acl, pe) {
 288                 switch (pa->e_tag) {
 289                 case ACL_USER_OBJ:
 290                         pa->e_perm &= (mode >> 6) | ~S_IRWXO;
 291                         mode &= (pa->e_perm << 6) | ~S_IRWXU;
 292                         break;
 293 
 294                 case ACL_USER:
 295                 case ACL_GROUP:
 296                         not_equiv = 1;
 297                         break;
 298 
 299                 case ACL_GROUP_OBJ:
 300                         group_obj = pa;
 301                         break;
 302 
 303                 case ACL_OTHER:
 304                         pa->e_perm &= mode | ~S_IRWXO;
 305                         mode &= pa->e_perm | ~S_IRWXO;
 306                         break;
 307 
 308                 case ACL_MASK:
 309                         mask_obj = pa;
 310                         not_equiv = 1;
 311                         break;
 312 
 313                 default:
 314                         return -EIO;
 315                 }
 316         }
 317 
 318         if (mask_obj) {
 319                 mask_obj->e_perm &= (mode >> 3) | ~S_IRWXO;
 320                 mode &= (mask_obj->e_perm << 3) | ~S_IRWXG;
 321         } else {
 322                 if (!group_obj)
 323                         return -EIO;
 324                 group_obj->e_perm &= (mode >> 3) | ~S_IRWXO;
 325                 mode &= (group_obj->e_perm << 3) | ~S_IRWXG;
 326         }
 327 
 328         *mode_p = (*mode_p & ~S_IRWXUGO) | mode;
 329         return not_equiv;
 330 }
 331 
 332 static int f2fs_acl_create(struct inode *dir, umode_t *mode,
 333                 struct posix_acl **default_acl, struct posix_acl **acl,
 334                 struct page *dpage)
 335 {
 336         struct posix_acl *p;
 337         struct posix_acl *clone;
 338         int ret;
 339 
 340         *acl = NULL;
 341         *default_acl = NULL;
 342 
 343         if (S_ISLNK(*mode) || !IS_POSIXACL(dir))
 344                 return 0;
 345 
 346         p = __f2fs_get_acl(dir, ACL_TYPE_DEFAULT, dpage);
 347         if (!p || p == ERR_PTR(-EOPNOTSUPP)) {
 348                 *mode &= ~current_umask();
 349                 return 0;
 350         }
 351         if (IS_ERR(p))
 352                 return PTR_ERR(p);
 353 
 354         clone = f2fs_acl_clone(p, GFP_NOFS);
 355         if (!clone) {
 356                 ret = -ENOMEM;
 357                 goto release_acl;
 358         }
 359 
 360         ret = f2fs_acl_create_masq(clone, mode);
 361         if (ret < 0)
 362                 goto release_clone;
 363 
 364         if (ret == 0)
 365                 posix_acl_release(clone);
 366         else
 367                 *acl = clone;
 368 
 369         if (!S_ISDIR(*mode))
 370                 posix_acl_release(p);
 371         else
 372                 *default_acl = p;
 373 
 374         return 0;
 375 
 376 release_clone:
 377         posix_acl_release(clone);
 378 release_acl:
 379         posix_acl_release(p);
 380         return ret;
 381 }
 382 
 383 int f2fs_init_acl(struct inode *inode, struct inode *dir, struct page *ipage,
 384                                                         struct page *dpage)
 385 {
 386         struct posix_acl *default_acl = NULL, *acl = NULL;
 387         int error = 0;
 388 
 389         error = f2fs_acl_create(dir, &inode->i_mode, &default_acl, &acl, dpage);
 390         if (error)
 391                 return error;
 392 
 393         f2fs_mark_inode_dirty_sync(inode, true);
 394 
 395         if (default_acl) {
 396                 error = __f2fs_set_acl(inode, ACL_TYPE_DEFAULT, default_acl,
 397                                        ipage);
 398                 posix_acl_release(default_acl);
 399         } else {
 400                 inode->i_default_acl = NULL;
 401         }
 402         if (acl) {
 403                 if (!error)
 404                         error = __f2fs_set_acl(inode, ACL_TYPE_ACCESS, acl,
 405                                                ipage);
 406                 posix_acl_release(acl);
 407         } else {
 408                 inode->i_acl = NULL;
 409         }
 410 
 411         return error;
 412 }

/* [<][>][^][v][top][bottom][index][help] */