diff options
| -rw-r--r-- | fs/cifs/cifsglob.h | 4 | ||||
| -rw-r--r-- | fs/cifs/inode.c | 13 | ||||
| -rw-r--r-- | fs/cifs/smb2ops.c | 1 | ||||
| -rw-r--r-- | fs/cifs/smb2pdu.c | 153 | ||||
| -rw-r--r-- | fs/cifs/smb2proto.h | 4 | ||||
| -rw-r--r-- | fs/cifs/trace.h | 3 |
6 files changed, 175 insertions, 3 deletions
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 9dd5f1a3d64b..bd78da59a4fd 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h | |||
| @@ -315,6 +315,10 @@ struct smb_version_operations { | |||
| 315 | /* send echo request */ | 315 | /* send echo request */ |
| 316 | int (*echo)(struct TCP_Server_Info *); | 316 | int (*echo)(struct TCP_Server_Info *); |
| 317 | /* create directory */ | 317 | /* create directory */ |
| 318 | int (*posix_mkdir)(const unsigned int xid, struct inode *inode, | ||
| 319 | umode_t mode, struct cifs_tcon *tcon, | ||
| 320 | const char *full_path, | ||
| 321 | struct cifs_sb_info *cifs_sb); | ||
| 318 | int (*mkdir)(const unsigned int, struct cifs_tcon *, const char *, | 322 | int (*mkdir)(const unsigned int, struct cifs_tcon *, const char *, |
| 319 | struct cifs_sb_info *); | 323 | struct cifs_sb_info *); |
| 320 | /* set info on created directory */ | 324 | /* set info on created directory */ |
diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index f4697f548a39..a2cfb33e85c1 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c | |||
| @@ -1575,6 +1575,17 @@ int cifs_mkdir(struct inode *inode, struct dentry *direntry, umode_t mode) | |||
| 1575 | goto mkdir_out; | 1575 | goto mkdir_out; |
| 1576 | } | 1576 | } |
| 1577 | 1577 | ||
| 1578 | server = tcon->ses->server; | ||
| 1579 | |||
| 1580 | #ifdef CONFIG_CIFS_SMB311 | ||
| 1581 | if ((server->ops->posix_mkdir) && (tcon->posix_extensions)) { | ||
| 1582 | rc = server->ops->posix_mkdir(xid, inode, mode, tcon, full_path, | ||
| 1583 | cifs_sb); | ||
| 1584 | d_drop(direntry); /* for time being always refresh inode info */ | ||
| 1585 | goto mkdir_out; | ||
| 1586 | } | ||
| 1587 | #endif /* SMB311 */ | ||
| 1588 | |||
| 1578 | if (cap_unix(tcon->ses) && (CIFS_UNIX_POSIX_PATH_OPS_CAP & | 1589 | if (cap_unix(tcon->ses) && (CIFS_UNIX_POSIX_PATH_OPS_CAP & |
| 1579 | le64_to_cpu(tcon->fsUnixInfo.Capability))) { | 1590 | le64_to_cpu(tcon->fsUnixInfo.Capability))) { |
| 1580 | rc = cifs_posix_mkdir(inode, direntry, mode, full_path, cifs_sb, | 1591 | rc = cifs_posix_mkdir(inode, direntry, mode, full_path, cifs_sb, |
| @@ -1583,8 +1594,6 @@ int cifs_mkdir(struct inode *inode, struct dentry *direntry, umode_t mode) | |||
| 1583 | goto mkdir_out; | 1594 | goto mkdir_out; |
| 1584 | } | 1595 | } |
| 1585 | 1596 | ||
| 1586 | server = tcon->ses->server; | ||
| 1587 | |||
| 1588 | if (!server->ops->mkdir) { | 1597 | if (!server->ops->mkdir) { |
| 1589 | rc = -ENOSYS; | 1598 | rc = -ENOSYS; |
| 1590 | goto mkdir_out; | 1599 | goto mkdir_out; |
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index b2390e9a6843..badcfb2f3c22 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c | |||
| @@ -3313,6 +3313,7 @@ struct smb_version_operations smb311_operations = { | |||
| 3313 | .set_compression = smb2_set_compression, | 3313 | .set_compression = smb2_set_compression, |
| 3314 | .mkdir = smb2_mkdir, | 3314 | .mkdir = smb2_mkdir, |
| 3315 | .mkdir_setinfo = smb2_mkdir_setinfo, | 3315 | .mkdir_setinfo = smb2_mkdir_setinfo, |
| 3316 | .posix_mkdir = smb311_posix_mkdir, | ||
| 3316 | .rmdir = smb2_rmdir, | 3317 | .rmdir = smb2_rmdir, |
| 3317 | .unlink = smb2_unlink, | 3318 | .unlink = smb2_unlink, |
| 3318 | .rename = smb2_rename_path, | 3319 | .rename = smb2_rename_path, |
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 7daf38ab814a..810b85787c91 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c | |||
| @@ -1911,6 +1911,159 @@ alloc_path_with_tree_prefix(__le16 **out_path, int *out_size, int *out_len, | |||
| 1911 | return 0; | 1911 | return 0; |
| 1912 | } | 1912 | } |
| 1913 | 1913 | ||
| 1914 | #ifdef CONFIG_CIFS_SMB311 | ||
| 1915 | int smb311_posix_mkdir(const unsigned int xid, struct inode *inode, | ||
| 1916 | umode_t mode, struct cifs_tcon *tcon, | ||
| 1917 | const char *full_path, | ||
| 1918 | struct cifs_sb_info *cifs_sb) | ||
| 1919 | { | ||
| 1920 | struct smb_rqst rqst; | ||
| 1921 | struct smb2_create_req *req; | ||
| 1922 | struct smb2_create_rsp *rsp; | ||
| 1923 | struct TCP_Server_Info *server; | ||
| 1924 | struct cifs_ses *ses = tcon->ses; | ||
| 1925 | struct kvec iov[3]; /* make sure at least one for each open context */ | ||
| 1926 | struct kvec rsp_iov = {NULL, 0}; | ||
| 1927 | int resp_buftype; | ||
| 1928 | int uni_path_len; | ||
| 1929 | __le16 *copy_path = NULL; | ||
| 1930 | int copy_size; | ||
| 1931 | int rc = 0; | ||
| 1932 | unsigned int n_iov = 2; | ||
| 1933 | __u32 file_attributes = 0; | ||
| 1934 | char *pc_buf = NULL; | ||
| 1935 | int flags = 0; | ||
| 1936 | unsigned int total_len; | ||
| 1937 | __le16 *path = cifs_convert_path_to_utf16(full_path, cifs_sb); | ||
| 1938 | |||
| 1939 | if (!path) | ||
| 1940 | return -ENOMEM; | ||
| 1941 | |||
| 1942 | cifs_dbg(FYI, "mkdir\n"); | ||
| 1943 | |||
| 1944 | if (ses && (ses->server)) | ||
| 1945 | server = ses->server; | ||
| 1946 | else | ||
| 1947 | return -EIO; | ||
| 1948 | |||
| 1949 | rc = smb2_plain_req_init(SMB2_CREATE, tcon, (void **) &req, &total_len); | ||
| 1950 | |||
| 1951 | if (rc) | ||
| 1952 | return rc; | ||
| 1953 | |||
| 1954 | if (smb3_encryption_required(tcon)) | ||
| 1955 | flags |= CIFS_TRANSFORM_REQ; | ||
| 1956 | |||
| 1957 | |||
| 1958 | req->ImpersonationLevel = IL_IMPERSONATION; | ||
| 1959 | req->DesiredAccess = cpu_to_le32(FILE_WRITE_ATTRIBUTES); | ||
| 1960 | /* File attributes ignored on open (used in create though) */ | ||
| 1961 | req->FileAttributes = cpu_to_le32(file_attributes); | ||
| 1962 | req->ShareAccess = FILE_SHARE_ALL_LE; | ||
| 1963 | req->CreateDisposition = cpu_to_le32(FILE_CREATE); | ||
| 1964 | req->CreateOptions = cpu_to_le32(CREATE_NOT_FILE); | ||
| 1965 | |||
| 1966 | iov[0].iov_base = (char *)req; | ||
| 1967 | /* -1 since last byte is buf[0] which is sent below (path) */ | ||
| 1968 | iov[0].iov_len = total_len - 1; | ||
| 1969 | |||
| 1970 | req->NameOffset = cpu_to_le16(sizeof(struct smb2_create_req)); | ||
| 1971 | |||
| 1972 | /* [MS-SMB2] 2.2.13 NameOffset: | ||
| 1973 | * If SMB2_FLAGS_DFS_OPERATIONS is set in the Flags field of | ||
| 1974 | * the SMB2 header, the file name includes a prefix that will | ||
| 1975 | * be processed during DFS name normalization as specified in | ||
| 1976 | * section 3.3.5.9. Otherwise, the file name is relative to | ||
| 1977 | * the share that is identified by the TreeId in the SMB2 | ||
| 1978 | * header. | ||
| 1979 | */ | ||
| 1980 | if (tcon->share_flags & SHI1005_FLAGS_DFS) { | ||
| 1981 | int name_len; | ||
| 1982 | |||
| 1983 | req->sync_hdr.Flags |= SMB2_FLAGS_DFS_OPERATIONS; | ||
| 1984 | rc = alloc_path_with_tree_prefix(©_path, ©_size, | ||
| 1985 | &name_len, | ||
| 1986 | tcon->treeName, path); | ||
| 1987 | if (rc) { | ||
| 1988 | cifs_small_buf_release(req); | ||
| 1989 | return rc; | ||
| 1990 | } | ||
| 1991 | req->NameLength = cpu_to_le16(name_len * 2); | ||
| 1992 | uni_path_len = copy_size; | ||
| 1993 | path = copy_path; | ||
| 1994 | } else { | ||
| 1995 | uni_path_len = (2 * UniStrnlen((wchar_t *)path, PATH_MAX)) + 2; | ||
| 1996 | /* MUST set path len (NameLength) to 0 opening root of share */ | ||
| 1997 | req->NameLength = cpu_to_le16(uni_path_len - 2); | ||
| 1998 | if (uni_path_len % 8 != 0) { | ||
| 1999 | copy_size = roundup(uni_path_len, 8); | ||
| 2000 | copy_path = kzalloc(copy_size, GFP_KERNEL); | ||
| 2001 | if (!copy_path) { | ||
| 2002 | cifs_small_buf_release(req); | ||
| 2003 | return -ENOMEM; | ||
| 2004 | } | ||
| 2005 | memcpy((char *)copy_path, (const char *)path, | ||
| 2006 | uni_path_len); | ||
| 2007 | uni_path_len = copy_size; | ||
| 2008 | path = copy_path; | ||
| 2009 | } | ||
| 2010 | } | ||
| 2011 | |||
| 2012 | iov[1].iov_len = uni_path_len; | ||
| 2013 | iov[1].iov_base = path; | ||
| 2014 | req->RequestedOplockLevel = SMB2_OPLOCK_LEVEL_NONE; | ||
| 2015 | |||
| 2016 | if (tcon->posix_extensions) { | ||
| 2017 | if (n_iov > 2) { | ||
| 2018 | struct create_context *ccontext = | ||
| 2019 | (struct create_context *)iov[n_iov-1].iov_base; | ||
| 2020 | ccontext->Next = | ||
| 2021 | cpu_to_le32(iov[n_iov-1].iov_len); | ||
| 2022 | } | ||
| 2023 | |||
| 2024 | rc = add_posix_context(iov, &n_iov, mode); | ||
| 2025 | if (rc) { | ||
| 2026 | cifs_small_buf_release(req); | ||
| 2027 | kfree(copy_path); | ||
| 2028 | return rc; | ||
| 2029 | } | ||
| 2030 | pc_buf = iov[n_iov-1].iov_base; | ||
| 2031 | } | ||
| 2032 | |||
| 2033 | |||
| 2034 | memset(&rqst, 0, sizeof(struct smb_rqst)); | ||
| 2035 | rqst.rq_iov = iov; | ||
| 2036 | rqst.rq_nvec = n_iov; | ||
| 2037 | |||
| 2038 | rc = cifs_send_recv(xid, ses, &rqst, &resp_buftype, flags, | ||
| 2039 | &rsp_iov); | ||
| 2040 | |||
| 2041 | cifs_small_buf_release(req); | ||
| 2042 | rsp = (struct smb2_create_rsp *)rsp_iov.iov_base; | ||
| 2043 | |||
| 2044 | if (rc != 0) { | ||
| 2045 | cifs_stats_fail_inc(tcon, SMB2_CREATE_HE); | ||
| 2046 | trace_smb3_posix_mkdir_err(xid, tcon->tid, ses->Suid, | ||
| 2047 | CREATE_NOT_FILE, FILE_WRITE_ATTRIBUTES, rc); | ||
| 2048 | goto smb311_mkdir_exit; | ||
| 2049 | } else | ||
| 2050 | trace_smb3_posix_mkdir_done(xid, rsp->PersistentFileId, tcon->tid, | ||
| 2051 | ses->Suid, CREATE_NOT_FILE, | ||
| 2052 | FILE_WRITE_ATTRIBUTES); | ||
| 2053 | |||
| 2054 | SMB2_close(xid, tcon, rsp->PersistentFileId, rsp->VolatileFileId); | ||
| 2055 | |||
