From 471087c9d28a4058efc16f98784cb179ffc1e4c4 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Fri, 17 Jul 1998 22:21:24 +0000 Subject: Code added to fix the renaming of a directory under NT SMB calls. local.h: Changed MAXDIR to MAX_OPEN_DIRECTORIES - shmem size also tuned by this. dir.c: Use MAX_OPEN_DIRECTORIES. nttrans.c: Allow opening of a directory to succeed. Doesn't actually open a file descriptor but takes a files_struct slot marked as an fd. reply.c: Changed to close any outstanding is_directory files. reply_close changed to understand directory files. server.c: Added open_directory(), close_directory() calls. smb.h: Added is_directory to files_struct. Changed OPEN_FNUM to check that target is !is_directory (this prevents the normal file calls from processing a directory files_struct. Jeremy. (This used to be commit e01ce693f47e75e277f3440d46e32b0bd866b550) --- source3/include/local.h | 12 ++--- source3/include/proto.h | 2 + source3/include/smb.h | 19 ++++---- source3/smbd/dir.c | 4 +- source3/smbd/nttrans.c | 81 ++++++++++++++++++++++--------- source3/smbd/reply.c | 55 +++++++++++++++------ source3/smbd/server.c | 125 +++++++++++++++++++++++++++++++++++++++--------- 7 files changed, 222 insertions(+), 76 deletions(-) diff --git a/source3/include/local.h b/source3/include/local.h index f32700c0eb..4a69325b77 100644 --- a/source3/include/local.h +++ b/source3/include/local.h @@ -37,19 +37,19 @@ #define MAX_CONNECTIONS 127 #define MAX_OPEN_FILES 100 +/* max number of directories open at once */ +/* note that with the new directory code this no longer requires a + file handle per directory, but large numbers do use more memory */ +#define MAX_OPEN_DIRECTORIES 64 + /* Default size of shared memory used for share mode locking */ #ifndef SHMEM_SIZE -#define SHMEM_SIZE (1024*MAX_OPEN_FILES) +#define SHMEM_SIZE (1024*(MAX_OPEN_FILES+MAX_OPEN_DIRECTORIES)) #endif /* the max number of simultanous connections to the server by all clients */ #define MAXSTATUS 100000 -/* max number of directories open at once */ -/* note that with the new directory code this no longer requires a - file handle per directory, but large numbers do use more memory */ -#define MAXDIR 64 - #define WORDMAX 0xFFFF /* the maximum password length before we declare a likely attack */ diff --git a/source3/include/proto.h b/source3/include/proto.h index 3acfb0cf5e..b390cb60d9 100644 --- a/source3/include/proto.h +++ b/source3/include/proto.h @@ -1815,6 +1815,8 @@ int sys_disk_free(char *path,int *bsize,int *dfree,int *dsize); BOOL check_name(char *name,int cnum); void sync_file(int fnum); void close_file(int fnum, BOOL normal_close); +void close_directory(int fnum); +void open_directory(int fnum,int cnum,char *fname, int *action); BOOL check_file_sharing(int cnum,char *fname, BOOL rename_op); int check_share_mode( share_mode_entry *share, int deny_mode, char *fname, BOOL fcbopen, int *flags); diff --git a/source3/include/smb.h b/source3/include/smb.h index ca295f3b42..921cb4284d 100644 --- a/source3/include/smb.h +++ b/source3/include/smb.h @@ -418,14 +418,14 @@ typedef struct /* Structure used when SMBwritebmpx is active */ typedef struct - { - int wr_total_written; /* So we know when to discard this */ - int32 wr_timeout; - int32 wr_errclass; - int32 wr_error; /* Cached errors */ - BOOL wr_mode; /* write through mode) */ - BOOL wr_discard; /* discard all further data */ - } write_bmpx_struct; +{ + int wr_total_written; /* So we know when to discard this */ + int32 wr_timeout; + int32 wr_errclass; + int32 wr_error; /* Cached errors */ + BOOL wr_mode; /* write through mode) */ + BOOL wr_discard; /* discard all further data */ +} write_bmpx_struct; /* * Structure used to indirect fd's from the files_struct. @@ -467,6 +467,7 @@ typedef struct BOOL modified; BOOL granted_oplock; BOOL sent_oplock_break; + BOOL is_directory; BOOL reserved; char *name; } files_struct; @@ -781,7 +782,7 @@ struct parm_struct /* these are useful macros for checking validity of handles */ #define VALID_FNUM(fnum) (((fnum) >= 0) && ((fnum) < MAX_OPEN_FILES)) -#define OPEN_FNUM(fnum) (VALID_FNUM(fnum) && Files[fnum].open) +#define OPEN_FNUM(fnum) (VALID_FNUM(fnum) && Files[fnum].open && !Files[fnum].is_directory) #define VALID_CNUM(cnum) (((cnum) >= 0) && ((cnum) < MAX_CONNECTIONS)) #define OPEN_CNUM(cnum) (VALID_CNUM(cnum) && Connections[cnum].open) #define IS_IPC(cnum) (VALID_CNUM(cnum) && Connections[cnum].ipc) diff --git a/source3/smbd/dir.c b/source3/smbd/dir.c index 37fcd05743..8296a90fa1 100644 --- a/source3/smbd/dir.c +++ b/source3/smbd/dir.c @@ -115,7 +115,7 @@ static void *dptr_get(int key,uint32 lastused) if (dp->valid) { if (lastused) dp->lastused = lastused; if (!dp->ptr) { - if (dptrs_open >= MAXDIR) + if (dptrs_open >= MAX_OPEN_DIRECTORIES) dptr_idleoldest(); DEBUG(4,("Reopening dptr key %d\n",key)); if ((dp->ptr = OpenDir(dp->cnum, dp->path, True))) @@ -284,7 +284,7 @@ int dptr_create(int cnum,char *path, BOOL expect_close,int pid) if (!start_dir(cnum,path)) return(-2); /* Code to say use a unix error return code. */ - if (dptrs_open >= MAXDIR) + if (dptrs_open >= MAX_OPEN_DIRECTORIES) dptr_idleoldest(); for (i=0;ireserved = False; restore_case_semantics(file_attributes); @@ -493,39 +495,72 @@ int reply_ntcreate_and_X(char *inbuf,char *outbuf,int length,int bufsize) oplock_request = (flags & REQUEST_OPLOCK) ? EXCLUSIVE_OPLOCK : 0; oplock_request |= (flags & REQUEST_BATCH_OPLOCK) ? BATCH_OPLOCK : 0; + /* + * NB. We have a potential bug here. If we cause an oplock + * break to ourselves, then we could end up processing filename + * related SMB requests whilst we await the oplock break + * response. As we may have changed the filename case + * semantics to be POSIX-like, this could mean a filename + * request could fail when it should succeed. This is a + * rare condition, but eventually we must arrange to restore + * the correct case semantics before issuing an oplock break + * request to our client. JRA. + */ + open_file_shared(fnum,cnum,fname,smb_open_mode,smb_ofun,unixmode, oplock_request,&rmode,&smb_action); - fsp = &Files[fnum]; - - if (!fsp->open) { - if((errno == ENOENT) && bad_path) { - unix_ERR_class = ERRDOS; - unix_ERR_code = ERRbadpath; - } - Files[fnum].reserved = False; + restore_case_semantics(file_attributes); - restore_case_semantics(file_attributes); + if (!fsp->open) { + /* + * We cheat here. The only case we care about is a directory + * rename, where the NT client will attempt to open the source + * directory for DELETE access. Note that when the NT client + * does this it does *not* set the directory bit in the + * request packet. This is translated into a read/write open + * request. POSIX states that any open for write request on a directory + * will generate an EISDIR error, so we can catch this here and open + * a pseudo handle that is flagged as a directory. JRA. + */ - return(UNIXERROR(ERRDOS,ERRnoaccess)); + if(errno == EISDIR) { + oplock_request = 0; + open_directory(fnum, cnum, fname, &smb_action); + + if(!fsp->open) { + fsp->reserved = False; + return(UNIXERROR(ERRDOS,ERRnoaccess)); + } + } else { + if((errno == ENOENT) && bad_path) { + unix_ERR_class = ERRDOS; + unix_ERR_code = ERRbadpath; + } + + fsp->reserved = False; + return(UNIXERROR(ERRDOS,ERRnoaccess)); + } } - if (fstat(fsp->fd_ptr->fd,&sbuf) != 0) { - close_file(fnum,False); - - restore_case_semantics(file_attributes); - - return(ERROR(ERRDOS,ERRnoaccess)); - } + if(fsp->is_directory) { + if(stat(fsp->name, &sbuf) != 0) { + close_directory(fnum); + return(ERROR(ERRDOS,ERRnoaccess)); + } + } else { + if (fstat(fsp->fd_ptr->fd,&sbuf) != 0) { + close_file(fnum,False); + return(ERROR(ERRDOS,ERRnoaccess)); + } + } - restore_case_semantics(file_attributes); - file_len = sbuf.st_size; fmode = dos_mode(cnum,fname,&sbuf); if(fmode == 0) fmode = FILE_ATTRIBUTE_NORMAL; mtime = sbuf.st_mtime; - if (fmode & aDIR) { + if (!fsp->is_directory && (fmode & aDIR)) { close_file(fnum,False); return(ERROR(ERRDOS,ERRnoaccess)); } @@ -591,10 +626,12 @@ int reply_ntcreate_and_X(char *inbuf,char *outbuf,int length,int bufsize) } else { SIVAL(p,0,file_len); } + p += 12; + SCVAL(p,0,fsp->is_directory ? 1 : 0); } chain_fnum = fnum; - + return chain_reply(inbuf,outbuf,length,bufsize); } diff --git a/source3/smbd/reply.c b/source3/smbd/reply.c index 07b72738c5..a0d1775b21 100644 --- a/source3/smbd/reply.c +++ b/source3/smbd/reply.c @@ -1490,10 +1490,15 @@ int reply_ulogoffX(char *inbuf,char *outbuf,int length,int bufsize) open by this user */ if ((vuser != 0) && (lp_security() != SEC_SHARE)) { int i; - for (i=0;ivuid == vuid) && fsp->open) { + if(!fsp->is_directory) + close_file(i,False); + else + close_directory(i); } + } } invalidate_vuid(vuid); @@ -2416,14 +2421,16 @@ int reply_exit(char *inbuf,char *outbuf, int dum_size, int dum_buffsize) /**************************************************************************** - reply to a close + Reply to a close - has to deal with closing a directory opened by NT SMB's. ****************************************************************************/ + int reply_close(char *inbuf,char *outbuf, int dum_size, int dum_buffsize) { int fnum,cnum; int outsize = 0; time_t mtime; int32 eclass = 0, err = 0; + files_struct *fsp = NULL; outsize = set_message(outbuf,0,0,True); @@ -2435,23 +2442,43 @@ int reply_close(char *inbuf,char *outbuf, int dum_size, int dum_buffsize) fnum = GETFNUM(inbuf,smb_vwv0); - CHECK_FNUM(fnum,cnum); + /* + * We can only use CHECK_FNUM if we know it's not a directory. + */ + + if(!(VALID_FNUM(fnum) && Files[fnum].open && Files[fnum].is_directory)) + CHECK_FNUM(fnum,cnum); + + fsp = &Files[fnum]; if(HAS_CACHED_ERROR(fnum)) { - eclass = Files[fnum].wbmpx_ptr->wr_errclass; - err = Files[fnum].wbmpx_ptr->wr_error; + eclass = fsp->wbmpx_ptr->wr_errclass; + err = fsp->wbmpx_ptr->wr_error; } - mtime = make_unix_date3(inbuf+smb_vwv1); + if(fsp->is_directory) { + /* + * Special case - close NT SMB directory + * handle. + */ + DEBUG(3,("%s close directory fnum=%d cnum=%d\n", + timestring(), fnum, cnum )); + close_directory(fnum); + } else { + /* + * Close ordinary file. + */ + mtime = make_unix_date3(inbuf+smb_vwv1); - /* try and set the date */ - set_filetime(cnum, Files[fnum].name,mtime); + /* try and set the date */ + set_filetime(cnum, fsp->name,mtime); - DEBUG(3,("%s close fd=%d fnum=%d cnum=%d (numopen=%d)\n", - timestring(),Files[fnum].fd_ptr->fd,fnum,cnum, - Connections[cnum].num_files_open)); + DEBUG(3,("%s close fd=%d fnum=%d cnum=%d (numopen=%d)\n", + timestring(),fsp->fd_ptr->fd,fnum,cnum, + Connections[cnum].num_files_open)); - close_file(fnum,True); + close_file(fnum,True); + } /* We have a cached error */ if(eclass || err) diff --git a/source3/smbd/server.c b/source3/smbd/server.c index a236e2e6ec..cabf2d44b1 100644 --- a/source3/smbd/server.c +++ b/source3/smbd/server.c @@ -1511,6 +1511,7 @@ static void open_file(int fnum,int cnum,char *fname1,int flags,int mode, struct fsp->modified = False; fsp->granted_oplock = False; fsp->sent_oplock_break = False; + fsp->is_directory = False; fsp->cnum = cnum; /* * Note that the file name here is the *untranslated* name @@ -1609,15 +1610,43 @@ static void check_magic(int fnum,int cnum) } } +/**************************************************************************** + Common code to close a file or a directory. +****************************************************************************/ + +static void close_filestruct(files_struct *fs_p) +{ + int cnum = fs_p->cnum; + + fs_p->reserved = False; + fs_p->open = False; + fs_p->is_directory = False; + + Connections[cnum].num_files_open--; + if(fs_p->wbmpx_ptr) + { + free((char *)fs_p->wbmpx_ptr); + fs_p->wbmpx_ptr = NULL; + } + +#if USE_MMAP + if(fs_p->mmap_ptr) + { + munmap(fs_p->mmap_ptr,fs_p->mmap_size); + fs_p->mmap_ptr = NULL; + } +#endif +} /**************************************************************************** -close a file - possibly invalidating the read prediction + Close a file - possibly invalidating the read prediction. -If normal_close is 1 then this came from a normal SMBclose (or equivalent) -operation otherwise it came as the result of some other operation such as -the closing of the connection. In the latter case printing and -magic scripts are not run + If normal_close is 1 then this came from a normal SMBclose (or equivalent) + operation otherwise it came as the result of some other operation such as + the closing of the connection. In the latter case printing and + magic scripts are not run. ****************************************************************************/ + void close_file(int fnum, BOOL normal_close) { files_struct *fs_p = &Files[fnum]; @@ -1626,28 +1655,12 @@ void close_file(int fnum, BOOL normal_close) uint32 inode = fs_p->fd_ptr->inode; int token; - Files[fnum].reserved = False; + close_filestruct(fs_p); #if USE_READ_PREDICTION invalidate_read_prediction(fs_p->fd_ptr->fd); #endif - fs_p->open = False; - Connections[cnum].num_files_open--; - if(fs_p->wbmpx_ptr) - { - free((char *)fs_p->wbmpx_ptr); - fs_p->wbmpx_ptr = NULL; - } - -#if USE_MMAP - if(fs_p->mmap_ptr) - { - munmap(fs_p->mmap_ptr,fs_p->mmap_size); - fs_p->mmap_ptr = NULL; - } -#endif - if (lp_share_modes(SNUM(cnum))) { lock_share_entry( cnum, dev, inode, &token); @@ -1684,6 +1697,69 @@ void close_file(int fnum, BOOL normal_close) memset(fs_p, 0, sizeof(*fs_p)); } +/**************************************************************************** + Close a directory opened by an NT SMB call. +****************************************************************************/ + +void close_directory(int fnum) +{ + files_struct *fs_p = &Files[fnum]; + + /* + * Do the code common to files and directories. + */ + close_filestruct(fs_p); + + if (fs_p->name) { + string_free(&fs_p->name); + } + + /* we will catch bugs faster by zeroing this structure */ + memset(fs_p, 0, sizeof(*fs_p)); +} + +/**************************************************************************** + Open a directory from an NT SMB call. +****************************************************************************/ + +void open_directory(int fnum,int cnum,char *fname, int *action) +{ + extern struct current_user current_user; + files_struct *fsp = &Files[fnum]; + + fsp->fd_ptr = NULL; + Connections[cnum].num_files_open++; + fsp->mode = 0; + GetTimeOfDay(&fsp->open_time); + fsp->vuid = current_user.vuid; + fsp->size = 0; + fsp->pos = -1; + fsp->open = True; + fsp->mmap_ptr = NULL; + fsp->mmap_size = 0; + fsp->can_lock = True; + fsp->can_read = False; + fsp->can_write = False; + fsp->share_mode = 0; + fsp->print_file = False; + fsp->modified = False; + fsp->granted_oplock = False; + fsp->sent_oplock_break = False; + fsp->is_directory = True; + fsp->cnum = cnum; + /* + * Note that the file name here is the *untranslated* name + * ie. it is still in the DOS codepage sent from the client. + * All use of this filename will pass though the sys_xxxx + * functions which will do the dos_to_unix translation before + * mapping into a UNIX filename. JRA. + */ + string_set(&fsp->name,fname); + fsp->wbmpx_ptr = NULL; + + *action = FILE_WAS_OPENED; +} + enum {AFAIL,AREAD,AWRITE,AALL}; /******************************************************************* @@ -4413,7 +4489,10 @@ static void close_open_files(int cnum) int i; for (i=0;i