Samba 3.0.x trans2findfirst optimization revisited

Matt Mitchell mmitchell at virtualproperties.com
Fri Jan 14 22:16:14 GMT 2005


Ladies and gents,

Late last year I was whining about Samba3's relatively poor performance 
when you have many files (> 100000) in a directory.  Regrettably, 
turning case sensitivity on did not help much (since it presumably would 
have made unnecessary all of the gymnastics that Samba has to do in 
order to honor Windows filename semantics).

After some searching I had found that, regardless of case sensitivity, 
the call_trans2findfirst was always opening a dptr to the directory, 
which loaded up the whole thing into memory each time.  The simple 
optimization I settled on involved short-circuiting that; if we are in 
case-sensitive mode and not looking for a wildcard, we can simply use 
the result of the VFS stat when doing a findfirst.

To avoid duplicating all of the logic inside of get_lanman2_dir_entry I 
split its functionality into two different functions, one that does the 
existing lookup logic and another that simply populates the return data 
structure.  (The new function is populate_lanman2_dir_entry and that 
modification makes up the majority of the size of the patch.)

I had submitted a patch against 3.0.8 which was correct and a patch 
against svn which was not.  Here is a patch against the SAMBA_3_0 head. 
  Please send comments and flames.  I know it is hackish, but it makes 
Samba usable for huge directories provided you are in case-sensitive 
mode (eg. CIFS operations between Unix boxes).

Thanks in advance (and I will actually attach a patch this time!).

Matt Mitchell
-------------- next part --------------
--- samba3-orig/source/smbd/trans2.c	2004-12-10 17:26:44.000000000 -0600
+++ samba3/source/smbd/trans2.c	2004-12-10 17:11:19.000000000 -0600
@@ -33,6 +33,12 @@
 #define get_file_size(sbuf) ((sbuf).st_size)
 #define DIR_ENTRY_SAFETY_MARGIN 4096
 
+static BOOL populate_lanman2_dir_entry(connection_struct *conn, 
+				void *outbuf, char** ppdata, char** pp,
+				pstring fname, pstring pathreal,
+				int dirtype, int info_level,
+				int requires_resume_key,
+				SMB_STRUCT_STAT sbuf, char* base_data, int* last_name_off);
 /********************************************************************
  Roundup a value to the nearest SMB_ROUNDUP_ALLOCATION_SIZE boundary.
  Only do this for Windows clients.
@@ -835,17 +841,11 @@
 	pstring mask;
 	pstring pathreal;
 	pstring fname;
-	char *p, *q, *pdata = *ppdata;
+	char *p, *pdata = *ppdata;
 	uint32 reskey=0;
 	int prev_dirpos=0;
 	int mode=0;
-	SMB_OFF_T file_size = 0;
-	SMB_BIG_UINT allocation_size = 0;
-	uint32 len;
-	time_t mdate=0, adate=0, cdate=0;
 	char *nameptr;
-	BOOL was_8_3;
-	int nt_extmode; /* Used for NT connections instead of mode */
 	BOOL needslash = ( conn->dirpath[strlen(conn->dirpath) -1] != '/');
 
 	*fname = 0;
@@ -944,35 +944,14 @@
 				}
 			}
 
-			mode = dos_mode(conn,pathreal,&sbuf);
+			DEBUG(5,("get_lanman2_dir_entry found %s fname=%s\n",pathreal,fname));
 
+			mode = dos_mode(conn,pathreal,&sbuf);
+	  
 			if (!dir_check_ftype(conn,mode,&sbuf,dirtype)) {
 				DEBUG(5,("[%s] attribs didn't match %x\n",fname,dirtype));
-				continue;
-			}
-
-			file_size = get_file_size(sbuf);
-			allocation_size = get_allocation_size(NULL,&sbuf);
-			mdate = sbuf.st_mtime;
-			adate = sbuf.st_atime;
-			cdate = get_create_time(&sbuf,lp_fake_dir_create_times(SNUM(conn)));
-
-			if (lp_dos_filetime_resolution(SNUM(conn))) {
-				cdate &= ~1;
-				mdate &= ~1;
-				adate &= ~1;
-			}
-
-			if(mode & aDIR) {
-				/* This is necessary, as otherwise the
-				 * desktop.ini file in this folder is
-				 * ignored */
-				mode |= (lp_profile_acls(SNUM(conn)) ? aRONLY : 0);
-				file_size = 0;
+				return False;
 			}
-
-			DEBUG(5,("get_lanman2_dir_entry found %s fname=%s\n",pathreal,fname));
-	  
 			found = True;
 		}
 	}
@@ -982,411 +961,103 @@
 	p = pdata;
 	nameptr = p;
 
-	nt_extmode = mode ? mode : FILE_ATTRIBUTE_NORMAL;
+	if(!populate_lanman2_dir_entry(conn, 
+			outbuf, ppdata, &p, 
+			fname, pathreal,
+			dirtype, info_level,
+			requires_resume_key, 
+			sbuf, base_data, last_name_off)) {
+		/* Some problem we should probably do a better job of handling... */
+		DEBUG(3,("populate_lanman2_dir_entry failed!"));
+		return False;
+	}
+			
+	
+	if (PTR_DIFF(p,pdata) > space_remaining) {
+		/* Move the dirptr back to prev_dirpos */
+		SeekDir(conn->dirptr, prev_dirpos);
+		*out_of_space = True;
+		DEBUG(9,("get_lanman2_dir_entry: out of space\n"));
+		return False; /* Not finished - just out of space */
+	}
 
