r18580: map the PVFS_FLAG_READONLY bit in the posix backend onto
[mat/samba.git] / source4 / ntvfs / posix / pvfs_acl.c
1 /* 
2    Unix SMB/CIFS implementation.
3
4    POSIX NTVFS backend - ACL support
5
6    Copyright (C) Andrew Tridgell 2004
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23 #include "includes.h"
24 #include "auth/auth.h"
25 #include "vfs_posix.h"
26 #include "librpc/gen_ndr/xattr.h"
27 #include "libcli/security/security.h"
28
29
30 /*
31   map a single access_mask from generic to specific bits for files/dirs
32 */
33 static uint32_t pvfs_translate_mask(uint32_t access_mask)
34 {
35         if (access_mask & SEC_MASK_GENERIC) {
36                 if (access_mask & SEC_GENERIC_READ)    access_mask |= SEC_RIGHTS_FILE_READ;
37                 if (access_mask & SEC_GENERIC_WRITE)   access_mask |= SEC_RIGHTS_FILE_WRITE;
38                 if (access_mask & SEC_GENERIC_EXECUTE) access_mask |= SEC_RIGHTS_FILE_EXECUTE;
39                 if (access_mask & SEC_GENERIC_ALL)     access_mask |= SEC_RIGHTS_FILE_ALL;
40                 access_mask &= ~SEC_MASK_GENERIC;
41         }
42         return access_mask;
43 }
44
45
46 /*
47   map any generic access bits in the given acl
48   this relies on the fact that the mappings for files and directories
49   are the same
50 */
51 static void pvfs_translate_generic_bits(struct security_acl *acl)
52 {
53         unsigned i;
54
55         if (!acl) return;
56
57         for (i=0;i<acl->num_aces;i++) {
58                 struct security_ace *ace = &acl->aces[i];
59                 ace->access_mask = pvfs_translate_mask(ace->access_mask);
60         }
61 }
62
63
64 /*
65   setup a default ACL for a file
66 */
67 static NTSTATUS pvfs_default_acl(struct pvfs_state *pvfs,
68                                  struct ntvfs_request *req,
69                                  struct pvfs_filename *name, int fd, 
70                                  struct xattr_NTACL *acl)
71 {
72         struct security_descriptor *sd;
73         NTSTATUS status;
74         struct security_ace ace;
75         mode_t mode;
76
77         sd = security_descriptor_initialise(req);
78         if (sd == NULL) {
79                 return NT_STATUS_NO_MEMORY;
80         }
81
82         status = sidmap_uid_to_sid(pvfs->sidmap, sd, name->st.st_uid, &sd->owner_sid);
83         if (!NT_STATUS_IS_OK(status)) {
84                 return status;
85         }
86         status = sidmap_gid_to_sid(pvfs->sidmap, sd, name->st.st_gid, &sd->group_sid);
87         if (!NT_STATUS_IS_OK(status)) {
88                 return status;
89         }
90
91         sd->type |= SEC_DESC_DACL_PRESENT;
92
93         mode = name->st.st_mode;
94
95         /*
96           we provide up to 4 ACEs
97             - Owner
98             - Group
99             - Everyone
100             - Administrator
101          */
102
103
104         /* setup owner ACE */
105         ace.type = SEC_ACE_TYPE_ACCESS_ALLOWED;
106         ace.flags = 0;
107         ace.trustee = *sd->owner_sid;
108         ace.access_mask = 0;
109
110         if (mode & S_IRUSR) {
111                 if (mode & S_IWUSR) {
112                         ace.access_mask |= SEC_RIGHTS_FILE_ALL;
113                 } else {
114                         ace.access_mask |= SEC_RIGHTS_FILE_READ | SEC_FILE_EXECUTE;
115                 }
116         }
117         if (mode & S_IWUSR) {
118                 ace.access_mask |= SEC_RIGHTS_FILE_WRITE | SEC_STD_DELETE;
119         }
120         if (ace.access_mask) {
121                 security_descriptor_dacl_add(sd, &ace);
122         }
123
124
125         /* setup group ACE */
126         ace.trustee = *sd->group_sid;
127         ace.access_mask = 0;
128         if (mode & S_IRGRP) {
129                 ace.access_mask |= SEC_RIGHTS_FILE_READ | SEC_FILE_EXECUTE;
130         }
131         if (mode & S_IWGRP) {
132                 /* note that delete is not granted - this matches posix behaviour */
133                 ace.access_mask |= SEC_RIGHTS_FILE_WRITE;
134         }
135         if (ace.access_mask) {
136                 security_descriptor_dacl_add(sd, &ace);
137         }
138
139         /* setup other ACE */
140         ace.trustee = *dom_sid_parse_talloc(req, SID_WORLD);
141         ace.access_mask = 0;
142         if (mode & S_IROTH) {
143                 ace.access_mask |= SEC_RIGHTS_FILE_READ | SEC_FILE_EXECUTE;
144         }
145         if (mode & S_IWOTH) {
146                 ace.access_mask |= SEC_RIGHTS_FILE_WRITE;
147         }
148         if (ace.access_mask) {
149                 security_descriptor_dacl_add(sd, &ace);
150         }
151
152         /* setup system ACE */
153         ace.trustee = *dom_sid_parse_talloc(req, SID_NT_SYSTEM);
154         ace.access_mask = SEC_RIGHTS_FILE_ALL;
155         security_descriptor_dacl_add(sd, &ace);
156         
157         acl->version = 1;
158         acl->info.sd = sd;
159
160         return NT_STATUS_OK;
161 }
162                                  
163
164 /*
165   omit any security_descriptor elements not specified in the given
166   secinfo flags
167 */
168 static void normalise_sd_flags(struct security_descriptor *sd, uint32_t secinfo_flags)
169 {
170         if (!(secinfo_flags & SECINFO_OWNER)) {
171                 sd->owner_sid = NULL;
172         }
173         if (!(secinfo_flags & SECINFO_GROUP)) {
174                 sd->group_sid = NULL;
175         }
176         if (!(secinfo_flags & SECINFO_DACL)) {
177                 sd->dacl = NULL;
178         }
179         if (!(secinfo_flags & SECINFO_SACL)) {
180                 sd->sacl = NULL;
181         }
182 }
183
184 /*
185   answer a setfileinfo for an ACL
186 */
187 NTSTATUS pvfs_acl_set(struct pvfs_state *pvfs, 
188                       struct ntvfs_request *req,
189                       struct pvfs_filename *name, int fd, 
190                       uint32_t access_mask,
191                       union smb_setfileinfo *info)
192 {
193         struct xattr_NTACL *acl;
194         uint32_t secinfo_flags = info->set_secdesc.in.secinfo_flags;
195         struct security_descriptor *new_sd, *sd, orig_sd;
196         NTSTATUS status;
197         uid_t old_uid = -1;
198         gid_t old_gid = -1;
199         uid_t new_uid = -1;
200         gid_t new_gid = -1;
201
202         acl = talloc(req, struct xattr_NTACL);
203         if (acl == NULL) {
204                 return NT_STATUS_NO_MEMORY;
205         }
206
207         status = pvfs_acl_load(pvfs, name, fd, acl);
208         if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
209                 status = pvfs_default_acl(pvfs, req, name, fd, acl);
210         }
211         if (!NT_STATUS_IS_OK(status)) {
212                 return status;
213         }
214
215         switch (acl->version) {
216         case 1:
217                 sd = acl->info.sd;
218                 break;
219         default:
220                 return NT_STATUS_INVALID_ACL;
221         }
222
223         new_sd = info->set_secdesc.in.sd;
224         orig_sd = *sd;
225
226         old_uid = name->st.st_uid;
227         old_gid = name->st.st_gid;
228
229         /* only set the elements that have been specified */
230         if (secinfo_flags & SECINFO_OWNER) {
231                 if (!(access_mask & SEC_STD_WRITE_OWNER)) {
232                         return NT_STATUS_ACCESS_DENIED;
233                 }
234                 if (!dom_sid_equal(sd->owner_sid, new_sd->owner_sid)) {
235                         status = sidmap_sid_to_unixuid(pvfs->sidmap, new_sd->owner_sid, &new_uid);
236                         NT_STATUS_NOT_OK_RETURN(status);
237                 }
238                 sd->owner_sid = new_sd->owner_sid;
239         }
240         if (secinfo_flags & SECINFO_GROUP) {
241                 if (!(access_mask & SEC_STD_WRITE_OWNER)) {
242                         return NT_STATUS_ACCESS_DENIED;
243                 }
244                 if (!dom_sid_equal(sd->group_sid, new_sd->group_sid)) {
245                         status = sidmap_sid_to_unixgid(pvfs->sidmap, new_sd->group_sid, &new_gid);
246                         NT_STATUS_NOT_OK_RETURN(status);
247                 }
248                 sd->group_sid = new_sd->group_sid;
249         }
250         if (secinfo_flags & SECINFO_DACL) {
251                 if (!(access_mask & SEC_STD_WRITE_DAC)) {
252                         return NT_STATUS_ACCESS_DENIED;
253                 }
254                 sd->dacl = new_sd->dacl;
255                 pvfs_translate_generic_bits(sd->dacl);
256         }
257         if (secinfo_flags & SECINFO_SACL) {
258                 if (!(access_mask & SEC_FLAG_SYSTEM_SECURITY)) {
259                         return NT_STATUS_ACCESS_DENIED;
260                 }
261                 sd->sacl = new_sd->sacl;
262                 pvfs_translate_generic_bits(sd->sacl);
263         }
264
265         if (new_uid == old_uid) {
266                 new_uid = -1;
267         }
268
269         if (new_gid == old_gid) {
270                 new_gid = -1;
271         }
272
273         /* if there's something to change try it */
274         if (new_uid != -1 || new_gid != -1) {
275                 int ret;
276                 if (fd == -1) {
277                         ret = chown(name->full_name, new_uid, new_gid);
278                 } else {
279                         ret = fchown(fd, new_uid, new_gid);
280                 }
281                 if (ret == -1) {
282                         return pvfs_map_errno(pvfs, errno);
283                 }
284         }
285
286         /* we avoid saving if the sd is the same. This means when clients
287            copy files and end up copying the default sd that we don't
288            needlessly use xattrs */
289         if (!security_descriptor_equal(sd, &orig_sd)) {
290                 status = pvfs_acl_save(pvfs, name, fd, acl);
291         }
292
293         return status;
294 }
295
296
297 /*
298   answer a fileinfo query for the ACL
299 */
300 NTSTATUS pvfs_acl_query(struct pvfs_state *pvfs, 
301                         struct ntvfs_request *req,
302                         struct pvfs_filename *name, int fd, 
303                         union smb_fileinfo *info)
304 {
305         struct xattr_NTACL *acl;
306         NTSTATUS status;
307         struct security_descriptor *sd;
308
309         acl = talloc(req, struct xattr_NTACL);
310         if (acl == NULL) {
311                 return NT_STATUS_NO_MEMORY;
312         }
313
314         status = pvfs_acl_load(pvfs, name, fd, acl);
315         if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
316                 status = pvfs_default_acl(pvfs, req, name, fd, acl);
317         }
318         if (!NT_STATUS_IS_OK(status)) {
319                 return status;
320         }
321
322         switch (acl->version) {
323         case 1:
324                 sd = acl->info.sd;
325                 break;
326         default:
327                 return NT_STATUS_INVALID_ACL;
328         }
329
330         normalise_sd_flags(sd, info->query_secdesc.in.secinfo_flags);
331
332         info->query_secdesc.out.sd = sd;
333
334         return NT_STATUS_OK;
335 }
336
337
338 /*
339   default access check function based on unix permissions
340   doing this saves on building a full security descriptor
341   for the common case of access check on files with no 
342   specific NT ACL
343 */
344 NTSTATUS pvfs_access_check_unix(struct pvfs_state *pvfs, 
345                                 struct ntvfs_request *req,
346                                 struct pvfs_filename *name,
347                                 uint32_t *access_mask)
348 {
349         uid_t uid = geteuid();
350         uint32_t max_bits = SEC_RIGHTS_FILE_READ | SEC_FILE_ALL;
351
352         if ((pvfs->flags & PVFS_FLAG_READONLY) &&
353             ((*access_mask) & (SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA | 
354                                SEC_FILE_WRITE_EA | SEC_FILE_WRITE_ATTRIBUTE | 
355                                SEC_DIR_DELETE_CHILD))) {
356                 return NT_STATUS_ACCESS_DENIED;
357         }
358
359         /* owner and root get extra permissions */
360         if (uid == 0) {
361                 max_bits |= SEC_STD_ALL | SEC_FLAG_SYSTEM_SECURITY;
362         } else if (uid == name->st.st_uid) {
363                 max_bits |= SEC_STD_ALL;
364         }
365
366         if (*access_mask == SEC_FLAG_MAXIMUM_ALLOWED) {
367                 *access_mask = max_bits;
368                 return NT_STATUS_OK;
369         }
370
371         if (uid != 0 && (*access_mask & SEC_FLAG_SYSTEM_SECURITY)) {
372                 return NT_STATUS_PRIVILEGE_NOT_HELD;
373         }
374
375         if (*access_mask & ~max_bits) {
376                 return NT_STATUS_ACCESS_DENIED;
377         }
378
379         *access_mask |= SEC_FILE_READ_ATTRIBUTE;
380
381         return NT_STATUS_OK;
382 }
383
384
385 /*
386   check the security descriptor on a file, if any
387   
388   *access_mask is modified with the access actually granted
389 */
390 NTSTATUS pvfs_access_check(struct pvfs_state *pvfs, 
391                            struct ntvfs_request *req,
392                            struct pvfs_filename *name,
393                            uint32_t *access_mask)
394 {
395         struct security_token *token = req->session_info->security_token;
396         struct xattr_NTACL *acl;
397         NTSTATUS status;
398         struct security_descriptor *sd;
399
400         if ((pvfs->flags & PVFS_FLAG_READONLY) &&
401             ((*access_mask) & (SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA | 
402                                SEC_FILE_WRITE_EA | SEC_FILE_WRITE_ATTRIBUTE | 
403                                SEC_DIR_DELETE_CHILD))) {
404                 return NT_STATUS_ACCESS_DENIED;
405         }
406
407         acl = talloc(req, struct xattr_NTACL);
408         if (acl == NULL) {
409                 return NT_STATUS_NO_MEMORY;
410         }
411
412         /* expand the generic access bits to file specific bits */
413         *access_mask = pvfs_translate_mask(*access_mask);
414         *access_mask &= ~SEC_FILE_READ_ATTRIBUTE;
415
416         status = pvfs_acl_load(pvfs, name, -1, acl);
417         if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
418                 talloc_free(acl);
419                 return pvfs_access_check_unix(pvfs, req, name, access_mask);
420         }
421         if (!NT_STATUS_IS_OK(status)) {
422                 return status;
423         }
424
425         switch (acl->version) {
426         case 1:
427                 sd = acl->info.sd;
428                 break;
429         default:
430                 return NT_STATUS_INVALID_ACL;
431         }
432
433         /* check the acl against the required access mask */
434         status = sec_access_check(sd, token, *access_mask, access_mask);
435
436         /* this bit is always granted, even if not asked for */
437         *access_mask |= SEC_FILE_READ_ATTRIBUTE;
438
439         talloc_free(acl);
440         
441         return status;
442 }
443
444
445 /*
446   a simplified interface to access check, designed for calls that
447   do not take or return an access check mask
448 */
449 NTSTATUS pvfs_access_check_simple(struct pvfs_state *pvfs, 
450                                   struct ntvfs_request *req,
451                                   struct pvfs_filename *name,
452                                   uint32_t access_needed)
453 {
454         if (access_needed == 0) {
455                 return NT_STATUS_OK;
456         }
457         return pvfs_access_check(pvfs, req, name, &access_needed);
458 }
459
460 /*
461   access check for creating a new file/directory
462 */
463 NTSTATUS pvfs_access_check_create(struct pvfs_state *pvfs, 
464                                   struct ntvfs_request *req,
465                                   struct pvfs_filename *name,
466                                   uint32_t *access_mask)
467 {
468         struct pvfs_filename *parent;
469         NTSTATUS status;
470
471         status = pvfs_resolve_parent(pvfs, req, name, &parent);
472         if (!NT_STATUS_IS_OK(status)) {
473                 return status;
474         }
475
476         status = pvfs_access_check(pvfs, req, parent, access_mask);
477         if (!NT_STATUS_IS_OK(status)) {
478                 return status;
479         }
480
481         if (! ((*access_mask) & SEC_DIR_ADD_FILE)) {
482                 return pvfs_access_check_simple(pvfs, req, parent, SEC_DIR_ADD_FILE);
483         }
484
485         return status;
486 }
487
488 /*
489   access check for creating a new file/directory - no access mask supplied
490 */
491 NTSTATUS pvfs_access_check_parent(struct pvfs_state *pvfs, 
492                                   struct ntvfs_request *req,
493                                   struct pvfs_filename *name,
494                                   uint32_t access_mask)
495 {
496         struct pvfs_filename *parent;
497         NTSTATUS status;
498
499         status = pvfs_resolve_parent(pvfs, req, name, &parent);
500         if (!NT_STATUS_IS_OK(status)) {
501                 return status;
502         }
503
504         return pvfs_access_check_simple(pvfs, req, parent, access_mask);
505 }
506
507
508 /*
509   determine if an ACE is inheritable
510 */
511 static BOOL pvfs_inheritable_ace(struct pvfs_state *pvfs,
512                                  const struct security_ace *ace,
513                                  BOOL container)
514 {
515         if (!container) {
516                 return (ace->flags & SEC_ACE_FLAG_OBJECT_INHERIT) != 0;
517         }
518
519         if (ace->flags & SEC_ACE_FLAG_CONTAINER_INHERIT) {
520                 return True;
521         }
522
523         if ((ace->flags & SEC_ACE_FLAG_OBJECT_INHERIT) &&
524             !(ace->flags & SEC_ACE_FLAG_NO_PROPAGATE_INHERIT)) {
525                 return True;
526         }
527
528         return False;
529 }
530
531 /*
532   this is the core of ACL inheritance. It copies any inheritable
533   aces from the parent SD to the child SD. Note that the algorithm 
534   depends on whether the child is a container or not
535 */
536 static NTSTATUS pvfs_acl_inherit_aces(struct pvfs_state *pvfs, 
537                                       struct security_descriptor *parent_sd,
538                                       struct security_descriptor *sd,
539                                       BOOL container)
540 {
541         int i;
542         
543         for (i=0;i<parent_sd->dacl->num_aces;i++) {
544                 struct security_ace ace = parent_sd->dacl->aces[i];
545                 NTSTATUS status;
546                 const struct dom_sid *creator = NULL, *new_id = NULL;
547                 uint32_t orig_flags;
548
549                 if (!pvfs_inheritable_ace(pvfs, &ace, container)) {
550                         continue;
551                 }
552
553                 orig_flags = ace.flags;
554
555                 /* see the RAW-ACLS inheritance test for details on these rules */
556                 if (!container) {
557                         ace.flags = 0;
558                 } else {
559                         ace.flags &= ~SEC_ACE_FLAG_INHERIT_ONLY;
560
561                         if (!(ace.flags & SEC_ACE_FLAG_CONTAINER_INHERIT)) {
562                                 ace.flags |= SEC_ACE_FLAG_INHERIT_ONLY;
563                         }
564                         if (ace.flags & SEC_ACE_FLAG_NO_PROPAGATE_INHERIT) {
565                                 ace.flags = 0;
566                         }
567                 }
568
569                 /* the CREATOR sids are special when inherited */
570                 if (dom_sid_equal(&ace.trustee, pvfs->sid_cache.creator_owner)) {
571                         creator = pvfs->sid_cache.creator_owner;
572                         new_id = sd->owner_sid;
573                 } else if (dom_sid_equal(&ace.trustee, pvfs->sid_cache.creator_group)) {
574                         creator = pvfs->sid_cache.creator_group;
575                         new_id = sd->group_sid;
576                 } else {
577                         new_id = &ace.trustee;
578                 }
579
580                 if (creator && container && 
581                     (ace.flags & SEC_ACE_FLAG_CONTAINER_INHERIT)) {
582                         uint32_t flags = ace.flags;
583
584                         ace.trustee = *new_id;
585                         ace.flags = 0;
586                         status = security_descriptor_dacl_add(sd, &ace);
587                         if (!NT_STATUS_IS_OK(status)) {
588                                 return status;
589                         }
590
591                         ace.trustee = *creator;
592                         ace.flags = flags | SEC_ACE_FLAG_INHERIT_ONLY;
593                         status = security_descriptor_dacl_add(sd, &ace);
594                 } else if (container && 
595                            !(orig_flags & SEC_ACE_FLAG_NO_PROPAGATE_INHERIT)) {
596                         status = security_descriptor_dacl_add(sd, &ace);
597                 } else {
598                         ace.trustee = *new_id;
599                         status = security_descriptor_dacl_add(sd, &ace);
600                 }
601
602                 if (!NT_STATUS_IS_OK(status)) {
603                         return status;
604                 }
605         }
606
607         return NT_STATUS_OK;
608 }
609
610
611
612 /*
613   setup an ACL on a new file/directory based on the inherited ACL from
614   the parent. If there is no inherited ACL then we don't set anything,
615   as the default ACL applies anyway
616 */
617 NTSTATUS pvfs_acl_inherit(struct pvfs_state *pvfs, 
618                           struct ntvfs_request *req,
619                           struct pvfs_filename *name,
620                           int fd)
621 {
622         struct xattr_NTACL *acl;
623         NTSTATUS status;
624         struct pvfs_filename *parent;
625         struct security_descriptor *parent_sd, *sd;
626         BOOL container;
627
628         /* form the parents path */
629         status = pvfs_resolve_parent(pvfs, req, name, &parent);
630         if (!NT_STATUS_IS_OK(status)) {
631                 return status;
632         }
633
634         acl = talloc(req, struct xattr_NTACL);
635         if (acl == NULL) {
636                 return NT_STATUS_NO_MEMORY;
637         }
638
639         status = pvfs_acl_load(pvfs, parent, -1, acl);
640         if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
641                 return NT_STATUS_OK;
642         }
643         if (!NT_STATUS_IS_OK(status)) {
644                 return status;
645         }
646
647         switch (acl->version) {
648         case 1:
649                 parent_sd = acl->info.sd;
650                 break;
651         default:
652                 return NT_STATUS_INVALID_ACL;
653         }
654
655         if (parent_sd == NULL ||
656             parent_sd->dacl == NULL ||
657             parent_sd->dacl->num_aces == 0) {
658                 /* go with the default ACL */
659                 return NT_STATUS_OK;
660         }
661
662         /* create the new sd */
663         sd = security_descriptor_initialise(req);
664         if (sd == NULL) {
665                 return NT_STATUS_NO_MEMORY;
666         }
667
668         status = sidmap_uid_to_sid(pvfs->sidmap, sd, name->st.st_uid, &sd->owner_sid);
669         if (!NT_STATUS_IS_OK(status)) {
670                 return status;
671         }
672         status = sidmap_gid_to_sid(pvfs->sidmap, sd, name->st.st_gid, &sd->group_sid);
673         if (!NT_STATUS_IS_OK(status)) {
674                 return status;
675         }
676
677         sd->type |= SEC_DESC_DACL_PRESENT;
678
679         container = (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) ? True:False;
680
681         /* fill in the aces from the parent */
682         status = pvfs_acl_inherit_aces(pvfs, parent_sd, sd, container);
683         if (!NT_STATUS_IS_OK(status)) {
684                 return status;
685         }
686
687         /* if there is nothing to inherit then we fallback to the
688            default acl */
689         if (sd->dacl == NULL || sd->dacl->num_aces == 0) {
690                 return NT_STATUS_OK;
691         }
692
693         acl->info.sd = sd;
694
695         status = pvfs_acl_save(pvfs, name, fd, acl);
696         
697         return status;
698 }