Merge branch 'for-linus' of git://oss.sgi.com/xfs/xfs into for-2.6.34-incoming
[safe/jmp/linux-2.6] / fs / cifs / cifsacl.c
index dabbce0..7dfe084 100644 (file)
@@ -1,7 +1,7 @@
 /*
  *   fs/cifs/cifsacl.c
  *
- *   Copyright (C) International Business Machines  Corp., 2007
+ *   Copyright (C) International Business Machines  Corp., 2007,2008
  *   Author(s): Steve French (sfrench@us.ibm.com)
  *
  *   Contains the routines for mapping CIFS/NTFS ACLs
 static struct cifs_wksid wksidarr[NUM_WK_SIDS] = {
        {{1, 0, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0} }, "null user"},
        {{1, 1, {0, 0, 0, 0, 0, 1}, {0, 0, 0, 0, 0} }, "nobody"},
-       {{1, 1, {0, 0, 0, 0, 0, 5}, {cpu_to_le32(11), 0, 0, 0, 0} }, "net-users"},
-       {{1, 1, {0, 0, 0, 0, 0, 5}, {cpu_to_le32(18), 0, 0, 0, 0} }, "sys"},
-       {{1, 2, {0, 0, 0, 0, 0, 5}, {cpu_to_le32(32), cpu_to_le32(544), 0, 0, 0} }, "root"},
-       {{1, 2, {0, 0, 0, 0, 0, 5}, {cpu_to_le32(32), cpu_to_le32(545), 0, 0, 0} }, "users"},
-       {{1, 2, {0, 0, 0, 0, 0, 5}, {cpu_to_le32(32), cpu_to_le32(546), 0, 0, 0} }, "guest"} }
+       {{1, 1, {0, 0, 0, 0, 0, 5}, {__constant_cpu_to_le32(11), 0, 0, 0, 0} }, "net-users"},
+       {{1, 1, {0, 0, 0, 0, 0, 5}, {__constant_cpu_to_le32(18), 0, 0, 0, 0} }, "sys"},
+       {{1, 2, {0, 0, 0, 0, 0, 5}, {__constant_cpu_to_le32(32), __constant_cpu_to_le32(544), 0, 0, 0} }, "root"},
+       {{1, 2, {0, 0, 0, 0, 0, 5}, {__constant_cpu_to_le32(32), __constant_cpu_to_le32(545), 0, 0, 0} }, "users"},
+       {{1, 2, {0, 0, 0, 0, 0, 5}, {__constant_cpu_to_le32(32), __constant_cpu_to_le32(546), 0, 0, 0} }, "guest"} }
 ;
 
 
@@ -46,8 +46,7 @@ static struct cifs_wksid wksidarr[NUM_WK_SIDS] = {
 static const struct cifs_sid sid_everyone = {
        1, 1, {0, 0, 0, 0, 0, 1}, {0} };
 /* group users */
-static const struct cifs_sid sid_user =
-               {1, 2 , {0, 0, 0, 0, 0, 5}, {} };
+static const struct cifs_sid sid_user = {1, 2 , {0, 0, 0, 0, 0, 5}, {} };
 
 
 int match_sid(struct cifs_sid *ctsid)
@@ -57,7 +56,7 @@ int match_sid(struct cifs_sid *ctsid)
        struct cifs_sid *cwsid;
 
        if (!ctsid)
-               return (-1);
+               return -1;
 
        for (i = 0; i < NUM_WK_SIDS; ++i) {
                cwsid = &(wksidarr[i].cifssid);
@@ -88,11 +87,11 @@ int match_sid(struct cifs_sid *ctsid)
                }
 
                cFYI(1, ("matching sid: %s\n", wksidarr[i].sidname));
-               return (0); /* sids compare/match */
+               return 0; /* sids compare/match */
        }
 
        cFYI(1, ("No matching sid"));
-       return (-1);
+       return -1;
 }
 
 /* if the two SIDs (roughly equivalent to a UUID for a user or group) are
@@ -103,16 +102,16 @@ int compare_sids(const struct cifs_sid *ctsid, const struct cifs_sid *cwsid)
        int num_subauth, num_sat, num_saw;
 
        if ((!ctsid) || (!cwsid))
-               return (0);
+               return 0;
 
        /* compare the revision */
        if (ctsid->revision != cwsid->revision)
