From 3db52feb1f3b2c07ce0b06ad4a7099fa6efe3fc7 Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Mon, 13 Dec 1999 13:27:58 +0000 Subject: first pass at updating head branch to be to be the same as the SAMBA_2_0 branch (This used to be commit 453a822a76780063dff23526c35408866d0c0154) --- source3/smbd/trans2.c | 1034 +++++++++++++++++++++---------------------------- 1 file changed, 432 insertions(+), 602 deletions(-) (limited to 'source3/smbd/trans2.c') diff --git a/source3/smbd/trans2.c b/source3/smbd/trans2.c index 14fa26dd55..e971f8bbb6 100644 --- a/source3/smbd/trans2.c +++ b/source3/smbd/trans2.c @@ -31,7 +31,7 @@ extern int Client; extern int smb_read_error; extern fstring local_machine; extern int global_oplock_break; -extern dfs_internal dfs_struct; +extern uint32 global_client_caps; /**************************************************************************** Send the required number of replies back. @@ -113,8 +113,7 @@ static int send_trans2_replies(char *outbuf, int bufsize, char *params, SSVAL(outbuf,smb_prcnt, params_sent_thistime); if(params_sent_thistime == 0) { - /*SSVAL(outbuf,smb_proff,0);*/ - SSVAL(outbuf,smb_proff,((smb_buf(outbuf)+alignment_offset) - smb_base(outbuf))); + SSVAL(outbuf,smb_proff,0); SSVAL(outbuf,smb_prdisp,0); } else @@ -216,11 +215,7 @@ static int call_trans2open(connection_struct *conn, char *inbuf, char *outbuf, /* XXXX we need to handle passed times, sattr and flags */ - if (!unix_dfs_convert(fname,conn,0,&bad_path,NULL)) - { - SSVAL(outbuf, smb_flg2, FLAGS2_32_BIT_ERROR_CODES); - return(ERROR(0, 0xc0000000|NT_STATUS_PATH_NOT_COVERED)); - } + unix_convert(fname,conn,0,&bad_path,NULL); fsp = file_new(); if (!fsp) @@ -253,7 +248,7 @@ static int call_trans2open(connection_struct *conn, char *inbuf, char *outbuf, return(UNIXERROR(ERRDOS,ERRnoaccess)); } - if (fsp->conn->vfs_ops.fstat(fsp->fd_ptr->fd,&sbuf) != 0) { + if (sys_fstat(fsp->fd_ptr->fd,&sbuf) != 0) { close_file(fsp,False); return(UNIXERROR(ERRDOS,ERRnoaccess)); } @@ -272,7 +267,7 @@ static int call_trans2open(connection_struct *conn, char *inbuf, char *outbuf, if(params == NULL) return(ERROR(ERRDOS,ERRnomem)); - bzero(params,28); + memset((char *)params,'\0',28); SSVAL(params,0,fsp->fnum); SSVAL(params,2,fmode); put_dos_date2(params,4, mtime); @@ -298,12 +293,12 @@ static int call_trans2open(connection_struct *conn, char *inbuf, char *outbuf, /**************************************************************************** get a level dependent lanman2 dir entry. ****************************************************************************/ -static int get_lanman2_dir_entry(connection_struct *conn, +static BOOL get_lanman2_dir_entry(connection_struct *conn, char *path_mask,int dirtype,int info_level, int requires_resume_key, BOOL dont_descend,char **ppdata, char *base_data, int space_remaining, - BOOL *out_of_space, + BOOL *out_of_space, BOOL *got_exact_match, int *last_name_off) { char *dname; @@ -329,6 +324,7 @@ static int get_lanman2_dir_entry(connection_struct *conn, *fname = 0; *out_of_space = False; + *got_exact_match = False; if (!conn->dirptr) return(False); @@ -346,6 +342,8 @@ static int get_lanman2_dir_entry(connection_struct *conn, while (!found) { + BOOL got_match; + /* Needed if we run out of space */ prev_dirpos = TellDir(conn->dirptr); dname = ReadDirName(conn->dirptr); @@ -367,7 +365,26 @@ static int get_lanman2_dir_entry(connection_struct *conn, pstrcpy(fname,dname); - if(mask_match(fname, mask, case_sensitive, True)) + if(!(got_match = *got_exact_match = exact_match(fname, mask, case_sensitive))) + got_match = mask_match(fname, mask, case_sensitive, True); + + if(!got_match && !is_8_3(fname, False)) { + + /* + * It turns out that NT matches wildcards against + * both long *and* short names. This may explain some + * of the wildcard wierdness from old DOS clients + * that some people have been seeing.... JRA. + */ + + pstring newname; + pstrcpy( newname, fname); + name_map_mangle( newname, True, False, SNUM(conn)); + if(!(got_match = *got_exact_match = exact_match(newname, mask, case_sensitive))) + got_match = mask_match(newname, mask, case_sensitive, True); + } + + if(got_match) { BOOL isdots = (strequal(fname,"..") || strequal(fname,".")); if (dont_descend && !isdots) @@ -380,7 +397,7 @@ static int get_lanman2_dir_entry(connection_struct *conn, if(needslash) pstrcat(pathreal,"/"); pstrcat(pathreal,dname); - if (conn->vfs_ops.stat(dos_to_unix(pathreal,False),&sbuf) != 0) + if (dos_stat(pathreal,&sbuf) != 0) { DEBUG(5,("get_lanman2_dir_entry:Couldn't stat [%s] (%s)\n",pathreal,strerror(errno))); continue; @@ -406,7 +423,7 @@ static int get_lanman2_dir_entry(connection_struct *conn, } } - name_map_mangle(fname,False,SNUM(conn)); + name_map_mangle(fname,False,True,SNUM(conn)); p = pdata; nameptr = p; @@ -502,7 +519,7 @@ static int get_lanman2_dir_entry(connection_struct *conn, SIVAL(p,0,0); p += 4; if (!was_8_3) { pstrcpy(p+2,fname); - if (!name_map_mangle(p+2,True,SNUM(conn))) + if (!name_map_mangle(p+2,True,True,SNUM(conn))) (p+2)[12] = 0; } else *(p+2) = 0; @@ -579,6 +596,7 @@ static int get_lanman2_dir_entry(connection_struct *conn, *last_name_off = PTR_DIFF(nameptr,base_data); /* Advance the data pointer to the next slot */ *ppdata = p; + return(found); } @@ -610,8 +628,9 @@ void mask_convert( char *mask) } /**************************************************************************** - reply to a TRANS2_FINDFIRST + Reply to a TRANS2_FINDFIRST. ****************************************************************************/ + static int call_trans2findfirst(connection_struct *conn, char *inbuf, char *outbuf, int bufsize, char **pparams, char **ppdata) @@ -669,11 +688,7 @@ static int call_trans2findfirst(connection_struct *conn, DEBUG(5,("path=%s\n",directory)); - if (!unix_dfs_convert(directory,conn,0,&bad_path,NULL)) - { - SSVAL(outbuf, smb_flg2, FLAGS2_32_BIT_ERROR_CODES); - return(ERROR(0, 0xc0000000|NT_STATUS_PATH_NOT_COVERED)); - } + unix_convert(directory,conn,0,&bad_path,NULL); if(!check_name(directory,conn)) { if((errno == ENOENT) && bad_path) { @@ -708,40 +723,25 @@ static int call_trans2findfirst(connection_struct *conn, pdata = *ppdata = Realloc(*ppdata, max_data_bytes + 1024); if(!*ppdata) return(ERROR(ERRDOS,ERRnomem)); - bzero(pdata,max_data_bytes); + memset((char *)pdata,'\0',max_data_bytes + 1024); /* Realloc the params space */ params = *pparams = Realloc(*pparams, 10); if(params == NULL) return(ERROR(ERRDOS,ERRnomem)); - dptr_num = dptr_create(conn,directory, True ,SVAL(inbuf,smb_pid)); + dptr_num = dptr_create(conn,directory, False, True ,SVAL(inbuf,smb_pid)); if (dptr_num < 0) return(UNIXERROR(ERRDOS,ERRbadfile)); /* Convert the formatted mask. */ mask_convert(mask); -#if 0 /* JRA */ - /* - * Now we have a working mask_match in util.c, I believe - * we no longer need these hacks (in fact they break - * things). JRA. - */ - - /* a special case for 16 bit apps */ - if (strequal(mask,"????????.???")) pstrcpy(mask,"*"); - - /* handle broken clients that send us old 8.3 format */ - string_sub(mask,"????????","*"); - string_sub(mask,".???",".*"); -#endif /* JRA */ - /* Save the wildcard match and attribs we are using on this directory - needed as lanman2 assumes these are being saved between calls */ if(!(wcard = strdup(mask))) { - dptr_close(dptr_num); + dptr_close(&dptr_num); return(ERROR(ERRDOS,ERRnomem)); } @@ -763,39 +763,49 @@ static int call_trans2findfirst(connection_struct *conn, out_of_space = False; for (i=0;(i 0) { + out_of_space = True; + finished = False; + } + else + { + finished = !get_lanman2_dir_entry(conn,mask,dirtype,info_level, + requires_resume_key,dont_descend, + &p,pdata,space_remaining, &out_of_space, &got_exact_match, + &last_name_off); + } - /* this is a heuristic to avoid seeking the dirptr except when - absolutely necessary. It allows for a filename of about 40 chars */ - if (space_remaining < DIRLEN_GUESS && numentries > 0) - { - out_of_space = True; - finished = False; - } - else - { - finished = - !get_lanman2_dir_entry(conn,mask,dirtype,info_level, - requires_resume_key,dont_descend, - &p,pdata,space_remaining, &out_of_space, - &last_name_off); - } + if (finished && out_of_space) + finished = False; - if (finished && out_of_space) - finished = False; + if (!finished && !out_of_space) + numentries++; - if (!finished && !out_of_space) - numentries++; - space_remaining = max_data_bytes - PTR_DIFF(p,pdata); - } + /* + * As an optimisation if we know we aren't looking + * for a wildcard name (ie. the name matches the wildcard exactly) + * then we can finish on any (first) match. + * This speeds up large directory searches. JRA. + */ + + if(got_exact_match) + finished = True; + + space_remaining = max_data_bytes - PTR_DIFF(p,pdata); + } /* Check if we can close the dirptr */ if(close_after_first || (finished && close_if_end)) - { - dptr_close(dptr_num); - DEBUG(5,("call_trans2findfirst - (2) closing dptr_num %d\n", dptr_num)); - dptr_num = -1; - } + { + DEBUG(5,("call_trans2findfirst - (2) closing dptr_num %d\n", dptr_num)); + dptr_close(&dptr_num); + } /* * If there are no matching entries we must return ERRDOS/ERRbadfile - @@ -803,7 +813,10 @@ static int call_trans2findfirst(connection_struct *conn, */ if(numentries == 0) + { + dptr_close(&dptr_num); return(ERROR(ERRDOS,ERRbadfile)); + } /* At this point pdata points to numentries directory entries. */ @@ -823,6 +836,17 @@ static int call_trans2findfirst(connection_struct *conn, smb_fn_name(CVAL(inbuf,smb_com)), mask, directory, dirtype, numentries ) ); + /* + * Force a name mangle here to ensure that the + * mask as an 8.3 name is top of the mangled cache. + * The reasons for this are subtle. Don't remove + * this code unless you know what you are doing + * (see PR#13758). JRA. + */ + + if(!is_8_3( mask, False)) + name_map_mangle(mask, True, True, SNUM(conn)); + return(-1); } @@ -843,7 +867,7 @@ static int call_trans2findnext(connection_struct *conn, int max_data_bytes = SVAL(inbuf, smb_mdrcnt); char *params = *pparams; char *pdata = *ppdata; - int16 dptr_num = SVAL(params,0); + int dptr_num = SVAL(params,0); int maxentries = SVAL(params,2); uint16 info_level = SVAL(params,4); uint32 resume_key = IVAL(params,6); @@ -891,7 +915,7 @@ resume_key = %d resume name = %s continue=%d level = %d\n", pdata = *ppdata = Realloc( *ppdata, max_data_bytes + 1024); if(!*ppdata) return(ERROR(ERRDOS,ERRnomem)); - bzero(pdata,max_data_bytes); + memset((char *)pdata,'\0',max_data_bytes + 1024); /* Realloc the params space */ params = *pparams = Realloc(*pparams, 6*SIZEOFWORD); @@ -968,7 +992,7 @@ resume_key = %d resume name = %s continue=%d level = %d\n", */ if(dname != NULL) - name_map_mangle( dname, False, SNUM(conn)); + name_map_mangle( dname, False, True, SNUM(conn)); if(dname && strcsequal( resume_name, dname)) { @@ -996,7 +1020,7 @@ resume_key = %d resume name = %s continue=%d level = %d\n", */ if(dname != NULL) - name_map_mangle( dname, False, SNUM(conn)); + name_map_mangle( dname, False, True, SNUM(conn)); if(dname && strcsequal( resume_name, dname)) { @@ -1009,38 +1033,49 @@ resume_key = %d resume name = %s continue=%d level = %d\n", } /* end if requires_resume_key && !continue_bit */ for (i=0;(i<(int)maxentries) && !finished && !out_of_space ;i++) + { + BOOL got_exact_match; + + /* this is a heuristic to avoid seeking the dirptr except when + absolutely necessary. It allows for a filename of about 40 chars */ + if (space_remaining < DIRLEN_GUESS && numentries > 0) { - /* this is a heuristic to avoid seeking the dirptr except when - absolutely necessary. It allows for a filename of about 40 chars */ - if (space_remaining < DIRLEN_GUESS && numentries > 0) - { - out_of_space = True; - finished = False; - } - else - { - finished = - !get_lanman2_dir_entry(conn,mask,dirtype,info_level, - requires_resume_key,dont_descend, - &p,pdata,space_remaining, &out_of_space, - &last_name_off); - } + out_of_space = True; + finished = False; + } + else + { + finished = !get_lanman2_dir_entry(conn,mask,dirtype,info_level, + requires_resume_key,dont_descend, + &p,pdata,space_remaining, &out_of_space, &got_exact_match, + &last_name_off); + } - if (finished && out_of_space) - finished = False; + if (finished && out_of_space) + finished = False; - if (!finished && !out_of_space) - numentries++; - space_remaining = max_data_bytes - PTR_DIFF(p,pdata); - } + if (!finished && !out_of_space) + numentries++; + + /* + * As an optimisation if we know we aren't looking + * for a wildcard name (ie. the name matches the wildcard exactly) + * then we can finish on any (first) match. + * This speeds up large directory searches. JRA. + */ + + if(got_exact_match) + finished = True; + + space_remaining = max_data_bytes - PTR_DIFF(p,pdata); + } /* Check if we can close the dirptr */ if(close_after_request || (finished && close_if_end)) - { - dptr_close(dptr_num); /* This frees up the saved mask */ - DEBUG(5,("call_trans2findnext: closing dptr_num = %d\n", dptr_num)); - dptr_num = -1; - } + { + DEBUG(5,("call_trans2findnext: closing dptr_num = %d\n", dptr_num)); + dptr_close(&dptr_num); /* This frees up the saved mask */ + } /* Set up the return parameter block */ @@ -1070,6 +1105,7 @@ static int call_trans2qfsinfo(connection_struct *conn, int length, int bufsize, char **pparams, char **ppdata) { + int max_data_bytes = SVAL(inbuf, smb_mdrcnt); char *pdata = *ppdata; char *params = *pparams; uint16 info_level = SVAL(params,0); @@ -1078,16 +1114,16 @@ static int call_trans2qfsinfo(connection_struct *conn, char *vname = volume_label(SNUM(conn)); int snum = SNUM(conn); char *fstype = lp_fstype(SNUM(conn)); - extern uint32 global_client_caps; DEBUG(3,("call_trans2qfsinfo: level = %d\n", info_level)); - if(conn->vfs_ops.stat(".",&st)!=0) { + if(dos_stat(".",&st)!=0) { DEBUG(2,("call_trans2qfsinfo: stat of . failed (%s)\n", strerror(errno))); return (ERROR(ERRSRV,ERRinvdevice)); } - pdata = *ppdata = Realloc(*ppdata, 1024); bzero(pdata,1024); + pdata = *ppdata = Realloc(*ppdata, max_data_bytes + 1024); + memset((char *)pdata,'\0',max_data_bytes + 1024); switch (info_level) { @@ -1095,7 +1131,7 @@ static int call_trans2qfsinfo(connection_struct *conn, { SMB_BIG_UINT dfree,dsize,bsize; data_len = 18; - conn->vfs_ops.disk_free(".",&bsize,&dfree,&dsize); + sys_disk_free(".",False,&bsize,&dfree,&dsize); SIVAL(pdata,l1_idFileSystem,st.st_dev); SIVAL(pdata,l1_cSectorUnit,bsize/512); SIVAL(pdata,l1_cUnit,dsize); @@ -1124,16 +1160,21 @@ static int call_trans2qfsinfo(connection_struct *conn, break; } case SMB_QUERY_FS_ATTRIBUTE_INFO: - data_len = 12 + 2*strlen(fstype); - SIVAL(pdata,0,FILE_CASE_PRESERVED_NAMES|FILE_CASE_SENSITIVE_SEARCH); /* FS ATTRIBUTES */ + { + int fstype_len; + SIVAL(pdata,0,FILE_CASE_PRESERVED_NAMES|FILE_CASE_SENSITIVE_SEARCH| + lp_nt_acl_support() ? FILE_PERSISTENT_ACLS : 0); /* FS ATTRIBUTES */ #if 0 /* Old code. JRA. */ SIVAL(pdata,0,0x4006); /* FS ATTRIBUTES == long filenames supported? */ #endif /* Old code. */ + SIVAL(pdata,4,128); /* Max filename component length */ - SIVAL(pdata,8,2*strlen(fstype)); - ascii_to_unibuf(pdata+12, fstype, 1024-2-12); + fstype_len = dos_PutUniCode(pdata+12,unix_to_dos(fstype,False),sizeof(pstring)/2); + SIVAL(pdata,8,fstype_len); + data_len = 12 + fstype_len; SSVAL(outbuf,smb_flg2,SVAL(outbuf,smb_flg2)|FLAGS2_UNICODE_STRINGS); break; + } case SMB_QUERY_FS_LABEL_INFO: data_len = 4 + strlen(vname); SIVAL(pdata,0,strlen(vname)); @@ -1158,19 +1199,19 @@ static int call_trans2qfsinfo(connection_struct *conn, } else { data_len = 18 + 2*strlen(vname); SIVAL(pdata,12,strlen(vname)*2); - ascii_to_unibuf(pdata+18, vname, 1024-2-18); + dos_PutUniCode(pdata+18,unix_to_dos(vname,False),sizeof(pstring)/2); } DEBUG(5,("call_trans2qfsinfo : SMB_QUERY_FS_VOLUME_INFO namelen = %d, vol = %s\n", - strlen(vname),vname)); + (int)strlen(vname),vname)); break; case SMB_QUERY_FS_SIZE_INFO: { SMB_BIG_UINT dfree,dsize,bsize; data_len = 24; - conn->vfs_ops.disk_free(".",&bsize,&dfree,&dsize); - SIVAL(pdata,0,dsize); - SIVAL(pdata,8,dfree); + sys_disk_free(".",False,&bsize,&dfree,&dsize); + SBIG_UINT(pdata,0,dsize); + SBIG_UINT(pdata,8,dfree); SIVAL(pdata,16,bsize/512); SIVAL(pdata,20,512); break; @@ -1236,6 +1277,7 @@ static int call_trans2qfilepathinfo(connection_struct *conn, char **pparams,char **ppdata, int total_data) { + int max_data_bytes = SVAL(inbuf, smb_mdrcnt); char *params = *pparams; char *pdata = *ppdata; uint16 tran_call = SVAL(inbuf, smb_setup0); @@ -1256,20 +1298,17 @@ static int call_trans2qfilepathinfo(connection_struct *conn, files_struct *fsp = file_fsp(params,0); info_level = SVAL(params,2); - if(fsp && fsp->open && fsp->is_directory) { + DEBUG(3,("call_trans2qfilepathinfo: TRANSACT2_QFILEINFO: level = %d\n", info_level)); + + if(fsp && fsp->open && (fsp->is_directory || fsp->stat_open)) { /* * This is actually a QFILEINFO on a directory * handle (returned from an NT SMB). NT5.0 seems * to do this call. JRA. */ fname = fsp->fsp_name; - if (!unix_dfs_convert(fname,conn,0,&bad_path,&sbuf)) - { - SSVAL(outbuf, smb_flg2, FLAGS2_32_BIT_ERROR_CODES); - return(ERROR(0, 0xc0000000|NT_STATUS_PATH_NOT_COVERED)); - } - if (!check_name(fname,conn) || (!VALID_STAT(sbuf) && - conn->vfs_ops.stat(dos_to_unix(fname,False),&sbuf))) { + unix_convert(fname,conn,0,&bad_path,&sbuf); + if (!check_name(fname,conn) || (!VALID_STAT(sbuf) && dos_stat(fname,&sbuf))) { DEBUG(3,("fileinfo of %s failed (%s)\n",fname,strerror(errno))); if((errno == ENOENT) && bad_path) { @@ -1278,6 +1317,9 @@ static int call_trans2qfilepathinfo(connection_struct *conn, } return(UNIXERROR(ERRDOS,ERRbadpath)); } + + delete_pending = fsp->directory_delete_on_close; + } else { /* * Original code - this is an open file. @@ -1286,11 +1328,11 @@ static int call_trans2qfilepathinfo(connection_struct *conn, CHECK_ERROR(fsp); fname = fsp->fsp_name; - if (fsp->conn->vfs_ops.fstat(fsp->fd_ptr->fd,&sbuf) != 0) { + if (sys_fstat(fsp->fd_ptr->fd,&sbuf) != 0) { DEBUG(3,("fstat of fnum %d failed (%s)\n",fsp->fnum, strerror(errno))); return(UNIXERROR(ERRDOS,ERRbadfid)); } - if((pos = fsp->conn->vfs_ops.lseek(fsp->fd_ptr->fd,0,SEEK_CUR)) == -1) + if((pos = sys_lseek(fsp->fd_ptr->fd,0,SEEK_CUR)) == -1) return(UNIXERROR(ERRDOS,ERRnoaccess)); delete_pending = fsp->fd_ptr->delete_on_close; @@ -1298,16 +1340,13 @@ static int call_trans2qfilepathinfo(connection_struct *conn, } else { /* qpathinfo */ info_level = SVAL(params,0); + + DEBUG(3,("call_trans2qfilepathinfo: TRANSACT2_QPATHINFO: level = %d\n", info_level)); + fname = &fname1[0]; pstrcpy(fname,¶ms[6]); - if (!unix_dfs_convert(fname,conn,0,&bad_path,&sbuf)) - { - SSVAL(outbuf, smb_flg2, FLAGS2_32_BIT_ERROR_CODES); - return(ERROR(0, 0xc0000000|NT_STATUS_PATH_NOT_COVERED)); - } - if (!check_name(fname,conn) || - (!VALID_STAT(sbuf) && conn->vfs_ops.stat(dos_to_unix(fname,False), - &sbuf))) { + unix_convert(fname,conn,0,&bad_path,&sbuf); + if (!check_name(fname,conn) || (!VALID_STAT(sbuf) && dos_stat(fname,&sbuf))) { DEBUG(3,("fileinfo of %s failed (%s)\n",fname,strerror(errno))); if((errno == ENOENT) && bad_path) { @@ -1335,8 +1374,9 @@ static int call_trans2qfilepathinfo(connection_struct *conn, /* from now on we only want the part after the / */ fname = p; - params = *pparams = Realloc(*pparams,2); bzero(params,2); - data_size = 1024; + params = *pparams = Realloc(*pparams,2); + memset((char *)params,'\0',2); + data_size = max_data_bytes + 1024; pdata = *ppdata = Realloc(*ppdata, data_size); if (total_data > 0 && IVAL(pdata,0) == total_data) { @@ -1345,7 +1385,7 @@ static int call_trans2qfilepathinfo(connection_struct *conn, return(ERROR(ERRDOS,ERROR_EAS_NOT_SUPPORTED)); } - bzero(pdata,data_size); + memset((char *)pdata,'\0',data_size); switch (info_level) { @@ -1416,26 +1456,37 @@ static int call_trans2qfilepathinfo(connection_struct *conn, case SMB_QUERY_FILE_ALT_NAME_INFO: { pstring short_name; - char *data_end; - pstrcpy(short_name,p); /* Mangle if not already 8.3 */ if(!is_8_3(short_name, True)) { - if(!name_map_mangle(short_name,True,SNUM(conn))) + if(!name_map_mangle(short_name,True,True,SNUM(conn))) *short_name = '\0'; } strupper(short_name); - data_end = ascii_to_unibuf(pdata + 4, short_name, 1024-2-4); - data_size = data_end - pdata; - SIVAL(pdata,0,2*(data_size-4)); + l = strlen(short_name); + dos_PutUniCode(pdata + 4, unix_to_dos(short_name,False),sizeof(pstring)*2); + data_size = 4 + (2*l); + SIVAL(pdata,0,2*l); } break; case SMB_QUERY_FILE_NAME_INFO: + /* + * The first part of this code is essential + * to get security descriptors to work on mapped + * drives. Don't ask how I discovered this unless + * you like hearing about me suffering.... :-). JRA. + */ + if(strequal(".", fname) && (global_client_caps & CAP_UNICODE)) { + l = l*2; + SSVAL(outbuf,smb_flg2,SVAL(outbuf,smb_flg2)|FLAGS2_UNICODE_STRINGS); + dos_PutUniCode(pdata + 4, unix_to_dos("\\",False),sizeof(pstring)*2); + } else { + pstrcpy(pdata+4,fname); + } data_size = 4 + l; SIVAL(pdata,0,l); - pstrcpy(pdata+4,fname); break; case SMB_QUERY_FILE_ALLOCATION_INFO: @@ -1527,20 +1578,15 @@ static int call_trans2setfilepathinfo(connection_struct *conn, fsp = file_fsp(params,0); info_level = SVAL(params,2); - if(fsp && fsp->open && fsp->is_directory) { + if(fsp && fsp->open && (fsp->is_directory || fsp->stat_open)) { /* * This is actually a SETFILEINFO on a directory * handle (returned from an NT SMB). NT5.0 seems * to do this call. JRA. */ fname = fsp->fsp_name; - if (!unix_dfs_convert(fname,conn,0,&bad_path,&st)) - { - SSVAL(outbuf, smb_flg2, FLAGS2_32_BIT_ERROR_CODES); - return(ERROR(0, 0xc0000000|NT_STATUS_PATH_NOT_COVERED)); - } - if (!check_name(fname,conn) || (!VALID_STAT(st) && - conn->vfs_ops.stat(dos_to_unix(fname,False),&st))) { + unix_convert(fname,conn,0,&bad_path,&st); + if (!check_name(fname,conn) || (!VALID_STAT(st) && dos_stat(fname,&st))) { DEBUG(3,("fileinfo of %s failed (%s)\n",fname,strerror(errno))); if((errno == ENOENT) && bad_path) { @@ -1559,7 +1605,7 @@ static int call_trans2setfilepathinfo(connection_struct *conn, fname = fsp->fsp_name; fd = fsp->fd_ptr->fd; - if (fsp->conn->vfs_ops.fstat(fd,&st) != 0) { + if (sys_fstat(fd,&st) != 0) { DEBUG(3,("fstat of fnum %d failed (%s)\n",fsp->fnum, strerror(errno))); return(UNIXERROR(ERRDOS,ERRbadfid)); } @@ -1569,11 +1615,7 @@ static int call_trans2setfilepathinfo(connection_struct *conn, info_level = SVAL(params,0); fname = fname1; pstrcpy(fname,¶ms[6]); - if (!unix_dfs_convert(fname,conn,0,&bad_path,&st)) - { - SSVAL(outbuf, smb_flg2, FLAGS2_32_BIT_ERROR_CODES); - return(ERROR(0, 0xc0000000|NT_STATUS_PATH_NOT_COVERED)); - } + unix_convert(fname,conn,0,&bad_path,&st); if(!check_name(fname, conn)) { if((errno == ENOENT) && bad_path) @@ -1584,7 +1626,7 @@ static int call_trans2setfilepathinfo(connection_struct *conn, return(UNIXERROR(ERRDOS,ERRbadpath)); } - if(!VALID_STAT(st) && conn->vfs_ops.stat(dos_to_unix(fname,False),&st)!=0) { + if(!VALID_STAT(st) && dos_stat(fname,&st)!=0) { DEBUG(3,("stat of %s failed (%s)\n", fname, strerror(errno))); if((errno == ENOENT) && bad_path) { @@ -1599,10 +1641,12 @@ static int call_trans2setfilepathinfo(connection_struct *conn, tran_call,fname,info_level,total_data)); /* Realloc the parameter and data sizes */ - params = *pparams = Realloc(*pparams,2); SSVAL(params,0,0); + params = *pparams = Realloc(*pparams,2); if(params == NULL) return(ERROR(ERRDOS,ERRnomem)); + SSVAL(params,0,0); + size = st.st_size; tvs.modtime = st.st_mtime; tvs.actime = st.st_atime; @@ -1656,7 +1700,7 @@ static int call_trans2setfilepathinfo(connection_struct *conn, tvs.actime = interpret_long_date(pdata+8); /* write time + changed time, combined. */ - tvs.modtime=MAX(interpret_long_date(pdata+16), + tvs.modtime=MIN(interpret_long_date(pdata+16), interpret_long_date(pdata+24)); #if 0 /* Needs more testing... */ @@ -1672,6 +1716,26 @@ static int call_trans2setfilepathinfo(connection_struct *conn, break; } + /* + * NT seems to use this call with a size of zero + * to mean truncate the file. JRA. + */ + + case SMB_SET_FILE_ALLOCATION_INFO: + { + SMB_OFF_T newsize = IVAL(pdata,0); +#ifdef LARGE_SMB_OFF_T + newsize |= (((SMB_OFF_T)IVAL(pdata,4)) << 32); +#else /* LARGE_SMB_OFF_T */ + if (IVAL(pdata,4) != 0) /* more than 32 bits? */ + return(ERROR(ERRDOS,ERRunknownlevel)); +#endif /* LARGE_SMB_OFF_T */ + DEBUG(10,("call_trans2setfilepathinfo: Set file allocation info for file %s to %.0f\n", fname, (double)newsize )); + if(newsize == 0) + size = 0; + break; + } + case SMB_SET_FILE_END_OF_FILE_INFO: { size = IVAL(pdata,0); @@ -1681,12 +1745,10 @@ static int call_trans2setfilepathinfo(connection_struct *conn, if (IVAL(pdata,4) != 0) /* more than 32 bits? */ return(ERROR(ERRDOS,ERRunknownlevel)); #endif /* LARGE_SMB_OFF_T */ + DEBUG(10,("call_trans2setfilepathinfo: Set end of file info for file %s to %.0f\n", fname, (double)size )); break; } - case SMB_SET_FILE_ALLOCATION_INFO: - break; /* We don't need to do anything for this call. */ - case SMB_SET_FILE_DISPOSITION_INFO: /* Set delete on close for open file. */ { if ((tran_call == TRANSACT2_SETFILEINFO) && (fsp != NULL)) @@ -1694,121 +1756,134 @@ static int call_trans2setfilepathinfo(connection_struct *conn, BOOL delete_on_close = (CVAL(pdata,0) ? True : False); if(fsp->is_directory) - return(ERROR(ERRDOS,ERRnoaccess)); - - /* - * We can only set the delete on close flag if - * the share mode contained ALLOW_SHARE_DELETE - */ + { + fsp->directory_delete_on_close = delete_on_close; + DEBUG(10, ("call_trans2setfilepathinfo: %s delete on close flag for fnum = %d, directory %s\n", + delete_on_close ? "Added" : "Removed", fsp->fnum, fsp->fsp_name )); - if(lp_share_modes(SNUM(conn))) + } + else if(fsp->stat_open) + { + DEBUG(10, ("call_trans2setfilepathinfo: %s delete on close flag for fnum = %d, stat open %s\n", + delete_on_close ? "Added" : "Removed", fsp->fnum, fsp->fsp_name )); + } + else { - if(!GET_ALLOW_SHARE_DELETE(fsp->share_mode)) - return(ERROR(ERRDOS,ERRnoaccess)); /* - * If the flag has been set then - * modify the share mode entry for all files we have open - * on this device and inode to tell other smbds we have - * changed the delete on close flag. + * We can only set the delete on close flag if + * the share mode contained ALLOW_SHARE_DELETE */ - if(delete_on_close && !GET_DELETE_ON_CLOSE_FLAG(fsp->share_mode)) + if(lp_share_modes(SNUM(conn))) { - int token; - int i; - files_struct *iterate_fsp; - SMB_DEV_T dev = fsp->fd_ptr->dev; - SMB_INO_T inode = fsp->fd_ptr->inode; - int num_share_modes; - share_mode_entry *current_shares = NULL; - - if(lock_share_entry(fsp->conn, dev, inode, &token) == False) + if(!GET_ALLOW_SHARE_DELETE(fsp->share_mode)) return(ERROR(ERRDOS,ERRnoaccess)); /* - * Before we allow this we need to ensure that all current opens - * on the file have the GET_ALLOW_SHARE_DELETE flag set. If they - * do not then we deny this (as we are essentially deleting the - * file at this point. + * If the flag has been set then + * modify the share mode entry for all files we have open + * on this device and inode to tell other smbds we have + * changed the delete on close flag. */ - num_share_modes = get_share_modes(conn, token, dev, inode, ¤t_shares); - for(i = 0; i < num_share_modes; i++) + if(delete_on_close && !GET_DELETE_ON_CLOSE_FLAG(fsp->share_mode)) { - if(!GET_ALLOW_SHARE_DELETE(current_shares[i].share_mode)) + int token; + int i; + files_struct *iterate_fsp; + SMB_DEV_T dev = fsp->fd_ptr->dev; + SMB_INO_T inode = fsp->fd_ptr->inode; + int num_share_modes; + share_mode_entry *current_shares = NULL; + + if(lock_share_entry(fsp->conn, dev, inode, &token) == False) + return(ERROR(ERRDOS,ERRnoaccess)); + + /* + * Before we allow this we need to ensure that all current opens + * on the file have the GET_ALLOW_SHARE_DELETE flag set. If they + * do not then we deny this (as we are essentially deleting the + * file at this point. + */ + + num_share_modes = get_share_modes(conn, token, dev, inode, ¤t_shares); + for(i = 0; i < num_share_modes; i++) { - DEBUG(5,("call_trans2setfilepathinfo: refusing to set delete on close flag for fnum = %d, \ + if(!GET_ALLOW_SHARE_DELETE(current_shares[i].share_mode)) + { + DEBUG(5,("call_trans2setfilepathinfo: refusing to set delete on close flag for fnum = %d, \ file %s as a share exists that was not opened with FILE_DELETE access.\n", - fsp->fnum, fsp->fsp_name )); - /* - * Release the lock. - */ + fsp->fnum, fsp->fsp_name )); + /* + * Release the lock. + */ - unlock_share_entry(fsp->conn, dev, inode, token); + unlock_share_entry(fsp->conn, dev, inode, token); - /* - * current_shares was malloced by get_share_modes - free it here. - */ + /* + * current_shares was malloced by get_share_modes - free it here. + */ - free((char *)current_shares); + free((char *)current_shares); - /* - * Even though share violation would be more appropriate here, - * return ERRnoaccess as that's what NT does. - */ + /* + * Even though share violation would be more appropriate here, + * return ERRnoaccess as that's what NT does. + */ - return(ERROR(ERRDOS,ERRnoaccess)); + return(ERROR(ERRDOS,ERRnoaccess)); + } } - } - /* - * current_shares was malloced by get_share_modes - free it here. - */ + /* + * current_shares was malloced by get_share_modes - free it here. + */ - free((char *)current_shares); + free((char *)current_shares); - DEBUG(10,("call_trans2setfilepathinfo: %s delete on close flag for fnum = %d, file %s\n", - delete_on_close ? "Adding" : "Removing", fsp->fnum, fsp->fsp_name )); + DEBUG(10,("call_trans2setfilepathinfo: %s delete on close flag for fnum = %d, file %s\n", + delete_on_close ? "Adding" : "Removing", fsp->fnum, fsp->fsp_name )); - /* - * Go through all files we have open on the same device and - * inode (hanging off the same hash bucket) and set the DELETE_ON_CLOSE_FLAG. - * Other smbd's that have this file open will have to fend for themselves. We - * take care of this (rare) case in close_file(). See the comment there. - */ + /* + * Go through all files we have open on the same device and + * inode (hanging off the same hash bucket) and set the DELETE_ON_CLOSE_FLAG. + * Other smbd's that have this file open will have to fend for themselves. We + * take care of this (rare) case in close_file(). See the comment there. + */ - for(iterate_fsp = file_find_di_first(dev, inode); iterate_fsp; - iterate_fsp = file_find_di_next(iterate_fsp)) - { - int new_share_mode = (delete_on_close ? - (iterate_fsp->share_mode | DELETE_ON_CLOSE_FLAG) : - (iterate_fsp->share_mode & ~DELETE_ON_CLOSE_FLAG) ); + for(iterate_fsp = file_find_di_first(dev, inode); iterate_fsp; + iterate_fsp = file_find_di_next(iterate_fsp)) + { + int new_share_mode = (delete_on_close ? + (iterate_fsp->share_mode | DELETE_ON_CLOSE_FLAG) : + (iterate_fsp->share_mode & ~DELETE_ON_CLOSE_FLAG) ); - DEBUG(10,("call_trans2setfilepathinfo: Changing share mode for fnum %d, file %s \ + DEBUG(10,("call_trans2setfilepathinfo: Changing share mode for fnum %d, file %s \ dev = %x, inode = %.0f from %x to %x\n", - iterate_fsp->fnum, iterate_fsp->fsp_name, (unsigned int)dev, - (double)inode, iterate_fsp->share_mode, new_share_mode )); + iterate_fsp->fnum, iterate_fsp->fsp_name, (unsigned int)dev, + (double)inode, iterate_fsp->share_mode, new_share_mode )); - if(modify_share_mode(token, iterate_fsp, new_share_mode)==False) - DEBUG(0,("call_trans2setfilepathinfo: failed to change delete on close for fnum %d, \ + if(modify_share_mode(token, iterate_fsp, new_share_mode, iterate_fsp->oplock_type)==False) + DEBUG(0,("call_trans2setfilepathinfo: failed to change delete on close for fnum %d, \ dev = %x, inode = %.0f\n", iterate_fsp->fnum, (unsigned int)dev, (double)inode)); - } + } - /* - * Set the delete on close flag in the reference - * counted struct. Delete when the last reference - * goes away. - */ - fsp->fd_ptr->delete_on_close = delete_on_close; - unlock_share_entry(fsp->conn, dev, inode, token); + /* + * Set the delete on close flag in the reference + * counted struct. Delete when the last reference + * goes away. + */ + fsp->fd_ptr->delete_on_close = delete_on_close; - DEBUG(10, ("call_trans2setfilepathinfo: %s delete on close flag for fnum = %d, file %s\n", - delete_on_close ? "Added" : "Removed", fsp->fnum, fsp->fsp_name )); + unlock_share_entry(fsp->conn, dev, inode, token); - } /* end if(delete_on_close && !GET_DELETE_ON_CLOSE_FLAG(fsp->share_mode)) */ - } /* end if lp_share_modes() */ + DEBUG(10, ("call_trans2setfilepathinfo: %s delete on close flag for fnum = %d, file %s\n", + delete_on_close ? "Added" : "Removed", fsp->fnum, fsp->fsp_name )); + } /* end if(delete_on_close && !GET_DELETE_ON_CLOSE_FLAG(fsp->share_mode)) */ + } /* end if lp_share_modes() */ + } /* end if is_directory. */ } else return(ERROR(ERRDOS,ERRunknownlevel)); break; @@ -1820,23 +1895,33 @@ dev = %x, inode = %.0f\n", iterate_fsp->fnum, (unsigned int)dev, (double)inode)) } } + /* get some defaults (no modifications) if any info is zero or -1. */ + if (tvs.actime == (time_t)0 || tvs.actime == (time_t)-1) + tvs.actime = st.st_atime; + + if (tvs.modtime == (time_t)0 || tvs.modtime == (time_t)-1) + tvs.modtime = st.st_mtime; + DEBUG(6,("actime: %s " , ctime(&tvs.actime))); DEBUG(6,("modtime: %s ", ctime(&tvs.modtime))); DEBUG(6,("size: %.0f ", (double)size)); DEBUG(6,("mode: %x\n" , mode)); - /* get some defaults (no modifications) if any info is zero. */ - if (!tvs.actime) tvs.actime = st.st_atime; - if (!tvs.modtime) tvs.modtime = st.st_mtime; - if (!size) size = st.st_size; + if(!((info_level == SMB_SET_FILE_END_OF_FILE_INFO) || + (info_level == SMB_SET_FILE_ALLOCATION_INFO))) { + /* + * Only do this test if we are not explicitly + * changing the size of a file. + */ + if (!size) + size = st.st_size; + } /* Try and set the times, size and mode of this file - if they are different from the current values */ - if (st.st_mtime != tvs.modtime || st.st_atime != tvs.actime) - { - if(fsp != NULL) - { + if (st.st_mtime != tvs.modtime || st.st_atime != tvs.actime) { + if(fsp != NULL) { /* * This was a setfileinfo on an open file. * NT does this a lot. It's actually pointless @@ -1844,38 +1929,51 @@ dev = %x, inode = %.0f\n", iterate_fsp->fnum, (unsigned int)dev, (double)inode)) * on the next write, so we save the request * away and will set it on file code. JRA. */ - fsp->pending_modtime = tvs.modtime; - } - else if(file_utime(conn, fname, &tvs)!=0) - { - return(UNIXERROR(ERRDOS,ERRnoaccess)); + + if (tvs.modtime != (time_t)0 && tvs.modtime != (time_t)-1) { + DEBUG(10,("call_trans2setfilepathinfo: setting pending modtime to %s\n", + ctime(&tvs.modtime) )); + fsp->pending_modtime = tvs.modtime; + } + + } else { + + DEBUG(10,("call_trans2setfilepathinfo: setting utimes to modified values.\n")); + + if(file_utime(conn, fname, &tvs)!=0) + return(UNIXERROR(ERRDOS,ERRnoaccess)); } } /* check the mode isn't different, before changing it */ - if (mode != dos_mode(conn, fname, &st) && file_chmod(conn, fname, mode, NULL)) - { - DEBUG(2,("chmod of %s failed (%s)\n", fname, strerror(errno))); - return(UNIXERROR(ERRDOS,ERRnoaccess)); + if ((mode != 0) && (mode != dos_mode(conn, fname, &st))) { + + DEBUG(10,("call_trans2setfilepathinfo: file %s : setting dos mode %x\n", + fname, mode )); + + if(file_chmod(conn, fname, mode, NULL)) { + DEBUG(2,("chmod of %s failed (%s)\n", fname, strerror(errno))); + return(UNIXERROR(ERRDOS,ERRnoaccess)); + } } - if(size != st.st_size) - { - if (fd == -1) - { -DEBUG(0, ("@@@ 23 @@@\n")); + if(size != st.st_size) { + + DEBUG(10,("call_trans2setfilepathinfo: file %s : setting new size to %.0f\n", + fname, (double)size )); + + if (fd == -1) { fd = dos_open(fname,O_RDWR,0); if (fd == -1) - { return(UNIXERROR(ERRDOS,ERRbadpath)); - } set_filelen(fd, size); close(fd); - } - else - { + } else { set_filelen(fd, size); } + + if(fsp) + set_filelen_write_cache(fsp, size); } SSVAL(params,0,0); @@ -1904,14 +2002,9 @@ static int call_trans2mkdir(connection_struct *conn, DEBUG(3,("call_trans2mkdir : name = %s\n", directory)); - if (!unix_dfs_convert(directory,conn,0,&bad_path,NULL)) - { - SSVAL(outbuf, smb_flg2, FLAGS2_32_BIT_ERROR_CODES); - return(ERROR(0, 0xc0000000|NT_STATUS_PATH_NOT_COVERED)); - } + unix_convert(directory,conn,0,&bad_path,NULL); if (check_name(directory,conn)) - ret = conn->vfs_ops.mkdir(dos_to_unix(directory,False), - unix_mode(conn,aDIR)); + ret = dos_mkdir(directory,unix_mode(conn,aDIR)); if(ret < 0) { @@ -2012,11 +2105,11 @@ int reply_findclose(connection_struct *conn, char *inbuf,char *outbuf,int length,int bufsize) { int outsize = 0; - int16 dptr_num=SVALS(inbuf,smb_vwv0); + int dptr_num=SVALS(inbuf,smb_vwv0); DEBUG(3,("reply_findclose, dptr_num = %d\n", dptr_num)); - dptr_close(dptr_num); + dptr_close(&dptr_num); outsize = set_message(outbuf,0,0,True); @@ -2049,237 +2142,6 @@ int reply_findnclose(connection_struct *conn, return(outsize); } -#define UNICODE_DFS - -/**************************************************************************** - reply to a TRANS2_GET_DFS_REFERRAL - ****************************************************************************/ -static int call_trans2getdfsreferral(connection_struct *conn, - char *inbuf, char *outbuf, int length, - int bufsize, - char **pparams, char **ppdata, - int total_data) -{ - char *params = *pparams; - char *pdata; - char *pheader; - char *localstring_offset; - char *mangledstring_offset; - char *sharename_offset; - char *referal_offset; - - int i; - int j; - unsigned int total_params = SVAL(inbuf, smb_tpscnt); - int query_file_len=0; - int bytesreq=0; - int filename_len; - - BOOL first_one=True; - - referal_trans_param rtp; - dfs_internal_table *list=dfs_struct.table; - dfs_response reply; - - DEBUG(0,("call_trans2getdfsreferral:1\n")); - - ZERO_STRUCT(rtp); - ZERO_STRUCT(reply); - - /* decode the param member of query */ - rtp.level=SVAL(params, 0); - DEBUGADD(0,("rtp.level:[%d]\n",rtp.level)); - - DEBUGADD(0,("total_params:[%d]\n",total_params)); - for (i=0; i<(total_params-2)/2; i++) - { - rtp.directory[i]=SVAL(params, 2+2*i); - } -/* - strupper(rtp.directory); -*/ - query_file_len=strlen(rtp.directory); - DEBUGADD(0,("rtp.directory:[%s]\n",rtp.directory)); - DEBUGADD(0,("query_file_len:[%d]\n",query_file_len)); - - /* - lookup in the internal DFS table all the entries - and calculate the required data buffer size - */ - bytesreq=8; /* the header */ - reply.number_of_referal=0; - DEBUGADD(0,("call_trans2getdfsreferral:2\n")); - - for(i=0; i