summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source3/locking/locking.c200
-rw-r--r--source3/smbd/open.c10
-rw-r--r--source3/smbd/oplock.c13
3 files changed, 159 insertions, 64 deletions
diff --git a/source3/locking/locking.c b/source3/locking/locking.c
index ee3ce7c7d4..4b037c0f3d 100644
--- a/source3/locking/locking.c
+++ b/source3/locking/locking.c
@@ -239,6 +239,7 @@ static int delete_fn(TDB_CONTEXT *ttdb, TDB_DATA kbuf, TDB_DATA dbuf, void *stat
int i, del_count=0;
pid_t mypid = sys_getpid();
BOOL check_self = *(BOOL *)state;
+ int ret = 0;
tdb_chainlock(tdb, kbuf);
@@ -270,13 +271,16 @@ static int delete_fn(TDB_CONTEXT *ttdb, TDB_DATA kbuf, TDB_DATA dbuf, void *stat
dbuf.dsize -= del_count * sizeof(*shares);
/* store it back in the database */
- if (data->num_share_mode_entries == 0)
- tdb_delete(ttdb, kbuf);
- else
- tdb_store(ttdb, kbuf, dbuf, TDB_REPLACE);
+ if (data->num_share_mode_entries == 0) {
+ if (tdb_delete(ttdb, kbuf) == -1)
+ ret = -1;
+ } else {
+ if (tdb_store(ttdb, kbuf, dbuf, TDB_REPLACE) == -1)
+ ret = -1;
+ }
tdb_chainunlock(tdb, kbuf);
- return 0;
+ return ret;
}
/****************************************************************************
@@ -340,8 +344,9 @@ BOOL locking_end(void)
}
/*******************************************************************
- form a static locking key for a dev/inode pair
+ Form a static locking key for a dev/inode pair.
******************************************************************/
+
static TDB_DATA locking_key(SMB_DEV_T dev, SMB_INO_T inode)
{
static struct locking_key key;
@@ -354,6 +359,7 @@ static TDB_DATA locking_key(SMB_DEV_T dev, SMB_INO_T inode)
kbuf.dsize = sizeof(key);
return kbuf;
}
+
static TDB_DATA locking_key_fsp(files_struct *fsp)
{
return locking_key(fsp->dev, fsp->inode);
@@ -377,10 +383,10 @@ void unlock_share_entry(connection_struct *conn,
tdb_chainunlock(tdb, locking_key(dev, inode));
}
-
/*******************************************************************
Lock a hash bucket entry. use a fsp for convenience
******************************************************************/
+
BOOL lock_share_entry_fsp(files_struct *fsp)
{
return tdb_chainlock(tdb, locking_key(fsp->dev, fsp->inode)) == 0;
@@ -389,6 +395,7 @@ BOOL lock_share_entry_fsp(files_struct *fsp)
/*******************************************************************
Unlock a hash bucket entry.
******************************************************************/
+
void unlock_share_entry_fsp(files_struct *fsp)
{
tdb_chainunlock(tdb, locking_key(fsp->dev, fsp->inode));
@@ -397,6 +404,7 @@ void unlock_share_entry_fsp(files_struct *fsp)
/*******************************************************************
Get all share mode entries for a dev/inode pair.
********************************************************************/
+
int get_share_modes(connection_struct *conn,
SMB_DEV_T dev, SMB_INO_T inode,
share_mode_entry **shares)
@@ -408,7 +416,8 @@ int get_share_modes(connection_struct *conn,
*shares = NULL;
dbuf = tdb_fetch(tdb, locking_key(dev, inode));
- if (!dbuf.dptr) return 0;
+ if (!dbuf.dptr)
+ return 0;
data = (struct locking_data *)dbuf.dptr;
ret = data->num_share_mode_entries;
@@ -416,41 +425,76 @@ int get_share_modes(connection_struct *conn,
*shares = (share_mode_entry *)memdup(dbuf.dptr + sizeof(*data), ret * sizeof(**shares));
free(dbuf.dptr);
- if (! *shares) return 0;
+ if (! *shares)
+ return 0;
return ret;
}
/*******************************************************************
- Del the share mode of a file for this process. Return the number
- of entries left, and a memdup'ed copy of the entry deleted.
+ Fill a share mode entry.
********************************************************************/
-size_t del_share_mode(files_struct *fsp, share_mode_entry **ppse)
+static void fill_share_mode(char *p, files_struct *fsp, uint16 port, uint16 op_type)
+{
+ share_mode_entry *e = (share_mode_entry *)p;
+ memset(e, '\0', sizeof(share_mode_entry));
+ e->pid = sys_getpid();
+ e->share_mode = fsp->share_mode;
+ e->op_port = port;
+ e->op_type = op_type;
+ memcpy((char *)&e->time, (char *)&fsp->open_time, sizeof(struct timeval));
+}
+
+/*******************************************************************
+ Check if two share mode entries are identical, ignoring oplock
+ and port info.
+********************************************************************/
+
+static BOOL share_modes_identical( share_mode_entry *e1, share_mode_entry *e2)
+{
+ return (e1->pid == e2->pid &&
+ e1->share_mode == e2->share_mode &&
+ e1->time.tv_sec == e2->time.tv_sec &&
+ e1->time.tv_usec == e2->time.tv_usec );
+}
+
+/*******************************************************************
+ Delete a specific share mode. Return the number
+ of entries left, and a memdup'ed copy of the entry deleted (if required).
+ Ignore if no entry deleted.
+********************************************************************/
+
+ssize_t del_share_entry( SMB_DEV_T dev, SMB_INO_T inode,
+ share_mode_entry *entry, share_mode_entry **ppse)
{
TDB_DATA dbuf;
struct locking_data *data;
int i, del_count=0;
share_mode_entry *shares;
- pid_t pid = sys_getpid();
- size_t count;
+ ssize_t count;
- *ppse = NULL;
+ if (ppse)
+ *ppse = NULL;
/* read in the existing share modes */
- dbuf = tdb_fetch(tdb, locking_key_fsp(fsp));
- if (!dbuf.dptr) return 0;
+ dbuf = tdb_fetch(tdb, locking_key(dev, inode));
+ if (!dbuf.dptr)
+ return -1;
data = (struct locking_data *)dbuf.dptr;
shares = (share_mode_entry *)(dbuf.dptr + sizeof(*data));
- /* find any with our pid and delete it by overwriting with the rest of the data
- from the record */
+ /*
+ * Find any with this pid and delete it
+ * by overwriting with the rest of the data
+ * from the record.
+ */
+
for (i=0;i<data->num_share_mode_entries;) {
- if (shares[i].pid == pid &&
- shares[i].time.tv_sec == fsp->open_time.tv_sec &&
- shares[i].time.tv_usec == fsp->open_time.tv_usec ) {
- *ppse = memdup(&shares[i], sizeof(*shares));
+ if (share_modes_identical(&shares[i], entry)) {
+ if (ppse)
+ *ppse = memdup(&shares[i], sizeof(*shares));
data->num_share_mode_entries--;
memmove(&shares[i], &shares[i+1],
dbuf.dsize - (sizeof(*data) + (i+1)*sizeof(*shares)));
@@ -460,44 +504,53 @@ size_t del_share_mode(files_struct *fsp, share_mode_entry **ppse)
}
}
- /* the record has shrunk a bit */
- dbuf.dsize -= del_count * sizeof(*shares);
+ if (del_count) {
+ /* the record may have shrunk a bit */
+ dbuf.dsize -= del_count * sizeof(*shares);
- count = data->num_share_mode_entries;
+ count = (ssize_t)data->num_share_mode_entries;
- /* store it back in the database */
- if (data->num_share_mode_entries == 0) {
- tdb_delete(tdb, locking_key_fsp(fsp));
- } else {
- tdb_store(tdb, locking_key_fsp(fsp), dbuf, TDB_REPLACE);
+ /* store it back in the database */
+ if (data->num_share_mode_entries == 0) {
+ if (tdb_delete(tdb, locking_key(dev, inode)) == -1)
+ count = -1;
+ } else {
+ if (tdb_store(tdb, locking_key(dev, inode), dbuf, TDB_REPLACE) == -1)
+ count = -1;
+ }
}
-
free(dbuf.dptr);
return count;
}
/*******************************************************************
-fill a share mode entry
+ Del the share mode of a file for this process. Return the number
+ of entries left, and a memdup'ed copy of the entry deleted.
********************************************************************/
-static void fill_share_mode(char *p, files_struct *fsp, uint16 port, uint16 op_type)
+
+ssize_t del_share_mode(files_struct *fsp, share_mode_entry **ppse)
{
- share_mode_entry *e = (share_mode_entry *)p;
- e->pid = sys_getpid();
- e->share_mode = fsp->share_mode;
- e->op_port = port;
- e->op_type = op_type;
- memcpy((char *)&e->time, (char *)&fsp->open_time, sizeof(struct timeval));
+ share_mode_entry entry;
+
+ /*
+ * Fake up a share_mode_entry for comparisons.
+ */
+
+ fill_share_mode((char *)&entry, fsp, 0, 0);
+ return del_share_entry(fsp->dev, fsp->inode, &entry, ppse);
}
/*******************************************************************
Set the share mode of a file. Return False on fail, True on success.
********************************************************************/
+
BOOL set_share_mode(files_struct *fsp, uint16 port, uint16 op_type)
{
TDB_DATA dbuf;
struct locking_data *data;
char *p=NULL;
int size;
+ BOOL ret = True;
/* read in the existing share modes if any */
dbuf = tdb_fetch(tdb, locking_key_fsp(fsp));
@@ -511,15 +564,18 @@ BOOL set_share_mode(files_struct *fsp, uint16 port, uint16 op_type)
size = sizeof(*data) + sizeof(share_mode_entry) + strlen(fname) + 1;
p = (char *)malloc(size);
+ if (!p)
+ return False;
data = (struct locking_data *)p;
data->num_share_mode_entries = 1;
pstrcpy(p + sizeof(*data) + sizeof(share_mode_entry), fname);
fill_share_mode(p + sizeof(*data), fsp, port, op_type);
dbuf.dptr = p;
dbuf.dsize = size;
- tdb_store(tdb, locking_key_fsp(fsp), dbuf, TDB_REPLACE);
+ if (tdb_store(tdb, locking_key_fsp(fsp), dbuf, TDB_REPLACE) == -1)
+ ret = False;
free(p);
- return True;
+ return ret;
}
/* we're adding to an existing entry - this is a bit fiddly */
@@ -528,6 +584,8 @@ BOOL set_share_mode(files_struct *fsp, uint16 port, uint16 op_type)
data->num_share_mode_entries++;
size = dbuf.dsize + sizeof(share_mode_entry);
p = malloc(size);
+ if (!p)
+ return False;
memcpy(p, dbuf.dptr, sizeof(*data));
fill_share_mode(p + sizeof(*data), fsp, port, op_type);
memcpy(p + sizeof(*data) + sizeof(share_mode_entry), dbuf.dptr + sizeof(*data),
@@ -535,15 +593,16 @@ BOOL set_share_mode(files_struct *fsp, uint16 port, uint16 op_type)
free(dbuf.dptr);
dbuf.dptr = p;
dbuf.dsize = size;
- tdb_store(tdb, locking_key_fsp(fsp), dbuf, TDB_REPLACE);
+ if (tdb_store(tdb, locking_key_fsp(fsp), dbuf, TDB_REPLACE) == -1)
+ ret = False;
free(p);
- return True;
+ return ret;
}
-
/*******************************************************************
-a generic in-place modification call for share mode entries
+ A generic in-place modification call for share mode entries.
********************************************************************/
+
static BOOL mod_share_mode(files_struct *fsp,
void (*mod_fn)(share_mode_entry *, SMB_DEV_T, SMB_INO_T, void *),
void *param)
@@ -551,34 +610,39 @@ static BOOL mod_share_mode(files_struct *fsp,
TDB_DATA dbuf;
struct locking_data *data;
int i;
- share_mode_entry *shares;
- pid_t pid = sys_getpid();
- int need_store=0;
+ share_mode_entry *shares, entry;
+ BOOL need_store=False;
/* read in the existing share modes */
dbuf = tdb_fetch(tdb, locking_key_fsp(fsp));
- if (!dbuf.dptr) return False;
+ if (!dbuf.dptr)
+ return False;
data = (struct locking_data *)dbuf.dptr;
shares = (share_mode_entry *)(dbuf.dptr + sizeof(*data));
+ /*
+ * Fake up a share_mode_entry for comparisons.
+ */
+
+ fill_share_mode((char *)&entry, fsp, 0, 0);
+
/* find any with our pid and call the supplied function */
for (i=0;i<data->num_share_mode_entries;i++) {
- if (pid == shares[i].pid &&
- shares[i].share_mode == fsp->share_mode &&
- shares[i].time.tv_sec == fsp->open_time.tv_sec &&
- shares[i].time.tv_usec == fsp->open_time.tv_usec ) {
+ if (share_modes_identical(&entry, &shares[i])) {
mod_fn(&shares[i], fsp->dev, fsp->inode, param);
- need_store=1;
+ need_store=True;
}
}
/* if the mod fn was called then store it back */
if (need_store) {
if (data->num_share_mode_entries == 0) {
- tdb_delete(tdb, locking_key_fsp(fsp));
+ if (tdb_delete(tdb, locking_key_fsp(fsp)) == -1)
+ need_store = False;
} else {
- tdb_store(tdb, locking_key_fsp(fsp), dbuf, TDB_REPLACE);
+ if (tdb_store(tdb, locking_key_fsp(fsp), dbuf, TDB_REPLACE) == -1)
+ need_store = False;
}
}
@@ -586,11 +650,11 @@ static BOOL mod_share_mode(files_struct *fsp,
return need_store;
}
-
/*******************************************************************
Static function that actually does the work for the generic function
below.
********************************************************************/
+
static void remove_share_oplock_fn(share_mode_entry *entry, SMB_DEV_T dev, SMB_INO_T inode,
void *param)
{
@@ -604,6 +668,7 @@ static void remove_share_oplock_fn(share_mode_entry *entry, SMB_DEV_T dev, SMB_I
/*******************************************************************
Remove an oplock port and mode entry from a share mode.
********************************************************************/
+
BOOL remove_share_oplock(files_struct *fsp)
{
return mod_share_mode(fsp, remove_share_oplock_fn, NULL);
@@ -613,6 +678,7 @@ BOOL remove_share_oplock(files_struct *fsp)
Static function that actually does the work for the generic function
below.
********************************************************************/
+
static void downgrade_share_oplock_fn(share_mode_entry *entry, SMB_DEV_T dev, SMB_INO_T inode,
void *param)
{
@@ -624,6 +690,7 @@ static void downgrade_share_oplock_fn(share_mode_entry *entry, SMB_DEV_T dev, SM
/*******************************************************************
Downgrade a oplock type from exclusive to level II.
********************************************************************/
+
BOOL downgrade_share_oplock(files_struct *fsp)
{
return mod_share_mode(fsp, downgrade_share_oplock_fn, NULL);
@@ -643,7 +710,8 @@ BOOL modify_delete_flag( SMB_DEV_T dev, SMB_INO_T inode, BOOL delete_on_close)
/* read in the existing share modes */
dbuf = tdb_fetch(tdb, locking_key(dev, inode));
- if (!dbuf.dptr) return False;
+ if (!dbuf.dptr)
+ return False;
data = (struct locking_data *)dbuf.dptr;
shares = (share_mode_entry *)(dbuf.dptr + sizeof(*data));
@@ -657,19 +725,21 @@ BOOL modify_delete_flag( SMB_DEV_T dev, SMB_INO_T inode, BOOL delete_on_close)
/* store it back */
if (data->num_share_mode_entries) {
- if (tdb_store(tdb, locking_key(dev,inode), dbuf, TDB_REPLACE)==-1)
+ if (tdb_store(tdb, locking_key(dev,inode), dbuf, TDB_REPLACE)==-1) {
+ free(dbuf.dptr);
return False;
+ }
}
free(dbuf.dptr);
return True;
}
-
/****************************************************************************
-traverse the whole database with this function, calling traverse_callback
-on each share mode
+ Traverse the whole database with this function, calling traverse_callback
+ on each share mode
****************************************************************************/
+
static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
void* state)
{
@@ -694,8 +764,10 @@ static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf,
Call the specified function on each entry under management by the
share mode system.
********************************************************************/
+
int share_mode_forall(SHAREMODE_FN(fn))
{
- if (!tdb) return 0;
+ if (!tdb)
+ return 0;
return tdb_traverse(tdb, traverse_fn, (void*)fn);
}
diff --git a/source3/smbd/open.c b/source3/smbd/open.c
index a32e032921..143aa934a6 100644
--- a/source3/smbd/open.c
+++ b/source3/smbd/open.c
@@ -464,6 +464,7 @@ static int open_mode_check(connection_struct *conn, const char *fname, SMB_DEV_T
*/
do {
+ share_mode_entry broken_entry;
broke_oplock = False;
*p_all_current_opens_are_level_II = True;
@@ -506,6 +507,7 @@ dev = %x, inode = %.0f\n", old_shares[i].op_type, fname, (unsigned int)dev, (dou
}
broke_oplock = True;
+ broken_entry = *share_entry;
break;
} else if (!LEVEL_II_OPLOCK_TYPE(share_entry->op_type)) {
@@ -525,6 +527,14 @@ dev = %x, inode = %.0f\n", old_shares[i].op_type, fname, (unsigned int)dev, (dou
if(broke_oplock) {
free((char *)old_shares);
+ if (del_share_entry(dev, inode, &broken_entry, NULL) == -1) {
+ DEBUG(0,("open_mode_check: cannot delete entry when breaking oplock (%x) on file %s, \
+dev = %x, inode = %.0f\n", broken_entry.op_type, fname, (unsigned int)dev, (double)inode));
+ errno = EACCES;
+ unix_ERR_class = ERRDOS;
+ unix_ERR_code = ERRbadshare;
+ return -1;
+ }
num_share_modes = get_share_modes(conn, dev, inode, &old_shares);
oplock_contention_count++;
}
diff --git a/source3/smbd/oplock.c b/source3/smbd/oplock.c
index cc2581375e..4bc8fce698 100644
--- a/source3/smbd/oplock.c
+++ b/source3/smbd/oplock.c
@@ -903,6 +903,19 @@ should be %d\n", (int)pid, share_entry->op_port, global_oplock_port));
DEBUG(5,("request_oplock_break: breaking our own oplock\n"));
+#if 1 /* JRA PARANOIA TEST.... */
+ {
+ files_struct *fsp = file_find_dit(dev, inode, &share_entry->time);
+ if (!fsp) {
+ DEBUG(0,("request_oplock_break: PANIC : breaking our own oplock requested for \
+dev = %x, inode = %.0f, tv_sec = %x, tv_usec = %x and no fsp found !\n",
+ (unsigned int)dev, (double)inode, (int)share_entry->time.tv_sec,
+ (int)share_entry->time.tv_usec ));
+ smb_panic("request_oplock_break: no fsp found for our own oplock\n");
+ }
+ }
+#endif /* END JRA PARANOIA TEST... */
+
/* Call oplock break direct. */
return oplock_break(dev, inode, &share_entry->time, True);
}