-               return (0);
+               return 0;
 
        /* compare all of the six auth values */
        for (i = 0; i < 6; ++i) {
                if (ctsid->authority[i] != cwsid->authority[i])
-                       return (0);
+                       return 0;
        }
 
        /* compare all of the subauth values if any */
@@ -122,21 +121,69 @@ int compare_sids(const struct cifs_sid *ctsid, const struct cifs_sid *cwsid)
        if (num_subauth) {
                for (i = 0; i < num_subauth; ++i) {
                        if (ctsid->sub_auth[i] != cwsid->sub_auth[i])
-                               return (0);
+                               return 0;
                }
        }
 
-       return (1); /* sids compare/match */
+       return 1; /* sids compare/match */
 }
 
+
+/* copy ntsd, owner sid, and group sid from a security descriptor to another */
+static void copy_sec_desc(const struct cifs_ntsd *pntsd,
+                               struct cifs_ntsd *pnntsd, __u32 sidsoffset)
+{
+       int i;
+
+       struct cifs_sid *owner_sid_ptr, *group_sid_ptr;
+       struct cifs_sid *nowner_sid_ptr, *ngroup_sid_ptr;
+
+       /* copy security descriptor control portion */
+       pnntsd->revision = pntsd->revision;
+       pnntsd->type = pntsd->type;
+       pnntsd->dacloffset = cpu_to_le32(sizeof(struct cifs_ntsd));
+       pnntsd->sacloffset = 0;
+       pnntsd->osidoffset = cpu_to_le32(sidsoffset);
+       pnntsd->gsidoffset = cpu_to_le32(sidsoffset + sizeof(struct cifs_sid));
+
+       /* copy owner sid */
+       owner_sid_ptr = (struct cifs_sid *)((char *)pntsd +
+                               le32_to_cpu(pntsd->osidoffset));
+       nowner_sid_ptr = (struct cifs_sid *)((char *)pnntsd + sidsoffset);
+
+       nowner_sid_ptr->revision = owner_sid_ptr->revision;
+       nowner_sid_ptr->num_subauth = owner_sid_ptr->num_subauth;
+       for (i = 0; i < 6; i++)
+               nowner_sid_ptr->authority[i] = owner_sid_ptr->authority[i];
+       for (i = 0; i < 5; i++)
+               nowner_sid_ptr->sub_auth[i] = owner_sid_ptr->sub_auth[i];
+
+       /* copy group sid */
+       group_sid_ptr = (struct cifs_sid *)((char *)pntsd +
+                               le32_to_cpu(pntsd->gsidoffset));
+       ngroup_sid_ptr = (struct cifs_sid *)((char *)pnntsd + sidsoffset +
+                                       sizeof(struct cifs_sid));
+
+       ngroup_sid_ptr->revision = group_sid_ptr->revision;
+       ngroup_sid_ptr->num_subauth = group_sid_ptr->num_subauth;
+       for (i = 0; i < 6; i++)
+               ngroup_sid_ptr->authority[i] = group_sid_ptr->authority[i];
+       for (i = 0; i < 5; i++)
+               ngroup_sid_ptr->sub_auth[i] = group_sid_ptr->sub_auth[i];
+
+       return;
+}
+
+
 /*
    change posix mode to reflect permissions
    pmode is the existing mode (we only want to overwrite part of this
    bits to set can be: S_IRWXU, S_IRWXG or S_IRWXO ie 00700 or 00070 or 00007
 */
-static void access_flags_to_mode(__u32 ace_flags, int type, umode_t *pmode,
+static void access_flags_to_mode(__le32 ace_flags, int type, umode_t *pmode,
                                 umode_t *pbits_to_set)
 {
+       __u32 flags = le32_to_cpu(ace_flags);
        /* the order of ACEs is important.  The canonical order is to begin with
           DENY entries followed by ALLOW, otherwise an allow entry could be
           encountered first, making the subsequent deny entry like "dead code"
@@ -146,17 +193,17 @@ static void access_flags_to_mode(__u32 ace_flags, int type, umode_t *pmode,
        /* For deny ACEs we change the mask so that subsequent allow access
           control entries do not turn on the bits we are denying */
        if (type == ACCESS_DENIED) {
-               if (ace_flags & GENERIC_ALL) {
+               if (flags & GENERIC_ALL)
                        *pbits_to_set &= ~S_IRWXUGO;
-               }
-               if ((ace_flags & GENERIC_WRITE) ||
-                       ((ace_flags & FILE_WRITE_RIGHTS) == FILE_WRITE_RIGHTS))
+
+               if ((flags & GENERIC_WRITE) ||
+                       ((flags & FILE_WRITE_RIGHTS) == FILE_WRITE_RIGHTS))
                        *pbits_to_set &= ~S_IWUGO;
-               if ((ace_flags & GENERIC_READ) ||
-                       ((ace_flags & FILE_READ_RIGHTS) == FILE_READ_RIGHTS))
+               if ((flags & GENERIC_READ) ||
+                       ((flags & FILE_READ_RIGHTS) == FILE_READ_RIGHTS))
                        *pbits_to_set &= ~S_IRUGO;
-               if ((ace_flags & GENERIC_EXECUTE) ||
-                       ((ace_flags & FILE_EXEC_RIGHTS) == FILE_EXEC_RIGHTS))
+               if ((flags & GENERIC_EXECUTE) ||
+                       ((flags & FILE_EXEC_RIGHTS) == FILE_EXEC_RIGHTS))
                        *pbits_to_set &= ~S_IXUGO;
                return;
        } else if (type != ACCESS_ALLOWED) {
@@ -165,26 +212,22 @@ static void access_flags_to_mode(__u32 ace_flags, int type, umode_t *pmode,
        }
        /* else ACCESS_ALLOWED type */
 
-       if (ace_flags & GENERIC_ALL) {
+       if (flags & GENERIC_ALL) {
                *pmode |= (S_IRWXUGO & (*pbits_to_set));
-#ifdef CONFIG_CIFS_DEBUG2
-               cFYI(1, ("all perms"));
-#endif
+               cFYI(DBG2, ("all perms"));
                return;
        }
-       if ((ace_flags & GENERIC_WRITE) ||
-                       ((ace_flags & FILE_WRITE_RIGHTS) == FILE_WRITE_RIGHTS))
+       if ((flags & GENERIC_WRITE) ||
+                       ((flags & FILE_WRITE_RIGHTS) == FILE_WRITE_RIGHTS))
                *pmode |= (S_IWUGO & (*pbits_to_set));
-       if ((ace_flags & GENERIC_READ) ||
-                       ((ace_flags & FILE_READ_RIGHTS) == FILE_READ_RIGHTS))
+       if ((flags & GENERIC_READ) ||
+                       ((flags & FILE_READ_RIGHTS) == FILE_READ_RIGHTS))
                *pmode |= (S_IRUGO & (*pbits_to_set));
-       if ((ace_flags & GENERIC_EXECUTE) ||
-                       ((ace_flags & FILE_EXEC_RIGHTS) == FILE_EXEC_RIGHTS))
+       if ((flags & GENERIC_EXECUTE) ||
+                       ((flags & FILE_EXEC_RIGHTS) == FILE_EXEC_RIGHTS))
                *pmode |= (S_IXUGO & (*pbits_to_set));
 
-#ifdef CONFIG_CIFS_DEBUG2
-       cFYI(1, ("access flags 0x%x mode now 0x%x", ace_flags, *pmode));
-#endif
+       cFYI(DBG2, ("access flags 0x%x mode now 0x%x", flags, *pmode));
        return;
 }
 
@@ -213,12 +256,37 @@ static void mode_to_access_flags(umode_t mode, umode_t bits_to_use,
        if (mode & S_IXUGO)
                *pace_flags |= SET_FILE_EXEC_RIGHTS;
 
-#ifdef CONFIG_CIFS_DEBUG2
-       cFYI(1, ("mode: 0x%x, access flags now 0x%x", mode, *pace_flags));
-#endif
+       cFYI(DBG2, ("mode: 0x%x, access flags now 0x%x", mode, *pace_flags));
        return;
 }
 
+static __u16 fill_ace_for_sid(struct cifs_ace *pntace,
+                       const struct cifs_sid *psid, __u64 nmode, umode_t bits)
+{
+       int i;
+       __u16 size = 0;
+       __u32 access_req = 0;
+
+       pntace->type = ACCESS_ALLOWED;
+       pntace->flags = 0x0;
+       mode_to_access_flags(nmode, bits, &access_req);
+       if (!access_req)
+               access_req = SET_MINIMUM_RIGHTS;
+       pntace->access_req = cpu_to_le32(access_req);
+
+       pntace->sid.revision = psid->revision;
+       pntace->sid.num_subauth = psid->num_subauth;
+       for (i = 0; i < 6; i++)
+               pntace->sid.authority[i] = psid->authority[i];
+       for (i = 0; i < psid->num_subauth; i++)
+               pntace->sid.sub_auth[i] = psid->sub_auth[i];
+
+       size = 1 + 1 + 2 + 4 + 1 + 1 + 6 + (psid->num_subauth * 4);
+       pntace->size = cpu_to_le16(size);
+
+       return size;
+}
+
 
 #ifdef CONFIG_CIFS_DEBUG2
 static void dump_ace(struct cifs_ace *pace, char *end_of_acl)
@@ -242,7 +310,7 @@ static void dump_ace(struct cifs_ace *pace, char *end_of_acl)
                int i;
                cFYI(1, ("ACE revision %d num_auth %d type %d flags %d size %d",
                        pace->sid.revision, pace->sid.num_subauth, pace->type,
-                       pace->flags, pace->size));
+                       pace->flags, le16_to_cpu(pace->size)));
                for (i = 0; i < num_subauth; ++i) {
                        cFYI(1, ("ACE sub_auth[%d]: 0x%x", i,
                                le32_to_cpu(pace->sid.sub_auth[i])));
@@ -259,7 +327,7 @@ static void dump_ace(struct cifs_ace *pace, char *end_of_acl)
 
 static void parse_dacl(struct cifs_acl *pdacl, char *end_of_acl,
                       struct cifs_sid *pownersid, struct cifs_sid *pgrpsid,
-                      struct inode *inode)
+                      struct cifs_fattr *fattr)
 {
        int i;
        int num_aces = 0;
@@ -269,29 +337,28 @@ static void parse_dacl(struct cifs_acl *pdacl, char *end_of_acl,
 
        /* BB need to add parm so we can store the SID BB */
 
+       if (!pdacl) {
+               /* no DACL in the security descriptor, set
+                  all the permissions for user/group/other */
+               fattr->cf_mode |= S_IRWXUGO;
+               return;
+       }
+
        /* validate that we do not go past end of acl */
        if (end_of_acl < (char *)pdacl + le16_to_cpu(pdacl->size)) {
                cERROR(1, ("ACL too small to parse DACL"));
                return;
        }
 
-#ifdef CONFIG_CIFS_DEBUG2
-       cFYI(1, ("DACL revision %d size %d num aces %d",
+       cFYI(DBG2, ("DACL revision %d size %d num aces %d",
                le16_to_cpu(pdacl->revision), le16_to_cpu(pdacl->size),
                le32_to_cpu(pdacl->num_aces)));
-#endif
 
        /* reset rwx permissions for user/group/other.
           Also, if num_aces is 0 i.e. DACL has no ACEs,
           user/group/other have no permissions */
-       inode->i_mode &= ~(S_IRWXUGO);
+       fattr->cf_mode &= ~(S_IRWXUGO);
 
-       if (!pdacl) {
-               /* no DACL in the security descriptor, set
-                  all the permissions for user/group/other */
-               inode->i_mode |= S_IRWXUGO;
-               return;
-       }
        acl_base = (char *)pdacl;
        acl_size = sizeof(struct cifs_acl);
 
@@ -304,10 +371,6 @@ static void parse_dacl(struct cifs_acl *pdacl, char *end_of_acl,
                ppace = kmalloc(num_aces * sizeof(struct cifs_ace *),
                                GFP_KERNEL);
 
-/*             cifscred->cecount = pdacl->num_aces;
-               cifscred->aces = kmalloc(num_aces *
-                       sizeof(struct cifs_ace *), GFP_KERNEL);*/
-
                for (i = 0; i < num_aces; ++i) {
                        ppace[i] = (struct cifs_ace *) (acl_base + acl_size);
 #ifdef CONFIG_CIFS_DEBUG2
@@ -316,17 +379,17 @@ static void parse_dacl(struct cifs_acl *pdacl, char *end_of_acl,
                        if (compare_sids(&(ppace[i]->sid), pownersid))
                                access_flags_to_mode(ppace[i]->access_req,
                                                     ppace[i]->type,
-                                                    &(inode->i_mode),
+                                                    &fattr->cf_mode,
                                                     &user_mask);
                        if (compare_sids(&(ppace[i]->sid), pgrpsid))
                                access_flags_to_mode(ppace[i]->access_req,
                                                     ppace[i]->type,
-                                                    &(inode->i_mode),
+                                                    &fattr->cf_mode,
                                                     &group_mask);
                        if (compare_sids(&(ppace[i]->sid), &sid_everyone))
                                access_flags_to_mode(ppace[i]->access_req,
                                                     ppace[i]->type,
-                                                    &(inode->i_mode),
+                                                    &fattr->cf_mode,
                                                     &other_mask);
 
 /*                     memcpy((void *)(&(cifscred->aces[i])),
@@ -344,6 +407,28 @@ static void parse_dacl(struct cifs_acl *pdacl, char *end_of_acl,
 }
 
 
+static int set_chmod_dacl(struct cifs_acl *pndacl, struct cifs_sid *pownersid,
+                       struct cifs_sid *pgrpsid, __u64 nmode)
+{
+       u16 size = 0;
+       struct cifs_acl *pnndacl;
+
+       pnndacl = (struct cifs_acl *)((char *)pndacl + sizeof(struct cifs_acl));
+
+       size += fill_ace_for_sid((struct cifs_ace *) ((char *)pnndacl + size),
+                                       pownersid, nmode, S_IRWXU);
+       size += fill_ace_for_sid((struct cifs_ace *)((char *)pnndacl + size),
+                                       pgrpsid, nmode, S_IRWXG);
+       size += fill_ace_for_sid((struct cifs_ace *)((char *)pnndacl + size),
+                                        &sid_everyone, nmode, S_IRWXO);
+
+       pndacl->size = cpu_to_le16(size + sizeof(struct cifs_acl));
+       pndacl->num_aces = cpu_to_le32(3);
+
+       return 0;
+}
+
+
 static int parse_sid(struct cifs_sid *psid, char *end_of_acl)
 {
        /* BB need to add parm so we can store the SID BB */
@@ -379,7 +464,7 @@ static int parse_sid(struct cifs_sid *psid, char *end_of_acl)
 
 /* Convert CIFS ACL to POSIX form */
 static int parse_sec_desc(struct cifs_ntsd *pntsd, int acl_len,
-                         struct inode *inode)
+                         struct cifs_fattr *fattr)
 {
        int rc;
        struct cifs_sid *owner_sid_ptr, *group_sid_ptr;
@@ -387,7 +472,7 @@ static int parse_sec_desc(struct cifs_ntsd *pntsd, int acl_len,
        char *end_of_acl = ((char *)pntsd) + acl_len;
        __u32 dacloffset;
 
-       if ((inode == NULL) || (pntsd == NULL))
+       if (pntsd == NULL)
                return -EIO;
 
        owner_sid_ptr = (struct cifs_sid *)((char *)pntsd +
@@ -396,13 +481,11 @@ static int parse_sec_desc(struct cifs_ntsd *pntsd, int acl_len,
                                le32_to_cpu(pntsd->gsidoffset));
        dacloffset = le32_to_cpu(pntsd->dacloffset);
        dacl_ptr = (struct cifs_acl *)((char *)pntsd + dacloffset);
-#ifdef CONFIG_CIFS_DEBUG2
-       cFYI(1, ("revision %d type 0x%x ooffset 0x%x goffset 0x%x "
+       cFYI(DBG2, ("revision %d type 0x%x ooffset 0x%x goffset 0x%x "
                 "sacloffset 0x%x dacloffset 0x%x",
                 pntsd->revision, pntsd->type, le32_to_cpu(pntsd->osidoffset),
                 le32_to_cpu(pntsd->gsidoffset),
                 le32_to_cpu(pntsd->sacloffset), dacloffset));
-#endif
 /*     cifs_dump_mem("owner_sid: ", owner_sid_ptr, 64); */
        rc = parse_sid(owner_sid_ptr, end_of_acl);
        if (rc)
@@ -414,7 +497,7 @@ static int parse_sec_desc(struct cifs_ntsd *pntsd, int acl_len,
 
        if (dacloffset)
                parse_dacl(dacl_ptr, end_of_acl, owner_sid_ptr,
-                          group_sid_ptr, inode);
+                          group_sid_ptr, fattr);
        else
                cFYI(1, ("no ACL")); /* BB grant all or default perms? */
 
@@ -425,81 +508,186 @@ static int parse_sec_desc(struct cifs_ntsd *pntsd, int acl_len,
        memcpy((void *)(&(cifscred->gsid)), (void *)group_sid_ptr,
                        sizeof(struct cifs_sid)); */
 
-
-       return (0);
+       return 0;
 }
 
 
-/* Retrieve an ACL from the server */
-static struct cifs_ntsd *get_cifs_acl(u32 *pacllen, struct inode *inode,
-                                      const char *path)
+/* Convert permission bits from mode to equivalent CIFS ACL */
+static int build_sec_desc(struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd,
+                               struct inode *inode, __u64 nmode)
+{
+       int rc = 0;
+       __u32 dacloffset;
+       __u32 ndacloffset;
+       __u32 sidsoffset;
+       struct cifs_sid *owner_sid_ptr, *group_sid_ptr;
+       struct cifs_acl *dacl_ptr = NULL;  /* no need for SACL ptr */
+       struct cifs_acl *ndacl_ptr = NULL; /* no need for SACL ptr */
+
+       if ((inode == NULL) || (pntsd == NULL) || (pnntsd == NULL))
+               return -EIO;
+
+       owner_sid_ptr = (struct cifs_sid *)((char *)pntsd +
+                               le32_to_cpu(pntsd->osidoffset));
+       group_sid_ptr = (struct cifs_sid *)((char *)pntsd +
+                               le32_to_cpu(pntsd->gsidoffset));
+
+       dacloffset = le32_to_cpu(pntsd->dacloffset);
+       dacl_ptr = (struct cifs_acl *)((char *)pntsd + dacloffset);
+
+       ndacloffset = sizeof(struct cifs_ntsd);
+       ndacl_ptr = (struct cifs_acl *)((char *)pnntsd + ndacloffset);
+       ndacl_ptr->revision = dacl_ptr->revision;
+       ndacl_ptr->size = 0;
+       ndacl_ptr->num_aces = 0;
+
+       rc = set_chmod_dacl(ndacl_ptr, owner_sid_ptr, group_sid_ptr, nmode);
+
+       sidsoffset = ndacloffset + le16_to_cpu(ndacl_ptr->size);
+
+       /* copy security descriptor control portion and owner and group sid */
+       copy_sec_desc(pntsd, pnntsd, sidsoffset);
+
+       return rc;
+}
+
+static struct cifs_ntsd *get_cifs_acl_by_fid(struct cifs_sb_info *cifs_sb,
+               __u16 fid, u32 *pacllen)
 {
-       struct cifsFileInfo *open_file;
-       int unlock_file = FALSE;
-       int xid;
-       int rc = -EIO;
-       __u16 fid;
-       struct super_block *sb;
-       struct cifs_sb_info *cifs_sb;
        struct cifs_ntsd *pntsd = NULL;
+       int xid, rc;
 
-       cFYI(1, ("get mode from ACL for %s", path));
+       xid = GetXid();
+       rc = CIFSSMBGetCIFSACL(xid, cifs_sb->tcon, fid, &pntsd, pacllen);
+       FreeXid(xid);
 
-       if (inode == NULL)
-               return NULL;
+
+       cFYI(1, ("GetCIFSACL rc = %d ACL len %d", rc, *pacllen));
+       return pntsd;
+}
+
+static struct cifs_ntsd *get_cifs_acl_by_path(struct cifs_sb_info *cifs_sb,
+               const char *path, u32 *pacllen)
+{
+       struct cifs_ntsd *pntsd = NULL;
+       int oplock = 0;
+       int xid, rc;
+       __u16 fid;
 
        xid = GetXid();
-       open_file = find_readable_file(CIFS_I(inode));
-       sb = inode->i_sb;
-       if (sb == NULL) {
-               FreeXid(xid);
-               return NULL;
-       }
-       cifs_sb = CIFS_SB(sb);
-
-       if (open_file) {
-               unlock_file = TRUE;
-               fid = open_file->netfid;
-       } else {
-               int oplock = FALSE;
-               /* open file */
-               rc = CIFSSMBOpen(xid, cifs_sb->tcon, path, FILE_OPEN,
-                               READ_CONTROL, 0, &fid, &oplock, NULL,
-                               cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
-                                       CIFS_MOUNT_MAP_SPECIAL_CHR);
-               if (rc != 0) {
-                       cERROR(1, ("Unable to open file to get ACL"));
-                       FreeXid(xid);
-                       return NULL;
-               }
+
+       rc = CIFSSMBOpen(xid, cifs_sb->tcon, path, FILE_OPEN, READ_CONTROL, 0,
+                        &fid, &oplock, NULL, cifs_sb->local_nls,
+                        cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
+       if (rc) {
+               cERROR(1, ("Unable to open file to get ACL"));
+               goto out;
        }
 
        rc = CIFSSMBGetCIFSACL(xid, cifs_sb->tcon, fid, &pntsd, pacllen);
        cFYI(1, ("GetCIFSACL rc = %d ACL len %d", rc, *pacllen));
-       if (unlock_file == TRUE)
-               atomic_dec(&open_file->wrtPending);
-       else
-               CIFSSMBClose(xid, cifs_sb->tcon, fid);
 
+       CIFSSMBClose(xid, cifs_sb->tcon, fid);
+ out:
        FreeXid(xid);
        return pntsd;
 }
 
+/* Retrieve an ACL from the server */
+static struct cifs_ntsd *get_cifs_acl(struct cifs_sb_info *cifs_sb,
+                                     struct inode *inode, const char *path,
+                                     u32 *pacllen)
+{
+       struct cifs_ntsd *pntsd = NULL;
+       struct cifsFileInfo *open_file = NULL;
+
+       if (inode)
+               open_file = find_readable_file(CIFS_I(inode));
+       if (!open_file)
+               return get_cifs_acl_by_path(cifs_sb, path, pacllen);
+
+       pntsd = get_cifs_acl_by_fid(cifs_sb, open_file->netfid, pacllen);
+       cifsFileInfo_put(open_file);
+       return pntsd;
+}
+
+static int set_cifs_acl_by_fid(struct cifs_sb_info *cifs_sb, __u16 fid,
+               struct cifs_ntsd *pnntsd, u32 acllen)
+{
+       int xid, rc;
+
+       xid = GetXid();
+       rc = CIFSSMBSetCIFSACL(xid, cifs_sb->tcon, fid, pnntsd, acllen);
+       FreeXid(xid);
+
+       cFYI(DBG2, ("SetCIFSACL rc = %d", rc));
+       return rc;
+}
+
+static int set_cifs_acl_by_path(struct cifs_sb_info *cifs_sb, const char *path,
+               struct cifs_ntsd *pnntsd, u32 acllen)
+{
+       int oplock = 0;
+       int xid, rc;
+       __u16 fid;
+
+       xid = GetXid();
+
+       rc = CIFSSMBOpen(xid, cifs_sb->tcon, path, FILE_OPEN, WRITE_DAC, 0,
+                        &fid, &oplock, NULL, cifs_sb->local_nls,
+                        cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
+       if (rc) {
+               cERROR(1, ("Unable to open file to set ACL"));
+               goto out;
+       }
+
+       rc = CIFSSMBSetCIFSACL(xid, cifs_sb->tcon, fid, pnntsd, acllen);
+       cFYI(DBG2, ("SetCIFSACL rc = %d", rc));
+
+       CIFSSMBClose(xid, cifs_sb->tcon, fid);
+ out:
+       FreeXid(xid);
+       return rc;
+}
+
+/* Set an ACL on the server */
+static int set_cifs_acl(struct cifs_ntsd *pnntsd, __u32 acllen,
+                               struct inode *inode, const char *path)
+{
+       struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
+       struct cifsFileInfo *open_file;
+       int rc;
+
+       cFYI(DBG2, ("set ACL for %s from mode 0x%x", path, inode->i_mode));
+
+       open_file = find_readable_file(CIFS_I(inode));
+       if (!open_file)
+               return set_cifs_acl_by_path(cifs_sb, path, pnntsd, acllen);
+
+       rc = set_cifs_acl_by_fid(cifs_sb, open_file->netfid, pnntsd, acllen);
+       cifsFileInfo_put(open_file);
+       return rc;
+}
+
 /* Translate the CIFS ACL (simlar to NTFS ACL) for a file into mode bits */
-void acl_to_uid_mode(struct inode *inode, const char *path)
+void
+cifs_acl_to_fattr(struct cifs_sb_info *cifs_sb, struct cifs_fattr *fattr,
+                 struct inode *inode, const char *path, const __u16 *pfid)
 {
        struct cifs_ntsd *pntsd = NULL;
        u32 acllen = 0;
        int rc = 0;
 
-#ifdef CONFIG_CIFS_DEBUG2
-       cFYI(1, ("converting ACL to mode for %s", path));
-#endif
-       pntsd = get_cifs_acl(&acllen, inode, path);
+       cFYI(DBG2, ("converting ACL to mode for %s", path));
+
+       if (pfid)
+               pntsd = get_cifs_acl_by_fid(cifs_sb, *pfid, &acllen);
+       else
+               pntsd = get_cifs_acl(cifs_sb, inode, path, &acllen);
 
        /* if we can retrieve the ACL, now parse Access Control Entries, ACEs */
        if (pntsd)
-               rc = parse_sec_desc(pntsd, acllen, inode);
+               rc = parse_sec_desc(pntsd, acllen, fattr);
        if (rc)
                cFYI(1, ("parse sec desc failed rc = %d", rc));
 
@@ -508,24 +696,49 @@ void acl_to_uid_mode(struct inode *inode, const char *path)
 }
 
 /* Convert mode bits to an ACL so we can update the ACL on the server */
-int mode_to_acl(struct inode *inode, const char *path)
+int mode_to_acl(struct inode *inode, const char *path, __u64 nmode)
 {
        int rc = 0;
-       __u32 acllen = 0;
-       struct cifs_ntsd *pntsd = NULL;
+       __u32 secdesclen = 0;
+       struct cifs_ntsd *pntsd = NULL; /* acl obtained from server */
+       struct cifs_ntsd *pnntsd = NULL; /* modified acl to be sent to server */
 
-       cFYI(1, ("set ACL from mode for %s", path));
+       cFYI(DBG2, ("set ACL from mode for %s", path));
 
        /* Get the security descriptor */
-       pntsd = get_cifs_acl(&acllen, inode, path);
+       pntsd = get_cifs_acl(CIFS_SB(inode->i_sb), inode, path, &secdesclen);
+
+       /* Add three ACEs for owner, group, everyone getting rid of
+          other ACEs as chmod disables ACEs and set the security descriptor */
+
+       if (pntsd) {
+               /* allocate memory for the smb header,
+                  set security descriptor request security descriptor
+                  parameters, and secuirty descriptor itself */
+
+               secdesclen = secdesclen < DEFSECDESCLEN ?
+                                       DEFSECDESCLEN : secdesclen;
+               pnntsd = kmalloc(secdesclen, GFP_KERNEL);
+               if (!pnntsd) {
+                       cERROR(1, ("Unable to allocate security descriptor"));
+                       kfree(pntsd);
+                       return -ENOMEM;
+               }
 
-       /* Add/Modify the three ACEs for owner, group, everyone
-          while retaining the other ACEs */
+               rc = build_sec_desc(pntsd, pnntsd, inode, nmode);
 
-       /* Set the security descriptor */
+               cFYI(DBG2, ("build_sec_desc rc: %d", rc));
 
+               if (!rc) {
+                       /* Set the security descriptor */
+                       rc = set_cifs_acl(pnntsd, secdesclen, inode, path);
+                       cFYI(DBG2, ("set_cifs_acl rc: %d", rc));
+               }
+
+               kfree(pnntsd);
+               kfree(pntsd);
+       }
 
-       kfree(pntsd);
        return rc;
 }
 #endif /* CONFIG_CIFS_EXPERIMENTAL */