Skip to content

Commit 2099306

Browse files
committed
Merge tag '6.7-rc4-smb3-client-fixes' of git://git.samba.org/sfrench/cifs-2.6
Pull smb client fixes from Steve French: "Six smb3 client fixes: - Fixes for copy_file_range and clone (cache invalidation and file size), also addresses an xfstest failure - Fix to return proper error if REMAP_FILE_DEDUP set (also fixes xfstest generic/304) - Fix potential null pointer reference with DFS - Multichannel fix addressing (reverting an earlier patch) some of the problems with enabling/disabling channels dynamically Still working on a followon multichannel fix to address another issue found in reconnect testing that will send next week" * tag '6.7-rc4-smb3-client-fixes' of git://git.samba.org/sfrench/cifs-2.6: cifs: reconnect worker should take reference on server struct unconditionally Revert "cifs: reconnect work should have reference on server struct" cifs: Fix non-availability of dedup breaking generic/304 smb: client: fix potential NULL deref in parse_dfs_referrals() cifs: Fix flushing, invalidation and file size with FICLONE cifs: Fix flushing, invalidation and file size with copy_file_range()
2 parents f2e8a57 + 0490919 commit 2099306

File tree

4 files changed

+187
-58
lines changed

4 files changed

+187
-58
lines changed

fs/smb/client/cifsfs.c

Lines changed: 159 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1196,32 +1196,103 @@ const struct inode_operations cifs_symlink_inode_ops = {
11961196
.listxattr = cifs_listxattr,
11971197
};
11981198

