Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5bafd765 authored by Steve French's avatar Steve French
Browse files

[CIFS] Add support for readdir to legacy servers



Fixes oops to OS/2 on ls and removes redundant NTCreateX calls to servers
which do not support NT SMBs.  Key operations to OS/2 work.

Signed-off-by: default avatarSteve French <sfrench@us.ibm.com>
parent a8ee0344
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -2,6 +2,10 @@ Version 1.44
------------
Rewritten sessionsetup support, including support for legacy SMB
session setup needed for OS/2 and older servers such as Windows 95 and 98.
Fix oops on ls to OS/2 servers.  Add support for level 1 FindFirst
so we can do search (ls etc.) to OS/2.  Do not send NTCreateX
or recent levels of FindFirst unless server says it supports NT SMBs
(instead use legacy equivalents from LANMAN dialect).

Version 1.43
------------
+24 −5
Original line number Diff line number Diff line
@@ -1374,6 +1374,9 @@ struct smb_t2_rsp {
#define SMB_FILE_MAXIMUM_INFO           0x40d

/* Find File infolevels */
#define SMB_FIND_FILE_INFO_STANDARD       0x001
#define SMB_FIND_FILE_QUERY_EA_SIZE       0x002
#define SMB_FIND_FILE_QUERY_EAS_FROM_LIST 0x003
#define SMB_FIND_FILE_DIRECTORY_INFO      0x101
#define SMB_FIND_FILE_FULL_DIRECTORY_INFO 0x102
#define SMB_FIND_FILE_NAMES_INFO          0x103
@@ -1998,7 +2001,8 @@ typedef struct {

struct file_allocation_info {
	__le64 AllocationSize; /* Note old Samba srvr rounds this up too much */
} __attribute__((packed));	/* size used on disk, level 0x103 for set, 0x105 for query */
} __attribute__((packed));	/* size used on disk, for level 0x103 for set,
				   0x105 for query */

struct file_end_of_file_info {
	__le64 FileSize;		/* offset to end of file */
@@ -2105,7 +2109,7 @@ typedef struct {
	__le32 ExtFileAttributes;
	__le32 FileNameLength;
	char FileName[1];
} __attribute__((packed)) FILE_DIRECTORY_INFO;   /* level 0x101 FF response data area */
} __attribute__((packed)) FILE_DIRECTORY_INFO;   /* level 0x101 FF resp data */

typedef struct {
	__le32 NextEntryOffset;
@@ -2120,7 +2124,7 @@ typedef struct {
	__le32 FileNameLength;
	__le32 EaSize; /* length of the xattrs */
	char FileName[1];
} __attribute__((packed)) FILE_FULL_DIRECTORY_INFO;   /* level 0x102 FF response data area */
} __attribute__((packed)) FILE_FULL_DIRECTORY_INFO; /* level 0x102 rsp data */

typedef struct {
	__le32 NextEntryOffset;
@@ -2137,7 +2141,7 @@ typedef struct {
	__le32 Reserved;
	__u64 UniqueId; /* inode num - le since Samba puts ino in low 32 bit*/
	char FileName[1];
} __attribute__((packed)) SEARCH_ID_FULL_DIR_INFO;   /* level 0x105 FF response data area */
} __attribute__((packed)) SEARCH_ID_FULL_DIR_INFO; /* level 0x105 FF rsp data */

typedef struct {
	__le32 NextEntryOffset;
@@ -2155,7 +2159,22 @@ typedef struct {
	__u8   Reserved;
	__u8   ShortName[12];
	char FileName[1];
} __attribute__((packed)) FILE_BOTH_DIRECTORY_INFO;   /* level 0x104 FF response data area */
} __attribute__((packed)) FILE_BOTH_DIRECTORY_INFO; /* level 0x104 FFrsp data */

typedef struct {
	__u32  ResumeKey;
	__le16 CreationDate; /* SMB Date */
	__le16 CreationTime; /* SMB Time */
	__le16 LastAccessDate;
	__le16 LastAccessTime;
	__le16 LastWriteDate;
	__le16 LastWriteTime;
	__le32 DataSize; /* File Size (EOF) */
	__le32 AllocationSize;
	__le16 Attributes; /* verify not u32 */
	__u8   FileNameLength;
	char FileName[1];
} __attribute__((packed)) FIND_FILE_STANDARD_INFO; /* level 0x1 FF resp data */


struct win_dev {
+9 −2
Original line number Diff line number Diff line
@@ -178,11 +178,14 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode,
		FreeXid(xid);
		return -ENOMEM;
	}

	if (cifs_sb->tcon->ses->capabilities & CAP_NT_SMBS) 
		rc = CIFSSMBOpen(xid, pTcon, full_path, disposition,
			 desiredAccess, CREATE_NOT_DIR,
			 &fileHandle, &oplock, buf, cifs_sb->local_nls,
			 cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
	else
		rc = -EIO; /* no NT SMB support fall into legacy open below */

	if(rc == -EIO) {
		/* old server, retry the open legacy style */
		rc = SMBLegacyOpen(xid, pTcon, full_path, disposition,
@@ -369,6 +372,10 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, int mode,
					 cifs_sb->mnt_cifs_flags & 
					    CIFS_MOUNT_MAP_SPECIAL_CHR);

			/* BB FIXME - add handling for backlevel servers
			   which need legacy open and check for all
			   calls to SMBOpen for fallback to 
			   SMBLeagcyOpen */
			if(!rc) {
				/* BB Do not bother to decode buf since no
				   local inode yet to put timestamps in,
+7 −2
Original line number Diff line number Diff line
@@ -260,10 +260,15 @@ int cifs_open(struct inode *inode, struct file *file)
		rc = -ENOMEM;
		goto out;
	}
	rc = CIFSSMBOpen(xid, pTcon, full_path, disposition, desiredAccess,
			 CREATE_NOT_DIR, &netfid, &oplock, buf,

	if (cifs_sb->tcon->ses->capabilities & CAP_NT_SMBS)
		rc = CIFSSMBOpen(xid, pTcon, full_path, disposition, 
			 desiredAccess, CREATE_NOT_DIR, &netfid, &oplock, buf,
			 cifs_sb->local_nls, cifs_sb->mnt_cifs_flags
				 & CIFS_MOUNT_MAP_SPECIAL_CHR);
	else
		rc = -EIO; /* no NT SMB support fall into legacy open below */

	if (rc == -EIO) {
		/* Old server, try legacy style OpenX */
		rc = SMBLegacyOpen(xid, pTcon, full_path, disposition,
+109 −41
Original line number Diff line number Diff line
@@ -109,32 +109,52 @@ static int construct_dentry(struct qstr *qstring, struct file *file,
	return rc;
}

static void fill_in_inode(struct inode *tmp_inode,
	FILE_DIRECTORY_INFO *pfindData, int *pobject_type, int isNewInode)
static void fill_in_inode(struct inode *tmp_inode, int new_buf_type,
		char * buf, int *pobject_type, int isNewInode)
{
	loff_t local_size;
	struct timespec local_mtime;

	struct cifsInodeInfo *cifsInfo = CIFS_I(tmp_inode);
	struct cifs_sb_info *cifs_sb = CIFS_SB(tmp_inode->i_sb);
	__u32 attr = le32_to_cpu(pfindData->ExtFileAttributes);
	__u64 allocation_size = le64_to_cpu(pfindData->AllocationSize);
	__u64 end_of_file = le64_to_cpu(pfindData->EndOfFile);

	cifsInfo->cifsAttrs = attr;
	cifsInfo->time = jiffies;
	__u32 attr;
	__u64 allocation_size;
	__u64 end_of_file;

	/* save mtime and size */
	local_mtime = tmp_inode->i_mtime;
	local_size  = tmp_inode->i_size;

	/* Linux can not store file creation time unfortunately so ignore it */
	if(new_buf_type) {
		FILE_DIRECTORY_INFO *pfindData = (FILE_DIRECTORY_INFO *)buf;

		attr = le32_to_cpu(pfindData->ExtFileAttributes);
		allocation_size = le64_to_cpu(pfindData->AllocationSize);
		end_of_file = le64_to_cpu(pfindData->EndOfFile);
		tmp_inode->i_atime =
		      cifs_NTtimeToUnix(le64_to_cpu(pfindData->LastAccessTime));
		tmp_inode->i_mtime =
		      cifs_NTtimeToUnix(le64_to_cpu(pfindData->LastWriteTime));
		tmp_inode->i_ctime =
		      cifs_NTtimeToUnix(le64_to_cpu(pfindData->ChangeTime));
	} else { /* legacy, OS2 and DOS style */
		FIND_FILE_STANDARD_INFO * pfindData = 
			(FIND_FILE_STANDARD_INFO *)buf;

		attr = le16_to_cpu(pfindData->Attributes);
		allocation_size = le32_to_cpu(pfindData->AllocationSize);
		end_of_file = le32_to_cpu(pfindData->DataSize);
		tmp_inode->i_atime = CURRENT_TIME;
		/* tmp_inode->i_mtime =  BB FIXME - add dos time handling
		tmp_inode->i_ctime = 0;   BB FIXME */

	}

	/* Linux can not store file creation time unfortunately so ignore it */

	cifsInfo->cifsAttrs = attr;
	cifsInfo->time = jiffies;

	/* treat dos attribute of read-only as read-only mode bit e.g. 555? */
	/* 2767 perms - indicate mandatory locking */
		/* BB fill in uid and gid here? with help from winbind? 
@@ -421,6 +441,9 @@ static int initiate_cifs_search(const int xid, struct file *file)
	/* test for Unix extensions */
	if (pTcon->ses->capabilities & CAP_UNIX) {
		cifsFile->srch_inf.info_level = SMB_FIND_FILE_UNIX;
	} else if ((pTcon->ses->capabilities & 
			(CAP_NT_SMBS | CAP_NT_FIND)) == 0) {
		cifsFile->srch_inf.info_level = SMB_FIND_FILE_INFO_STANDARD;
	} else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) {
		cifsFile->srch_inf.info_level = SMB_FIND_FILE_ID_FULL_DIR_INFO;
	} else /* not srvinos - BB fixme add check for backlevel? */ {
@@ -456,11 +479,18 @@ static int cifs_unicode_bytelen(char *str)
	return len << 1;
}

static char *nxt_dir_entry(char *old_entry, char *end_of_smb)
static char *nxt_dir_entry(char *old_entry, char *end_of_smb, int level)
{
	char * new_entry;
	FILE_DIRECTORY_INFO * pDirInfo = (FILE_DIRECTORY_INFO *)old_entry;

	if(level == SMB_FIND_FILE_INFO_STANDARD) {
		FIND_FILE_STANDARD_INFO * pfData;
		pfData = (FIND_FILE_STANDARD_INFO *)pDirInfo;

		new_entry = old_entry + sizeof(FIND_FILE_STANDARD_INFO) +
				pfData->FileNameLength;
	} else
		new_entry = old_entry + le32_to_cpu(pDirInfo->NextEntryOffset);
	cFYI(1,("new entry %p old entry %p",new_entry,old_entry));
	/* validate that new_entry is not past end of SMB */
@@ -469,7 +499,10 @@ static char *nxt_dir_entry(char *old_entry, char *end_of_smb)
		      ("search entry %p began after end of SMB %p old entry %p",
			new_entry, end_of_smb, old_entry)); 
		return NULL;
	} else if (new_entry + sizeof(FILE_DIRECTORY_INFO) > end_of_smb) {
	} else if(((level == SMB_FIND_FILE_INFO_STANDARD) &&
		   (new_entry + sizeof(FIND_FILE_STANDARD_INFO) > end_of_smb)) ||
		  ((level != SMB_FIND_FILE_INFO_STANDARD) &&
		   (new_entry + sizeof(FILE_DIRECTORY_INFO) > end_of_smb)))  {
		cERROR(1,("search entry %p extends after end of SMB %p",
			new_entry, end_of_smb));
		return NULL;
@@ -487,7 +520,7 @@ static int cifs_entry_is_dot(char *current_entry, struct cifsFileInfo *cfile)
	char * filename = NULL;
	int len = 0; 

	if(cfile->srch_inf.info_level == 0x202) {
	if(cfile->srch_inf.info_level == SMB_FIND_FILE_UNIX) {
		FILE_UNIX_INFO * pFindData = (FILE_UNIX_INFO *)current_entry;
		filename = &pFindData->FileName[0];
		if(cfile->srch_inf.unicode) {
@@ -496,26 +529,34 @@ static int cifs_entry_is_dot(char *current_entry, struct cifsFileInfo *cfile)
			/* BB should we make this strnlen of PATH_MAX? */
			len = strnlen(filename, 5);
		}
	} else if(cfile->srch_inf.info_level == 0x101) {
	} else if(cfile->srch_inf.info_level == SMB_FIND_FILE_DIRECTORY_INFO) {
		FILE_DIRECTORY_INFO * pFindData = 
			(FILE_DIRECTORY_INFO *)current_entry;
		filename = &pFindData->FileName[0];
		len = le32_to_cpu(pFindData->FileNameLength);
	} else if(cfile->srch_inf.info_level == 0x102) {
	} else if(cfile->srch_inf.info_level == 
			SMB_FIND_FILE_FULL_DIRECTORY_INFO) {
		FILE_FULL_DIRECTORY_INFO * pFindData = 
			(FILE_FULL_DIRECTORY_INFO *)current_entry;
		filename = &pFindData->FileName[0];
		len = le32_to_cpu(pFindData->FileNameLength);
	} else if(cfile->srch_inf.info_level == 0x105) {
	} else if(cfile->srch_inf.info_level ==
			SMB_FIND_FILE_ID_FULL_DIR_INFO) {
		SEARCH_ID_FULL_DIR_INFO * pFindData = 
			(SEARCH_ID_FULL_DIR_INFO *)current_entry;
		filename = &pFindData->FileName[0];
		len = le32_to_cpu(pFindData->FileNameLength);
	} else if(cfile->srch_inf.info_level == 0x104) {
	} else if(cfile->srch_inf.info_level == 
			SMB_FIND_FILE_BOTH_DIRECTORY_INFO) {
		FILE_BOTH_DIRECTORY_INFO * pFindData = 
			(FILE_BOTH_DIRECTORY_INFO *)current_entry;
		filename = &pFindData->FileName[0];
		len = le32_to_cpu(pFindData->FileNameLength);
	} else if(cfile->srch_inf.info_level == SMB_FIND_FILE_INFO_STANDARD) {
		FIND_FILE_STANDARD_INFO * pFindData =
			(FIND_FILE_STANDARD_INFO *)current_entry;
		filename = &pFindData->FileName[0];
		len = le32_to_cpu(pFindData->FileNameLength);
	} else {
		cFYI(1,("Unknown findfirst level %d",cfile->srch_inf.info_level));
	}
@@ -652,9 +693,11 @@ static int find_cifs_entry(const int xid, struct cifsTconInfo *pTcon,
					- cifsFile->srch_inf.entries_in_buffer;
		pos_in_buf = index_to_find - first_entry_in_buffer;
		cFYI(1,("found entry - pos_in_buf %d",pos_in_buf));

		for(i=0;(i<(pos_in_buf)) && (current_entry != NULL);i++) {
			/* go entry by entry figuring out which is first */
			current_entry = nxt_dir_entry(current_entry,end_of_smb);
			current_entry = nxt_dir_entry(current_entry,end_of_smb,
						cifsFile->srch_inf.info_level);
		}
		if((current_entry == NULL) && (i < pos_in_buf)) {
			/* BB fixme - check if we should flag this error */
@@ -681,7 +724,7 @@ static int find_cifs_entry(const int xid, struct cifsTconInfo *pTcon,
/* inode num, inode type and filename returned */
static int cifs_get_name_from_search_buf(struct qstr *pqst,
	char *current_entry, __u16 level, unsigned int unicode,
	struct cifs_sb_info * cifs_sb, ino_t *pinum)
	struct cifs_sb_info * cifs_sb, int max_len, ino_t *pinum)
{
	int rc = 0;
	unsigned int len = 0;
@@ -725,10 +768,22 @@ static int cifs_get_name_from_search_buf(struct qstr *pqst,
			(FILE_BOTH_DIRECTORY_INFO *)current_entry;
		filename = &pFindData->FileName[0];
		len = le32_to_cpu(pFindData->FileNameLength);
	} else if(level == SMB_FIND_FILE_INFO_STANDARD) {
		FIND_FILE_STANDARD_INFO * pFindData =
			(FIND_FILE_STANDARD_INFO *)current_entry;
		filename = &pFindData->FileName[0];
		/* one byte length, no name conversion */
		len = (unsigned int)pFindData->FileNameLength;
	} else {
		cFYI(1,("Unknown findfirst level %d",level));
		return -EINVAL;
	}

	if(len > max_len) {
		cERROR(1,("bad search response length %d past smb end", len));
		return -EINVAL;
	}

	if(unicode) {
		/* BB fixme - test with long names */
		/* Note converted filename can be longer than in unicode */
@@ -748,7 +803,7 @@ static int cifs_get_name_from_search_buf(struct qstr *pqst,
}

static int cifs_filldir(char *pfindEntry, struct file *file,
	filldir_t filldir, void *direntry, char *scratch_buf)
	filldir_t filldir, void *direntry, char *scratch_buf, int max_len)
{
	int rc = 0;
	struct qstr qstring;
@@ -784,6 +839,7 @@ static int cifs_filldir(char *pfindEntry, struct file *file,
	rc = cifs_get_name_from_search_buf(&qstring,pfindEntry,
			pCifsF->srch_inf.info_level,
			pCifsF->srch_inf.unicode,cifs_sb,
			max_len,
			&inum /* returned */);

	if(rc)
@@ -805,13 +861,16 @@ static int cifs_filldir(char *pfindEntry, struct file *file,
	/* we pass in rc below, indicating whether it is a new inode,
	   so we can figure out whether to invalidate the inode cached
	   data if the file has changed */
	if(pCifsF->srch_inf.info_level == SMB_FIND_FILE_UNIX) {
	if(pCifsF->srch_inf.info_level == SMB_FIND_FILE_UNIX)
		unix_fill_in_inode(tmp_inode,
				   (FILE_UNIX_INFO *)pfindEntry,&obj_type, rc);
	} else {
		fill_in_inode(tmp_inode,
			      (FILE_DIRECTORY_INFO *)pfindEntry,&obj_type, rc);
	}
				   (FILE_UNIX_INFO *)pfindEntry,
				   &obj_type, rc);
	else if(pCifsF->srch_inf.info_level == SMB_FIND_FILE_INFO_STANDARD)
		fill_in_inode(tmp_inode, 0 /* old level 1 buffer type */,
				pfindEntry, &obj_type, rc);
	else
		fill_in_inode(tmp_inode, 1 /* NT */, pfindEntry, &obj_type, rc);
	
	
	rc = filldir(direntry,qstring.name,qstring.len,file->f_pos,
		     tmp_inode->i_ino,obj_type);
@@ -871,6 +930,12 @@ static int cifs_save_resume_key(const char *current_entry,
		filename = &pFindData->FileName[0];
		len = le32_to_cpu(pFindData->FileNameLength);
		cifsFile->srch_inf.resume_key = pFindData->FileIndex;
	} else if(level == SMB_FIND_FILE_INFO_STANDARD) {
		FIND_FILE_STANDARD_INFO * pFindData =
			(FIND_FILE_STANDARD_INFO *)current_entry;
		filename = &pFindData->FileName[0];
		/* one byte length, no name conversion */
		len = (unsigned int)pFindData->FileNameLength;
	} else {
		cFYI(1,("Unknown findfirst level %d",level));
		return -EINVAL;
@@ -891,6 +956,7 @@ int cifs_readdir(struct file *file, void *direntry, filldir_t filldir)
	int num_to_fill = 0;
	char * tmp_buf = NULL;
	char * end_of_smb;
	int max_len;

	xid = GetXid();

@@ -967,9 +1033,10 @@ int cifs_readdir(struct file *file, void *direntry, filldir_t filldir)
		}
		cFYI(1,("loop through %d times filling dir for net buf %p",
			num_to_fill,cifsFile->srch_inf.ntwrk_buf_start));
		end_of_smb = cifsFile->srch_inf.ntwrk_buf_start +
			smbCalcSize((struct smb_hdr *)
		max_len = smbCalcSize((struct smb_hdr *)
				cifsFile->srch_inf.ntwrk_buf_start);
		end_of_smb = cifsFile->srch_inf.ntwrk_buf_start + max_len;

		/* To be safe - for UCS to UTF-8 with strings loaded
		with the rare long characters alloc more to account for
		such multibyte target UTF-8 characters. cifs_unicode.c,
@@ -985,7 +1052,7 @@ int cifs_readdir(struct file *file, void *direntry, filldir_t filldir)
			/* if buggy server returns . and .. late do
			we want to check for that here? */
			rc = cifs_filldir(current_entry, file,
					filldir, direntry,tmp_buf);
					filldir, direntry, tmp_buf, max_len);
			file->f_pos++;
			if(file->f_pos == 
				cifsFile->srch_inf.index_of_last_entry) {
@@ -994,8 +1061,9 @@ int cifs_readdir(struct file *file, void *direntry, filldir_t filldir)
				cifs_save_resume_key(current_entry,cifsFile);
				break;
			} else 
				current_entry = nxt_dir_entry(current_entry,
							      end_of_smb);
				current_entry = 
					nxt_dir_entry(current_entry, end_of_smb,
						cifsFile->srch_inf.info_level);
		}
		kfree(tmp_buf);
		break;