-	switch (info_level) {
-		case SMB_INFO_STANDARD:
-			DEBUG(10,("get_lanman2_dir_entry: SMB_INFO_STANDARD\n"));
-			if(requires_resume_key) {
-				SIVAL(p,0,reskey);
-				p += 4;
-			}
-			put_dos_date2(p,l1_fdateCreation,cdate);
-			put_dos_date2(p,l1_fdateLastAccess,adate);
-			put_dos_date2(p,l1_fdateLastWrite,mdate);
-			SIVAL(p,l1_cbFile,(uint32)file_size);
-			SIVAL(p,l1_cbFileAlloc,(uint32)allocation_size);
-			SSVAL(p,l1_attrFile,mode);
-			p += l1_achName;
-			nameptr = p;
-			p += align_string(outbuf, p, 0);
-			len = srvstr_push(outbuf, p, fname, -1, STR_TERMINATE);
-			if (SVAL(outbuf, smb_flg2) & FLAGS2_UNICODE_STRINGS) {
-				if (len > 2) {
-					SCVAL(nameptr, -1, len - 2);
-				} else {
-					SCVAL(nameptr, -1, 0);
-				}
-			} else {
-				if (len > 1) {
-					SCVAL(nameptr, -1, len - 1);
-				} else {
-					SCVAL(nameptr, -1, 0);
-				}
-			}
-			p += len;
-			break;
+	/* Setup the last_filename pointer, as an offset from base_data */
+	/* *last_name_off = PTR_DIFF(nameptr,base_data); */
+	/* Advance the data pointer to the next slot */
+	*ppdata = p;
 
-		case SMB_INFO_QUERY_EA_SIZE:
-			DEBUG(10,("get_lanman2_dir_entry: SMB_INFO_QUERY_EA_SIZE\n"));
-			if(requires_resume_key) {
-				SIVAL(p,0,reskey);
-				p += 4;
-			}
-			put_dos_date2(p,l2_fdateCreation,cdate);
-			put_dos_date2(p,l2_fdateLastAccess,adate);
-			put_dos_date2(p,l2_fdateLastWrite,mdate);
-			SIVAL(p,l2_cbFile,(uint32)file_size);
-			SIVAL(p,l2_cbFileAlloc,(uint32)allocation_size);
-			SSVAL(p,l2_attrFile,mode);
-			{
-				unsigned int ea_size = estimate_ea_size(conn, NULL, pathreal);
-				SIVAL(p,l2_cbList,ea_size); /* Extended attributes */
-			}
-			p += l2_achName;
-			nameptr = p - 1;
-			len = srvstr_push(outbuf, p, fname, -1, STR_TERMINATE | STR_NOALIGN);
-			if (SVAL(outbuf, smb_flg2) & FLAGS2_UNICODE_STRINGS) {
-				if (len > 2) {
-					len -= 2;
-				} else {
-					len = 0;
-				}
-			} else {
-				if (len > 1) {
-					len -= 1;
-				} else {
-					len = 0;
-				}
-			}
-			SCVAL(nameptr,0,len);
-			p += len;
-			SCVAL(p,0,0); p += 1; /* Extra zero byte ? - why.. */
-			break;
+	return(found);
+}
 
-		case SMB_FIND_FILE_BOTH_DIRECTORY_INFO:
-			DEBUG(10,("get_lanman2_dir_entry: SMB_FIND_FILE_BOTH_DIRECTORY_INFO\n"));
-			was_8_3 = mangle_is_8_3(fname, True);
-			p += 4;
-			SIVAL(p,0,reskey); p += 4;
-			put_long_date(p,cdate); p += 8;
-			put_long_date(p,adate); p += 8;
-			put_long_date(p,mdate); p += 8;
-			put_long_date(p,mdate); p += 8;
-			SOFF_T(p,0,file_size); p += 8;
-			SOFF_T(p,0,allocation_size); p += 8;
-			SIVAL(p,0,nt_extmode); p += 4;
-			q = p; p += 4; /* q is placeholder for name length. */
-			{
-				unsigned int ea_size = estimate_ea_size(conn, NULL, pathreal);
-				SIVAL(p,0,ea_size); /* Extended attributes */
-				p += 4;
-			}
-			/* Clear the short name buffer. This is
-			 * IMPORTANT as not doing so will trigger
-			 * a Win2k client bug. JRA.
-			 */
-			memset(p,'\0',26);
-			if (!was_8_3 && lp_manglednames(SNUM(conn))) {
-				pstring mangled_name;
-				pstrcpy(mangled_name, fname);
-				mangle_map(mangled_name,True,True,SNUM(conn));
-				mangled_name[12] = 0;
-				len = srvstr_push(outbuf, p+2, mangled_name, 24, STR_UPPER|STR_UNICODE);
-				SSVAL(p, 0, len);
-			} else {
-				SSVAL(p,0,0);
-				*(p+2) = 0;
-			}
-			p += 2 + 24;
-			len = srvstr_push(outbuf, p, fname, -1, STR_TERMINATE_ASCII);
-			SIVAL(q,0,len);
-			p += len;
-			len = PTR_DIFF(p, pdata);
-			len = (len + 3) & ~3;
-			SIVAL(pdata,0,len);
-			p = pdata + len;
-			break;
+/****************************************************************************
+ Reply to a TRANS2_FINDFIRST.
+****************************************************************************/
 
-		case SMB_FIND_FILE_DIRECTORY_INFO:
-			DEBUG(10,("get_lanman2_dir_entry: SMB_FIND_FILE_DIRECTORY_INFO\n"));
-			p += 4;
-			SIVAL(p,0,reskey); p += 4;
-			put_long_date(p,cdate); p += 8;
-			put_long_date(p,adate); p += 8;
-			put_long_date(p,mdate); p += 8;
-			put_long_date(p,mdate); p += 8;
-			SOFF_T(p,0,file_size); p += 8;
-			SOFF_T(p,0,allocation_size); p += 8;
-			SIVAL(p,0,nt_extmode); p += 4;
-			len = srvstr_push(outbuf, p + 4, fname, -1, STR_TERMINATE_ASCII);
-			SIVAL(p,0,len);
-			p += 4 + len;
-			len = PTR_DIFF(p, pdata);
-			len = (len + 3) & ~3;
-			SIVAL(pdata,0,len);
-			p = pdata + len;
-			break;
-      
-		case SMB_FIND_FILE_FULL_DIRECTORY_INFO:
-			DEBUG(10,("get_lanman2_dir_entry: SMB_FIND_FILE_FULL_DIRECTORY_INFO\n"));
-			p += 4;
-			SIVAL(p,0,reskey); p += 4;
-			put_long_date(p,cdate); p += 8;
-			put_long_date(p,adate); p += 8;
-			put_long_date(p,mdate); p += 8;
-			put_long_date(p,mdate); p += 8;
-			SOFF_T(p,0,file_size); p += 8;
-			SOFF_T(p,0,allocation_size); p += 8;
-			SIVAL(p,0,nt_extmode); p += 4;
-			q = p; p += 4; /* q is placeholder for name length. */
-			{
-				unsigned int ea_size = estimate_ea_size(conn, NULL, pathreal);
-				SIVAL(p,0,ea_size); /* Extended attributes */
-				p +=4;
-			}
-			len = srvstr_push(outbuf, p, fname, -1, STR_TERMINATE_ASCII);
-			SIVAL(q, 0, len);
-			p += len;
+static int call_trans2findfirst(connection_struct *conn, char *inbuf, char *outbuf, int bufsize,  
+				char **pparams, int total_params, char **ppdata, int total_data,
+				unsigned int max_data_bytes)
+{
+	/* We must be careful here that we don't return more than the
+		allowed number of data bytes. If this means returning fewer than
+		maxentries then so be it. We assume that the redirector has
+		enough room for the fixed number of parameter bytes it has
+		requested. */
+	char *params = *pparams;
+	char *pdata = *ppdata;
+	int dirtype = SVAL(params,0);
+	int maxentries = SVAL(params,2);
+	uint16 findfirst_flags = SVAL(params,4);
+	BOOL close_after_first = (findfirst_flags & FLAG_TRANS2_FIND_CLOSE);
+	BOOL close_if_end = (findfirst_flags & FLAG_TRANS2_FIND_CLOSE_IF_END);
+	BOOL requires_resume_key = (findfirst_flags & FLAG_TRANS2_FIND_REQUIRE_RESUME);
+	int info_level = SVAL(params,6);
+	pstring directory;
+	pstring mask;
+	char *p, *wcard;
+	int last_name_off=0;
+	int dptr_num = -1;
+	int numentries = 0;
+	int i;
+	BOOL finished = False;
+	BOOL dont_descend = False;
+	BOOL out_of_space = False;
+	int space_remaining;
+	BOOL bad_path = False;
+	SMB_STRUCT_STAT sbuf;
+	NTSTATUS ntstatus = NT_STATUS_OK;
 
-			len = PTR_DIFF(p, pdata);
-			len = (len + 3) & ~3;
-			SIVAL(pdata,0,len);
-			p = pdata + len;
-			break;
+	if (total_params < 12)
+		return(ERROR_DOS(ERRDOS,ERRinvalidparam));
+
+	*directory = *mask = 0;
+
+	DEBUG(3,("call_trans2findfirst: dirtype = %d, maxentries = %d, close_after_first=%d, \
+close_if_end = %d requires_resume_key = %d level = 0x%x, max_data_bytes = %d\n",
+		dirtype, maxentries, close_after_first, close_if_end, requires_resume_key,
+		info_level, max_data_bytes));
 
+	if (!maxentries) {
+		/* W2K3 seems to treat zero as 1. */
+		maxentries = 1;
+	}
+ 
+	switch (info_level) {
+		case SMB_INFO_STANDARD:
+		case SMB_INFO_QUERY_EA_SIZE:
+		case SMB_FIND_FILE_DIRECTORY_INFO:
+		case SMB_FIND_FILE_FULL_DIRECTORY_INFO:
 		case SMB_FIND_FILE_NAMES_INFO:
-			DEBUG(10,("get_lanman2_dir_entry: SMB_FIND_FILE_NAMES_INFO\n"));
-			p += 4;
-			SIVAL(p,0,reskey); p += 4;
-			p += 4;
-			/* this must *not* be null terminated or w2k gets in a loop trying to set an
-			   acl on a dir (tridge) */
-			len = srvstr_push(outbuf, p, fname, -1, STR_TERMINATE_ASCII);
-			SIVAL(p, -4, len);
-			p += len;
-			len = PTR_DIFF(p, pdata);
-			len = (len + 3) & ~3;
-			SIVAL(pdata,0,len);
-			p = pdata + len;
+		case SMB_FIND_FILE_BOTH_DIRECTORY_INFO:
+		case SMB_FIND_ID_FULL_DIRECTORY_INFO:
+		case SMB_FIND_ID_BOTH_DIRECTORY_INFO:
+			break;
+		case SMB_FIND_FILE_UNIX:
+			if (!lp_unix_extensions())
+				return(ERROR_DOS(ERRDOS,ERRunknownlevel));
 			break;
-
-		case SMB_FIND_ID_FULL_DIRECTORY_INFO:
-			DEBUG(10,("get_lanman2_dir_entry: SMB_FIND_ID_FULL_DIRECTORY_INFO\n"));
-			p += 4;
-			SIVAL(p,0,reskey); p += 4;
-			put_long_date(p,cdate); p += 8;
-			put_long_date(p,adate); p += 8;
-			put_long_date(p,mdate); p += 8;
-			put_long_date(p,mdate); p += 8;
-			SOFF_T(p,0,file_size); p += 8;
-			SOFF_T(p,0,allocation_size); p += 8;
-			SIVAL(p,0,nt_extmode); p += 4;
-			q = p; p += 4; /* q is placeholder for name length. */
-			{
-				unsigned int ea_size = estimate_ea_size(conn, NULL, pathreal);
-				SIVAL(p,0,ea_size); /* Extended attributes */
-				p +=4;
-			}
-			SIVAL(p,0,0); p += 4; /* Unknown - reserved ? */
-			SIVAL(p,0,sbuf.st_dev); p += 4;
-			SIVAL(p,0,sbuf.st_ino); p += 4;
-			len = srvstr_push(outbuf, p, fname, -1, STR_TERMINATE_ASCII);
-			SIVAL(q, 0, len);
-			p += len; 
-			len = PTR_DIFF(p, pdata);
-			len = (len + 3) & ~3;
-			SIVAL(pdata,0,len);
-			p = pdata + len;
-			break;
-
-		case SMB_FIND_ID_BOTH_DIRECTORY_INFO:
-			DEBUG(10,("get_lanman2_dir_entry: SMB_FIND_ID_BOTH_DIRECTORY_INFO\n"));
-			was_8_3 = mangle_is_8_3(fname, True);
-			p += 4;
-			SIVAL(p,0,reskey); p += 4;
-			put_long_date(p,cdate); p += 8;
-			put_long_date(p,adate); p += 8;
-			put_long_date(p,mdate); p += 8;
-			put_long_date(p,mdate); p += 8;
-			SOFF_T(p,0,file_size); p += 8;
-			SOFF_T(p,0,allocation_size); p += 8;
-			SIVAL(p,0,nt_extmode); p += 4;
-			q = p; p += 4; /* q is placeholder for name length */
-			{
-				unsigned int ea_size = estimate_ea_size(conn, NULL, pathreal);
-				SIVAL(p,0,ea_size); /* Extended attributes */
-				p +=4;
-			}
-			/* Clear the short name buffer. This is
-			 * IMPORTANT as not doing so will trigger
-			 * a Win2k client bug. JRA.
-			 */
-			memset(p,'\0',26);
-			if (!was_8_3 && lp_manglednames(SNUM(conn))) {
-				pstring mangled_name;
-				pstrcpy(mangled_name, fname);
-				mangle_map(mangled_name,True,True,SNUM(conn));
-				mangled_name[12] = 0;
-				len = srvstr_push(outbuf, p+2, mangled_name, 24, STR_UPPER|STR_UNICODE);
-				SSVAL(p, 0, len);
-			} else {
-				SSVAL(p,0,0);
-				*(p+2) = 0;
-			}
-			p += 26;
-			SSVAL(p,0,0); p += 2; /* Reserved ? */
-			SIVAL(p,0,sbuf.st_dev); p += 4;
-			SIVAL(p,0,sbuf.st_ino); p += 4;
-			len = srvstr_push(outbuf, p, fname, -1, STR_TERMINATE_ASCII);
-			SIVAL(q,0,len);
-			p += len;
-			len = PTR_DIFF(p, pdata);
-			len = (len + 3) & ~3;
-			SIVAL(pdata,0,len);
-			p = pdata + len;
-			break;
-
-		/* CIFS UNIX Extension. */
-
-		case SMB_FIND_FILE_UNIX:
-			DEBUG(10,("get_lanman2_dir_entry: SMB_FIND_FILE_UNIX\n"));
-			p+= 4;
-			SIVAL(p,0,reskey); p+= 4;    /* Used for continuing search. */
-
-			/* Begin of SMB_QUERY_FILE_UNIX_BASIC */
-			SOFF_T(p,0,get_file_size(sbuf));             /* File size 64 Bit */
-			p+= 8;
-
-			SOFF_T(p,0,get_allocation_size(NULL,&sbuf)); /* Number of bytes used on disk - 64 Bit */
-			p+= 8;
-
-			put_long_date(p,sbuf.st_ctime);       /* Inode change Time 64 Bit */
-			put_long_date(p+8,sbuf.st_atime);     /* Last access time 64 Bit */
-			put_long_date(p+16,sbuf.st_mtime);    /* Last modification time 64 Bit */
-			p+= 24;
-
-			SIVAL(p,0,sbuf.st_uid);               /* user id for the owner */
-			SIVAL(p,4,0);
-			p+= 8;
-
-			SIVAL(p,0,sbuf.st_gid);               /* group id of owner */
-			SIVAL(p,4,0);
-			p+= 8;
-
-			SIVAL(p,0,unix_filetype(sbuf.st_mode));
-			p+= 4;
-
-			SIVAL(p,0,unix_dev_major(sbuf.st_rdev));   /* Major device number if type is device */
-			SIVAL(p,4,0);
-			p+= 8;
-
-			SIVAL(p,0,unix_dev_minor(sbuf.st_rdev));   /* Minor device number if type is device */
-			SIVAL(p,4,0);
-			p+= 8;
-
-			SINO_T(p,0,(SMB_INO_T)sbuf.st_ino);   /* inode number */
-			p+= 8;
-
-			SIVAL(p,0, unix_perms_to_wire(sbuf.st_mode));     /* Standard UNIX file permissions */
-			SIVAL(p,4,0);
-			p+= 8;
-
-			SIVAL(p,0,sbuf.st_nlink);             /* number of hard links */
-			SIVAL(p,4,0);
-			p+= 8;
-
-			len = srvstr_push(outbuf, p, fname, -1, STR_TERMINATE);
-			p += len;
-
-			len = PTR_DIFF(p, pdata);
-			len = (len + 3) & ~3;
-			SIVAL(pdata,0,len);	/* Offset from this structure to the beginning of the next one */
-			p = pdata + len;
-			/* End of SMB_QUERY_FILE_UNIX_BASIC */
-
-			break;
-
-		default:      
-			return(False);
-	}
-
-
-	if (PTR_DIFF(p,pdata) > space_remaining) {
-		/* Move the dirptr back to prev_dirpos */
-		SeekDir(conn->dirptr, prev_dirpos);
-		*out_of_space = True;
-		DEBUG(9,("get_lanman2_dir_entry: out of space\n"));
-		return False; /* Not finished - just out of space */
-	}
-
-	/* Setup the last_filename pointer, as an offset from base_data */
-	*last_name_off = PTR_DIFF(nameptr,base_data);
-	/* Advance the data pointer to the next slot */
-	*ppdata = p;
-
-	return(found);
-}
-
-/****************************************************************************
- Reply to a TRANS2_FINDFIRST.
-****************************************************************************/
-
-static int call_trans2findfirst(connection_struct *conn, char *inbuf, char *outbuf, int bufsize,  
-				char **pparams, int total_params, char **ppdata, int total_data,
-				unsigned int max_data_bytes)
-{
-	/* We must be careful here that we don't return more than the
-		allowed number of data bytes. If this means returning fewer than
-		maxentries then so be it. We assume that the redirector has
-		enough room for the fixed number of parameter bytes it has
-		requested. */
-	char *params = *pparams;
-	char *pdata = *ppdata;
-	int dirtype = SVAL(params,0);
-	int maxentries = SVAL(params,2);
-	uint16 findfirst_flags = SVAL(params,4);
-	BOOL close_after_first = (findfirst_flags & FLAG_TRANS2_FIND_CLOSE);
-	BOOL close_if_end = (findfirst_flags & FLAG_TRANS2_FIND_CLOSE_IF_END);
-	BOOL requires_resume_key = (findfirst_flags & FLAG_TRANS2_FIND_REQUIRE_RESUME);
-	int info_level = SVAL(params,6);
-	pstring directory;
-	pstring mask;
-	char *p, *wcard;
-	int last_name_off=0;
-	int dptr_num = -1;
-	int numentries = 0;
-	int i;
-	BOOL finished = False;
-	BOOL dont_descend = False;
-	BOOL out_of_space = False;
-	int space_remaining;
-	BOOL bad_path = False;
-	SMB_STRUCT_STAT sbuf;
-	NTSTATUS ntstatus = NT_STATUS_OK;
-
-	if (total_params < 12)
-		return(ERROR_DOS(ERRDOS,ERRinvalidparam));
-
-	*directory = *mask = 0;
-
-	DEBUG(3,("call_trans2findfirst: dirtype = %d, maxentries = %d, close_after_first=%d, \
-close_if_end = %d requires_resume_key = %d level = 0x%x, max_data_bytes = %d\n",
-		dirtype, maxentries, close_after_first, close_if_end, requires_resume_key,
-		info_level, max_data_bytes));
-
-	if (!maxentries) {
-		/* W2K3 seems to treat zero as 1. */
-		maxentries = 1;
-	}
- 
-	switch (info_level) {
-		case SMB_INFO_STANDARD:
-		case SMB_INFO_QUERY_EA_SIZE:
-		case SMB_FIND_FILE_DIRECTORY_INFO:
-		case SMB_FIND_FILE_FULL_DIRECTORY_INFO:
-		case SMB_FIND_FILE_NAMES_INFO:
-		case SMB_FIND_FILE_BOTH_DIRECTORY_INFO:
-		case SMB_FIND_ID_FULL_DIRECTORY_INFO:
-		case SMB_FIND_ID_BOTH_DIRECTORY_INFO:
-			break;
-		case SMB_FIND_FILE_UNIX:
-			if (!lp_unix_extensions())
-				return(ERROR_DOS(ERRDOS,ERRunknownlevel));
-			break;
-		default:
-			return(ERROR_DOS(ERRDOS,ERRunknownlevel));
-	}
+		default:
+			return(ERROR_DOS(ERRDOS,ERRunknownlevel));
+	}
 
 	srvstr_get_path(inbuf, directory, params+12, sizeof(directory), -1, STR_TERMINATE, &ntstatus, True);
 	if (!NT_STATUS_IS_OK(ntstatus)) {
@@ -4574,3 +4245,384 @@
 	END_PROFILE(SMBtrans2);
 	return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
 }
