@@ -1803,140 +1803,226 @@ smb2_ioctl_query_info(const unsigned int xid,
18031803 return rc ;
18041804}
18051805
1806+ /**
1807+ * calc_chunk_count - calculates the number chunks to be filled in the Chunks[]
1808+ * array of struct copychunk_ioctl
1809+ *
1810+ * @tcon: destination file tcon
1811+ * @bytes_left: how many bytes are left to copy
1812+ *
1813+ * Return: maximum number of chunks with which Chunks[] can be filled.
1814+ */
1815+ static inline u32
1816+ calc_chunk_count (struct cifs_tcon * tcon , u64 bytes_left )
1817+ {
1818+ u32 max_chunks = READ_ONCE (tcon -> max_chunks );
1819+ u32 max_bytes_copy = READ_ONCE (tcon -> max_bytes_copy );
1820+ u32 max_bytes_chunk = READ_ONCE (tcon -> max_bytes_chunk );
1821+ u64 need ;
1822+ u32 allowed ;
1823+
1824+ if (!max_bytes_chunk || !max_bytes_copy || !max_chunks )
1825+ return 0 ;
1826+
1827+ /* chunks needed for the remaining bytes */
1828+ need = DIV_ROUND_UP_ULL (bytes_left , max_bytes_chunk );
1829+ /* chunks allowed per cc request */
1830+ allowed = DIV_ROUND_UP (max_bytes_copy , max_bytes_chunk );
1831+
1832+ return (u32 )umin (need , umin (max_chunks , allowed ));
1833+ }
1834+
1835+ /**
1836+ * smb2_copychunk_range - server-side copy of data range
1837+ *
1838+ * @xid: transaction id
1839+ * @src_file: source file
1840+ * @dst_file: destination file
1841+ * @src_off: source file byte offset
1842+ * @len: number of bytes to copy
1843+ * @dst_off: destination file byte offset
1844+ *
1845+ * Obtains a resume key for @src_file and issues FSCTL_SRV_COPYCHUNK_WRITE
1846+ * IOCTLs, splitting the request into chunks limited by tcon->max_*.
1847+ *
1848+ * Return: @len on success; negative errno on failure.
1849+ */
18061850static ssize_t
18071851smb2_copychunk_range (const unsigned int xid ,
1808- struct cifsFileInfo * srcfile ,
1809- struct cifsFileInfo * trgtfile , u64 src_off ,
1810- u64 len , u64 dest_off )
1852+ struct cifsFileInfo * src_file ,
1853+ struct cifsFileInfo * dst_file ,
1854+ u64 src_off ,
1855+ u64 len ,
1856+ u64 dst_off )
18111857{
1812- int rc ;
1813- unsigned int ret_data_len ;
1814- struct copychunk_ioctl * pcchunk ;
1815- struct copychunk_ioctl_rsp * retbuf = NULL ;
1858+ int rc = 0 ;
1859+ unsigned int ret_data_len = 0 ;
1860+ struct copychunk_ioctl * cc_req = NULL ;
1861+ struct copychunk_ioctl_rsp * cc_rsp = NULL ;
18161862 struct cifs_tcon * tcon ;
1817- int chunks_copied = 0 ;
1818- bool chunk_sizes_updated = false;
1819- ssize_t bytes_written , total_bytes_written = 0 ;
1863+ struct copychunk * chunk ;
1864+ u32 chunks , chunk_count , chunk_bytes ;
1865+ u32 copy_bytes , copy_bytes_left ;
1866+ u32 chunks_written , bytes_written ;
1867+ u64 total_bytes_left = len ;
1868+ u64 src_off_prev , dst_off_prev ;
1869+ u32 retries = 0 ;
1870+
1871+ tcon = tlink_tcon (dst_file -> tlink );
1872+
1873+ trace_smb3_copychunk_enter (xid , src_file -> fid .volatile_fid ,
1874+ dst_file -> fid .volatile_fid , tcon -> tid ,
1875+ tcon -> ses -> Suid , src_off , dst_off , len );
1876+
1877+ retry :
1878+ chunk_count = calc_chunk_count (tcon , total_bytes_left );
1879+ if (!chunk_count ) {
1880+ rc = - EOPNOTSUPP ;
1881+ goto out ;
1882+ }
18201883
1821- pcchunk = kmalloc (sizeof (struct copychunk_ioctl ), GFP_KERNEL );
1822- if (pcchunk == NULL )
1823- return - ENOMEM ;
1884+ cc_req = kzalloc (struct_size (cc_req , Chunks , chunk_count ), GFP_KERNEL );
1885+ if (!cc_req ) {
1886+ rc = - ENOMEM ;
1887+ goto out ;
1888+ }
18241889
1825- cifs_dbg (FYI , "%s: about to call request res key\n" , __func__ );
18261890 /* Request a key from the server to identify the source of the copy */
1827- rc = SMB2_request_res_key (xid , tlink_tcon (srcfile -> tlink ),
1828- srcfile -> fid .persistent_fid ,
1829- srcfile -> fid .volatile_fid , pcchunk );
1891+ rc = SMB2_request_res_key (xid ,
1892+ tlink_tcon (src_file -> tlink ),
1893+ src_file -> fid .persistent_fid ,
1894+ src_file -> fid .volatile_fid ,
1895+ cc_req );
18301896
1831- /* Note: request_res_key sets res_key null only if rc !=0 */
1897+ /* Note: request_res_key sets res_key null only if rc != 0 */
18321898 if (rc )
1833- goto cchunk_out ;
1899+ goto out ;
18341900
1835- /* For now array only one chunk long, will make more flexible later */
1836- pcchunk -> ChunkCount = cpu_to_le32 (1 );
1837- pcchunk -> Reserved = 0 ;
1838- pcchunk -> Reserved2 = 0 ;
1901+ while (total_bytes_left > 0 ) {
18391902
1840- tcon = tlink_tcon (trgtfile -> tlink );
1903+ /* Store previous offsets to allow rewind */
1904+ src_off_prev = src_off ;
1905+ dst_off_prev = dst_off ;
18411906
1842- trace_smb3_copychunk_enter (xid , srcfile -> fid .volatile_fid ,
1843- trgtfile -> fid .volatile_fid , tcon -> tid ,
1844- tcon -> ses -> Suid , src_off , dest_off , len );
1907+ chunks = 0 ;
1908+ copy_bytes = 0 ;
1909+ copy_bytes_left = umin (total_bytes_left , tcon -> max_bytes_copy );
1910+ while (copy_bytes_left > 0 && chunks < chunk_count ) {
1911+ chunk = & cc_req -> Chunks [chunks ++ ];
18451912
1846- while (len > 0 ) {
1847- pcchunk -> SourceOffset = cpu_to_le64 (src_off );
1848- pcchunk -> TargetOffset = cpu_to_le64 (dest_off );
1849- pcchunk -> Length =
1850- cpu_to_le32 (min_t (u64 , len , tcon -> max_bytes_chunk ));
1913+ chunk -> SourceOffset = cpu_to_le64 (src_off );
1914+ chunk -> TargetOffset = cpu_to_le64 (dst_off );
1915+
1916+ chunk_bytes = umin (copy_bytes_left , tcon -> max_bytes_chunk );
1917+
1918+ chunk -> Length = cpu_to_le32 (chunk_bytes );
1919+ /* Buffer is zeroed, no need to set chunk->Reserved = 0 */
1920+
1921+ src_off += chunk_bytes ;
1922+ dst_off += chunk_bytes ;
1923+
1924+ copy_bytes_left -= chunk_bytes ;
1925+ copy_bytes += chunk_bytes ;
1926+ }
1927+
1928+ cc_req -> ChunkCount = cpu_to_le32 (chunks );
1929+ /* Buffer is zeroed, no need to set cc_req->Reserved = 0 */
18511930
18521931 /* Request server copy to target from src identified by key */
1853- kfree (retbuf );
1854- retbuf = NULL ;
1855- rc = SMB2_ioctl (xid , tcon , trgtfile -> fid .persistent_fid ,
1856- trgtfile -> fid .volatile_fid , FSCTL_SRV_COPYCHUNK_WRITE ,
1857- (char * )pcchunk , sizeof (struct copychunk_ioctl ),
1858- CIFSMaxBufSize , (char * * )& retbuf , & ret_data_len );
1932+ kfree (cc_rsp );
1933+ cc_rsp = NULL ;
1934+ rc = SMB2_ioctl (xid , tcon , dst_file -> fid .persistent_fid ,
1935+ dst_file -> fid .volatile_fid , FSCTL_SRV_COPYCHUNK_WRITE ,
1936+ (char * )cc_req , struct_size (cc_req , Chunks , chunks ),
1937+ CIFSMaxBufSize , (char * * )& cc_rsp , & ret_data_len );
1938+
1939+ if (rc && rc != - EINVAL )
1940+ goto out ;
1941+
1942+ if (unlikely (ret_data_len != sizeof (* cc_rsp ))) {
1943+ cifs_tcon_dbg (VFS , "Copychunk invalid response: size %u/%zu\n" ,
1944+ ret_data_len , sizeof (* cc_rsp ));
1945+ rc = - EIO ;
1946+ goto out ;
1947+ }
1948+
1949+ bytes_written = le32_to_cpu (cc_rsp -> TotalBytesWritten );
1950+ chunks_written = le32_to_cpu (cc_rsp -> ChunksWritten );
1951+ chunk_bytes = le32_to_cpu (cc_rsp -> ChunkBytesWritten );
1952+
18591953 if (rc == 0 ) {
1860- if (ret_data_len !=
1861- sizeof (struct copychunk_ioctl_rsp )) {
1862- cifs_tcon_dbg (VFS , "Invalid cchunk response size\n" );
1863- rc = - EIO ;
1864- goto cchunk_out ;
1865- }
1866- if (retbuf -> TotalBytesWritten == 0 ) {
1867- cifs_dbg (FYI , "no bytes copied\n" );
1868- rc = - EIO ;
1869- goto cchunk_out ;
1870- }
1871- /*
1872- * Check if server claimed to write more than we asked
1873- */
1874- if (le32_to_cpu (retbuf -> TotalBytesWritten ) >
1875- le32_to_cpu (pcchunk -> Length )) {
1876- cifs_tcon_dbg (VFS , "Invalid copy chunk response\n" );
1954+ /* Check if server claimed to write more than we asked */
1955+ if (unlikely (!bytes_written || bytes_written > copy_bytes ||
1956+ !chunks_written || chunks_written > chunks )) {
1957+ cifs_tcon_dbg (VFS , "Copychunk invalid response: bytes written %u/%u, chunks written %u/%u\n" ,
1958+ bytes_written , copy_bytes , chunks_written , chunks );
18771959 rc = - EIO ;
1878- goto cchunk_out ;
1960+ goto out ;
18791961 }
1880- if (le32_to_cpu (retbuf -> ChunksWritten ) != 1 ) {
1881- cifs_tcon_dbg (VFS , "Invalid num chunks written\n" );
1882- rc = - EIO ;
1883- goto cchunk_out ;
1962+
1963+ /* Partial write: rewind */
1964+ if (bytes_written < copy_bytes ) {
1965+ u32 delta = copy_bytes - bytes_written ;
1966+
1967+ src_off -= delta ;
1968+ dst_off -= delta ;
18841969 }
1885- chunks_copied ++ ;
1886-
1887- bytes_written = le32_to_cpu (retbuf -> TotalBytesWritten );
1888- src_off += bytes_written ;
1889- dest_off += bytes_written ;
1890- len -= bytes_written ;
1891- total_bytes_written += bytes_written ;
1892-
1893- cifs_dbg (FYI , "Chunks %d PartialChunk %d Total %zu\n" ,
1894- le32_to_cpu (retbuf -> ChunksWritten ),
1895- le32_to_cpu (retbuf -> ChunkBytesWritten ),
1896- bytes_written );
1897- trace_smb3_copychunk_done (xid , srcfile -> fid .volatile_fid ,
1898- trgtfile -> fid .volatile_fid , tcon -> tid ,
1899- tcon -> ses -> Suid , src_off , dest_off , len );
1900- } else if (rc == - EINVAL ) {
1901- if (ret_data_len != sizeof (struct copychunk_ioctl_rsp ))
1902- goto cchunk_out ;
1903-
1904- cifs_dbg (FYI , "MaxChunks %d BytesChunk %d MaxCopy %d\n" ,
1905- le32_to_cpu (retbuf -> ChunksWritten ),
1906- le32_to_cpu (retbuf -> ChunkBytesWritten ),
1907- le32_to_cpu (retbuf -> TotalBytesWritten ));
19081970
1909- /*
1910- * Check if this is the first request using these sizes,
1911- * (ie check if copy succeed once with original sizes
1912- * and check if the server gave us different sizes after
1913- * we already updated max sizes on previous request).
1914- * if not then why is the server returning an error now
1915- */
1916- if ((chunks_copied != 0 ) || chunk_sizes_updated )
1917- goto cchunk_out ;
1918-
1919- /* Check that server is not asking us to grow size */
1920- if (le32_to_cpu (retbuf -> ChunkBytesWritten ) <
1921- tcon -> max_bytes_chunk )
1922- tcon -> max_bytes_chunk =
1923- le32_to_cpu (retbuf -> ChunkBytesWritten );
1924- else
1925- goto cchunk_out ; /* server gave us bogus size */
1971+ total_bytes_left -= bytes_written ;
1972+ continue ;
1973+ }
19261974
1927- /* No need to change MaxChunks since already set to 1 */
1928- chunk_sizes_updated = true;
1929- } else
1930- goto cchunk_out ;
1975+ /*
1976+ * Check if server is not asking us to reduce size.
1977+ *
1978+ * Note: As per MS-SMB2 2.2.32.1, the values returned
1979+ * in cc_rsp are not strictly lower than what existed
1980+ * before.
1981+ */
1982+ if (bytes_written < tcon -> max_bytes_copy ) {
1983+ cifs_tcon_dbg (FYI , "Copychunk MaxBytesCopy updated: %u -> %u\n" ,
1984+ tcon -> max_bytes_copy , bytes_written );
1985+ tcon -> max_bytes_copy = bytes_written ;
1986+ }
1987+
1988+ if (chunks_written < tcon -> max_chunks ) {
1989+ cifs_tcon_dbg (FYI , "Copychunk MaxChunks updated: %u -> %u\n" ,
1990+ tcon -> max_chunks , chunks_written );
1991+ tcon -> max_chunks = chunks_written ;
1992+ }
1993+
1994+ if (chunk_bytes < tcon -> max_bytes_chunk ) {
1995+ cifs_tcon_dbg (FYI , "Copychunk MaxBytesChunk updated: %u -> %u\n" ,
1996+ tcon -> max_bytes_chunk , chunk_bytes );
1997+ tcon -> max_bytes_chunk = chunk_bytes ;
1998+ }
1999+
2000+ /* reset to last offsets */
2001+ if (retries ++ < 2 ) {
2002+ src_off = src_off_prev ;
2003+ dst_off = dst_off_prev ;
2004+ kfree (cc_req );
2005+ cc_req = NULL ;
2006+ goto retry ;
2007+ }
2008+
2009+ break ;
19312010 }
19322011
1933- cchunk_out :
1934- kfree (pcchunk );
1935- kfree (retbuf );
1936- if (rc )
2012+ out :
2013+ kfree (cc_req );
2014+ kfree (cc_rsp );
2015+ if (rc ) {
2016+ trace_smb3_copychunk_err (xid , src_file -> fid .volatile_fid ,
2017+ dst_file -> fid .volatile_fid , tcon -> tid ,
2018+ tcon -> ses -> Suid , src_off , dst_off , len , rc );
19372019 return rc ;
1938- else
1939- return total_bytes_written ;
2020+ } else {
2021+ trace_smb3_copychunk_done (xid , src_file -> fid .volatile_fid ,
2022+ dst_file -> fid .volatile_fid , tcon -> tid ,
2023+ tcon -> ses -> Suid , src_off , dst_off , len );
2024+ return len ;
2025+ }
19402026}
19412027
19422028static int
0 commit comments