1199+
/*
1200+
* Advance the EOF marker to after the source range.
1201+
*/
1202+
static int cifs_precopy_set_eof(struct inode *src_inode, struct cifsInodeInfo *src_cifsi,
1203+
struct cifs_tcon *src_tcon,
1204+
unsigned int xid, loff_t src_end)
1205+
{
1206+
struct cifsFileInfo *writeable_srcfile;
1207+
int rc = -EINVAL;
1208+
1209+
writeable_srcfile = find_writable_file(src_cifsi, FIND_WR_FSUID_ONLY);
1210+
if (writeable_srcfile) {
1211+
if (src_tcon->ses->server->ops->set_file_size)
1212+
rc = src_tcon->ses->server->ops->set_file_size(
1213+
xid, src_tcon, writeable_srcfile,
1214+
src_inode->i_size, true /* no need to set sparse */);
1215+
else
1216+
rc = -ENOSYS;
1217+
cifsFileInfo_put(writeable_srcfile);
1218+
cifs_dbg(FYI, "SetFSize for copychunk rc = %d\n", rc);
1219+
}
1220+
1221+
if (rc < 0)
1222+
goto set_failed;
1223+
1224+
netfs_resize_file(&src_cifsi->netfs, src_end);
1225+
fscache_resize_cookie(cifs_inode_cookie(src_inode), src_end);
1226+
return 0;
1227+
1228+
set_failed:
1229+
return filemap_write_and_wait(src_inode->i_mapping);
1230+
}
1231+
1232+
/*
1233+
* Flush out either the folio that overlaps the beginning of a range in which
1234+
* pos resides or the folio that overlaps the end of a range unless that folio
1235+
* is entirely within the range we're going to invalidate. We extend the flush
1236+
* bounds to encompass the folio.
1237+
*/
1238+
static int cifs_flush_folio(struct inode *inode, loff_t pos, loff_t *_fstart, loff_t *_fend,
1239+
bool first)
1240+
{
1241+
struct folio *folio;
1242+
unsigned long long fpos, fend;
1243+
pgoff_t index = pos / PAGE_SIZE;
1244+
size_t size;
1245+
int rc = 0;
1246+
1247+
folio = filemap_get_folio(inode->i_mapping, index);
1248+
if (IS_ERR(folio))
1249+
return 0;
1250+
1251+
size = folio_size(folio);
1252+
fpos = folio_pos(folio);
1253+
fend = fpos + size - 1;
1254+
*_fstart = min_t(unsigned long long, *_fstart, fpos);
1255+
*_fend = max_t(unsigned long long, *_fend, fend);
1256+
if ((first && pos == fpos) || (!first && pos == fend))
1257+
goto out;
1258+
1259+
rc = filemap_write_and_wait_range(inode->i_mapping, fpos, fend);
1260+
out:
1261+
folio_put(folio);
1262+
return rc;
1263+
}
1264+
11991265
static loff_t cifs_remap_file_range(struct file *src_file, loff_t off,
12001266
struct file *dst_file, loff_t destoff, loff_t len,
12011267
unsigned int remap_flags)
12021268
{
12031269
struct inode *src_inode = file_inode(src_file);
12041270
struct inode *target_inode = file_inode(dst_file);
1271+
struct cifsInodeInfo *src_cifsi = CIFS_I(src_inode);
1272+
struct cifsInodeInfo *target_cifsi = CIFS_I(target_inode);
12051273
struct cifsFileInfo *smb_file_src = src_file->private_data;
1206-
struct cifsFileInfo *smb_file_target;
1207-
struct cifs_tcon *target_tcon;
1274+
struct cifsFileInfo *smb_file_target = dst_file->private_data;
1275+
struct cifs_tcon *target_tcon, *src_tcon;
1276+
unsigned long long destend, fstart, fend, new_size;
12081277
unsigned int xid;
12091278
int rc;
12101279

1211-
if (remap_flags & ~(REMAP_FILE_DEDUP | REMAP_FILE_ADVISORY))
1280+
if (remap_flags & REMAP_FILE_DEDUP)
1281+
return -EOPNOTSUPP;
1282+
if (remap_flags & ~REMAP_FILE_ADVISORY)
12121283
return -EINVAL;
12131284

12141285
cifs_dbg(FYI, "clone range\n");
12151286

12161287
xid = get_xid();
12171288

1218-
if (!src_file->private_data || !dst_file->private_data) {
1289+
if (!smb_file_src || !smb_file_target) {
12191290
rc = -EBADF;
12201291
cifs_dbg(VFS, "missing cifsFileInfo on copy range src file\n");
12211292
goto out;
12221293
}
12231294

1224-
smb_file_target = dst_file->private_data;
1295+
src_tcon = tlink_tcon(smb_file_src->tlink);
12251296
target_tcon = tlink_tcon(smb_file_target->tlink);
12261297

12271298
/*
@@ -1234,20 +1305,63 @@ static loff_t cifs_remap_file_range(struct file *src_file, loff_t off,
12341305
if (len == 0)
12351306
len = src_inode->i_size - off;
12361307

1237-
cifs_dbg(FYI, "about to flush pages\n");
1238-
/* should we flush first and last page first */
1239-
truncate_inode_pages_range(&target_inode->i_data, destoff,
1240-
PAGE_ALIGN(destoff + len)-1);
1308+
cifs_dbg(FYI, "clone range\n");
12411309

1242-
if (target_tcon->ses->server->ops->duplicate_extents)
1310+
/* Flush the source buffer */
1311+
rc = filemap_write_and_wait_range(src_inode->i_mapping, off,
1312+
off + len - 1);
1313+
if (rc)
1314+
goto unlock;
1315+
1316+
/* The server-side copy will fail if the source crosses the EOF marker.
1317+
* Advance the EOF marker after the flush above to the end of the range
1318+
* if it's short of that.
1319+
*/
1320+
if (src_cifsi->netfs.remote_i_size < off + len) {
1321+
rc = cifs_precopy_set_eof(src_inode, src_cifsi, src_tcon, xid, off + len);
1322+
if (rc < 0)
1323+
goto unlock;
1324+
}
1325+
1326+
new_size = destoff + len;
1327+
destend = destoff + len - 1;
1328+
1329+
/* Flush the folios at either end of the destination range to prevent
1330+
* accidental loss of dirty data outside of the range.
1331+
*/
1332+
fstart = destoff;
1333+
fend = destend;
1334+
1335+
rc = cifs_flush_folio(target_inode, destoff, &fstart, &fend, true);
1336+
if (rc)
1337+
goto unlock;
1338+
rc = cifs_flush_folio(target_inode, destend, &fstart, &fend, false);
1339+
if (rc)
1340+
goto unlock;
1341+
1342+
/* Discard all the folios that overlap the destination region. */
1343+
cifs_dbg(FYI, "about to discard pages %llx-%llx\n", fstart, fend);
1344+
truncate_inode_pages_range(&target_inode->i_data, fstart, fend);
1345+
1346+
fscache_invalidate(cifs_inode_cookie(target_inode), NULL,
1347+
i_size_read(target_inode), 0);
1348+
1349+
rc = -EOPNOTSUPP;
1350+
if (target_tcon->ses->server->ops->duplicate_extents) {
12431351
rc = target_tcon->ses->server->ops->duplicate_extents(xid,
12441352
smb_file_src, smb_file_target, off, len, destoff);
1245-
else
1246-
rc = -EOPNOTSUPP;
1353+
if (rc == 0 && new_size > i_size_read(target_inode)) {
1354+
truncate_setsize(target_inode, new_size);
1355+
netfs_resize_file(&target_cifsi->netfs, new_size);
1356+
fscache_resize_cookie(cifs_inode_cookie(target_inode),
1357+
new_size);
1358+
}
1359+
}
12471360

12481361
/* force revalidate of size and timestamps of target file now
12491362
that target is updated on the server */
12501363
CIFS_I(target_inode)->time = 0;
1364+
unlock:
12511365
/* although unlocking in the reverse order from locking is not
12521366
strictly necessary here it is a little cleaner to be consistent */
12531367
unlock_two_nondirectories(src_inode, target_inode);
@@ -1263,10 +1377,12 @@ ssize_t cifs_file_copychunk_range(unsigned int xid,
12631377
{
12641378
struct inode *src_inode = file_inode(src_file);
12651379
struct inode *target_inode = file_inode(dst_file);
1380+
struct cifsInodeInfo *src_cifsi = CIFS_I(src_inode);
12661381
struct cifsFileInfo *smb_file_src;
12671382
struct cifsFileInfo *smb_file_target;
12681383
struct cifs_tcon *src_tcon;
12691384
struct cifs_tcon *target_tcon;
1385+
unsigned long long destend, fstart, fend;
12701386
ssize_t rc;
12711387

12721388
cifs_dbg(FYI, "copychunk range\n");
@@ -1306,13 +1422,41 @@ ssize_t cifs_file_copychunk_range(unsigned int xid,
13061422
if (rc)
13071423
goto unlock;
13081424

1309-
/* should we flush first and last page first */
1310-
truncate_inode_pages(&target_inode->i_data, 0);
1425+
/* The server-side copy will fail if the source crosses the EOF marker.
1426+
* Advance the EOF marker after the flush above to the end of the range
1427+
* if it's short of that.
1428+
*/
1429+
if (src_cifsi->server_eof < off + len) {
1430+
rc = cifs_precopy_set_eof(src_inode, src_cifsi, src_tcon, xid, off + len);
1431+
if (rc < 0)
1432+
goto unlock;
1433+
}
1434+
1435+
destend = destoff + len - 1;
1436+
1437+
/* Flush the folios at either end of the destination range to prevent
1438+
* accidental loss of dirty data outside of the range.
1439+
*/
1440+
fstart = destoff;
1441+
fend = destend;
1442+
1443+
rc = cifs_flush_folio(target_inode, destoff, &fstart, &fend, true);
1444+
if (rc)
1445+
goto unlock;
1446+
rc = cifs_flush_folio(target_inode, destend, &fstart, &fend, false);
1447+
if (rc)
1448+
goto unlock;
1449+
1450+
/* Discard all the folios that overlap the destination region. */
1451+
truncate_inode_pages_range(&target_inode->i_data, fstart, fend);
13111452

13121453
rc = file_modified(dst_file);
1313-
if (!rc)
1454+
if (!rc) {
13141455
rc = target_tcon->ses->server->ops->copychunk_range(xid,
13151456
smb_file_src, smb_file_target, off, len, destoff);
1457+
if (rc > 0 && destoff + rc > i_size_read(target_inode))
1458+
truncate_setsize(target_inode, destoff + rc);
1459+
}
13161460

13171461
file_accessed(src_file);
13181462

fs/smb/client/connect.c

Lines changed: 10 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -402,13 +402,7 @@ static int __cifs_reconnect(struct TCP_Server_Info *server,
402402
spin_unlock(&server->srv_lock);
403403
cifs_swn_reset_server_dstaddr(server);
404404
cifs_server_unlock(server);
405-
406-
/* increase ref count which reconnect work will drop */
407-
spin_lock(&cifs_tcp_ses_lock);
408-
server->srv_count++;
409-
spin_unlock(&cifs_tcp_ses_lock);
410-
if (mod_delayed_work(cifsiod_wq, &server->reconnect, 0))
411-
cifs_put_tcp_session(server, false);
405+
mod_delayed_work(cifsiod_wq, &server->reconnect, 0);
412406
}
413407
} while (server->tcpStatus == CifsNeedReconnect);
414408

@@ -538,13 +532,7 @@ static int reconnect_dfs_server(struct TCP_Server_Info *server)
538532
spin_unlock(&server->srv_lock);
539533
cifs_swn_reset_server_dstaddr(server);
540534
cifs_server_unlock(server);
541-
542-
/* increase ref count which reconnect work will drop */
543-
spin_lock(&cifs_tcp_ses_lock);
544-
server->srv_count++;
545-
spin_unlock(&cifs_tcp_ses_lock);
546-
if (mod_delayed_work(cifsiod_wq, &server->reconnect, 0))
547-
cifs_put_tcp_session(server, false);
535+
mod_delayed_work(cifsiod_wq, &server->reconnect, 0);
548536
} while (server->tcpStatus == CifsNeedReconnect);
549537

550538
mutex_lock(&server->refpath_lock);
@@ -1620,25 +1608,22 @@ cifs_put_tcp_session(struct TCP_Server_Info *server, int from_reconnect)
16201608
list_del_init(&server->tcp_ses_list);
16211609
spin_unlock(&cifs_tcp_ses_lock);
16221610

1623-
/* For secondary channels, we pick up ref-count on the primary server */
1624-
if (SERVER_IS_CHAN(server))
1625-
cifs_put_tcp_session(server->primary_server, from_reconnect);
1626-
16271611
cancel_delayed_work_sync(&server->echo);
16281612

1629-
if (from_reconnect) {
1613+
if (from_reconnect)
16301614
/*
16311615
* Avoid deadlock here: reconnect work calls
16321616
* cifs_put_tcp_session() at its end. Need to be sure
16331617
* that reconnect work does nothing with server pointer after
16341618
* that step.
16351619
*/
1636-
if (cancel_delayed_work(&server->reconnect))
1637-
cifs_put_tcp_session(server, from_reconnect);
1638-
} else {
1639-
if (cancel_delayed_work_sync(&server->reconnect))
1640-
cifs_put_tcp_session(server, from_reconnect);
1641-
}
1620+
cancel_delayed_work(&server->reconnect);
1621+
else
1622+
cancel_delayed_work_sync(&server->reconnect);
1623+
1624+
/* For secondary channels, we pick up ref-count on the primary server */
1625+
if (SERVER_IS_CHAN(server))
1626+
cifs_put_tcp_session(server->primary_server, from_reconnect);
16421627

16431628
spin_lock(&server->srv_lock);
16441629
server->tcpStatus = CifsExiting;

fs/smb/client/smb2ops.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2836,6 +2836,8 @@ smb2_get_dfs_refer(const unsigned int xid, struct cifs_ses *ses,
28362836
usleep_range(512, 2048);
28372837
} while (++retry_count < 5);
28382838

2839+
if (!rc && !dfs_rsp)
2840+
rc = -EIO;
28392841
if (rc) {
28402842
if (!is_retryable_error(rc) && rc != -ENOENT && rc != -EOPNOTSUPP)
28412843
cifs_tcon_dbg(VFS, "%s: ioctl error: rc=%d\n", __func__, rc);

0 commit comments

Comments
 (0)