source: gpfs_3.1_ker2.6.20/lpp/mmfs/src/gpl-linux/acl.c @ 177

Last change on this file since 177 was 16, checked in by rock, 17 years ago
File size: 12.2 KB
Line 
1/***************************************************************************
2 *
3 * Copyright (C) 2001 International Business Machines
4 * All rights reserved.
5 *
6 * This file is part of the GPFS mmfslinux kernel module.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 *  1. Redistributions of source code must retain the above copyright notice,
13 *     this list of conditions and the following disclaimer.
14 *  2. Redistributions in binary form must reproduce the above copyright
15 *     notice, this list of conditions and the following disclaimer in the
16 *     documentation and/or other materials provided with the distribution.
17 *  3. The name of the author may not be used to endorse or promote products
18 *     derived from this software without specific prior written
19 *     permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
27 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
29 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
30 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 *
32 *************************************************************************** */
33/* @(#)00       1.19.1.1  src/avs/fs/mmfs/ts/kernext/gpl-linux/acl.c, mmfs, avs_rgpfs24, rgpfs24s012a 4/17/07 15:54:41 */
34
35#if LINUX_KERNEL_VERSION > 2060000
36#ifdef CONFIG_FS_POSIX_ACL
37#include <Shark-gpl.h>
38
39#include <linux/string.h>
40#include <linux/module.h>
41#include <linux/errno.h>
42#include <linux/fs.h>
43#include <linux/smp_lock.h>
44
45#include <verdep.h>
46#include <cxiTSFattr.h>
47#include <cxiAclUser.h>
48
49/* Defines from posix_acl.h. We can not include that file because it conflicts
50   with GPFS defines in cxiAclUser.h. We can rename all of GPFS defines but it
51   will require many changes. */
52#define LINUX_ACL_UNDEFINED_ID  (-1)
53
54/* e_tag entry in struct posix_acl_entry */
55#define LINUX_ACL_USER_OBJ  (0x01)
56#define LINUX_ACL_USER    (0x02)
57#define LINUX_ACL_GROUP_OBJ (0x04)
58#define LINUX_ACL_GROUP   (0x08)
59#define LINUX_ACL_MASK    (0x10)
60#define LINUX_ACL_OTHER   (0x20)
61
62/* permissions in the e_perm field */
63#define LINUX_ACL_READ    (0x04)
64#define LINUX_ACL_WRITE   (0x02)
65#define LINUX_ACL_EXECUTE (0x01)
66
67struct linux_posix_acl_entry {
68  short     e_tag;
69  unsigned short    e_perm;
70  unsigned int    e_id;
71};
72
73struct linux_posix_acl {
74  atomic_t      a_refcount;
75  unsigned int      a_count;
76  struct linux_posix_acl_entry  a_entries[0];
77};
78
79struct linux_posix_acl * posix_acl_from_xattr(const void *value, size_t size);
80extern int posix_acl_valid(const struct linux_posix_acl *);
81int posix_acl_to_xattr(const struct linux_posix_acl *acl, void *buffer, size_t size);
82
83#define PERM_MASK_2_POSIX(perm) perm&(LINUX_ACL_READ|LINUX_ACL_WRITE|LINUX_ACL_EXECUTE)
84#define PERM_MASK_2_ACL(perm) perm&(ACL_PERM_EXECUTE|ACL_PERM_WRITE|ACL_PERM_READ)
85
86/* move data from ACL to linux_posix_acl */
87int
88ACL_2_posix_acl(ACL *tmpaclP, const void *buf, size_t buf_size)
89{
90  int i, len;
91  int rc = 0, count = 0;
92  acl_entry_t *aceP;
93  struct linux_posix_acl *aclP = (struct linux_posix_acl *)buf;
94
95  ENTER(0);
96  memset(aclP, '\0', buf_size);
97
98  TRACE2(TRACE_VNODE, 3, TRCID_LINUXOPS_ACL2POSIX_ENTER,
99         "ACL_2_posix_acl: enter size %d buf_size %d\n",
100         tmpaclP->aclLength, buf_size);
101
102  /* POSIX ACLs only.  Other types require additional translations.  */
103  if (tmpaclP->aclVersion != ACL_FIRST_VERSION)
104  {
105      rc = -EOPNOTSUPP; 
106      goto xerror;
107  }
108
109  if (buf_size == 0) {
110      rc = tmpaclP->aclLength;
111      goto xerror;
112  }
113
114  /* Get the address of the first extended acl entry */
115  aceP = ACL_ENTRY_START(tmpaclP, tmpaclP->aclVersion);
116  len = tmpaclP->aclLength - ACL_HEADER_SIZE_V1;
117  buf_size -= sizeof(struct linux_posix_acl);
118  i = 0;
119
120  while ((len > 4) && (len >= aceP->aceLength))
121  {
122    TRACE3(TRACE_VNODE, 3, TRCID_LINUXOPS_ACL2POSIX_2,
123         "ACL_2_posix_acl: %d len %d buf_size %d\n",
124         i, len, buf_size);
125
126    if (buf_size < sizeof(struct linux_posix_acl_entry)) {
127      rc = -ENOSPC;
128      goto xerror;
129    }
130    switch(aceP->aceTagType)
131    {
132      case ACL_USER:
133        aclP->a_entries[i].e_tag = LINUX_ACL_USER;
134        aclP->a_entries[i].e_id = aceP->aceQualifier;
135        break;
136      case ACL_GROUP:
137        aclP->a_entries[i].e_tag = LINUX_ACL_GROUP;
138        aclP->a_entries[i].e_id = aceP->aceQualifier;
139        break;
140      case ACL_USER_OBJ:
141        aclP->a_entries[i].e_tag = LINUX_ACL_USER_OBJ;
142        aclP->a_entries[i].e_id = LINUX_ACL_UNDEFINED_ID;
143        break;
144      case ACL_GROUP_OBJ:
145        aclP->a_entries[i].e_tag = LINUX_ACL_GROUP_OBJ;
146        aclP->a_entries[i].e_id = LINUX_ACL_UNDEFINED_ID;
147        break;
148      case ACL_OTHER:
149        aclP->a_entries[i].e_tag = LINUX_ACL_OTHER;
150        aclP->a_entries[i].e_id = LINUX_ACL_UNDEFINED_ID;
151        break;
152      case ACL_MASK:
153        aclP->a_entries[i].e_tag = LINUX_ACL_MASK;
154        aclP->a_entries[i].e_id = LINUX_ACL_UNDEFINED_ID;
155        break;
156    }
157    aclP->a_entries[i].e_perm = PERM_MASK_2_POSIX(aceP->acePermissions);
158
159    TRACE7(TRACE_VNODE, 3, TRCID_LINUXOPS_ACL2POSIX,
160         "ACL_2_posix_acl: %d e_tag 0x%02X aceTagType %d e_id %d aceQualifier %d e_perm 0x%02X acePermissions 0x%01X\n",
161         i, aclP->a_entries[i].e_tag, aceP->aceTagType, aclP->a_entries[i].e_id,
162         aceP->aceQualifier, aclP->a_entries[i].e_perm, aceP->acePermissions);
163
164    /* move to the next acl entry */
165    len -= aceP->aceLength;
166    aceP = (acl_entry_t *)((caddr_t)aceP + aceP->aceLength);
167    buf_size -= sizeof(struct linux_posix_acl_entry);
168    i++;
169  }
170  aclP->a_count = i;
171
172xerror:
173  TRACE1(TRACE_VNODE, 1, TRCID_LINUXOPS_ACL2POSIX_EXIT,
174         "ACL_2_posix_acl: exit rc %d\n", rc);
175
176  EXIT(0);
177  return rc;
178}
179
180/* move data from linux_posix_acl to ACL */
181void
182posix_acl_2_ACL(ACL *tmpaclP, struct linux_posix_acl *aclP)
183{
184  int i, count;
185  acl_entry_t *aceP;
186
187  count = aclP->a_count;
188  /* Get the address of the first extended acl entry */
189  aceP = ACL_ENTRY_START(tmpaclP, tmpaclP->aclVersion);
190
191  for (i = 0; i < count; i++)
192  {
193    if (aclP->a_entries[i].e_tag & LINUX_ACL_USER) {
194        aceP->aceTagType = ACL_USER;
195        aceP->aceQualifier = aclP->a_entries[i].e_id;
196    }
197    if (aclP->a_entries[i].e_tag & LINUX_ACL_GROUP) {
198        aceP->aceTagType = ACL_GROUP;
199        aceP->aceQualifier = aclP->a_entries[i].e_id;
200    }
201    if (aclP->a_entries[i].e_tag & LINUX_ACL_USER_OBJ) {
202        aceP->aceTagType = ACL_USER_OBJ;
203        aceP->aceQualifier = aclP->a_entries[i].e_id;
204    }
205    if (aclP->a_entries[i].e_tag & LINUX_ACL_GROUP_OBJ) {
206        aceP->aceTagType = ACL_GROUP_OBJ;
207        aceP->aceQualifier = aclP->a_entries[i].e_id;
208    }
209    if (aclP->a_entries[i].e_tag & LINUX_ACL_OTHER) {
210        aceP->aceTagType = ACL_OTHER;
211        aceP->aceQualifier = aclP->a_entries[i].e_id;
212    }
213    if (aclP->a_entries[i].e_tag & LINUX_ACL_MASK) {
214        aceP->aceTagType = ACL_MASK;
215        aceP->aceQualifier = aclP->a_entries[i].e_id;
216    }
217    aceP->acePermissions = PERM_MASK_2_ACL(aclP->a_entries[i].e_perm);
218    aceP->aceLength = sizeof(acl_entry_t);
219
220    TRACE6(TRACE_VNODE, 3, TRCID_LINUXOPS_POSIX2ACL,
221         "posix_acl_2_ACL: %d e_tag 0x%X aceTagType %d aceQualifier %d e_perm 0x%X acePermissions 0x%X\n",
222         i, aclP->a_entries[i].e_tag, aceP->aceTagType, aceP->aceQualifier,
223         aclP->a_entries[i].e_perm, aceP->acePermissions);
224
225    /* move to the next acl entry */
226    aceP = (acl_entry_t *)((caddr_t)aceP + sizeof(acl_entry_t));
227  }
228}
229
230int
231gpfs_set_posix_acl(struct dentry *dentry, int type, const void *buf, size_t buf_size)
232{
233  int count, size, rc;
234  cxiNode_t *cnP;
235  struct gpfsVfsData_t *privVfsP;
236  ext_cred_t eCred;
237  ACL tmpacl, *tmpaclP = &tmpacl;
238  int flags = 0;
239  struct inode *iP = dentry->d_inode;
240  struct linux_posix_acl *aclP = NULL;
241  mm_segment_t oldfs;
242
243  ENTER(0);
244  TRACE5(TRACE_VNODE, 1, TRCID_LINUXOPS_SETACLATTR_ENTER,
245         "gpfs_i_gpfs_set_posix_acl enter: iP 0x%lX ino %d type 0x%X buf 0x%lX size %d\n",
246         iP, iP->i_ino, type, buf, buf_size);
247
248  if (buf) {
249    aclP = posix_acl_from_xattr(buf, buf_size);
250    if (IS_ERR(aclP)) {
251      rc = PTR_ERR(aclP);
252      aclP = NULL; /* no aclP to free on exit */
253      goto xerror;
254    }
255  }
256
257  if (aclP == NULL) {
258    /* If no buf was given, or no acl was returned, there is nothing else
259     * to do. This is in effect deleting all extended ACLs.
260     */
261    memset(tmpaclP, '\0', sizeof(ACL));
262    tmpaclP->aclLength = ACL_HEADER_SIZE_V1;
263    if (type == ACL_TYPE_ACCESS)
264      tmpaclP->aclType = ACL_TYPE_ACCESS_DELETE;
265    else if (type == ACL_TYPE_DEFAULT)
266      tmpaclP->aclType = ACL_TYPE_DEFAULT_DELETE;
267    else
268    {
269      rc = EINVAL;
270      goto xerror;
271    }
272  }
273  else {
274    rc = posix_acl_valid(aclP);
275    if (rc)
276      goto xerror;
277
278    count = aclP->a_count;
279    size = ACL_HEADER_SIZE_V1 + (sizeof(acl_entry_t) * count);
280    if (size > sizeof(ACL)) {
281      tmpaclP = cxiMallocUnpinned(size);
282      if (tmpaclP == NULL) {
283        rc = ENOMEM;
284        goto xerror;
285      }
286    }
287    memset(tmpaclP, '\0', size);
288    tmpaclP->aclLength = size;
289    tmpaclP->aclType = type;
290  }
291
292  tmpaclP->aclVersion = ACL_FIRST_VERSION;
293
294  privVfsP = VP_TO_PVP(iP);
295  DBGASSERT(privVfsP != NULL);
296  cnP = VP_TO_CNP(iP);
297
298  /* Retrieve the ACL data */
299  setCred(&eCred);
300
301  oldfs = get_fs();
302  set_fs(get_ds());
303  if (aclP)
304     posix_acl_2_ACL(tmpaclP, aclP);
305
306  rc = gpfs_ops.gpfsPutAcl(privVfsP, cnP, flags, tmpaclP, &eCred);
307  set_fs(oldfs);
308
309xerror:
310  if (aclP)
311    kfree(aclP);
312
313  if (tmpaclP && (tmpaclP != &tmpacl))
314    cxiFreeUnpinned(tmpaclP);
315
316  TRACE2(TRACE_VNODE, 1, TRCID_LINUXOPS_SETACLATTR_EXIT,
317         "gpfs_i_gpfs_set_posix_acl exit: iP 0x%lX rc %d\n", iP, rc);
318  EXIT(0);
319  return rc;
320}
321
322int
323gpfs_get_posix_acl(struct dentry *dentry, int type, const void *buf, size_t buf_size)
324{
325  int rc = 0;
326  int i, size;
327  cxiNode_t *cnP;
328  struct gpfsVfsData_t *privVfsP;
329  ext_cred_t eCred;
330  int flags = GETACL_DONOT_BLD_ACL;
331  ACL tmpacl, *tmpaclP = &tmpacl;
332  struct inode *iP = dentry->d_inode;
333  mm_segment_t oldfs;
334
335  ENTER(0);
336  TRACE3(TRACE_VNODE, 1, TRCID_LINUXOPS_GETACLATTR_ENTER,
337         "gpfs_get_posix_acl enter: iP 0x%lX type %d size %d\n", iP, type, buf_size);
338
339  privVfsP = VP_TO_PVP(iP);
340  DBGASSERT(privVfsP != NULL);
341  cnP = VP_TO_CNP(iP);
342
343  /* Retrieve the ACL data */
344  size = sizeof(ACL);
345
346try_again:
347
348  setCred(&eCred);     // ids may have been remapped before goto try_again
349
350  tmpaclP->aclType = type;
351  tmpaclP->aclVersion = ACL_FIRST_VERSION;
352  tmpaclP->aclLength = size;
353
354  oldfs = get_fs();
355  set_fs(get_ds());
356  rc = gpfs_ops.gpfsGetAcl(privVfsP, cnP, flags, tmpaclP, &eCred);
357  set_fs(oldfs);
358  if (rc)
359  {
360    if (rc == ENOSPC)
361    {
362      if (tmpaclP->aclLength >= size)
363      {
364        size = tmpaclP->aclLength;
365        if (tmpaclP != &tmpacl)
366          cxiFreeUnpinned(tmpaclP);
367        tmpaclP = cxiMallocUnpinned(size);
368        if (!tmpaclP)
369        {
370          rc = -ENOMEM;
371          goto xerror;
372        }
373        goto try_again;
374      }
375      else
376      {
377        rc = -EINVAL;  // buffer not even big enough to return length
378        goto xerror;
379      }
380    }
381    else {
382      if (rc == ENOENT)
383        rc = ENODATA;
384      rc = -rc;
385      goto xerror;
386    }
387  }
388
389  /* move data from ACL to linux_posix_acl */
390  rc = ACL_2_posix_acl(tmpaclP, buf, buf_size);
391  if (!rc) {
392     if (size < buf_size) {
393       if (tmpaclP != &tmpacl)
394         cxiFreeUnpinned(tmpaclP);
395       tmpaclP = cxiMallocUnpinned(buf_size);
396       if (!tmpaclP)
397       {
398         rc = -ENOMEM;
399         goto xerror;
400       }
401     }
402     memcpy(tmpaclP, buf, buf_size);
403     rc = posix_acl_to_xattr((struct linux_posix_acl *)tmpaclP, (void *)buf, size);
404  }
405xerror:
406
407  if (tmpaclP != &tmpacl)
408    cxiFreeUnpinned(tmpaclP);
409
410  TRACE2(TRACE_VNODE, 1, TRCID_LINUXOPS_GETACLATTR_EXIT,
411         "gpfs_get_posix_acl exit: iP 0x%lX rc %d\n", iP, rc);
412
413  EXIT(0);
414  return rc;
415}
416#endif /* CONFIG_FS_POSIX_ACL */
417#endif /* LINUX_KERNEL_VERSION > 2060000 */
Note: See TracBrowser for help on using the repository browser.