diff --git a/fs/cifs/CHANGES b/fs/cifs/CHANGES index f92e0ee661ae..6d84ca2beead 100644 --- a/fs/cifs/CHANGES +++ b/fs/cifs/CHANGES @@ -1,7 +1,9 @@ Version 1.50 ------------ Fix NTLMv2 signing. NFS server mounted over cifs works (if cifs mount is -done with "serverino" mount option). +done with "serverino" mount option). Add support for POSIX Unlink +(helps with certain sharing violation cases when server such as +Samba supports newer POSIX CIFS Protocol Extensions). Version 1.49 ------------ diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h index 9044d9886f0d..6a2056e58ceb 100644 --- a/fs/cifs/cifspdu.h +++ b/fs/cifs/cifspdu.h @@ -2155,6 +2155,12 @@ typedef struct { /* struct following varies based on requested level */ } __attribute__((packed)) OPEN_PSX_RSP; /* level 0x209 SetPathInfo data */ +#define SMB_POSIX_UNLINK_FILE_TARGET 0 +#define SMB_POSIX_UNLINK_DIRECTORY_TARGET 1 + +struct unlink_psx_rq { /* level 0x20a SetPathInfo */ + __le16 type; +} __attribute__((packed)); struct file_internal_info { __u64 UniqueId; /* inode number */ diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 3a76c72f3c89..04a69dafedba 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -196,7 +196,10 @@ extern int CIFSSMBMkDir(const int xid, struct cifsTconInfo *tcon, extern int CIFSSMBRmDir(const int xid, struct cifsTconInfo *tcon, const char *name, const struct nls_table *nls_codepage, int remap_special_chars); - +extern int CIFSPOSIXDelFile(const int xid, struct cifsTconInfo *tcon, + const char *name, __u16 type, + const struct nls_table *nls_codepage, + int remap_special_chars); extern int CIFSSMBDelFile(const int xid, struct cifsTconInfo *tcon, const char *name, const struct nls_table *nls_codepage, diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 3ab78b776977..b339f5f128da 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -788,6 +788,82 @@ CIFSSMBLogoff(const int xid, struct cifsSesInfo *ses) return rc; } +int +CIFSPOSIXDelFile(const int xid, struct cifsTconInfo *tcon, const char *fileName, + __u16 type, const struct nls_table *nls_codepage, int remap) +{ + TRANSACTION2_SPI_REQ *pSMB = NULL; + TRANSACTION2_SPI_RSP *pSMBr = NULL; + struct unlink_psx_rq *pRqD; + int name_len; + int rc = 0; + int bytes_returned = 0; + __u16 params, param_offset, offset, byte_count; + + cFYI(1, ("In POSIX delete")); +PsxDelete: + rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return rc; + + if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { + name_len = + cifsConvertToUCS((__le16 *) pSMB->FileName, fileName, + PATH_MAX, nls_codepage, remap); + name_len++; /* trailing null */ + name_len *= 2; + } else { /* BB add path length overrun check */ + name_len = strnlen(fileName, PATH_MAX); + name_len++; /* trailing null */ + strncpy(pSMB->FileName, fileName, name_len); + } + + params = 6 + name_len; + pSMB->MaxParameterCount = cpu_to_le16(2); + pSMB->MaxDataCount = 0; /* BB double check this with jra */ + pSMB->MaxSetupCount = 0; + pSMB->Reserved = 0; + pSMB->Flags = 0; + pSMB->Timeout = 0; + pSMB->Reserved2 = 0; + param_offset = offsetof(struct smb_com_transaction2_spi_req, + InformationLevel) - 4; + offset = param_offset + params; + + /* Setup pointer to Request Data (inode type) */ + pRqD = (struct unlink_psx_rq *)(((char *)&pSMB->hdr.Protocol) + offset); + pRqD->type = cpu_to_le16(type); + pSMB->ParameterOffset = cpu_to_le16(param_offset); + pSMB->DataOffset = cpu_to_le16(offset); + pSMB->SetupCount = 1; + pSMB->Reserved3 = 0; + pSMB->SubCommand = cpu_to_le16(TRANS2_SET_PATH_INFORMATION); + byte_count = 3 /* pad */ + params + sizeof(struct unlink_psx_rq); + + pSMB->DataCount = cpu_to_le16(sizeof(struct unlink_psx_rq)); + pSMB->TotalDataCount = cpu_to_le16(sizeof(struct unlink_psx_rq)); + pSMB->ParameterCount = cpu_to_le16(params); + pSMB->TotalParameterCount = pSMB->ParameterCount; + pSMB->InformationLevel = cpu_to_le16(SMB_POSIX_UNLINK); + pSMB->Reserved4 = 0; + pSMB->hdr.smb_buf_length += byte_count; + pSMB->ByteCount = cpu_to_le16(byte_count); + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned, 0); + if (rc) { + cFYI(1, ("Posix delete returned %d", rc)); + } + cifs_buf_release(pSMB); + + cifs_stats_inc(&tcon->num_deletes); + + if (rc == -EAGAIN) + goto PsxDelete; + + return rc; +} + int CIFSSMBDelFile(const int xid, struct cifsTconInfo *tcon, const char *fileName, const struct nls_table *nls_codepage, int remap) @@ -933,7 +1009,6 @@ CIFSPOSIXCreate(const int xid, struct cifsTconInfo *tcon, __u32 posix_flags, int name_len; int rc = 0; int bytes_returned = 0; - char *data_offset; __u16 params, param_offset, offset, byte_count, count; OPEN_PSX_REQ * pdata; OPEN_PSX_RSP * psx_rsp; @@ -969,7 +1044,6 @@ CIFSPOSIXCreate(const int xid, struct cifsTconInfo *tcon, __u32 posix_flags, param_offset = offsetof(struct smb_com_transaction2_spi_req, InformationLevel) - 4; offset = param_offset + params; - data_offset = (char *) (&pSMB->hdr.Protocol) + offset; pdata = (OPEN_PSX_REQ *)(((char *)&pSMB->hdr.Protocol) + offset); pdata->Level = SMB_QUERY_FILE_UNIX_BASIC; pdata->Permissions = cpu_to_le64(mode); @@ -1671,7 +1745,6 @@ CIFSSMBPosixLock(const int xid, struct cifsTconInfo *tcon, { struct smb_com_transaction2_sfi_req *pSMB = NULL; struct smb_com_transaction2_sfi_rsp *pSMBr = NULL; - char *data_offset; struct cifs_posix_lock *parm_data; int rc = 0; int timeout = 0; @@ -1698,8 +1771,6 @@ CIFSSMBPosixLock(const int xid, struct cifsTconInfo *tcon, param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4; offset = param_offset + params; - data_offset = (char *) (&pSMB->hdr.Protocol) + offset; - count = sizeof(struct cifs_posix_lock); pSMB->MaxParameterCount = cpu_to_le16(2); pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find max SMB PDU from sess */ @@ -2120,9 +2191,7 @@ CIFSUnixCreateSymLink(const int xid, struct cifsTconInfo *tcon, (struct smb_hdr *) pSMBr, &bytes_returned, 0); cifs_stats_inc(&tcon->num_symlinks); if (rc) { - cFYI(1, - ("Send error in SetPathInfo (create symlink) = %d", - rc)); + cFYI(1, ("Send error in SetPathInfo create symlink = %d", rc)); } if (pSMB) diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index a1ca55650505..cfa5b360d12e 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -620,9 +620,21 @@ int cifs_unlink(struct inode *inode, struct dentry *direntry) FreeXid(xid); return -ENOMEM; } + + if ((pTcon->ses->capabilities & CAP_UNIX) && + (CIFS_UNIX_POSIX_PATH_OPS_CAP & + le64_to_cpu(pTcon->fsUnixInfo.Capability))) { + rc = CIFSPOSIXDelFile(xid, pTcon, full_path, + SMB_POSIX_UNLINK_FILE_TARGET, cifs_sb->local_nls, + cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); + cFYI(1, ("posix del rc %d", rc)); + if ((rc == 0) || (rc == -ENOENT)) + goto psx_del_no_retry; + } + rc = CIFSSMBDelFile(xid, pTcon, full_path, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); - +psx_del_no_retry: if (!rc) { if (direntry->d_inode) drop_nlink(direntry->d_inode);