提交 97837582 编写于 作者: S Steve French

[CIFS] Allow setting mode via cifs acl

Requires cifsacl mount flag to be on and CIFS_EXPERIMENTAL enabled

CC: Shirish Pargaonkar <shirishp@us.ibm.com>
Signed-off-by: NSteve French <sfrench@us.ibm.com>
上级 28c5a02a
......@@ -5,6 +5,8 @@ Enable experimental Kerberos support. Return writebehind errors on flush
and sync so that events like out of disk space get reported properly on
cached files. Fix setxattr failure to certain Samba versions. Fix mount
of second share to disconnected server session (autoreconnect on this).
Add ability to modify cifs acls for handling chmod (when mounted with
cifsacl flag).
Version 1.51
------------
......
......@@ -129,6 +129,54 @@ int compare_sids(const struct cifs_sid *ctsid, const struct cifs_sid *cwsid)
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] =
cpu_to_le32(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
......@@ -220,6 +268,33 @@ static void mode_to_access_flags(umode_t mode, umode_t bits_to_use,
return;
}
static __le16 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)
......@@ -243,7 +318,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])));
......@@ -346,6 +421,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)
{
__le16 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 = 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 */
......@@ -432,6 +529,46 @@ static int parse_sec_desc(struct cifs_ntsd *pntsd, int acl_len,
}
/* Convert permission bits from mode to equivalent CIFS ACL */
static int build_sec_desc(struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd,
int acl_len, 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);
}
/* Retrieve an ACL from the server */
static struct cifs_ntsd *get_cifs_acl(u32 *pacllen, struct inode *inode,
const char *path)
......@@ -487,6 +624,64 @@ static struct cifs_ntsd *get_cifs_acl(u32 *pacllen, struct inode *inode,
return pntsd;
}
/* Set an ACL on the server */
static int set_cifs_acl(struct cifs_ntsd *pnntsd, __u32 acllen,
struct inode *inode, const char *path)
{
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;
#ifdef CONFIG_CIFS_DEBUG2
cFYI(1, ("set ACL for %s from mode 0x%x", path, inode->i_mode));
#endif
if (!inode)
return (rc);
sb = inode->i_sb;
if (sb == NULL)
return (rc);
cifs_sb = CIFS_SB(sb);
xid = GetXid();
open_file = find_readable_file(CIFS_I(inode));
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,
WRITE_DAC, 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 set ACL"));
FreeXid(xid);
return (rc);
}
}
rc = CIFSSMBSetCIFSACL(xid, cifs_sb->tcon, fid, pnntsd, acllen);
#ifdef CONFIG_CIFS_DEBUG2
cFYI(1, ("SetCIFSACL rc = %d", rc));
#endif
if (unlock_file == TRUE)
atomic_dec(&open_file->wrtPending);
else
CIFSSMBClose(xid, cifs_sb->tcon, fid);
FreeXid(xid);
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)
{
......@@ -510,24 +705,53 @@ 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;
struct cifs_ntsd *pntsd = NULL; /* acl obtained from server */
struct cifs_ntsd *pnntsd = NULL; /* modified acl to be sent to server */
#ifdef CONFIG_CIFS_DEBUG2
cFYI(1, ("set ACL from mode for %s", path));
#endif
/* Get the security descriptor */
pntsd = get_cifs_acl(&acllen, inode, path);
/* Add/Modify the three ACEs for owner, group, everyone
while retaining the other ACEs */
/* Add three ACEs for owner, group, everyone getting rid of
other ACEs as chmod disables ACEs and set the security descriptor */
/* Set the security descriptor */
if (pntsd) {
/* allocate memory for the smb header,
set security descriptor request security descriptor
parameters, and secuirty descriptor itself */
pnntsd = kmalloc(acllen, GFP_KERNEL);
if (!pnntsd) {
cERROR(1, ("Unable to allocate security descriptor"));
kfree(pntsd);
return (-ENOMEM);
}
rc = build_sec_desc(pntsd, pnntsd, acllen, inode, nmode);
#ifdef CONFIG_CIFS_DEBUG2
cFYI(1, ("build_sec_desc rc: %d", rc));
#endif
if (!rc) {
/* Set the security descriptor */
rc = set_cifs_acl(pnntsd, acllen, inode, path);
#ifdef CONFIG_CIFS_DEBUG2
cFYI(1, ("set_cifs_acl rc: %d", rc));
#endif
}
kfree(pnntsd);
kfree(pntsd);
return rc;
}
return (rc);
}
#endif /* CONFIG_CIFS_EXPERIMENTAL */
......@@ -237,6 +237,9 @@
| DELETE | READ_CONTROL | WRITE_DAC \
| WRITE_OWNER | SYNCHRONIZE)
#define SET_MINIMUM_RIGHTS (FILE_READ_EA | FILE_READ_ATTRIBUTES \
| READ_CONTROL | SYNCHRONIZE)
/*
* Invalid readdir handle
......
......@@ -97,7 +97,7 @@ extern int cifs_get_inode_info_unix(struct inode **pinode,
const unsigned char *search_path,
struct super_block *sb, int xid);
extern void acl_to_uid_mode(struct inode *inode, const char *search_path);
extern int mode_to_acl(struct inode *inode, const char *path);
extern int mode_to_acl(struct inode *inode, const char *path, __u64);
extern int cifs_mount(struct super_block *, struct cifs_sb_info *, char *,
const char *);
......@@ -342,6 +342,8 @@ extern int CIFSSMBSetEA(const int xid, struct cifsTconInfo *tcon,
const struct nls_table *nls_codepage, int remap_special_chars);
extern int CIFSSMBGetCIFSACL(const int xid, struct cifsTconInfo *tcon,
__u16 fid, struct cifs_ntsd **acl_inf, __u32 *buflen);
extern int CIFSSMBSetCIFSACL(const int, struct cifsTconInfo *, __u16,
struct cifs_ntsd *, __u32);
extern int CIFSSMBGetPosixACL(const int xid, struct cifsTconInfo *tcon,
const unsigned char *searchName,
char *acl_inf, const int buflen, const int acl_type,
......
......@@ -3156,6 +3156,71 @@ CIFSSMBGetCIFSACL(const int xid, struct cifsTconInfo *tcon, __u16 fid,
/* cifs_small_buf_release(pSMB); */ /* Freed earlier now in SendReceive2 */
return rc;
}
int
CIFSSMBSetCIFSACL(const int xid, struct cifsTconInfo *tcon, __u16 fid,
struct cifs_ntsd *pntsd, __u32 acllen)
{
__u16 byte_count, param_count, data_count, param_offset, data_offset;
int rc = 0;
int bytes_returned = 0;
SET_SEC_DESC_REQ *pSMB = NULL;
NTRANSACT_RSP *pSMBr = NULL;
setCifsAclRetry:
rc = smb_init(SMB_COM_NT_TRANSACT, 19, tcon, (void **) &pSMB,
(void **) &pSMBr);
if (rc)
return (rc);
pSMB->MaxSetupCount = 0;
pSMB->Reserved = 0;
param_count = 8;
param_offset = offsetof(struct smb_com_transaction_ssec_req, Fid) - 4;
data_count = acllen;
data_offset = param_offset + param_count;
byte_count = 3 /* pad */ + param_count;
pSMB->DataCount = cpu_to_le32(data_count);
pSMB->TotalDataCount = pSMB->DataCount;
pSMB->MaxParameterCount = cpu_to_le32(4);
pSMB->MaxDataCount = cpu_to_le32(16384);
pSMB->ParameterCount = cpu_to_le32(param_count);
pSMB->ParameterOffset = cpu_to_le32(param_offset);
pSMB->TotalParameterCount = pSMB->ParameterCount;
pSMB->DataOffset = cpu_to_le32(data_offset);
pSMB->SetupCount = 0;
pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_SET_SECURITY_DESC);
pSMB->ByteCount = cpu_to_le16(byte_count+data_count);
pSMB->Fid = fid; /* file handle always le */
pSMB->Reserved2 = 0;
pSMB->AclFlags = cpu_to_le32(CIFS_ACL_DACL);
if (pntsd && acllen) {
memcpy((char *) &pSMBr->hdr.Protocol + data_offset,
(char *) pntsd,
acllen);
pSMB->hdr.smb_buf_length += (byte_count + data_count);
} else
pSMB->hdr.smb_buf_length += byte_count;
rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
(struct smb_hdr *) pSMBr, &bytes_returned, 0);
cFYI(1, ("SetCIFSACL bytes_returned: %d, rc: %d", bytes_returned, rc));
if (rc)
cFYI(1, ("Set CIFS ACL returned %d", rc));
cifs_buf_release(pSMB);
if (rc == -EAGAIN)
goto setCifsAclRetry;
return (rc);
}
#endif /* CONFIG_CIFS_EXPERIMENTAL */
/* Legacy Query Path Information call for lookup to old servers such
......
......@@ -1607,7 +1607,13 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs)
CIFS_MOUNT_MAP_SPECIAL_CHR);
else if (attrs->ia_valid & ATTR_MODE) {
rc = 0;
#ifdef CONFIG_CIFS_EXPERIMENTAL
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL)
rc = mode_to_acl(direntry->d_inode, full_path, mode);
else if ((mode & S_IWUGO) == 0) /* not writeable */ {
#else
if ((mode & S_IWUGO) == 0) /* not writeable */ {
#endif
if ((cifsInode->cifsAttrs & ATTR_READONLY) == 0) {
set_dosattr = TRUE;
time_buf.Attributes =
......@@ -1626,10 +1632,10 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs)
if (time_buf.Attributes == 0)
time_buf.Attributes |= cpu_to_le32(ATTR_NORMAL);
}
/* BB to be implemented -
via Windows security descriptors or streams */
/* CIFSSMBWinSetPerms(xid, pTcon, full_path, mode, uid, gid,
cifs_sb->local_nls); */
#ifdef CONFIG_CIFS_EXPERIMENTAL
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL)
mode_to_acl(direntry->d_inode, full_path, mode);
#endif
}
if (attrs->ia_valid & ATTR_ATIME) {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册