+
+/**************************************************************************
+ * Actually populate the lanman buffer pointed to by *ppdata.  Split out
+ * of get_lanman2_dir_entry so that we could short-circuit the dirptr
+ * operations where possible.
+ **************************************************************************/
+static BOOL populate_lanman2_dir_entry(connection_struct *conn, 
+				void *outbuf, char** ppdata, char** pp,
+				pstring fname, pstring pathreal,
+				int dirtype, int info_level,
+				int requires_resume_key,
+				SMB_STRUCT_STAT sbuf, char* base_data, int* last_name_off)
+{
+	BOOL was_8_3 = False;
+	SMB_OFF_T file_size;
+	SMB_BIG_UINT allocation_size;
+	int len = 0;
+	char *pdata = *ppdata;
+	char *p = *pp;
+	char *nameptr = p;
+	char *q;
+	int mode;
+	int cdate, mdate, adate;
+	int nt_extmode; /* Used for NT connections instead of mode */
+	uint32 reskey = 0;
+
+	DEBUG(10,("conn 0x%p, outbuf 0x%p, ppdata 0x%p, pp 0x%p, fname %s, pathreal %s, dirtype %d, info_level %d, requires_resume_key %d, base_data 0x%p\n",
+				conn, outbuf, ppdata, pp, fname, pathreal, dirtype, info_level, requires_resume_key, base_data));
+
+	mode = dos_mode(conn,pathreal,&sbuf);
+	DEBUG(5,("dos mode is %d\n",mode));
+	if (!dir_check_ftype(conn,mode,&sbuf,dirtype)) {
+		DEBUG(5,("[%s] attribs didn't match %x\n",fname,dirtype));
+		return False;
+	}
+
+	file_size = get_file_size(sbuf);
+	allocation_size = get_allocation_size(NULL,&sbuf);
+	mdate = sbuf.st_mtime;
+	adate = sbuf.st_atime;
+	cdate = get_create_time(&sbuf,lp_fake_dir_create_times(SNUM(conn)));
+
+	if (lp_dos_filetime_resolution(SNUM(conn))) {
+		cdate &= ~1;
+		mdate &= ~1;
+		adate &= ~1;
+	}
+
+	if(mode & aDIR)
+		file_size = 0;
+
+	nt_extmode = mode ? mode : FILE_ATTRIBUTE_NORMAL;
+
+	switch (info_level) {
+		case SMB_INFO_STANDARD:
+			DEBUG(10,("populate_lanman2_dir_entry: SMB_INFO_STANDARD\n"));
+			if(requires_resume_key) {
+				SIVAL(p,0,reskey);
+				p += 4;
+			}
+			put_dos_date2(p,l1_fdateCreation,cdate);
+			put_dos_date2(p,l1_fdateLastAccess,adate);
+			put_dos_date2(p,l1_fdateLastWrite,mdate);
+			SIVAL(p,l1_cbFile,(uint32)file_size);
+			SIVAL(p,l1_cbFileAlloc,(uint32)allocation_size);
+			SSVAL(p,l1_attrFile,mode);
+			p += l1_achName;
+			nameptr = p;
+			p += align_string(outbuf, p, 0);
+			len = srvstr_push(outbuf, p, fname, -1, STR_TERMINATE);
+			if (SVAL(outbuf, smb_flg2) & FLAGS2_UNICODE_STRINGS) {
+				if (len > 2) {
+					SCVAL(nameptr, -1, len - 2);
+				} else {
+					SCVAL(nameptr, -1, 0);
+				}
+			} else {
+				if (len > 1) {
+					SCVAL(nameptr, -1, len - 1);
+				} else {
+					SCVAL(nameptr, -1, 0);
+				}
+			}
+			p += len;
+			break;
+
+		case SMB_INFO_QUERY_EA_SIZE:
+			DEBUG(10,("populate_lanman2_dir_entry: SMB_INFO_QUERY_EA_SIZE\n"));
+			if(requires_resume_key) {
+				SIVAL(p,0,reskey);
+				p += 4;
+			}
+			put_dos_date2(p,l2_fdateCreation,cdate);
+			put_dos_date2(p,l2_fdateLastAccess,adate);
+			put_dos_date2(p,l2_fdateLastWrite,mdate);
+			SIVAL(p,l2_cbFile,(uint32)file_size);
+			SIVAL(p,l2_cbFileAlloc,(uint32)allocation_size);
+			SSVAL(p,l2_attrFile,mode);
+			{
+				unsigned int ea_size = estimate_ea_size(conn, NULL, pathreal);
+				SIVAL(p,l2_cbList,ea_size); /* Extended attributes */
+			}
+			p += l2_achName;
+			nameptr = p - 1;
+			len = srvstr_push(outbuf, p, fname, -1, STR_TERMINATE | STR_NOALIGN);
+			if (SVAL(outbuf, smb_flg2) & FLAGS2_UNICODE_STRINGS) {
+				if (len > 2) {
+					len -= 2;
+				} else {
+					len = 0;
+				}
+			} else {
+				if (len > 1) {
+					len -= 1;
+				} else {
+					len = 0;
+				}
+			}
+			SCVAL(nameptr,0,len);
+			p += len;
+			SCVAL(p,0,0); p += 1; /* Extra zero byte ? - why.. */
+			break;
+
+		case SMB_FIND_FILE_BOTH_DIRECTORY_INFO:
+			DEBUG(10,("populate_lanman2_dir_entry: SMB_FIND_FILE_BOTH_DIRECTORY_INFO\n"));
+			was_8_3 = mangle_is_8_3(fname, True);
+			p += 4;
+			SIVAL(p,0,reskey); p += 4;
+			put_long_date(p,cdate); p += 8;
+			put_long_date(p,adate); p += 8;
+			put_long_date(p,mdate); p += 8;
+			put_long_date(p,mdate); p += 8;
+			SOFF_T(p,0,file_size); p += 8;
+			SOFF_T(p,0,allocation_size); p += 8;
+			SIVAL(p,0,nt_extmode); p += 4;
+			q = p; p += 4; /* q is placeholder for name length. */
+			{
+				unsigned int ea_size = estimate_ea_size(conn, NULL, pathreal);
+				SIVAL(p,0,ea_size); /* Extended attributes */
+				p += 4;
+			}
+			/* Clear the short name buffer. This is
+			 * IMPORTANT as not doing so will trigger
+			 * a Win2k client bug. JRA.
+			 */
+			memset(p,'\0',26);
+			if (!was_8_3 && lp_manglednames(SNUM(conn))) {
+				pstring mangled_name;
+				pstrcpy(mangled_name, fname);
+				mangle_map(mangled_name,True,True,SNUM(conn));
+				mangled_name[12] = 0;
+				len = srvstr_push(outbuf, p+2, mangled_name, 24, STR_UPPER|STR_UNICODE);
+				SSVAL(p, 0, len);
+			} else {
+				SSVAL(p,0,0);
+				*(p+2) = 0;
+			}
+			p += 2 + 24;
+			len = srvstr_push(outbuf, p, fname, -1, STR_TERMINATE_ASCII);
+			SIVAL(q,0,len);
+			p += len;
+			len = PTR_DIFF(p, pdata);
+			len = (len + 3) & ~3;
+			SIVAL(pdata,0,len);
+			p = pdata + len;
+			break;
+
+		case SMB_FIND_FILE_DIRECTORY_INFO:
+			DEBUG(10,("populate_lanman2_dir_entry: SMB_FIND_FILE_DIRECTORY_INFO\n"));
+			p += 4;
+			SIVAL(p,0,reskey); p += 4;
+			put_long_date(p,cdate); p += 8;
+			put_long_date(p,adate); p += 8;
+			put_long_date(p,mdate); p += 8;
+			put_long_date(p,mdate); p += 8;
+			SOFF_T(p,0,file_size); p += 8;
+			SOFF_T(p,0,allocation_size); p += 8;
+			SIVAL(p,0,nt_extmode); p += 4;
+			len = srvstr_push(outbuf, p + 4, fname, -1, STR_TERMINATE_ASCII);
+			SIVAL(p,0,len);
+			p += 4 + len;
+			len = PTR_DIFF(p, pdata);
+			len = (len + 3) & ~3;
+			SIVAL(pdata,0,len);
+			p = pdata + len;
+			break;
+      
+		case SMB_FIND_FILE_FULL_DIRECTORY_INFO:
+			DEBUG(10,("populate_lanman2_dir_entry: SMB_FIND_FILE_FULL_DIRECTORY_INFO\n"));
+			p += 4;
+			SIVAL(p,0,reskey); p += 4;
+			put_long_date(p,cdate); p += 8;
+			put_long_date(p,adate); p += 8;
+			put_long_date(p,mdate); p += 8;
+			put_long_date(p,mdate); p += 8;
+			SOFF_T(p,0,file_size); p += 8;
+			SOFF_T(p,0,allocation_size); p += 8;
+			SIVAL(p,0,nt_extmode); p += 4;
+			q = p; p += 4; /* q is placeholder for name length. */
+			{
+				unsigned int ea_size = estimate_ea_size(conn, NULL, pathreal);
+				SIVAL(p,0,ea_size); /* Extended attributes */
+				p +=4;
+			}
+			len = srvstr_push(outbuf, p, fname, -1, STR_TERMINATE_ASCII);
+			SIVAL(q, 0, len);
+			p += len;
+
+			len = PTR_DIFF(p, pdata);
+			len = (len + 3) & ~3;
+			SIVAL(pdata,0,len);
+			p = pdata + len;
+			break;
+
+		case SMB_FIND_FILE_NAMES_INFO:
+			DEBUG(10,("populate_lanman2_dir_entry: SMB_FIND_FILE_NAMES_INFO\n"));
+			p += 4;
+			SIVAL(p,0,reskey); p += 4;
+			p += 4;
+			/* this must *not* be null terminated or w2k gets in a loop trying to set an
+			   acl on a dir (tridge) */
+			len = srvstr_push(outbuf, p, fname, -1, STR_TERMINATE_ASCII);
+			SIVAL(p, -4, len);
+			p += len;
+			len = PTR_DIFF(p, pdata);
+			len = (len + 3) & ~3;
+			SIVAL(pdata,0,len);
+			p = pdata + len;
+			break;
+
+		case SMB_FIND_ID_FULL_DIRECTORY_INFO:
+			DEBUG(10,("populate_lanman2_dir_entry: SMB_FIND_ID_FULL_DIRECTORY_INFO\n"));
+			p += 4;
+			SIVAL(p,0,reskey); p += 4;
+			put_long_date(p,cdate); p += 8;
+			put_long_date(p,adate); p += 8;
+			put_long_date(p,mdate); p += 8;
+			put_long_date(p,mdate); p += 8;
+			SOFF_T(p,0,file_size); p += 8;
+			SOFF_T(p,0,allocation_size); p += 8;
+			SIVAL(p,0,nt_extmode); p += 4;
+			q = p; p += 4; /* q is placeholder for name length. */
+			{
+				unsigned int ea_size = estimate_ea_size(conn, NULL, pathreal);
+				SIVAL(p,0,ea_size); /* Extended attributes */
+				p +=4;
+			}
+			SIVAL(p,0,0); p += 4; /* Unknown - reserved ? */
+			SIVAL(p,0,sbuf.st_dev); p += 4;
+			SIVAL(p,0,sbuf.st_ino); p += 4;
+			len = srvstr_push(outbuf, p, fname, -1, STR_TERMINATE_ASCII);
+			SIVAL(q, 0, len);
+			p += len; 
+			len = PTR_DIFF(p, pdata);
+			len = (len + 3) & ~3;
+			SIVAL(pdata,0,len);
+			p = pdata + len;
+			break;
+
+		case SMB_FIND_ID_BOTH_DIRECTORY_INFO:
+			DEBUG(10,("populate_lanman2_dir_entry: SMB_FIND_ID_BOTH_DIRECTORY_INFO\n"));
+			was_8_3 = mangle_is_8_3(fname, True);
+			p += 4;
+			SIVAL(p,0,reskey); p += 4;
+			put_long_date(p,cdate); p += 8;
+			put_long_date(p,adate); p += 8;
+			put_long_date(p,mdate); p += 8;
+			put_long_date(p,mdate); p += 8;
+			SOFF_T(p,0,file_size); p += 8;
+			SOFF_T(p,0,allocation_size); p += 8;
+			SIVAL(p,0,nt_extmode); p += 4;
+			q = p; p += 4; /* q is placeholder for name length */
+			{
+				unsigned int ea_size = estimate_ea_size(conn, NULL, pathreal);
+				SIVAL(p,0,ea_size); /* Extended attributes */
+				p +=4;
+			}
+			/* Clear the short name buffer. This is
+			 * IMPORTANT as not doing so will trigger
+			 * a Win2k client bug. JRA.
+			 */
+			memset(p,'\0',26);
+			if (!was_8_3 && lp_manglednames(SNUM(conn))) {
+				pstring mangled_name;
+				pstrcpy(mangled_name, fname);
+				mangle_map(mangled_name,True,True,SNUM(conn));
+				mangled_name[12] = 0;
+				len = srvstr_push(outbuf, p+2, mangled_name, 24, STR_UPPER|STR_UNICODE);
+				SSVAL(p, 0, len);
+			} else {
+				SSVAL(p,0,0);
+				*(p+2) = 0;
+			}
+			p += 26;
+			SSVAL(p,0,0); p += 2; /* Reserved ? */
+			SIVAL(p,0,sbuf.st_dev); p += 4;
+			SIVAL(p,0,sbuf.st_ino); p += 4;
+			len = srvstr_push(outbuf, p, fname, -1, STR_TERMINATE_ASCII);
+			SIVAL(q,0,len);
+			p += len;
+			len = PTR_DIFF(p, pdata);
+			len = (len + 3) & ~3;
+			SIVAL(pdata,0,len);
+			p = pdata + len;
+			break;
+
+		/* CIFS UNIX Extension. */
+
+		case SMB_FIND_FILE_UNIX:
+			DEBUG(10,("populate_lanman2_dir_entry: SMB_FIND_FILE_UNIX\n"));
+			p+= 4;
+			SIVAL(p,0,reskey); p+= 4;    /* Used for continuing search. */
+
+			/* Begin of SMB_QUERY_FILE_UNIX_BASIC */
+			SOFF_T(p,0,get_file_size(sbuf));             /* File size 64 Bit */
+			p+= 8;
+
+			SOFF_T(p,0,get_allocation_size(NULL,&sbuf)); /* Number of bytes used on disk - 64 Bit */
+			p+= 8;
+
+			put_long_date(p,sbuf.st_ctime);       /* Inode change Time 64 Bit */
+			put_long_date(p+8,sbuf.st_atime);     /* Last access time 64 Bit */
+			put_long_date(p+16,sbuf.st_mtime);    /* Last modification time 64 Bit */
+			p+= 24;
+
+			SIVAL(p,0,sbuf.st_uid);               /* user id for the owner */
+			SIVAL(p,4,0);
+			p+= 8;
+
+			SIVAL(p,0,sbuf.st_gid);               /* group id of owner */
+			SIVAL(p,4,0);
+			p+= 8;
+
+			SIVAL(p,0,unix_filetype(sbuf.st_mode));
+			p+= 4;
+
+			SIVAL(p,0,unix_dev_major(sbuf.st_rdev));   /* Major device number if type is device */
+			SIVAL(p,4,0);
+			p+= 8;
+
+			SIVAL(p,0,unix_dev_minor(sbuf.st_rdev));   /* Minor device number if type is device */
+			SIVAL(p,4,0);
+			p+= 8;
+
+			SINO_T(p,0,(SMB_INO_T)sbuf.st_ino);   /* inode number */
+			p+= 8;
+
+			SIVAL(p,0, unix_perms_to_wire(sbuf.st_mode));     /* Standard UNIX file permissions */
+			SIVAL(p,4,0);
+			p+= 8;
+
+			SIVAL(p,0,sbuf.st_nlink);             /* number of hard links */
+			SIVAL(p,4,0);
+			p+= 8;
+
+			len = srvstr_push(outbuf, p, fname, -1, STR_TERMINATE);
+			p += len;
+
+			len = PTR_DIFF(p, pdata);
+			len = (len + 3) & ~3;
+			SIVAL(pdata,0,len);	/* Offset from this structure to the beginning of the next one */
+			p = pdata + len;
+			/* End of SMB_QUERY_FILE_UNIX_BASIC */
+
+			break;
+
+		default:      
+			return(False);
+	}
+	DEBUG(10,("at exit: conn 0x%p, outbuf 0x%p, ppdata 0x%p, pp 0x%p, "
+			"fname %s, pathreal %s, dirtype %d, info_level %d, "
+			"requires_resume_key %d, base_data 0x%p\n",
+			conn, outbuf, ppdata, pp, fname, pathreal, dirtype, 
+			info_level, requires_resume_key, base_data));
+	DEBUG(10,("at exit: pdata = 0x%p, p = 0x%p\n", pdata, p));
+	*ppdata = pdata;
+	*pp = p;
+	*last_name_off = PTR_DIFF(nameptr,base_data); 
+	return(True);
+}
+    


More information about the samba-technical mailing list