aboutsummaryrefslogtreecommitdiffstats
path: root/fs/cifs/smb2ops.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/cifs/smb2ops.c')
-rw-r--r--fs/cifs/smb2ops.c170
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 */
735static 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
734static int 780static int
735smb2_set_file_size(const unsigned int xid, struct cifs_tcon *tcon, 781smb2_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
1018static 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
1068static 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
1102static 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
957static void 1117static void
958smb2_downgrade_oplock(struct TCP_Server_Info *server, 1118smb2_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
1324static bool
1325smb2_dir_needs_close(struct cifsFileInfo *cfile)
1326{
1327 return !cfile->invalidHandle;
1328}
1329
1164struct smb_version_operations smb20_operations = { 1330struct 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
1241struct smb_version_operations smb21_operations = { 1408struct 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
1318struct smb_version_operations smb30_operations = { 1486struct 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
1398struct smb_version_values smb20_values = { 1568struct smb_version_values smb20_values = {