diff options
Diffstat (limited to 'fs')
43 files changed, 850 insertions, 363 deletions
diff --git a/fs/Kconfig b/fs/Kconfig index c75c95406497..429a00228507 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -2007,7 +2007,7 @@ config CIFS_EXPERIMENTAL config CIFS_UPCALL bool "Kerberos/SPNEGO advanced session setup (EXPERIMENTAL)" depends on CIFS_EXPERIMENTAL - depends on CONNECTOR + depends on KEYS help Enables an upcall mechanism for CIFS which will be used to contact userspace helper utilities to provide SPNEGO packaged Kerberos diff --git a/fs/cifs/CHANGES b/fs/cifs/CHANGES index 3d419163c3d3..64dd22239b21 100644 --- a/fs/cifs/CHANGES +++ b/fs/cifs/CHANGES @@ -1,3 +1,7 @@ +Version 1.52 +------------ +Fix oops on second mount to server when null auth is used. + Version 1.51 ------------ Fix memory leak in statfs when mounted to very old servers (e.g. @@ -12,7 +16,12 @@ leak that causes cifsd not to stop and rmmod to fail to cleanup cifs_request_buffers pool. Fix problem with POSIX Open/Mkdir on bigendian architectures. Fix possible memory corruption when EAGAIN returned on kern_recvmsg. Return better error if server -requires packet signing but client has disabled it. +requires packet signing but client has disabled it. When mounted +with cifsacl mount option - mode bits are approximated based +on the contents of the ACL of the file or directory. When cifs +mount helper is missing convert make sure that UNC name +has backslash (not forward slash) between ip address of server +and the share name. Version 1.50 ------------ diff --git a/fs/cifs/Makefile b/fs/cifs/Makefile index ff6ba8d823f0..45e42fb97c19 100644 --- a/fs/cifs/Makefile +++ b/fs/cifs/Makefile @@ -3,4 +3,9 @@ # obj-$(CONFIG_CIFS) += cifs.o -cifs-objs := cifsfs.o cifssmb.o cifs_debug.o connect.o dir.o file.o inode.o link.o misc.o netmisc.o smbdes.o smbencrypt.o transport.o asn1.o md4.o md5.o cifs_unicode.o nterr.o xattr.o cifsencrypt.o fcntl.o readdir.o ioctl.o sess.o export.o cifsacl.o +cifs-y := cifsfs.o cifssmb.o cifs_debug.o connect.o dir.o file.o inode.o \ + link.o misc.o netmisc.o smbdes.o smbencrypt.o transport.o asn1.o \ + md4.o md5.o cifs_unicode.o nterr.o xattr.o cifsencrypt.o fcntl.o \ + readdir.o ioctl.o sess.o export.o cifsacl.o + +cifs-$(CONFIG_CIFS_UPCALL) += cifs_spnego.o diff --git a/fs/cifs/asn1.c b/fs/cifs/asn1.c index 2a01f3ef96a0..bcda2c6b6a04 100644 --- a/fs/cifs/asn1.c +++ b/fs/cifs/asn1.c @@ -77,8 +77,12 @@ #define SPNEGO_OID_LEN 7 #define NTLMSSP_OID_LEN 10 +#define KRB5_OID_LEN 7 +#define MSKRB5_OID_LEN 7 static unsigned long SPNEGO_OID[7] = { 1, 3, 6, 1, 5, 5, 2 }; static unsigned long NTLMSSP_OID[10] = { 1, 3, 6, 1, 4, 1, 311, 2, 2, 10 }; +static unsigned long KRB5_OID[7] = { 1, 2, 840, 113554, 1, 2, 2 }; +static unsigned long MSKRB5_OID[7] = { 1, 2, 840, 48018, 1, 2, 2 }; /* * ASN.1 context. @@ -457,6 +461,7 @@ decode_negTokenInit(unsigned char *security_blob, int length, unsigned long *oid = NULL; unsigned int cls, con, tag, oidlen, rc; int use_ntlmssp = FALSE; + int use_kerberos = FALSE; *secType = NTLM; /* BB eventually make Kerberos or NLTMSSP the default*/ @@ -545,18 +550,28 @@ decode_negTokenInit(unsigned char *security_blob, int length, return 0; } if ((tag == ASN1_OJI) && (con == ASN1_PRI)) { - rc = asn1_oid_decode(&ctx, end, &oid, &oidlen); - if (rc) { + if (asn1_oid_decode(&ctx, end, &oid, &oidlen)) { + cFYI(1, ("OID len = %d oid = 0x%lx 0x%lx " "0x%lx 0x%lx", oidlen, *oid, *(oid + 1), *(oid + 2), *(oid + 3))); - rc = compare_oid(oid, oidlen, - NTLMSSP_OID, NTLMSSP_OID_LEN); - kfree(oid); - if (rc) + + if (compare_oid(oid, oidlen, + MSKRB5_OID, + MSKRB5_OID_LEN)) + use_kerberos = TRUE; + else if (compare_oid(oid, oidlen, + KRB5_OID, + KRB5_OID_LEN)) + use_kerberos = TRUE; + else if (compare_oid(oid, oidlen, + NTLMSSP_OID, + NTLMSSP_OID_LEN)) use_ntlmssp = TRUE; + + kfree(oid); } } else { cFYI(1, ("Should be an oid what is going on?")); @@ -609,12 +624,10 @@ decode_negTokenInit(unsigned char *security_blob, int length, ctx.pointer)); /* is this UTF-8 or ASCII? */ } - /* if (use_kerberos) - *secType = Kerberos - else */ - if (use_ntlmssp) { + if (use_kerberos) + *secType = Kerberos; + else if (use_ntlmssp) *secType = NTLMSSP; - } return 1; } diff --git a/fs/cifs/cifs_spnego.c b/fs/cifs/cifs_spnego.c new file mode 100644 index 000000000000..ad54a3a6e434 --- /dev/null +++ b/fs/cifs/cifs_spnego.c @@ -0,0 +1,128 @@ +/* + * fs/cifs/cifs_spnego.c -- SPNEGO upcall management for CIFS + * + * Copyright (c) 2007 Red Hat, Inc. + * Author(s): Jeff Layton (jlayton@redhat.com) + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/list.h> +#include <linux/string.h> +#include <keys/user-type.h> +#include <linux/key-type.h> +#include "cifsglob.h" +#include "cifs_spnego.h" +#include "cifs_debug.h" + +/* create a new cifs key */ +static int +cifs_spnego_key_instantiate(struct key *key, const void *data, size_t datalen) +{ + char *payload; + int ret; + + ret = -ENOMEM; + payload = kmalloc(datalen, GFP_KERNEL); + if (!payload) + goto error; + + /* attach the data */ + memcpy(payload, data, datalen); + rcu_assign_pointer(key->payload.data, payload); + ret = 0; + +error: + return ret; +} + +static void +cifs_spnego_key_destroy(struct key *key) +{ + kfree(key->payload.data); +} + + +/* + * keytype for CIFS spnego keys + */ +struct key_type cifs_spnego_key_type = { + .name = "cifs.spnego", + .instantiate = cifs_spnego_key_instantiate, + .match = user_match, + .destroy = cifs_spnego_key_destroy, + .describe = user_describe, +}; + +/* get a key struct with a SPNEGO security blob, suitable for session setup */ +struct key * +cifs_get_spnego_key(struct cifsSesInfo *sesInfo, const char *hostname) +{ + struct TCP_Server_Info *server = sesInfo->server; + char *description, *dp; + size_t desc_len; + struct key *spnego_key; + + + /* version + ;ip{4|6}= + address + ;host=hostname + + ;sec= + ;uid= + NULL */ + desc_len = 4 + 5 + 32 + 1 + 5 + strlen(hostname) + + strlen(";sec=krb5") + 7 + sizeof(uid_t)*2 + 1; + spnego_key = ERR_PTR(-ENOMEM); + description = kzalloc(desc_len, GFP_KERNEL); + if (description == NULL) + goto out; + + dp = description; + /* start with version and hostname portion of UNC string */ + spnego_key = ERR_PTR(-EINVAL); + sprintf(dp, "0x%2.2x;host=%s;", CIFS_SPNEGO_UPCALL_VERSION, + hostname); + dp = description + strlen(description); + + /* add the server address */ + if (server->addr.sockAddr.sin_family == AF_INET) + sprintf(dp, "ip4=" NIPQUAD_FMT, + NIPQUAD(server->addr.sockAddr.sin_addr)); + else if (server->addr.sockAddr.sin_family == AF_INET6) + sprintf(dp, "ip6=" NIP6_SEQFMT, + NIP6(server->addr.sockAddr6.sin6_addr)); + else + goto out; + + dp = description + strlen(description); + + /* for now, only sec=krb5 is valid */ + if (server->secType == Kerberos) + sprintf(dp, ";sec=krb5"); + else + goto out; + + dp = description + strlen(description); + sprintf(dp, ";uid=0x%x", sesInfo->linux_uid); + + cFYI(1, ("key description = %s", description)); + spnego_key = request_key(&cifs_spnego_key_type, description, ""); + + if (cifsFYI && !IS_ERR(spnego_key)) { + struct cifs_spnego_msg *msg = spnego_key->payload.data; + cifs_dump_mem("SPNEGO reply blob:", msg->data, + msg->secblob_len + msg->sesskey_len); + } + +out: + kfree(description); + return spnego_key; +} diff --git a/fs/cifs/cifs_spnego.h b/fs/cifs/cifs_spnego.h new file mode 100644 index 000000000000..f443f3b35134 --- /dev/null +++ b/fs/cifs/cifs_spnego.h @@ -0,0 +1,46 @@ +/* + * fs/cifs/cifs_spnego.h -- SPNEGO upcall management for CIFS + * + * Copyright (c) 2007 Red Hat, Inc. + * Author(s): Jeff Layton (jlayton@redhat.com) + * Steve French (sfrench@us.ibm.com) + * + * This library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + * the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _CIFS_SPNEGO_H +#define _CIFS_SPNEGO_H + +#define CIFS_SPNEGO_UPCALL_VERSION 1 + +/* + * The version field should always be set to CIFS_SPNEGO_UPCALL_VERSION. + * The flags field is for future use. The request-key callout should set + * sesskey_len and secblob_len, and then concatenate the SessKey+SecBlob + * and stuff it in the data field. + */ +struct cifs_spnego_msg { + uint32_t version; + uint32_t flags; + uint32_t sesskey_len; + uint32_t secblob_len; + uint8_t data[1]; +}; + +#ifdef __KERNEL__ +extern struct key_type cifs_spnego_key_type; +#endif /* KERNEL */ + +#endif /* _CIFS_SPNEGO_H */ diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c index e8e56353f5a1..dabbce00712b 100644 --- a/fs/cifs/cifsacl.c +++ b/fs/cifs/cifsacl.c @@ -38,13 +38,13 @@ static struct cifs_wksid wksidarr[NUM_WK_SIDS] = { {{1, 1, {0, 0, 0, 0, 0, 5}, {cpu_to_le32(18), 0, 0, 0, 0} }, "sys"}, {{1, 2, {0, 0, 0, 0, 0, 5}, {cpu_to_le32(32), cpu_to_le32(544), 0, 0, 0} }, "root"}, {{1, 2, {0, 0, 0, 0, 0, 5}, {cpu_to_le32(32), cpu_to_le32(545), 0, 0, 0} }, "users"}, - {{1, 2, {0, 0, 0, 0, 0, 5}, {cpu_to_le32(32), cpu_to_le32(546), 0, 0, 0} }, "guest"} -}; + {{1, 2, {0, 0, 0, 0, 0, 5}, {cpu_to_le32(32), cpu_to_le32(546), 0, 0, 0} }, "guest"} } +; /* security id for everyone */ -static const struct cifs_sid sid_everyone = - {1, 1, {0, 0, 0, 0, 0, 0}, {} }; +static const struct cifs_sid sid_everyone = { + 1, 1, {0, 0, 0, 0, 0, 1}, {0} }; /* group users */ static const struct cifs_sid sid_user = {1, 2 , {0, 0, 0, 0, 0, 5}, {} }; @@ -97,7 +97,7 @@ int match_sid(struct cifs_sid *ctsid) /* if the two SIDs (roughly equivalent to a UUID for a user or group) are the same returns 1, if they do not match returns 0 */ -int compare_sids(struct cifs_sid *ctsid, struct cifs_sid *cwsid) +int compare_sids(const struct cifs_sid *ctsid, const struct cifs_sid *cwsid) { int i; int num_subauth, num_sat, num_saw; @@ -129,66 +129,142 @@ int compare_sids(struct cifs_sid *ctsid, struct cifs_sid *cwsid) return (1); /* sids compare/match */ } +/* + change posix mode to reflect permissions + pmode is the existing mode (we only want to overwrite part of this + bits to set can be: S_IRWXU, S_IRWXG or S_IRWXO ie 00700 or 00070 or 00007 +*/ +static void access_flags_to_mode(__u32 ace_flags, int type, umode_t *pmode, + umode_t *pbits_to_set) +{ + /* the order of ACEs is important. The canonical order is to begin with + DENY entries followed by ALLOW, otherwise an allow entry could be + encountered first, making the subsequent deny entry like "dead code" + which would be superflous since Windows stops when a match is made + for the operation you are trying to perform for your user */ + + /* For deny ACEs we change the mask so that subsequent allow access + control entries do not turn on the bits we are denying */ + if (type == ACCESS_DENIED) { + if (ace_flags & GENERIC_ALL) { + *pbits_to_set &= ~S_IRWXUGO; + } + if ((ace_flags & GENERIC_WRITE) || + ((ace_flags & FILE_WRITE_RIGHTS) == FILE_WRITE_RIGHTS)) + *pbits_to_set &= ~S_IWUGO; + if ((ace_flags & GENERIC_READ) || + ((ace_flags & FILE_READ_RIGHTS) == FILE_READ_RIGHTS)) + *pbits_to_set &= ~S_IRUGO; + if ((ace_flags & GENERIC_EXECUTE) || + ((ace_flags & FILE_EXEC_RIGHTS) == FILE_EXEC_RIGHTS)) + *pbits_to_set &= ~S_IXUGO; + return; + } else if (type != ACCESS_ALLOWED) { + cERROR(1, ("unknown access control type %d", type)); + return; + } + /* else ACCESS_ALLOWED type */ + + if (ace_flags & GENERIC_ALL) { + *pmode |= (S_IRWXUGO & (*pbits_to_set)); +#ifdef CONFIG_CIFS_DEBUG2 + cFYI(1, ("all perms")); +#endif + return; + } + if ((ace_flags & GENERIC_WRITE) || + ((ace_flags & FILE_WRITE_RIGHTS) == FILE_WRITE_RIGHTS)) + *pmode |= (S_IWUGO & (*pbits_to_set)); + if ((ace_flags & GENERIC_READ) || + ((ace_flags & FILE_READ_RIGHTS) == FILE_READ_RIGHTS)) + *pmode |= (S_IRUGO & (*pbits_to_set)); + if ((ace_flags & GENERIC_EXECUTE) || + ((ace_flags & FILE_EXEC_RIGHTS) == FILE_EXEC_RIGHTS)) + *pmode |= (S_IXUGO & (*pbits_to_set)); + +#ifdef CONFIG_CIFS_DEBUG2 + cFYI(1, ("access flags 0x%x mode now 0x%x", ace_flags, *pmode)); +#endif + return; +} + +/* + Generate access flags to reflect permissions mode is the existing mode. + This function is called for every ACE in the DACL whose SID matches + with either owner or group or everyone. +*/ + +static void mode_to_access_flags(umode_t mode, umode_t bits_to_use, + __u32 *pace_flags) +{ + /* reset access mask */ + *pace_flags = 0x0; + + /* bits to use are either S_IRWXU or S_IRWXG or S_IRWXO */ + mode &= bits_to_use; + + /* check for R/W/X UGO since we do not know whose flags + is this but we have cleared all the bits sans RWX for + either user or group or other as per bits_to_use */ + if (mode & S_IRUGO) + *pace_flags |= SET_FILE_READ_RIGHTS; + if (mode & S_IWUGO) + *pace_flags |= SET_FILE_WRITE_RIGHTS; + if (mode & S_IXUGO) + *pace_flags |= SET_FILE_EXEC_RIGHTS; + +#ifdef CONFIG_CIFS_DEBUG2 + cFYI(1, ("mode: 0x%x, access flags now 0x%x", mode, *pace_flags)); +#endif + return; +} + -static void parse_ace(struct cifs_ace *pace, char *end_of_acl) +#ifdef CONFIG_CIFS_DEBUG2 +static void dump_ace(struct cifs_ace *pace, char *end_of_acl) { int num_subauth; /* validate that we do not go past end of acl */ - /* XXX this if statement can be removed - if (end_of_acl < (char *)pace + sizeof(struct cifs_ace)) { + if (le16_to_cpu(pace->size) < 16) { + cERROR(1, ("ACE too small, %d", le16_to_cpu(pace->size))); + return; + } + + if (end_of_acl < (char *)pace + le16_to_cpu(pace->size)) { cERROR(1, ("ACL too small to parse ACE")); return; - } */ + } - num_subauth = pace->num_subauth; + num_subauth = pace->sid.num_subauth; if (num_subauth) { -#ifdef CONFIG_CIFS_DEBUG2 int i; - cFYI(1, ("ACE revision %d num_subauth %d", - pace->revision, pace->num_subauth)); + cFYI(1, ("ACE revision %d num_auth %d type %d flags %d size %d", + pace->sid.revision, pace->sid.num_subauth, pace->type, + pace->flags, pace->size)); for (i = 0; i < num_subauth; ++i) { cFYI(1, ("ACE sub_auth[%d]: 0x%x", i, - le32_to_cpu(pace->sub_auth[i]))); + le32_to_cpu(pace->sid.sub_auth[i]))); } /* BB add length check to make sure that we do not have huge num auths and therefore go off the end */ - - cFYI(1, ("RID %d", le32_to_cpu(pace->sub_auth[num_subauth-1]))); -#endif } return; } - -static void parse_ntace(struct cifs_ntace *pntace, char *end_of_acl) -{ - /* validate that we do not go past end of acl */ - if (end_of_acl < (char *)pntace + sizeof(struct cifs_ntace)) { - cERROR(1, ("ACL too small to parse NT ACE")); - return; - } - -#ifdef CONFIG_CIFS_DEBUG2 - cFYI(1, ("NTACE type %d flags 0x%x size %d, access Req 0x%x", - pntace->type, pntace->flags, pntace->size, - pntace->access_req)); #endif - return; -} - static void parse_dacl(struct cifs_acl *pdacl, char *end_of_acl, - struct cifs_sid *pownersid, struct cifs_sid *pgrpsid) + struct cifs_sid *pownersid, struct cifs_sid *pgrpsid, + struct inode *inode) { int i; int num_aces = 0; int acl_size; char *acl_base; - struct cifs_ntace **ppntace; struct cifs_ace **ppace; /* BB need to add parm so we can store the SID BB */ @@ -205,50 +281,63 @@ static void parse_dacl(struct cifs_acl *pdacl, char *end_of_acl, le32_to_cpu(pdacl->num_aces))); #endif + /* reset rwx permissions for user/group/other. + Also, if num_aces is 0 i.e. DACL has no ACEs, + user/group/other have no permissions */ + inode->i_mode &= ~(S_IRWXUGO); + + if (!pdacl) { + /* no DACL in the security descriptor, set + all the permissions for user/group/other */ + inode->i_mode |= S_IRWXUGO; + return; + } acl_base = (char *)pdacl; acl_size = sizeof(struct cifs_acl); num_aces = le32_to_cpu(pdacl->num_aces); if (num_aces > 0) { - ppntace = kmalloc(num_aces * sizeof(struct cifs_ntace *), - GFP_KERNEL); + umode_t user_mask = S_IRWXU; + umode_t group_mask = S_IRWXG; + umode_t other_mask = S_IRWXO; + ppace = kmalloc(num_aces * sizeof(struct cifs_ace *), GFP_KERNEL); /* cifscred->cecount = pdacl->num_aces; - cifscred->ntaces = kmalloc(num_aces * - sizeof(struct cifs_ntace *), GFP_KERNEL); cifscred->aces = kmalloc(num_aces * sizeof(struct cifs_ace *), GFP_KERNEL);*/ for (i = 0; i < num_aces; ++i) { - ppntace[i] = (struct cifs_ntace *) - (acl_base + acl_size); - ppace[i] = (struct cifs_ace *) ((char *)ppntace[i] + - sizeof(struct cifs_ntace)); - - parse_ntace(ppntace[i], end_of_acl); - if (end_of_acl < ((char *)ppace[i] + - (le16_to_cpu(ppntace[i]->size) - - sizeof(struct cifs_ntace)))) { - cERROR(1, ("ACL too small to parse ACE")); - break; - } else - parse_ace(ppace[i], end_of_acl); - -/* memcpy((void *)(&(cifscred->ntaces[i])), - (void *)ppntace[i], - sizeof(struct cifs_ntace)); - memcpy((void *)(&(cifscred->aces[i])), + ppace[i] = (struct cifs_ace *) (acl_base + acl_size); +#ifdef CONFIG_CIFS_DEBUG2 + dump_ace(ppace[i], end_of_acl); +#endif + if (compare_sids(&(ppace[i]->sid), pownersid)) + access_flags_to_mode(ppace[i]->access_req, + ppace[i]->type, + &(inode->i_mode), + &user_mask); + if (compare_sids(&(ppace[i]->sid), pgrpsid)) + access_flags_to_mode(ppace[i]->access_req, + ppace[i]->type, + &(inode->i_mode), + &group_mask); + if (compare_sids(&(ppace[i]->sid), &sid_everyone)) + access_flags_to_mode(ppace[i]->access_req, + ppace[i]->type, + &(inode->i_mode), + &other_mask); + +/* memcpy((void *)(&(cifscred->aces[i])), (void *)ppace[i], sizeof(struct cifs_ace)); */ - acl_base = (char *)ppntace[i]; - acl_size = le16_to_cpu(ppntace[i]->size); + acl_base = (char *)ppace[i]; + acl_size = le16_to_cpu(ppace[i]->size); } kfree(ppace); - kfree(ppntace); } return; @@ -257,20 +346,20 @@ static void parse_dacl(struct cifs_acl *pdacl, char *end_of_acl, static int parse_sid(struct cifs_sid *psid, char *end_of_acl) { - /* BB need to add parm so we can store the SID BB */ - /* validate that we do not go past end of acl */ - if (end_of_acl < (char *)psid + sizeof(struct cifs_sid)) { - cERROR(1, ("ACL too small to parse SID")); + /* validate that we do not go past end of ACL - sid must be at least 8 + bytes long (assuming no sub-auths - e.g. the null SID */ + if (end_of_acl < (char *)psid + 8) { + cERROR(1, ("ACL too small to parse SID %p", psid)); return -EINVAL; } if (psid->num_subauth) { #ifdef CONFIG_CIFS_DEBUG2 int i; - cFYI(1, ("SID revision %d num_auth %d First subauth 0x%x", - psid->revision, psid->num_subauth, psid->sub_auth[0])); + cFYI(1, ("SID revision %d num_auth %d", + psid->revision, psid->num_subauth)); for (i = 0; i < psid->num_subauth; i++) { cFYI(1, ("SID sub_auth[%d]: 0x%x ", i, @@ -289,27 +378,32 @@ static int parse_sid(struct cifs_sid *psid, char *end_of_acl) /* Convert CIFS ACL to POSIX form */ -int parse_sec_desc(struct cifs_ntsd *pntsd, int acl_len) +static int parse_sec_desc(struct cifs_ntsd *pntsd, int acl_len, + struct inode *inode) { int rc; struct cifs_sid *owner_sid_ptr, *group_sid_ptr; struct cifs_acl *dacl_ptr; /* no need for SACL ptr */ char *end_of_acl = ((char *)pntsd) + acl_len; + __u32 dacloffset; + + if ((inode == NULL) || (pntsd == NULL)) + return -EIO; owner_sid_ptr = (struct cifs_sid *)((char *)pntsd + le32_to_cpu(pntsd->osidoffset)); group_sid_ptr = (struct cifs_sid *)((char *)pntsd + le32_to_cpu(pntsd->gsidoffset)); - dacl_ptr = (struct cifs_acl *)((char *)pntsd + - le32_to_cpu(pntsd->dacloffset)); + dacloffset = le32_to_cpu(pntsd->dacloffset); + dacl_ptr = (struct cifs_acl *)((char *)pntsd + dacloffset); #ifdef CONFIG_CIFS_DEBUG2 cFYI(1, ("revision %d type 0x%x ooffset 0x%x goffset 0x%x " "sacloffset 0x%x dacloffset 0x%x", pntsd->revision, pntsd->type, le32_to_cpu(pntsd->osidoffset), le32_to_cpu(pntsd->gsidoffset), - le32_to_cpu(pntsd->sacloffset), - le32_to_cpu(pntsd->dacloffset))); + le32_to_cpu(pntsd->sacloffset), dacloffset)); #endif +/* cifs_dump_mem("owner_sid: ", owner_sid_ptr, 64); */ rc = parse_sid(owner_sid_ptr, end_of_acl); if (rc) return rc; @@ -318,16 +412,120 @@ int parse_sec_desc(struct cifs_ntsd *pntsd, int acl_len) if (rc) return rc; - parse_dacl(dacl_ptr, end_of_acl, owner_sid_ptr, group_sid_ptr); + if (dacloffset) + parse_dacl(dacl_ptr, end_of_acl, owner_sid_ptr, + group_sid_ptr, inode); + else + cFYI(1, ("no ACL")); /* BB grant all or default perms? */ /* cifscred->uid = owner_sid_ptr->rid; cifscred->gid = group_sid_ptr->rid; memcpy((void *)(&(cifscred->osid)), (void *)owner_sid_ptr, - sizeof (struct cifs_sid)); + sizeof(struct cifs_sid)); memcpy((void *)(&(cifscred->gsid)), (void *)group_sid_ptr, - sizeof (struct cifs_sid)); */ + sizeof(struct cifs_sid)); */ return (0); } + + +/* Retrieve an ACL from the server */ +static struct cifs_ntsd *get_cifs_acl(u32 *pacllen, struct inode *inode, + const char *path) +{ + struct cifsFileInfo *open_file; + int unlock_file = FALSE; + int xid; + int rc = -EIO; + __u16 fid; + struct super_block *sb; + struct cifs_sb_info *cifs_sb; + struct cifs_ntsd *pntsd = NULL; + + cFYI(1, ("get mode from ACL for %s", path)); + + if (inode == NULL) + return NULL; + + xid = GetXid(); + open_file = find_readable_file(CIFS_I(inode)); + sb = inode->i_sb; + if (sb == NULL) { + FreeXid(xid); + return NULL; + } + cifs_sb = CIFS_SB(sb); + + if (open_file) { + unlock_file = TRUE; + fid = open_file->netfid; + } else { + int oplock = FALSE; + /* open file */ + rc = CIFSSMBOpen(xid, cifs_sb->tcon, path, FILE_OPEN, + READ_CONTROL, 0, &fid, &oplock, NULL, + cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & + CIFS_MOUNT_MAP_SPECIAL_CHR); + if (rc != 0) { + cERROR(1, ("Unable to open file to get ACL")); + FreeXid(xid); + return NULL; + } + } + + rc = CIFSSMBGetCIFSACL(xid, cifs_sb->tcon, fid, &pntsd, pacllen); + cFYI(1, ("GetCIFSACL rc = %d ACL len %d", rc, *pacllen)); + if (unlock_file == TRUE) + atomic_dec(&open_file->wrtPending); + else + CIFSSMBClose(xid, cifs_sb->tcon, fid); + + FreeXid(xid); + return pntsd; +} + +/* Translate the CIFS ACL (simlar to NTFS ACL) for a file into mode bits */ +void acl_to_uid_mode(struct inode *inode, const char *path) +{ + struct cifs_ntsd *pntsd = NULL; + u32 acllen = 0; + int rc = 0; + +#ifdef CONFIG_CIFS_DEBUG2 + cFYI(1, ("converting ACL to mode for %s", path)); +#endif + pntsd = get_cifs_acl(&acllen, inode, path); + + /* if we can retrieve the ACL, now parse Access Control Entries, ACEs */ + if (pntsd) + rc = parse_sec_desc(pntsd, acllen, inode); + if (rc) + cFYI(1, ("parse sec desc failed rc = %d", rc)); + + kfree(pntsd); + return; +} + +/* Convert mode bits to an ACL so we can update the ACL on the server */ +int mode_to_acl(struct inode *inode, const char *path) +{ + int rc = 0; + __u32 acllen = 0; + struct cifs_ntsd *pntsd = NULL; + + cFYI(1, ("set ACL from mode for %s", path)); + + /* Get the security descriptor */ + pntsd = get_cifs_acl(&acllen, inode, path); + + /* Add/Modify the three ACEs for owner, group, everyone + while retaining the other ACEs */ + + /* Set the security descriptor */ + + + kfree(pntsd); + return rc; +} #endif /* CONFIG_CIFS_EXPERIMENTAL */ diff --git a/fs/cifs/cifsacl.h b/fs/cifs/cifsacl.h index 420f87813647..93a7c3462ea2 100644 --- a/fs/cifs/cifsacl.h +++ b/fs/cifs/cifsacl.h @@ -35,6 +35,9 @@ #define UBITSHIFT 6 #define GBITSHIFT 3 +#define ACCESS_ALLOWED 0 +#define ACCESS_DENIED 1 + struct cifs_ntsd { __le16 revision; /* revision level */ __le16 type; @@ -48,7 +51,7 @@ struct cifs_sid { __u8 revision; /* revision level */ __u8 num_subauth; __u8 authority[6]; - __le32 sub_auth[5]; /* sub_auth[num_subauth] */ /* BB FIXME endianness BB */ + __le32 sub_auth[5]; /* sub_auth[num_subauth] */ } __attribute__((packed)); struct cifs_acl { @@ -57,18 +60,12 @@ struct cifs_acl { __le32 num_aces; } __attribute__((packed)); -struct cifs_ntace { /* first part of ACE which contains perms */ +struct cifs_ace { __u8 type; __u8 flags; __le16 size; __le32 access_req; -} __attribute__((packed)); - -struct cifs_ace { /* last part of ACE which includes user info */ - __u8 revision; /* revision level */ - __u8 num_subauth; - __u8 authority[6]; - __le32 sub_auth[5]; + struct cifs_sid sid; /* ie UUID of user or group who gets these perms */ } __attribute__((packed)); struct cifs_wksid { @@ -79,7 +76,7 @@ struct cifs_wksid { #ifdef CONFIG_CIFS_EXPERIMENTAL extern int match_sid(struct cifs_sid *); -extern int compare_sids(struct cifs_sid *, struct cifs_sid *); +extern int compare_sids(const struct cifs_sid *, const struct cifs_sid *); #endif /* CONFIG_CIFS_EXPERIMENTAL */ diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c index 632070b4275d..4ff8939c6cc7 100644 --- a/fs/cifs/cifsencrypt.c +++ b/fs/cifs/cifsencrypt.c @@ -99,15 +99,16 @@ static int cifs_calc_signature2(const struct kvec *iov, int n_vec, MD5Init(&context); MD5Update(&context, (char *)&key->data, key->len); for (i = 0; i < n_vec; i++) { + if (iov[i].iov_len == 0) + continue; if (iov[i].iov_base == NULL) { cERROR(1, ("null iovec entry")); return -EIO; - } else if (iov[i].iov_len == 0) - break; /* bail out if we are sent nothing to sign */ + } /* The first entry includes a length field (which does not get signed that occupies the first 4 bytes before the header */ if (i == 0) { - if (iov[0].iov_len <= 8 ) /* cmd field at offset 9 */ + if (iov[0].iov_len <= 8) /* cmd field at offset 9 */ break; /* nothing to sign or corrupt header */ MD5Update(&context, iov[0].iov_base+4, iov[0].iov_len-4); @@ -122,7 +123,7 @@ static int cifs_calc_signature2(const struct kvec *iov, int n_vec, int cifs_sign_smb2(struct kvec *iov, int n_vec, struct TCP_Server_Info *server, - __u32 * pexpected_response_sequence_number) + __u32 *pexpected_response_sequence_number) { int rc = 0; char smb_signature[20]; diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index a6fbea57c4b1..416dc9fe8961 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -43,6 +43,8 @@ #include "cifs_debug.h" #include "cifs_fs_sb.h" #include <linux/mm.h> +#include <linux/key-type.h> +#include "cifs_spnego.h" #define CIFS_MAGIC_NUMBER 0xFF534D42 /* the first four bytes of SMB PDUs */ #ifdef CONFIG_CIFS_QUOTA @@ -1005,12 +1007,16 @@ init_cifs(void) rc = register_filesystem(&cifs_fs_type); if (rc) goto out_destroy_request_bufs; - +#ifdef CONFIG_CIFS_UPCALL + rc = register_key_type(&cifs_spnego_key_type); + if (rc) + goto out_unregister_filesystem; +#endif oplockThread = kthread_run(cifs_oplock_thread, NULL, "cifsoplockd"); if (IS_ERR(oplockThread)) { rc = PTR_ERR(oplockThread); cERROR(1, ("error %d create oplock thread", rc)); - goto out_unregister_filesystem; + goto out_unregister_key_type; } dnotifyThread = kthread_run(cifs_dnotify_thread, NULL, "cifsdnotifyd"); @@ -1024,7 +1030,11 @@ init_cifs(void) out_stop_oplock_thread: kthread_stop(oplockThread); + out_unregister_key_type: +#ifdef CONFIG_CIFS_UPCALL + unregister_key_type(&cifs_spnego_key_type); out_unregister_filesystem: +#endif unregister_filesystem(&cifs_fs_type); out_destroy_request_bufs: cifs_destroy_request_bufs(); @@ -1046,6 +1056,9 @@ exit_cifs(void) #ifdef CONFIG_PROC_FS cifs_proc_clean(); #endif +#ifdef CONFIG_CIFS_UPCALL + unregister_key_type(&cifs_spnego_key_type); +#endif unregister_filesystem(&cifs_fs_type); cifs_destroy_inodecache(); cifs_destroy_mids(); diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h index 5574ba3ab1f9..2a21dc66f0de 100644 --- a/fs/cifs/cifsfs.h +++ b/fs/cifs/cifsfs.h @@ -106,5 +106,5 @@ extern int cifs_ioctl(struct inode *inode, struct file *filep, extern const struct export_operations cifs_export_ops; #endif /* EXPERIMENTAL */ -#define CIFS_VERSION "1.51" +#define CIFS_VERSION "1.52" #endif /* _CIFSFS_H */ diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h index c41ff74e9128..dbe6b846f37f 100644 --- a/fs/cifs/cifspdu.h +++ b/fs/cifs/cifspdu.h @@ -220,6 +220,23 @@ | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES) #define FILE_EXEC_RIGHTS (FILE_EXECUTE) +#define SET_FILE_READ_RIGHTS (FILE_READ_DATA | FILE_READ_EA | FILE_WRITE_EA \ + | FILE_READ_ATTRIBUTES \ + | FILE_WRITE_ATTRIBUTES \ + | DELETE | READ_CONTROL | WRITE_DAC \ + | WRITE_OWNER | SYNCHRONIZE) +#define SET_FILE_WRITE_RIGHTS (FILE_WRITE_DATA | FILE_APPEND_DATA \ + | FILE_READ_EA | FILE_WRITE_EA \ + | FILE_DELETE_CHILD | FILE_READ_ATTRIBUTES \ + | FILE_WRITE_ATTRIBUTES \ + | DELETE | READ_CONTROL | WRITE_DAC \ + | WRITE_OWNER | SYNCHRONIZE) +#define SET_FILE_EXEC_RIGHTS (FILE_READ_EA | FILE_WRITE_EA | FILE_EXECUTE \ + | FILE_READ_ATTRIBUTES \ + | FILE_WRITE_ATTRIBUTES \ + | DELETE | READ_CONTROL | WRITE_DAC \ + | WRITE_OWNER | SYNCHRONIZE) + /* * Invalid readdir handle @@ -1211,6 +1228,29 @@ typedef struct smb_com_transaction_qsec_req { __le32 AclFlags; } __attribute__((packed)) QUERY_SEC_DESC_REQ; + +typedef struct smb_com_transaction_ssec_req { + struct smb_hdr hdr; /* wct = 19 */ + __u8 MaxSetupCount; + __u16 Reserved; + __le32 TotalParameterCount; + __le32 TotalDataCount; + __le32 MaxParameterCount; + __le32 MaxDataCount; + __le32 ParameterCount; + __le32 ParameterOffset; + __le32 DataCount; + __le32 DataOffset; + __u8 SetupCount; /* no setup words follow subcommand */ + /* SNIA spec incorrectly included spurious pad here */ + __le16 SubCommand; /* 3 = SET_SECURITY_DESC */ + __le16 ByteCount; /* bcc = 3 + 8 */ + __u8 Pad[3]; + __u16 Fid; + __u16 Reserved2; + __le32 AclFlags; +} __attribute__((packed)) SET_SEC_DESC_REQ; + typedef struct smb_com_transaction_change_notify_req { struct smb_hdr hdr; /* wct = 23 */ __u8 MaxSetupCount; diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 1a883663b22d..dd1d7c200ee6 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -61,6 +61,9 @@ extern int checkSMB(struct smb_hdr *smb, __u16 mid, unsigned int length); extern int is_valid_oplock_break(struct smb_hdr *smb, struct TCP_Server_Info *); extern int is_size_safe_to_change(struct cifsInodeInfo *, __u64 eof); extern struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *); +#ifdef CONFIG_CIFS_EXPERIMENTAL +extern struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *); +#endif extern unsigned int smbCalcSize(struct smb_hdr *ptr); extern unsigned int smbCalcSize_LE(struct smb_hdr *ptr); extern int decode_negTokenInit(unsigned char *security_blob, int length, @@ -73,6 +76,8 @@ extern void header_assemble(struct smb_hdr *, char /* command */ , extern int small_smb_init_no_tc(const int smb_cmd, const int wct, struct cifsSesInfo *ses, void **request_buf); +extern struct key *cifs_get_spnego_key(struct cifsSesInfo *sesInfo, + const char *hostname); extern int CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, const int stage, const struct nls_table *nls_cp); @@ -92,6 +97,8 @@ extern int cifs_get_inode_info(struct inode **pinode, extern int cifs_get_inode_info_unix(struct inode **pinode, const unsigned char *search_path, struct super_block *sb, int xid); +extern void acl_to_uid_mode(struct inode *inode, const char *search_path); +extern int mode_to_acl(struct inode *inode, const char *path); extern int cifs_mount(struct super_block *, struct cifs_sb_info *, char *, const char *); @@ -311,7 +318,6 @@ extern void setup_ntlmv2_rsp(struct cifsSesInfo *, char *, #ifdef CONFIG_CIFS_WEAK_PW_HASH extern void calc_lanman_hash(struct cifsSesInfo *ses, char *lnm_session_key); #endif /* CIFS_WEAK_PW_HASH */ -extern int parse_sec_desc(struct cifs_ntsd *, int); extern int CIFSSMBCopy(int xid, struct cifsTconInfo *source_tcon, const char *fromName, @@ -336,8 +342,7 @@ extern int CIFSSMBSetEA(const int xid, struct cifsTconInfo *tcon, const void *ea_value, const __u16 ea_value_len, const struct nls_table *nls_codepage, int remap_special_chars); extern int CIFSSMBGetCIFSACL(const int xid, struct cifsTconInfo *tcon, - __u16 fid, char *acl_inf, const int buflen, - const int acl_type /* ACCESS vs. DEFAULT */); + __u16 fid, struct cifs_ntsd **acl_inf, __u32 *buflen); extern int CIFSSMBGetPosixACL(const int xid, struct cifsTconInfo *tcon, const unsigned char *searchName, char *acl_inf, const int buflen, const int acl_type, diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index f0d9a485d095..59d7b7c037ad 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -647,8 +647,7 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses) count - 16, &server->secType); if (rc == 1) { - /* BB Need to fill struct for sessetup here */ - rc = -EOPNOTSUPP; + rc = 0; } else { rc = -EINVAL; } @@ -2486,6 +2485,7 @@ querySymLinkRetry: return rc; } +#ifdef CONFIG_CIFS_EXPERIMENTAL /* Initialize NT TRANSACT SMB into small smb request buffer. This assumes that all NT TRANSACTS that we init here have total parm and data under about 400 bytes (to fit in small cifs @@ -2494,7 +2494,7 @@ querySymLinkRetry: MaxSetupCount (size of returned setup area) and MaxParameterCount (returned parms size) must be set by caller */ static int -smb_init_ntransact(const __u16 sub_command, const int setup_count, +smb_init_nttransact(const __u16 sub_command, const int setup_count, const int parm_len, struct cifsTconInfo *tcon, void **ret_buf) { @@ -2525,12 +2525,15 @@ smb_init_ntransact(const __u16 sub_command, const int setup_count, static int validate_ntransact(char *buf, char **ppparm, char **ppdata, - int *pdatalen, int *pparmlen) + __u32 *pparmlen, __u32 *pdatalen) { char *end_of_smb; __u32 data_count, data_offset, parm_count, parm_offset; struct smb_com_ntransact_rsp *pSMBr; + *pdatalen = 0; + *pparmlen = 0; + if (buf == NULL) return -EINVAL; @@ -2567,8 +2570,11 @@ validate_ntransact(char *buf, char **ppparm, char **ppdata, cFYI(1, ("parm count and data count larger than SMB")); return -EINVAL; } + *pdatalen = data_count; + *pparmlen = parm_count; return 0; } +#endif /* CIFS_EXPERIMENTAL */ int CIFSSMBQueryReparseLinkInfo(const int xid, struct cifsTconInfo *tcon, @@ -3067,8 +3073,7 @@ GetExtAttrOut: /* Get Security Descriptor (by handle) from remote server for a file or dir */ int CIFSSMBGetCIFSACL(const int xid, struct cifsTconInfo *tcon, __u16 fid, - /* BB fix up return info */ char *acl_inf, const int buflen, - const int acl_type) + struct cifs_ntsd **acl_inf, __u32 *pbuflen) { int rc = 0; int buf_type = 0; @@ -3077,7 +3082,10 @@ CIFSSMBGetCIFSACL(const int xid, struct cifsTconInfo *tcon, __u16 fid, cFYI(1, ("GetCifsACL")); - rc = smb_init_ntransact(NT_TRANSACT_QUERY_SECURITY_DESC, 0, + *pbuflen = 0; + *acl_inf = NULL; + + rc = smb_init_nttransact(NT_TRANSACT_QUERY_SECURITY_DESC, 0, 8 /* parm len */, tcon, (void **) &pSMB); if (rc) return rc; @@ -3099,34 +3107,52 @@ CIFSSMBGetCIFSACL(const int xid, struct cifsTconInfo *tcon, __u16 fid, if (rc) { cFYI(1, ("Send error in QuerySecDesc = %d", rc)); } else { /* decode response */ - struct cifs_ntsd *psec_desc; __le32 * parm; - int parm_len; - int data_len; - int acl_len; + __u32 parm_len; + __u32 acl_len; struct smb_com_ntransact_rsp *pSMBr; + char *pdata; /* validate_nttransact */ rc = validate_ntransact(iov[0].iov_base, (char **)&parm, - (char **)&psec_desc, - &parm_len, &data_len); + &pdata, &parm_len, pbuflen); if (rc) goto qsec_out; pSMBr = (struct smb_com_ntransact_rsp *)iov[0].iov_base; - cFYI(1, ("smb %p parm %p data %p", pSMBr, parm, psec_desc)); + cFYI(1, ("smb %p parm %p data %p", pSMBr, parm, *acl_inf)); if (le32_to_cpu(pSMBr->ParameterCount) != 4) { rc = -EIO; /* bad smb */ + *pbuflen = 0; goto qsec_out; } /* BB check that data area is minimum length and as big as acl_len */ acl_len = le32_to_cpu(*parm); - /* BB check if (acl_len > bufsize) */ + if (acl_len != *pbuflen) { + cERROR(1, ("acl length %d does not match %d", + acl_len, *pbuflen)); + if (*pbuflen > acl_len) + *pbuflen = acl_len; + } - parse_sec_desc(psec_desc, acl_len); + /* check if buffer is big enough for the acl + header followed by the smallest SID */ + if ((*pbuflen < sizeof(struct cifs_ntsd) + 8) || + (*pbuflen >= 64 * 1024)) { + cERROR(1, ("bad acl length %d", *pbuflen)); + rc = -EINVAL; + *pbuflen = 0; + } else { + *acl_inf = kmalloc(*pbuflen, GFP_KERNEL); + if (*acl_inf == NULL) { + *pbuflen = 0; + rc = -ENOMEM; + } + memcpy(*acl_inf, pdata, *pbuflen); + } } qsec_out: if (buf_type == CIFS_SMALL_BUFFER) @@ -3381,7 +3407,7 @@ UnixQPathInfoRetry: memcpy((char *) pFindData, (char *) &pSMBr->hdr.Protocol + data_offset, - sizeof (FILE_UNIX_BASIC_INFO)); + sizeof(FILE_UNIX_BASIC_INFO)); } } cifs_buf_release(pSMB); @@ -3649,7 +3675,7 @@ int CIFSFindNext(const int xid, struct cifsTconInfo *tcon, pSMB->SubCommand = cpu_to_le16(TRANS2_FIND_NEXT); pSMB->SearchHandle = searchHandle; /* always kept as le */ pSMB->SearchCount = - cpu_to_le16(CIFSMaxBufSize / sizeof (FILE_UNIX_INFO)); + cpu_to_le16(CIFSMaxBufSize / sizeof(FILE_UNIX_INFO)); pSMB->InformationLevel = cpu_to_le16(psrch_inf->info_level); pSMB->ResumeKey = psrch_inf->resume_key; pSMB->SearchFlags = @@ -4331,7 +4357,7 @@ QFSDeviceRetry: } else { /* decode response */ rc = validate_t2((struct smb_t2_rsp *)pSMBr); - if (rc || (pSMBr->ByteCount < sizeof (FILE_SYSTEM_DEVICE_INFO))) + if (rc || (pSMBr->ByteCount < sizeof(FILE_SYSTEM_DEVICE_INFO))) rc = -EIO; /* bad smb */ else { __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 19ee11f7f35a..c52a76ff4bb9 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -160,7 +160,7 @@ cifs_reconnect(struct TCP_Server_Info *server) 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)); @@ -793,7 +793,7 @@ cifs_parse_mount_options(char *options, const char *devname, 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; @@ -1790,7 +1790,7 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, 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)); diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c index 793404b10925..37dc97af1487 100644 --- a/fs/cifs/dir.c +++ b/fs/cifs/dir.c @@ -593,7 +593,7 @@ static int cifs_ci_compare(struct dentry *dentry, struct qstr *a, * case take precedence. If a is not a negative dentry, this * should have no side effects */ - memcpy((unsigned char *)a->name, b->name, a->len); + memcpy(a->name, b->name, a->len); return 0; } return 1; diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 1e7e4c06d9e3..68ad4ca0cfa3 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -1026,6 +1026,37 @@ static ssize_t cifs_write(struct file *file, const char *write_data, return total_written; } +#ifdef CONFIG_CIFS_EXPERIMENTAL +struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode) +{ + struct cifsFileInfo *open_file = NULL; + + read_lock(&GlobalSMBSeslock); + /* we could simply get the first_list_entry since write-only entries + are always at the end of the list but since the first entry might + have a close pending, we go through the whole list */ + list_for_each_entry(open_file, &cifs_inode->openFileList, flist) { + if (open_file->closePend) + continue; + if (open_file->pfile && ((open_file->pfile->f_flags & O_RDWR) || + (open_file->pfile->f_flags & O_RDONLY))) { + if (!open_file->invalidHandle) { + /* found a good file */ + /* lock it so it will not be closed on us */ + atomic_inc(&open_file->wrtPending); + read_unlock(&GlobalSMBSeslock); + return open_file; + } /* else might as well continue, and look for + another, or simply have the caller reopen it + again rather than trying to fix this handle */ + } else /* write only file */ + break; /* write only files are last so must be done */ + } + read_unlock(&GlobalSMBSeslock); + return NULL; +} +#endif + struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode) { struct cifsFileInfo *open_file; diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index 5e8b388be3b6..7d907e84e032 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -289,7 +289,7 @@ static int decode_sfu_inode(struct inode *inode, __u64 size, #define SFBITS_MASK (S_ISVTX | S_ISGID | S_ISUID) /* SETFILEBITS valid bits */ -static int get_sfu_uid_mode(struct inode *inode, +static int get_sfu_mode(struct inode *inode, const unsigned char *path, struct cifs_sb_info *cifs_sb, int xid) { @@ -527,11 +527,16 @@ int cifs_get_inode_info(struct inode **pinode, /* BB fill in uid and gid here? with help from winbind? or retrieve from NTFS stream extended attribute */ +#ifdef CONFIG_CIFS_EXPERIMENTAL + /* fill in 0777 bits from ACL */ + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) { + cFYI(1, ("Getting mode bits from ACL")); + acl_to_uid_mode(inode, search_path); + } +#endif if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) { - /* fill in uid, gid, mode from server ACL */ - /* BB FIXME this should also take into account the - * default uid specified on mount if present */ - get_sfu_uid_mode(inode, search_path, cifs_sb, xid); + /* fill in remaining high mode bits e.g. SUID, VTX */ + get_sfu_mode(inode, search_path, cifs_sb, xid); } else if (atomic_read(&cifsInfo->inUse) == 0) { inode->i_uid = cifs_sb->mnt_uid; inode->i_gid = cifs_sb->mnt_gid; diff --git a/fs/cifs/md5.c b/fs/cifs/md5.c index e5c3e1212697..f13f96d42fcf 100644 --- a/fs/cifs/md5.c +++ b/fs/cifs/md5.c @@ -276,8 +276,8 @@ hmac_md5_init_rfc2104(unsigned char *key, int key_len, } /* start out by storing key in pads */ - memset(ctx->k_ipad, 0, sizeof (ctx->k_ipad)); - memset(ctx->k_opad, 0, sizeof (ctx->k_opad)); + memset(ctx->k_ipad, 0, sizeof(ctx->k_ipad)); + memset(ctx->k_opad, 0, sizeof(ctx->k_opad)); memcpy(ctx->k_ipad, key, key_len); memcpy(ctx->k_opad, key, key_len); @@ -307,8 +307,8 @@ hmac_md5_init_limK_to_64(const unsigned char *key, int key_len, } /* start out by storing key in pads */ - memset(ctx->k_ipad, 0, sizeof (ctx->k_ipad)); - memset(ctx->k_opad, 0, sizeof (ctx->k_opad)); + memset(ctx->k_ipad, 0, sizeof(ctx->k_ipad)); + memset(ctx->k_opad, 0, sizeof(ctx->k_opad)); memcpy(ctx->k_ipad, key, key_len); memcpy(ctx->k_opad, key, key_len); diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index 51ec681fe74a..15546c2354c5 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c @@ -73,7 +73,7 @@ sesInfoAlloc(void) { struct cifsSesInfo *ret_buf; - ret_buf = kzalloc(sizeof (struct cifsSesInfo), GFP_KERNEL); + ret_buf = kzalloc(sizeof(struct cifsSesInfo), GFP_KERNEL); if (ret_buf) { write_lock(&GlobalSMBSeslock); atomic_inc(&sesInfoAllocCount); @@ -109,7 +109,7 @@ struct cifsTconInfo * tconInfoAlloc(void) { struct cifsTconInfo *ret_buf; - ret_buf = kzalloc(sizeof (struct cifsTconInfo), GFP_KERNEL); + ret_buf = kzalloc(sizeof(struct cifsTconInfo), GFP_KERNEL); if (ret_buf) { write_lock(&GlobalSMBSeslock); atomic_inc(&tconInfoAllocCount); @@ -298,7 +298,7 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ , memset(temp, 0, 256); /* bigger than MAX_CIFS_HDR_SIZE */ buffer->smb_buf_length = - (2 * word_count) + sizeof (struct smb_hdr) - + (2 * word_count) + sizeof(struct smb_hdr) - 4 /* RFC 1001 length field does not count */ + 2 /* for bcc field itself */ ; /* Note that this is the only network field that has to be converted @@ -422,8 +422,8 @@ checkSMB(struct smb_hdr *smb, __u16 mid, unsigned int length) __u32 clc_len; /* calculated length */ cFYI(0, ("checkSMB Length: 0x%x, smb_buf_length: 0x%x", length, len)); - if (length < 2 + sizeof (struct smb_hdr)) { - if ((length >= sizeof (struct smb_hdr) - 1) + if (length < 2 + sizeof(struct smb_hdr)) { + if ((length >= sizeof(struct smb_hdr) - 1) && (smb->Status.CifsError != 0)) { smb->WordCount = 0; /* some error cases do not return wct and bcc */ diff --git a/fs/cifs/netmisc.c b/fs/cifs/netmisc.c index f06359cb22ee..646e1f06941b 100644 --- a/fs/cifs/netmisc.c +++ b/fs/cifs/netmisc.c @@ -132,6 +132,34 @@ static const struct smb_to_posix_error mapping_table_ERRHRD[] = { {0, 0} }; + +/* if the mount helper is missing we need to reverse the 1st slash + from '/' to backslash in order to format the UNC properly for + ip address parsing and for tree connect (unless the user + remembered to put the UNC name in properly). Fortunately we do + not have to call this twice (we check for IPv4 addresses + first, so it is already converted by the time we + try IPv6 addresses */ +static int canonicalize_unc(char *cp) +{ + int i; + + for (i = 0; i <= 46 /* INET6_ADDRSTRLEN */ ; i++) { + if (cp[i] == 0) + break; + if (cp[i] == '\\') + break; + if (cp[i] == '/') { +#ifdef CONFIG_CIFS_DEBUG2 + cFYI(1, ("change slash to backslash in malformed UNC")); +#endif + cp[i] = '\\'; + return 1; + } + } + return 0; +} + /* Convert string containing dotted ip address to binary form */ /* returns 0 if invalid address */ @@ -141,11 +169,13 @@ cifs_inet_pton(int address_family, char *cp, void *dst) int ret = 0; /* calculate length by finding first slash or NULL */ - /* BB Should we convert '/' slash to '\' here since it seems already - * done before this */ - if ( address_family == AF_INET ) { - ret = in4_pton(cp, -1 /* len */, dst , '\\', NULL); - } else if ( address_family == AF_INET6 ) { + if (address_family == AF_INET) { + ret = in4_pton(cp, -1 /* len */, dst, '\\', NULL); + if (ret == 0) { + if (canonicalize_unc(cp)) + ret = in4_pton(cp, -1, dst, '\\', NULL); + } + } else if (address_family == AF_INET6) { ret = in6_pton(cp, -1 /* len */, dst , '\\', NULL); } #ifdef CONFIG_CIFS_DEBUG2 @@ -740,7 +770,7 @@ cifs_print_status(__u32 status_code) static void -ntstatus_to_dos(__u32 ntstatus, __u8 * eclass, __u16 * ecode) +ntstatus_to_dos(__u32 ntstatus, __u8 *eclass, __u16 *ecode) { int i; if (ntstatus == 0) { @@ -793,8 +823,8 @@ map_smb_to_linux_error(struct smb_hdr *smb, int logErr) if (smberrclass == ERRDOS) { /* 1 byte field no need to byte reverse */ for (i = 0; i < - sizeof (mapping_table_ERRDOS) / - sizeof (struct smb_to_posix_error); i++) { + sizeof(mapping_table_ERRDOS) / + sizeof(struct smb_to_posix_error); i++) { if (mapping_table_ERRDOS[i].smb_err == 0) break; else if (mapping_table_ERRDOS[i].smb_err == @@ -807,8 +837,8 @@ map_smb_to_linux_error(struct smb_hdr *smb, int logErr) } else if (smberrclass == ERRSRV) { /* server class of error codes */ for (i = 0; i < - sizeof (mapping_table_ERRSRV) / - sizeof (struct smb_to_posix_error); i++) { + sizeof(mapping_table_ERRSRV) / + sizeof(struct smb_to_posix_error); i++) { if (mapping_table_ERRSRV[i].smb_err == 0) break; else if (mapping_table_ERRSRV[i].smb_err == @@ -837,14 +867,14 @@ map_smb_to_linux_error(struct smb_hdr *smb, int logErr) unsigned int smbCalcSize(struct smb_hdr *ptr) { - return (sizeof (struct smb_hdr) + (2 * ptr->WordCount) + + return (sizeof(struct smb_hdr) + (2 * ptr->WordCount) + 2 /* size of the bcc field */ + BCC(ptr)); } unsigned int smbCalcSize_LE(struct smb_hdr *ptr) { - return (sizeof (struct smb_hdr) + (2 * ptr->WordCount) + + return (sizeof(struct smb_hdr) + (2 * ptr->WordCount) + 2 /* size of the bcc field */ + le16_to_cpu(BCC_LE(ptr))); } diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c index 3746580e9701..0f22def4bdff 100644 --- a/fs/cifs/readdir.c +++ b/fs/cifs/readdir.c @@ -171,7 +171,13 @@ static void fill_in_inode(struct inode *tmp_inode, int new_buf_type, /* Linux can not store file creation time unfortunately so ignore it */ cifsInfo->cifsAttrs = attr; - cifsInfo->time = jiffies; +#ifdef CONFIG_CIFS_EXPERIMENTAL + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) { + /* get more accurate mode via ACL - so force inode refresh */ + cifsInfo->time = 0; + } else +#endif /* CONFIG_CIFS_EXPERIMENTAL */ + cifsInfo->time = jiffies; /* treat dos attribute of read-only as read-only mode bit e.g. 555? */ /* 2767 perms - indicate mandatory locking */ @@ -495,7 +501,7 @@ ffirst_retry: static int cifs_unicode_bytelen(char *str) { int len; - __le16 * ustr = (__le16 *)str; + __le16 *ustr = (__le16 *)str; for (len = 0; len <= PATH_MAX; len++) { if (ustr[len] == 0) diff --git a/fs/cifs/smbencrypt.c b/fs/cifs/smbencrypt.c index 90542a39be17..58bbfd992cc0 100644 --- a/fs/cifs/smbencrypt.c +++ b/fs/cifs/smbencrypt.c @@ -80,7 +80,7 @@ SMBencrypt(unsigned char *passwd, unsigned char *c8, unsigned char *p24) /* Routines for Windows NT MD4 Hash functions. */ static int -_my_wcslen(__u16 * str) +_my_wcslen(__u16 *str) { int len = 0; while (*str++ != 0) @@ -96,7 +96,7 @@ _my_wcslen(__u16 * str) */ static int -_my_mbstowcs(__u16 * dst, const unsigned char *src, int len) +_my_mbstowcs(__u16 *dst, const unsigned char *src, int len) { /* BB not a very good conversion routine - change/fix */ int i; __u16 val; @@ -125,9 +125,9 @@ E_md4hash(const unsigned char *passwd, unsigned char *p16) /* Password cannot be longer than 128 characters */ if (passwd) { len = strlen((char *) passwd); - if (len > 128) { + if (len > 128) len = 128; - } + /* Password must be converted to NT unicode */ _my_mbstowcs(wpwd, passwd, len); } else @@ -135,7 +135,7 @@ E_md4hash(const unsigned char *passwd, unsigned char *p16) wpwd[len] = 0; /* Ensure string is null terminated */ /* Calculate length in bytes */ - len = _my_wcslen(wpwd) * sizeof (__u16); + len = _my_wcslen(wpwd) * sizeof(__u16); mdfour(p16, (unsigned char *) wpwd, len); memset(wpwd, 0, 129 * 2); @@ -167,7 +167,7 @@ nt_lm_owf_gen(char *pwd, unsigned char nt_p16[16], unsigned char p16[16]) E_P16((unsigned char *) passwd, (unsigned char *) p16); /* clear out local copy of user's password (just being paranoid). */ - memset(passwd, '\0', sizeof (passwd)); + memset(passwd, '\0', sizeof(passwd)); } #endif @@ -189,8 +189,10 @@ ntv2_owf_gen(const unsigned char owf[16], const char *user_n, return; dom_u = user_u + 1024; - /* push_ucs2(NULL, user_u, user_n, (user_l+1)*2, STR_UNICODE|STR_NOALIGN|STR_TERMINATE|STR_UPPER); - push_ucs2(NULL, dom_u, domain_n, (domain_l+1)*2, STR_UNICODE|STR_NOALIGN|STR_TERMINATE|STR_UPPER); */ + /* push_ucs2(NULL, user_u, user_n, (user_l+1)*2, + STR_UNICODE|STR_NOALIGN|STR_TERMINATE|STR_UPPER); + push_ucs2(NULL, dom_u, domain_n, (domain_l+1)*2, + STR_UNICODE|STR_NOALIGN|STR_TERMINATE|STR_UPPER); */ /* BB user and domain may need to be uppercased */ user_l = cifs_strtoUCS(user_u, user_n, 511, nls_codepage); diff --git a/fs/cifs/xattr.c b/fs/cifs/xattr.c index 369e838bebd3..54e8ef96cb79 100644 --- a/fs/cifs/xattr.c +++ b/fs/cifs/xattr.c @@ -265,7 +265,9 @@ ssize_t cifs_getxattr(struct dentry *direntry, const char *ea_name, else if(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) { __u16 fid; int oplock = FALSE; - if (experimEnabled) + struct cifs_ntsd *pacl = NULL; + __u32 buflen = 0; + if (experimEnabled) rc = CIFSSMBOpen(xid, pTcon, full_path, FILE_OPEN, GENERIC_READ, 0, &fid, &oplock, NULL, cifs_sb->local_nls, @@ -273,10 +275,9 @@ ssize_t cifs_getxattr(struct dentry *direntry, const char *ea_name, CIFS_MOUNT_MAP_SPECIAL_CHR); /* else rc is EOPNOTSUPP from above */ - if(rc == 0) { - rc = CIFSSMBGetCIFSACL(xid, pTcon, fid, - ea_value, buf_size, - ACL_TYPE_ACCESS); + if (rc == 0) { + rc = CIFSSMBGetCIFSACL(xid, pTcon, fid, &pacl, + &buflen); CIFSSMBClose(xid, pTcon, fid); } } diff --git a/fs/dlm/lowcomms.c b/fs/dlm/lowcomms.c index 58bf3f5cdbe2..e9923ca9c2d9 100644 --- a/fs/dlm/lowcomms.c +++ b/fs/dlm/lowcomms.c @@ -1062,7 +1062,7 @@ static int sctp_listen_for_all(void) subscribe.sctp_shutdown_event = 1; subscribe.sctp_partial_delivery_event = 1; - result = kernel_setsockopt(sock, SOL_SOCKET, SO_RCVBUF, + result = kernel_setsockopt(sock, SOL_SOCKET, SO_RCVBUFFORCE, (char *)&bufsize, sizeof(bufsize)); if (result) log_print("Error increasing buffer space on socket %d", result); @@ -1454,10 +1454,6 @@ int dlm_lowcomms_start(void) if (!con_cache) goto out; - /* Set some sysctl minima */ - if (sysctl_rmem_max < NEEDED_RMEM) - sysctl_rmem_max = NEEDED_RMEM; - /* Start listening */ if (dlm_config.ci_protocol == 0) error = tcp_listen_for_all(); diff --git a/fs/exec.c b/fs/exec.c index 2c942e2d14ea..4ccaaa4b13b2 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -1692,7 +1692,10 @@ int do_coredump(long signr, int exit_code, struct pt_regs * regs) if (!binfmt || !binfmt->core_dump) goto fail; down_write(&mm->mmap_sem); - if (!get_dumpable(mm)) { + /* + * If another thread got here first, or we are not dumpable, bail out. + */ + if (mm->core_waiters || !get_dumpable(mm)) { up_write(&mm->mmap_sem); goto fail; } @@ -1706,7 +1709,6 @@ int do_coredump(long signr, int exit_code, struct pt_regs * regs) flag = O_EXCL; /* Stop rewrite attacks */ current->fsuid = 0; /* Dump root private */ } - set_dumpable(mm, 0); retval = coredump_wait(exit_code); if (retval < 0) diff --git a/fs/ext2/balloc.c b/fs/ext2/balloc.c index 18a42de25b55..377ad172d74b 100644 --- a/fs/ext2/balloc.c +++ b/fs/ext2/balloc.c @@ -69,14 +69,6 @@ struct ext2_group_desc * ext2_get_group_desc(struct super_block * sb, return desc + offset; } -static inline int -block_in_use(unsigned long block, struct super_block *sb, unsigned char *map) -{ - return ext2_test_bit ((block - - le32_to_cpu(EXT2_SB(sb)->s_es->s_first_data_block)) % - EXT2_BLOCKS_PER_GROUP(sb), map); -} - /* * Read the bitmap for a given block_group, reading into the specified * slot in the superblock's bitmap cache. @@ -86,51 +78,20 @@ block_in_use(unsigned long block, struct super_block *sb, unsigned char *map) static struct buffer_head * read_block_bitmap(struct super_block *sb, unsigned int block_group) { - int i; struct ext2_group_desc * desc; struct buffer_head * bh = NULL; - unsigned int bitmap_blk; - + desc = ext2_get_group_desc (sb, block_group, NULL); if (!desc) - return NULL; - bitmap_blk = le32_to_cpu(desc->bg_block_bitmap); - bh = sb_bread(sb, bitmap_blk); + goto error_out; + bh = sb_bread(sb, le32_to_cpu(desc->bg_block_bitmap)); if (!bh) - ext2_error (sb, __FUNCTION__, + ext2_error (sb, "read_block_bitmap", "Cannot read block bitmap - " "block_group = %d, block_bitmap = %u", block_group, le32_to_cpu(desc->bg_block_bitmap)); - - /* check whether block bitmap block number is set */ - if (!block_in_use(bitmap_blk, sb, bh->b_data)) { - /* bad block bitmap */ - goto error_out; - } - /* check whether the inode bitmap block number is set */ - bitmap_blk = le32_to_cpu(desc->bg_inode_bitmap); - if (!block_in_use(bitmap_blk, sb, bh->b_data)) { - /* bad block bitmap */ - goto error_out; - } - /* check whether the inode table block number is set */ - bitmap_blk = le32_to_cpu(desc->bg_inode_table); - for (i = 0; i < EXT2_SB(sb)->s_itb_per_group; i++, bitmap_blk++) { - if (!block_in_use(bitmap_blk, sb, bh->b_data)) { - /* bad block bitmap */ - goto error_out; - } - } - - return bh; - error_out: - brelse(bh); - ext2_error(sb, __FUNCTION__, - "Invalid block bitmap - " - "block_group = %d, block = %u", - block_group, bitmap_blk); - return NULL; + return bh; } static void release_blocks(struct super_block *sb, int count) @@ -1461,7 +1422,6 @@ unsigned long ext2_count_free_blocks (struct super_block * sb) #endif } - static inline int test_root(int a, int b) { int num = b; diff --git a/fs/ext3/balloc.c b/fs/ext3/balloc.c index 7a87d15523be..a8ba7e831278 100644 --- a/fs/ext3/balloc.c +++ b/fs/ext3/balloc.c @@ -80,14 +80,6 @@ struct ext3_group_desc * ext3_get_group_desc(struct super_block * sb, return desc + offset; } -static inline int -block_in_use(ext3_fsblk_t block, struct super_block *sb, unsigned char *map) -{ - return ext3_test_bit ((block - - le32_to_cpu(EXT3_SB(sb)->s_es->s_first_data_block)) % - EXT3_BLOCKS_PER_GROUP(sb), map); -} - /** * read_block_bitmap() * @sb: super block @@ -101,51 +93,20 @@ block_in_use(ext3_fsblk_t block, struct super_block *sb, unsigned char *map) static struct buffer_head * read_block_bitmap(struct super_block *sb, unsigned int block_group) { - int i; struct ext3_group_desc * desc; struct buffer_head * bh = NULL; - ext3_fsblk_t bitmap_blk; desc = ext3_get_group_desc (sb, block_group, NULL); if (!desc) - return NULL; - bitmap_blk = le32_to_cpu(desc->bg_block_bitmap); - bh = sb_bread(sb, bitmap_blk); + goto error_out; + bh = sb_bread(sb, le32_to_cpu(desc->bg_block_bitmap)); if (!bh) - ext3_error (sb, __FUNCTION__, + ext3_error (sb, "read_block_bitmap", "Cannot read block bitmap - " "block_group = %d, block_bitmap = %u", block_group, le32_to_cpu(desc->bg_block_bitmap)); - - /* check whether block bitmap block number is set */ - if (!block_in_use(bitmap_blk, sb, bh->b_data)) { - /* bad block bitmap */ - goto error_out; - } - /* check whether the inode bitmap block number is set */ - bitmap_blk = le32_to_cpu(desc->bg_inode_bitmap); - if (!block_in_use(bitmap_blk, sb, bh->b_data)) { - /* bad block bitmap */ - goto error_out; - } - /* check whether the inode table block number is set */ - bitmap_blk = le32_to_cpu(desc->bg_inode_table); - for (i = 0; i < EXT3_SB(sb)->s_itb_per_group; i++, bitmap_blk++) { - if (!block_in_use(bitmap_blk, sb, bh->b_data)) { - /* bad block bitmap */ - goto error_out; - } - } - - return bh; - error_out: - brelse(bh); - ext3_error(sb, __FUNCTION__, - "Invalid block bitmap - " - "block_group = %d, block = %lu", - block_group, bitmap_blk); - return NULL; + return bh; } /* * The reservation window structure operations @@ -1772,7 +1733,6 @@ ext3_fsblk_t ext3_count_free_blocks(struct super_block *sb) #endif } - static inline int test_root(int a, int b) { int num = b; diff --git a/fs/ext4/balloc.c b/fs/ext4/balloc.c index e906b65448e2..71ee95e534fd 100644 --- a/fs/ext4/balloc.c +++ b/fs/ext4/balloc.c @@ -189,15 +189,6 @@ struct ext4_group_desc * ext4_get_group_desc(struct super_block * sb, return desc; } -static inline int -block_in_use(ext4_fsblk_t block, struct super_block *sb, unsigned char *map) -{ - ext4_grpblk_t offset; - - ext4_get_group_no_and_offset(sb, block, NULL, &offset); - return ext4_test_bit (offset, map); -} - /** * read_block_bitmap() * @sb: super block @@ -211,7 +202,6 @@ block_in_use(ext4_fsblk_t block, struct super_block *sb, unsigned char *map) struct buffer_head * read_block_bitmap(struct super_block *sb, unsigned int block_group) { - int i; struct ext4_group_desc * desc; struct buffer_head * bh = NULL; ext4_fsblk_t bitmap_blk; @@ -239,38 +229,7 @@ read_block_bitmap(struct super_block *sb, unsigned int block_group) "Cannot read block bitmap - " "block_group = %d, block_bitmap = %llu", block_group, bitmap_blk); - - /* check whether block bitmap block number is set */ - if (!block_in_use(bitmap_blk, sb, bh->b_data)) { - /* bad block bitmap */ - goto error_out; - } - - /* check whether the inode bitmap block number is set */ - bitmap_blk = ext4_inode_bitmap(sb, desc); - if (!block_in_use(bitmap_blk, sb, bh->b_data)) { - /* bad block bitmap */ - goto error_out; - } - /* check whether the inode table block number is set */ - bitmap_blk = ext4_inode_table(sb, desc); - for (i = 0; i < EXT4_SB(sb)->s_itb_per_group; i++, bitmap_blk++) { - if (!block_in_use(bitmap_blk, sb, bh->b_data)) { - /* bad block bitmap */ - goto error_out; - } - } - return bh; - -error_out: - brelse(bh); - ext4_error(sb, __FUNCTION__, - "Invalid block bitmap - " - "block_group = %d, block = %llu", - block_group, bitmap_blk); - return NULL; - } /* * The reservation window structure operations diff --git a/fs/ioprio.c b/fs/ioprio.c index d6ff77e8e7ec..e4e01bc7f338 100644 --- a/fs/ioprio.c +++ b/fs/ioprio.c @@ -78,6 +78,10 @@ asmlinkage long sys_ioprio_set(int which, int who, int ioprio) if (!capable(CAP_SYS_ADMIN)) return -EPERM; break; + case IOPRIO_CLASS_NONE: + if (data) + return -EINVAL; + break; default: return -EINVAL; } diff --git a/fs/nfsd/nfs2acl.c b/fs/nfsd/nfs2acl.c index b61742885011..0e5fa11e6b44 100644 --- a/fs/nfsd/nfs2acl.c +++ b/fs/nfsd/nfs2acl.c @@ -41,7 +41,7 @@ static __be32 nfsacld_proc_getacl(struct svc_rqst * rqstp, fh = fh_copy(&resp->fh, &argp->fh); if ((nfserr = fh_verify(rqstp, &resp->fh, 0, MAY_NOP))) - RETURN_STATUS(nfserr_inval); + RETURN_STATUS(nfserr); if (argp->mask & ~(NFS_ACL|NFS_ACLCNT|NFS_DFACL|NFS_DFACLCNT)) RETURN_STATUS(nfserr_inval); diff --git a/fs/nfsd/nfs3acl.c b/fs/nfsd/nfs3acl.c index 3e3f2de82c36..b647f2f872dc 100644 --- a/fs/nfsd/nfs3acl.c +++ b/fs/nfsd/nfs3acl.c @@ -37,7 +37,7 @@ static __be32 nfsd3_proc_getacl(struct svc_rqst * rqstp, fh = fh_copy(&resp->fh, &argp->fh); if ((nfserr = fh_verify(rqstp, &resp->fh, 0, MAY_NOP))) - RETURN_STATUS(nfserr_inval); + RETURN_STATUS(nfserr); if (argp->mask & ~(NFS_ACL|NFS_ACLCNT|NFS_DFACL|NFS_DFACLCNT)) RETURN_STATUS(nfserr_inval); diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c index 4f712e970584..468f17a78441 100644 --- a/fs/nfsd/nfsfh.c +++ b/fs/nfsd/nfsfh.c @@ -95,6 +95,22 @@ nfsd_mode_check(struct svc_rqst *rqstp, umode_t mode, int type) return 0; } +static __be32 nfsd_setuser_and_check_port(struct svc_rqst *rqstp, + struct svc_export *exp) +{ + /* Check if the request originated from a secure port. */ + if (!rqstp->rq_secure && EX_SECURE(exp)) { + char buf[RPC_MAX_ADDRBUFLEN]; + dprintk(KERN_WARNING + "nfsd: request from insecure port %s!\n", + svc_print_addr(rqstp, buf, sizeof(buf))); + return nfserr_perm; + } + + /* Set user creds for this exportpoint */ + return nfserrno(nfsd_setuser(rqstp, exp)); +} + /* * Perform sanity checks on the dentry in a client's file handle. * @@ -167,18 +183,7 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access) goto out; } - /* Check if the request originated from a secure port. */ - error = nfserr_perm; - if (!rqstp->rq_secure && EX_SECURE(exp)) { - char buf[RPC_MAX_ADDRBUFLEN]; - printk(KERN_WARNING - "nfsd: request from insecure port %s!\n", - svc_print_addr(rqstp, buf, sizeof(buf))); - goto out; - } - - /* Set user creds for this exportpoint */ - error = nfserrno(nfsd_setuser(rqstp, exp)); + error = nfsd_setuser_and_check_port(rqstp, exp); if (error) goto out; @@ -227,18 +232,22 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, int access) fhp->fh_export = exp; nfsd_nr_verified++; } else { - /* just rechecking permissions - * (e.g. nfsproc_create calls fh_verify, then nfsd_create does as well) + /* + * just rechecking permissions + * (e.g. nfsproc_create calls fh_verify, then nfsd_create + * does as well) */ dprintk("nfsd: fh_verify - just checking\n"); dentry = fhp->fh_dentry; exp = fhp->fh_export; - /* Set user creds for this exportpoint; necessary even + /* + * Set user creds for this exportpoint; necessary even * in the "just checking" case because this may be a * filehandle that was created by fh_compose, and that * is about to be used in another nfsv4 compound - * operation */ - error = nfserrno(nfsd_setuser(rqstp, exp)); + * operation. + */ + error = nfsd_setuser_and_check_port(rqstp, exp); if (error) goto out; } diff --git a/fs/ocfs2/alloc.c b/fs/ocfs2/alloc.c index 4ba7f0bdc248..ce62c152823d 100644 --- a/fs/ocfs2/alloc.c +++ b/fs/ocfs2/alloc.c @@ -3946,7 +3946,7 @@ static int __ocfs2_mark_extent_written(struct inode *inode, struct ocfs2_merge_ctxt ctxt; struct ocfs2_extent_list *rightmost_el; - if (!rec->e_flags & OCFS2_EXT_UNWRITTEN) { + if (!(rec->e_flags & OCFS2_EXT_UNWRITTEN)) { ret = -EIO; mlog_errno(ret); goto out; diff --git a/fs/ocfs2/aops.c b/fs/ocfs2/aops.c index c69c1b300155..556e34ccb005 100644 --- a/fs/ocfs2/aops.c +++ b/fs/ocfs2/aops.c @@ -729,6 +729,27 @@ static void ocfs2_clear_page_regions(struct page *page, } /* + * Nonsparse file systems fully allocate before we get to the write + * code. This prevents ocfs2_write() from tagging the write as an + * allocating one, which means ocfs2_map_page_blocks() might try to + * read-in the blocks at the tail of our file. Avoid reading them by + * testing i_size against each block offset. + */ +static int ocfs2_should_read_blk(struct inode *inode, struct page *page, + unsigned int block_start) +{ + u64 offset = page_offset(page) + block_start; + + if (ocfs2_sparse_alloc(OCFS2_SB(inode->i_sb))) + return 1; + + if (i_size_read(inode) > offset) + return 1; + + return 0; +} + +/* * Some of this taken from block_prepare_write(). We already have our * mapping by now though, and the entire write will be allocating or * it won't, so not much need to use BH_New. @@ -781,6 +802,7 @@ int ocfs2_map_page_blocks(struct page *page, u64 *p_blkno, set_buffer_uptodate(bh); } else if (!buffer_uptodate(bh) && !buffer_delay(bh) && !buffer_new(bh) && + ocfs2_should_read_blk(inode, page, block_start) && (block_start < from || block_end > to)) { ll_rw_block(READ, 1, &bh); *wait_bh++=bh; diff --git a/fs/ocfs2/cluster/heartbeat.c b/fs/ocfs2/cluster/heartbeat.c index 9cc7c0418b70..f02ccb34604d 100644 --- a/fs/ocfs2/cluster/heartbeat.c +++ b/fs/ocfs2/cluster/heartbeat.c @@ -267,7 +267,7 @@ static struct bio *o2hb_setup_one_bio(struct o2hb_region *reg, current_page = cs / spp; page = reg->hr_slot_data[current_page]; - vec_len = min(PAGE_CACHE_SIZE, + vec_len = min(PAGE_CACHE_SIZE - vec_start, (max_slots-cs) * (PAGE_CACHE_SIZE/spp) ); mlog(ML_HB_BIO, "page %d, vec_len = %u, vec_start = %u\n", diff --git a/fs/ocfs2/cluster/tcp.c b/fs/ocfs2/cluster/tcp.c index 685c18065c82..d84bd155997b 100644 --- a/fs/ocfs2/cluster/tcp.c +++ b/fs/ocfs2/cluster/tcp.c @@ -58,6 +58,7 @@ #include <linux/slab.h> #include <linux/idr.h> #include <linux/kref.h> +#include <linux/net.h> #include <net/tcp.h> #include <asm/uaccess.h> @@ -616,8 +617,7 @@ static void o2net_shutdown_sc(struct work_struct *work) del_timer_sync(&sc->sc_idle_timeout); o2net_sc_cancel_delayed_work(sc, &sc->sc_keepalive_work); sc_put(sc); - sc->sc_sock->ops->shutdown(sc->sc_sock, - RCV_SHUTDOWN|SEND_SHUTDOWN); + kernel_sock_shutdown(sc->sc_sock, SHUT_RDWR); } /* not fatal so failed connects before the other guy has our diff --git a/fs/ocfs2/dcache.c b/fs/ocfs2/dcache.c index 3094ddb7a254..1957a5ed219e 100644 --- a/fs/ocfs2/dcache.c +++ b/fs/ocfs2/dcache.c @@ -318,9 +318,9 @@ out_attach: static void ocfs2_drop_dentry_lock(struct ocfs2_super *osb, struct ocfs2_dentry_lock *dl) { + iput(dl->dl_inode); ocfs2_simple_drop_lockres(osb, &dl->dl_lockres); ocfs2_lock_res_free(&dl->dl_lockres); - iput(dl->dl_inode); kfree(dl); } diff --git a/fs/ocfs2/dir.c b/fs/ocfs2/dir.c index 6a2f143e269c..63b28fdceb4a 100644 --- a/fs/ocfs2/dir.c +++ b/fs/ocfs2/dir.c @@ -208,9 +208,9 @@ out: return NULL; } -struct buffer_head *ocfs2_find_entry_el(const char *name, int namelen, - struct inode *dir, - struct ocfs2_dir_entry **res_dir) +static struct buffer_head *ocfs2_find_entry_el(const char *name, int namelen, + struct inode *dir, + struct ocfs2_dir_entry **res_dir) { struct super_block *sb; struct buffer_head *bh_use[NAMEI_RA_SIZE]; diff --git a/fs/ocfs2/dlmglue.c b/fs/ocfs2/dlmglue.c index 41c76ff2fcfb..4e97dcceaf8f 100644 --- a/fs/ocfs2/dlmglue.c +++ b/fs/ocfs2/dlmglue.c @@ -670,7 +670,7 @@ static inline void ocfs2_generic_handle_attach_action(struct ocfs2_lock_res *loc { mlog_entry_void(); - BUG_ON((!lockres->l_flags & OCFS2_LOCK_BUSY)); + BUG_ON((!(lockres->l_flags & OCFS2_LOCK_BUSY))); BUG_ON(lockres->l_flags & OCFS2_LOCK_ATTACHED); if (lockres->l_requested > LKM_NLMODE && @@ -980,18 +980,6 @@ again: goto unlock; } - if (!(lockres->l_flags & OCFS2_LOCK_ATTACHED)) { - /* lock has not been created yet. */ - spin_unlock_irqrestore(&lockres->l_lock, flags); - - ret = ocfs2_lock_create(osb, lockres, LKM_NLMODE, 0); - if (ret < 0) { - mlog_errno(ret); - goto out; - } - goto again; - } - if (lockres->l_flags & OCFS2_LOCK_BLOCKED && !ocfs2_may_continue_on_blocked_lock(lockres, level)) { /* is the lock is currently blocked on behalf of @@ -1006,7 +994,14 @@ again: mlog(ML_ERROR, "lockres %s has action %u pending\n", lockres->l_name, lockres->l_action); - lockres->l_action = OCFS2_AST_CONVERT; + if (!(lockres->l_flags & OCFS2_LOCK_ATTACHED)) { + lockres->l_action = OCFS2_AST_ATTACH; + lkm_flags &= ~LKM_CONVERT; + } else { + lockres->l_action = OCFS2_AST_CONVERT; + lkm_flags |= LKM_CONVERT; + } + lockres->l_requested = level; lockres_or_flags(lockres, OCFS2_LOCK_BUSY); spin_unlock_irqrestore(&lockres->l_lock, flags); @@ -1021,7 +1016,7 @@ again: status = dlmlock(osb->dlm, level, &lockres->l_lksb, - lkm_flags|LKM_CONVERT, + lkm_flags, lockres->l_name, OCFS2_LOCK_ID_MAX_LEN - 1, ocfs2_locking_ast, diff --git a/fs/ocfs2/file.c b/fs/ocfs2/file.c index f92fe91ff260..bbac7cd33e0b 100644 --- a/fs/ocfs2/file.c +++ b/fs/ocfs2/file.c @@ -1891,9 +1891,11 @@ static ssize_t ocfs2_file_aio_write(struct kiocb *iocb, ssize_t written = 0; size_t ocount; /* original count */ size_t count; /* after file limit checks */ - loff_t *ppos = &iocb->ki_pos; + loff_t old_size, *ppos = &iocb->ki_pos; + u32 old_clusters; struct file *file = iocb->ki_filp; struct inode *inode = file->f_path.dentry->d_inode; + struct ocfs2_super *osb = OCFS2_SB(inode->i_sb); mlog_entry("(0x%p, %u, '%.*s')\n", file, (unsigned int)nr_segs, @@ -1949,6 +1951,13 @@ relock: goto relock; } + /* + * To later detect whether a journal commit for sync writes is + * necessary, we sample i_size, and cluster count here. + */ + old_size = i_size_read(inode); + old_clusters = OCFS2_I(inode)->ip_clusters; + /* communicate with ocfs2_dio_end_io */ ocfs2_iocb_set_rw_locked(iocb, rw_level); @@ -1978,6 +1987,21 @@ out_dio: /* buffered aio wouldn't have proper lock coverage today */ BUG_ON(ret == -EIOCBQUEUED && !(file->f_flags & O_DIRECT)); + if ((file->f_flags & O_SYNC && !direct_io) || IS_SYNC(inode)) { + /* + * The generic write paths have handled getting data + * to disk, but since we don't make use of the dirty + * inode list, a manual journal commit is necessary + * here. + */ + if (old_size != i_size_read(inode) || + old_clusters != OCFS2_I(inode)->ip_clusters) { + ret = journal_force_commit(osb->journal->j_journal); + if (ret < 0) + written = ret; + } + } + /* * deep in g_f_a_w_n()->ocfs2_direct_IO we pass in a ocfs2_dio_end_io * function pointer which is called when o_direct io completes so that diff --git a/fs/ocfs2/namei.c b/fs/ocfs2/namei.c index 729259016c18..989ac2718587 100644 --- a/fs/ocfs2/namei.c +++ b/fs/ocfs2/namei.c @@ -1105,9 +1105,16 @@ static int ocfs2_rename(struct inode *old_dir, goto bail; } - if (!new_de && new_inode) - mlog(ML_ERROR, "inode %lu does not exist in it's parent " - "directory!", new_inode->i_ino); + if (!new_de && new_inode) { + /* + * Target was unlinked by another node while we were + * waiting to get to ocfs2_rename(). There isn't + * anything we can do here to help the situation, so + * bubble up the appropriate error. + */ + status = -ENOENT; + goto bail; + } /* In case we need to overwrite an existing file, we blow it * away first */ diff --git a/fs/proc/proc_net.c b/fs/proc/proc_net.c index 749def054a34..131f9c68be5f 100644 --- a/fs/proc/proc_net.c +++ b/fs/proc/proc_net.c @@ -26,13 +26,6 @@ #include "internal.h" -struct proc_dir_entry *proc_net_create(struct net *net, - const char *name, mode_t mode, get_info_t *get_info) -{ - return create_proc_info_entry(name,mode, net->proc_net, get_info); -} -EXPORT_SYMBOL_GPL(proc_net_create); - struct proc_dir_entry *proc_net_fops_create(struct net *net, const char *name, mode_t mode, const struct file_operations *fops) { @@ -185,7 +178,7 @@ static __net_exit void proc_net_ns_exit(struct net *net) kfree(net->proc_net_root); } -static struct pernet_operations proc_net_ns_ops = { +static struct pernet_operations __net_initdata proc_net_ns_ops = { .init = proc_net_ns_init, .exit = proc_net_ns_exit, }; |