[CIFS] acl support part 6
Acked-by: Shirish Pargaonkar <shirishp@us.ibm.com> CC: Cyrill Gorcunov <gorcunov@gmail.com> Signed-off-by: Steve French <sfrench@us.ibm.com>
This commit is contained in:
parent
44093ca2fe
commit
630f3f0c45
10 changed files with 169 additions and 60 deletions
|
@ -97,7 +97,7 @@ int match_sid(struct cifs_sid *ctsid)
|
||||||
|
|
||||||
/* if the two SIDs (roughly equivalent to a UUID for a user or group) are
|
/* if the two SIDs (roughly equivalent to a UUID for a user or group) are
|
||||||
the same returns 1, if they do not match returns 0 */
|
the same returns 1, if they do not match returns 0 */
|
||||||
int compare_sids(struct cifs_sid *ctsid, struct cifs_sid *cwsid)
|
int compare_sids(const struct cifs_sid *ctsid, const struct cifs_sid *cwsid)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
int num_subauth, num_sat, num_saw;
|
int num_subauth, num_sat, num_saw;
|
||||||
|
@ -129,28 +129,77 @@ int compare_sids(struct cifs_sid *ctsid, struct cifs_sid *cwsid)
|
||||||
return (1); /* sids compare/match */
|
return (1); /* sids compare/match */
|
||||||
}
|
}
|
||||||
|
|
||||||
void get_mode_from_acl(struct inode * inode, const char * path)
|
/*
|
||||||
|
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 access_flags, umode_t * pmode,
|
||||||
|
umode_t bits_to_set)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
#ifdef CONFIG_CIFS_DEBUG2
|
||||||
|
cFYI(1, ("access flags 0x%x mode now 0x%x", access_flags, *pmode);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
|
||||||
cFYI(1, ("get mode from ACL for %s", path));
|
cFYI(1, ("get mode from ACL for %s", path));
|
||||||
|
|
||||||
if (inode == NULL)
|
if (inode == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* find an open readable handle
|
xid = GetXid();
|
||||||
if handle found
|
open_file = find_readable_file(CIFS_I(inode));
|
||||||
lock handle
|
if (open_file) {
|
||||||
else open file
|
unlock_file = TRUE;
|
||||||
if no open file can not hurt to check if path is null
|
fid = open_file->netfid;
|
||||||
GetCIFSACL
|
} else {
|
||||||
for all ACEs in ACL {
|
int oplock = FALSE;
|
||||||
if U or G or O
|
/* open file */
|
||||||
inode->i_mode = parse_ace(file_type, UG or O, ace->perms, inode->i_mode)
|
sb = inode->i_sb;
|
||||||
else continue
|
if (sb == NULL) {
|
||||||
|
FreeXid(xid);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cifs_sb = CIFS_SB(sb);
|
||||||
|
rc = CIFSSMBOpen(xid, cifs_sb->tcon, path, FILE_OPEN,
|
||||||
|
GENERIC_READ, 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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if handle open close it
|
|
||||||
else unlock handle */
|
|
||||||
|
|
||||||
|
/* rc = CIFSSMBGetCIFSACL(xid, cifs_sb->tcon, fid, pntsd, acllen,
|
||||||
|
ACL_TYPE_ACCESS); */
|
||||||
|
|
||||||
|
if (unlock_file == TRUE)
|
||||||
|
atomic_dec(&open_file->wrtPending);
|
||||||
|
else
|
||||||
|
CIFSSMBClose(xid, cifs_sb->tcon, fid);
|
||||||
|
|
||||||
|
/* parse ACEs e.g.
|
||||||
|
rc = parse_sec_desc(pntsd, acllen, inode);
|
||||||
|
*/
|
||||||
|
|
||||||
|
FreeXid(xid);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,7 +242,8 @@ static void parse_ace(struct cifs_ace *pace, char *end_of_acl)
|
||||||
|
|
||||||
|
|
||||||
static void parse_dacl(struct cifs_acl *pdacl, 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 cifs_sid *pownersid, struct cifs_sid *pgrpsid
|
||||||
|
struct inode *inode)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
int num_aces = 0;
|
int num_aces = 0;
|
||||||
|
@ -281,7 +331,8 @@ static int parse_sid(struct cifs_sid *psid, char *end_of_acl)
|
||||||
|
|
||||||
|
|
||||||
/* Convert CIFS ACL to POSIX form */
|
/* Convert CIFS ACL to POSIX form */
|
||||||
int parse_sec_desc(struct cifs_ntsd *pntsd, int acl_len)
|
static int parse_sec_desc(struct cifs_ntsd *pntsd, int acl_len,
|
||||||
|
struct inode *inode)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
struct cifs_sid *owner_sid_ptr, *group_sid_ptr;
|
struct cifs_sid *owner_sid_ptr, *group_sid_ptr;
|
||||||
|
@ -310,7 +361,7 @@ int parse_sec_desc(struct cifs_ntsd *pntsd, int acl_len)
|
||||||
if (rc)
|
if (rc)
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
parse_dacl(dacl_ptr, end_of_acl, owner_sid_ptr, group_sid_ptr);
|
parse_dacl(dacl_ptr, end_of_acl, owner_sid_ptr, group_sid_ptr, inode);
|
||||||
|
|
||||||
/* cifscred->uid = owner_sid_ptr->rid;
|
/* cifscred->uid = owner_sid_ptr->rid;
|
||||||
cifscred->gid = group_sid_ptr->rid;
|
cifscred->gid = group_sid_ptr->rid;
|
||||||
|
|
|
@ -61,6 +61,9 @@ extern int checkSMB(struct smb_hdr *smb, __u16 mid, unsigned int length);
|
||||||
extern int is_valid_oplock_break(struct smb_hdr *smb, struct TCP_Server_Info *);
|
extern int is_valid_oplock_break(struct smb_hdr *smb, struct TCP_Server_Info *);
|
||||||
extern int is_size_safe_to_change(struct cifsInodeInfo *, __u64 eof);
|
extern int is_size_safe_to_change(struct cifsInodeInfo *, __u64 eof);
|
||||||
extern struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *);
|
extern struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *);
|
||||||
|
#ifdef CONFIG_CIFS_EXPERIMENTAL
|
||||||
|
extern struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *);
|
||||||
|
#endif
|
||||||
extern unsigned int smbCalcSize(struct smb_hdr *ptr);
|
extern unsigned int smbCalcSize(struct smb_hdr *ptr);
|
||||||
extern unsigned int smbCalcSize_LE(struct smb_hdr *ptr);
|
extern unsigned int smbCalcSize_LE(struct smb_hdr *ptr);
|
||||||
extern int decode_negTokenInit(unsigned char *security_blob, int length,
|
extern int decode_negTokenInit(unsigned char *security_blob, int length,
|
||||||
|
@ -92,7 +95,7 @@ extern int cifs_get_inode_info(struct inode **pinode,
|
||||||
extern int cifs_get_inode_info_unix(struct inode **pinode,
|
extern int cifs_get_inode_info_unix(struct inode **pinode,
|
||||||
const unsigned char *search_path,
|
const unsigned char *search_path,
|
||||||
struct super_block *sb, int xid);
|
struct super_block *sb, int xid);
|
||||||
extern void get_mode_from_acl(struct inode * inode, const char * search_path);
|
extern void acl_to_uid_mode(struct inode *inode, const char *search_path);
|
||||||
extern int cifs_mount(struct super_block *, struct cifs_sb_info *, char *,
|
extern int cifs_mount(struct super_block *, struct cifs_sb_info *, char *,
|
||||||
const char *);
|
const char *);
|
||||||
extern int cifs_umount(struct super_block *, struct cifs_sb_info *);
|
extern int cifs_umount(struct super_block *, struct cifs_sb_info *);
|
||||||
|
@ -311,7 +314,6 @@ extern void setup_ntlmv2_rsp(struct cifsSesInfo *, char *,
|
||||||
#ifdef CONFIG_CIFS_WEAK_PW_HASH
|
#ifdef CONFIG_CIFS_WEAK_PW_HASH
|
||||||
extern void calc_lanman_hash(struct cifsSesInfo *ses, char *lnm_session_key);
|
extern void calc_lanman_hash(struct cifsSesInfo *ses, char *lnm_session_key);
|
||||||
#endif /* CIFS_WEAK_PW_HASH */
|
#endif /* CIFS_WEAK_PW_HASH */
|
||||||
extern int parse_sec_desc(struct cifs_ntsd *, int);
|
|
||||||
extern int CIFSSMBCopy(int xid,
|
extern int CIFSSMBCopy(int xid,
|
||||||
struct cifsTconInfo *source_tcon,
|
struct cifsTconInfo *source_tcon,
|
||||||
const char *fromName,
|
const char *fromName,
|
||||||
|
@ -336,8 +338,7 @@ extern int CIFSSMBSetEA(const int xid, struct cifsTconInfo *tcon,
|
||||||
const void *ea_value, const __u16 ea_value_len,
|
const void *ea_value, const __u16 ea_value_len,
|
||||||
const struct nls_table *nls_codepage, int remap_special_chars);
|
const struct nls_table *nls_codepage, int remap_special_chars);
|
||||||
extern int CIFSSMBGetCIFSACL(const int xid, struct cifsTconInfo *tcon,
|
extern int CIFSSMBGetCIFSACL(const int xid, struct cifsTconInfo *tcon,
|
||||||
__u16 fid, char *acl_inf, const int buflen,
|
__u16 fid, struct cifs_ntsd **acl_inf, __u32 *buflen);
|
||||||
const int acl_type /* ACCESS vs. DEFAULT */);
|
|
||||||
extern int CIFSSMBGetPosixACL(const int xid, struct cifsTconInfo *tcon,
|
extern int CIFSSMBGetPosixACL(const int xid, struct cifsTconInfo *tcon,
|
||||||
const unsigned char *searchName,
|
const unsigned char *searchName,
|
||||||
char *acl_inf, const int buflen, const int acl_type,
|
char *acl_inf, const int buflen, const int acl_type,
|
||||||
|
|
|
@ -2526,12 +2526,15 @@ smb_init_ntransact(const __u16 sub_command, const int setup_count,
|
||||||
|
|
||||||
static int
|
static int
|
||||||
validate_ntransact(char *buf, char **ppparm, char **ppdata,
|
validate_ntransact(char *buf, char **ppparm, char **ppdata,
|
||||||
int *pdatalen, int *pparmlen)
|
__u32 *pdatalen, __u32 *pparmlen)
|
||||||
{
|
{
|
||||||
char *end_of_smb;
|
char *end_of_smb;
|
||||||
__u32 data_count, data_offset, parm_count, parm_offset;
|
__u32 data_count, data_offset, parm_count, parm_offset;
|
||||||
struct smb_com_ntransact_rsp *pSMBr;
|
struct smb_com_ntransact_rsp *pSMBr;
|
||||||
|
|
||||||
|
*pdatalen = 0;
|
||||||
|
*pparmlen = 0;
|
||||||
|
|
||||||
if (buf == NULL)
|
if (buf == NULL)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
|
@ -2568,6 +2571,8 @@ validate_ntransact(char *buf, char **ppparm, char **ppdata,
|
||||||
cFYI(1, ("parm count and data count larger than SMB"));
|
cFYI(1, ("parm count and data count larger than SMB"));
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
*pdatalen = data_count;
|
||||||
|
*pparmlen = parm_count;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#endif /* CIFS_EXPERIMENTAL */
|
#endif /* CIFS_EXPERIMENTAL */
|
||||||
|
@ -3069,8 +3074,7 @@ CIFSGetExtAttr(const int xid, struct cifsTconInfo *tcon,
|
||||||
/* Get Security Descriptor (by handle) from remote server for a file or dir */
|
/* Get Security Descriptor (by handle) from remote server for a file or dir */
|
||||||
int
|
int
|
||||||
CIFSSMBGetCIFSACL(const int xid, struct cifsTconInfo *tcon, __u16 fid,
|
CIFSSMBGetCIFSACL(const int xid, struct cifsTconInfo *tcon, __u16 fid,
|
||||||
/* BB fix up return info */ char *acl_inf, const int buflen,
|
struct cifs_ntsd **acl_inf, __u32 *pbuflen)
|
||||||
const int acl_type)
|
|
||||||
{
|
{
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
int buf_type = 0;
|
int buf_type = 0;
|
||||||
|
@ -3079,6 +3083,9 @@ CIFSSMBGetCIFSACL(const int xid, struct cifsTconInfo *tcon, __u16 fid,
|
||||||
|
|
||||||
cFYI(1, ("GetCifsACL"));
|
cFYI(1, ("GetCifsACL"));
|
||||||
|
|
||||||
|
*pbuflen = 0;
|
||||||
|
*acl_inf = NULL;
|
||||||
|
|
||||||
rc = smb_init_ntransact(NT_TRANSACT_QUERY_SECURITY_DESC, 0,
|
rc = smb_init_ntransact(NT_TRANSACT_QUERY_SECURITY_DESC, 0,
|
||||||
8 /* parm len */, tcon, (void **) &pSMB);
|
8 /* parm len */, tcon, (void **) &pSMB);
|
||||||
if (rc)
|
if (rc)
|
||||||
|
@ -3101,34 +3108,52 @@ CIFSSMBGetCIFSACL(const int xid, struct cifsTconInfo *tcon, __u16 fid,
|
||||||
if (rc) {
|
if (rc) {
|
||||||
cFYI(1, ("Send error in QuerySecDesc = %d", rc));
|
cFYI(1, ("Send error in QuerySecDesc = %d", rc));
|
||||||
} else { /* decode response */
|
} else { /* decode response */
|
||||||
struct cifs_ntsd *psec_desc;
|
|
||||||
__le32 * parm;
|
__le32 * parm;
|
||||||
int parm_len;
|
__u32 parm_len;
|
||||||
int data_len;
|
__u32 acl_len;
|
||||||
int acl_len;
|
|
||||||
struct smb_com_ntransact_rsp *pSMBr;
|
struct smb_com_ntransact_rsp *pSMBr;
|
||||||
|
char *pdata;
|
||||||
|
|
||||||
/* validate_nttransact */
|
/* validate_nttransact */
|
||||||
rc = validate_ntransact(iov[0].iov_base, (char **)&parm,
|
rc = validate_ntransact(iov[0].iov_base, (char **)&parm,
|
||||||
(char **)&psec_desc,
|
&pdata, &parm_len, pbuflen);
|
||||||
&parm_len, &data_len);
|
|
||||||
if (rc)
|
if (rc)
|
||||||
goto qsec_out;
|
goto qsec_out;
|
||||||
pSMBr = (struct smb_com_ntransact_rsp *)iov[0].iov_base;
|
pSMBr = (struct smb_com_ntransact_rsp *)iov[0].iov_base;
|
||||||
|
|
||||||
cFYI(1, ("smb %p parm %p data %p", pSMBr, parm, psec_desc));
|
cFYI(1, ("smb %p parm %p data %p", pSMBr, parm, *acl_inf));
|
||||||
|
|
||||||
if (le32_to_cpu(pSMBr->ParameterCount) != 4) {
|
if (le32_to_cpu(pSMBr->ParameterCount) != 4) {
|
||||||
rc = -EIO; /* bad smb */
|
rc = -EIO; /* bad smb */
|
||||||
|
*pbuflen = 0;
|
||||||
goto qsec_out;
|
goto qsec_out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* BB check that data area is minimum length and as big as acl_len */
|
/* BB check that data area is minimum length and as big as acl_len */
|
||||||
|
|
||||||
acl_len = le32_to_cpu(*parm);
|
acl_len = le32_to_cpu(*parm);
|
||||||
/* BB check if (acl_len > bufsize) */
|
if (acl_len != *pbuflen) {
|
||||||
|
cERROR(1, ("acl length %d does not match %d",
|
||||||
|
acl_len, *pbuflen));
|
||||||
|
if (*pbuflen > acl_len)
|
||||||
|
*pbuflen = acl_len;
|
||||||
|
}
|
||||||
|
|
||||||
parse_sec_desc(psec_desc, acl_len);
|
/* check if buffer is big enough for the acl
|
||||||
|
header followed by the smallest SID */
|
||||||
|
if ((*pbuflen < sizeof(struct cifs_ntsd) + 8) ||
|
||||||
|
(*pbuflen >= 64 * 1024)) {
|
||||||
|
cERROR(1, ("bad acl length %d", *pbuflen));
|
||||||
|
rc = -EINVAL;
|
||||||
|
*pbuflen = 0;
|
||||||
|
} else {
|
||||||
|
*acl_inf = kmalloc(*pbuflen, GFP_KERNEL);
|
||||||
|
if (*acl_inf == NULL) {
|
||||||
|
*pbuflen = 0;
|
||||||
|
rc = -ENOMEM;
|
||||||
|
}
|
||||||
|
memcpy(*acl_inf, pdata, *pbuflen);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
qsec_out:
|
qsec_out:
|
||||||
if (buf_type == CIFS_SMALL_BUFFER)
|
if (buf_type == CIFS_SMALL_BUFFER)
|
||||||
|
|
|
@ -1026,6 +1026,37 @@ static ssize_t cifs_write(struct file *file, const char *write_data,
|
||||||
return total_written;
|
return total_written;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_CIFS_EXPERIMENTAL
|
||||||
|
struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode)
|
||||||
|
{
|
||||||
|
struct cifsFileInfo *open_file = NULL;
|
||||||
|
|
||||||
|
read_lock(&GlobalSMBSeslock);
|
||||||
|
/* we could simply get the first_list_entry since write-only entries
|
||||||
|
are always at the end of the list but since the first entry might
|
||||||
|
have a close pending, we go through the whole list */
|
||||||
|
list_for_each_entry(open_file, &cifs_inode->openFileList, flist) {
|
||||||
|
if (open_file->closePend)
|
||||||
|
continue;
|
||||||
|
if (open_file->pfile && ((open_file->pfile->f_flags & O_RDWR) ||
|
||||||
|
(open_file->pfile->f_flags & O_RDONLY))) {
|
||||||
|
if (!open_file->invalidHandle) {
|
||||||
|
/* found a good file */
|
||||||
|
/* lock it so it will not be closed on us */
|
||||||
|
atomic_inc(&open_file->wrtPending);
|
||||||
|
read_unlock(&GlobalSMBSeslock);
|
||||||
|
return open_file;
|
||||||
|
} /* else might as well continue, and look for
|
||||||
|
another, or simply have the caller reopen it
|
||||||
|
again rather than trying to fix this handle */
|
||||||
|
} else /* write only file */
|
||||||
|
break; /* write only files are last so must be done */
|
||||||
|
}
|
||||||
|
read_unlock(&GlobalSMBSeslock);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode)
|
struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode)
|
||||||
{
|
{
|
||||||
struct cifsFileInfo *open_file;
|
struct cifsFileInfo *open_file;
|
||||||
|
|
|
@ -530,7 +530,7 @@ int cifs_get_inode_info(struct inode **pinode,
|
||||||
#ifdef CONFIG_CIFS_EXPERIMENTAL
|
#ifdef CONFIG_CIFS_EXPERIMENTAL
|
||||||
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) {
|
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) {
|
||||||
cFYI(1, ("Getting mode bits from ACL"));
|
cFYI(1, ("Getting mode bits from ACL"));
|
||||||
get_mode_from_acl(inode, search_path);
|
acl_to_uid_mode(inode, search_path);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) {
|
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) {
|
||||||
|
|
|
@ -265,6 +265,8 @@ ssize_t cifs_getxattr(struct dentry *direntry, const char *ea_name,
|
||||||
else if(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) {
|
else if(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) {
|
||||||
__u16 fid;
|
__u16 fid;
|
||||||
int oplock = FALSE;
|
int oplock = FALSE;
|
||||||
|
struct cifs_ntsd *pacl = NULL;
|
||||||
|
__u32 buflen = 0;
|
||||||
if (experimEnabled)
|
if (experimEnabled)
|
||||||
rc = CIFSSMBOpen(xid, pTcon, full_path,
|
rc = CIFSSMBOpen(xid, pTcon, full_path,
|
||||||
FILE_OPEN, GENERIC_READ, 0, &fid,
|
FILE_OPEN, GENERIC_READ, 0, &fid,
|
||||||
|
@ -274,9 +276,8 @@ ssize_t cifs_getxattr(struct dentry *direntry, const char *ea_name,
|
||||||
/* else rc is EOPNOTSUPP from above */
|
/* else rc is EOPNOTSUPP from above */
|
||||||
|
|
||||||
if(rc == 0) {
|
if(rc == 0) {
|
||||||
rc = CIFSSMBGetCIFSACL(xid, pTcon, fid,
|
rc = CIFSSMBGetCIFSACL(xid, pTcon, fid, &pacl,
|
||||||
ea_value, buf_size,
|
&buflen);
|
||||||
ACL_TYPE_ACCESS);
|
|
||||||
CIFSSMBClose(xid, pTcon, fid);
|
CIFSSMBClose(xid, pTcon, fid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue