/*
* fs/cifs/connect.c
*
- * Copyright (C) International Business Machines Corp., 2002,2007
+ * Copyright (C) International Business Machines Corp., 2002,2008
* Author(s): Steve French (sfrench@us.ibm.com)
*
* This library is free software; you can redistribute it and/or modify
#define CIFS_PORT 445
#define RFC1001_PORT 139
-static DECLARE_COMPLETION(cifsd_complete);
-
extern void SMBNTencrypt(unsigned char *passwd, unsigned char *c8,
unsigned char *p24);
char *domainname;
char *UNC;
char *UNCip;
- char *in6_addr; /* ipv6 address as human readable form of in6_addr */
+ char *in6_addr; /* ipv6 address as human readable form of in6_addr */
char *iocharset; /* local code page for mapping to and from Unicode */
char source_rfc1001_name[16]; /* netbios name of client */
char target_rfc1001_name[16]; /* netbios name of server for Win9x/ME */
mode_t file_mode;
mode_t dir_mode;
unsigned secFlg;
- unsigned rw:1;
- unsigned retry:1;
- unsigned intr:1;
- unsigned setuids:1;
- unsigned override_uid:1;
- unsigned override_gid:1;
- unsigned noperm:1;
- unsigned no_psx_acl:1; /* set if posix acl support should be disabled */
- unsigned cifs_acl:1;
- unsigned no_xattr:1; /* set if xattr (EA) support should be disabled*/
- unsigned server_ino:1; /* use inode numbers from server ie UniqueId */
- unsigned direct_io:1;
- unsigned remap:1; /* set to remap seven reserved chars in filenames */
- unsigned posix_paths:1; /* unset to not ask for posix pathnames. */
- unsigned no_linux_ext:1;
- unsigned sfu_emul:1;
- unsigned nullauth:1; /* attempt to authenticate with null user */
- unsigned nocase; /* request case insensitive filenames */
- unsigned nobrl; /* disable sending byte range locks to srv */
+ bool rw:1;
+ bool retry:1;
+ bool intr:1;
+ bool setuids:1;
+ bool override_uid:1;
+ bool override_gid:1;
+ bool dynperm:1;
+ bool noperm:1;
+ bool no_psx_acl:1; /* set if posix acl support should be disabled */
+ bool cifs_acl:1;
+ bool no_xattr:1; /* set if xattr (EA) support should be disabled*/
+ bool server_ino:1; /* use inode numbers from server ie UniqueId */
+ bool direct_io:1;
+ bool remap:1; /* set to remap seven reserved chars in filenames */
+ bool posix_paths:1; /* unset to not ask for posix pathnames. */
+ bool no_linux_ext:1;
+ bool sfu_emul:1;
+ bool nullauth:1; /* attempt to authenticate with null user */
+ bool nocase:1; /* request case insensitive filenames */
+ bool nobrl:1; /* disable sending byte range locks to srv */
+ bool seal:1; /* request transport encryption on share */
unsigned int rsize;
unsigned int wsize;
unsigned int sockopt;
if (server->ssocket) {
cFYI(1, ("State: 0x%x Flags: 0x%lx", server->ssocket->state,
server->ssocket->flags));
- server->ssocket->ops->shutdown(server->ssocket, SEND_SHUTDOWN);
+ kernel_sock_shutdown(server->ssocket, SHUT_WR);
cFYI(1, ("Post shutdown state: 0x%x Flags: 0x%lx",
server->ssocket->state,
server->ssocket->flags));
struct task_struct *task_to_wake = NULL;
struct mid_q_entry *mid_entry;
char temp;
- int isLargeBuf = FALSE;
- int isMultiRsp;
+ bool isLargeBuf = false;
+ bool isMultiRsp;
int reconnect;
current->flags |= PF_MEMALLOC;
- server->tsk = current; /* save process info to wake at shutdown */
- cFYI(1, ("Demultiplex PID: %d", current->pid));
+ cFYI(1, ("Demultiplex PID: %d", task_pid_nr(current)));
write_lock(&GlobalSMBSeslock);
atomic_inc(&tcpSesAllocCount);
length = tcpSesAllocCount.counter;
write_unlock(&GlobalSMBSeslock);
- complete(&cifsd_complete);
if (length > 1)
mempool_resize(cifs_req_poolp, length + cifs_min_rcv,
GFP_KERNEL);
} else /* if existing small buf clear beginning */
memset(smallbuf, 0, sizeof(struct smb_hdr));
- isLargeBuf = FALSE;
- isMultiRsp = FALSE;
+ isLargeBuf = false;
+ isMultiRsp = false;
smb_buffer = smallbuf;
iov.iov_base = smb_buffer;
iov.iov_len = 4;
msleep(1); /* minimum sleep to prevent looping
allowing socket to clear and app threads to set
tcpStatus CifsNeedReconnect if server hung */
- continue;
+ if (pdu_length < 4)
+ goto incomplete_rcv;
+ else
+ continue;
} else if (length <= 0) {
if (server->tcpStatus == CifsNew) {
cFYI(1, ("tcp session abend after SMBnegprot"));
csocket = server->ssocket;
wake_up(&server->response_q);
continue;
- } else if (length < 4) {
- cFYI(1, ("less than four bytes received (%d bytes)",
- length));
+ } else if (length < pdu_length) {
+ cFYI(1, ("requested %d bytes but only got %d bytes",
+ pdu_length, length));
pdu_length -= length;
msleep(1);
goto incomplete_rcv;
reconnect = 0;
if (pdu_length > MAX_CIFS_SMALL_BUFFER_SIZE - 4) {
- isLargeBuf = TRUE;
+ isLargeBuf = true;
memcpy(bigbuf, smallbuf, 4);
smb_buffer = bigbuf;
}
allowing socket to clear and app
threads to set tcpStatus
CifsNeedReconnect if server hung*/
+ length = 0;
continue;
} else if (length <= 0) {
cERROR(1, ("Received no data, expecting %d",
(mid_entry->command == smb_buffer->Command)) {
if (check2ndT2(smb_buffer,server->maxBuf) > 0) {
/* We have a multipart transact2 resp */
- isMultiRsp = TRUE;
+ isMultiRsp = true;
if (mid_entry->resp_buf) {
/* merge response - fix up 1st*/
if (coalesce_t2(smb_buffer,
mid_entry->resp_buf)) {
- mid_entry->multiRsp = 1;
+ mid_entry->multiRsp =
+ true;
break;
} else {
/* all parts received */
- mid_entry->multiEnd = 1;
+ mid_entry->multiEnd =
+ true;
goto multi_t2_fnd;
}
} else {
/* Have first buffer */
mid_entry->resp_buf =
smb_buffer;
- mid_entry->largeBuf = 1;
+ mid_entry->largeBuf =
+ true;
bigbuf = NULL;
}
}
break;
}
mid_entry->resp_buf = smb_buffer;
- if (isLargeBuf)
- mid_entry->largeBuf = 1;
- else
- mid_entry->largeBuf = 0;
+ mid_entry->largeBuf = isLargeBuf;
multi_t2_fnd:
task_to_wake = mid_entry->tsk;
mid_entry->midState = MID_RESPONSE_RECEIVED;
smallbuf = NULL;
}
wake_up_process(task_to_wake);
- } else if ((is_valid_oplock_break(smb_buffer, server) == FALSE)
- && (isMultiRsp == FALSE)) {
+ } else if (!is_valid_oplock_break(smb_buffer, server) &&
+ !isMultiRsp) {
cERROR(1, ("No task to wake, unknown frame received! "
"NumMids %d", midCount.counter));
cifs_dump_mem("Received Data is: ", (char *)smb_buffer,
spin_lock(&GlobalMid_Lock);
server->tcpStatus = CifsExiting;
- server->tsk = NULL;
+ spin_unlock(&GlobalMid_Lock);
+ wake_up_all(&server->response_q);
+
+ /* don't exit until kthread_stop is called */
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ while (!kthread_should_stop()) {
+ schedule();
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ }
+ set_current_state(TASK_RUNNING);
+
/* check if we have blocked requests that need to free */
/* Note that cifs_max_pending is normally 50, but
can be set at module install time to as little as two */
+ spin_lock(&GlobalMid_Lock);
if (atomic_read(&server->inFlight) >= cifs_max_pending)
atomic_set(&server->inFlight, cifs_max_pending - 1);
/* We do not want to set the max_pending too low or we
server->ssocket = NULL;
}
/* buffer usuallly freed in free_mid - need to free it here on exit */
- if (bigbuf != NULL)
- cifs_buf_release(bigbuf);
- if (smallbuf != NULL)
+ cifs_buf_release(bigbuf);
+ if (smallbuf) /* no sense logging a debug message if NULL */
cifs_small_buf_release(smallbuf);
read_lock(&GlobalSMBSeslock);
}
write_unlock(&GlobalSMBSeslock);
+ kfree(server->hostname);
kfree(server);
if (length > 0)
mempool_resize(cifs_req_poolp, length + cifs_min_rcv,
return 0;
}
+/* extract the host portion of the UNC string */
+static char *
+extract_hostname(const char *unc)
+{
+ const char *src;
+ char *dst, *delim;
+ unsigned int len;
+
+ /* skip double chars at beginning of string */
+ /* BB: check validity of these bytes? */
+ src = unc + 2;
+
+ /* delimiter between hostname and sharename is always '\\' now */
+ delim = strchr(src, '\\');
+ if (!delim)
+ return ERR_PTR(-EINVAL);
+
+ len = delim - src;
+ dst = kmalloc((len + 1), GFP_KERNEL);
+ if (dst == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ memcpy(dst, src, len);
+ dst[len] = '\0';
+
+ return dst;
+}
+
static int
cifs_parse_mount_options(char *options, const char *devname,
struct smb_vol *vol)
vol->linux_gid = current->gid;
vol->dir_mode = S_IRWXUGO;
/* 2767 perms indicate mandatory locking support */
- vol->file_mode = S_IALLUGO & ~(S_ISUID | S_IXGRP);
+ vol->file_mode = (S_IRWXUGO | S_ISGID) & (~S_IXGRP);
/* vol->retry default is 0 (i.e. "soft" limited retry not hard retry) */
- vol->rw = TRUE;
+ vol->rw = true;
/* default is always to request posix paths. */
vol->posix_paths = 1;
} else if (strnicmp(data, "guest", 5) == 0) {
/* ignore */
} else if (strnicmp(data, "rw", 2) == 0) {
- vol->rw = TRUE;
+ vol->rw = true;
} else if ((strnicmp(data, "suid", 4) == 0) ||
(strnicmp(data, "nosuid", 6) == 0) ||
(strnicmp(data, "exec", 4) == 0) ||
is ok to just ignore them */
continue;
} else if (strnicmp(data, "ro", 2) == 0) {
- vol->rw = FALSE;
+ vol->rw = false;
} else if (strnicmp(data, "hard", 4) == 0) {
vol->retry = 1;
} else if (strnicmp(data, "soft", 4) == 0) {
vol->setuids = 1;
} else if (strnicmp(data, "nosetuids", 9) == 0) {
vol->setuids = 0;
+ } else if (strnicmp(data, "dynperm", 7) == 0) {
+ vol->dynperm = true;
+ } else if (strnicmp(data, "nodynperm", 9) == 0) {
+ vol->dynperm = false;
} else if (strnicmp(data, "nohard", 6) == 0) {
vol->retry = 0;
} else if (strnicmp(data, "nosoft", 6) == 0) {
vol->no_psx_acl = 1;
} else if (strnicmp(data, "sign", 4) == 0) {
vol->secFlg |= CIFSSEC_MUST_SIGN;
-/* } else if (strnicmp(data, "seal",4) == 0) {
- vol->secFlg |= CIFSSEC_MUST_SEAL; */
+ } else if (strnicmp(data, "seal", 4) == 0) {
+ /* we do not do the following in secFlags because seal
+ is a per tree connection (mount) not a per socket
+ or per-smb connection option in the protocol */
+ /* vol->secFlg |= CIFSSEC_MUST_SEAL; */
+ vol->seal = 1;
} else if (strnicmp(data, "direct", 6) == 0) {
vol->direct_io = 1;
} else if (strnicmp(data, "forcedirectio", 13) == 0) {
"begin with // or \\\\ \n");
return 1;
}
+ value = strpbrk(vol->UNC+2, "/\\");
+ if (value)
+ *value = '\\';
} else {
printk(KERN_WARNING "CIFS: UNC name too long\n");
return 1;
static struct cifsSesInfo *
cifs_find_tcp_session(struct in_addr *target_ip_addr,
- struct in6_addr *target_ip6_addr,
- char *userName, struct TCP_Server_Info **psrvTcp)
+ struct in6_addr *target_ip6_addr,
+ char *userName, struct TCP_Server_Info **psrvTcp)
{
struct list_head *tmp;
struct cifsSesInfo *ses;
+
*psrvTcp = NULL;
- read_lock(&GlobalSMBSeslock);
+ read_lock(&GlobalSMBSeslock);
list_for_each(tmp, &GlobalSMBSessionList) {
ses = list_entry(tmp, struct cifsSesInfo, cifsSessionList);
- if (ses->server) {
- if ((target_ip_addr &&
- (ses->server->addr.sockAddr.sin_addr.s_addr
- == target_ip_addr->s_addr)) || (target_ip6_addr
- && memcmp(&ses->server->addr.sockAddr6.sin6_addr,
- target_ip6_addr, sizeof(*target_ip6_addr)))) {
- /* BB lock server and tcp session and increment
- use count here?? */
-
- /* found a match on the TCP session */
- *psrvTcp = ses->server;
-
- /* BB check if reconnection needed */
- if (strncmp
- (ses->userName, userName,
- MAX_USERNAME_SIZE) == 0){
- read_unlock(&GlobalSMBSeslock);
- /* Found exact match on both TCP and
- SMB sessions */
- return ses;
- }
- }
+ if (!ses->server)
+ continue;
+
+ if (target_ip_addr &&
+ ses->server->addr.sockAddr.sin_addr.s_addr != target_ip_addr->s_addr)
+ continue;
+ else if (target_ip6_addr &&
+ memcmp(&ses->server->addr.sockAddr6.sin6_addr,
+ target_ip6_addr, sizeof(*target_ip6_addr)))
+ continue;
+ /* BB lock server and tcp session; increment use count here?? */
+
+ /* found a match on the TCP session */
+ *psrvTcp = ses->server;
+
+ /* BB check if reconnection needed */
+ if (strncmp(ses->userName, userName, MAX_USERNAME_SIZE) == 0) {
+ read_unlock(&GlobalSMBSeslock);
+ /* Found exact match on both TCP and
+ SMB sessions */
+ return ses;
}
/* else tcp and smb sessions need reconnection */
}
read_unlock(&GlobalSMBSeslock);
+
return NULL;
}
{
struct list_head *tmp;
struct cifsTconInfo *tcon;
+ __be32 old_ip;
read_lock(&GlobalSMBSeslock);
+
list_for_each(tmp, &GlobalTreeConnectionList) {
cFYI(1, ("Next tcon"));
tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList);
- if (tcon->ses) {
- if (tcon->ses->server) {
- cFYI(1,
- ("old ip addr: %x == new ip %x ?",
- tcon->ses->server->addr.sockAddr.sin_addr.
- s_addr, new_target_ip_addr));
- if (tcon->ses->server->addr.sockAddr.sin_addr.
- s_addr == new_target_ip_addr) {
- /* BB lock tcon, server and tcp session and increment use count here? */
- /* found a match on the TCP session */
- /* BB check if reconnection needed */
- cFYI(1,
- ("IP match, old UNC: %s new: %s",
- tcon->treeName, uncName));
- if (strncmp
- (tcon->treeName, uncName,
- MAX_TREE_SIZE) == 0) {
- cFYI(1,
- ("and old usr: %s new: %s",
- tcon->treeName, uncName));
- if (strncmp
- (tcon->ses->userName,
- userName,
- MAX_USERNAME_SIZE) == 0) {
- read_unlock(&GlobalSMBSeslock);
- /* matched smb session
- (user name */
- return tcon;
- }
- }
- }
- }
- }
- }
- read_unlock(&GlobalSMBSeslock);
- return NULL;
-}
+ if (!tcon->ses || !tcon->ses->server)
+ continue;
-int
-connect_to_dfs_path(int xid, struct cifsSesInfo *pSesInfo,
- const char *old_path, const struct nls_table *nls_codepage,
- int remap)
-{
- unsigned char *referrals = NULL;
- unsigned int num_referrals;
- int rc = 0;
+ old_ip = tcon->ses->server->addr.sockAddr.sin_addr.s_addr;
+ cFYI(1, ("old ip addr: %x == new ip %x ?",
+ old_ip, new_target_ip_addr));
+
+ if (old_ip != new_target_ip_addr)
+ continue;
- rc = get_dfs_path(xid, pSesInfo, old_path, nls_codepage,
- &num_referrals, &referrals, remap);
+ /* BB lock tcon, server, tcp session and increment use count? */
+ /* found a match on the TCP session */
+ /* BB check if reconnection needed */
+ cFYI(1, ("IP match, old UNC: %s new: %s",
+ tcon->treeName, uncName));
- /* BB Add in code to: if valid refrl, if not ip address contact
- the helper that resolves tcp names, mount to it, try to
- tcon to it unmount it if fail */
+ if (strncmp(tcon->treeName, uncName, MAX_TREE_SIZE))
+ continue;
- kfree(referrals);
+ cFYI(1, ("and old usr: %s new: %s",
+ tcon->treeName, uncName));
- return rc;
+ if (strncmp(tcon->ses->userName, userName, MAX_USERNAME_SIZE))
+ continue;
+
+ /* matched smb session (user name) */
+ read_unlock(&GlobalSMBSeslock);
+ return tcon;
+ }
+
+ read_unlock(&GlobalSMBSeslock);
+ return NULL;
}
int
get_dfs_path(int xid, struct cifsSesInfo *pSesInfo, const char *old_path,
const struct nls_table *nls_codepage, unsigned int *pnum_referrals,
- unsigned char **preferrals, int remap)
+ struct dfs_info3_param **preferrals, int remap)
{
char *temp_unc;
int rc = 0;
*pnum_referrals = 0;
+ *preferrals = NULL;
if (pSesInfo->ipc_tid == 0) {
temp_unc = kmalloc(2 /* for slashes */ +
if (rc == 0)
rc = CIFSGetDFSRefer(xid, pSesInfo, old_path, preferrals,
pnum_referrals, nls_codepage, remap);
+ /* BB map targetUNCs to dfs_info3 structures, here or
+ in CIFSGetDFSRefer BB */
return rc;
}
if (psin_server->sin_port) { /* user overrode default port */
rc = (*csocket)->ops->connect(*csocket,
(struct sockaddr *) psin_server,
- sizeof (struct sockaddr_in), 0);
+ sizeof(struct sockaddr_in), 0);
if (rc >= 0)
connected = 1;
}
rc = (*csocket)->ops->connect(*csocket,
(struct sockaddr *) psin_server,
- sizeof (struct sockaddr_in), 0);
+ sizeof(struct sockaddr_in), 0);
if (rc >= 0)
connected = 1;
}
psin_server->sin_port = htons(RFC1001_PORT);
rc = (*csocket)->ops->connect(*csocket, (struct sockaddr *)
psin_server,
- sizeof (struct sockaddr_in), 0);
+ sizeof(struct sockaddr_in), 0);
if (rc >= 0)
connected = 1;
}
if (psin_server->sin6_port) { /* user overrode default port */
rc = (*csocket)->ops->connect(*csocket,
(struct sockaddr *) psin_server,
- sizeof (struct sockaddr_in6), 0);
+ sizeof(struct sockaddr_in6), 0);
if (rc >= 0)
connected = 1;
}
rc = (*csocket)->ops->connect(*csocket,
(struct sockaddr *) psin_server,
- sizeof (struct sockaddr_in6), 0);
+ sizeof(struct sockaddr_in6), 0);
if (rc >= 0)
connected = 1;
}
if (!connected) {
psin_server->sin6_port = htons(RFC1001_PORT);
rc = (*csocket)->ops->connect(*csocket, (struct sockaddr *)
- psin_server, sizeof (struct sockaddr_in6), 0);
+ psin_server, sizeof(struct sockaddr_in6), 0);
if (rc >= 0)
connected = 1;
}
originally at mount time */
if ((saved_cap & CIFS_UNIX_POSIX_ACL_CAP) == 0)
cap &= ~CIFS_UNIX_POSIX_ACL_CAP;
- if ((saved_cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) == 0)
+ if ((saved_cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) == 0) {
+ if (cap & CIFS_UNIX_POSIX_PATHNAMES_CAP)
+ cERROR(1, ("POSIXPATH support change"));
cap &= ~CIFS_UNIX_POSIX_PATHNAMES_CAP;
+ } else if ((cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) == 0) {
+ cERROR(1, ("possible reconnect error"));
+ cERROR(1,
+ ("server disabled POSIX path support"));
+ }
}
cap &= CIFS_UNIX_CAP_MASK;
if (sb && (CIFS_SB(sb)->rsize > 127 * 1024)) {
if ((cap & CIFS_UNIX_LARGE_READ_CAP) == 0) {
CIFS_SB(sb)->rsize = 127 * 1024;
-#ifdef CONFIG_CIFS_DEBUG2
- cFYI(1, ("larger reads not supported by srv"));
-#endif
+ cFYI(DBG2,
+ ("larger reads not supported by srv"));
}
}
cFYI(1, ("very large write cap"));
#endif /* CIFS_DEBUG2 */
if (CIFSSMBSetFSUnixInfo(xid, tcon, cap)) {
- cFYI(1, ("setting capabilities failed"));
+ if (vol_info == NULL) {
+ cFYI(1, ("resetting capabilities failed"));
+ } else
+ cERROR(1, ("Negotiating Unix capabilities "
+ "with the server failed. Consider "
+ "mounting with the Unix Extensions\n"
+ "disabled, if problems are found, "
+ "by specifying the nounix mount "
+ "option."));
+
}
}
}
+static void
+convert_delimiter(char *path, char delim)
+{
+ int i;
+ char old_delim;
+
+ if (path == NULL)
+ return;
+
+ if (delim == '/')
+ old_delim = '\\';
+ else
+ old_delim = '/';
+
+ for (i = 0; path[i] != '\0'; i++) {
+ if (path[i] == old_delim)
+ path[i] = delim;
+ }
+}
+
int
cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
char *mount_data, const char *devname)
memset(&volume_info, 0, sizeof(struct smb_vol));
if (cifs_parse_mount_options(mount_data, devname, &volume_info)) {
- kfree(volume_info.UNC);
- kfree(volume_info.password);
- kfree(volume_info.prepath);
- FreeXid(xid);
- return -EINVAL;
+ rc = -EINVAL;
+ goto out;
}
if (volume_info.nullauth) {
cFYI(1, ("null user"));
- volume_info.username = NULL;
+ volume_info.username = "";
} else if (volume_info.username) {
/* BB fixme parse for domain name here */
cFYI(1, ("Username: %s", volume_info.username));
cifserror("No username specified");
/* In userspace mount helper we can get user name from alternate
locations such as env variables and files on disk */
- kfree(volume_info.UNC);
- kfree(volume_info.password);
- kfree(volume_info.prepath);
- FreeXid(xid);
- return -EINVAL;
+ rc = -EINVAL;
+ goto out;
}
if (volume_info.UNCip && volume_info.UNC) {
if (rc <= 0) {
/* we failed translating address */
- kfree(volume_info.UNC);
- kfree(volume_info.password);
- kfree(volume_info.prepath);
- FreeXid(xid);
- return -EINVAL;
+ rc = -EINVAL;
+ goto out;
}
cFYI(1, ("UNC: %s ip: %s", volume_info.UNC, volume_info.UNCip));
/* BB using ip addr as server name to connect to the
DFS root below */
cERROR(1, ("Connecting to DFS root not implemented yet"));
- kfree(volume_info.UNC);
- kfree(volume_info.password);
- kfree(volume_info.prepath);
- FreeXid(xid);
- return -EINVAL;
+ rc = -EINVAL;
+ goto out;
} else /* which servers DFS root would we conect to */ {
cERROR(1,
("CIFS mount error: No UNC path (e.g. -o "
"unc=//192.168.1.100/public) specified"));
- kfree(volume_info.UNC);
- kfree(volume_info.password);
- kfree(volume_info.prepath);
- FreeXid(xid);
- return -EINVAL;
+ rc = -EINVAL;
+ goto out;
}
/* this is needed for ASCII cp to Unicode converts */
if (cifs_sb->local_nls == NULL) {
cERROR(1, ("CIFS mount error: iocharset %s not found",
volume_info.iocharset));
- kfree(volume_info.UNC);
- kfree(volume_info.password);
- kfree(volume_info.prepath);
- FreeXid(xid);
- return -ELIBACC;
+ rc = -ELIBACC;
+ goto out;
}
}
&sin_server6.sin6_addr,
volume_info.username, &srvTcp);
} else {
- kfree(volume_info.UNC);
- kfree(volume_info.password);
- kfree(volume_info.prepath);
- FreeXid(xid);
- return -EINVAL;
+ rc = -EINVAL;
+ goto out;
}
if (srvTcp) {
"Aborting operation"));
if (csocket != NULL)
sock_release(csocket);
- kfree(volume_info.UNC);
- kfree(volume_info.password);
- kfree(volume_info.prepath);
- FreeXid(xid);
- return rc;
+ goto out;
}
- srvTcp = kmalloc(sizeof (struct TCP_Server_Info), GFP_KERNEL);
- if (srvTcp == NULL) {
+ srvTcp = kzalloc(sizeof(struct TCP_Server_Info), GFP_KERNEL);
+ if (!srvTcp) {
rc = -ENOMEM;
sock_release(csocket);
- kfree(volume_info.UNC);
- kfree(volume_info.password);
- kfree(volume_info.prepath);
- FreeXid(xid);
- return rc;
+ goto out;
} else {
- memset(srvTcp, 0, sizeof (struct TCP_Server_Info));
memcpy(&srvTcp->addr.sockAddr, &sin_server,
- sizeof (struct sockaddr_in));
+ sizeof(struct sockaddr_in));
atomic_set(&srvTcp->inFlight, 0);
/* BB Add code for ipv6 case too */
srvTcp->ssocket = csocket;
srvTcp->protocolType = IPV4;
+ srvTcp->hostname = extract_hostname(volume_info.UNC);
+ if (IS_ERR(srvTcp->hostname)) {
+ rc = PTR_ERR(srvTcp->hostname);
+ sock_release(csocket);
+ goto out;
+ }
init_waitqueue_head(&srvTcp->response_q);
init_waitqueue_head(&srvTcp->request_q);
INIT_LIST_HEAD(&srvTcp->pending_mid_q);
srvTcp->tcpStatus = CifsNew;
init_MUTEX(&srvTcp->tcpSem);
srvTcp->tsk = kthread_run((void *)(void *)cifs_demultiplex_thread, srvTcp, "cifsd");
- if ( IS_ERR(srvTcp->tsk) ) {
+ if (IS_ERR(srvTcp->tsk)) {
rc = PTR_ERR(srvTcp->tsk);
cERROR(1, ("error %d create cifsd thread", rc));
srvTcp->tsk = NULL;
sock_release(csocket);
- kfree(volume_info.UNC);
- kfree(volume_info.password);
- kfree(volume_info.prepath);
- FreeXid(xid);
- return rc;
+ kfree(srvTcp->hostname);
+ goto out;
}
- wait_for_completion(&cifsd_complete);
rc = 0;
memcpy(srvTcp->workstation_RFC1001_name,
volume_info.source_rfc1001_name, 16);
if (existingCifsSes) {
pSesInfo = existingCifsSes;
- cFYI(1, ("Existing smb sess found"));
- kfree(volume_info.password);
- /* volume_info.UNC freed at end of function */
+ cFYI(1, ("Existing smb sess found (status=%d)",
+ pSesInfo->status));
+ down(&pSesInfo->sesSem);
+ if (pSesInfo->status == CifsNeedReconnect) {
+ cFYI(1, ("Session needs reconnect"));
+ rc = cifs_setup_session(xid, pSesInfo,
+ cifs_sb->local_nls);
+ }
+ up(&pSesInfo->sesSem);
} else if (!rc) {
cFYI(1, ("Existing smb sess not found"));
pSesInfo = sesInfoAlloc();
if (!rc) {
/* volume_info.password freed at unmount */
- if (volume_info.password)
+ if (volume_info.password) {
pSesInfo->password = volume_info.password;
+ /* set to NULL to prevent freeing on exit */
+ volume_info.password = NULL;
+ }
if (volume_info.username)
strncpy(pSesInfo->userName,
volume_info.username,
up(&pSesInfo->sesSem);
if (!rc)
atomic_inc(&srvTcp->socketUseCount);
- } else
- kfree(volume_info.password);
+ }
}
/* search for existing tcon to this server share */
cifs_sb->prepath = volume_info.prepath;
if (cifs_sb->prepath) {
cifs_sb->prepathlen = strlen(cifs_sb->prepath);
- cifs_sb->prepath[0] = CIFS_DIR_SEP(cifs_sb);
+ /* we can not convert the / to \ in the path
+ separators in the prefixpath yet because we do not
+ know (until reset_cifs_unix_caps is called later)
+ whether POSIX PATH CAP is available. We normalize
+ the / to \ after reset_cifs_unix_caps is called */
volume_info.prepath = NULL;
} else
cifs_sb->prepathlen = 0;
cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_OVERR_UID;
if (volume_info.override_gid)
cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_OVERR_GID;
+ if (volume_info.dynperm)
+ cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DYNPERM;
if (volume_info.direct_io) {
cFYI(1, ("mounting share using direct i/o"));
cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DIRECT_IO;
}
+ if ((volume_info.cifs_acl) && (volume_info.dynperm))
+ cERROR(1, ("mount option dynperm ignored if cifsacl "
+ "mount option supported"));
+
tcon =
find_unc(sin_server.sin_addr.s_addr, volume_info.UNC,
volume_info.username);
for the retry flag is used */
tcon->retry = volume_info.retry;
tcon->nocase = volume_info.nocase;
+ if (tcon->seal != volume_info.seal)
+ cERROR(1, ("transport encryption setting "
+ "conflicts with existing tid"));
} else {
tcon = tconInfoAlloc();
if (tcon == NULL)
if ((strchr(volume_info.UNC + 3, '\\') == NULL)
&& (strchr(volume_info.UNC + 3, '/') ==
NULL)) {
- rc = connect_to_dfs_path(xid, pSesInfo,
+/* rc = connect_to_dfs_path(xid, pSesInfo,
"", cifs_sb->local_nls,
cifs_sb->mnt_cifs_flags &
- CIFS_MOUNT_MAP_SPECIAL_CHR);
- kfree(volume_info.UNC);
- FreeXid(xid);
- return -ENODEV;
+ CIFS_MOUNT_MAP_SPECIAL_CHR);*/
+ cFYI(1, ("DFS root not supported"));
+ rc = -ENODEV;
+ goto out;
} else {
/* BB Do we need to wrap sesSem around
* this TCon call and Unix SetFS as
atomic_inc(&pSesInfo->inUse);
tcon->retry = volume_info.retry;
tcon->nocase = volume_info.nocase;
+ tcon->seal = volume_info.seal;
}
}
}
srvTcp->tcpStatus = CifsExiting;
spin_unlock(&GlobalMid_Lock);
if (srvTcp->tsk) {
- struct task_struct *tsk;
/* If we could verify that kthread_stop would
always wake up processes blocked in
tcp in recv_mesg then we could remove the
send_sig call */
force_sig(SIGKILL, srvTcp->tsk);
- tsk = srvTcp->tsk;
- if (tsk)
- kthread_stop(tsk);
+ kthread_stop(srvTcp->tsk);
}
}
/* If find_unc succeeded then rc == 0 so we can not end */
if ((temp_rc == -ESHUTDOWN) &&
(pSesInfo->server) &&
(pSesInfo->server->tsk)) {
- struct task_struct *tsk;
force_sig(SIGKILL,
pSesInfo->server->tsk);
- tsk = pSesInfo->server->tsk;
- if (tsk)
- kthread_stop(tsk);
+ kthread_stop(pSesInfo->server->tsk);
}
- } else
+ } else {
cFYI(1, ("No session or bad tcon"));
+ if ((pSesInfo->server) &&
+ (pSesInfo->server->tsk)) {
+ force_sig(SIGKILL,
+ pSesInfo->server->tsk);
+ kthread_stop(pSesInfo->server->tsk);
+ }
+ }
sesInfoFree(pSesInfo);
/* pSesInfo = NULL; */
}
tcon->ses = pSesInfo;
/* do not care if following two calls succeed - informational */
- CIFSSMBQFSDeviceInfo(xid, tcon);
- CIFSSMBQFSAttributeInfo(xid, tcon);
+ if (!tcon->ipc) {
+ CIFSSMBQFSDeviceInfo(xid, tcon);
+ CIFSSMBQFSAttributeInfo(xid, tcon);
+ }
/* tell server which Unix caps we support */
if (tcon->ses->capabilities & CAP_UNIX)
else
tcon->unix_ext = 0; /* server does not support them */
+ /* convert forward to back slashes in prepath here if needed */
+ if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) == 0)
+ convert_delimiter(cifs_sb->prepath,
+ CIFS_DIR_SEP(cifs_sb));
+
if ((tcon->unix_ext == 0) && (cifs_sb->rsize > (1024 * 127))) {
cifs_sb->rsize = 1024 * 127;
-#ifdef CONFIG_CIFS_DEBUG2
- cFYI(1, ("no very large read support, rsize now 127K"));
-#endif
+ cFYI(DBG2,
+ ("no very large read support, rsize now 127K"));
}
if (!(tcon->ses->capabilities & CAP_LARGE_WRITE_X))
cifs_sb->wsize = min(cifs_sb->wsize,
(in which case it is not needed anymore) but when new sesion is created
the password ptr is put in the new session structure (in which case the
password will be freed at unmount time) */
+out:
+ /* zero out password before freeing */
+ if (volume_info.password != NULL) {
+ memset(volume_info.password, 0, strlen(volume_info.password));
+ kfree(volume_info.password);
+ }
kfree(volume_info.UNC);
kfree(volume_info.prepath);
FreeXid(xid);
user = ses->userName;
domain = ses->domainName;
smb_buffer = cifs_buf_get();
- if (smb_buffer == NULL) {
+
+ if (smb_buffer == NULL)
return -ENOMEM;
- }
+
smb_buffer_response = smb_buffer;
pSMBr = pSMB = (SESSION_SETUP_ANDX *) smb_buffer;
pSMB->req_no_secext.ByteCount = cpu_to_le16(count);
rc = SendReceive(xid, ses, smb_buffer, smb_buffer_response,
- &bytes_returned, 1);
+ &bytes_returned, CIFS_LONG_OP);
if (rc) {
/* rc = map_smb_to_linux_error(smb_buffer_response); now done in SendReceive */
} else if ((smb_buffer_response->WordCount == 3)
sesssetup_nomem: /* do not return an error on nomem for the info strings,
since that could make reconnection harder, and
reconnection might be needed to free memory */
- if (smb_buffer)
- cifs_buf_release(smb_buffer);
+ cifs_buf_release(smb_buffer);
return rc;
}
static int
CIFSNTLMSSPNegotiateSessSetup(unsigned int xid,
- struct cifsSesInfo *ses, int *pNTLMv2_flag,
+ struct cifsSesInfo *ses, bool *pNTLMv2_flag,
const struct nls_table *nls_codepage)
{
struct smb_hdr *smb_buffer;
int remaining_words = 0;
int bytes_returned = 0;
int len;
- int SecurityBlobLength = sizeof (NEGOTIATE_MESSAGE);
+ int SecurityBlobLength = sizeof(NEGOTIATE_MESSAGE);
PNEGOTIATE_MESSAGE SecurityBlob;
PCHALLENGE_MESSAGE SecurityBlob2;
__u32 negotiate_flags, capabilities;
if (ses == NULL)
return -EINVAL;
domain = ses->domainName;
- *pNTLMv2_flag = FALSE;
+ *pNTLMv2_flag = false;
smb_buffer = cifs_buf_get();
if (smb_buffer == NULL) {
return -ENOMEM;
pSMB->req.ByteCount = cpu_to_le16(count);
rc = SendReceive(xid, ses, smb_buffer, smb_buffer_response,
- &bytes_returned, 1);
+ &bytes_returned, CIFS_LONG_OP);
if (smb_buffer_response->Status.CifsError ==
cpu_to_le32(NT_STATUS_MORE_PROCESSING_REQUIRED))
CIFS_CRYPTO_KEY_SIZE);
if (SecurityBlob2->NegotiateFlags &
cpu_to_le32(NTLMSSP_NEGOTIATE_NTLMV2))
- *pNTLMv2_flag = TRUE;
+ *pNTLMv2_flag = true;
if ((SecurityBlob2->NegotiateFlags &
cpu_to_le32(NTLMSSP_NEGOTIATE_ALWAYS_SIGN))
rc = -EIO;
}
- if (smb_buffer)
- cifs_buf_release(smb_buffer);
+ cifs_buf_release(smb_buffer);
return rc;
}
static int
CIFSNTLMSSPAuthSessSetup(unsigned int xid, struct cifsSesInfo *ses,
- char *ntlm_session_key, int ntlmv2_flag,
- const struct nls_table *nls_codepage)
+ char *ntlm_session_key, bool ntlmv2_flag,
+ const struct nls_table *nls_codepage)
{
struct smb_hdr *smb_buffer;
struct smb_hdr *smb_buffer_response;
int remaining_words = 0;
int bytes_returned = 0;
int len;
- int SecurityBlobLength = sizeof (AUTHENTICATE_MESSAGE);
+ int SecurityBlobLength = sizeof(AUTHENTICATE_MESSAGE);
PAUTHENTICATE_MESSAGE SecurityBlob;
__u32 negotiate_flags, capabilities;
__u16 count;
return -ENOMEM;
}
smb_buffer_response = smb_buffer;
- pSMB = (SESSION_SETUP_ANDX *) smb_buffer;
- pSMBr = (SESSION_SETUP_ANDX *) smb_buffer_response;
+ pSMB = (SESSION_SETUP_ANDX *)smb_buffer;
+ pSMBr = (SESSION_SETUP_ANDX *)smb_buffer_response;
/* send SMBsessionSetup here */
header_assemble(smb_buffer, SMB_COM_SESSION_SETUP_ANDX,
smb_buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
capabilities = CAP_LARGE_FILES | CAP_NT_SMBS | CAP_LEVEL_II_OPLOCKS |
- CAP_EXTENDED_SECURITY;
+ CAP_EXTENDED_SECURITY;
if (ses->capabilities & CAP_UNICODE) {
smb_buffer->Flags2 |= SMBFLG2_UNICODE;
capabilities |= CAP_UNICODE;
}
pSMB->req.Capabilities = cpu_to_le32(capabilities);
- bcc_ptr = (char *) &pSMB->req.SecurityBlob;
- SecurityBlob = (PAUTHENTICATE_MESSAGE) bcc_ptr;
+ bcc_ptr = (char *)&pSMB->req.SecurityBlob;
+ SecurityBlob = (PAUTHENTICATE_MESSAGE)bcc_ptr;
strncpy(SecurityBlob->Signature, NTLMSSP_SIGNATURE, 8);
SecurityBlob->MessageType = NtLmAuthenticate;
bcc_ptr += SecurityBlobLength;
- negotiate_flags =
- NTLMSSP_NEGOTIATE_UNICODE | NTLMSSP_REQUEST_TARGET |
- NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_TARGET_INFO |
- 0x80000000 | NTLMSSP_NEGOTIATE_128;
+ negotiate_flags = NTLMSSP_NEGOTIATE_UNICODE | NTLMSSP_REQUEST_TARGET |
+ NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_TARGET_INFO |
+ 0x80000000 | NTLMSSP_NEGOTIATE_128;
if (sign_CIFS_PDUs)
negotiate_flags |= /* NTLMSSP_NEGOTIATE_ALWAYS_SIGN |*/ NTLMSSP_NEGOTIATE_SIGN;
if (ntlmv2_flag)
pSMB->req.ByteCount = cpu_to_le16(count);
rc = SendReceive(xid, ses, smb_buffer, smb_buffer_response,
- &bytes_returned, 1);
+ &bytes_returned, CIFS_LONG_OP);
if (rc) {
-/* rc = map_smb_to_linux_error(smb_buffer_response); *//* done in SendReceive now */
- } else if ((smb_buffer_response->WordCount == 3)
- || (smb_buffer_response->WordCount == 4)) {
+/* rc = map_smb_to_linux_error(smb_buffer_response) done in SendReceive now */
+ } else if ((smb_buffer_response->WordCount == 3) ||
+ (smb_buffer_response->WordCount == 4)) {
__u16 action = le16_to_cpu(pSMBr->resp.Action);
- __u16 blob_len =
- le16_to_cpu(pSMBr->resp.SecurityBlobLength);
+ __u16 blob_len = le16_to_cpu(pSMBr->resp.SecurityBlobLength);
if (action & GUEST_LOGIN)
cFYI(1, (" Guest login")); /* BB Should we set anything
in SesInfo struct ? */
bcc_ptr[0] = 0;
bcc_ptr++;
} else
- cFYI(1,
- ("field of length %d "
+ cFYI(1, ("field of length %d "
"extends beyond end of smb ",
len));
}
} else {
- cERROR(1,
- (" Security Blob extends beyond end "
+ cERROR(1, ("Security Blob extends beyond end "
"of SMB"));
}
} else {
cERROR(1, ("No session structure passed in."));
}
} else {
- cERROR(1,
- (" Invalid Word count %d: ",
+ cERROR(1, ("Invalid Word count %d: ",
smb_buffer_response->WordCount));
rc = -EIO;
}
- if (smb_buffer)
- cifs_buf_release(smb_buffer);
+ cifs_buf_release(smb_buffer);
return rc;
}
pSMB->hdr.smb_buf_length += count;
pSMB->ByteCount = cpu_to_le16(count);
- rc = SendReceive(xid, ses, smb_buffer, smb_buffer_response, &length, 0);
+ rc = SendReceive(xid, ses, smb_buffer, smb_buffer_response, &length,
+ CIFS_STD_OP);
/* if (rc) rc = map_smb_to_linux_error(smb_buffer_response); */
/* above now done in SendReceive */
bcc_ptr = pByteArea(smb_buffer_response);
length = strnlen(bcc_ptr, BCC(smb_buffer_response) - 2);
/* skip service field (NB: this field is always ASCII) */
+ if (length == 3) {
+ if ((bcc_ptr[0] == 'I') && (bcc_ptr[1] == 'P') &&
+ (bcc_ptr[2] == 'C')) {
+ cFYI(1, ("IPC connection"));
+ tcon->ipc = 1;
+ }
+ } else if (length == 2) {
+ if ((bcc_ptr[0] == 'A') && (bcc_ptr[1] == ':')) {
+ /* the most common case */
+ cFYI(1, ("disk share connection"));
+ }
+ }
bcc_ptr += length + 1;
strncpy(tcon->treeName, tree, MAX_TREE_SIZE);
if (smb_buffer->Flags2 & SMBFLG2_UNICODE) {
ses->ipc_tid = smb_buffer_response->Tid;
}
- if (smb_buffer)
- cifs_buf_release(smb_buffer);
+ cifs_buf_release(smb_buffer);
return rc;
}
FreeXid(xid);
return 0;
}
+ DeleteTconOplockQEntries(cifs_sb->tcon);
tconInfoFree(cifs_sb->tcon);
if ((ses) && (ses->server)) {
/* save off task so we do not refer to ses later */
cifs_sb->prepath = NULL;
kfree(tmp);
if (ses)
- schedule_timeout_interruptible(msecs_to_jiffies(500));
- if (ses)
sesInfoFree(ses);
FreeXid(xid);
- return rc; /* BB check if we should always return zero here */
+ return rc;
}
int cifs_setup_session(unsigned int xid, struct cifsSesInfo *pSesInfo,
{
int rc = 0;
char ntlm_session_key[CIFS_SESS_KEY_SIZE];
- int ntlmv2_flag = FALSE;
+ bool ntlmv2_flag = false;
int first_time = 0;
/* what if server changes its buffer size after dropping the session? */