mirror of
https://github.com/adulau/aha.git
synced 2024-12-30 20:56:23 +00:00
Merge git://git.kernel.org/pub/scm/linux/kernel/git/sfrench/cifs-2.6
* git://git.kernel.org/pub/scm/linux/kernel/git/sfrench/cifs-2.6: [CIFS] Check that last search entry resume key is valid [CIFS] make sure we have the right resume info before calling CIFSFindNext [CIFS] clean up error handling in cifs_unlink [CIFS] fix some settings of cifsAttrs after calling SetFileInfo and SetPathInfo cifs: explicitly revoke SPNEGO key after session setup cifs: Convert cifs to new aops. [CIFS] update DOS attributes in cifsInode if we successfully changed them cifs: remove NULL termination from rename target in CIFSSMBRenameOpenFIle cifs: work around samba returning -ENOENT on SetFileDisposition call cifs: fix inverted NULL check after kmalloc [CIFS] clean up upcall handling for dns_resolver keys [CIFS] fix busy-file renames and refactor cifs_rename logic cifs: add function to set file disposition [CIFS] add constants for string lengths of keynames in SPNEGO upcall string cifs: move rename and delete-on-close logic into helper function cifs: have find_writeable_file prefer filehandles opened by same task cifs: don't use GFP_KERNEL with GFP_NOFS [CIFS] use common code for turning off ATTR_READONLY in cifs_unlink cifs: clean up variables in cifs_unlink
This commit is contained in:
commit
86ed5a93b8
12 changed files with 636 additions and 487 deletions
|
@ -66,11 +66,28 @@ struct key_type cifs_spnego_key_type = {
|
||||||
.describe = user_describe,
|
.describe = user_describe,
|
||||||
};
|
};
|
||||||
|
|
||||||
#define MAX_VER_STR_LEN 8 /* length of longest version string e.g.
|
/* length of longest version string e.g. strlen("ver=0xFF") */
|
||||||
strlen("ver=0xFF") */
|
#define MAX_VER_STR_LEN 8
|
||||||
#define MAX_MECH_STR_LEN 13 /* length of longest security mechanism name, eg
|
|
||||||
in future could have strlen(";sec=ntlmsspi") */
|
/* length of longest security mechanism name, eg in future could have
|
||||||
#define MAX_IPV6_ADDR_LEN 42 /* eg FEDC:BA98:7654:3210:FEDC:BA98:7654:3210/60 */
|
* strlen(";sec=ntlmsspi") */
|
||||||
|
#define MAX_MECH_STR_LEN 13
|
||||||
|
|
||||||
|
/* max possible addr len eg FEDC:BA98:7654:3210:FEDC:BA98:7654:3210/60 */
|
||||||
|
#define MAX_IPV6_ADDR_LEN 42
|
||||||
|
|
||||||
|
/* strlen of "host=" */
|
||||||
|
#define HOST_KEY_LEN 5
|
||||||
|
|
||||||
|
/* strlen of ";ip4=" or ";ip6=" */
|
||||||
|
#define IP_KEY_LEN 5
|
||||||
|
|
||||||
|
/* strlen of ";uid=0x" */
|
||||||
|
#define UID_KEY_LEN 7
|
||||||
|
|
||||||
|
/* strlen of ";user=" */
|
||||||
|
#define USER_KEY_LEN 6
|
||||||
|
|
||||||
/* get a key struct with a SPNEGO security blob, suitable for session setup */
|
/* get a key struct with a SPNEGO security blob, suitable for session setup */
|
||||||
struct key *
|
struct key *
|
||||||
cifs_get_spnego_key(struct cifsSesInfo *sesInfo)
|
cifs_get_spnego_key(struct cifsSesInfo *sesInfo)
|
||||||
|
@ -84,11 +101,11 @@ cifs_get_spnego_key(struct cifsSesInfo *sesInfo)
|
||||||
/* length of fields (with semicolons): ver=0xyz ip4=ipaddress
|
/* length of fields (with semicolons): ver=0xyz ip4=ipaddress
|
||||||
host=hostname sec=mechanism uid=0xFF user=username */
|
host=hostname sec=mechanism uid=0xFF user=username */
|
||||||
desc_len = MAX_VER_STR_LEN +
|
desc_len = MAX_VER_STR_LEN +
|
||||||
6 /* len of "host=" */ + strlen(hostname) +
|
HOST_KEY_LEN + strlen(hostname) +
|
||||||
5 /* len of ";ipv4=" */ + MAX_IPV6_ADDR_LEN +
|
IP_KEY_LEN + MAX_IPV6_ADDR_LEN +
|
||||||
MAX_MECH_STR_LEN +
|
MAX_MECH_STR_LEN +
|
||||||
7 /* len of ";uid=0x" */ + (sizeof(uid_t) * 2) +
|
UID_KEY_LEN + (sizeof(uid_t) * 2) +
|
||||||
6 /* len of ";user=" */ + strlen(sesInfo->userName) + 1;
|
USER_KEY_LEN + strlen(sesInfo->userName) + 1;
|
||||||
|
|
||||||
spnego_key = ERR_PTR(-ENOMEM);
|
spnego_key = ERR_PTR(-ENOMEM);
|
||||||
description = kzalloc(desc_len, GFP_KERNEL);
|
description = kzalloc(desc_len, GFP_KERNEL);
|
||||||
|
|
|
@ -41,7 +41,7 @@ extern int cifs_create(struct inode *, struct dentry *, int,
|
||||||
struct nameidata *);
|
struct nameidata *);
|
||||||
extern struct dentry *cifs_lookup(struct inode *, struct dentry *,
|
extern struct dentry *cifs_lookup(struct inode *, struct dentry *,
|
||||||
struct nameidata *);
|
struct nameidata *);
|
||||||
extern int cifs_unlink(struct inode *, struct dentry *);
|
extern int cifs_unlink(struct inode *dir, struct dentry *dentry);
|
||||||
extern int cifs_hardlink(struct dentry *, struct inode *, struct dentry *);
|
extern int cifs_hardlink(struct dentry *, struct inode *, struct dentry *);
|
||||||
extern int cifs_mknod(struct inode *, struct dentry *, int, dev_t);
|
extern int cifs_mknod(struct inode *, struct dentry *, int, dev_t);
|
||||||
extern int cifs_mkdir(struct inode *, struct dentry *, int);
|
extern int cifs_mkdir(struct inode *, struct dentry *, int);
|
||||||
|
|
|
@ -309,6 +309,7 @@ struct cifs_search_info {
|
||||||
__u32 resume_key;
|
__u32 resume_key;
|
||||||
char *ntwrk_buf_start;
|
char *ntwrk_buf_start;
|
||||||
char *srch_entries_start;
|
char *srch_entries_start;
|
||||||
|
char *last_entry;
|
||||||
char *presume_name;
|
char *presume_name;
|
||||||
unsigned int resume_name_len;
|
unsigned int resume_name_len;
|
||||||
bool endOfSearch:1;
|
bool endOfSearch:1;
|
||||||
|
|
|
@ -179,6 +179,8 @@ extern int CIFSSMBSetPathInfo(const int xid, struct cifsTconInfo *tcon,
|
||||||
extern int CIFSSMBSetFileInfo(const int xid, struct cifsTconInfo *tcon,
|
extern int CIFSSMBSetFileInfo(const int xid, struct cifsTconInfo *tcon,
|
||||||
const FILE_BASIC_INFO *data, __u16 fid,
|
const FILE_BASIC_INFO *data, __u16 fid,
|
||||||
__u32 pid_of_opener);
|
__u32 pid_of_opener);
|
||||||
|
extern int CIFSSMBSetFileDisposition(const int xid, struct cifsTconInfo *tcon,
|
||||||
|
bool delete_file, __u16 fid, __u32 pid_of_opener);
|
||||||
#if 0
|
#if 0
|
||||||
extern int CIFSSMBSetAttrLegacy(int xid, struct cifsTconInfo *tcon,
|
extern int CIFSSMBSetAttrLegacy(int xid, struct cifsTconInfo *tcon,
|
||||||
char *fileName, __u16 dos_attributes,
|
char *fileName, __u16 dos_attributes,
|
||||||
|
@ -229,7 +231,7 @@ extern int CIFSSMBRename(const int xid, struct cifsTconInfo *tcon,
|
||||||
const struct nls_table *nls_codepage,
|
const struct nls_table *nls_codepage,
|
||||||
int remap_special_chars);
|
int remap_special_chars);
|
||||||
extern int CIFSSMBRenameOpenFile(const int xid, struct cifsTconInfo *pTcon,
|
extern int CIFSSMBRenameOpenFile(const int xid, struct cifsTconInfo *pTcon,
|
||||||
int netfid, char *target_name,
|
int netfid, const char *target_name,
|
||||||
const struct nls_table *nls_codepage,
|
const struct nls_table *nls_codepage,
|
||||||
int remap_special_chars);
|
int remap_special_chars);
|
||||||
extern int CIFSCreateHardLink(const int xid,
|
extern int CIFSCreateHardLink(const int xid,
|
||||||
|
|
|
@ -2017,7 +2017,7 @@ renameRetry:
|
||||||
}
|
}
|
||||||
|
|
||||||
int CIFSSMBRenameOpenFile(const int xid, struct cifsTconInfo *pTcon,
|
int CIFSSMBRenameOpenFile(const int xid, struct cifsTconInfo *pTcon,
|
||||||
int netfid, char *target_name,
|
int netfid, const char *target_name,
|
||||||
const struct nls_table *nls_codepage, int remap)
|
const struct nls_table *nls_codepage, int remap)
|
||||||
{
|
{
|
||||||
struct smb_com_transaction2_sfi_req *pSMB = NULL;
|
struct smb_com_transaction2_sfi_req *pSMB = NULL;
|
||||||
|
@ -2071,7 +2071,7 @@ int CIFSSMBRenameOpenFile(const int xid, struct cifsTconInfo *pTcon,
|
||||||
remap);
|
remap);
|
||||||
}
|
}
|
||||||
rename_info->target_name_len = cpu_to_le32(2 * len_of_str);
|
rename_info->target_name_len = cpu_to_le32(2 * len_of_str);
|
||||||
count = 12 /* sizeof(struct set_file_rename) */ + (2 * len_of_str) + 2;
|
count = 12 /* sizeof(struct set_file_rename) */ + (2 * len_of_str);
|
||||||
byte_count += count;
|
byte_count += count;
|
||||||
pSMB->DataCount = cpu_to_le16(count);
|
pSMB->DataCount = cpu_to_le16(count);
|
||||||
pSMB->TotalDataCount = pSMB->DataCount;
|
pSMB->TotalDataCount = pSMB->DataCount;
|
||||||
|
@ -3614,6 +3614,8 @@ findFirstRetry:
|
||||||
/* BB remember to free buffer if error BB */
|
/* BB remember to free buffer if error BB */
|
||||||
rc = validate_t2((struct smb_t2_rsp *)pSMBr);
|
rc = validate_t2((struct smb_t2_rsp *)pSMBr);
|
||||||
if (rc == 0) {
|
if (rc == 0) {
|
||||||
|
unsigned int lnoff;
|
||||||
|
|
||||||
if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)
|
if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)
|
||||||
psrch_inf->unicode = true;
|
psrch_inf->unicode = true;
|
||||||
else
|
else
|
||||||
|
@ -3636,6 +3638,17 @@ findFirstRetry:
|
||||||
le16_to_cpu(parms->SearchCount);
|
le16_to_cpu(parms->SearchCount);
|
||||||
psrch_inf->index_of_last_entry = 2 /* skip . and .. */ +
|
psrch_inf->index_of_last_entry = 2 /* skip . and .. */ +
|
||||||
psrch_inf->entries_in_buffer;
|
psrch_inf->entries_in_buffer;
|
||||||
|
lnoff = le16_to_cpu(parms->LastNameOffset);
|
||||||
|
if (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE <
|
||||||
|
lnoff) {
|
||||||
|
cERROR(1, ("ignoring corrupt resume name"));
|
||||||
|
psrch_inf->last_entry = NULL;
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
psrch_inf->last_entry = psrch_inf->srch_entries_start +
|
||||||
|
lnoff;
|
||||||
|
|
||||||
*pnetfid = parms->SearchHandle;
|
*pnetfid = parms->SearchHandle;
|
||||||
} else {
|
} else {
|
||||||
cifs_buf_release(pSMB);
|
cifs_buf_release(pSMB);
|
||||||
|
@ -3725,6 +3738,8 @@ int CIFSFindNext(const int xid, struct cifsTconInfo *tcon,
|
||||||
rc = validate_t2((struct smb_t2_rsp *)pSMBr);
|
rc = validate_t2((struct smb_t2_rsp *)pSMBr);
|
||||||
|
|
||||||
if (rc == 0) {
|
if (rc == 0) {
|
||||||
|
unsigned int lnoff;
|
||||||
|
|
||||||
/* BB fixme add lock for file (srch_info) struct here */
|
/* BB fixme add lock for file (srch_info) struct here */
|
||||||
if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)
|
if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)
|
||||||
psrch_inf->unicode = true;
|
psrch_inf->unicode = true;
|
||||||
|
@ -3751,6 +3766,16 @@ int CIFSFindNext(const int xid, struct cifsTconInfo *tcon,
|
||||||
le16_to_cpu(parms->SearchCount);
|
le16_to_cpu(parms->SearchCount);
|
||||||
psrch_inf->index_of_last_entry +=
|
psrch_inf->index_of_last_entry +=
|
||||||
psrch_inf->entries_in_buffer;
|
psrch_inf->entries_in_buffer;
|
||||||
|
lnoff = le16_to_cpu(parms->LastNameOffset);
|
||||||
|
if (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE <
|
||||||
|
lnoff) {
|
||||||
|
cERROR(1, ("ignoring corrupt resume name"));
|
||||||
|
psrch_inf->last_entry = NULL;
|
||||||
|
return rc;
|
||||||
|
} else
|
||||||
|
psrch_inf->last_entry =
|
||||||
|
psrch_inf->srch_entries_start + lnoff;
|
||||||
|
|
||||||
/* cFYI(1,("fnxt2 entries in buf %d index_of_last %d",
|
/* cFYI(1,("fnxt2 entries in buf %d index_of_last %d",
|
||||||
psrch_inf->entries_in_buffer, psrch_inf->index_of_last_entry)); */
|
psrch_inf->entries_in_buffer, psrch_inf->index_of_last_entry)); */
|
||||||
|
|
||||||
|
@ -4876,6 +4901,61 @@ CIFSSMBSetFileInfo(const int xid, struct cifsTconInfo *tcon,
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
CIFSSMBSetFileDisposition(const int xid, struct cifsTconInfo *tcon,
|
||||||
|
bool delete_file, __u16 fid, __u32 pid_of_opener)
|
||||||
|
{
|
||||||
|
struct smb_com_transaction2_sfi_req *pSMB = NULL;
|
||||||
|
char *data_offset;
|
||||||
|
int rc = 0;
|
||||||
|
__u16 params, param_offset, offset, byte_count, count;
|
||||||
|
|
||||||
|
cFYI(1, ("Set File Disposition (via SetFileInfo)"));
|
||||||
|
rc = small_smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB);
|
||||||
|
|
||||||
|
if (rc)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener);
|
||||||
|
pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16));
|
||||||
|
|
||||||
|
params = 6;
|
||||||
|
pSMB->MaxSetupCount = 0;
|
||||||
|
pSMB->Reserved = 0;
|
||||||
|
pSMB->Flags = 0;
|
||||||
|
pSMB->Timeout = 0;
|
||||||
|
pSMB->Reserved2 = 0;
|
||||||
|
param_offset = offsetof(struct smb_com_transaction2_sfi_req, Fid) - 4;
|
||||||
|
offset = param_offset + params;
|
||||||
|
|
||||||
|
data_offset = (char *) (&pSMB->hdr.Protocol) + offset;
|
||||||
|
|
||||||
|
count = 1;
|
||||||
|
pSMB->MaxParameterCount = cpu_to_le16(2);
|
||||||
|
/* BB find max SMB PDU from sess */
|
||||||
|
pSMB->MaxDataCount = cpu_to_le16(1000);
|
||||||
|
pSMB->SetupCount = 1;
|
||||||
|
pSMB->Reserved3 = 0;
|
||||||
|
pSMB->SubCommand = cpu_to_le16(TRANS2_SET_FILE_INFORMATION);
|
||||||
|
byte_count = 3 /* pad */ + params + count;
|
||||||
|
pSMB->DataCount = cpu_to_le16(count);
|
||||||
|
pSMB->ParameterCount = cpu_to_le16(params);
|
||||||
|
pSMB->TotalDataCount = pSMB->DataCount;
|
||||||
|
pSMB->TotalParameterCount = pSMB->ParameterCount;
|
||||||
|
pSMB->ParameterOffset = cpu_to_le16(param_offset);
|
||||||
|
pSMB->DataOffset = cpu_to_le16(offset);
|
||||||
|
pSMB->Fid = fid;
|
||||||
|
pSMB->InformationLevel = cpu_to_le16(SMB_SET_FILE_DISPOSITION_INFO);
|
||||||
|
pSMB->Reserved4 = 0;
|
||||||
|
pSMB->hdr.smb_buf_length += byte_count;
|
||||||
|
pSMB->ByteCount = cpu_to_le16(byte_count);
|
||||||
|
*data_offset = delete_file ? 1 : 0;
|
||||||
|
rc = SendReceiveNoRsp(xid, tcon->ses, (struct smb_hdr *) pSMB, 0);
|
||||||
|
if (rc)
|
||||||
|
cFYI(1, ("Send error in SetFileDisposition = %d", rc));
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
CIFSSMBSetPathInfo(const int xid, struct cifsTconInfo *tcon,
|
CIFSSMBSetPathInfo(const int xid, struct cifsTconInfo *tcon,
|
||||||
|
|
|
@ -29,45 +29,13 @@
|
||||||
#include "cifsproto.h"
|
#include "cifsproto.h"
|
||||||
#include "cifs_debug.h"
|
#include "cifs_debug.h"
|
||||||
|
|
||||||
static int dns_resolver_instantiate(struct key *key, const void *data,
|
|
||||||
size_t datalen)
|
|
||||||
{
|
|
||||||
int rc = 0;
|
|
||||||
char *ip;
|
|
||||||
|
|
||||||
ip = kmalloc(datalen+1, GFP_KERNEL);
|
|
||||||
if (!ip)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
memcpy(ip, data, datalen);
|
|
||||||
ip[datalen] = '\0';
|
|
||||||
|
|
||||||
rcu_assign_pointer(key->payload.data, ip);
|
|
||||||
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
dns_resolver_destroy(struct key *key)
|
|
||||||
{
|
|
||||||
kfree(key->payload.data);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct key_type key_type_dns_resolver = {
|
|
||||||
.name = "dns_resolver",
|
|
||||||
.def_datalen = sizeof(struct in_addr),
|
|
||||||
.describe = user_describe,
|
|
||||||
.instantiate = dns_resolver_instantiate,
|
|
||||||
.destroy = dns_resolver_destroy,
|
|
||||||
.match = user_match,
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Checks if supplied name is IP address
|
/* Checks if supplied name is IP address
|
||||||
* returns:
|
* returns:
|
||||||
* 1 - name is IP
|
* 1 - name is IP
|
||||||
* 0 - name is not IP
|
* 0 - name is not IP
|
||||||
*/
|
*/
|
||||||
static int is_ip(const char *name)
|
static int
|
||||||
|
is_ip(const char *name)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
struct sockaddr_in sin_server;
|
struct sockaddr_in sin_server;
|
||||||
|
@ -89,6 +57,47 @@ static int is_ip(const char *name)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
dns_resolver_instantiate(struct key *key, const void *data,
|
||||||
|
size_t datalen)
|
||||||
|
{
|
||||||
|
int rc = 0;
|
||||||
|
char *ip;
|
||||||
|
|
||||||
|
ip = kmalloc(datalen + 1, GFP_KERNEL);
|
||||||
|
if (!ip)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
memcpy(ip, data, datalen);
|
||||||
|
ip[datalen] = '\0';
|
||||||
|
|
||||||
|
/* make sure this looks like an address */
|
||||||
|
if (!is_ip((const char *) ip)) {
|
||||||
|
kfree(ip);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
key->type_data.x[0] = datalen;
|
||||||
|
rcu_assign_pointer(key->payload.data, ip);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
dns_resolver_destroy(struct key *key)
|
||||||
|
{
|
||||||
|
kfree(key->payload.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct key_type key_type_dns_resolver = {
|
||||||
|
.name = "dns_resolver",
|
||||||
|
.def_datalen = sizeof(struct in_addr),
|
||||||
|
.describe = user_describe,
|
||||||
|
.instantiate = dns_resolver_instantiate,
|
||||||
|
.destroy = dns_resolver_destroy,
|
||||||
|
.match = user_match,
|
||||||
|
};
|
||||||
|
|
||||||
/* Resolves server name to ip address.
|
/* Resolves server name to ip address.
|
||||||
* input:
|
* input:
|
||||||
* unc - server UNC
|
* unc - server UNC
|
||||||
|
@ -140,6 +149,7 @@ dns_resolve_server_name_to_ip(const char *unc, char **ip_addr)
|
||||||
|
|
||||||
rkey = request_key(&key_type_dns_resolver, name, "");
|
rkey = request_key(&key_type_dns_resolver, name, "");
|
||||||
if (!IS_ERR(rkey)) {
|
if (!IS_ERR(rkey)) {
|
||||||
|
len = rkey->type_data.x[0];
|
||||||
data = rkey->payload.data;
|
data = rkey->payload.data;
|
||||||
} else {
|
} else {
|
||||||
cERROR(1, ("%s: unable to resolve: %s", __func__, name));
|
cERROR(1, ("%s: unable to resolve: %s", __func__, name));
|
||||||
|
@ -148,11 +158,9 @@ dns_resolve_server_name_to_ip(const char *unc, char **ip_addr)
|
||||||
|
|
||||||
skip_upcall:
|
skip_upcall:
|
||||||
if (data) {
|
if (data) {
|
||||||
len = strlen(data);
|
*ip_addr = kmalloc(len + 1, GFP_KERNEL);
|
||||||
*ip_addr = kmalloc(len+1, GFP_KERNEL);
|
|
||||||
if (*ip_addr) {
|
if (*ip_addr) {
|
||||||
memcpy(*ip_addr, data, len);
|
memcpy(*ip_addr, data, len + 1);
|
||||||
(*ip_addr)[len] = '\0';
|
|
||||||
if (!IS_ERR(rkey))
|
if (!IS_ERR(rkey))
|
||||||
cFYI(1, ("%s: resolved: %s to %s", __func__,
|
cFYI(1, ("%s: resolved: %s to %s", __func__,
|
||||||
name,
|
name,
|
||||||
|
|
130
fs/cifs/file.c
130
fs/cifs/file.c
|
@ -107,7 +107,7 @@ static inline int cifs_open_inode_helper(struct inode *inode, struct file *file,
|
||||||
|
|
||||||
/* want handles we can use to read with first
|
/* want handles we can use to read with first
|
||||||
in the list so we do not have to walk the
|
in the list so we do not have to walk the
|
||||||
list to search for one in prepare_write */
|
list to search for one in write_begin */
|
||||||
if ((file->f_flags & O_ACCMODE) == O_WRONLY) {
|
if ((file->f_flags & O_ACCMODE) == O_WRONLY) {
|
||||||
list_add_tail(&pCifsFile->flist,
|
list_add_tail(&pCifsFile->flist,
|
||||||
&pCifsInode->openFileList);
|
&pCifsInode->openFileList);
|
||||||
|
@ -915,7 +915,7 @@ ssize_t cifs_user_write(struct file *file, const char __user *write_data,
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t cifs_write(struct file *file, const char *write_data,
|
static ssize_t cifs_write(struct file *file, const char *write_data,
|
||||||
size_t write_size, loff_t *poffset)
|
size_t write_size, loff_t *poffset)
|
||||||
{
|
{
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
unsigned int bytes_written = 0;
|
unsigned int bytes_written = 0;
|
||||||
|
@ -1065,6 +1065,7 @@ struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode)
|
||||||
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;
|
||||||
|
bool any_available = false;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
/* Having a null inode here (because mapping->host was set to zero by
|
/* Having a null inode here (because mapping->host was set to zero by
|
||||||
|
@ -1080,8 +1081,10 @@ struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode)
|
||||||
read_lock(&GlobalSMBSeslock);
|
read_lock(&GlobalSMBSeslock);
|
||||||
refind_writable:
|
refind_writable:
|
||||||
list_for_each_entry(open_file, &cifs_inode->openFileList, flist) {
|
list_for_each_entry(open_file, &cifs_inode->openFileList, flist) {
|
||||||
if (open_file->closePend)
|
if (open_file->closePend ||
|
||||||
|
(!any_available && open_file->pid != current->tgid))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (open_file->pfile &&
|
if (open_file->pfile &&
|
||||||
((open_file->pfile->f_flags & O_RDWR) ||
|
((open_file->pfile->f_flags & O_RDWR) ||
|
||||||
(open_file->pfile->f_flags & O_WRONLY))) {
|
(open_file->pfile->f_flags & O_WRONLY))) {
|
||||||
|
@ -1131,6 +1134,11 @@ refind_writable:
|
||||||
of the loop here. */
|
of the loop here. */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/* couldn't find useable FH with same pid, try any available */
|
||||||
|
if (!any_available) {
|
||||||
|
any_available = true;
|
||||||
|
goto refind_writable;
|
||||||
|
}
|
||||||
read_unlock(&GlobalSMBSeslock);
|
read_unlock(&GlobalSMBSeslock);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -1447,49 +1455,52 @@ static int cifs_writepage(struct page *page, struct writeback_control *wbc)
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int cifs_commit_write(struct file *file, struct page *page,
|
static int cifs_write_end(struct file *file, struct address_space *mapping,
|
||||||
unsigned offset, unsigned to)
|
loff_t pos, unsigned len, unsigned copied,
|
||||||
|
struct page *page, void *fsdata)
|
||||||
{
|
{
|
||||||
int xid;
|
int rc;
|
||||||
int rc = 0;
|
struct inode *inode = mapping->host;
|
||||||
struct inode *inode = page->mapping->host;
|
|
||||||
loff_t position = ((loff_t)page->index << PAGE_CACHE_SHIFT) + to;
|
|
||||||
char *page_data;
|
|
||||||
|
|
||||||
xid = GetXid();
|
cFYI(1, ("write_end for page %p from pos %lld with %d bytes",
|
||||||
cFYI(1, ("commit write for page %p up to position %lld for %d",
|
page, pos, copied));
|
||||||
page, position, to));
|
|
||||||
spin_lock(&inode->i_lock);
|
if (!PageUptodate(page) && copied == PAGE_CACHE_SIZE)
|
||||||
if (position > inode->i_size)
|
SetPageUptodate(page);
|
||||||
i_size_write(inode, position);
|
|
||||||
|
|
||||||
spin_unlock(&inode->i_lock);
|
|
||||||
if (!PageUptodate(page)) {
|
if (!PageUptodate(page)) {
|
||||||
position = ((loff_t)page->index << PAGE_CACHE_SHIFT) + offset;
|
char *page_data;
|
||||||
/* can not rely on (or let) writepage write this data */
|
unsigned offset = pos & (PAGE_CACHE_SIZE - 1);
|
||||||
if (to < offset) {
|
int xid;
|
||||||
cFYI(1, ("Illegal offsets, can not copy from %d to %d",
|
|
||||||
offset, to));
|
xid = GetXid();
|
||||||
FreeXid(xid);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
/* this is probably better than directly calling
|
/* this is probably better than directly calling
|
||||||
partialpage_write since in this function the file handle is
|
partialpage_write since in this function the file handle is
|
||||||
known which we might as well leverage */
|
known which we might as well leverage */
|
||||||
/* BB check if anything else missing out of ppw
|
/* BB check if anything else missing out of ppw
|
||||||
such as updating last write time */
|
such as updating last write time */
|
||||||
page_data = kmap(page);
|
page_data = kmap(page);
|
||||||
rc = cifs_write(file, page_data + offset, to-offset,
|
rc = cifs_write(file, page_data + offset, copied, &pos);
|
||||||
&position);
|
/* if (rc < 0) should we set writebehind rc? */
|
||||||
if (rc > 0)
|
|
||||||
rc = 0;
|
|
||||||
/* else if (rc < 0) should we set writebehind rc? */
|
|
||||||
kunmap(page);
|
kunmap(page);
|
||||||
|
|
||||||
|
FreeXid(xid);
|
||||||
} else {
|
} else {
|
||||||
|
rc = copied;
|
||||||
|
pos += copied;
|
||||||
set_page_dirty(page);
|
set_page_dirty(page);
|
||||||
}
|
}
|
||||||
|
|
||||||
FreeXid(xid);
|
if (rc > 0) {
|
||||||
|
spin_lock(&inode->i_lock);
|
||||||
|
if (pos > inode->i_size)
|
||||||
|
i_size_write(inode, pos);
|
||||||
|
spin_unlock(&inode->i_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
unlock_page(page);
|
||||||
|
page_cache_release(page);
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2035,49 +2046,44 @@ bool is_size_safe_to_change(struct cifsInodeInfo *cifsInode, __u64 end_of_file)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int cifs_prepare_write(struct file *file, struct page *page,
|
static int cifs_write_begin(struct file *file, struct address_space *mapping,
|
||||||
unsigned from, unsigned to)
|
loff_t pos, unsigned len, unsigned flags,
|
||||||
|
struct page **pagep, void **fsdata)
|
||||||
{
|
{
|
||||||
int rc = 0;
|
pgoff_t index = pos >> PAGE_CACHE_SHIFT;
|
||||||
loff_t i_size;
|
loff_t offset = pos & (PAGE_CACHE_SIZE - 1);
|
||||||
loff_t offset;
|
|
||||||
|
|
||||||
cFYI(1, ("prepare write for page %p from %d to %d", page, from, to));
|
cFYI(1, ("write_begin from %lld len %d", (long long)pos, len));
|
||||||
if (PageUptodate(page))
|
|
||||||
|
*pagep = __grab_cache_page(mapping, index);
|
||||||
|
if (!*pagep)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
if (PageUptodate(*pagep))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* If we are writing a full page it will be up to date,
|
/* If we are writing a full page it will be up to date,
|
||||||
no need to read from the server */
|
no need to read from the server */
|
||||||
if ((to == PAGE_CACHE_SIZE) && (from == 0)) {
|
if (len == PAGE_CACHE_SIZE && flags & AOP_FLAG_UNINTERRUPTIBLE)
|
||||||
SetPageUptodate(page);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
|
||||||
|
|
||||||
offset = (loff_t)page->index << PAGE_CACHE_SHIFT;
|
if ((file->f_flags & O_ACCMODE) != O_WRONLY) {
|
||||||
i_size = i_size_read(page->mapping->host);
|
int rc;
|
||||||
|
|
||||||
if ((offset >= i_size) ||
|
|
||||||
((from == 0) && (offset + to) >= i_size)) {
|
|
||||||
/*
|
|
||||||
* We don't need to read data beyond the end of the file.
|
|
||||||
* zero it, and set the page uptodate
|
|
||||||
*/
|
|
||||||
simple_prepare_write(file, page, from, to);
|
|
||||||
SetPageUptodate(page);
|
|
||||||
} else if ((file->f_flags & O_ACCMODE) != O_WRONLY) {
|
|
||||||
/* might as well read a page, it is fast enough */
|
/* might as well read a page, it is fast enough */
|
||||||
rc = cifs_readpage_worker(file, page, &offset);
|
rc = cifs_readpage_worker(file, *pagep, &offset);
|
||||||
|
|
||||||
|
/* we do not need to pass errors back
|
||||||
|
e.g. if we do not have read access to the file
|
||||||
|
because cifs_write_end will attempt synchronous writes
|
||||||
|
-- shaggy */
|
||||||
} else {
|
} else {
|
||||||
/* we could try using another file handle if there is one -
|
/* we could try using another file handle if there is one -
|
||||||
but how would we lock it to prevent close of that handle
|
but how would we lock it to prevent close of that handle
|
||||||
racing with this read? In any case
|
racing with this read? In any case
|
||||||
this will be written out by commit_write so is fine */
|
this will be written out by write_end so is fine */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* we do not need to pass errors back
|
|
||||||
e.g. if we do not have read access to the file
|
|
||||||
because cifs_commit_write will do the right thing. -- shaggy */
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2086,8 +2092,8 @@ const struct address_space_operations cifs_addr_ops = {
|
||||||
.readpages = cifs_readpages,
|
.readpages = cifs_readpages,
|
||||||
.writepage = cifs_writepage,
|
.writepage = cifs_writepage,
|
||||||
.writepages = cifs_writepages,
|
.writepages = cifs_writepages,
|
||||||
.prepare_write = cifs_prepare_write,
|
.write_begin = cifs_write_begin,
|
||||||
.commit_write = cifs_commit_write,
|
.write_end = cifs_write_end,
|
||||||
.set_page_dirty = __set_page_dirty_nobuffers,
|
.set_page_dirty = __set_page_dirty_nobuffers,
|
||||||
/* .sync_page = cifs_sync_page, */
|
/* .sync_page = cifs_sync_page, */
|
||||||
/* .direct_IO = */
|
/* .direct_IO = */
|
||||||
|
@ -2102,8 +2108,8 @@ const struct address_space_operations cifs_addr_ops_smallbuf = {
|
||||||
.readpage = cifs_readpage,
|
.readpage = cifs_readpage,
|
||||||
.writepage = cifs_writepage,
|
.writepage = cifs_writepage,
|
||||||
.writepages = cifs_writepages,
|
.writepages = cifs_writepages,
|
||||||
.prepare_write = cifs_prepare_write,
|
.write_begin = cifs_write_begin,
|
||||||
.commit_write = cifs_commit_write,
|
.write_end = cifs_write_end,
|
||||||
.set_page_dirty = __set_page_dirty_nobuffers,
|
.set_page_dirty = __set_page_dirty_nobuffers,
|
||||||
/* .sync_page = cifs_sync_page, */
|
/* .sync_page = cifs_sync_page, */
|
||||||
/* .direct_IO = */
|
/* .direct_IO = */
|
||||||
|
|
642
fs/cifs/inode.c
642
fs/cifs/inode.c
|
@ -665,40 +665,201 @@ struct inode *cifs_iget(struct super_block *sb, unsigned long ino)
|
||||||
return inode;
|
return inode;
|
||||||
}
|
}
|
||||||
|
|
||||||
int cifs_unlink(struct inode *inode, struct dentry *direntry)
|
static int
|
||||||
|
cifs_set_file_info(struct inode *inode, struct iattr *attrs, int xid,
|
||||||
|
char *full_path, __u32 dosattr)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
int oplock = 0;
|
||||||
|
__u16 netfid;
|
||||||
|
__u32 netpid;
|
||||||
|
bool set_time = false;
|
||||||
|
struct cifsFileInfo *open_file;
|
||||||
|
struct cifsInodeInfo *cifsInode = CIFS_I(inode);
|
||||||
|
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
|
||||||
|
struct cifsTconInfo *pTcon = cifs_sb->tcon;
|
||||||
|
FILE_BASIC_INFO info_buf;
|
||||||
|
|
||||||
|
if (attrs->ia_valid & ATTR_ATIME) {
|
||||||
|
set_time = true;
|
||||||
|
info_buf.LastAccessTime =
|
||||||
|
cpu_to_le64(cifs_UnixTimeToNT(attrs->ia_atime));
|
||||||
|
} else
|
||||||
|
info_buf.LastAccessTime = 0;
|
||||||
|
|
||||||
|
if (attrs->ia_valid & ATTR_MTIME) {
|
||||||
|
set_time = true;
|
||||||
|
info_buf.LastWriteTime =
|
||||||
|
cpu_to_le64(cifs_UnixTimeToNT(attrs->ia_mtime));
|
||||||
|
} else
|
||||||
|
info_buf.LastWriteTime = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Samba throws this field away, but windows may actually use it.
|
||||||
|
* Do not set ctime unless other time stamps are changed explicitly
|
||||||
|
* (i.e. by utimes()) since we would then have a mix of client and
|
||||||
|
* server times.
|
||||||
|
*/
|
||||||
|
if (set_time && (attrs->ia_valid & ATTR_CTIME)) {
|
||||||
|
cFYI(1, ("CIFS - CTIME changed"));
|
||||||
|
info_buf.ChangeTime =
|
||||||
|
cpu_to_le64(cifs_UnixTimeToNT(attrs->ia_ctime));
|
||||||
|
} else
|
||||||
|
info_buf.ChangeTime = 0;
|
||||||
|
|
||||||
|
info_buf.CreationTime = 0; /* don't change */
|
||||||
|
info_buf.Attributes = cpu_to_le32(dosattr);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the file is already open for write, just use that fileid
|
||||||
|
*/
|
||||||
|
open_file = find_writable_file(cifsInode);
|
||||||
|
if (open_file) {
|
||||||
|
netfid = open_file->netfid;
|
||||||
|
netpid = open_file->pid;
|
||||||
|
goto set_via_filehandle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* NT4 apparently returns success on this call, but it doesn't
|
||||||
|
* really work.
|
||||||
|
*/
|
||||||
|
if (!(pTcon->ses->flags & CIFS_SES_NT4)) {
|
||||||
|
rc = CIFSSMBSetPathInfo(xid, pTcon, full_path,
|
||||||
|
&info_buf, cifs_sb->local_nls,
|
||||||
|
cifs_sb->mnt_cifs_flags &
|
||||||
|
CIFS_MOUNT_MAP_SPECIAL_CHR);
|
||||||
|
if (rc == 0) {
|
||||||
|
cifsInode->cifsAttrs = dosattr;
|
||||||
|
goto out;
|
||||||
|
} else if (rc != -EOPNOTSUPP && rc != -EINVAL)
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
cFYI(1, ("calling SetFileInfo since SetPathInfo for "
|
||||||
|
"times not supported by this server"));
|
||||||
|
rc = CIFSSMBOpen(xid, pTcon, full_path, FILE_OPEN,
|
||||||
|
SYNCHRONIZE | FILE_WRITE_ATTRIBUTES,
|
||||||
|
CREATE_NOT_DIR, &netfid, &oplock,
|
||||||
|
NULL, cifs_sb->local_nls,
|
||||||
|
cifs_sb->mnt_cifs_flags &
|
||||||
|
CIFS_MOUNT_MAP_SPECIAL_CHR);
|
||||||
|
|
||||||
|
if (rc != 0) {
|
||||||
|
if (rc == -EIO)
|
||||||
|
rc = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
netpid = current->tgid;
|
||||||
|
|
||||||
|
set_via_filehandle:
|
||||||
|
rc = CIFSSMBSetFileInfo(xid, pTcon, &info_buf, netfid, netpid);
|
||||||
|
if (!rc)
|
||||||
|
cifsInode->cifsAttrs = dosattr;
|
||||||
|
|
||||||
|
if (open_file == NULL)
|
||||||
|
CIFSSMBClose(xid, pTcon, netfid);
|
||||||
|
else
|
||||||
|
atomic_dec(&open_file->wrtPending);
|
||||||
|
out:
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* open the given file (if it isn't already), set the DELETE_ON_CLOSE bit
|
||||||
|
* and rename it to a random name that hopefully won't conflict with
|
||||||
|
* anything else.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
cifs_rename_pending_delete(char *full_path, struct inode *inode, int xid)
|
||||||
|
{
|
||||||
|
int oplock = 0;
|
||||||
|
int rc;
|
||||||
|
__u16 netfid;
|
||||||
|
struct cifsInodeInfo *cifsInode = CIFS_I(inode);
|
||||||
|
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
|
||||||
|
struct cifsTconInfo *tcon = cifs_sb->tcon;
|
||||||
|
__u32 dosattr;
|
||||||
|
FILE_BASIC_INFO *info_buf;
|
||||||
|
|
||||||
|
rc = CIFSSMBOpen(xid, tcon, full_path, FILE_OPEN,
|
||||||
|
DELETE|FILE_WRITE_ATTRIBUTES,
|
||||||
|
CREATE_NOT_DIR|CREATE_DELETE_ON_CLOSE,
|
||||||
|
&netfid, &oplock, NULL, cifs_sb->local_nls,
|
||||||
|
cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
|
||||||
|
if (rc != 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
/* set ATTR_HIDDEN and clear ATTR_READONLY */
|
||||||
|
cifsInode = CIFS_I(inode);
|
||||||
|
dosattr = cifsInode->cifsAttrs & ~ATTR_READONLY;
|
||||||
|
if (dosattr == 0)
|
||||||
|
dosattr |= ATTR_NORMAL;
|
||||||
|
dosattr |= ATTR_HIDDEN;
|
||||||
|
|
||||||
|
info_buf = kzalloc(sizeof(*info_buf), GFP_KERNEL);
|
||||||
|
if (info_buf == NULL) {
|
||||||
|
rc = -ENOMEM;
|
||||||
|
goto out_close;
|
||||||
|
}
|
||||||
|
info_buf->Attributes = cpu_to_le32(dosattr);
|
||||||
|
rc = CIFSSMBSetFileInfo(xid, tcon, info_buf, netfid, current->tgid);
|
||||||
|
kfree(info_buf);
|
||||||
|
if (rc != 0)
|
||||||
|
goto out_close;
|
||||||
|
cifsInode->cifsAttrs = dosattr;
|
||||||
|
|
||||||
|
/* silly-rename the file */
|
||||||
|
CIFSSMBRenameOpenFile(xid, tcon, netfid, NULL, cifs_sb->local_nls,
|
||||||
|
cifs_sb->mnt_cifs_flags &
|
||||||
|
CIFS_MOUNT_MAP_SPECIAL_CHR);
|
||||||
|
|
||||||
|
/* set DELETE_ON_CLOSE */
|
||||||
|
rc = CIFSSMBSetFileDisposition(xid, tcon, true, netfid, current->tgid);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* some samba versions return -ENOENT when we try to set the file
|
||||||
|
* disposition here. Likely a samba bug, but work around it for now
|
||||||
|
*/
|
||||||
|
if (rc == -ENOENT)
|
||||||
|
rc = 0;
|
||||||
|
|
||||||
|
out_close:
|
||||||
|
CIFSSMBClose(xid, tcon, netfid);
|
||||||
|
out:
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cifs_unlink(struct inode *dir, struct dentry *dentry)
|
||||||
{
|
{
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
int xid;
|
int xid;
|
||||||
struct cifs_sb_info *cifs_sb;
|
|
||||||
struct cifsTconInfo *pTcon;
|
|
||||||
char *full_path = NULL;
|
char *full_path = NULL;
|
||||||
struct cifsInodeInfo *cifsInode;
|
struct inode *inode = dentry->d_inode;
|
||||||
FILE_BASIC_INFO *pinfo_buf;
|
struct cifsInodeInfo *cifsInode = CIFS_I(inode);
|
||||||
|
struct super_block *sb = dir->i_sb;
|
||||||
|
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
|
||||||
|
struct cifsTconInfo *tcon = cifs_sb->tcon;
|
||||||
|
struct iattr *attrs = NULL;
|
||||||
|
__u32 dosattr = 0, origattr = 0;
|
||||||
|
|
||||||
cFYI(1, ("cifs_unlink, inode = 0x%p", inode));
|
cFYI(1, ("cifs_unlink, dir=0x%p, dentry=0x%p", dir, dentry));
|
||||||
|
|
||||||
xid = GetXid();
|
xid = GetXid();
|
||||||
|
|
||||||
if (inode)
|
/* Unlink can be called from rename so we can not take the
|
||||||
cifs_sb = CIFS_SB(inode->i_sb);
|
* sb->s_vfs_rename_mutex here */
|
||||||
else
|
full_path = build_path_from_dentry(dentry);
|
||||||
cifs_sb = CIFS_SB(direntry->d_sb);
|
|
||||||
pTcon = cifs_sb->tcon;
|
|
||||||
|
|
||||||
/* Unlink can be called from rename so we can not grab the sem here
|
|
||||||
since we deadlock otherwise */
|
|
||||||
/* mutex_lock(&direntry->d_sb->s_vfs_rename_mutex);*/
|
|
||||||
full_path = build_path_from_dentry(direntry);
|
|
||||||
/* mutex_unlock(&direntry->d_sb->s_vfs_rename_mutex);*/
|
|
||||||
if (full_path == NULL) {
|
if (full_path == NULL) {
|
||||||
FreeXid(xid);
|
FreeXid(xid);
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((pTcon->ses->capabilities & CAP_UNIX) &&
|
if ((tcon->ses->capabilities & CAP_UNIX) &&
|
||||||
(CIFS_UNIX_POSIX_PATH_OPS_CAP &
|
(CIFS_UNIX_POSIX_PATH_OPS_CAP &
|
||||||
le64_to_cpu(pTcon->fsUnixInfo.Capability))) {
|
le64_to_cpu(tcon->fsUnixInfo.Capability))) {
|
||||||
rc = CIFSPOSIXDelFile(xid, pTcon, full_path,
|
rc = CIFSPOSIXDelFile(xid, tcon, full_path,
|
||||||
SMB_POSIX_UNLINK_FILE_TARGET, cifs_sb->local_nls,
|
SMB_POSIX_UNLINK_FILE_TARGET, cifs_sb->local_nls,
|
||||||
cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
|
cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
|
||||||
cFYI(1, ("posix del rc %d", rc));
|
cFYI(1, ("posix del rc %d", rc));
|
||||||
|
@ -706,125 +867,60 @@ int cifs_unlink(struct inode *inode, struct dentry *direntry)
|
||||||
goto psx_del_no_retry;
|
goto psx_del_no_retry;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = CIFSSMBDelFile(xid, pTcon, full_path, cifs_sb->local_nls,
|
retry_std_delete:
|
||||||
|
rc = CIFSSMBDelFile(xid, tcon, full_path, cifs_sb->local_nls,
|
||||||
cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
|
cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
|
||||||
|
|
||||||
psx_del_no_retry:
|
psx_del_no_retry:
|
||||||
if (!rc) {
|
if (!rc) {
|
||||||
if (direntry->d_inode)
|
if (inode)
|
||||||
drop_nlink(direntry->d_inode);
|
drop_nlink(inode);
|
||||||
} else if (rc == -ENOENT) {
|
} else if (rc == -ENOENT) {
|
||||||
d_drop(direntry);
|
d_drop(dentry);
|
||||||
} else if (rc == -ETXTBSY) {
|
} else if (rc == -ETXTBSY) {
|
||||||
int oplock = 0;
|
rc = cifs_rename_pending_delete(full_path, inode, xid);
|
||||||
__u16 netfid;
|
if (rc == 0)
|
||||||
|
drop_nlink(inode);
|
||||||
rc = CIFSSMBOpen(xid, pTcon, full_path, FILE_OPEN, DELETE,
|
} else if (rc == -EACCES && dosattr == 0) {
|
||||||
CREATE_NOT_DIR | CREATE_DELETE_ON_CLOSE,
|
attrs = kzalloc(sizeof(*attrs), GFP_KERNEL);
|
||||||
&netfid, &oplock, NULL, cifs_sb->local_nls,
|
if (attrs == NULL) {
|
||||||
cifs_sb->mnt_cifs_flags &
|
rc = -ENOMEM;
|
||||||
CIFS_MOUNT_MAP_SPECIAL_CHR);
|
goto out_reval;
|
||||||
if (rc == 0) {
|
|
||||||
CIFSSMBRenameOpenFile(xid, pTcon, netfid, NULL,
|
|
||||||
cifs_sb->local_nls,
|
|
||||||
cifs_sb->mnt_cifs_flags &
|
|
||||||
CIFS_MOUNT_MAP_SPECIAL_CHR);
|
|
||||||
CIFSSMBClose(xid, pTcon, netfid);
|
|
||||||
if (direntry->d_inode)
|
|
||||||
drop_nlink(direntry->d_inode);
|
|
||||||
}
|
}
|
||||||
} else if (rc == -EACCES) {
|
|
||||||
/* try only if r/o attribute set in local lookup data? */
|
|
||||||
pinfo_buf = kzalloc(sizeof(FILE_BASIC_INFO), GFP_KERNEL);
|
|
||||||
if (pinfo_buf) {
|
|
||||||
/* ATTRS set to normal clears r/o bit */
|
|
||||||
pinfo_buf->Attributes = cpu_to_le32(ATTR_NORMAL);
|
|
||||||
if (!(pTcon->ses->flags & CIFS_SES_NT4))
|
|
||||||
rc = CIFSSMBSetPathInfo(xid, pTcon, full_path,
|
|
||||||
pinfo_buf,
|
|
||||||
cifs_sb->local_nls,
|
|
||||||
cifs_sb->mnt_cifs_flags &
|
|
||||||
CIFS_MOUNT_MAP_SPECIAL_CHR);
|
|
||||||
else
|
|
||||||
rc = -EOPNOTSUPP;
|
|
||||||
|
|
||||||
if (rc == -EOPNOTSUPP) {
|
/* try to reset dos attributes */
|
||||||
int oplock = 0;
|
origattr = cifsInode->cifsAttrs;
|
||||||
__u16 netfid;
|
if (origattr == 0)
|
||||||
/* rc = CIFSSMBSetAttrLegacy(xid, pTcon,
|
origattr |= ATTR_NORMAL;
|
||||||
full_path,
|
dosattr = origattr & ~ATTR_READONLY;
|
||||||
(__u16)ATTR_NORMAL,
|
if (dosattr == 0)
|
||||||
cifs_sb->local_nls);
|
dosattr |= ATTR_NORMAL;
|
||||||
For some strange reason it seems that NT4 eats the
|
dosattr |= ATTR_HIDDEN;
|
||||||
old setattr call without actually setting the
|
|
||||||
attributes so on to the third attempted workaround
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* BB could scan to see if we already have it open
|
rc = cifs_set_file_info(inode, attrs, xid, full_path, dosattr);
|
||||||
and pass in pid of opener to function */
|
if (rc != 0)
|
||||||
rc = CIFSSMBOpen(xid, pTcon, full_path,
|
goto out_reval;
|
||||||
FILE_OPEN, SYNCHRONIZE |
|
|
||||||
FILE_WRITE_ATTRIBUTES, 0,
|
|
||||||
&netfid, &oplock, NULL,
|
|
||||||
cifs_sb->local_nls,
|
|
||||||
cifs_sb->mnt_cifs_flags &
|
|
||||||
CIFS_MOUNT_MAP_SPECIAL_CHR);
|
|
||||||
if (rc == 0) {
|
|
||||||
rc = CIFSSMBSetFileInfo(xid, pTcon,
|
|
||||||
pinfo_buf,
|
|
||||||
netfid,
|
|
||||||
current->tgid);
|
|
||||||
CIFSSMBClose(xid, pTcon, netfid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
kfree(pinfo_buf);
|
|
||||||
}
|
|
||||||
if (rc == 0) {
|
|
||||||
rc = CIFSSMBDelFile(xid, pTcon, full_path,
|
|
||||||
cifs_sb->local_nls,
|
|
||||||
cifs_sb->mnt_cifs_flags &
|
|
||||||
CIFS_MOUNT_MAP_SPECIAL_CHR);
|
|
||||||
if (!rc) {
|
|
||||||
if (direntry->d_inode)
|
|
||||||
drop_nlink(direntry->d_inode);
|
|
||||||
} else if (rc == -ETXTBSY) {
|
|
||||||
int oplock = 0;
|
|
||||||
__u16 netfid;
|
|
||||||
|
|
||||||
rc = CIFSSMBOpen(xid, pTcon, full_path,
|
goto retry_std_delete;
|
||||||
FILE_OPEN, DELETE,
|
|
||||||
CREATE_NOT_DIR |
|
|
||||||
CREATE_DELETE_ON_CLOSE,
|
|
||||||
&netfid, &oplock, NULL,
|
|
||||||
cifs_sb->local_nls,
|
|
||||||
cifs_sb->mnt_cifs_flags &
|
|
||||||
CIFS_MOUNT_MAP_SPECIAL_CHR);
|
|
||||||
if (rc == 0) {
|
|
||||||
CIFSSMBRenameOpenFile(xid, pTcon,
|
|
||||||
netfid, NULL,
|
|
||||||
cifs_sb->local_nls,
|
|
||||||
cifs_sb->mnt_cifs_flags &
|
|
||||||
CIFS_MOUNT_MAP_SPECIAL_CHR);
|
|
||||||
CIFSSMBClose(xid, pTcon, netfid);
|
|
||||||
if (direntry->d_inode)
|
|
||||||
drop_nlink(direntry->d_inode);
|
|
||||||
}
|
|
||||||
/* BB if rc = -ETXTBUSY goto the rename logic BB */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (direntry->d_inode) {
|
|
||||||
cifsInode = CIFS_I(direntry->d_inode);
|
/* undo the setattr if we errored out and it's needed */
|
||||||
|
if (rc != 0 && dosattr != 0)
|
||||||
|
cifs_set_file_info(inode, attrs, xid, full_path, origattr);
|
||||||
|
|
||||||
|
out_reval:
|
||||||
|
if (inode) {
|
||||||
|
cifsInode = CIFS_I(inode);
|
||||||
cifsInode->time = 0; /* will force revalidate to get info
|
cifsInode->time = 0; /* will force revalidate to get info
|
||||||
when needed */
|
when needed */
|
||||||
direntry->d_inode->i_ctime = current_fs_time(inode->i_sb);
|
inode->i_ctime = current_fs_time(sb);
|
||||||
}
|
|
||||||
if (inode) {
|
|
||||||
inode->i_ctime = inode->i_mtime = current_fs_time(inode->i_sb);
|
|
||||||
cifsInode = CIFS_I(inode);
|
|
||||||
cifsInode->time = 0; /* force revalidate of dir as well */
|
|
||||||
}
|
}
|
||||||
|
dir->i_ctime = dir->i_mtime = current_fs_time(sb);
|
||||||
|
cifsInode = CIFS_I(dir);
|
||||||
|
CIFS_I(dir)->time = 0; /* force revalidate of dir as well */
|
||||||
|
|
||||||
kfree(full_path);
|
kfree(full_path);
|
||||||
|
kfree(attrs);
|
||||||
FreeXid(xid);
|
FreeXid(xid);
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
@ -869,7 +965,7 @@ static void posix_fill_in_inode(struct inode *tmp_inode,
|
||||||
|
|
||||||
int cifs_mkdir(struct inode *inode, struct dentry *direntry, int mode)
|
int cifs_mkdir(struct inode *inode, struct dentry *direntry, int mode)
|
||||||
{
|
{
|
||||||
int rc = 0;
|
int rc = 0, tmprc;
|
||||||
int xid;
|
int xid;
|
||||||
struct cifs_sb_info *cifs_sb;
|
struct cifs_sb_info *cifs_sb;
|
||||||
struct cifsTconInfo *pTcon;
|
struct cifsTconInfo *pTcon;
|
||||||
|
@ -931,6 +1027,7 @@ int cifs_mkdir(struct inode *inode, struct dentry *direntry, int mode)
|
||||||
kfree(pInfo);
|
kfree(pInfo);
|
||||||
goto mkdir_get_info;
|
goto mkdir_get_info;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Is an i_ino of zero legal? */
|
/* Is an i_ino of zero legal? */
|
||||||
/* Are there sanity checks we can use to ensure that
|
/* Are there sanity checks we can use to ensure that
|
||||||
the server is really filling in that field? */
|
the server is really filling in that field? */
|
||||||
|
@ -1019,12 +1116,20 @@ mkdir_get_info:
|
||||||
if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) &&
|
if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) &&
|
||||||
(mode & S_IWUGO) == 0) {
|
(mode & S_IWUGO) == 0) {
|
||||||
FILE_BASIC_INFO pInfo;
|
FILE_BASIC_INFO pInfo;
|
||||||
|
struct cifsInodeInfo *cifsInode;
|
||||||
|
u32 dosattrs;
|
||||||
|
|
||||||
memset(&pInfo, 0, sizeof(pInfo));
|
memset(&pInfo, 0, sizeof(pInfo));
|
||||||
pInfo.Attributes = cpu_to_le32(ATTR_READONLY);
|
cifsInode = CIFS_I(newinode);
|
||||||
CIFSSMBSetPathInfo(xid, pTcon, full_path,
|
dosattrs = cifsInode->cifsAttrs|ATTR_READONLY;
|
||||||
&pInfo, cifs_sb->local_nls,
|
pInfo.Attributes = cpu_to_le32(dosattrs);
|
||||||
|
tmprc = CIFSSMBSetPathInfo(xid, pTcon,
|
||||||
|
full_path, &pInfo,
|
||||||
|
cifs_sb->local_nls,
|
||||||
cifs_sb->mnt_cifs_flags &
|
cifs_sb->mnt_cifs_flags &
|
||||||
CIFS_MOUNT_MAP_SPECIAL_CHR);
|
CIFS_MOUNT_MAP_SPECIAL_CHR);
|
||||||
|
if (tmprc == 0)
|
||||||
|
cifsInode->cifsAttrs = dosattrs;
|
||||||
}
|
}
|
||||||
if (direntry->d_inode) {
|
if (direntry->d_inode) {
|
||||||
if (cifs_sb->mnt_cifs_flags &
|
if (cifs_sb->mnt_cifs_flags &
|
||||||
|
@ -1096,117 +1201,141 @@ int cifs_rmdir(struct inode *inode, struct dentry *direntry)
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
cifs_do_rename(int xid, struct dentry *from_dentry, const char *fromPath,
|
||||||
|
struct dentry *to_dentry, const char *toPath)
|
||||||
|
{
|
||||||
|
struct cifs_sb_info *cifs_sb = CIFS_SB(from_dentry->d_sb);
|
||||||
|
struct cifsTconInfo *pTcon = cifs_sb->tcon;
|
||||||
|
__u16 srcfid;
|
||||||
|
int oplock, rc;
|
||||||
|
|
||||||
|
/* try path-based rename first */
|
||||||
|
rc = CIFSSMBRename(xid, pTcon, fromPath, toPath, cifs_sb->local_nls,
|
||||||
|
cifs_sb->mnt_cifs_flags &
|
||||||
|
CIFS_MOUNT_MAP_SPECIAL_CHR);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* don't bother with rename by filehandle unless file is busy and
|
||||||
|
* source Note that cross directory moves do not work with
|
||||||
|
* rename by filehandle to various Windows servers.
|
||||||
|
*/
|
||||||
|
if (rc == 0 || rc != -ETXTBSY)
|
||||||
|
return rc;
|
||||||
|
|
||||||
|
/* open the file to be renamed -- we need DELETE perms */
|
||||||
|
rc = CIFSSMBOpen(xid, pTcon, fromPath, FILE_OPEN, DELETE,
|
||||||
|
CREATE_NOT_DIR, &srcfid, &oplock, NULL,
|
||||||
|
cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
|
||||||
|
CIFS_MOUNT_MAP_SPECIAL_CHR);
|
||||||
|
|
||||||
|
if (rc == 0) {
|
||||||
|
rc = CIFSSMBRenameOpenFile(xid, pTcon, srcfid,
|
||||||
|
(const char *) to_dentry->d_name.name,
|
||||||
|
cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
|
||||||
|
CIFS_MOUNT_MAP_SPECIAL_CHR);
|
||||||
|
|
||||||
|
CIFSSMBClose(xid, pTcon, srcfid);
|
||||||
|
}
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
int cifs_rename(struct inode *source_inode, struct dentry *source_direntry,
|
int cifs_rename(struct inode *source_inode, struct dentry *source_direntry,
|
||||||
struct inode *target_inode, struct dentry *target_direntry)
|
struct inode *target_inode, struct dentry *target_direntry)
|
||||||
{
|
{
|
||||||
char *fromName;
|
char *fromName = NULL;
|
||||||
char *toName;
|
char *toName = NULL;
|
||||||
struct cifs_sb_info *cifs_sb_source;
|
struct cifs_sb_info *cifs_sb_source;
|
||||||
struct cifs_sb_info *cifs_sb_target;
|
struct cifs_sb_info *cifs_sb_target;
|
||||||
struct cifsTconInfo *pTcon;
|
struct cifsTconInfo *pTcon;
|
||||||
|
FILE_UNIX_BASIC_INFO *info_buf_source = NULL;
|
||||||
|
FILE_UNIX_BASIC_INFO *info_buf_target;
|
||||||
int xid;
|
int xid;
|
||||||
int rc = 0;
|
int rc;
|
||||||
|
|
||||||
xid = GetXid();
|
|
||||||
|
|
||||||
cifs_sb_target = CIFS_SB(target_inode->i_sb);
|
cifs_sb_target = CIFS_SB(target_inode->i_sb);
|
||||||
cifs_sb_source = CIFS_SB(source_inode->i_sb);
|
cifs_sb_source = CIFS_SB(source_inode->i_sb);
|
||||||
pTcon = cifs_sb_source->tcon;
|
pTcon = cifs_sb_source->tcon;
|
||||||
|
|
||||||
|
xid = GetXid();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* BB: this might be allowed if same server, but different share.
|
||||||
|
* Consider adding support for this
|
||||||
|
*/
|
||||||
if (pTcon != cifs_sb_target->tcon) {
|
if (pTcon != cifs_sb_target->tcon) {
|
||||||
FreeXid(xid);
|
rc = -EXDEV;
|
||||||
return -EXDEV; /* BB actually could be allowed if same server,
|
goto cifs_rename_exit;
|
||||||
but different share.
|
|
||||||
Might eventually add support for this */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* we already have the rename sem so we do not need to grab it again
|
/*
|
||||||
here to protect the path integrity */
|
* we already have the rename sem so we do not need to
|
||||||
|
* grab it again here to protect the path integrity
|
||||||
|
*/
|
||||||
fromName = build_path_from_dentry(source_direntry);
|
fromName = build_path_from_dentry(source_direntry);
|
||||||
toName = build_path_from_dentry(target_direntry);
|
if (fromName == NULL) {
|
||||||
if ((fromName == NULL) || (toName == NULL)) {
|
|
||||||
rc = -ENOMEM;
|
rc = -ENOMEM;
|
||||||
goto cifs_rename_exit;
|
goto cifs_rename_exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = CIFSSMBRename(xid, pTcon, fromName, toName,
|
toName = build_path_from_dentry(target_direntry);
|
||||||
cifs_sb_source->local_nls,
|
if (toName == NULL) {
|
||||||
cifs_sb_source->mnt_cifs_flags &
|
rc = -ENOMEM;
|
||||||
CIFS_MOUNT_MAP_SPECIAL_CHR);
|
goto cifs_rename_exit;
|
||||||
if (rc == -EEXIST) {
|
}
|
||||||
/* check if they are the same file because rename of hardlinked
|
|
||||||
files is a noop */
|
rc = cifs_do_rename(xid, source_direntry, fromName,
|
||||||
FILE_UNIX_BASIC_INFO *info_buf_source;
|
target_direntry, toName);
|
||||||
FILE_UNIX_BASIC_INFO *info_buf_target;
|
|
||||||
|
if (rc == -EEXIST) {
|
||||||
|
if (pTcon->unix_ext) {
|
||||||
|
/*
|
||||||
|
* Are src and dst hardlinks of same inode? We can
|
||||||
|
* only tell with unix extensions enabled
|
||||||
|
*/
|
||||||
|
info_buf_source =
|
||||||
|
kmalloc(2 * sizeof(FILE_UNIX_BASIC_INFO),
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (info_buf_source == NULL)
|
||||||
|
goto unlink_target;
|
||||||
|
|
||||||
info_buf_source =
|
|
||||||
kmalloc(2 * sizeof(FILE_UNIX_BASIC_INFO), GFP_KERNEL);
|
|
||||||
if (info_buf_source != NULL) {
|
|
||||||
info_buf_target = info_buf_source + 1;
|
info_buf_target = info_buf_source + 1;
|
||||||
if (pTcon->unix_ext)
|
rc = CIFSSMBUnixQPathInfo(xid, pTcon, fromName,
|
||||||
rc = CIFSSMBUnixQPathInfo(xid, pTcon, fromName,
|
info_buf_source,
|
||||||
info_buf_source,
|
cifs_sb_source->local_nls,
|
||||||
cifs_sb_source->local_nls,
|
cifs_sb_source->mnt_cifs_flags &
|
||||||
cifs_sb_source->mnt_cifs_flags &
|
|
||||||
CIFS_MOUNT_MAP_SPECIAL_CHR);
|
CIFS_MOUNT_MAP_SPECIAL_CHR);
|
||||||
/* else rc is still EEXIST so will fall through to
|
if (rc != 0)
|
||||||
unlink the target and retry rename */
|
goto unlink_target;
|
||||||
if (rc == 0) {
|
|
||||||
rc = CIFSSMBUnixQPathInfo(xid, pTcon, toName,
|
rc = CIFSSMBUnixQPathInfo(xid, pTcon,
|
||||||
info_buf_target,
|
toName, info_buf_target,
|
||||||
cifs_sb_target->local_nls,
|
cifs_sb_target->local_nls,
|
||||||
/* remap based on source sb */
|
/* remap based on source sb */
|
||||||
cifs_sb_source->mnt_cifs_flags &
|
cifs_sb_source->mnt_cifs_flags &
|
||||||
CIFS_MOUNT_MAP_SPECIAL_CHR);
|
|
||||||
}
|
|
||||||
if ((rc == 0) &&
|
|
||||||
(info_buf_source->UniqueId ==
|
|
||||||
info_buf_target->UniqueId)) {
|
|
||||||
/* do not rename since the files are hardlinked which
|
|
||||||
is a noop */
|
|
||||||
} else {
|
|
||||||
/* we either can not tell the files are hardlinked
|
|
||||||
(as with Windows servers) or files are not
|
|
||||||
hardlinked so delete the target manually before
|
|
||||||
renaming to follow POSIX rather than Windows
|
|
||||||
semantics */
|
|
||||||
cifs_unlink(target_inode, target_direntry);
|
|
||||||
rc = CIFSSMBRename(xid, pTcon, fromName,
|
|
||||||
toName,
|
|
||||||
cifs_sb_source->local_nls,
|
|
||||||
cifs_sb_source->mnt_cifs_flags
|
|
||||||
& CIFS_MOUNT_MAP_SPECIAL_CHR);
|
|
||||||
}
|
|
||||||
kfree(info_buf_source);
|
|
||||||
} /* if we can not get memory just leave rc as EEXIST */
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rc)
|
|
||||||
cFYI(1, ("rename rc %d", rc));
|
|
||||||
|
|
||||||
if ((rc == -EIO) || (rc == -EEXIST)) {
|
|
||||||
int oplock = 0;
|
|
||||||
__u16 netfid;
|
|
||||||
|
|
||||||
/* BB FIXME Is Generic Read correct for rename? */
|
|
||||||
/* if renaming directory - we should not say CREATE_NOT_DIR,
|
|
||||||
need to test renaming open directory, also GENERIC_READ
|
|
||||||
might not right be right access to request */
|
|
||||||
rc = CIFSSMBOpen(xid, pTcon, fromName, FILE_OPEN, GENERIC_READ,
|
|
||||||
CREATE_NOT_DIR, &netfid, &oplock, NULL,
|
|
||||||
cifs_sb_source->local_nls,
|
|
||||||
cifs_sb_source->mnt_cifs_flags &
|
|
||||||
CIFS_MOUNT_MAP_SPECIAL_CHR);
|
|
||||||
if (rc == 0) {
|
|
||||||
rc = CIFSSMBRenameOpenFile(xid, pTcon, netfid, toName,
|
|
||||||
cifs_sb_source->local_nls,
|
|
||||||
cifs_sb_source->mnt_cifs_flags &
|
|
||||||
CIFS_MOUNT_MAP_SPECIAL_CHR);
|
CIFS_MOUNT_MAP_SPECIAL_CHR);
|
||||||
CIFSSMBClose(xid, pTcon, netfid);
|
|
||||||
}
|
if (rc == 0 && (info_buf_source->UniqueId ==
|
||||||
|
info_buf_target->UniqueId))
|
||||||
|
/* same file, POSIX says that this is a noop */
|
||||||
|
goto cifs_rename_exit;
|
||||||
|
} /* else ... BB we could add the same check for Windows by
|
||||||
|
checking the UniqueId via FILE_INTERNAL_INFO */
|
||||||
|
unlink_target:
|
||||||
|
/*
|
||||||
|
* we either can not tell the files are hardlinked (as with
|
||||||
|
* Windows servers) or files are not hardlinked. Delete the
|
||||||
|
* target manually before renaming to follow POSIX rather than
|
||||||
|
* Windows semantics
|
||||||
|
*/
|
||||||
|
cifs_unlink(target_inode, target_direntry);
|
||||||
|
rc = cifs_do_rename(xid, source_direntry, fromName,
|
||||||
|
target_direntry, toName);
|
||||||
}
|
}
|
||||||
|
|
||||||
cifs_rename_exit:
|
cifs_rename_exit:
|
||||||
|
kfree(info_buf_source);
|
||||||
kfree(fromName);
|
kfree(fromName);
|
||||||
kfree(toName);
|
kfree(toName);
|
||||||
FreeXid(xid);
|
FreeXid(xid);
|
||||||
|
@ -1506,101 +1635,6 @@ cifs_set_file_size(struct inode *inode, struct iattr *attrs,
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
|
||||||
cifs_set_file_info(struct inode *inode, struct iattr *attrs, int xid,
|
|
||||||
char *full_path, __u32 dosattr)
|
|
||||||
{
|
|
||||||
int rc;
|
|
||||||
int oplock = 0;
|
|
||||||
__u16 netfid;
|
|
||||||
__u32 netpid;
|
|
||||||
bool set_time = false;
|
|
||||||
struct cifsFileInfo *open_file;
|
|
||||||
struct cifsInodeInfo *cifsInode = CIFS_I(inode);
|
|
||||||
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
|
|
||||||
struct cifsTconInfo *pTcon = cifs_sb->tcon;
|
|
||||||
FILE_BASIC_INFO info_buf;
|
|
||||||
|
|
||||||
if (attrs->ia_valid & ATTR_ATIME) {
|
|
||||||
set_time = true;
|
|
||||||
info_buf.LastAccessTime =
|
|
||||||
cpu_to_le64(cifs_UnixTimeToNT(attrs->ia_atime));
|
|
||||||
} else
|
|
||||||
info_buf.LastAccessTime = 0;
|
|
||||||
|
|
||||||
if (attrs->ia_valid & ATTR_MTIME) {
|
|
||||||
set_time = true;
|
|
||||||
info_buf.LastWriteTime =
|
|
||||||
cpu_to_le64(cifs_UnixTimeToNT(attrs->ia_mtime));
|
|
||||||
} else
|
|
||||||
info_buf.LastWriteTime = 0;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Samba throws this field away, but windows may actually use it.
|
|
||||||
* Do not set ctime unless other time stamps are changed explicitly
|
|
||||||
* (i.e. by utimes()) since we would then have a mix of client and
|
|
||||||
* server times.
|
|
||||||
*/
|
|
||||||
if (set_time && (attrs->ia_valid & ATTR_CTIME)) {
|
|
||||||
cFYI(1, ("CIFS - CTIME changed"));
|
|
||||||
info_buf.ChangeTime =
|
|
||||||
cpu_to_le64(cifs_UnixTimeToNT(attrs->ia_ctime));
|
|
||||||
} else
|
|
||||||
info_buf.ChangeTime = 0;
|
|
||||||
|
|
||||||
info_buf.CreationTime = 0; /* don't change */
|
|
||||||
info_buf.Attributes = cpu_to_le32(dosattr);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If the file is already open for write, just use that fileid
|
|
||||||
*/
|
|
||||||
open_file = find_writable_file(cifsInode);
|
|
||||||
if (open_file) {
|
|
||||||
netfid = open_file->netfid;
|
|
||||||
netpid = open_file->pid;
|
|
||||||
goto set_via_filehandle;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* NT4 apparently returns success on this call, but it doesn't
|
|
||||||
* really work.
|
|
||||||
*/
|
|
||||||
if (!(pTcon->ses->flags & CIFS_SES_NT4)) {
|
|
||||||
rc = CIFSSMBSetPathInfo(xid, pTcon, full_path,
|
|
||||||
&info_buf, cifs_sb->local_nls,
|
|
||||||
cifs_sb->mnt_cifs_flags &
|
|
||||||
CIFS_MOUNT_MAP_SPECIAL_CHR);
|
|
||||||
if (rc != -EOPNOTSUPP && rc != -EINVAL)
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
cFYI(1, ("calling SetFileInfo since SetPathInfo for "
|
|
||||||
"times not supported by this server"));
|
|
||||||
rc = CIFSSMBOpen(xid, pTcon, full_path, FILE_OPEN,
|
|
||||||
SYNCHRONIZE | FILE_WRITE_ATTRIBUTES,
|
|
||||||
CREATE_NOT_DIR, &netfid, &oplock,
|
|
||||||
NULL, cifs_sb->local_nls,
|
|
||||||
cifs_sb->mnt_cifs_flags &
|
|
||||||
CIFS_MOUNT_MAP_SPECIAL_CHR);
|
|
||||||
|
|
||||||
if (rc != 0) {
|
|
||||||
if (rc == -EIO)
|
|
||||||
rc = -EINVAL;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
netpid = current->tgid;
|
|
||||||
|
|
||||||
set_via_filehandle:
|
|
||||||
rc = CIFSSMBSetFileInfo(xid, pTcon, &info_buf, netfid, netpid);
|
|
||||||
if (open_file == NULL)
|
|
||||||
CIFSSMBClose(xid, pTcon, netfid);
|
|
||||||
else
|
|
||||||
atomic_dec(&open_file->wrtPending);
|
|
||||||
out:
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs)
|
cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs)
|
||||||
{
|
{
|
||||||
|
|
|
@ -150,8 +150,7 @@ cifs_buf_get(void)
|
||||||
but it may be more efficient to always alloc same size
|
but it may be more efficient to always alloc same size
|
||||||
albeit slightly larger than necessary and maxbuffersize
|
albeit slightly larger than necessary and maxbuffersize
|
||||||
defaults to this and can not be bigger */
|
defaults to this and can not be bigger */
|
||||||
ret_buf = (struct smb_hdr *) mempool_alloc(cifs_req_poolp,
|
ret_buf = mempool_alloc(cifs_req_poolp, GFP_NOFS);
|
||||||
GFP_KERNEL | GFP_NOFS);
|
|
||||||
|
|
||||||
/* clear the first few header bytes */
|
/* clear the first few header bytes */
|
||||||
/* for most paths, more is cleared in header_assemble */
|
/* for most paths, more is cleared in header_assemble */
|
||||||
|
@ -188,8 +187,7 @@ cifs_small_buf_get(void)
|
||||||
but it may be more efficient to always alloc same size
|
but it may be more efficient to always alloc same size
|
||||||
albeit slightly larger than necessary and maxbuffersize
|
albeit slightly larger than necessary and maxbuffersize
|
||||||
defaults to this and can not be bigger */
|
defaults to this and can not be bigger */
|
||||||
ret_buf = (struct smb_hdr *) mempool_alloc(cifs_sm_req_poolp,
|
ret_buf = mempool_alloc(cifs_sm_req_poolp, GFP_NOFS);
|
||||||
GFP_KERNEL | GFP_NOFS);
|
|
||||||
if (ret_buf) {
|
if (ret_buf) {
|
||||||
/* No need to clear memory here, cleared in header assemble */
|
/* No need to clear memory here, cleared in header assemble */
|
||||||
/* memset(ret_buf, 0, sizeof(struct smb_hdr) + 27);*/
|
/* memset(ret_buf, 0, sizeof(struct smb_hdr) + 27);*/
|
||||||
|
|
|
@ -640,6 +640,70 @@ static int is_dir_changed(struct file *file)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int cifs_save_resume_key(const char *current_entry,
|
||||||
|
struct cifsFileInfo *cifsFile)
|
||||||
|
{
|
||||||
|
int rc = 0;
|
||||||
|
unsigned int len = 0;
|
||||||
|
__u16 level;
|
||||||
|
char *filename;
|
||||||
|
|
||||||
|
if ((cifsFile == NULL) || (current_entry == NULL))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
level = cifsFile->srch_inf.info_level;
|
||||||
|
|
||||||
|
if (level == SMB_FIND_FILE_UNIX) {
|
||||||
|
FILE_UNIX_INFO *pFindData = (FILE_UNIX_INFO *)current_entry;
|
||||||
|
|
||||||
|
filename = &pFindData->FileName[0];
|
||||||
|
if (cifsFile->srch_inf.unicode) {
|
||||||
|
len = cifs_unicode_bytelen(filename);
|
||||||
|
} else {
|
||||||
|
/* BB should we make this strnlen of PATH_MAX? */
|
||||||
|
len = strnlen(filename, PATH_MAX);
|
||||||
|
}
|
||||||
|
cifsFile->srch_inf.resume_key = pFindData->ResumeKey;
|
||||||
|
} else if (level == SMB_FIND_FILE_DIRECTORY_INFO) {
|
||||||
|
FILE_DIRECTORY_INFO *pFindData =
|
||||||
|
(FILE_DIRECTORY_INFO *)current_entry;
|
||||||
|
filename = &pFindData->FileName[0];
|
||||||
|
len = le32_to_cpu(pFindData->FileNameLength);
|
||||||
|
cifsFile->srch_inf.resume_key = pFindData->FileIndex;
|
||||||
|
} else if (level == SMB_FIND_FILE_FULL_DIRECTORY_INFO) {
|
||||||
|
FILE_FULL_DIRECTORY_INFO *pFindData =
|
||||||
|
(FILE_FULL_DIRECTORY_INFO *)current_entry;
|
||||||
|
filename = &pFindData->FileName[0];
|
||||||
|
len = le32_to_cpu(pFindData->FileNameLength);
|
||||||
|
cifsFile->srch_inf.resume_key = pFindData->FileIndex;
|
||||||
|
} else if (level == SMB_FIND_FILE_ID_FULL_DIR_INFO) {
|
||||||
|
SEARCH_ID_FULL_DIR_INFO *pFindData =
|
||||||
|
(SEARCH_ID_FULL_DIR_INFO *)current_entry;
|
||||||
|
filename = &pFindData->FileName[0];
|
||||||
|
len = le32_to_cpu(pFindData->FileNameLength);
|
||||||
|
cifsFile->srch_inf.resume_key = pFindData->FileIndex;
|
||||||
|
} else if (level == SMB_FIND_FILE_BOTH_DIRECTORY_INFO) {
|
||||||
|
FILE_BOTH_DIRECTORY_INFO *pFindData =
|
||||||
|
(FILE_BOTH_DIRECTORY_INFO *)current_entry;
|
||||||
|
filename = &pFindData->FileName[0];
|
||||||
|
len = le32_to_cpu(pFindData->FileNameLength);
|
||||||
|
cifsFile->srch_inf.resume_key = pFindData->FileIndex;
|
||||||
|
} else if (level == SMB_FIND_FILE_INFO_STANDARD) {
|
||||||
|
FIND_FILE_STANDARD_INFO *pFindData =
|
||||||
|
(FIND_FILE_STANDARD_INFO *)current_entry;
|
||||||
|
filename = &pFindData->FileName[0];
|
||||||
|
/* one byte length, no name conversion */
|
||||||
|
len = (unsigned int)pFindData->FileNameLength;
|
||||||
|
cifsFile->srch_inf.resume_key = pFindData->ResumeKey;
|
||||||
|
} else {
|
||||||
|
cFYI(1, ("Unknown findfirst level %d", level));
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
cifsFile->srch_inf.resume_name_len = len;
|
||||||
|
cifsFile->srch_inf.presume_name = filename;
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
/* find the corresponding entry in the search */
|
/* find the corresponding entry in the search */
|
||||||
/* Note that the SMB server returns search entries for . and .. which
|
/* Note that the SMB server returns search entries for . and .. which
|
||||||
complicates logic here if we choose to parse for them and we do not
|
complicates logic here if we choose to parse for them and we do not
|
||||||
|
@ -703,6 +767,7 @@ static int find_cifs_entry(const int xid, struct cifsTconInfo *pTcon,
|
||||||
while ((index_to_find >= cifsFile->srch_inf.index_of_last_entry) &&
|
while ((index_to_find >= cifsFile->srch_inf.index_of_last_entry) &&
|
||||||
(rc == 0) && !cifsFile->srch_inf.endOfSearch) {
|
(rc == 0) && !cifsFile->srch_inf.endOfSearch) {
|
||||||
cFYI(1, ("calling findnext2"));
|
cFYI(1, ("calling findnext2"));
|
||||||
|
cifs_save_resume_key(cifsFile->srch_inf.last_entry, cifsFile);
|
||||||
rc = CIFSFindNext(xid, pTcon, cifsFile->netfid,
|
rc = CIFSFindNext(xid, pTcon, cifsFile->netfid,
|
||||||
&cifsFile->srch_inf);
|
&cifsFile->srch_inf);
|
||||||
if (rc)
|
if (rc)
|
||||||
|
@ -919,69 +984,6 @@ static int cifs_filldir(char *pfindEntry, struct file *file,
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int cifs_save_resume_key(const char *current_entry,
|
|
||||||
struct cifsFileInfo *cifsFile)
|
|
||||||
{
|
|
||||||
int rc = 0;
|
|
||||||
unsigned int len = 0;
|
|
||||||
__u16 level;
|
|
||||||
char *filename;
|
|
||||||
|
|
||||||
if ((cifsFile == NULL) || (current_entry == NULL))
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
level = cifsFile->srch_inf.info_level;
|
|
||||||
|
|
||||||
if (level == SMB_FIND_FILE_UNIX) {
|
|
||||||
FILE_UNIX_INFO *pFindData = (FILE_UNIX_INFO *)current_entry;
|
|
||||||
|
|
||||||
filename = &pFindData->FileName[0];
|
|
||||||
if (cifsFile->srch_inf.unicode) {
|
|
||||||
len = cifs_unicode_bytelen(filename);
|
|
||||||
} else {
|
|
||||||
/* BB should we make this strnlen of PATH_MAX? */
|
|
||||||
len = strnlen(filename, PATH_MAX);
|
|
||||||
}
|
|
||||||
cifsFile->srch_inf.resume_key = pFindData->ResumeKey;
|
|
||||||
} else if (level == SMB_FIND_FILE_DIRECTORY_INFO) {
|
|
||||||
FILE_DIRECTORY_INFO *pFindData =
|
|
||||||
(FILE_DIRECTORY_INFO *)current_entry;
|
|
||||||
filename = &pFindData->FileName[0];
|
|
||||||
len = le32_to_cpu(pFindData->FileNameLength);
|
|
||||||
cifsFile->srch_inf.resume_key = pFindData->FileIndex;
|
|
||||||
} else if (level == SMB_FIND_FILE_FULL_DIRECTORY_INFO) {
|
|
||||||
FILE_FULL_DIRECTORY_INFO *pFindData =
|
|
||||||
(FILE_FULL_DIRECTORY_INFO *)current_entry;
|
|
||||||
filename = &pFindData->FileName[0];
|
|
||||||
len = le32_to_cpu(pFindData->FileNameLength);
|
|
||||||
cifsFile->srch_inf.resume_key = pFindData->FileIndex;
|
|
||||||
} else if (level == SMB_FIND_FILE_ID_FULL_DIR_INFO) {
|
|
||||||
SEARCH_ID_FULL_DIR_INFO *pFindData =
|
|
||||||
(SEARCH_ID_FULL_DIR_INFO *)current_entry;
|
|
||||||
filename = &pFindData->FileName[0];
|
|
||||||
len = le32_to_cpu(pFindData->FileNameLength);
|
|
||||||
cifsFile->srch_inf.resume_key = pFindData->FileIndex;
|
|
||||||
} else if (level == SMB_FIND_FILE_BOTH_DIRECTORY_INFO) {
|
|
||||||
FILE_BOTH_DIRECTORY_INFO *pFindData =
|
|
||||||
(FILE_BOTH_DIRECTORY_INFO *)current_entry;
|
|
||||||
filename = &pFindData->FileName[0];
|
|
||||||
len = le32_to_cpu(pFindData->FileNameLength);
|
|
||||||
cifsFile->srch_inf.resume_key = pFindData->FileIndex;
|
|
||||||
} else if (level == SMB_FIND_FILE_INFO_STANDARD) {
|
|
||||||
FIND_FILE_STANDARD_INFO *pFindData =
|
|
||||||
(FIND_FILE_STANDARD_INFO *)current_entry;
|
|
||||||
filename = &pFindData->FileName[0];
|
|
||||||
/* one byte length, no name conversion */
|
|
||||||
len = (unsigned int)pFindData->FileNameLength;
|
|
||||||
cifsFile->srch_inf.resume_key = pFindData->ResumeKey;
|
|
||||||
} else {
|
|
||||||
cFYI(1, ("Unknown findfirst level %d", level));
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
cifsFile->srch_inf.resume_name_len = len;
|
|
||||||
cifsFile->srch_inf.presume_name = filename;
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
int cifs_readdir(struct file *file, void *direntry, filldir_t filldir)
|
int cifs_readdir(struct file *file, void *direntry, filldir_t filldir)
|
||||||
{
|
{
|
||||||
|
|
|
@ -624,8 +624,10 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time,
|
||||||
ses, nls_cp);
|
ses, nls_cp);
|
||||||
|
|
||||||
ssetup_exit:
|
ssetup_exit:
|
||||||
if (spnego_key)
|
if (spnego_key) {
|
||||||
|
key_revoke(spnego_key);
|
||||||
key_put(spnego_key);
|
key_put(spnego_key);
|
||||||
|
}
|
||||||
kfree(str_area);
|
kfree(str_area);
|
||||||
if (resp_buf_type == CIFS_SMALL_BUFFER) {
|
if (resp_buf_type == CIFS_SMALL_BUFFER) {
|
||||||
cFYI(1, ("ssetup freeing small buf %p", iov[0].iov_base));
|
cFYI(1, ("ssetup freeing small buf %p", iov[0].iov_base));
|
||||||
|
|
|
@ -50,8 +50,7 @@ AllocMidQEntry(const struct smb_hdr *smb_buffer, struct cifsSesInfo *ses)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
temp = (struct mid_q_entry *) mempool_alloc(cifs_mid_poolp,
|
temp = mempool_alloc(cifs_mid_poolp, GFP_NOFS);
|
||||||
GFP_KERNEL | GFP_NOFS);
|
|
||||||
if (temp == NULL)
|
if (temp == NULL)
|
||||||
return temp;
|
return temp;
|
||||||
else {
|
else {
|
||||||
|
|
Loading…
Reference in a new issue