diff options
Diffstat (limited to 'fs/cifs/smb2ops.c')
-rw-r--r-- | fs/cifs/smb2ops.c | 170 |
1 files changed, 170 insertions, 0 deletions
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index 77f8aeb9c2fc..5a48aa290dfe 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c | |||
@@ -731,11 +731,72 @@ smb2_sync_write(const unsigned int xid, struct cifsFileInfo *cfile, | |||
731 | return SMB2_write(xid, parms, written, iov, nr_segs); | 731 | return SMB2_write(xid, parms, written, iov, nr_segs); |
732 | } | 732 | } |
733 | 733 | ||
734 | /* Set or clear the SPARSE_FILE attribute based on value passed in setsparse */ | ||
735 | static bool smb2_set_sparse(const unsigned int xid, struct cifs_tcon *tcon, | ||
736 | struct cifsFileInfo *cfile, struct inode *inode, __u8 setsparse) | ||
737 | { | ||
738 | struct cifsInodeInfo *cifsi; | ||
739 | int rc; | ||
740 | |||
741 | cifsi = CIFS_I(inode); | ||
742 | |||
743 | /* if file already sparse don't bother setting sparse again */ | ||
744 | if ((cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE) && setsparse) | ||
745 | return true; /* already sparse */ | ||
746 | |||
747 | if (!(cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE) && !setsparse) | ||
748 | return true; /* already not sparse */ | ||
749 | |||
750 | /* | ||
751 | * Can't check for sparse support on share the usual way via the | ||
752 | * FS attribute info (FILE_SUPPORTS_SPARSE_FILES) on the share | ||
753 | * since Samba server doesn't set the flag on the share, yet | ||
754 | * supports the set sparse FSCTL and returns sparse correctly | ||
755 | * in the file attributes. If we fail setting sparse though we | ||
756 | * mark that server does not support sparse files for this share | ||
757 | * to avoid repeatedly sending the unsupported fsctl to server | ||
758 | * if the file is repeatedly extended. | ||
759 | */ | ||
760 | if (tcon->broken_sparse_sup) | ||
761 | return false; | ||
762 | |||
763 | rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid, | ||
764 | cfile->fid.volatile_fid, FSCTL_SET_SPARSE, | ||
765 | true /* is_fctl */, &setsparse, 1, NULL, NULL); | ||
766 | if (rc) { | ||
767 | tcon->broken_sparse_sup = true; | ||
768 | cifs_dbg(FYI, "set sparse rc = %d\n", rc); | ||
769 | return false; | ||
770 | } | ||
771 | |||
772 | if (setsparse) | ||
773 | cifsi->cifsAttrs |= FILE_ATTRIBUTE_SPARSE_FILE; | ||
774 | else | ||
775 | cifsi->cifsAttrs &= (~FILE_ATTRIBUTE_SPARSE_FILE); | ||
776 | |||
777 | return true; | ||
778 | } | ||
779 | |||
734 | static int | 780 | static int |
735 | smb2_set_file_size(const unsigned int xid, struct cifs_tcon *tcon, | 781 | smb2_set_file_size(const unsigned int xid, struct cifs_tcon *tcon, |
736 | struct cifsFileInfo *cfile, __u64 size, bool set_alloc) | 782 | struct cifsFileInfo *cfile, __u64 size, bool set_alloc) |
737 | { | 783 | { |
738 | __le64 eof = cpu_to_le64(size); | 784 | __le64 eof = cpu_to_le64(size); |
785 | struct inode *inode; | ||
786 | |||
787 | /* | ||
788 | * If extending file more than one page make sparse. Many Linux fs | ||
789 | * make files sparse by default when extending via ftruncate | ||
790 | */ | ||
791 | inode = cfile->dentry->d_inode; | ||
792 | |||
793 | if (!set_alloc && (size > inode->i_size + 8192)) { | ||
794 | __u8 set_sparse = 1; | ||
795 | |||
796 | /* whether set sparse succeeds or not, extend the file */ | ||
797 | smb2_set_sparse(xid, tcon, cfile, inode, set_sparse); | ||
798 | } | ||
799 | |||
739 | return SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid, | 800 | return SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid, |
740 | cfile->fid.volatile_fid, cfile->pid, &eof, false); | 801 | cfile->fid.volatile_fid, cfile->pid, &eof, false); |
741 | } | 802 | } |
@@ -954,6 +1015,105 @@ smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon, | |||
954 | return rc; | 1015 | return rc; |
955 | } | 1016 | } |
956 | 1017 | ||
1018 | static long smb3_zero_range(struct file *file, struct cifs_tcon *tcon, | ||
1019 | loff_t offset, loff_t len, bool keep_size) | ||
1020 | { | ||
1021 | struct inode *inode; | ||
1022 | struct cifsInodeInfo *cifsi; | ||
1023 | struct cifsFileInfo *cfile = file->private_data; | ||
1024 | struct file_zero_data_information fsctl_buf; | ||
1025 | long rc; | ||
1026 | unsigned int xid; | ||
1027 | |||
1028 | xid = get_xid(); | ||
1029 | |||
1030 | inode = cfile->dentry->d_inode; | ||
1031 | cifsi = CIFS_I(inode); | ||
1032 | |||
1033 | /* if file not oplocked can't be sure whether asking to extend size */ | ||
1034 | if (!CIFS_CACHE_READ(cifsi)) | ||
1035 | if (keep_size == false) | ||
1036 | return -EOPNOTSUPP; | ||
1037 | |||
1038 | /* | ||
1039 | * Must check if file sparse since fallocate -z (zero range) assumes | ||
1040 | * non-sparse allocation | ||
1041 | */ | ||
1042 | if (!(cifsi->cifsAttrs & FILE_ATTRIBUTE_SPARSE_FILE)) | ||
1043 | return -EOPNOTSUPP; | ||
1044 | |||
1045 | /* | ||
1046 | * need to make sure we are not asked to extend the file since the SMB3 | ||
1047 | * fsctl does not change the file size. In the future we could change | ||
1048 | * this to zero the first part of the range then set the file size | ||
1049 | * which for a non sparse file would zero the newly extended range | ||
1050 | */ | ||
1051 | if (keep_size == false) | ||
1052 | if (i_size_read(inode) < offset + len) | ||
1053 | return -EOPNOTSUPP; | ||
1054 | |||
1055 | cifs_dbg(FYI, "offset %lld len %lld", offset, len); | ||
1056 | |||
1057 | fsctl_buf.FileOffset = cpu_to_le64(offset); | ||
1058 | fsctl_buf.BeyondFinalZero = cpu_to_le64(offset + len); | ||
1059 | |||
1060 | rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid, | ||
1061 | cfile->fid.volatile_fid, FSCTL_SET_ZERO_DATA, | ||
1062 | true /* is_fctl */, (char *)&fsctl_buf, | ||
1063 | sizeof(struct file_zero_data_information), NULL, NULL); | ||
1064 | free_xid(xid); | ||
1065 | return rc; | ||
1066 | } | ||
1067 | |||
1068 | static long smb3_punch_hole(struct file *file, struct cifs_tcon *tcon, | ||
1069 | loff_t offset, loff_t len) | ||
1070 | { | ||
1071 | struct inode *inode; | ||
1072 | struct cifsInodeInfo *cifsi; | ||
1073 | struct cifsFileInfo *cfile = file->private_data; | ||
1074 | struct file_zero_data_information fsctl_buf; | ||
1075 | long rc; | ||
1076 | unsigned int xid; | ||
1077 | __u8 set_sparse = 1; | ||
1078 | |||
1079 | xid = get_xid(); | ||
1080 | |||
1081 | inode = cfile->dentry->d_inode; | ||
1082 | cifsi = CIFS_I(inode); | ||
1083 | |||
1084 | /* Need to make file sparse, if not already, before freeing range. */ | ||
1085 | /* Consider adding equivalent for compressed since it could also work */ | ||
1086 | if (!smb2_set_sparse(xid, tcon, cfile, inode, set_sparse)) | ||
1087 | return -EOPNOTSUPP; | ||
1088 | |||
1089 | cifs_dbg(FYI, "offset %lld len %lld", offset, len); | ||
1090 | |||
1091 | fsctl_buf.FileOffset = cpu_to_le64(offset); | ||
1092 | fsctl_buf.BeyondFinalZero = cpu_to_le64(offset + len); | ||
1093 | |||
1094 | rc = SMB2_ioctl(xid, tcon, cfile->fid.persistent_fid, | ||
1095 | cfile->fid.volatile_fid, FSCTL_SET_ZERO_DATA, | ||
1096 | true /* is_fctl */, (char *)&fsctl_buf, | ||
1097 | sizeof(struct file_zero_data_information), NULL, NULL); | ||
1098 | free_xid(xid); | ||
1099 | return rc; | ||
1100 | } | ||
1101 | |||
1102 | static long smb3_fallocate(struct file *file, struct cifs_tcon *tcon, int mode, | ||
1103 | loff_t off, loff_t len) | ||
1104 | { | ||
1105 | /* KEEP_SIZE already checked for by do_fallocate */ | ||
1106 | if (mode & FALLOC_FL_PUNCH_HOLE) | ||
1107 | return smb3_punch_hole(file, tcon, off, len); | ||
1108 | else if (mode & FALLOC_FL_ZERO_RANGE) { | ||
1109 | if (mode & FALLOC_FL_KEEP_SIZE) | ||
1110 | return smb3_zero_range(file, tcon, off, len, true); | ||
1111 | return smb3_zero_range(file, tcon, off, len, false); | ||
1112 | } | ||
1113 | |||
1114 | return -EOPNOTSUPP; | ||
1115 | } | ||
1116 | |||
957 | static void | 1117 | static void |
958 | smb2_downgrade_oplock(struct TCP_Server_Info *server, | 1118 | smb2_downgrade_oplock(struct TCP_Server_Info *server, |
959 | struct cifsInodeInfo *cinode, bool set_level2) | 1119 | struct cifsInodeInfo *cinode, bool set_level2) |
@@ -1161,6 +1321,12 @@ smb2_wp_retry_size(struct inode *inode) | |||
1161 | SMB2_MAX_BUFFER_SIZE); | 1321 | SMB2_MAX_BUFFER_SIZE); |
1162 | } | 1322 | } |
1163 | 1323 | ||
1324 | static bool | ||
1325 | smb2_dir_needs_close(struct cifsFileInfo *cfile) | ||
1326 | { | ||
1327 | return !cfile->invalidHandle; | ||
1328 | } | ||
1329 | |||
1164 | struct smb_version_operations smb20_operations = { | 1330 | struct smb_version_operations smb20_operations = { |
1165 | .compare_fids = smb2_compare_fids, | 1331 | .compare_fids = smb2_compare_fids, |
1166 | .setup_request = smb2_setup_request, | 1332 | .setup_request = smb2_setup_request, |
@@ -1236,6 +1402,7 @@ struct smb_version_operations smb20_operations = { | |||
1236 | .parse_lease_buf = smb2_parse_lease_buf, | 1402 | .parse_lease_buf = smb2_parse_lease_buf, |
1237 | .clone_range = smb2_clone_range, | 1403 | .clone_range = smb2_clone_range, |
1238 | .wp_retry_size = smb2_wp_retry_size, | 1404 | .wp_retry_size = smb2_wp_retry_size, |
1405 | .dir_needs_close = smb2_dir_needs_close, | ||
1239 | }; | 1406 | }; |
1240 | 1407 | ||
1241 | struct smb_version_operations smb21_operations = { | 1408 | struct smb_version_operations smb21_operations = { |
@@ -1313,6 +1480,7 @@ struct smb_version_operations smb21_operations = { | |||
1313 | .parse_lease_buf = smb2_parse_lease_buf, | 1480 | .parse_lease_buf = smb2_parse_lease_buf, |
1314 | .clone_range = smb2_clone_range, | 1481 | .clone_range = smb2_clone_range, |
1315 | .wp_retry_size = smb2_wp_retry_size, | 1482 | .wp_retry_size = smb2_wp_retry_size, |
1483 | .dir_needs_close = smb2_dir_needs_close, | ||
1316 | }; | 1484 | }; |
1317 | 1485 | ||
1318 | struct smb_version_operations smb30_operations = { | 1486 | struct smb_version_operations smb30_operations = { |
@@ -1393,6 +1561,8 @@ struct smb_version_operations smb30_operations = { | |||
1393 | .clone_range = smb2_clone_range, | 1561 | .clone_range = smb2_clone_range, |
1394 | .validate_negotiate = smb3_validate_negotiate, | 1562 | .validate_negotiate = smb3_validate_negotiate, |
1395 | .wp_retry_size = smb2_wp_retry_size, | 1563 | .wp_retry_size = smb2_wp_retry_size, |
1564 | .dir_needs_close = smb2_dir_needs_close, | ||
1565 | .fallocate = smb3_fallocate, | ||
1396 | }; | 1566 | }; |
1397 | 1567 | ||
1398 | struct smb_version_values smb20_values = { | 1568 | struct smb_version_values smb20_values = { |