Bug in getting file attributes with SMB3.1.1 and posix

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hello,

I encountered a funny bug when a share is mounted with vers=3.1.1, posix,... If a file size has bits 0x410 = ATTR_DIRECTORY | ATTR_REPARSE = 1040 set, then the file is regarded as a directory and its open fails. A simplest test example is any file 1040 bytes long.

The cause of this bug is that Attributes field in smb2_file_all_info struct occupies the same place that EndOfFile field in smb311_posix_qinfo, and sometimes the latter struct is incorrectly processed as if it was the first one. I attach an example patch that solves the problem for me, obviously not ready for submission, but just to show which places in the code are subject to problems. The patch is against linux-6.12.6 kernel, but, AFAICS, nothing has changed since then in relevant places. If I have guessed more or less correctly what the intended functionality is, please feel free to use my patch as a basis for corrections.

Best regards

Olen Nykyforchyn
--- linux-6.12.6/fs/smb/client/reparse.h.orig	2024-12-28 15:37:32.252621378 +0200
+++ linux-6.12.6/fs/smb/client/reparse.h	2024-12-28 15:51:00.004654390 +0200
@@ -108,6 +108,16 @@
 	return ret;
 }
 
+static inline bool smb311_open_data_reparse(struct cifs_open_info_data *data)
+{
+	struct smb311_posix_qinfo *posix_fi = &data->posix_fi;
+	u32 tag = le32_to_cpu(posix_fi->ReparseTag);
+	bool ret;
+
+	ret = data->reparse_point || tag;
+	return ret;
+}
+
 bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb,
 				 struct cifs_fattr *fattr,
 				 struct cifs_open_info_data *data);
--- linux-6.12.6/fs/smb/client/inode.c.orig	2024-12-28 15:47:56.603646895 +0200
+++ linux-6.12.6/fs/smb/client/inode.c	2024-12-30 18:19:52.816039798 +0200
@@ -845,7 +845,7 @@
 	fattr->cf_mode = wire_mode_to_posix(le32_to_cpu(info->Mode),
 					    fattr->cf_cifsattrs & ATTR_DIRECTORY);
 
-	if (cifs_open_data_reparse(data) &&
+	if (smb311_open_data_reparse(data) &&
 	    cifs_reparse_point_to_fattr(cifs_sb, fattr, data))
 		goto out_reparse;
 
@@ -976,7 +976,10 @@
 			rc = PTR_ERR(path);
 			goto cgfi_exit;
 		}
-		cifs_open_info_to_fattr(&fattr, &data, inode->i_sb);
+		if (tcon->posix_extensions)
+			smb311_posix_info_to_fattr(&fattr, &data, inode->i_sb);
+		else
+			cifs_open_info_to_fattr(&fattr, &data, inode->i_sb);
 		if (fattr.cf_flags & CIFS_FATTR_DELETE_PENDING)
 			cifs_mark_open_handles_for_deleted_file(inode, path);
 		break;
@@ -1176,7 +1179,7 @@
 		break;
 	case IO_REPARSE_TAG_INTERNAL:
 		rc = 0;
-		if (le32_to_cpu(data->fi.Attributes) & ATTR_DIRECTORY) {
+		if (tcon->posix_extensions || (le32_to_cpu(data->fi.Attributes) & ATTR_DIRECTORY)) {
 			cifs_create_junction_fattr(fattr, sb);
 			goto out;
 		}
@@ -1250,12 +1253,12 @@
 		 * since we have to check if its reparse tag matches a known
 		 * special file type e.g. symlink or fifo or char etc.
 		 */
-		if (cifs_open_data_reparse(data)) {
+		if (cifs_open_data_reparse(data))
 			rc = reparse_info_to_fattr(data, sb, xid, tcon,
-						   full_path, fattr);
-		} else {
+					   full_path, fattr);
+		else
 			cifs_open_info_to_fattr(fattr, data, sb);
-		}
+		cifs_dbg(FYI, "cifs data to fattr, rc=%d\n", rc);
 		if (!rc && *inode &&
 		    (fattr->cf_flags & CIFS_FATTR_DELETE_PENDING))
 			cifs_mark_open_handles_for_deleted_file(*inode, full_path);
@@ -1385,6 +1388,8 @@
 		cifs_dbg(FYI, "No need to revalidate cached inode sizes\n");
 		return 0;
 	}
+	else
+		cifs_dbg(FYI, "Getting cifs attributes for %s\n", full_path);
 
 	rc = cifs_get_fattr(data, sb, xid, fid, &fattr, inode, full_path);
 	if (rc)
@@ -1431,7 +1436,7 @@
 
 	switch (rc) {
 	case 0:
-		if (cifs_open_data_reparse(data)) {
+		if (smb311_open_data_reparse(data)) {
 			rc = reparse_info_to_fattr(data, sb, xid, tcon,
 						   full_path, fattr);
 		} else {
@@ -1484,6 +1489,8 @@
 		cifs_dbg(FYI, "No need to revalidate cached inode sizes\n");
 		return 0;
 	}
+	else
+		cifs_dbg(FYI, "Getting smb3 posix attributes for %s\n", full_path);
 
 	rc = smb311_posix_get_fattr(data, &fattr, full_path, sb, xid);
 	if (rc)

[Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux