| @@ -1196,32 +1196,103 @@ const struct inode_operations cifs_symlink_inode_ops = { · 1195 unmodified lines |
| 1196 | 1196 | .listxattr = cifs_listxattr, |
| 1197 | 1197 | }; |
| 1198 | 1198 | |
| 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 | + |
| 1199 | 1265 | static loff_t cifs_remap_file_range(struct file *src_file, loff_t off, |
| 1200 | 1266 | struct file *dst_file, loff_t destoff, loff_t len, |
| 1201 | 1267 | unsigned int remap_flags) |
| 1202 | 1268 | { |
| 1203 | 1269 | struct inode *src_inode = file_inode(src_file); |
| 1204 | 1270 | 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); |
| 1205 | 1273 | 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; |
| 1208 | 1277 | unsigned int xid; |
| 1209 | 1278 | int rc; |
| 1210 | 1279 | |
| 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) |
| 1212 | 1283 | return -EINVAL; |
| 1213 | 1284 | |
| 1214 | 1285 | cifs_dbg(FYI, "clone range\n"); |
| 1215 | 1286 | |
| 1216 | 1287 | xid = get_xid(); |
| 1217 | 1288 | |
| 1218 | | − if (!src_file->private_data || !dst_file->private_data) { |
| 1289 | + if (!smb_file_src || !smb_file_target) { |
| 1219 | 1290 | rc = -EBADF; |
| 1220 | 1291 | cifs_dbg(VFS, "missing cifsFileInfo on copy range src file\n"); |
| 1221 | 1292 | goto out; |
| 1222 | 1293 | } |
| 1223 | 1294 | |
| 1224 | | − smb_file_target = dst_file->private_data; |
| 1295 | + src_tcon = tlink_tcon(smb_file_src->tlink); |
| 1225 | 1296 | target_tcon = tlink_tcon(smb_file_target->tlink); |
| 1226 | 1297 | |
| 1227 | 1298 | /* |
| @@ -1234,20 +1305,63 @@ static loff_t cifs_remap_file_range(struct file *src_file, loff_t off, · 6 unmodified lines |
| 1234 | 1305 | if (len == 0) |
| 1235 | 1306 | len = src_inode->i_size - off; |
| 1236 | 1307 | |
| 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"); |
| 1241 | 1309 | |
| 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) { |
| 1243 | 1351 | rc = target_tcon->ses->server->ops->duplicate_extents(xid, |
| 1244 | 1352 | 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 | + } |
| 1247 | 1360 | |
| 1248 | 1361 | /* force revalidate of size and timestamps of target file now |
| 1249 | 1362 | that target is updated on the server */ |
| 1250 | 1363 | CIFS_I(target_inode)->time = 0; |
| 1364 | +unlock: |
| 1251 | 1365 | /* although unlocking in the reverse order from locking is not |
| 1252 | 1366 | strictly necessary here it is a little cleaner to be consistent */ |
| 1253 | 1367 | unlock_two_nondirectories(src_inode, target_inode); |
| @@ -1263,10 +1377,12 @@ ssize_t cifs_file_copychunk_range(unsigned int xid, · 9 unmodified lines |
| 1263 | 1377 | { |
| 1264 | 1378 | struct inode *src_inode = file_inode(src_file); |
| 1265 | 1379 | struct inode *target_inode = file_inode(dst_file); |
| 1380 | + struct cifsInodeInfo *src_cifsi = CIFS_I(src_inode); |
| 1266 | 1381 | struct cifsFileInfo *smb_file_src; |
| 1267 | 1382 | struct cifsFileInfo *smb_file_target; |
| 1268 | 1383 | struct cifs_tcon *src_tcon; |
| 1269 | 1384 | struct cifs_tcon *target_tcon; |
| 1385 | + unsigned long long destend, fstart, fend; |
| 1270 | 1386 | ssize_t rc; |
| 1271 | 1387 | |
| 1272 | 1388 | cifs_dbg(FYI, "copychunk range\n"); |
| @@ -1306,13 +1422,41 @@ ssize_t cifs_file_copychunk_range(unsigned int xid, · 33 unmodified lines |
| 1306 | 1422 | if (rc) |
| 1307 | 1423 | goto unlock; |
| 1308 | 1424 | |
| 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); |
| 1311 | 1452 | |
| 1312 | 1453 | rc = file_modified(dst_file); |
| 1313 | | − if (!rc) |
| 1454 | + if (!rc) { |
| 1314 | 1455 | rc = target_tcon->ses->server->ops->copychunk_range(xid, |
| 1315 | 1456 | 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 | + } |
| 1316 | 1460 | |
| 1317 | 1461 | file_accessed(src_file); |
| 1318 | 1462 | |