From 2fc57c9a2ce3a266534dd20e6fed4883e052c557 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Tue, 8 Jun 2004 16:14:31 +0000 Subject: r1085: Now it's had some proper user testing, merge in the deferred open fix. I'm still doing more testing, but it fixes a behaviour that we've been wrong on ever since the start of Samba. Jeremy. (This used to be commit 894cc6d16296b934c112786eec896846156aee5d) --- source3/client/client.c | 2 +- source3/include/local.h | 3 + source3/include/smb.h | 62 ++++-- source3/lib/time.c | 6 + source3/locking/locking.c | 392 ++++++++++++++++++++++++++++++++++++- source3/printing/nt_printing.c | 12 +- source3/rpc_server/srv_srvsvc_nt.c | 10 +- source3/script/mkproto.awk | 2 +- source3/smbd/close.c | 30 +++ source3/smbd/dir.c | 3 +- source3/smbd/nttrans.c | 8 + source3/smbd/open.c | 200 +++++++++++++++++-- source3/smbd/oplock.c | 70 ++++++- source3/smbd/process.c | 217 ++++++++++++++++++-- source3/smbd/reply.c | 32 ++- source3/smbd/trans2.c | 8 +- 16 files changed, 974 insertions(+), 83 deletions(-) diff --git a/source3/client/client.c b/source3/client/client.c index a0470315f8..63d73c2d4c 100644 --- a/source3/client/client.c +++ b/source3/client/client.c @@ -1571,7 +1571,7 @@ static int cmd_open(void) } pstrcat(mask,buf); - cli_open(cli, mask, O_RDWR, DENY_ALL); + cli_nt_create(cli, mask, FILE_READ_DATA); return 0; } diff --git a/source3/include/local.h b/source3/include/local.h index 540365047a..ee8d672553 100644 --- a/source3/include/local.h +++ b/source3/include/local.h @@ -230,4 +230,7 @@ /* size of listen() backlog in smbd */ #define SMBD_LISTEN_BACKLOG 50 +/* Number of microseconds to wait before a sharing violation. */ +#define SHARING_VIOLATION_USEC_WAIT 950000 + #endif diff --git a/source3/include/smb.h b/source3/include/smb.h index 54a69d1433..9100701e21 100644 --- a/source3/include/smb.h +++ b/source3/include/smb.h @@ -584,6 +584,24 @@ struct interface struct in_addr nmask; }; +/* struct used by share mode violation error processing */ +typedef struct { + pid_t pid; + uint16 mid; + struct timeval time; + SMB_DEV_T dev; + SMB_INO_T inode; + uint16 port; +} deferred_open_entry; + +/* Internal message queue for deferred opens. */ +struct pending_message_list { + struct pending_message_list *next, *prev; + struct timeval msg_time; /* The timeout time */ + DATA_BLOB buf; + DATA_BLOB private_data; +}; + /* struct returned by get_share_modes */ typedef struct { pid_t pid; @@ -663,28 +681,14 @@ struct locking_key { SMB_INO_T inode; }; -struct locking_data { - union { - int num_share_mode_entries; - share_mode_entry dummy; /* Needed for alignment. */ - } u; - /* the following two entries are implicit - share_mode_entry modes[num_share_mode_entries]; - char file_name[]; - */ -}; - - /* the following are used by loadparm for option lists */ -typedef enum -{ - P_BOOL,P_BOOLREV,P_CHAR,P_INTEGER,P_OCTAL,P_LIST, - P_STRING,P_USTRING,P_GSTRING,P_UGSTRING,P_ENUM,P_SEP +typedef enum { + P_BOOL,P_BOOLREV,P_CHAR,P_INTEGER,P_OCTAL,P_LIST, + P_STRING,P_USTRING,P_GSTRING,P_UGSTRING,P_ENUM,P_SEP } parm_type; -typedef enum -{ - P_LOCAL,P_GLOBAL,P_SEPARATOR,P_NONE +typedef enum { + P_LOCAL,P_GLOBAL,P_SEPARATOR,P_NONE } parm_class; /* passed to br lock code */ @@ -1410,6 +1414,7 @@ extern int chain_size; #define EXCLUSIVE_OPLOCK 1 #define BATCH_OPLOCK 2 #define LEVEL_II_OPLOCK 4 +#define INTERNAL_OPEN_ONLY 8 #define EXCLUSIVE_OPLOCK_TYPE(lck) ((lck) & (EXCLUSIVE_OPLOCK|BATCH_OPLOCK)) #define BATCH_OPLOCK_TYPE(lck) ((lck) & BATCH_OPLOCK) @@ -1461,6 +1466,25 @@ extern int chain_size; #define LEVEL_II_OPLOCK_BREAK_CMD 0x3 #define ASYNC_LEVEL_II_OPLOCK_BREAK_CMD 0x4 +/* Add the "deferred open" message. */ +#define RETRY_DEFERRED_OPEN_CMD 0x5 + +/* + * And the message format for it. Keep the same message length. + * + * 0 2 2+pid 2+pid+dev 2+pid+dev+ino + * +----+--------+-------+--------+---------+ + * | cmd| pid | dev | inode | mid | + * +----+--------+-------+--------+---------+ + */ + +#define DEFERRED_OPEN_CMD_OFFSET 0 +#define DEFERRED_OPEN_PID_OFFSET 2 /* pid we're *sending* from. */ +#define DEFERRED_OPEN_DEV_OFFSET (DEFERRED_OPEN_PID_OFFSET + sizeof(pid_t)) +#define DEFERRED_OPEN_INODE_OFFSET (DEFERRED_OPEN_DEV_OFFSET + sizeof(SMB_DEV_T)) +#define DEFERRED_OPEN_MID_OFFSET (DEFERRED_OPEN_INODE_OFFSET + sizeof(SMB_INO_T)) +#define DEFERRED_OPEN_MSG_LEN OPLOCK_BREAK_MSG_LEN + /* * Capabilities abstracted for different systems. */ diff --git a/source3/lib/time.c b/source3/lib/time.c index faca2cba87..e63e0b2965 100644 --- a/source3/lib/time.c +++ b/source3/lib/time.c @@ -754,3 +754,9 @@ BOOL nt_time_is_zero(NTTIME *nt) return True; return False; } + +SMB_BIG_INT usec_time_diff(struct timeval *larget, struct timeval *smallt) +{ + SMB_BIG_INT sec_diff = larget->tv_sec - smallt->tv_sec; + return (sec_diff * 1000000) + (SMB_BIG_INT)(larget->tv_usec - smallt->tv_usec); +} diff --git a/source3/locking/locking.c b/source3/locking/locking.c index fd03a25940..8f53b55fc5 100644 --- a/source3/locking/locking.c +++ b/source3/locking/locking.c @@ -40,6 +40,17 @@ uint16 global_smbpid; /* the locking database handle */ static TDB_CONTEXT *tdb; +struct locking_data { + union { + int num_share_mode_entries; + share_mode_entry dummy; /* Needed for alignment. */ + } u; + /* the following two entries are implicit + share_mode_entry modes[num_share_mode_entries]; + char file_name[]; + */ +}; + /**************************************************************************** Debugging aid :-). ****************************************************************************/ @@ -432,6 +443,7 @@ int get_share_modes(connection_struct *conn, data = (struct locking_data *)dbuf.dptr; num_share_modes = data->u.num_share_mode_entries; if(num_share_modes) { + pstring fname; int i; int del_count = 0; @@ -443,6 +455,9 @@ int get_share_modes(connection_struct *conn, return 0; } + /* Save off the associated filename. */ + pstrcpy(fname, dbuf.dptr + sizeof(*data) + num_share_modes * sizeof(share_mode_entry)); + /* * Ensure that each entry has a real process attached. */ @@ -467,17 +482,28 @@ int get_share_modes(connection_struct *conn, if (del_count) { data->u.num_share_mode_entries = num_share_modes; - if (num_share_modes) + if (num_share_modes) { memcpy(dbuf.dptr + sizeof(*data), shares, num_share_modes * sizeof(share_mode_entry)); + /* Append the filename. */ + pstrcpy(dbuf.dptr + sizeof(*data) + num_share_modes * sizeof(share_mode_entry), fname); + } /* The record has shrunk a bit */ dbuf.dsize -= del_count * sizeof(share_mode_entry); - if (tdb_store(tdb, key, dbuf, TDB_REPLACE) == -1) { - SAFE_FREE(shares); - SAFE_FREE(dbuf.dptr); - return 0; + if (data->u.num_share_mode_entries == 0) { + if (tdb_delete(tdb, key) == -1) { + SAFE_FREE(shares); + SAFE_FREE(dbuf.dptr); + return 0; + } + } else { + if (tdb_store(tdb, key, dbuf, TDB_REPLACE) == -1) { + SAFE_FREE(shares); + SAFE_FREE(dbuf.dptr); + return 0; + } } } } @@ -847,6 +873,358 @@ BOOL modify_delete_flag( SMB_DEV_T dev, SMB_INO_T inode, BOOL delete_on_close) return True; } +/******************************************************************* + Print out a deferred open entry. +********************************************************************/ + +char *deferred_open_str(int num, deferred_open_entry *e) +{ + static pstring de_str; + + slprintf(de_str, sizeof(de_str)-1, "deferred_open_entry[%d]: \ +pid = %lu, mid = %u, dev = 0x%x, inode = %.0f, port = %u, time = [%u.%06u]", + num, (unsigned long)e->pid, (unsigned int)e->mid, (unsigned int)e->dev, (double)e->inode, + (unsigned int)e->port, + (unsigned int)e->time.tv_sec, (unsigned int)e->time.tv_usec ); + + return de_str; +} + +/* Internal data structures for deferred opens... */ + +struct de_locking_key { + char name[4]; + SMB_DEV_T dev; + SMB_INO_T inode; +}; + +struct deferred_open_data { + union { + int num_deferred_open_entries; + deferred_open_entry dummy; /* Needed for alignment. */ + } u; + /* the following two entries are implicit + deferred_open_entry de_entries[num_deferred_open_entries]; + char file_name[]; + */ +}; + +/******************************************************************* + Print out a deferred open table. +********************************************************************/ + +static void print_deferred_open_table(struct deferred_open_data *data) +{ + int num_de_entries = data->u.num_deferred_open_entries; + deferred_open_entry *de_entries = (deferred_open_entry *)(data + 1); + int i; + + for (i = 0; i < num_de_entries; i++) { + deferred_open_entry *entry_p = &de_entries[i]; + DEBUG(10,("print_deferred_open_table: %s\n", deferred_open_str(i, entry_p) )); + } +} + + +/******************************************************************* + Form a static deferred open locking key for a dev/inode pair. +******************************************************************/ + +static TDB_DATA deferred_open_locking_key(SMB_DEV_T dev, SMB_INO_T inode) +{ + static struct de_locking_key key; + TDB_DATA kbuf; + + memset(&key, '\0', sizeof(key)); + memcpy(&key.name[0], "DOE", 4); + key.dev = dev; + key.inode = inode; + kbuf.dptr = (char *)&key; + kbuf.dsize = sizeof(key); + return kbuf; +} + +/******************************************************************* + Get all deferred open entries for a dev/inode pair. +********************************************************************/ + +int get_deferred_opens(connection_struct *conn, + SMB_DEV_T dev, SMB_INO_T inode, + deferred_open_entry **pp_de_entries) +{ + TDB_DATA dbuf; + struct deferred_open_data *data; + int num_de_entries; + deferred_open_entry *de_entries = NULL; + TDB_DATA key = deferred_open_locking_key(dev, inode); + + *pp_de_entries = NULL; + + dbuf = tdb_fetch(tdb, key); + if (!dbuf.dptr) + return 0; + + data = (struct deferred_open_data *)dbuf.dptr; + num_de_entries = data->u.num_deferred_open_entries; + if(num_de_entries) { + pstring fname; + int i; + int del_count = 0; + + de_entries = (deferred_open_entry *)memdup(dbuf.dptr + sizeof(*data), + num_de_entries * sizeof(deferred_open_entry)); + + if (!de_entries) { + SAFE_FREE(dbuf.dptr); + return 0; + } + + /* Save off the associated filename. */ + pstrcpy(fname, dbuf.dptr + sizeof(*data) + num_de_entries * sizeof(deferred_open_entry)); + + /* + * Ensure that each entry has a real process attached. + */ + + for (i = 0; i < num_de_entries; ) { + deferred_open_entry *entry_p = &de_entries[i]; + if (process_exists(entry_p->pid)) { + DEBUG(10,("get_deferred_opens: %s\n", deferred_open_str(i, entry_p) )); + i++; + } else { + DEBUG(10,("get_deferred_opens: deleted %s\n", deferred_open_str(i, entry_p) )); + if (num_de_entries - i - 1 > 0) { + memcpy( &de_entries[i], &de_entries[i+1], + sizeof(deferred_open_entry) * (num_de_entries - i - 1)); + } + num_de_entries--; + del_count++; + } + } + + /* Did we delete any ? If so, re-store in tdb. */ + if (del_count) { + data->u.num_deferred_open_entries = num_de_entries; + + if (num_de_entries) { + memcpy(dbuf.dptr + sizeof(*data), de_entries, + num_de_entries * sizeof(deferred_open_entry)); + /* Append the filename. */ + pstrcpy(dbuf.dptr + sizeof(*data) + num_de_entries * sizeof(deferred_open_entry), fname); + } + + /* The record has shrunk a bit */ + dbuf.dsize -= del_count * sizeof(deferred_open_entry); + + if (data->u.num_deferred_open_entries == 0) { + if (tdb_delete(tdb, key) == -1) { + SAFE_FREE(de_entries); + SAFE_FREE(dbuf.dptr); + return 0; + } + } else { + if (tdb_store(tdb, key, dbuf, TDB_REPLACE) == -1) { + SAFE_FREE(de_entries); + SAFE_FREE(dbuf.dptr); + return 0; + } + } + } + } + + SAFE_FREE(dbuf.dptr); + *pp_de_entries = de_entries; + return num_de_entries; +} + +/******************************************************************* + Check if two deferred open entries are identical. +********************************************************************/ + +static BOOL deferred_open_entries_identical( deferred_open_entry *e1, deferred_open_entry *e2) +{ +#if 1 /* JRA PARANOIA TEST - REMOVE LATER */ + if (e1->pid == e2->pid && + e1->port == e2->port && + e1->dev == e2->dev && + e1->inode == e2->inode && + ((e1->time.tv_sec != e2->time.tv_sec) || + (e1->time.tv_usec != e2->time.tv_usec) || + (e1->mid != e2->mid))) { + smb_panic("PANIC: deferred_open_entries_identical: logic error.\n"); + } +#endif + + return (e1->pid == e2->pid && + e1->mid == e2->mid && + e1->port == e2->port && + e1->dev == e2->dev && + e1->inode == e2->inode && + e1->time.tv_sec == e2->time.tv_sec && + e1->time.tv_usec == e2->time.tv_usec); +} + + +/******************************************************************* + Delete a specific deferred open entry. + Ignore if no entry deleted. +********************************************************************/ + +BOOL delete_deferred_open_entry(deferred_open_entry *entry) +{ + TDB_DATA dbuf; + struct deferred_open_data *data; + int i, del_count=0; + deferred_open_entry *de_entries; + BOOL ret = True; + TDB_DATA key = deferred_open_locking_key(entry->dev, entry->inode); + + /* read in the existing share modes */ + dbuf = tdb_fetch(tdb, key); + if (!dbuf.dptr) + return -1; + + data = (struct deferred_open_data *)dbuf.dptr; + de_entries = (deferred_open_entry *)(dbuf.dptr + sizeof(*data)); + + /* + * Find any with this pid and delete it + * by overwriting with the rest of the data + * from the record. + */ + + DEBUG(10,("delete_deferred_open_entry: num_deferred_open_entries = %d\n", + data->u.num_deferred_open_entries )); + + for (i=0;iu.num_deferred_open_entries;) { + if (deferred_open_entries_identical(&de_entries[i], entry)) { + DEBUG(10,("delete_deferred_open_entry: deleted %s\n", + deferred_open_str(i, &de_entries[i]) )); + + data->u.num_deferred_open_entries--; + if ((dbuf.dsize - (sizeof(*data) + (i+1)*sizeof(*de_entries))) > 0) { + memmove(&de_entries[i], &de_entries[i+1], + dbuf.dsize - (sizeof(*data) + (i+1)*sizeof(*de_entries))); + } + del_count++; + + DEBUG(10,("delete_deferred_open_entry: deleting entry %d\n", i )); + + } else { + i++; + } + } + + SMB_ASSERT(del_count == 0 || del_count == 1); + + if (del_count) { + /* the record may have shrunk a bit */ + dbuf.dsize -= del_count * sizeof(*de_entries); + + /* store it back in the database */ + if (data->u.num_deferred_open_entries == 0) { + if (tdb_delete(tdb, key) == -1) + ret = False; + } else { + if (tdb_store(tdb, key, dbuf, TDB_REPLACE) == -1) + ret = False; + } + } + DEBUG(10,("delete_deferred_open_entry: Remaining table.\n")); + print_deferred_open_table((struct deferred_open_data*)dbuf.dptr); + SAFE_FREE(dbuf.dptr); + return ret; +} + +/******************************************************************* + Fill a deferred open entry. +********************************************************************/ + +static void fill_deferred_open(char *p, uint16 mid, struct timeval *ptv, SMB_DEV_T dev, SMB_INO_T inode, uint16 port) +{ + deferred_open_entry *e = (deferred_open_entry *)p; + void *x = &e->time; /* Needed to force alignment. p may not be aligned.... */ + + memset(e, '\0', sizeof(deferred_open_entry)); + e->mid = mid; + e->pid = sys_getpid(); + memcpy(x, ptv, sizeof(struct timeval)); + e->dev = dev; + e->inode = inode; + e->port = port; +} + +/******************************************************************* + Add a deferred open record. Return False on fail, True on success. +********************************************************************/ + +BOOL add_deferred_open(uint16 mid, struct timeval *ptv, SMB_DEV_T dev, SMB_INO_T inode, uint16 port, const char *fname) +{ + TDB_DATA dbuf; + struct deferred_open_data *data; + char *p=NULL; + int size; + TDB_DATA key = deferred_open_locking_key(dev, inode); + BOOL ret = True; + + /* read in the existing deferred open records if any */ + dbuf = tdb_fetch(tdb, key); + if (!dbuf.dptr) { + size_t offset; + /* we'll need to create a new record */ + + size = sizeof(*data) + sizeof(deferred_open_entry) + strlen(fname) + 1; + p = (char *)malloc(size); + if (!p) + return False; + data = (struct deferred_open_data *)p; + data->u.num_deferred_open_entries = 1; + + DEBUG(10,("add_deferred_open: creating entry for file %s. num_deferred_open_entries = 1\n", + fname )); + + offset = sizeof(*data) + sizeof(deferred_open_entry); + safe_strcpy(p + offset, fname, size - offset - 1); + fill_deferred_open(p + sizeof(*data), mid, ptv, dev, inode, port); + dbuf.dptr = p; + dbuf.dsize = size; + if (tdb_store(tdb, key, dbuf, TDB_REPLACE) == -1) + ret = False; + + print_deferred_open_table((struct deferred_open_data *)p); + + SAFE_FREE(p); + return ret; + } + + /* we're adding to an existing entry - this is a bit fiddly */ + data = (struct deferred_open_data *)dbuf.dptr; + + data->u.num_deferred_open_entries++; + + DEBUG(10,("add_deferred_open: adding entry for file %s. new num_deferred_open_entries = %d\n", + fname, data->u.num_deferred_open_entries )); + + size = dbuf.dsize + sizeof(deferred_open_entry); + p = malloc(size); + if (!p) { + SAFE_FREE(dbuf.dptr); + return False; + } + memcpy(p, dbuf.dptr, sizeof(*data)); + fill_deferred_open(p + sizeof(*data), mid, ptv, dev, inode, port); + memcpy(p + sizeof(*data) + sizeof(deferred_open_entry), dbuf.dptr + sizeof(*data), + dbuf.dsize - sizeof(*data)); + SAFE_FREE(dbuf.dptr); + dbuf.dptr = p; + dbuf.dsize = size; + if (tdb_store(tdb, key, dbuf, TDB_REPLACE) == -1) + ret = False; + print_deferred_open_table((struct deferred_open_data *)p); + SAFE_FREE(p); + return ret; +} + /**************************************************************************** Traverse the whole database with this function, calling traverse_callback on each share mode @@ -862,6 +1240,10 @@ static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, SHAREMODE_FN(traverse_callback) = (SHAREMODE_FN_CAST())state; + /* Ensure this is a locking_key record. */ + if (kbuf.dsize != sizeof(struct locking_key)) + return 0; + data = (struct locking_data *)dbuf.dptr; shares = (share_mode_entry *)(dbuf.dptr + sizeof(*data)); name = dbuf.dptr + sizeof(*data) + data->u.num_share_mode_entries*sizeof(*shares); diff --git a/source3/printing/nt_printing.c b/source3/printing/nt_printing.c index 909aed6c86..225ff20ec3 100644 --- a/source3/printing/nt_printing.c +++ b/source3/printing/nt_printing.c @@ -1003,9 +1003,9 @@ static int file_version_is_newer(connection_struct *conn, fstring new_file, fstr driver_unix_convert(filepath,conn,NULL,&bad_path,&stat_buf); fsp = open_file_shared(conn, filepath, &stat_buf, - SET_OPEN_MODE(DOS_OPEN_RDONLY), + SET_DENY_MODE(DENY_NONE)|SET_OPEN_MODE(DOS_OPEN_RDONLY), (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN), - FILE_ATTRIBUTE_NORMAL, 0, &access_mode, &action); + FILE_ATTRIBUTE_NORMAL, INTERNAL_OPEN_ONLY, &access_mode, &action); if (!fsp) { /* Old file not found, so by definition new file is in fact newer */ DEBUG(10,("file_version_is_newer: Can't open old file [%s], errno = %d\n", @@ -1032,9 +1032,9 @@ static int file_version_is_newer(connection_struct *conn, fstring new_file, fstr driver_unix_convert(filepath,conn,NULL,&bad_path,&stat_buf); fsp = open_file_shared(conn, filepath, &stat_buf, - SET_OPEN_MODE(DOS_OPEN_RDONLY), + SET_DENY_MODE(DENY_NONE)|SET_OPEN_MODE(DOS_OPEN_RDONLY), (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN), - FILE_ATTRIBUTE_NORMAL, 0, &access_mode, &action); + FILE_ATTRIBUTE_NORMAL, INTERNAL_OPEN_ONLY, &access_mode, &action); if (!fsp) { /* New file not found, this shouldn't occur if the caller did its job */ DEBUG(3,("file_version_is_newer: Can't open new file [%s], errno = %d\n", @@ -1148,9 +1148,9 @@ static uint32 get_correct_cversion(const char *architecture, fstring driverpath_ driver_unix_convert(driverpath,conn,NULL,&bad_path,&st); fsp = open_file_shared(conn, driverpath, &st, - SET_OPEN_MODE(DOS_OPEN_RDONLY), + SET_DENY_MODE(DENY_NONE)|SET_OPEN_MODE(DOS_OPEN_RDONLY), (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN), - FILE_ATTRIBUTE_NORMAL, 0, &access_mode, &action); + FILE_ATTRIBUTE_NORMAL, INTERNAL_OPEN_ONLY, &access_mode, &action); if (!fsp) { DEBUG(3,("get_correct_cversion: Can't open file [%s], errno = %d\n", driverpath, errno)); diff --git a/source3/rpc_server/srv_srvsvc_nt.c b/source3/rpc_server/srv_srvsvc_nt.c index 77b9be9966..087c50451e 100644 --- a/source3/rpc_server/srv_srvsvc_nt.c +++ b/source3/rpc_server/srv_srvsvc_nt.c @@ -1886,8 +1886,9 @@ WERROR _srv_net_file_query_secdesc(pipes_struct *p, SRV_Q_NET_FILE_QUERY_SECDESC unistr2_to_ascii(filename, &q_u->uni_file_name, sizeof(filename)); unix_convert(filename, conn, NULL, &bad_path, &st); - fsp = open_file_shared(conn, filename, &st, SET_OPEN_MODE(DOS_OPEN_RDONLY), - (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN), FILE_ATTRIBUTE_NORMAL, 0, &access_mode, &action); + fsp = open_file_shared(conn, filename, &st, SET_DENY_MODE(DENY_NONE)|SET_OPEN_MODE(DOS_OPEN_RDONLY), + (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN), FILE_ATTRIBUTE_NORMAL, INTERNAL_OPEN_ONLY, + &access_mode, &action); if (!fsp) { /* Perhaps it is a directory */ @@ -1990,8 +1991,9 @@ WERROR _srv_net_file_set_secdesc(pipes_struct *p, SRV_Q_NET_FILE_SET_SECDESC *q_ unistr2_to_ascii(filename, &q_u->uni_file_name, sizeof(filename)); unix_convert(filename, conn, NULL, &bad_path, &st); - fsp = open_file_shared(conn, filename, &st, SET_OPEN_MODE(DOS_OPEN_RDWR), - (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN), FILE_ATTRIBUTE_NORMAL, 0, &access_mode, &action); + fsp = open_file_shared(conn, filename, &st, SET_DENY_MODE(DENY_NONE)|SET_OPEN_MODE(DOS_OPEN_RDWR), + (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN), FILE_ATTRIBUTE_NORMAL, INTERNAL_OPEN_ONLY, + &access_mode, &action); if (!fsp) { /* Perhaps it is a directory */ diff --git a/source3/script/mkproto.awk b/source3/script/mkproto.awk index b38f405e0d..fbe1bddf35 100644 --- a/source3/script/mkproto.awk +++ b/source3/script/mkproto.awk @@ -124,7 +124,7 @@ END { gotstart = 1; } - if( $0 ~ /^smb_iconv_t|^long|^char|^uint|^NTSTATUS|^WERROR|^CLI_POLICY_HND|^struct|^BOOL|^void|^time|^smb_shm_offset_t|^shm_offset_t|^FILE|^XFILE|^SMB_OFF_T|^size_t|^ssize_t|^SMB_BIG_UINT/ ) { + if( $0 ~ /^smb_iconv_t|^long|^char|^uint|^NTSTATUS|^WERROR|^CLI_POLICY_HND|^struct|^BOOL|^void|^time|^smb_shm_offset_t|^shm_offset_t|^FILE|^XFILE|^SMB_OFF_T|^size_t|^ssize_t|^SMB_BIG_UINT|^SMB_BIG_INT/ ) { gotstart = 1; } diff --git a/source3/smbd/close.c b/source3/smbd/close.c index 8b3010c1b2..305377dfc6 100644 --- a/source3/smbd/close.c +++ b/source3/smbd/close.c @@ -2,6 +2,7 @@ Unix SMB/CIFS implementation. file closing Copyright (C) Andrew Tridgell 1992-1998 + Copyright (C) Jeremy Allison 1992-2004. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -104,6 +105,32 @@ static int close_filestruct(files_struct *fsp) return ret; } +/**************************************************************************** + If any deferred opens are waiting on this close, notify them. +****************************************************************************/ + +static void notify_deferred_opens(files_struct *fsp) +{ + deferred_open_entry *de_array = NULL; + int num_de_entries, i; + pid_t mypid = sys_getpid(); + + num_de_entries = get_deferred_opens(fsp->conn, fsp->dev, fsp->inode, &de_array); + for (i = 0; i < num_de_entries; i++) { + deferred_open_entry *entry = &de_array[i]; + if (entry->pid == mypid) { + /* + * We need to notify ourself to retry the open. + * Do this by finding the queued SMB record, moving it + * to the head of the queue and changing the wait time to zero. + */ + schedule_sharing_violation_open_smb_message(entry->mid); + } else { + send_deferred_open_retry_message(entry); + } + } +} + /**************************************************************************** Close a file. @@ -177,6 +204,9 @@ static int close_normal_file(files_struct *fsp, BOOL normal_close) SAFE_FREE(share_entry); + /* Notify any deferred opens waiting on this close. */ + notify_deferred_opens(fsp); + /* * NT can set delete_on_close of the last open * reference to a file. diff --git a/source3/smbd/dir.c b/source3/smbd/dir.c index 06ef23ab8c..b88f687766 100644 --- a/source3/smbd/dir.c +++ b/source3/smbd/dir.c @@ -763,7 +763,8 @@ static BOOL user_can_write_file(connection_struct *conn, char *name, SMB_STRUCT_ return True; else fsp = open_file_shared1(conn, name, pst, FILE_WRITE_ATTRIBUTES, SET_DENY_MODE(DENY_NONE), - (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN), FILE_ATTRIBUTE_NORMAL, 0, &access_mode, &smb_action); + (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN), FILE_ATTRIBUTE_NORMAL, INTERNAL_OPEN_ONLY, + &access_mode, &smb_action); if (!fsp) return False; diff --git a/source3/smbd/nttrans.c b/source3/smbd/nttrans.c index 26be4434fd..e540db234a 100644 --- a/source3/smbd/nttrans.c +++ b/source3/smbd/nttrans.c @@ -863,6 +863,10 @@ create_options = 0x%x root_dir_fid = 0x%x\n", flags, desired_access, file_attrib restore_case_semantics(conn, file_attributes); END_PROFILE(SMBntcreateX); + if (open_was_deferred(SVAL(inbuf,smb_mid))) { + /* We have re-scheduled this call. */ + return -1; + } return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRnoaccess); } } @@ -1347,6 +1351,10 @@ static int call_nt_transact_create(connection_struct *conn, char *inbuf, char *o } } else { restore_case_semantics(conn, file_attributes); + if (open_was_deferred(SVAL(inbuf,smb_mid))) { + /* We have re-scheduled this call. */ + return -1; + } return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRnoaccess); } } diff --git a/source3/smbd/open.c b/source3/smbd/open.c index ab5ea236fa..04e074d56e 100644 --- a/source3/smbd/open.c +++ b/source3/smbd/open.c @@ -2,7 +2,7 @@ Unix SMB/CIFS implementation. file opening and share modes Copyright (C) Andrew Tridgell 1992-1998 - Copyright (C) Jeremy Allison 2001 + Copyright (C) Jeremy Allison 2001-2004 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -26,6 +26,11 @@ extern uint16 global_oplock_port; extern uint16 global_smbpid; extern BOOL global_client_failed_oplock_break; +struct dev_inode_bundle { + SMB_DEV_T dev; + SMB_INO_T inode; +}; + /**************************************************************************** fd support routines - attempt to do a dos_open. ****************************************************************************/ @@ -243,7 +248,7 @@ static BOOL open_file(files_struct *fsp,connection_struct *conn, } /******************************************************************* -return True if the filename is one of the special executable types + Return True if the filename is one of the special executable types. ********************************************************************/ static BOOL is_executable(const char *fname) @@ -262,12 +267,13 @@ static BOOL is_executable(const char *fname) enum {AFAIL,AREAD,AWRITE,AALL}; /******************************************************************* -reproduce the share mode access table -this is horrendoously complex, and really can't be justified on any -rational grounds except that this is _exactly_ what NT does. See -the DENY1 and DENY2 tests in smbtorture for a comprehensive set of -test routines. + Reproduce the share mode access table. + This is horrendoously complex, and really can't be justified on any + rational grounds except that this is _exactly_ what NT does. See + the DENY1 and DENY2 tests in smbtorture for a comprehensive set of + test routines. ********************************************************************/ + static int access_table(int new_deny,int old_deny,int old_mode, BOOL same_pid, BOOL isexe) { @@ -353,9 +359,8 @@ static int access_table(int new_deny,int old_deny,int old_mode, return(AFAIL); } - /**************************************************************************** -check if we can open a file with a share mode + Check if we can open a file with a share mode. ****************************************************************************/ static BOOL check_share_mode(connection_struct *conn, share_mode_entry *share, int share_mode, uint32 desired_access, @@ -770,9 +775,101 @@ after break ! For file %s, dev = %x, inode = %.0f. Deleting it to continue...\n" } /**************************************************************************** -set a kernel flock on a file for NFS interoperability -this requires a patch to Linux + Delete the record for a handled deferred open entry. +****************************************************************************/ + +static void delete_defered_open_entry_record(connection_struct *conn, SMB_DEV_T dev, SMB_INO_T inode) +{ + uint16 mid = get_current_mid(); + pid_t mypid = sys_getpid(); + deferred_open_entry *de_array = NULL; + int num_de_entries, i; + + num_de_entries = get_deferred_opens(conn, dev, inode, &de_array); + for (i = 0; i < num_de_entries; i++) { + deferred_open_entry *entry = &de_array[i]; + if (entry->pid == mypid && entry->mid == mid && entry->dev == dev && + entry->inode == inode) { + + /* Remove the deferred open entry from the array. */ + delete_deferred_open_entry(entry); + SAFE_FREE(de_array); + return; + } + } + SAFE_FREE(de_array); +} + +/**************************************************************************** + Handle the 1 second delay in returning a SHARING_VIOLATION error. +****************************************************************************/ + +void defer_open_sharing_error(connection_struct *conn, struct timeval *ptv, + char *fname, SMB_DEV_T dev, SMB_INO_T inode) +{ + uint16 mid = get_current_mid(); + pid_t mypid = sys_getpid(); + deferred_open_entry *de_array = NULL; + int num_de_entries, i; + struct dev_inode_bundle dib; + + dib.dev = dev; + dib.inode = inode; + + num_de_entries = get_deferred_opens(conn, dev, inode, &de_array); + for (i = 0; i < num_de_entries; i++) { + deferred_open_entry *entry = &de_array[i]; + if (entry->pid == mypid && entry->mid == mid) { + /* + * Check if a 1 second timeout has expired. + */ + if (usec_time_diff(ptv, &entry->time) > SHARING_VIOLATION_USEC_WAIT) { + DEBUG(10,("defer_open_sharing_error: Deleting deferred open entry for mid %u, \ +file %s\n", + (unsigned int)mid, fname )); + + /* Expired, return a real error. */ + /* Remove the deferred open entry from the array. */ + + delete_deferred_open_entry(entry); + SAFE_FREE(de_array); + return; + } + /* + * If the timeout hasn't expired yet and we still have a sharing violation, + * just leave the entry in the deferred open array alone. We do need to + * reschedule this open call though (with the original created time). + */ + DEBUG(10,("defer_open_sharing_error: time [%u.%06u] updating \ +deferred open entry for mid %u, file %s\n", + (unsigned int)entry->time.tv_sec, + (unsigned int)entry->time.tv_usec, + (unsigned int)mid, fname )); + + push_sharing_violation_open_smb_message(&entry->time, (char *)&dib, sizeof(dib)); + SAFE_FREE(de_array); + return; + } + } + + DEBUG(10,("defer_open_sharing_error: time [%u.%06u] adding deferred open entry for mid %u, file %s\n", + (unsigned int)ptv->tv_sec, (unsigned int)ptv->tv_usec, (unsigned int)mid, fname )); + + if (!push_sharing_violation_open_smb_message(ptv, (char *)&dib, sizeof(dib))) { + SAFE_FREE(de_array); + return; + } + if (!add_deferred_open(mid, ptv, dev, inode, global_oplock_port, fname)) { + remove_sharing_violation_open_smb_message(mid); + } + SAFE_FREE(de_array); +} + +/**************************************************************************** + Set a kernel flock on a file for NFS interoperability. + This requires a patch to Linux. ****************************************************************************/ + static void kernel_flock(files_struct *fsp, int deny_mode) { #if HAVE_KERNEL_SHARE_MODES @@ -847,6 +944,7 @@ files_struct *open_file_shared1(connection_struct *conn,char *fname, SMB_STRUCT_ BOOL fcbopen = False; BOOL def_acl = False; BOOL add_share_mode = True; + BOOL internal_only_open = False; SMB_DEV_T dev = 0; SMB_INO_T inode = 0; int num_share_modes = 0; @@ -858,9 +956,50 @@ files_struct *open_file_shared1(connection_struct *conn,char *fname, SMB_STRUCT_ mode_t new_mode = (mode_t)0; int action; uint32 existing_dos_mode = 0; + struct pending_message_list *pml = NULL; + uint16 mid = get_current_mid(); /* We add aARCH to this as this mode is only used if the file is created new. */ mode_t mode = unix_mode(conn,new_dos_mode | aARCH,fname); + if (oplock_request == INTERNAL_OPEN_ONLY) { + internal_only_open = True; + oplock_request = 0; + } + + if ((pml = get_open_deferred_message(mid)) != NULL) { + struct dev_inode_bundle dib; + + memcpy(&dib, pml->private_data.data, sizeof(dib)); + + /* There could be a race condition where the dev/inode pair + has changed since we deferred the message. If so, just + remove the deferred open entry and return sharing violation. */ + + /* If the timeout value is non-zero, we need to just + return sharing violation. Don't retry the open + as we were not notified of a close and we don't want to + trigger another spurious oplock break. */ + + if (!file_existed || dib.dev != psbuf->st_dev || dib.inode != psbuf->st_ino || + pml->msg_time.tv_sec || pml->msg_time.tv_usec) { + /* Ensure we don't reprocess this message. */ + remove_sharing_violation_open_smb_message(mid); + + /* Now remove the deferred open entry under lock. */ + lock_share_entry(conn, dib.dev, dib.inode); + delete_defered_open_entry_record(conn, dib.dev, dib.inode); + unlock_share_entry(conn, dib.dev, dib.inode); + + unix_ERR_class = ERRDOS; + unix_ERR_code = ERRbadshare; + unix_ERR_ntstatus = NT_STATUS_SHARING_VIOLATION; + return NULL; + } + /* Ensure we don't reprocess this message. */ + remove_sharing_violation_open_smb_message(mid); + + } + if (conn->printer) { /* printers are handled completely differently. Most of the passed parameters are ignored */ @@ -1043,17 +1182,28 @@ flags=0x%X flags2=0x%X mode=0%o returned %d\n", unix_ERR_ntstatus = NT_STATUS_ACCESS_DENIED; } + /* + * If we're returning a share violation, ensure we cope with + * the braindead 1 second delay. + */ + + if (!internal_only_open && NT_STATUS_EQUAL(unix_ERR_ntstatus,NT_STATUS_SHARING_VIOLATION)) { + /* The fsp->open_time here represents the current time of day. */ + defer_open_sharing_error(conn, &fsp->open_time, fname, dev, inode); + } + unlock_share_entry(conn, dev, inode); - if (fsp_open) + if (fsp_open) { fd_close(conn, fsp); + /* + * We have detected a sharing violation here + * so return the correct error code + */ + unix_ERR_class = ERRDOS; + unix_ERR_code = ERRbadshare; + unix_ERR_ntstatus = NT_STATUS_SHARING_VIOLATION; + } file_free(fsp); - /* - * We have detected a sharing violation here - * so return the correct error code - */ - unix_ERR_class = ERRDOS; - unix_ERR_code = ERRbadshare; - unix_ERR_ntstatus = NT_STATUS_SHARING_VIOLATION; return NULL; } @@ -1118,6 +1268,16 @@ flags=0x%X flags2=0x%X mode=0%o returned %d\n", &flags, &oplock_request, &all_current_opens_are_level_II); if(num_share_modes == -1) { + /* + * If we're returning a share violation, ensure we cope with + * the braindead 1 second delay. + */ + + if (!internal_only_open && NT_STATUS_EQUAL(unix_ERR_ntstatus,NT_STATUS_SHARING_VIOLATION)) { + /* The fsp->open_time here represents the current time of day. */ + defer_open_sharing_error(conn, &fsp->open_time, fname, dev, inode); + } + unlock_share_entry_fsp(fsp); fd_close(conn,fsp); file_free(fsp); @@ -1286,6 +1446,8 @@ flags=0x%X flags2=0x%X mode=0%o returned %d\n", fname, (int)new_mode)); } + /* If this is a successful open, we must remove any deferred open records. */ + delete_defered_open_entry_record(conn, fsp->dev, fsp->inode); unlock_share_entry_fsp(fsp); conn->num_files_open++; diff --git a/source3/smbd/oplock.c b/source3/smbd/oplock.c index 19e6956d9e..6739f5654f 100644 --- a/source3/smbd/oplock.c +++ b/source3/smbd/oplock.c @@ -388,6 +388,30 @@ pid %d, port %d, dev = %x, inode = %.0f, file_id = %lu\n", (int)remotepid, from_port, (unsigned int)dev, (double)inode, file_id)); break; + case RETRY_DEFERRED_OPEN_CMD: + + /* Request to retry and open that would return SHARING_VIOLATION. */ + if (msg_len != DEFERRED_OPEN_MSG_LEN) { + DEBUG(0,("process_local_message: incorrect length for RETRY_DEFERRED_OPEN_CMD (was %d, should be %d).\n", + (int)msg_len, (int)DEFERRED_OPEN_MSG_LEN)); + return False; + } + { + uint16 mid; + + memcpy((char *)&remotepid, msg_start+DEFERRED_OPEN_PID_OFFSET,sizeof(remotepid)); + memcpy((char *)&inode, msg_start+DEFERRED_OPEN_INODE_OFFSET,sizeof(inode)); + memcpy((char *)&dev, msg_start+DEFERRED_OPEN_DEV_OFFSET,sizeof(dev)); + memcpy((char *)&mid, msg_start+DEFERRED_OPEN_MID_OFFSET,sizeof(mid)); + + DEBUG(5,("process_local_message: RETRY_DEFERRED_OPEN from \ +pid %d, port %d, dev = %x, inode = %.0f, mid = %u\n", + (int)remotepid, from_port, (unsigned int)dev, (double)inode, (unsigned int)mid)); + + schedule_sharing_violation_open_smb_message(mid); + } + break; + /* * Keep this as a debug case - eventually we can remove it. */ @@ -1215,7 +1239,51 @@ void release_level_2_oplocks_on_change(files_struct *fsp) } /**************************************************************************** -setup oplocks for this process + Send a 'retry your open' message to a process with a deferred open entry. +****************************************************************************/ + +BOOL send_deferred_open_retry_message(deferred_open_entry *entry) +{ + char de_msg[DEFERRED_OPEN_MSG_LEN]; + struct sockaddr_in addr_out; + pid_t pid = sys_getpid(); + + memset(de_msg, '\0', DEFERRED_OPEN_MSG_LEN); + SSVAL(de_msg,DEFERRED_OPEN_CMD_OFFSET,RETRY_DEFERRED_OPEN_CMD); + memcpy(de_msg+DEFERRED_OPEN_PID_OFFSET,(char *)&pid,sizeof(pid)); + memcpy(de_msg+DEFERRED_OPEN_DEV_OFFSET,(char *)&entry->dev,sizeof(entry->dev)); + memcpy(de_msg+DEFERRED_OPEN_INODE_OFFSET,(char *)&entry->inode,sizeof(entry->inode)); + memcpy(de_msg+DEFERRED_OPEN_MID_OFFSET,(char *)&entry->mid,sizeof(entry->mid)); + + /* Set the address and port. */ + memset((char *)&addr_out,'\0',sizeof(addr_out)); + addr_out.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + addr_out.sin_port = htons( entry->port ); + addr_out.sin_family = AF_INET; + + if( DEBUGLVL( 3 ) ) { + dbgtext( "send_deferred_open_retry_message: sending a message to "); + dbgtext( "pid %d on port %d ", (int)entry->pid, entry->port ); + dbgtext( "for dev = %x, inode = %.0f, mid = %u\n", + (unsigned int)entry->dev, (double)entry->inode, (unsigned int)entry->mid ); + } + + if(sys_sendto(oplock_sock,de_msg,DEFERRED_OPEN_MSG_LEN,0, + (struct sockaddr *)&addr_out,sizeof(addr_out)) < 0) { + if( DEBUGLVL( 0 ) ) { + dbgtext( "send_deferred_open_retry_message: failed sending a message to "); + dbgtext( "pid %d on port %d ", (int)entry->pid, entry->port ); + dbgtext( "for dev = %x, inode = %.0f, mid = %u\n", + (unsigned int)entry->dev, (double)entry->inode, (unsigned int)entry->mid ); + dbgtext( "Error was %s\n", strerror(errno) ); + } + return False; + } + return True; +} + +/**************************************************************************** + Setup oplocks for this process. ****************************************************************************/ BOOL init_oplocks(void) diff --git a/source3/smbd/process.c b/source3/smbd/process.c index ccebd2b86c..698c8475f7 100644 --- a/source3/smbd/process.c +++ b/source3/smbd/process.c @@ -61,20 +61,28 @@ uint16 get_current_mid(void) for processing. ****************************************************************************/ -struct pending_message_list { - struct pending_message_list *next, *prev; - char *msg_buf; - int msg_len; -}; - static struct pending_message_list *smb_oplock_queue; +static struct pending_message_list *smb_sharing_violation_queue; + +enum q_type { OPLOCK_QUEUE, SHARE_VIOLATION_QUEUE }; + +/**************************************************************************** + Free up a message. +****************************************************************************/ + +static void free_queued_message(struct pending_message_list *msg) +{ + data_blob_free(&msg->buf); + data_blob_free(&msg->private_data); + SAFE_FREE(msg); +} /**************************************************************************** Function to push a message onto the tail of a linked list of smb messages ready for processing. ****************************************************************************/ -static BOOL push_message(char *buf, int msg_len) +static BOOL push_queued_message(enum q_type qt, char *buf, int msg_len, struct timeval *ptv, char *private, size_t private_len) { struct pending_message_list *tmp_msg; struct pending_message_list *msg = (struct pending_message_list *) @@ -85,32 +93,159 @@ static BOOL push_message(char *buf, int msg_len) return False; } - msg->msg_buf = (char *)malloc(msg_len); - if(msg->msg_buf == NULL) { + memset(msg,'\0',sizeof(*msg)); + + msg->buf = data_blob(buf, msg_len); + if(msg->buf.data == NULL) { DEBUG(0,("push_message: malloc fail (2)\n")); SAFE_FREE(msg); return False; } - memcpy(msg->msg_buf, buf, msg_len); - msg->msg_len = msg_len; + if (ptv) { + msg->msg_time = *ptv; + } - DLIST_ADD_END(smb_oplock_queue, msg, tmp_msg); + if (private) { + msg->private_data = data_blob(private, private_len); + if (msg->private_data.data == NULL) { + DEBUG(0,("push_message: malloc fail (3)\n")); + data_blob_free(&msg->buf); + SAFE_FREE(msg); + } + } + + if (qt == OPLOCK_QUEUE) { + DLIST_ADD_END(smb_oplock_queue, msg, tmp_msg); + } else { + DLIST_ADD_END(smb_sharing_violation_queue, msg, tmp_msg); + } /* Push the MID of this packet on the signing queue. */ srv_defer_sign_response(SVAL(buf,smb_mid)); + DEBUG(10,("push_message: pushed message length %u on queue %s\n", + (unsigned int)msg_len, + qt == OPLOCK_QUEUE ? "smb_oplock_queue" : "smb_sharing_violation_queue" )); + return True; } /**************************************************************************** - Function to push a smb message onto a linked list of local smb messages ready + Function to push an oplock smb message onto a linked list of local smb messages ready for processing. ****************************************************************************/ BOOL push_oplock_pending_smb_message(char *buf, int msg_len) { - return push_message(buf, msg_len); + return push_queued_message(OPLOCK_QUEUE, buf, msg_len, NULL, NULL, 0); +} + +/**************************************************************************** + Function to delete a sharing violation open message by mid. +****************************************************************************/ + +void remove_sharing_violation_open_smb_message(uint16 mid) +{ + struct pending_message_list *pml; + + for (pml = smb_sharing_violation_queue; pml; pml = pml->next) { + if (mid == SVAL(pml->buf.data,smb_mid)) { + DEBUG(10,("remove_sharing_violation_open_smb_message: deleting mid %u len %u\n", + (unsigned int)mid, (unsigned int)pml->buf.length )); + DLIST_REMOVE(smb_sharing_violation_queue, pml); + free_queued_message(pml); + return; + } + } +} + +/**************************************************************************** + Move a sharing violation open retry message to the front of the list and + schedule it for immediate processing. +****************************************************************************/ + +void schedule_sharing_violation_open_smb_message(uint16 mid) +{ + struct pending_message_list *pml; + int i = 0; + + for (pml = smb_sharing_violation_queue; pml; pml = pml->next) { + uint16 msg_mid = SVAL(pml->buf.data,smb_mid); + DEBUG(10,("schedule_sharing_violation_open_smb_message: [%d] msg_mid = %u\n", i++, + (unsigned int)msg_mid )); + if (mid == msg_mid) { + DEBUG(10,("schedule_sharing_violation_open_smb_message: scheduling mid %u\n", + mid )); + pml->msg_time.tv_sec = 0; + pml->msg_time.tv_usec = 0; + DLIST_PROMOTE(smb_sharing_violation_queue, pml); + return; + } + } + + DEBUG(10,("schedule_sharing_violation_open_smb_message: failed to find message mid %u\n", + mid )); +} + +/**************************************************************************** + Return true if this mid is on the deferred queue. +****************************************************************************/ + +BOOL open_was_deferred(uint16 mid) +{ + struct pending_message_list *pml; + for (pml = smb_sharing_violation_queue; pml; pml = pml->next) { + if (SVAL(pml->buf.data,smb_mid) == mid) { + return True; + } + } + return False; +} + +/**************************************************************************** + Return the message queued by this mid. +****************************************************************************/ + +struct pending_message_list *get_open_deferred_message(uint16 mid) +{ + struct pending_message_list *pml; + for (pml = smb_sharing_violation_queue; pml; pml = pml->next) { + if (SVAL(pml->buf.data,smb_mid) == mid) { + return pml; + } + } + return NULL; +} + +/**************************************************************************** + Function to push a sharing violation open smb message onto a linked list of local smb messages ready + for processing. +****************************************************************************/ + +BOOL push_sharing_violation_open_smb_message(struct timeval *ptv, char *private, size_t priv_len) +{ + uint16 mid = SVAL(InBuffer,smb_mid); + struct timeval tv; + SMB_BIG_INT tdif; + + tv = *ptv; + tdif = tv.tv_sec; + tdif *= 1000000; + tdif += tv.tv_usec; + + /* Add on the timeout. */ + tdif += SHARING_VIOLATION_USEC_WAIT; + + tv.tv_sec = tdif / 1000000; + tv.tv_usec = tdif % 1000000; + + DEBUG(10,("push_sharing_violation_open_smb_message: pushing message len %u mid %u\ + timeout time [%u.%06u]\n", (unsigned int) smb_len(InBuffer)+4, (unsigned int)mid, + (unsigned int)tv.tv_sec, (unsigned int)tv.tv_usec)); + + return push_queued_message(SHARE_VIOLATION_QUEUE, InBuffer, + smb_len(InBuffer)+4, &tv, private, priv_len); } /**************************************************************************** @@ -169,12 +304,17 @@ static BOOL receive_message_or_smb(char *buffer, int buffer_len, int timeout) fd_set fds; int selrtn; struct timeval to; + struct timeval *pto; int maxfd; smb_read_error = 0; again: + to.tv_sec = timeout / 1000; + to.tv_usec = (timeout % 1000) * 1000; + pto = timeout > 0 ? &to : NULL; + /* * Note that this call must be before processing any SMB * messages as we need to synchronously process any messages @@ -188,17 +328,55 @@ static BOOL receive_message_or_smb(char *buffer, int buffer_len, int timeout) */ if(smb_oplock_queue != NULL) { struct pending_message_list *msg = smb_oplock_queue; - memcpy(buffer, msg->msg_buf, MIN(buffer_len, msg->msg_len)); + memcpy(buffer, msg->buf.data, MIN(buffer_len, msg->buf.length)); /* Free the message we just copied. */ DLIST_REMOVE(smb_oplock_queue, msg); - SAFE_FREE(msg->msg_buf); - SAFE_FREE(msg); + free_queued_message(msg); DEBUG(5,("receive_message_or_smb: returning queued smb message.\n")); return True; } + /* + * Check to see if we already have a message on the deferred open queue + * and it's time to schedule. + */ + if(smb_sharing_violation_queue != NULL) { + BOOL pop_message = False; + struct pending_message_list *msg = smb_sharing_violation_queue; + + if (msg->msg_time.tv_sec == 0 && msg->msg_time.tv_usec == 0) { + pop_message = True; + } else { + struct timeval tv; + SMB_BIG_INT tdif; + + GetTimeOfDay(&tv); + tdif = usec_time_diff(&msg->msg_time, &tv); + if (tdif <= 0) { + /* Timed out. Schedule...*/ + pop_message = True; + DEBUG(10,("receive_message_or_smb: queued message timed out.\n")); + } else { + /* Make a more accurate select timeout. */ + to.tv_sec = tdif / 1000000; + to.tv_usec = tdif % 1000000; + pto = &to; + DEBUG(10,("receive_message_or_smb: select with timeout of [%u.%06u]\n", + (unsigned int)pto->tv_sec, (unsigned int)pto->tv_usec )); + } + } + + if (pop_message) { + memcpy(buffer, msg->buf.data, MIN(buffer_len, msg->buf.length)); + + /* We leave this message on the queue so the open code can + know this is a retry. */ + DEBUG(5,("receive_message_or_smb: returning deferred open smb message.\n")); + return True; + } + } /* * Setup the select read fd set. @@ -229,10 +407,7 @@ static BOOL receive_message_or_smb(char *buffer, int buffer_len, int timeout) FD_SET(smbd_server_fd(),&fds); maxfd = setup_oplock_select_set(&fds); - to.tv_sec = timeout / 1000; - to.tv_usec = (timeout % 1000) * 1000; - - selrtn = sys_select(MAX(maxfd,smbd_server_fd())+1,&fds,NULL,NULL,timeout>0?&to:NULL); + selrtn = sys_select(MAX(maxfd,smbd_server_fd())+1,&fds,NULL,NULL,pto); /* if we get EINTR then maybe we have received an oplock signal - treat this as select returning 1. This is ugly, but diff --git a/source3/smbd/reply.c b/source3/smbd/reply.c index fff5385171..bf208b0fa4 100644 --- a/source3/smbd/reply.c +++ b/source3/smbd/reply.c @@ -1043,6 +1043,10 @@ int reply_open(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, if (!fsp) { END_PROFILE(SMBopen); + if (open_was_deferred(SVAL(inbuf,smb_mid))) { + /* We have re-scheduled this call. */ + return -1; + } return set_bad_path_error(errno, bad_path, outbuf, ERRDOS, ERRnoaccess); } @@ -1132,6 +1136,10 @@ int reply_open_and_X(connection_struct *conn, char *inbuf,char *outbuf,int lengt if (!fsp) { END_PROFILE(SMBopenX); + if (open_was_deferred(SVAL(inbuf,smb_mid))) { + /* We have re-scheduled this call. */ + return -1; + } return set_bad_path_error(errno, bad_path, outbuf, ERRDOS, ERRnoaccess); } @@ -1257,6 +1265,10 @@ int reply_mknew(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, if (!fsp) { END_PROFILE(SMBcreate); + if (open_was_deferred(SVAL(inbuf,smb_mid))) { + /* We have re-scheduled this call. */ + return -1; + } return set_bad_path_error(errno, bad_path, outbuf, ERRDOS, ERRnoaccess); } @@ -1332,6 +1344,10 @@ int reply_ctemp(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, if (!fsp) { END_PROFILE(SMBctemp); + if (open_was_deferred(SVAL(inbuf,smb_mid))) { + /* We have re-scheduled this call. */ + return -1; + } return set_bad_path_error(errno, bad_path, outbuf, ERRDOS, ERRnoaccess); } @@ -1623,8 +1639,13 @@ int reply_unlink(connection_struct *conn, char *inbuf,char *outbuf, int dum_size DEBUG(3,("reply_unlink : %s\n",name)); status = unlink_internals(conn, dirtype, name); - if (!NT_STATUS_IS_OK(status)) + if (!NT_STATUS_IS_OK(status)) { + if (open_was_deferred(SVAL(inbuf,smb_mid))) { + /* We have re-scheduled this call. */ + return -1; + } return ERROR_NT(status); + } /* * Win2k needs a changenotify request response before it will @@ -3944,6 +3965,10 @@ int reply_mv(connection_struct *conn, char *inbuf,char *outbuf, int dum_size, status = rename_internals(conn, name, newname, attrs, False); if (!NT_STATUS_IS_OK(status)) { END_PROFILE(SMBmv); + if (open_was_deferred(SVAL(inbuf,smb_mid))) { + /* We have re-scheduled this call. */ + return -1; + } return ERROR_NT(status); } @@ -3989,7 +4014,8 @@ static BOOL copy_file(char *src,char *dest1,connection_struct *conn, int ofun, return(False); fsp1 = open_file_shared(conn,src,&src_sbuf,SET_DENY_MODE(DENY_NONE)|SET_OPEN_MODE(DOS_OPEN_RDONLY), - (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN),FILE_ATTRIBUTE_NORMAL,0,&Access,&action); + (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN),FILE_ATTRIBUTE_NORMAL,INTERNAL_OPEN_ONLY, + &Access,&action); if (!fsp1) return(False); @@ -4002,7 +4028,7 @@ static BOOL copy_file(char *src,char *dest1,connection_struct *conn, int ofun, ZERO_STRUCTP(&sbuf2); fsp2 = open_file_shared(conn,dest,&sbuf2,SET_DENY_MODE(DENY_NONE)|SET_OPEN_MODE(DOS_OPEN_WRONLY), - ofun,dosattrs,0,&Access,&action); + ofun,dosattrs,INTERNAL_OPEN_ONLY,&Access,&action); if (!fsp2) { close_file(fsp1,False); diff --git a/source3/smbd/trans2.c b/source3/smbd/trans2.c index 02a6bf6e4b..0ba26a9147 100644 --- a/source3/smbd/trans2.c +++ b/source3/smbd/trans2.c @@ -627,6 +627,10 @@ static int call_trans2open(connection_struct *conn, char *inbuf, char *outbuf, i oplock_request, &rmode,&smb_action); if (!fsp) { + if (open_was_deferred(SVAL(inbuf,smb_mid))) { + /* We have re-scheduled this call. */ + return -1; + } return set_bad_path_error(errno, bad_path, outbuf, ERRDOS,ERRnoaccess); } @@ -3205,7 +3209,7 @@ static int call_trans2setfilepathinfo(connection_struct *conn, SET_OPEN_MODE(DOS_OPEN_RDWR), (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN), FILE_ATTRIBUTE_NORMAL, - 0, &access_mode, &action); + INTERNAL_OPEN_ONLY, &access_mode, &action); if (new_fsp == NULL) return(UNIXERROR(ERRDOS,ERRbadpath)); @@ -3655,7 +3659,7 @@ size = %.0f, uid = %u, gid = %u, raw perms = 0%o\n", SET_OPEN_MODE(DOS_OPEN_RDWR), (FILE_FAIL_IF_NOT_EXIST|FILE_EXISTS_OPEN), FILE_ATTRIBUTE_NORMAL, - 0, &access_mode, &action); + INTERNAL_OPEN_ONLY, &access_mode, &action); if (new_fsp == NULL) return(UNIXERROR(ERRDOS,ERRbadpath)); -- cgit