diff options
-rw-r--r-- | fs/cifs/cifs_debug.c | 33 | ||||
-rw-r--r-- | fs/cifs/cifsfs.c | 3 | ||||
-rw-r--r-- | fs/cifs/cifsfs.h | 4 | ||||
-rw-r--r-- | fs/cifs/cifsglob.h | 27 | ||||
-rw-r--r-- | fs/cifs/cifsproto.h | 9 | ||||
-rw-r--r-- | fs/cifs/cifssmb.c | 98 | ||||
-rw-r--r-- | fs/cifs/connect.c | 63 | ||||
-rw-r--r-- | fs/cifs/dfs_cache.c | 140 | ||||
-rw-r--r-- | fs/cifs/dfs_cache.h | 5 | ||||
-rw-r--r-- | fs/cifs/file.c | 5 | ||||
-rw-r--r-- | fs/cifs/inode.c | 37 | ||||
-rw-r--r-- | fs/cifs/link.c | 13 | ||||
-rw-r--r-- | fs/cifs/smb1ops.c | 9 | ||||
-rw-r--r-- | fs/cifs/smb2ops.c | 309 | ||||
-rw-r--r-- | fs/cifs/smb2pdu.c | 72 | ||||
-rw-r--r-- | fs/cifs/smb2pdu.h | 71 | ||||
-rw-r--r-- | fs/cifs/smb2status.h | 3480 | ||||
-rw-r--r-- | fs/cifs/smbdirect.c | 292 | ||||
-rw-r--r-- | fs/cifs/smbdirect.h | 19 | ||||
-rw-r--r-- | fs/cifs/smbfsctl.h | 29 | ||||
-rw-r--r-- | fs/cifs/transport.c | 48 |
21 files changed, 2582 insertions, 2184 deletions
diff --git a/fs/cifs/cifs_debug.c b/fs/cifs/cifs_debug.c index 13c1288b04a7..6a69f11aacf7 100644 --- a/fs/cifs/cifs_debug.c +++ b/fs/cifs/cifs_debug.c @@ -312,12 +312,10 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v) atomic_read(&server->smbd_conn->send_credits), atomic_read(&server->smbd_conn->receive_credits), server->smbd_conn->receive_credit_target); - seq_printf(m, "\nPending send_pending: %x send_payload_pending:" - " %x smbd_send_pending: %x smbd_recv_pending: %x", + seq_printf(m, "\nPending send_pending: %x " + "send_payload_pending: %x", atomic_read(&server->smbd_conn->send_pending), - atomic_read(&server->smbd_conn->send_payload_pending), - server->smbd_conn->smbd_send_pending, - server->smbd_conn->smbd_recv_pending); + atomic_read(&server->smbd_conn->send_payload_pending)); seq_printf(m, "\nReceive buffers count_receive_queue: %x " "count_empty_packet_queue: %x", server->smbd_conn->count_receive_queue, @@ -334,6 +332,12 @@ skip_rdma: #endif seq_printf(m, "\nNumber of credits: %d Dialect 0x%x", server->credits, server->dialect); + if (server->compress_algorithm == SMB3_COMPRESS_LZNT1) + seq_printf(m, " COMPRESS_LZNT1"); + else if (server->compress_algorithm == SMB3_COMPRESS_LZ77) + seq_printf(m, " COMPRESS_LZ77"); + else if (server->compress_algorithm == SMB3_COMPRESS_LZ77_HUFF) + seq_printf(m, " COMPRESS_LZ77_HUFF"); if (server->sign) seq_printf(m, " signed"); if (server->posix_ext_supported) @@ -462,8 +466,13 @@ static ssize_t cifs_stats_proc_write(struct file *file, server = list_entry(tmp1, struct TCP_Server_Info, tcp_ses_list); #ifdef CONFIG_CIFS_STATS2 - for (i = 0; i < NUMBER_OF_SMB2_COMMANDS; i++) + for (i = 0; i < NUMBER_OF_SMB2_COMMANDS; i++) { + atomic_set(&server->num_cmds[i], 0); atomic_set(&server->smb2slowcmd[i], 0); + server->time_per_cmd[i] = 0; + server->slowest_cmd[i] = 0; + server->fastest_cmd[0] = 0; + } #endif /* CONFIG_CIFS_STATS2 */ list_for_each(tmp2, &server->smb_ses_list) { ses = list_entry(tmp2, struct cifs_ses, @@ -531,9 +540,19 @@ static int cifs_stats_proc_show(struct seq_file *m, void *v) server = list_entry(tmp1, struct TCP_Server_Info, tcp_ses_list); #ifdef CONFIG_CIFS_STATS2 + seq_puts(m, "\nTotal time spent processing by command. Time "); + seq_printf(m, "units are jiffies (%d per second)\n", HZ); + seq_puts(m, " SMB3 CMD\tNumber\tTotal Time\tFastest\tSlowest\n"); + seq_puts(m, " --------\t------\t----------\t-------\t-------\n"); + for (j = 0; j < NUMBER_OF_SMB2_COMMANDS; j++) + seq_printf(m, " %d\t\t%d\t%llu\t\t%u\t%u\n", j, + atomic_read(&server->num_cmds[j]), + server->time_per_cmd[j], + server->fastest_cmd[j], + server->slowest_cmd[j]); for (j = 0; j < NUMBER_OF_SMB2_COMMANDS; j++) if (atomic_read(&server->smb2slowcmd[j])) - seq_printf(m, "%d slow responses from %s for command %d\n", + seq_printf(m, " %d slow responses from %s for command %d\n", atomic_read(&server->smb2slowcmd[j]), server->hostname, j); #endif /* STATS2 */ diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 877174761efb..b1a5fcfa3ce1 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -483,6 +483,8 @@ cifs_show_options(struct seq_file *s, struct dentry *root) seq_puts(s, ",seal"); if (tcon->nocase) seq_puts(s, ",nocase"); + if (tcon->local_lease) + seq_puts(s, ",locallease"); if (tcon->retry) seq_puts(s, ",hard"); else @@ -984,6 +986,7 @@ const struct inode_operations cifs_file_inode_ops = { .getattr = cifs_getattr, .permission = cifs_permission, .listxattr = cifs_listxattr, + .fiemap = cifs_fiemap, }; const struct inode_operations cifs_symlink_inode_ops = { diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h index 5c0298b9998f..aea005703785 100644 --- a/fs/cifs/cifsfs.h +++ b/fs/cifs/cifsfs.h @@ -84,6 +84,8 @@ extern int cifs_revalidate_mapping(struct inode *inode); extern int cifs_zap_mapping(struct inode *inode); extern int cifs_getattr(const struct path *, struct kstat *, u32, unsigned int); extern int cifs_setattr(struct dentry *, struct iattr *); +extern int cifs_fiemap(struct inode *, struct fiemap_extent_info *, u64 start, + u64 len); extern const struct inode_operations cifs_file_inode_ops; extern const struct inode_operations cifs_symlink_inode_ops; @@ -150,5 +152,5 @@ extern long cifs_ioctl(struct file *filep, unsigned int cmd, unsigned long arg); extern const struct export_operations cifs_export_ops; #endif /* CONFIG_CIFS_NFSD_EXPORT */ -#define CIFS_VERSION "2.19" +#define CIFS_VERSION "2.20" #endif /* _CIFSFS_H */ diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 585ad3207cb1..33c251b408aa 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -355,7 +355,8 @@ struct smb_version_operations { struct cifs_sb_info *); /* query symlink target */ int (*query_symlink)(const unsigned int, struct cifs_tcon *, - const char *, char **, struct cifs_sb_info *); + struct cifs_sb_info *, const char *, + char **, bool); /* open a file for non-posix mounts */ int (*open)(const unsigned int, struct cifs_open_parms *, __u32 *, FILE_ALL_INFO *); @@ -493,6 +494,9 @@ struct smb_version_operations { char *full_path, umode_t mode, dev_t device_number); + /* version specific fiemap implementation */ + int (*fiemap)(struct cifs_tcon *tcon, struct cifsFileInfo *, + struct fiemap_extent_info *, u64, u64); }; struct smb_version_values { @@ -596,6 +600,10 @@ struct smb_vol { unsigned int max_credits; /* smb3 max_credits 10 < credits < 60000 */ }; +/** + * CIFS superblock mount flags (mnt_cifs_flags) to consider when + * trying to reuse existing superblock for a new mount + */ #define CIFS_MOUNT_MASK (CIFS_MOUNT_NO_PERM | CIFS_MOUNT_SET_UID | \ CIFS_MOUNT_SERVER_INUM | CIFS_MOUNT_DIRECT_IO | \ CIFS_MOUNT_NO_XATTR | CIFS_MOUNT_MAP_SPECIAL_CHR | \ @@ -606,8 +614,13 @@ struct smb_vol { CIFS_MOUNT_NOPOSIXBRL | CIFS_MOUNT_NOSSYNC | \ CIFS_MOUNT_FSCACHE | CIFS_MOUNT_MF_SYMLINKS | \ CIFS_MOUNT_MULTIUSER | CIFS_MOUNT_STRICT_IO | \ - CIFS_MOUNT_CIFS_BACKUPUID | CIFS_MOUNT_CIFS_BACKUPGID) + CIFS_MOUNT_CIFS_BACKUPUID | CIFS_MOUNT_CIFS_BACKUPGID | \ + CIFS_MOUNT_NO_DFS) +/** + * Generic VFS superblock mount flags (s_flags) to consider when + * trying to reuse existing superblock for a new mount + */ #define CIFS_MS_MASK (SB_RDONLY | SB_MANDLOCK | SB_NOEXEC | SB_NOSUID | \ SB_NODEV | SB_SYNCHRONOUS) @@ -714,10 +727,15 @@ struct TCP_Server_Info { #ifdef CONFIG_CIFS_STATS2 atomic_t in_send; /* requests trying to send */ atomic_t num_waiters; /* blocked waiting to get in sendrecv */ + atomic_t num_cmds[NUMBER_OF_SMB2_COMMANDS]; /* total requests by cmd */ atomic_t smb2slowcmd[NUMBER_OF_SMB2_COMMANDS]; /* count resps > 1 sec */ + __u64 time_per_cmd[NUMBER_OF_SMB2_COMMANDS]; /* total time per cmd */ + __u32 slowest_cmd[NUMBER_OF_SMB2_COMMANDS]; + __u32 fastest_cmd[NUMBER_OF_SMB2_COMMANDS]; #endif /* STATS2 */ unsigned int max_read; unsigned int max_write; + __le16 compress_algorithm; __le16 cipher_type; /* save initital negprot hash */ __u8 preauth_sha_hash[SMB2_PREAUTH_HASH_SIZE]; @@ -1673,11 +1691,11 @@ static inline bool is_retryable_error(int error) /* Type of Request to SendReceive2 */ #define CIFS_BLOCKING_OP 1 /* operation can block */ -#define CIFS_ASYNC_OP 2 /* do not wait for response */ +#define CIFS_NON_BLOCKING 2 /* do not block waiting for credits */ #define CIFS_TIMEOUT_MASK 0x003 /* only one of above set in req */ #define CIFS_LOG_ERROR 0x010 /* log NT STATUS if non-zero */ #define CIFS_LARGE_BUF_OP 0x020 /* large request buffer */ -#define CIFS_NO_RESP 0x040 /* no response buffer required */ +#define CIFS_NO_RSP_BUF 0x040 /* no response buffer required */ /* Type of request operation */ #define CIFS_ECHO_OP 0x080 /* echo request */ @@ -1687,6 +1705,7 @@ static inline bool is_retryable_error(int error) #define CIFS_HAS_CREDITS 0x0400 /* already has credits */ #define CIFS_TRANSFORM_REQ 0x0800 /* transform request before sending */ +#define CIFS_NO_SRV_RSP 0x1000 /* there is no server response */ /* Security Flags: indicate type of session setup needed */ #define CIFSSEC_MAY_SIGN 0x00001 diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index 4f96b3b00a7a..e23234207fc2 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -526,12 +526,21 @@ extern int E_md4hash(const unsigned char *passwd, unsigned char *p16, const struct nls_table *codepage); extern int SMBencrypt(unsigned char *passwd, const unsigned char *c8, unsigned char *p24); + +extern int +cifs_setup_volume_info(struct smb_vol *volume_info, char *mount_data, + const char *devname, bool is_smb3); extern void cifs_cleanup_volume_info_contents(struct smb_vol *volume_info); extern struct TCP_Server_Info * cifs_find_tcp_session(struct smb_vol *vol); +extern void cifs_put_smb_ses(struct cifs_ses *ses); + +extern struct cifs_ses * +cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb_vol *volume_info); + void cifs_readdata_release(struct kref *refcount); int cifs_async_readv(struct cifs_readdata *rdata); int cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid); diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index f43747c062a7..1fbd92843a73 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -860,7 +860,7 @@ CIFSSMBEcho(struct TCP_Server_Info *server) iov[1].iov_base = (char *)smb + 4; rc = cifs_call_async(server, &rqst, NULL, cifs_echo_callback, NULL, - server, CIFS_ASYNC_OP | CIFS_ECHO_OP, NULL); + server, CIFS_NON_BLOCKING | CIFS_ECHO_OP, NULL); if (rc) cifs_dbg(FYI, "Echo request failed: %d\n", rc); @@ -2508,8 +2508,8 @@ int cifs_lockv(const unsigned int xid, struct cifs_tcon *tcon, iov[1].iov_len = (num_unlock + num_lock) * sizeof(LOCKING_ANDX_RANGE); cifs_stats_inc(&tcon->stats.cifs_stats.num_locks); - rc = SendReceive2(xid, tcon->ses, iov, 2, &resp_buf_type, CIFS_NO_RESP, - &rsp_iov); + rc = SendReceive2(xid, tcon->ses, iov, 2, &resp_buf_type, + CIFS_NO_RSP_BUF, &rsp_iov); cifs_small_buf_release(pSMB); if (rc) cifs_dbg(FYI, "Send error in cifs_lockv = %d\n", rc); @@ -2540,7 +2540,7 @@ CIFSSMBLock(const unsigned int xid, struct cifs_tcon *tcon, if (lockType == LOCKING_ANDX_OPLOCK_RELEASE) { /* no response expected */ - flags = CIFS_ASYNC_OP | CIFS_OBREAK_OP; + flags = CIFS_NO_SRV_RSP | CIFS_NON_BLOCKING | CIFS_OBREAK_OP; pSMB->Timeout = 0; } else if (waitFlag) { flags = CIFS_BLOCKING_OP; /* blocking operation, no timeout */ @@ -6567,93 +6567,3 @@ SetEARetry: return rc; } #endif - -#ifdef CONFIG_CIFS_DNOTIFY_EXPERIMENTAL /* BB unused temporarily */ -/* - * Years ago the kernel added a "dnotify" function for Samba server, - * to allow network clients (such as Windows) to display updated - * lists of files in directory listings automatically when - * files are added by one user when another user has the - * same directory open on their desktop. The Linux cifs kernel - * client hooked into the kernel side of this interface for - * the same reason, but ironically when the VFS moved from - * "dnotify" to "inotify" it became harder to plug in Linux - * network file system clients (the most obvious use case - * for notify interfaces is when multiple users can update - * the contents of the same directory - exactly what network - * file systems can do) although the server (Samba) could - * still use it. For the short term we leave the worker - * function ifdeffed out (below) until inotify is fixed - * in the VFS to make it easier to plug in network file - * system clients. If inotify turns out to be permanently - * incompatible for network fs clients, we could instead simply - * expose this config flag by adding a future cifs (and smb2) notify ioctl. - */ -int CIFSSMBNotify(const unsigned int xid, struct cifs_tcon *tcon, - const int notify_subdirs, const __u16 netfid, - __u32 filter, struct file *pfile, int multishot, - const struct nls_table *nls_codepage) -{ - int rc = 0; - struct smb_com_transaction_change_notify_req *pSMB = NULL; - struct smb_com_ntransaction_change_notify_rsp *pSMBr = NULL; - struct dir_notify_req *dnotify_req; - int bytes_returned; - - cifs_dbg(FYI, "In CIFSSMBNotify for file handle %d\n", (int)netfid); - rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon, (void **) &pSMB, - (void **) &pSMBr); - if (rc) - return rc; - - pSMB->TotalParameterCount = 0 ; - pSMB->TotalDataCount = 0; - pSMB->MaxParameterCount = cpu_to_le32(2); - pSMB->MaxDataCount = cpu_to_le32(CIFSMaxBufSize & 0xFFFFFF00); - pSMB->MaxSetupCount = 4; - pSMB->Reserved = 0; - pSMB->ParameterOffset = 0; - pSMB->DataCount = 0; - pSMB->DataOffset = 0; - pSMB->SetupCount = 4; /* single byte does not need le conversion */ - pSMB->SubCommand = cpu_to_le16(NT_TRANSACT_NOTIFY_CHANGE); - pSMB->ParameterCount = pSMB->TotalParameterCount; - if (notify_subdirs) - pSMB->WatchTree = 1; /* one byte - no le conversion needed */ - pSMB->Reserved2 = 0; - pSMB->CompletionFilter = cpu_to_le32(filter); - pSMB->Fid = netfid; /* file handle always le */ - pSMB->ByteCount = 0; - - rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, - (struct smb_hdr *)pSMBr, &bytes_returned, - CIFS_ASYNC_OP); - if (rc) { - cifs_dbg(FYI, "Error in Notify = %d\n", rc); - } else { - /* Add file to outstanding requests */ - /* BB change to kmem cache alloc */ - dnotify_req = kmalloc( - sizeof(struct dir_notify_req), - GFP_KERNEL); - if (dnotify_req) { - dnotify_req->Pid = pSMB->hdr.Pid; - dnotify_req->PidHigh = pSMB->hdr.PidHigh; - dnotify_req->Mid = pSMB->hdr.Mid; - dnotify_req->Tid = pSMB->hdr.Tid; - dnotify_req->Uid = pSMB->hdr.Uid; - dnotify_req->netfid = netfid; - dnotify_req->pfile = pfile; - dnotify_req->filter = filter; - dnotify_req->multishot = multishot; - spin_lock(&GlobalMid_Lock); - list_add_tail(&dnotify_req->lhead, - &GlobalDnotifyReqList); - spin_unlock(&GlobalMid_Lock); - } else - rc = -ENOMEM; - } - cifs_buf_release(pSMB); - return rc; -} -#endif /* was needed for dnotify, and will be needed for inotify when VFS fix */ diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 4c0e44489f21..084756cfdaee 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -323,8 +323,6 @@ static int ip_connect(struct TCP_Server_Info *server); static int generic_ip_connect(struct TCP_Server_Info *server); static void tlink_rb_insert(struct rb_root *root, struct tcon_link *new_tlink); static void cifs_prune_tlinks(struct work_struct *work); -static int cifs_setup_volume_info(struct smb_vol *volume_info, char *mount_data, - const char *devname, bool is_smb3); static char *extract_hostname(const char *unc); /* @@ -530,21 +528,6 @@ cifs_reconnect(struct TCP_Server_Info *server) /* do not want to be sending data on a socket we are freeing */ cifs_dbg(FYI, "%s: tearing down socket\n", __func__); mutex_lock(&server->srv_mutex); - if (server->ssocket) { - cifs_dbg(FYI, "State: 0x%x Flags: 0x%lx\n", - server->ssocket->state, server->ssocket->flags); - kernel_sock_shutdown(server->ssocket, SHUT_WR); - cifs_dbg(FYI, "Post shutdown state: 0x%x Flags: 0x%lx\n", - server->ssocket->state, server->ssocket->flags); - sock_release(server->ssocket); - server->ssocket = NULL; - } - server->sequence_number = 0; - server->session_estab = false; - kfree(server->session_key.response); - server->session_key.response = NULL; - server->session_key.len = 0; - server->lstrp = jiffies; /* mark submitted MIDs for retry and issue callback */ INIT_LIST_HEAD(&retry_list); @@ -557,7 +540,6 @@ cifs_reconnect(struct TCP_Server_Info *server) list_move(&mid_entry->qhead, &retry_list); } spin_unlock(&GlobalMid_Lock); - mutex_unlock(&server->srv_mutex); cifs_dbg(FYI, "%s: issuing mid callbacks\n", __func__); list_for_each_safe(tmp, tmp2, &retry_list) { @@ -566,6 +548,25 @@ cifs_reconnect(struct TCP_Server_Info *server) mid_entry->callback(mid_entry); } + if (server->ssocket) { + cifs_dbg(FYI, "State: 0x%x Flags: 0x%lx\n", + server->ssocket->state, server->ssocket->flags); + kernel_sock_shutdown(server->ssocket, SHUT_WR); + cifs_dbg(FYI, "Post shutdown state: 0x%x Flags: 0x%lx\n", + server->ssocket->state, server->ssocket->flags); + sock_release(server->ssocket); + server->ssocket = NULL; + } else if (cifs_rdma_enabled(server)) + smbd_destroy(server); + server->sequence_number = 0; + server->session_estab = false; + kfree(server->session_key.response); + server->session_key.response = NULL; + server->session_key.len = 0; + server->lstrp = jiffies; + + mutex_unlock(&server->srv_mutex); + do { try_to_freeze(); @@ -931,10 +932,8 @@ static void clean_demultiplex_info(struct TCP_Server_Info *server) wake_up_all(&server->request_q); /* give those requests time to exit */ msleep(125); - if (cifs_rdma_enabled(server) && server->smbd_conn) { - smbd_destroy(server->smbd_conn); - server->smbd_conn = NULL; - } + if (cifs_rdma_enabled(server)) + smbd_destroy(server); if (server->ssocket) { sock_release(server->ssocket); server->ssocket = NULL; @@ -2904,8 +2903,7 @@ cifs_find_smb_ses(struct TCP_Server_Info *server, struct smb_vol *vol) return NULL; } -static void -cifs_put_smb_ses(struct cifs_ses *ses) +void cifs_put_smb_ses(struct cifs_ses *ses) { unsigned int rc, xid; struct TCP_Server_Info *server = ses->server; @@ -3082,7 +3080,7 @@ cifs_set_cifscreds(struct smb_vol *vol __attribute__((unused)), * already got a server reference (server refcount +1). See * cifs_get_tcon() for refcount explanations. */ -static struct cifs_ses * +struct cifs_ses * cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb_vol *volume_info) { int rc = -ENOMEM; @@ -4389,7 +4387,7 @@ static int mount_do_dfs_failover(const char *path, } #endif -static int +int cifs_setup_volume_info(struct smb_vol *volume_info, char *mount_data, const char *devname, bool is_smb3) { @@ -4543,7 +4541,7 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *vol) struct cifs_tcon *tcon = NULL; struct TCP_Server_Info *server; char *root_path = NULL, *full_path = NULL; - char *old_mountdata; + char *old_mountdata, *origin_mountdata = NULL; int count; rc = mount_get_conns(vol, cifs_sb, &xid, &server, &ses, &tcon); @@ -4602,6 +4600,14 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *vol) goto error; } + /* Save DFS root volume information for DFS refresh worker */ + origin_mountdata = kstrndup(cifs_sb->mountdata, + strlen(cifs_sb->mountdata), GFP_KERNEL); + if (!origin_mountdata) { + rc = -ENOMEM; + goto error; + } + if (cifs_sb->mountdata != old_mountdata) { /* If we were redirected, reconnect to new target server */ mount_put_conns(cifs_sb, xid, server, ses, tcon); @@ -4710,7 +4716,7 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *vol) } spin_unlock(&cifs_tcp_ses_lock); - rc = dfs_cache_add_vol(vol, cifs_sb->origin_fullpath); + rc = dfs_cache_add_vol(origin_mountdata, vol, cifs_sb->origin_fullpath); if (rc) { kfree(cifs_sb->origin_fullpath); goto error; @@ -4728,6 +4734,7 @@ out: error: kfree(full_path); kfree(root_path); + kfree(origin_mountdata); mount_put_conns(cifs_sb, xid, server, ses, tcon); return rc; } diff --git a/fs/cifs/dfs_cache.c b/fs/cifs/dfs_cache.c index 09b7d0d4f6e4..85dc89d3a203 100644 --- a/fs/cifs/dfs_cache.c +++ b/fs/cifs/dfs_cache.c @@ -2,7 +2,7 @@ /* * DFS referral cache routines * - * Copyright (c) 2018 Paulo Alcantara <palcantara@suse.de> + * Copyright (c) 2018-2019 Paulo Alcantara <palcantara@suse.de> */ #include <linux/rcupdate.h> @@ -52,6 +52,7 @@ static struct kmem_cache *dfs_cache_slab __read_mostly; struct dfs_cache_vol_info { char *vi_fullpath; struct smb_vol vi_vol; + char *vi_mntdata; struct list_head vi_list; }; @@ -529,6 +530,7 @@ static inline void free_vol(struct dfs_cache_vol_info *vi) { list_del(&vi->vi_list); kfree(vi->vi_fullpath); + kfree(vi->vi_mntdata); cifs_cleanup_volume_info_contents(&vi->vi_vol); kfree(vi); } @@ -1139,17 +1141,18 @@ err_free_username: * dfs_cache_add_vol - add a cifs volume during mount() that will be handled by * DFS cache refresh worker. * + * @mntdata: mount data. * @vol: cifs volume. * @fullpath: origin full path. * * Return zero if volume was set up correctly, otherwise non-zero. */ -int dfs_cache_add_vol(struct smb_vol *vol, const char *fullpath) +int dfs_cache_add_vol(char *mntdata, struct smb_vol *vol, const char *fullpath) { int rc; struct dfs_cache_vol_info *vi; - if (!vol || !fullpath) + if (!vol || !fullpath || !mntdata) return -EINVAL; cifs_dbg(FYI, "%s: fullpath: %s\n", __func__, fullpath); @@ -1168,6 +1171,8 @@ int dfs_cache_add_vol(struct smb_vol *vol, const char *fullpath) if (rc) goto err_free_fullpath; + vi->vi_mntdata = mntdata; + mutex_lock(&dfs_cache.dc_lock); list_add_tail(&vi->vi_list, &dfs_cache.dc_vol_list); mutex_unlock(&dfs_cache.dc_lock); @@ -1275,8 +1280,102 @@ static void get_tcons(struct TCP_Server_Info *server, struct list_head *head) spin_unlock(&cifs_tcp_ses_lock); } +static inline bool is_dfs_link(const char *path) +{ + char *s; + + s = strchr(path + 1, '\\'); + if (!s) + return false; + return !!strchr(s + 1, '\\'); +} + +static inline char *get_dfs_root(const char *path) +{ + char *s, *npath; + + s = strchr(path + 1, '\\'); + if (!s) + return ERR_PTR(-EINVAL); + + s = strchr(s + 1, '\\'); + if (!s) + return ERR_PTR(-EINVAL); + + npath = kstrndup(path, s - path, GFP_KERNEL); + if (!npath) + return ERR_PTR(-ENOMEM); + + return npath; +} + +/* Find root SMB session out of a DFS link path */ +static struct cifs_ses *find_root_ses(struct dfs_cache_vol_info *vi, + struct cifs_tcon *tcon, const char *path) +{ + char *rpath; + int rc; + struct dfs_info3_param ref = {0}; + char *mdata = NULL, *devname = NULL; + bool is_smb3 = tcon->ses->server->vals->header_preamble_size == 0; + struct TCP_Server_Info *server; + struct cifs_ses *ses; + struct smb_vol vol; + + rpath = get_dfs_root(path); + if (IS_ERR(rpath)) + return ERR_CAST(rpath); + + memset(&vol, 0, sizeof(vol)); + + rc = dfs_cache_noreq_find(rpath, &ref, NULL); + if (rc) { + ses = ERR_PTR(rc); + goto out; + } + + mdata = cifs_compose_mount_options(vi->vi_mntdata, rpath, &ref, + &devname); + free_dfs_info_param(&ref); + + if (IS_ERR(mdata)) { + ses = ERR_CAST(mdata); + mdata = NULL; + goto out; + } + + rc = cifs_setup_volume_info(&vol, mdata, devname, is_smb3); + kfree(devname); + + if (rc) { + ses = ERR_PTR(rc); + goto out; + } + + server = cifs_find_tcp_session(&vol); + if (IS_ERR_OR_NULL(server)) { + ses = ERR_PTR(-EHOSTDOWN); + goto out; + } + if (server->tcpStatus != CifsGood) { + cifs_put_tcp_session(server, 0); + ses = ERR_PTR(-EHOSTDOWN); + goto out; + } + + ses = cifs_get_smb_ses(server, &vol); + +out: + cifs_cleanup_volume_info_contents(&vol); + kfree(mdata); + kfree(rpath); + + return ses; +} + /* Refresh DFS cache entry from a given tcon */ -static void do_refresh_tcon(struct dfs_cache *dc, struct cifs_tcon *tcon) +static void do_refresh_tcon(struct dfs_cache *dc, struct dfs_cache_vol_info *vi, + struct cifs_tcon *tcon) { int rc = 0; unsigned int xid; @@ -1285,6 +1384,7 @@ static void do_refresh_tcon(struct dfs_cache *dc, struct cifs_tcon *tcon) struct dfs_cache_entry *ce; struct dfs_info3_param *refs = NULL; int numrefs = 0; + struct cifs_ses *root_ses = NULL, *ses; xid = get_xid(); @@ -1306,13 +1406,24 @@ static void do_refresh_tcon(struct dfs_cache *dc, struct cifs_tcon *tcon) if (!cache_entry_expired(ce)) goto out; - if (unlikely(!tcon->ses->server->ops->get_dfs_refer)) { + /* If it's a DFS Link, then use root SMB session for refreshing it */ + if (is_dfs_link(npath)) { + ses = root_ses = find_root_ses(vi, tcon, npath); + if (IS_ERR(ses)) { + rc = PTR_ERR(ses); + root_ses = NULL; + goto out; + } + } else { + ses = tcon->ses; + } + + if (unlikely(!ses->server->ops->get_dfs_refer)) { rc = -EOPNOTSUPP; } else { - rc = tcon->ses->server->ops->get_dfs_refer(xid, tcon->ses, path, - &refs, &numrefs, - dc->dc_nlsc, - tcon->remap); + rc = ses->server->ops->get_dfs_refer(xid, ses, path, &refs, + &numrefs, dc->dc_nlsc, + tcon->remap); if (!rc) { mutex_lock(&dfs_cache_list_lock); ce = __update_cache_entry(npath, refs, numrefs); @@ -1323,9 +1434,11 @@ static void do_refresh_tcon(struct dfs_cache *dc, struct cifs_tcon *tcon) rc = PTR_ERR(ce); } } - if (rc) - cifs_dbg(FYI, "%s: failed to update expired entry\n", __func__); + out: + if (root_ses) + cifs_put_smb_ses(root_ses); + free_xid(xid); free_normalized_path(path, npath); } @@ -1333,9 +1446,6 @@ out: /* * Worker that will refresh DFS cache based on lowest TTL value from a DFS * referral. - * - * FIXME: ensure that all requests are sent to DFS root for refreshing the - * cache. */ static void refresh_cache_worker(struct work_struct *work) { @@ -1356,7 +1466,7 @@ static void refresh_cache_worker(struct work_struct *work) goto next; get_tcons(server, &list); list_for_each_entry_safe(tcon, ntcon, &list, ulist) { - do_refresh_tcon(dc, tcon); + do_refresh_tcon(dc, vi, tcon); list_del_init(&tcon->ulist); cifs_put_tcon(tcon); } diff --git a/fs/cifs/dfs_cache.h b/fs/cifs/dfs_cache.h index 22f366514f3a..76c732943f5f 100644 --- a/fs/cifs/dfs_cache.h +++ b/fs/cifs/dfs_cache.h @@ -2,7 +2,7 @@ /* * DFS referral cache routines * - * Copyright (c) 2018 Paulo Alcantara <palcantara@suse.de> + * Copyright (c) 2018-2019 Paulo Alcantara <palcantara@suse.de> */ #ifndef _CIFS_DFS_CACHE_H @@ -43,7 +43,8 @@ dfs_cache_noreq_update_tgthint(const char *path, extern int dfs_cache_get_tgt_referral(const char *path, const struct dfs_cache_tgt_iterator *it, struct dfs_info3_param *ref); -extern int dfs_cache_add_vol(struct smb_vol *vol, const char *fullpath); +extern int dfs_cache_add_vol(char *mntdata, struct smb_vol *vol, + const char *fullpath); extern int dfs_cache_update_vol(const char *fullpath, struct TCP_Server_Info *server); extern void dfs_cache_del_vol(const char *fullpath); diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 7037a137fa53..ce9a5be11df5 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -2443,7 +2443,6 @@ int cifs_strict_fsync(struct file *file, loff_t start, loff_t end, rc = file_write_and_wait_range(file, start, end); if (rc) return rc; - inode_lock(inode); xid = get_xid(); @@ -2468,7 +2467,6 @@ int cifs_strict_fsync(struct file *file, loff_t start, loff_t end, } free_xid(xid); - inode_unlock(inode); return rc; } @@ -2480,12 +2478,10 @@ int cifs_fsync(struct file *file, loff_t start, loff_t end, int datasync) struct TCP_Server_Info *server; struct cifsFileInfo *smbfile = file->private_data; struct cifs_sb_info *cifs_sb = CIFS_FILE_SB(file); - struct inode *inode = file->f_mapping->host; rc = file_write_and_wait_range(file, start, end); if (rc) return rc; - inode_lock(inode); xid = get_xid(); @@ -2502,7 +2498,6 @@ int cifs_fsync(struct file *file, loff_t start, loff_t end, int datasync) } free_xid(xid); - inode_unlock(inode); return rc; } diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index 538fd7d807e4..d7cc62252634 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -2116,6 +2116,43 @@ int cifs_getattr(const struct path *path, struct kstat *stat, return rc; } +int cifs_fiemap(struct inode *inode, struct fiemap_extent_info *fei, u64 start, + u64 len) +{ + struct cifsInodeInfo *cifs_i = CIFS_I(inode); + struct cifs_sb_info *cifs_sb = CIFS_SB(cifs_i->vfs_inode.i_sb); + struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); + struct TCP_Server_Info *server = tcon->ses->server; + struct cifsFileInfo *cfile; + int rc; + + /* + * We need to be sure that all dirty pages are written as they + * might fill holes on the server. + */ + if (!CIFS_CACHE_READ(CIFS_I(inode)) && inode->i_mapping && + inode->i_mapping->nrpages != 0) { + rc = filemap_fdatawait(inode->i_mapping); + if (rc) { + mapping_set_error(inode->i_mapping, rc); + return rc; + } + } + + cfile = find_readable_file(cifs_i, false); + if (cfile == NULL) + return -EINVAL; + + if (server->ops->fiemap) { + rc = server->ops->fiemap(tcon, cfile, fei, start, len); + cifsFileInfo_put(cfile); + return rc; + } + + cifsFileInfo_put(cfile); + return -ENOTSUPP; +} + static int cifs_truncate_page(struct address_space *mapping, loff_t from) { pgoff_t index = from >> PAGE_SHIFT; diff --git a/fs/cifs/link.c b/fs/cifs/link.c index 62216dc8f9f5..b736acd3917b 100644 --- a/fs/cifs/link.c +++ b/fs/cifs/link.c @@ -648,9 +648,16 @@ cifs_get_link(struct dentry *direntry, struct inode *inode, rc = query_mf_symlink(xid, tcon, cifs_sb, full_path, &target_path); - if (rc != 0 && server->ops->query_symlink) - rc = server->ops->query_symlink(xid, tcon, full_path, - &target_path, cifs_sb); + if (rc != 0 && server->ops->query_symlink) { + struct cifsInodeInfo *cifsi = CIFS_I(inode); + bool reparse_point = false; + + if (cifsi->cifsAttrs & ATTR_REPARSE) + reparse_point = true; + + rc = server->ops->query_symlink(xid, tcon, cifs_sb, full_path, + &target_path, reparse_point); + } kfree(full_path); free_xid(xid); diff --git a/fs/cifs/smb1ops.c b/fs/cifs/smb1ops.c index c711f1f39bf2..c4e75afa3258 100644 --- a/fs/cifs/smb1ops.c +++ b/fs/cifs/smb1ops.c @@ -950,8 +950,8 @@ cifs_unix_dfs_readlink(const unsigned int xid, struct cifs_tcon *tcon, static int cifs_query_symlink(const unsigned int xid, struct cifs_tcon *tcon, - const char *full_path, char **tar |