From dae5dbdbd786798ad2249e54df1156d524da30aa Mon Sep 17 00:00:00 2001 From: Steve French Date: Sun, 30 Dec 2007 23:49:57 +0000 Subject: [CIFS] fix SetEA failure to some Samba versions Thanks to Oleg Gvozdev for noticing the problem. CC: Andrew Morton Signed-off-by: Steve French --- fs/cifs/CHANGES | 2 +- fs/cifs/cifssmb.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/cifs/CHANGES b/fs/cifs/CHANGES index a609599287aa..13d788f9e5f0 100644 --- a/fs/cifs/CHANGES +++ b/fs/cifs/CHANGES @@ -3,7 +3,7 @@ Version 1.52 Fix oops on second mount to server when null auth is used. Enable experimental Kerberos support. Return writebehind errors on flush and sync so that events like out of disk space get reported properly on -cached files. +cached files. Fix setxattr failure to certain Samba versions. Version 1.51 ------------ diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 9e8a6bef029a..618542b8ce0b 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -5499,7 +5499,7 @@ SetEARetry: else name_len = strnlen(ea_name, 255); - count = sizeof(*parm_data) + ea_value_len + name_len + 1; + count = sizeof(*parm_data) + ea_value_len + name_len; pSMB->MaxParameterCount = cpu_to_le16(2); pSMB->MaxDataCount = cpu_to_le16(1000); /* BB find max SMB size from sess */ pSMB->MaxSetupCount = 0; -- cgit v1.2.3 From 05b3de63da2abe804f5dbe0174298bf48949079f Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Mon, 31 Dec 2007 00:51:45 +0000 Subject: [CIFS] Only dump SPNEGO key if CONFIG_CIFS_DEBUG2 is set The SPNEGO key data is not terribly interesting except in certain debugging situations. Only dump it to the ring buffer if needed. Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/cifs_spnego.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/fs/cifs/cifs_spnego.c b/fs/cifs/cifs_spnego.c index 1529d2b12e9c..d543accc10dd 100644 --- a/fs/cifs/cifs_spnego.c +++ b/fs/cifs/cifs_spnego.c @@ -122,11 +122,13 @@ cifs_get_spnego_key(struct cifsSesInfo *sesInfo) cFYI(1, ("key description = %s", description)); spnego_key = request_key(&cifs_spnego_key_type, description, ""); +#ifdef CONFIG_CIFS_DEBUG2 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); + cifs_dump_mem("SPNEGO reply blob:", msg->data, min(1024, + msg->secblob_len + msg->sesskey_len)); } +#endif /* CONFIG_CIFS_DEBUG2 */ out: kfree(description); -- cgit v1.2.3 From 1d9a8852c365fb7f8db0f8364210138985f457b8 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Mon, 31 Dec 2007 01:37:11 +0000 Subject: [CIFS] redo existing session setup if needed in cifs_mount When cifs_mount finds an existing SMB session that it can use for a new mount, it does not check to see whether that session is in need of being reconnected. An easy way to reproduce: 1) mount //server/share1 2) watch /proc/fs/cifs/DebugData for the share to go DISCONNECTED 3) mount //server/share2 with same creds as in step 1. The second mount will fail because CIFSTCon returned -EAGAIN. If you do an operation in share1 and then reattempt the mount it will work (since the session is reestablished). The following patch fixes this by having cifs_mount check the status of the session when it picks an existing session and calling cifs_setup_session on it again if it's in need of reconnection. Thanks to Wojciech Pilorz for the initial bug report. Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/CHANGES | 3 ++- fs/cifs/connect.c | 10 +++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/fs/cifs/CHANGES b/fs/cifs/CHANGES index 13d788f9e5f0..0c778765bd79 100644 --- a/fs/cifs/CHANGES +++ b/fs/cifs/CHANGES @@ -3,7 +3,8 @@ Version 1.52 Fix oops on second mount to server when null auth is used. Enable experimental Kerberos support. Return writebehind errors on flush and sync so that events like out of disk space get reported properly on -cached files. Fix setxattr failure to certain Samba versions. +cached files. Fix setxattr failure to certain Samba versions. Fix mount +of second share to disconnected server session (autoreconnect on this). Version 1.51 ------------ diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index fd9147cdb5a9..658f58b99e6f 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -1964,7 +1964,15 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, if (existingCifsSes) { pSesInfo = existingCifsSes; - cFYI(1, ("Existing smb sess found")); + cFYI(1, ("Existing smb sess found (status=%d)", + pSesInfo->status)); + if (pSesInfo->status == CifsNeedReconnect) { + cFYI(1, ("Session needs reconnect")); + down(&pSesInfo->sesSem); + 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(); -- cgit v1.2.3 From 1a67570c76402b36695cd0725e28649ee8fe830d Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Mon, 31 Dec 2007 04:03:02 +0000 Subject: [CIFS] use krb5 session key from first SMB session after a NegProt Currently, any new kerberos SMB session overwrites the server's session key. The session key should only be set by the first SMB session set up on the socket. Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/sess.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c index d0cb469daab7..ce698d5f6107 100644 --- a/fs/cifs/sess.c +++ b/fs/cifs/sess.c @@ -528,9 +528,11 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time, rc = -EOVERFLOW; goto ssetup_exit; } - ses->server->mac_signing_key.len = msg->sesskey_len; - memcpy(ses->server->mac_signing_key.data.krb5, msg->data, - msg->sesskey_len); + if (first_time) { + ses->server->mac_signing_key.len = msg->sesskey_len; + memcpy(ses->server->mac_signing_key.data.krb5, + msg->data, msg->sesskey_len); + } pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC; capabilities |= CAP_EXTENDED_SECURITY; pSMB->req.Capabilities = cpu_to_le32(capabilities); -- cgit v1.2.3 From bb5a9a04d4cab4b13d63ac5cd3e1fb35f9583607 Mon Sep 17 00:00:00 2001 From: Steve French Date: Mon, 31 Dec 2007 04:21:29 +0000 Subject: [CIFS] cifs_partialpagewrite() cleanup rc cannot be -EBADF now and condition is always true Signed-off-by: Vasily Averin Signed-off-by: Steve French --- fs/cifs/file.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/fs/cifs/file.c b/fs/cifs/file.c index dd26e2759b17..5f7c374ae89c 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -1179,12 +1179,10 @@ static int cifs_partialpagewrite(struct page *page, unsigned from, unsigned to) atomic_dec(&open_file->wrtPending); /* Does mm or vfs already set times? */ inode->i_atime = inode->i_mtime = current_fs_time(inode->i_sb); - if ((bytes_written > 0) && (offset)) { + if ((bytes_written > 0) && (offset)) rc = 0; - } else if (bytes_written < 0) { - if (rc != -EBADF) - rc = bytes_written; - } + else if (bytes_written < 0) + rc = bytes_written; } else { cFYI(1, ("No writeable filehandles for inode")); rc = -EIO; -- cgit v1.2.3 From 28c5a02a11f70bb1fd8dd3b633206e2db3220308 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Mon, 31 Dec 2007 04:56:21 +0000 Subject: [CIFS] fix unicode string alignment in SPNEGO setup Unicode strings need to be word aligned, but the code that handles that is currently not taking the length of the SPNEGO blob into account. Fix it to do so. Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/sess.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c index ce698d5f6107..d2153abcba6d 100644 --- a/fs/cifs/sess.c +++ b/fs/cifs/sess.c @@ -542,7 +542,7 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, int first_time, if (ses->capabilities & CAP_UNICODE) { /* unicode strings must be word aligned */ - if (iov[0].iov_len % 2) { + if ((iov[0].iov_len + iov[1].iov_len) % 2) { *bcc_ptr = 0; bcc_ptr++; } -- cgit v1.2.3 From 97837582bc1e191d2792af74c1f3762ed01243b9 Mon Sep 17 00:00:00 2001 From: Steve French Date: Mon, 31 Dec 2007 07:47:21 +0000 Subject: [CIFS] Allow setting mode via cifs acl Requires cifsacl mount flag to be on and CIFS_EXPERIMENTAL enabled CC: Shirish Pargaonkar Signed-off-by: Steve French --- fs/cifs/CHANGES | 2 + fs/cifs/cifsacl.c | 240 ++++++++++++++++++++++++++++++++++++++++++++++++++-- fs/cifs/cifspdu.h | 3 + fs/cifs/cifsproto.h | 4 +- fs/cifs/cifssmb.c | 65 ++++++++++++++ fs/cifs/inode.c | 14 ++- 6 files changed, 315 insertions(+), 13 deletions(-) diff --git a/fs/cifs/CHANGES b/fs/cifs/CHANGES index 0c778765bd79..edd248367b36 100644 --- a/fs/cifs/CHANGES +++ b/fs/cifs/CHANGES @@ -5,6 +5,8 @@ Enable experimental Kerberos support. Return writebehind errors on flush and sync so that events like out of disk space get reported properly on cached files. Fix setxattr failure to certain Samba versions. Fix mount of second share to disconnected server session (autoreconnect on this). +Add ability to modify cifs acls for handling chmod (when mounted with +cifsacl flag). Version 1.51 ------------ diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c index c312adcba4fc..a7035bd18e4e 100644 --- a/fs/cifs/cifsacl.c +++ b/fs/cifs/cifsacl.c @@ -129,6 +129,54 @@ int compare_sids(const struct cifs_sid *ctsid, const struct cifs_sid *cwsid) return (1); /* sids compare/match */ } + +/* copy ntsd, owner sid, and group sid from a security descriptor to another */ +static void copy_sec_desc(const struct cifs_ntsd *pntsd, + struct cifs_ntsd *pnntsd, __u32 sidsoffset) +{ + int i; + + struct cifs_sid *owner_sid_ptr, *group_sid_ptr; + struct cifs_sid *nowner_sid_ptr, *ngroup_sid_ptr; + + /* copy security descriptor control portion */ + pnntsd->revision = pntsd->revision; + pnntsd->type = pntsd->type; + pnntsd->dacloffset = cpu_to_le32(sizeof(struct cifs_ntsd)); + pnntsd->sacloffset = 0; + pnntsd->osidoffset = cpu_to_le32(sidsoffset); + pnntsd->gsidoffset = cpu_to_le32(sidsoffset + sizeof(struct cifs_sid)); + + /* copy owner sid */ + owner_sid_ptr = (struct cifs_sid *)((char *)pntsd + + le32_to_cpu(pntsd->osidoffset)); + nowner_sid_ptr = (struct cifs_sid *)((char *)pnntsd + sidsoffset); + + nowner_sid_ptr->revision = owner_sid_ptr->revision; + nowner_sid_ptr->num_subauth = owner_sid_ptr->num_subauth; + for (i = 0; i < 6; i++) + nowner_sid_ptr->authority[i] = owner_sid_ptr->authority[i]; + for (i = 0; i < 5; i++) + nowner_sid_ptr->sub_auth[i] = owner_sid_ptr->sub_auth[i]; + + /* copy group sid */ + group_sid_ptr = (struct cifs_sid *)((char *)pntsd + + le32_to_cpu(pntsd->gsidoffset)); + ngroup_sid_ptr = (struct cifs_sid *)((char *)pnntsd + sidsoffset + + sizeof(struct cifs_sid)); + + ngroup_sid_ptr->revision = group_sid_ptr->revision; + ngroup_sid_ptr->num_subauth = group_sid_ptr->num_subauth; + for (i = 0; i < 6; i++) + ngroup_sid_ptr->authority[i] = group_sid_ptr->authority[i]; + for (i = 0; i < 5; i++) + ngroup_sid_ptr->sub_auth[i] = + cpu_to_le32(group_sid_ptr->sub_auth[i]); + + return; +} + + /* change posix mode to reflect permissions pmode is the existing mode (we only want to overwrite part of this @@ -220,6 +268,33 @@ static void mode_to_access_flags(umode_t mode, umode_t bits_to_use, return; } +static __le16 fill_ace_for_sid(struct cifs_ace *pntace, + const struct cifs_sid *psid, __u64 nmode, umode_t bits) +{ + int i; + __u16 size = 0; + __u32 access_req = 0; + + pntace->type = ACCESS_ALLOWED; + pntace->flags = 0x0; + mode_to_access_flags(nmode, bits, &access_req); + if (!access_req) + access_req = SET_MINIMUM_RIGHTS; + pntace->access_req = cpu_to_le32(access_req); + + pntace->sid.revision = psid->revision; + pntace->sid.num_subauth = psid->num_subauth; + for (i = 0; i < 6; i++) + pntace->sid.authority[i] = psid->authority[i]; + for (i = 0; i < psid->num_subauth; i++) + pntace->sid.sub_auth[i] = psid->sub_auth[i]; + + size = 1 + 1 + 2 + 4 + 1 + 1 + 6 + (psid->num_subauth * 4); + pntace->size = cpu_to_le16(size); + + return (size); +} + #ifdef CONFIG_CIFS_DEBUG2 static void dump_ace(struct cifs_ace *pace, char *end_of_acl) @@ -243,7 +318,7 @@ static void dump_ace(struct cifs_ace *pace, char *end_of_acl) int i; 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)); + pace->flags, le16_to_cpu(pace->size))); for (i = 0; i < num_subauth; ++i) { cFYI(1, ("ACE sub_auth[%d]: 0x%x", i, le32_to_cpu(pace->sid.sub_auth[i]))); @@ -346,6 +421,28 @@ static void parse_dacl(struct cifs_acl *pdacl, char *end_of_acl, } +static int set_chmod_dacl(struct cifs_acl *pndacl, struct cifs_sid *pownersid, + struct cifs_sid *pgrpsid, __u64 nmode) +{ + __le16 size = 0; + struct cifs_acl *pnndacl; + + pnndacl = (struct cifs_acl *)((char *)pndacl + sizeof(struct cifs_acl)); + + size += fill_ace_for_sid((struct cifs_ace *) ((char *)pnndacl + size), + pownersid, nmode, S_IRWXU); + size += fill_ace_for_sid((struct cifs_ace *)((char *)pnndacl + size), + pgrpsid, nmode, S_IRWXG); + size += fill_ace_for_sid((struct cifs_ace *)((char *)pnndacl + size), + &sid_everyone, nmode, S_IRWXO); + + pndacl->size = cpu_to_le16(size + sizeof(struct cifs_acl)); + pndacl->num_aces = 3; + + return (0); +} + + static int parse_sid(struct cifs_sid *psid, char *end_of_acl) { /* BB need to add parm so we can store the SID BB */ @@ -432,6 +529,46 @@ static int parse_sec_desc(struct cifs_ntsd *pntsd, int acl_len, } +/* Convert permission bits from mode to equivalent CIFS ACL */ +static int build_sec_desc(struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd, + int acl_len, struct inode *inode, __u64 nmode) +{ + int rc = 0; + __u32 dacloffset; + __u32 ndacloffset; + __u32 sidsoffset; + struct cifs_sid *owner_sid_ptr, *group_sid_ptr; + struct cifs_acl *dacl_ptr = NULL; /* no need for SACL ptr */ + struct cifs_acl *ndacl_ptr = NULL; /* no need for SACL ptr */ + + if ((inode == NULL) || (pntsd == NULL) || (pnntsd == 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)); + + dacloffset = le32_to_cpu(pntsd->dacloffset); + dacl_ptr = (struct cifs_acl *)((char *)pntsd + dacloffset); + + ndacloffset = sizeof(struct cifs_ntsd); + ndacl_ptr = (struct cifs_acl *)((char *)pnntsd + ndacloffset); + ndacl_ptr->revision = dacl_ptr->revision; + ndacl_ptr->size = 0; + ndacl_ptr->num_aces = 0; + + rc = set_chmod_dacl(ndacl_ptr, owner_sid_ptr, group_sid_ptr, nmode); + + sidsoffset = ndacloffset + le16_to_cpu(ndacl_ptr->size); + + /* copy security descriptor control portion and owner and group sid */ + copy_sec_desc(pntsd, pnntsd, sidsoffset); + + return (rc); +} + + /* Retrieve an ACL from the server */ static struct cifs_ntsd *get_cifs_acl(u32 *pacllen, struct inode *inode, const char *path) @@ -487,6 +624,64 @@ static struct cifs_ntsd *get_cifs_acl(u32 *pacllen, struct inode *inode, return pntsd; } +/* Set an ACL on the server */ +static int set_cifs_acl(struct cifs_ntsd *pnntsd, __u32 acllen, + 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; + +#ifdef CONFIG_CIFS_DEBUG2 + cFYI(1, ("set ACL for %s from mode 0x%x", path, inode->i_mode)); +#endif + + if (!inode) + return (rc); + + sb = inode->i_sb; + if (sb == NULL) + return (rc); + + cifs_sb = CIFS_SB(sb); + xid = GetXid(); + + open_file = find_readable_file(CIFS_I(inode)); + 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, + WRITE_DAC, 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 set ACL")); + FreeXid(xid); + return (rc); + } + } + + rc = CIFSSMBSetCIFSACL(xid, cifs_sb->tcon, fid, pnntsd, acllen); +#ifdef CONFIG_CIFS_DEBUG2 + cFYI(1, ("SetCIFSACL rc = %d", rc)); +#endif + if (unlock_file == TRUE) + atomic_dec(&open_file->wrtPending); + else + CIFSSMBClose(xid, cifs_sb->tcon, fid); + + FreeXid(xid); + + return (rc); +} + /* 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) { @@ -510,24 +705,53 @@ void acl_to_uid_mode(struct inode *inode, const char *path) } /* 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 mode_to_acl(struct inode *inode, const char *path, __u64 nmode) { int rc = 0; __u32 acllen = 0; - struct cifs_ntsd *pntsd = NULL; + struct cifs_ntsd *pntsd = NULL; /* acl obtained from server */ + struct cifs_ntsd *pnntsd = NULL; /* modified acl to be sent to server */ +#ifdef CONFIG_CIFS_DEBUG2 cFYI(1, ("set ACL from mode for %s", path)); +#endif /* 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 */ + /* Add three ACEs for owner, group, everyone getting rid of + other ACEs as chmod disables ACEs and set the security descriptor */ - /* Set the security descriptor */ + if (pntsd) { + /* allocate memory for the smb header, + set security descriptor request security descriptor + parameters, and secuirty descriptor itself */ + pnntsd = kmalloc(acllen, GFP_KERNEL); + if (!pnntsd) { + cERROR(1, ("Unable to allocate security descriptor")); + kfree(pntsd); + return (-ENOMEM); + } - kfree(pntsd); - return rc; + rc = build_sec_desc(pntsd, pnntsd, acllen, inode, nmode); + +#ifdef CONFIG_CIFS_DEBUG2 + cFYI(1, ("build_sec_desc rc: %d", rc)); +#endif + + if (!rc) { + /* Set the security descriptor */ + rc = set_cifs_acl(pnntsd, acllen, inode, path); +#ifdef CONFIG_CIFS_DEBUG2 + cFYI(1, ("set_cifs_acl rc: %d", rc)); +#endif + } + + kfree(pnntsd); + kfree(pntsd); + } + + return (rc); } #endif /* CONFIG_CIFS_EXPERIMENTAL */ diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h index dbe6b846f37f..47f79504f57b 100644 --- a/fs/cifs/cifspdu.h +++ b/fs/cifs/cifspdu.h @@ -237,6 +237,9 @@ | DELETE | READ_CONTROL | WRITE_DAC \ | WRITE_OWNER | SYNCHRONIZE) +#define SET_MINIMUM_RIGHTS (FILE_READ_EA | FILE_READ_ATTRIBUTES \ + | READ_CONTROL | SYNCHRONIZE) + /* * Invalid readdir handle diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 8350eec49663..7093cb4b0212 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -97,7 +97,7 @@ 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 mode_to_acl(struct inode *inode, const char *path, __u64); extern int cifs_mount(struct super_block *, struct cifs_sb_info *, char *, const char *); @@ -342,6 +342,8 @@ extern int CIFSSMBSetEA(const int xid, struct cifsTconInfo *tcon, const struct nls_table *nls_codepage, int remap_special_chars); extern int CIFSSMBGetCIFSACL(const int xid, struct cifsTconInfo *tcon, __u16 fid, struct cifs_ntsd **acl_inf, __u32 *buflen); +extern int CIFSSMBSetCIFSACL(const int, struct cifsTconInfo *, __u16, + struct cifs_ntsd *, __u32); 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 618542b8ce0b..9409524e4bf8 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -3156,6 +3156,71 @@ qsec_out: /* cifs_small_buf_release(pSMB); */ /* Freed earlier now in SendReceive2 */ return rc; } + +int +CIFSSMBSetCIFSACL(const int xid, struct cifsTconInfo *tcon, __u16 fid, + struct cifs_ntsd *pntsd, __u32 acllen) +{ + __u16 byte_count, param_count, data_count, param_offset, data_offset; + int rc = 0; + int bytes_returned = 0; + SET_SEC_DESC_REQ *pSMB = NULL; + NTRANSACT_RSP *pSMBr = NULL; + +setCifsAclRetry: + rc = smb_init(SMB_COM_NT_TRANSACT, 19, tcon, (void **) &pSMB, + (void **) &pSMBr); + if (rc) + return (rc); + + pSMB->MaxSetupCount = 0; + pSMB->Reserved = 0; + + param_count = 8; + param_offset = offsetof(struct smb_com_transaction_ssec_req, Fid) - 4; + data_count = acllen; + data_offset = param_offset + param_count; + byte_count = 3 /* pad */ + param_count; + + pSMB->DataCount = cpu_to_le32(data_count); + pSMB->TotalDataCount = pSMB->DataCount; + pSMB->MaxParameterCount = cpu_to_le32(4); + pSMB->MaxDataCount = cpu_to_le32(16384); + pSMB->ParameterCount = cpu_to_le32(param_count); + pSMB->ParameterOffset = cpu_to_le32(param_offset); + pSMB->TotalParameterCount = pSMB->ParameterCount; + pSMB->DataOffset = cpu_to_le32(data_offset); + pSMB->SetupCount = 0; + pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_SET_SECURITY_DESC); + pSMB->ByteCount = cpu_to_le16(byte_count+data_count); + + pSMB->Fid = fid; /* file handle always le */ + pSMB->Reserved2 = 0; + pSMB->AclFlags = cpu_to_le32(CIFS_ACL_DACL); + + if (pntsd && acllen) { + memcpy((char *) &pSMBr->hdr.Protocol + data_offset, + (char *) pntsd, + acllen); + pSMB->hdr.smb_buf_length += (byte_count + data_count); + + } else + pSMB->hdr.smb_buf_length += byte_count; + + rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, + (struct smb_hdr *) pSMBr, &bytes_returned, 0); + + cFYI(1, ("SetCIFSACL bytes_returned: %d, rc: %d", bytes_returned, rc)); + if (rc) + cFYI(1, ("Set CIFS ACL returned %d", rc)); + cifs_buf_release(pSMB); + + if (rc == -EAGAIN) + goto setCifsAclRetry; + + return (rc); +} + #endif /* CONFIG_CIFS_EXPERIMENTAL */ /* Legacy Query Path Information call for lookup to old servers such diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index e915eb1d2e66..fdc0fe109d7b 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -1607,7 +1607,13 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs) CIFS_MOUNT_MAP_SPECIAL_CHR); else if (attrs->ia_valid & ATTR_MODE) { rc = 0; +#ifdef CONFIG_CIFS_EXPERIMENTAL + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) + rc = mode_to_acl(direntry->d_inode, full_path, mode); + else if ((mode & S_IWUGO) == 0) /* not writeable */ { +#else if ((mode & S_IWUGO) == 0) /* not writeable */ { +#endif if ((cifsInode->cifsAttrs & ATTR_READONLY) == 0) { set_dosattr = TRUE; time_buf.Attributes = @@ -1626,10 +1632,10 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs) if (time_buf.Attributes == 0) time_buf.Attributes |= cpu_to_le32(ATTR_NORMAL); } - /* BB to be implemented - - via Windows security descriptors or streams */ - /* CIFSSMBWinSetPerms(xid, pTcon, full_path, mode, uid, gid, - cifs_sb->local_nls); */ +#ifdef CONFIG_CIFS_EXPERIMENTAL + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) + mode_to_acl(direntry->d_inode, full_path, mode); +#endif } if (attrs->ia_valid & ATTR_ATIME) { -- cgit v1.2.3 From 88e7d705c4bdb729f02173583628ccbf49dba945 Mon Sep 17 00:00:00 2001 From: Steve French Date: Thu, 3 Jan 2008 17:37:09 +0000 Subject: [CIFS] hold ses sem on tcp session reconnect during mount Signed-off-by: Jeff Layton Signed-off-by: Steve French --- fs/cifs/connect.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 658f58b99e6f..db3746c891b5 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -1966,13 +1966,13 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, pSesInfo = existingCifsSes; cFYI(1, ("Existing smb sess found (status=%d)", pSesInfo->status)); + down(&pSesInfo->sesSem); if (pSesInfo->status == CifsNeedReconnect) { cFYI(1, ("Session needs reconnect")); - down(&pSesInfo->sesSem); rc = cifs_setup_session(xid, pSesInfo, cifs_sb->local_nls); - up(&pSesInfo->sesSem); } + up(&pSesInfo->sesSem); } else if (!rc) { cFYI(1, ("Existing smb sess not found")); pSesInfo = sesInfoAlloc(); @@ -3522,7 +3522,7 @@ cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb) 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, -- cgit v1.2.3 From f6d09982197c4163c70f6af0cf15bb78674105c0 Mon Sep 17 00:00:00 2001 From: Steve French Date: Tue, 8 Jan 2008 23:18:22 +0000 Subject: [CIFS] fix checkpatch warnings in fs/cifs/inode.c Signed-off-by: Steve French --- fs/cifs/README | 28 +++++++++++++++++----------- fs/cifs/TODO | 14 ++++++-------- fs/cifs/inode.c | 14 ++++++++------ 3 files changed, 31 insertions(+), 25 deletions(-) diff --git a/fs/cifs/README b/fs/cifs/README index bf11329ac784..c623e2f9c5db 100644 --- a/fs/cifs/README +++ b/fs/cifs/README @@ -56,7 +56,8 @@ the CIFS VFS web site) copy it to the same directory in which mount.smbfs and similar files reside (usually /sbin). Although the helper software is not required, mount.cifs is recommended. Eventually the Samba 3.0 utility program "net" may also be helpful since it may someday provide easier mount syntax for -users who are used to Windows e.g. net use +users who are used to Windows e.g. + net use Note that running the Winbind pam/nss module (logon service) on all of your Linux clients is useful in mapping Uids and Gids consistently across the domain to the proper network user. The mount.cifs mount helper can be @@ -248,7 +249,7 @@ A partial list of the supported mount options follows: the CIFS session. password The user password. If the mount helper is installed, the user will be prompted for password - if it is not supplied. + if not supplied. ip The ip address of the target server unc The target server Universal Network Name (export) to mount. @@ -283,7 +284,7 @@ A partial list of the supported mount options follows: can be enabled by specifying file_mode and dir_mode on the client. Note that the mount.cifs helper must be at version 1.10 or higher to support specifying the uid - (or gid) in non-numberic form. + (or gid) in non-numeric form. gid Set the default gid for inodes (similar to above). file_mode If CIFS Unix extensions are not supported by the server this overrides the default mode for file inodes. @@ -417,9 +418,10 @@ A partial list of the supported mount options follows: acl Allow setfacl and getfacl to manage posix ACLs if server supports them. (default) noacl Do not allow setfacl and getfacl calls on this mount - user_xattr Allow getting and setting user xattrs as OS/2 EAs (extended - attributes) to the server (default) e.g. via setfattr - and getfattr utilities. + user_xattr Allow getting and setting user xattrs (those attributes whose + name begins with "user." or "os2.") as OS/2 EAs (extended + attributes) to the server. This allows support of the + setfattr and getfattr utilities. (default) nouser_xattr Do not allow getfattr/setfattr to get/set/list xattrs mapchars Translate six of the seven reserved characters (not backslash) *?<>|: @@ -434,6 +436,7 @@ A partial list of the supported mount options follows: nomapchars Do not translate any of these seven characters (default). nocase Request case insensitive path name matching (case sensitive is the default if the server suports it). + (mount option "ignorecase" is identical to "nocase") posixpaths If CIFS Unix extensions are supported, attempt to negotiate posix path name support which allows certain characters forbidden in typical CIFS filenames, without @@ -485,6 +488,9 @@ A partial list of the supported mount options follows: ntlmv2i Use NTLMv2 password hashing with packet signing lanman (if configured in kernel config) use older lanman hash +hard Retry file operations if server is not responding +soft Limit retries to unresponsive servers (usually only + one retry) before returning an error. (default) The mount.cifs mount helper also accepts a few mount options before -o including: @@ -535,8 +541,8 @@ SecurityFlags Flags which control security negotiation and must use NTLM 0x02002 may use NTLMv2 0x00004 must use NTLMv2 0x04004 - may use Kerberos security (not implemented yet) 0x00008 - must use Kerberos (not implemented yet) 0x08008 + may use Kerberos security 0x00008 + must use Kerberos 0x08008 may use lanman (weak) password hash 0x00010 must use lanman password hash 0x10010 may use plaintext passwords 0x00020 @@ -626,6 +632,6 @@ returned success. Also note that "cat /proc/fs/cifs/DebugData" will display information about the active sessions and the shares that are mounted. -Enabling Kerberos (extended security) works when CONFIG_CIFS_EXPERIMENTAL is enabled -but requires a user space helper (from the Samba project). NTLM and NTLMv2 and -LANMAN support do not require this helpr. +Enabling Kerberos (extended security) works when CONFIG_CIFS_EXPERIMENTAL is +on but requires a user space helper (from the Samba project). NTLM and NTLMv2 and +LANMAN support do not require this helper. diff --git a/fs/cifs/TODO b/fs/cifs/TODO index a8852c200728..92c9feac440f 100644 --- a/fs/cifs/TODO +++ b/fs/cifs/TODO @@ -1,4 +1,4 @@ -Version 1.49 April 26, 2007 +Version 1.52 January 3, 2008 A Partial List of Missing Features ================================== @@ -16,16 +16,14 @@ SecurityDescriptors c) Better pam/winbind integration (e.g. to handle uid mapping better) -d) Verify that Kerberos signing works - -e) Cleanup now unneeded SessSetup code in +d) Cleanup now unneeded SessSetup code in fs/cifs/connect.c and add back in NTLMSSP code if any servers need it -f) MD5-HMAC signing SMB PDUs when SPNEGO style SessionSetup -used (Kerberos or NTLMSSP). Signing alreadyimplemented for NTLM -and raw NTLMSSP already. This is important when enabling -extended security and mounting to Windows 2003 Servers +e) ms-dfs and ms-dfs host name resolution cleanup + +f) fix NTLMv2 signing when two mounts with different users to same +server. g) Directory entry caching relies on a 1 second timer, rather than using FindNotify or equivalent. - (started) diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index fdc0fe109d7b..d9567ba2960b 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -54,9 +54,9 @@ int cifs_get_inode_info_unix(struct inode **pinode, MAX_TREE_SIZE + 1) + strnlen(search_path, MAX_PATHCONF) + 1, GFP_KERNEL); - if (tmp_path == NULL) { + if (tmp_path == NULL) return -ENOMEM; - } + /* have to skip first of the double backslash of UNC name */ strncpy(tmp_path, pTcon->treeName, MAX_TREE_SIZE); @@ -511,7 +511,8 @@ int cifs_get_inode_info(struct inode **pinode, } spin_lock(&inode->i_lock); - if (is_size_safe_to_change(cifsInfo, le64_to_cpu(pfindData->EndOfFile))) { + if (is_size_safe_to_change(cifsInfo, + le64_to_cpu(pfindData->EndOfFile))) { /* can not safely shrink the file size here if the client is writing to it due to potential races */ i_size_write(inode, le64_to_cpu(pfindData->EndOfFile)); @@ -931,7 +932,7 @@ int cifs_mkdir(struct inode *inode, struct dentry *direntry, int mode) (CIFS_UNIX_POSIX_PATH_OPS_CAP & le64_to_cpu(pTcon->fsUnixInfo.Capability))) { u32 oplock = 0; - FILE_UNIX_BASIC_INFO * pInfo = + FILE_UNIX_BASIC_INFO *pInfo = kzalloc(sizeof(FILE_UNIX_BASIC_INFO), GFP_KERNEL); if (pInfo == NULL) { rc = -ENOMEM; @@ -1610,10 +1611,11 @@ int cifs_setattr(struct dentry *direntry, struct iattr *attrs) #ifdef CONFIG_CIFS_EXPERIMENTAL if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) rc = mode_to_acl(direntry->d_inode, full_path, mode); - else if ((mode & S_IWUGO) == 0) /* not writeable */ { + else if ((mode & S_IWUGO) == 0) { #else - if ((mode & S_IWUGO) == 0) /* not writeable */ { + if ((mode & S_IWUGO) == 0) { #endif + /* not writeable */ if ((cifsInode->cifsAttrs & ATTR_READONLY) == 0) { set_dosattr = TRUE; time_buf.Attributes = -- cgit v1.2.3 From 6103335de8afa5d780dcd512abe85c696af7b040 Mon Sep 17 00:00:00 2001 From: Steve French Date: Wed, 9 Jan 2008 16:21:36 +0000 Subject: [CIFS] DNS name resolution helper upcall for cifs Adds additional option CIFS_DFS_UPCALL to fs/Kconfig for enabling DFS support. Resolved IP address is saved as a string in the key payload. Igor has a series of related patches that will follow which finish up CIFS DFS support Acked-by: Igor Mammedov Signed-off-by: Steve French --- fs/Kconfig | 39 ++++++++++++++++++++++++++------------- fs/cifs/Makefile | 2 ++ fs/cifs/cifsfs.c | 15 ++++++++++++++- 3 files changed, 42 insertions(+), 14 deletions(-) diff --git a/fs/Kconfig b/fs/Kconfig index 487236c65837..18cd22149466 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -1905,13 +1905,15 @@ config CIFS file servers such as Windows 2000 (including Windows 2003, NT 4 and Windows XP) as well by Samba (which provides excellent CIFS server support for Linux and many other operating systems). Limited - support for OS/2 and Windows ME and similar servers is provided as well. - - The intent of the cifs module is to provide an advanced - network file system client for mounting to CIFS compliant servers, - including support for dfs (hierarchical name space), secure per-user - session establishment, safe distributed caching (oplock), optional - packet signing, Unicode and other internationalization improvements. + support for OS/2 and Windows ME and similar servers is provided as + well. + + The cifs module provides an advanced network file system + client for mounting to CIFS compliant servers. It includes + support for DFS (hierarchical name space), secure per-user + session establishment via Kerberos or NTLM or NTLMv2, + safe distributed caching (oplock), optional packet + signing, Unicode and other internationalization improvements. If you need to mount to Samba or Windows from this machine, say Y. config CIFS_STATS @@ -1943,7 +1945,8 @@ config CIFS_WEAK_PW_HASH (since 1997) support stronger NTLM (and even NTLMv2 and Kerberos) security mechanisms. These hash the password more securely than the mechanisms used in the older LANMAN version of the - SMB protocol needed to establish sessions with old SMB servers. + SMB protocol but LANMAN based authentication is needed to + establish sessions with some old SMB servers. Enabling this option allows the cifs module to mount to older LANMAN based servers such as OS/2 and Windows 95, but such @@ -1951,8 +1954,8 @@ config CIFS_WEAK_PW_HASH security mechanisms if you are on a public network. Unless you have a need to access old SMB servers (and are on a private network) you probably want to say N. Even if this support - is enabled in the kernel build, they will not be used - automatically. At runtime LANMAN mounts are disabled but + is enabled in the kernel build, LANMAN authentication will not be + used automatically. At runtime LANMAN mounts are disabled but can be set to required (or optional) either in /proc/fs/cifs (see fs/cifs/README for more detail) or via an option on the mount command. This support is disabled by @@ -2018,12 +2021,22 @@ config CIFS_UPCALL depends on CIFS_EXPERIMENTAL depends on KEYS help - Enables an upcall mechanism for CIFS which will be used to contact - userspace helper utilities to provide SPNEGO packaged Kerberos - tickets which are needed to mount to certain secure servers + Enables an upcall mechanism for CIFS which accesses + userspace helper utilities to provide SPNEGO packaged (RFC 4178) + Kerberos tickets which are needed to mount to certain secure servers (for which more secure Kerberos authentication is required). If unsure, say N. +config CIFS_DFS_UPCALL + bool "DFS feature support (EXPERIMENTAL)" + depends on CIFS_EXPERIMENTAL + depends on KEYS + help + Enables an upcall mechanism for CIFS which contacts userspace + helper utilities to provide server name resolution (host names to + IP addresses) which is needed for implicit mounts of DFS junction + points. If unsure, say N. + config NCP_FS tristate "NCP file system support (to mount NetWare volumes)" depends on IPX!=n || INET diff --git a/fs/cifs/Makefile b/fs/cifs/Makefile index 45e42fb97c19..09898b8dc69b 100644 --- a/fs/cifs/Makefile +++ b/fs/cifs/Makefile @@ -9,3 +9,5 @@ cifs-y := cifsfs.o cifssmb.o cifs_debug.o connect.o dir.o file.o inode.o \ readdir.o ioctl.o sess.o export.o cifsacl.o cifs-$(CONFIG_CIFS_UPCALL) += cifs_spnego.o + +cifs-$(CONFIG_CIFS_DFS_UPCALL) += dns_resolve.o diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 093beaa3900d..000b4a5d3219 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -44,6 +44,7 @@ #include "cifs_fs_sb.h" #include #include +#include "dns_resolve.h" #include "cifs_spnego.h" #define CIFS_MAGIC_NUMBER 0xFF534D42 /* the first four bytes of SMB PDUs */ @@ -1014,12 +1015,17 @@ init_cifs(void) rc = register_key_type(&cifs_spnego_key_type); if (rc) goto out_unregister_filesystem; +#endif +#ifdef CONFIG_CIFS_DFS_UPCALL + rc = register_key_type(&key_type_dns_resolver); + if (rc) + goto out_unregister_key_type; #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_key_type; + goto out_unregister_dfs_key_type; } dnotifyThread = kthread_run(cifs_dnotify_thread, NULL, "cifsdnotifyd"); @@ -1033,7 +1039,11 @@ init_cifs(void) out_stop_oplock_thread: kthread_stop(oplockThread); + out_unregister_dfs_key_type: +#ifdef CONFIG_CIFS_DFS_UPCALL + unregister_key_type(&key_type_dns_resolver); out_unregister_key_type: +#endif #ifdef CONFIG_CIFS_UPCALL unregister_key_type(&cifs_spnego_key_type); out_unregister_filesystem: @@ -1059,6 +1069,9 @@ exit_cifs(void) #ifdef CONFIG_PROC_FS cifs_proc_clean(); #endif +#ifdef CONFIG_CIFS_DFS_UPCALL + unregister_key_type(&key_type_dns_resolver); +#endif #ifdef CONFIG_CIFS_UPCALL unregister_key_type(&cifs_spnego_key_type); #endif -- cgit v1.2.3 From 197c183f3526dc08aa52ca97ec66c268442d4b84 Mon Sep 17 00:00:00 2001 From: Steve French Date: Thu, 10 Jan 2008 17:10:23 +0000 Subject: [CIFS] Forgot to add two new files from previous commit Thanks to Igor for noticing this. CC: Igor Mammedov Signed-off-by: Steve French --- fs/cifs/dns_resolve.c | 123 ++++++++++++++++++++++++++++++++++++++++++++++++++ fs/cifs/dns_resolve.h | 32 +++++++++++++ 2 files changed, 155 insertions(+) create mode 100644 fs/cifs/dns_resolve.c create mode 100644 fs/cifs/dns_resolve.h diff --git a/fs/cifs/dns_resolve.c b/fs/cifs/dns_resolve.c new file mode 100644 index 000000000000..777a086abd6f --- /dev/null +++ b/fs/cifs/dns_resolve.c @@ -0,0 +1,123 @@ +/* + * fs/cifs/dns_resolve.c + * + * Copyright (c) 2007 Igor Mammedov + * Author(s): Igor Mammedov (niallain@gmail.com) + * Steve French (sfrench@us.ibm.com) + * + * Contains the CIFS DFS upcall routines used for hostname to + * IP address translation. + * + * 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 +#include "dns_resolve.h" +#include "cifsglob.h" +#include "cifsproto.h" +#include "cifs_debug.h" + +static int dns_resolver_instantiate(struct key *key, const void *data, + size_t datalen) +{ + int rc = 0; + char *ip; + + ip = kmalloc(datalen+1, GFP_KERNEL); + if (!ip) + return -ENOMEM; + + memcpy(ip, data, datalen); + ip[datalen] = '\0'; + + rcu_assign_pointer(key->payload.data, ip); + + return rc; +} + +struct key_type key_type_dns_resolver = { + .name = "dns_resolver", + .def_datalen = sizeof(struct in_addr), + .describe = user_describe, + .instantiate = dns_resolver_instantiate, + .match = user_match, +}; + + +/* Resolves server name to ip address. + * input: + * unc - server UNC + * output: + * *ip_addr - pointer to server ip, caller responcible for freeing it. + * return 0 on success + */ +int +dns_resolve_server_name_to_ip(const char *unc, char **ip_addr) { + int rc = -EAGAIN; + struct key *rkey; + char *name; + int len; + + if ((!ip_addr) || (!unc)) + return -EINVAL; + + /* search for server name delimiter */ + len = strlen(unc); + if (len < 3) { + cFYI(1, ("%s: unc is too short: %s", __FUNCTION__, unc)); + return -EINVAL; + } + len -= 2; + name = memchr(unc+2, '\\', len); + if (!name) { + cFYI(1, ("%s: probably server name is whole unc: %s", + __FUNCTION__, unc)); + } else { + len = (name - unc) - 2/* leading // */; + } + + name = kmalloc(len+1, GFP_KERNEL); + if (!name) { + rc = -ENOMEM; + return rc; + } + memcpy(name, unc+2, len); + name[len] = 0; + + rkey = request_key(&key_type_dns_resolver, name, ""); + if (!IS_ERR(rkey)) { + len = strlen(rkey->payload.data); + *ip_addr = kmalloc(len+1, GFP_KERNEL); + if (*ip_addr) { + memcpy(*ip_addr, rkey->payload.data, len); + (*ip_addr)[len] = '\0'; + cFYI(1, ("%s: resolved: %s to %s", __FUNCTION__, + rkey->description, + *ip_addr + )); + rc = 0; + } else { + rc = -ENOMEM; + } + key_put(rkey); + } else { + cERROR(1, ("%s: unable to resolve: %s", __FUNCTION__, name)); + } + + kfree(name); + return rc; +} + + diff --git a/fs/cifs/dns_resolve.h b/fs/cifs/dns_resolve.h new file mode 100644 index 000000000000..073fdc3db419 --- /dev/null +++ b/fs/cifs/dns_resolve.h @@ -0,0 +1,32 @@ +/* + * fs/cifs/dns_resolve.h -- DNS Resolver upcall management for CIFS DFS + * Handles host name to IP address resolution + * + * Copyright (c) International Business Machines Corp., 2008 + * Author(s): 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 _DNS_RESOLVE_H +#define _DNS_RESOLVE_H + +#ifdef __KERNEL__ +#include +extern struct key_type key_type_dns_resolver; +extern int dns_resolve_server_name_to_ip(const char *unc, char **ip_addr); +#endif /* KERNEL */ + +#endif /* _DNS_RESOLVE_H */ -- cgit v1.2.3 From e6ab15827eec0bc4444421f7ccf0223de321c708 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Fri, 11 Jan 2008 01:49:48 +0000 Subject: [CIFS] DFS support patchset: Added mountdata Also cifs_fs_type was made not static for ussage in dfs code. Signed-off-by: Igor Mammedov Signed-off-by: Steve French --- fs/cifs/cifs_fs_sb.h | 5 ++++- fs/cifs/cifsfs.c | 37 ++++++++++++++++++++++++++++++++++++- fs/cifs/cifsfs.h | 1 + 3 files changed, 41 insertions(+), 2 deletions(-) diff --git a/fs/cifs/cifs_fs_sb.h b/fs/cifs/cifs_fs_sb.h index 34af556cdd8d..8ad2330ba061 100644 --- a/fs/cifs/cifs_fs_sb.h +++ b/fs/cifs/cifs_fs_sb.h @@ -43,6 +43,9 @@ struct cifs_sb_info { mode_t mnt_dir_mode; int mnt_cifs_flags; int prepathlen; - char *prepath; + char *prepath; /* relative path under the share to mount to */ +#ifdef CONFIG_CIFS_DFS_UPCALL + char *mountdata; /* mount options received at mount time */ +#endif }; #endif /* _CIFS_FS_SB_H */ diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 000b4a5d3219..93e107883a61 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -97,6 +97,9 @@ cifs_read_super(struct super_block *sb, void *data, { struct inode *inode; struct cifs_sb_info *cifs_sb; +#ifdef CONFIG_CIFS_DFS_UPCALL + int len; +#endif int rc = 0; /* BB should we make this contingent on mount parm? */ @@ -106,6 +109,25 @@ cifs_read_super(struct super_block *sb, void *data, if (cifs_sb == NULL) return -ENOMEM; +#ifdef CONFIG_CIFS_DFS_UPCALL + /* copy mount params to sb for use in submounts */ + /* BB: should we move this after the mount so we + * do not have to do the copy on failed mounts? + * BB: May be it is better to do simple copy before + * complex operation (mount), and in case of fail + * just exit instead of doing mount and attempting + * undo it if this copy fails?*/ + len = strlen(data); + cifs_sb->mountdata = kzalloc(len + 1, GFP_KERNEL); + if (cifs_sb->mountdata == NULL) { + kfree(sb->s_fs_info); + sb->s_fs_info = NULL; + return -ENOMEM; + } + strncpy(cifs_sb->mountdata, data, len + 1); + cifs_sb->mountdata[len] = '\0'; +#endif + rc = cifs_mount(sb, cifs_sb, data, devname); if (rc) { @@ -155,6 +177,12 @@ out_no_root: out_mount_failed: if (cifs_sb) { +#ifdef CONFIG_CIFS_DFS_UPCALL + if (cifs_sb->mountdata) { + kfree(cifs_sb->mountdata); + cifs_sb->mountdata = NULL; + } +#endif if (cifs_sb->local_nls) unload_nls(cifs_sb->local_nls); kfree(cifs_sb); @@ -178,6 +206,13 @@ cifs_put_super(struct super_block *sb) if (rc) { cERROR(1, ("cifs_umount failed with return code %d", rc)); } +#ifdef CONFIG_CIFS_DFS_UPCALL + if (cifs_sb->mountdata) { + kfree(cifs_sb->mountdata); + cifs_sb->mountdata = NULL; + } +#endif + unload_nls(cifs_sb->local_nls); kfree(cifs_sb); return; @@ -553,7 +588,7 @@ static loff_t cifs_llseek(struct file *file, loff_t offset, int origin) return remote_llseek(file, offset, origin); } -static struct file_system_type cifs_fs_type = { +struct file_system_type cifs_fs_type = { .owner = THIS_MODULE, .name = "cifs", .get_sb = cifs_get_sb, diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h index 2a21dc66f0de..2e68126d07eb 100644 --- a/fs/cifs/cifsfs.h +++ b/fs/cifs/cifsfs.h @@ -32,6 +32,7 @@ #define TRUE 1 #endif +extern struct file_system_type cifs_fs_type; extern const struct address_space_operations cifs_addr_ops; extern const struct address_space_operations cifs_addr_ops_smallbuf; -- cgit v1.2.3 From ed2b91701d97047fa9970645e43d5e551e261adb Mon Sep 17 00:00:00 2001 From: Steve French Date: Sun, 20 Jan 2008 00:30:29 +0000 Subject: [CIFS] Do not log path names in lookup errors Andi Kleen noticed that we were logging access denied errors (which is noisy in the dmesg log, and not needed to be logged) and that we were logging path names on that an other errors (e.g. EIO) which we should not be doing. CC: Andi Kleen Signed-off-by: Steve French --- fs/cifs/dir.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c index 37dc97af1487..699ec1198409 100644 --- a/fs/cifs/dir.c +++ b/fs/cifs/dir.c @@ -517,12 +517,10 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry, d_add(direntry, NULL); /* if it was once a directory (but how can we tell?) we could do shrink_dcache_parent(direntry); */ - } else { - cERROR(1, ("Error 0x%x on cifs_get_inode_info in lookup of %s", - rc, full_path)); - /* BB special case check for Access Denied - watch security - exposure of returning dir info implicitly via different rc - if file exists or not but no access BB */ + } else if (rc != -EACCES) { + cERROR(1, ("Unexpected lookup error %d", rc)); + /* We special case check for Access Denied - since that + is a common return code */ } kfree(full_path); -- cgit v1.2.3 From 6d5ae0deb1641bf615eafd8fef64218e10cb2fd0 Mon Sep 17 00:00:00 2001 From: Igor Mammedov Date: Fri, 25 Jan 2008 03:28:31 +0000 Subject: [CIFS] DFS support: provide shrinkable mounts Signed-off-by: Igor Mammedov Signed-off-by: Steve French --- fs/cifs/Makefile | 2 +- fs/cifs/cifs_dfs_ref.c | 375 +++++++++++++++++++++++++++++++++++++++++++++++++ fs/cifs/cifsfs.c | 4 + fs/cifs/cifsfs.h | 4 + fs/cifs/cifsproto.h | 3 + 5 files changed, 387 insertions(+), 1 deletion(-) create mode 100644 fs/cifs/cifs_dfs_ref.c diff --git a/fs/cifs/Makefile b/fs/cifs/Makefile index 09898b8dc69b..6ba43fb346fb 100644 --- a/fs/cifs/Makefile +++ b/fs/cifs/Makefile @@ -10,4 +10,4 @@ cifs-y := cifsfs.o cifssmb.o cifs_debug.o connect.o dir.o file.o inode.o \ cifs-$(CONFIG_CIFS_UPCALL) += cifs_spnego.o -cifs-$(CONFIG_CIFS_DFS_UPCALL) += dns_resolve.o +cifs-$(CONFIG_CIFS_DFS_UPCALL) += dns_resolve.o cifs_dfs_ref.o diff --git a/fs/cifs/cifs_dfs_ref.c b/fs/cifs/cifs_dfs_ref.c new file mode 100644 index 000000000000..15e31f8435ba --- /dev/null +++ b/fs/cifs/cifs_dfs_ref.c @@ -0,0 +1,375 @@ +/* + * Contains the CIFS DFS referral mounting routines used for handling + * traversal via DFS junction point + * + * Copyright (c) 2007 Igor Mammedov + * Author(s): Igor Mammedov (niallain@gmail.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include "cifsglob.h" +#include "cifsproto.h" +#include "cifsfs.h" +#include "dns_resolve.h" +#include "cifs_debug.h" + +LIST_HEAD(cifs_dfs_automount_list); + +/* + * DFS functions +*/ + +void dfs_shrink_umount_helper(struct vfsmount *vfsmnt) +{ + mark_mounts_for_expiry(&cifs_dfs_automount_list); + mark_mounts_for_expiry(&cifs_dfs_automount_list); + shrink_submounts(vfsmnt, &cifs_dfs_automount_list); +} + +/** + * cifs_get_share_name - extracts share name from UNC + * @node_name: pointer to UNC string + * + * Extracts sharename form full UNC. + * i.e. strips from UNC trailing path that is not part of share + * name and fixup missing '\' in the begining of DFS node refferal + * if neccessary. + * Returns pointer to share name on success or NULL on error. + * Caller is responsible for freeing returned string. + */ +static char *cifs_get_share_name(const char *node_name) +{ + int len; + char *UNC; + char *pSep; + + len = strlen(node_name); + UNC = kmalloc(len+2 /*for term null and additional \ if it's missed */, + GFP_KERNEL); + if (!UNC) + return NULL; + + /* get share name and server name */ + if (node_name[1] != '\\') { + UNC[0] = '\\'; + strncpy(UNC+1, node_name, len); + len++; + UNC[len] = 0; + } else { + strncpy(UNC, node_name, len); + UNC[len] = 0; + } + + /* find server name end */ + pSep = memchr(UNC+2, '\\', len-2); + if (!pSep) { + cERROR(1, ("%s: no server name end in node name: %s", + __FUNCTION__, node_name)); + kfree(UNC); + return NULL; + } + + /* find sharename end */ + pSep++; + pSep = memchr(UNC+(pSep-UNC), '\\', len-(pSep-UNC)); + if (!pSep) { + cERROR(1, ("%s:2 cant find share name in node name: %s", + __FUNCTION__, node_name)); + kfree(UNC); + return NULL; + } + /* trim path up to sharename end + * * now we have share name in UNC */ + *pSep = 0; + + return UNC; +} + + +/** + * compose_mount_options - creates mount options for refferral + * @sb_mountdata: parent/root DFS mount options (template) + * @ref_unc: refferral server UNC + * @devname: pointer for saving device name + * + * creates mount options for submount based on template options sb_mountdata + * and replacing unc,ip,prefixpath options with ones we've got form ref_unc. + * + * Returns: pointer to new mount options or ERR_PTR. + * Caller is responcible for freeing retunrned value if it is not error. + */ +char *compose_mount_options(const char *sb_mountdata, const char *ref_unc, + char **devname) +{ + int rc; + char *mountdata; + int md_len; + char *tkn_e; + char *srvIP = NULL; + char sep = ','; + int off, noff; + + if (sb_mountdata == NULL) + return ERR_PTR(-EINVAL); + + *devname = cifs_get_share_name(ref_unc); + rc = dns_resolve_server_name_to_ip(*devname, &srvIP); + if (rc != 0) { + cERROR(1, ("%s: Failed to resolve server part of %s to IP", + __FUNCTION__, *devname)); + mountdata = ERR_PTR(rc); + goto compose_mount_options_out; + } + md_len = strlen(sb_mountdata) + strlen(srvIP) + strlen(ref_unc) + 3; + mountdata = kzalloc(md_len+1, GFP_KERNEL); + if (mountdata == NULL) { + mountdata = ERR_PTR(-ENOMEM); + goto compose_mount_options_out; + } + + /* copy all options except of unc,ip,prefixpath */ + off = 0; + if (strncmp(sb_mountdata, "sep=", 4) == 0) { + sep = sb_mountdata[4]; + strncpy(mountdata, sb_mountdata, 5); + off += 5; + } + while ((tkn_e = strchr(sb_mountdata+off, sep))) { + noff = (tkn_e - (sb_mountdata+off)) + 1; + if (strnicmp(sb_mountdata+off, "unc=", 4) == 0) { + off += noff; + continue; + } + if (strnicmp(sb_mountdata+off, "ip=", 3) == 0) { + off += noff; + continue; + } + if (strnicmp(sb_mountdata+off, "prefixpath=", 3) == 0) { + off += noff; + continue; + } + strncat(mountdata, sb_mountdata+off, noff); + off += noff; + } + strcat(mountdata, sb_mountdata+off); + mountdata[md_len] = '\0'; + + /* copy new IP and ref share name */ + strcat(mountdata, ",ip="); + strcat(mountdata, srvIP); + strcat(mountdata, ",unc="); + strcat(mountdata, *devname); + + /* find & copy prefixpath */ + tkn_e = strchr(ref_unc+2, '\\'); + if (tkn_e) { + tkn_e = strchr(tkn_e+1, '\\'); + if (tkn_e) { + strcat(mountdata, ",prefixpath="); + strcat(mountdata, tkn_e); + } + } + + /*cFYI(1,("%s: parent mountdata: %s", __FUNCTION__,sb_mountdata));*/ + /*cFYI(1, ("%s: submount mountdata: %s", __FUNCTION__, mountdata ));*/ + +compose_mount_options_out: + kfree(srvIP); + return mountdata; +} + + +struct vfsmount *cifs_dfs_do_refmount(const struct vfsmount *mnt_parent, + struct dentry *dentry, char *ref_unc) +{ + struct cifs_sb_info *cifs_sb; + struct vfsmount *mnt; + char *mountdata; + char *devname; + + cifs_sb = CIFS_SB(dentry->d_inode->i_sb); + mountdata = compose_mount_options(cifs_sb->mountdata, + ref_unc, &devname); + + if (IS_ERR(mountdata)) + return (struct vfsmount *)mountdata; + + mnt = vfs_kern_mount(&cifs_fs_type, 0, devname, mountdata); + kfree(mountdata); + kfree(devname); + return mnt; + +} + +static char *build_full_dfs_path_from_dentry(struct dentry *dentry) +{ + char *full_path = NULL; + char *search_path; + char *tmp_path; + size_t l_max_len; + struct cifs_sb_info *cifs_sb; + + if (dentry->d_inode == NULL) + return NULL; + + cifs_sb = CIFS_SB(dentry->d_inode->i_sb); + + if (cifs_sb->tcon == NULL) + return NULL; + + search_path = build_path_from_dentry(dentry); + if (search_path == NULL) + return NULL; + + if (cifs_sb->tcon->Flags & SMB_SHARE_IS_IN_DFS) { + /* we should use full path name to correct working with DFS */ + l_max_len = strnlen(cifs_sb->tcon->treeName, MAX_TREE_SIZE+1) + + strnlen(search_path, MAX_PATHCONF) + 1; + tmp_path = kmalloc(l_max_len, GFP_KERNEL); + if (tmp_path == NULL) { + kfree(search_path); + return NULL; + } + strncpy(tmp_path, cifs_sb->tcon->treeName, l_max_len); + strcat(tmp_path, search_path); + tmp_path[l_max_len-1] = 0; + full_path = tmp_path; + kfree(search_path); + } else { + full_path = search_path; + } + return full_path; +} + +static int add_mount_helper(struct vfsmount *newmnt, struct nameidata *nd, + struct list_head *mntlist) +{ + /* stolen from afs code */ + int err; + + mntget(newmnt); + err = do_add_mount(newmnt, nd, nd->mnt->mnt_flags, mntlist); + switch (err) { + case 0: + dput(nd->dentry); + mntput(nd->mnt); + nd->mnt = newmnt; + nd->dentry = dget(newmnt->mnt_root); + break; + case -EBUSY: + /* someone else made a mount here whilst we were busy */ + while (d_mountpoint(nd->dentry) && + follow_down(&nd->mnt, &nd->dentry)) + ; + err = 0; + default: + mntput(newmnt); + break; + } + return err; +} + +void dump_referral(const struct dfs_info3_param *ref) +{ + cFYI(1, ("DFS: ref path: %s", ref->path_name)); + cFYI(1, ("DFS: node path: %s", ref->node_name)); + cFYI(1, ("DFS: fl: %hd, srv_type: %hd", ref->flags, ref->server_type)); + cFYI(1, ("DFS: ref_flags: %hd, path_consumed: %hd", ref->ref_flag, + ref->PathConsumed)); +} + + +static void* +cifs_dfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd) +{ + struct dfs_info3_param *referrals = NULL; + unsigned int num_referrals = 0; + struct cifs_sb_info *cifs_sb; + struct cifsSesInfo *ses; + char *full_path = NULL; + int xid, i; + int rc = 0; + struct vfsmount *mnt = ERR_PTR(-ENOENT); + + cFYI(1, ("in %s", __FUNCTION__)); + BUG_ON(IS_ROOT(dentry)); + + xid = GetXid(); + + dput(nd->dentry); + nd->dentry = dget(dentry); + + cifs_sb = CIFS_SB(dentry->d_inode->i_sb); + ses = cifs_sb->tcon->ses; + + if (!ses) { + rc = -EINVAL; + goto out_err; + } + + full_path = build_full_dfs_path_from_dentry(dentry); + if (full_path == NULL) { + rc = -ENOMEM; + goto out_err; + } + + rc = get_dfs_path(xid, ses , full_path, cifs_sb->local_nls, + &num_referrals, &referrals, + cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); + + for (i = 0; i < num_referrals; i++) { + dump_referral(referrals+i); + /* connect to a storage node */ + if (referrals[i].flags & DFSREF_STORAGE_SERVER) { + int len; + len = strlen(referrals[i].node_name); + if (len < 2) { + cERROR(1, ("%s: Net Address path too short: %s", + __FUNCTION__, referrals[i].node_name)); + rc = -EINVAL; + goto out_err; + } + mnt = cifs_dfs_do_refmount(nd->mnt, nd->dentry, + referrals[i].node_name); + cFYI(1, ("%s: cifs_dfs_do_refmount:%s , mnt:%p", + __FUNCTION__, + referrals[i].node_name, mnt)); + + /* complete mount procedure if we accured submount */ + if (!IS_ERR(mnt)) + break; + } + } + + /* we need it cause for() above could exit without valid submount */ + rc = PTR_ERR(mnt); + if (IS_ERR(mnt)) + goto out_err; + + nd->mnt->mnt_flags |= MNT_SHRINKABLE; + rc = add_mount_helper(mnt, nd, &cifs_dfs_automount_list); + +out: + FreeXid(xid); + free_dfs_info_array(referrals, num_referrals); + kfree(full_path); + cFYI(1, ("leaving %s" , __FUNCTION__)); + return ERR_PTR(rc); +out_err: + path_release(nd); + goto out; +} + +struct inode_operations cifs_dfs_referral_inode_operations = { + .follow_link = cifs_dfs_follow_mountpoint, +}; + diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 93e107883a61..e9f4ec701092 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -471,6 +471,10 @@ static void cifs_umount_begin(struct vfsmount *vfsmnt, int flags) struct cifs_sb_info *cifs_sb; struct cifsTconInfo *tcon; +#ifdef CONFIG_CIFS_DFS_UPCALL + dfs_shrink_umount_helper(vfsmnt); +#endif /* CONFIG CIFS_DFS_UPCALL */ + if (!(flags & MNT_FORCE)) return; cifs_sb = CIFS_SB(vfsmnt->mnt_sb); diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h index 2e68126d07eb..195b14de5567 100644 --- a/fs/cifs/cifsfs.h +++ b/fs/cifs/cifsfs.h @@ -61,6 +61,10 @@ extern int cifs_setattr(struct dentry *, struct iattr *); extern const struct inode_operations cifs_file_inode_ops; extern const struct inode_operations cifs_symlink_inode_ops; +extern struct list_head cifs_dfs_automount_list; +extern struct inode_operations cifs_dfs_referral_inode_operations; + + /* Functions related to files and directories */ extern const struct file_operations cifs_file_ops; diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 7093cb4b0212..aaaf748f6a26 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -102,6 +102,9 @@ extern int mode_to_acl(struct inode *inode, const char *path, __u64); extern int cifs_mount(struct super_block *, struct cifs_sb_info *, char *, const char *); extern int cifs_umount(struct super_block *, struct cifs_sb_info *); +#ifdef CONFIG_CIFS_DFS_UPCALL +extern void dfs_shrink_umount_helper(struct vfsmount *vfsmnt); +#endif void cifs_proc_init(void); void cifs_proc_clean(void); -- cgit v1.2.3 From 366781c19635d861f43ff5e03388a3873ec912d9 Mon Sep 17 00:00:00 2001 From: Steve French Date: Fri, 25 Jan 2008 10:12:41 +0000 Subject: [CIFS] DFS build fixes Also includes a few minor changes suggested by Christoph Signed-off-by: Steve French --- fs/cifs/cifs_dfs_ref.c | 14 ++++++++------ fs/cifs/cifsglob.h | 41 ++++++++++++++++++++++++++++++++--------- fs/cifs/cifsproto.h | 4 ++-- fs/cifs/connect.c | 12 ++++++++---- fs/cifs/dns_resolve.c | 5 +++-- fs/cifs/link.c | 16 ++++++++-------- 6 files changed, 61 insertions(+), 31 deletions(-) diff --git a/fs/cifs/cifs_dfs_ref.c b/fs/cifs/cifs_dfs_ref.c index 15e31f8435ba..413ee2349d1a 100644 --- a/fs/cifs/cifs_dfs_ref.c +++ b/fs/cifs/cifs_dfs_ref.c @@ -3,8 +3,9 @@ * traversal via DFS junction point * * Copyright (c) 2007 Igor Mammedov + * Copyright (C) International Business Machines Corp., 2008 * Author(s): Igor Mammedov (niallain@gmail.com) - * + * Steve French (sfrench@us.ibm.com) * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version @@ -107,8 +108,9 @@ static char *cifs_get_share_name(const char *node_name) * Returns: pointer to new mount options or ERR_PTR. * Caller is responcible for freeing retunrned value if it is not error. */ -char *compose_mount_options(const char *sb_mountdata, const char *ref_unc, - char **devname) +static char *compose_mount_options(const char *sb_mountdata, + const char *ref_unc, + char **devname) { int rc; char *mountdata; @@ -188,13 +190,13 @@ compose_mount_options_out: } -struct vfsmount *cifs_dfs_do_refmount(const struct vfsmount *mnt_parent, +static struct vfsmount *cifs_dfs_do_refmount(const struct vfsmount *mnt_parent, struct dentry *dentry, char *ref_unc) { struct cifs_sb_info *cifs_sb; struct vfsmount *mnt; char *mountdata; - char *devname; + char *devname = NULL; cifs_sb = CIFS_SB(dentry->d_inode->i_sb); mountdata = compose_mount_options(cifs_sb->mountdata, @@ -278,7 +280,7 @@ static int add_mount_helper(struct vfsmount *newmnt, struct nameidata *nd, return err; } -void dump_referral(const struct dfs_info3_param *ref) +static void dump_referral(const struct dfs_info3_param *ref) { cFYI(1, ("DFS: ref path: %s", ref->path_name)); cFYI(1, ("DFS: node path: %s", ref->node_name)); diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 1fde2197ad76..5d32d8ddc82e 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -1,7 +1,7 @@ /* * fs/cifs/cifsglob.h * - * Copyright (C) International Business Machines Corp., 2002,2007 + * Copyright (C) International Business Machines Corp., 2002,2008 * Author(s): Steve French (sfrench@us.ibm.com) * Jeremy Allison (jra@samba.org) * @@ -69,14 +69,6 @@ #define XATTR_DOS_ATTRIB "user.DOSATTRIB" #endif -/* - * This information is kept on every Server we know about. - * - * Some things to note: - * - */ -#define SERVER_NAME_LEN_WITH_NULL (SERVER_NAME_LENGTH + 1) - /* * CIFS vfs client Status information (based on what we know.) */ @@ -460,6 +452,37 @@ struct dir_notify_req { struct file *pfile; }; +struct dfs_info3_param { + int flags; /* DFSREF_REFERRAL_SERVER, DFSREF_STORAGE_SERVER*/ + int PathConsumed; + int server_type; + int ref_flag; + char *path_name; + char *node_name; +}; + +static inline void free_dfs_info_param(struct dfs_info3_param *param) +{ + if (param) { + kfree(param->path_name); + kfree(param->node_name); + kfree(param); + } +} + +static inline void free_dfs_info_array(struct dfs_info3_param *param, + int number_of_items) +{ + int i; + if ((number_of_items == 0) || (param == NULL)) + return; + for (i = 0; i < number_of_items; i++) { + kfree(param[i].path_name); + kfree(param[i].node_name); + } + kfree(param); +} + #define MID_FREE 0 #define MID_REQUEST_ALLOCATED 1 #define MID_REQUEST_SUBMITTED 2 diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index aaaf748f6a26..2f09f565a3d9 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -1,7 +1,7 @@ /* * fs/cifs/cifsproto.h * - * 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 @@ -156,7 +156,7 @@ extern 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, + struct dfs_info3_param **preferrals, int remap); extern void reset_cifs_unix_caps(int xid, struct cifsTconInfo *tcon, struct super_block *sb, struct smb_vol *vol); diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index db3746c891b5..65d0ba72e78f 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -1,7 +1,7 @@ /* * 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 @@ -1410,7 +1410,7 @@ 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; + struct dfs_info3_param *referrals = NULL; unsigned int num_referrals; int rc = 0; @@ -1429,12 +1429,14 @@ connect_to_dfs_path(int xid, struct cifsSesInfo *pSesInfo, 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; + unsigned char *targetUNCs; *pnum_referrals = 0; + *preferrals = NULL; if (pSesInfo->ipc_tid == 0) { temp_unc = kmalloc(2 /* for slashes */ + @@ -1454,8 +1456,10 @@ get_dfs_path(int xid, struct cifsSesInfo *pSesInfo, const char *old_path, kfree(temp_unc); } if (rc == 0) - rc = CIFSGetDFSRefer(xid, pSesInfo, old_path, preferrals, + rc = CIFSGetDFSRefer(xid, pSesInfo, old_path, &targetUNCs, pnum_referrals, nls_codepage, remap); + /* BB map targetUNCs to dfs_info3 structures, here or + in CIFSGetDFSRefer BB */ return rc; } diff --git a/fs/cifs/dns_resolve.c b/fs/cifs/dns_resolve.c index 777a086abd6f..ef7f43824347 100644 --- a/fs/cifs/dns_resolve.c +++ b/fs/cifs/dns_resolve.c @@ -64,13 +64,14 @@ struct key_type key_type_dns_resolver = { * return 0 on success */ int -dns_resolve_server_name_to_ip(const char *unc, char **ip_addr) { +dns_resolve_server_name_to_ip(const char *unc, char **ip_addr) +{ int rc = -EAGAIN; struct key *rkey; char *name; int len; - if ((!ip_addr) || (!unc)) + if (!ip_addr || !unc) return -EINVAL; /* search for server name delimiter */ diff --git a/fs/cifs/link.c b/fs/cifs/link.c index 11f265726db7..1d6fb01b8e6d 100644 --- a/fs/cifs/link.c +++ b/fs/cifs/link.c @@ -1,7 +1,7 @@ /* * fs/cifs/link.c * - * Copyright (C) International Business Machines Corp., 2002,2003 + * 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 @@ -236,8 +236,6 @@ cifs_readlink(struct dentry *direntry, char __user *pBuffer, int buflen) char *full_path = NULL; char *tmp_path = NULL; char *tmpbuffer; - unsigned char *referrals = NULL; - unsigned int num_referrals = 0; int len; __u16 fid; @@ -297,8 +295,11 @@ cifs_readlink(struct dentry *direntry, char __user *pBuffer, int buflen) cFYI(1, ("Error closing junction point " "(open for ioctl)")); } + /* BB unwind this long, nested function, or remove BB */ if (rc == -EIO) { /* Query if DFS Junction */ + unsigned int num_referrals = 0; + struct dfs_info3_param *refs = NULL; tmp_path = kmalloc(MAX_TREE_SIZE + MAX_PATHCONF + 1, GFP_KERNEL); @@ -310,7 +311,7 @@ cifs_readlink(struct dentry *direntry, char __user *pBuffer, int buflen) rc = get_dfs_path(xid, pTcon->ses, tmp_path, cifs_sb->local_nls, - &num_referrals, &referrals, + &num_referrals, &refs, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); cFYI(1, ("Get DFS for %s rc = %d ", @@ -320,14 +321,13 @@ cifs_readlink(struct dentry *direntry, char __user *pBuffer, int buflen) else { cFYI(1, ("num referral: %d", num_referrals)); - if (referrals) { - cFYI(1,("referral string: %s", referrals)); + if (refs && refs->path_name) { strncpy(tmpbuffer, - referrals, + refs->path_name, len-1); } } - kfree(referrals); + kfree(refs); kfree(tmp_path); } /* BB add code like else decode referrals -- cgit v1.2.3