X-Git-Url: http://git.samba.org/?p=samba.git;a=blobdiff_plain;f=source3%2Flocking%2Flocking.c;h=ce7ba8fa6637de8116de1b6aaaeed6f42de98f8d;hp=07377831b4adc7bc627646e296b40af4cda3f0d7;hb=3d15137653a7d1b593a9af2eef12f6e5b9a04c4f;hpb=e7d2e311a293709c74a97d0e30665dadae510712 diff --git a/source3/locking/locking.c b/source3/locking/locking.c index 07377831b4a..55412ec8b2a 100644 --- a/source3/locking/locking.c +++ b/source3/locking/locking.c @@ -2,22 +2,21 @@ Unix SMB/CIFS implementation. Locking functions Copyright (C) Andrew Tridgell 1992-2000 - Copyright (C) Jeremy Allison 1992-2000 + Copyright (C) Jeremy Allison 1992-2006 Copyright (C) Volker Lendecke 2005 - + 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 - the Free Software Foundation; either version 2 of the License, or + the Free Software Foundation; either version 3 of the License, or (at your option) any later version. - + This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - + You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + along with this program. If not, see . Revision History: @@ -33,244 +32,369 @@ rewrtten completely to use new tdb code. Tridge, Dec '99 Added POSIX locking support. Jeremy Allison (jeremy@valinux.com), Apr. 2000. + Added Unix Extensions POSIX locking support. Jeremy Allison Mar 2006. */ #include "includes.h" -uint16 global_smbpid; +#include "system/filesys.h" +#include "locking/proto.h" +#include "smbd/globals.h" +#include "dbwrap.h" +#include "../libcli/security/security.h" +#include "serverid.h" +#include "messages.h" +#include "util_tdb.h" #undef DBGC_CLASS #define DBGC_CLASS DBGC_LOCKING +#define NO_LOCKING_COUNT (-1) + /* the locking database handle */ -static TDB_CONTEXT *tdb; - -struct locking_data { - union { - struct { - int num_share_mode_entries; - BOOL delete_on_close; - } s; - struct share_mode_entry dummy; /* Needed for alignment. */ - } u; - /* the following two entries are implicit - struct share_mode_entry modes[num_share_mode_entries]; - char file_name[]; - */ -}; +static struct db_context *lock_db; /**************************************************************************** - Debugging aid :-). + Debugging aids :-). ****************************************************************************/ -static const char *lock_type_name(enum brl_type lock_type) +const char *lock_type_name(enum brl_type lock_type) +{ + switch (lock_type) { + case READ_LOCK: + return "READ"; + case WRITE_LOCK: + return "WRITE"; + case PENDING_READ_LOCK: + return "PENDING_READ"; + case PENDING_WRITE_LOCK: + return "PENDING_WRITE"; + default: + return "other"; + } +} + +const char *lock_flav_name(enum brl_flavour lock_flav) { - return (lock_type == READ_LOCK) ? "READ" : "WRITE"; + return (lock_flav == WINDOWS_LOCK) ? "WINDOWS_LOCK" : "POSIX_LOCK"; } /**************************************************************************** Utility function called to see if a file region is locked. + Called in the read/write codepath. ****************************************************************************/ -BOOL is_locked(files_struct *fsp,connection_struct *conn, - SMB_BIG_UINT count,SMB_BIG_UINT offset, - enum brl_type lock_type) +void init_strict_lock_struct(files_struct *fsp, + uint64_t smblctx, + br_off start, + br_off size, + enum brl_type lock_type, + struct lock_struct *plock) +{ + SMB_ASSERT(lock_type == READ_LOCK || lock_type == WRITE_LOCK); + + plock->context.smblctx = smblctx; + plock->context.tid = fsp->conn->cnum; + plock->context.pid = sconn_server_id(fsp->conn->sconn); + plock->start = start; + plock->size = size; + plock->fnum = fsp->fnum; + plock->lock_type = lock_type; + plock->lock_flav = lp_posix_cifsu_locktype(fsp); +} + +bool strict_lock_default(files_struct *fsp, struct lock_struct *plock) { - int snum = SNUM(conn); - int strict_locking = lp_strict_locking(snum); - BOOL ret; - - if (count == 0) - return(False); + int strict_locking = lp_strict_locking(fsp->conn->params); + bool ret = False; - if (!lp_locking(snum) || !strict_locking) - return(False); + if (plock->size == 0) { + return True; + } + + if (!lp_locking(fsp->conn->params) || !strict_locking) { + return True; + } if (strict_locking == Auto) { - if (EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type) && (lock_type == READ_LOCK || lock_type == WRITE_LOCK)) { - DEBUG(10,("is_locked: optimisation - exclusive oplock on file %s\n", fsp->fsp_name )); - ret = 0; + if (EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type) && (plock->lock_type == READ_LOCK || plock->lock_type == WRITE_LOCK)) { + DEBUG(10,("is_locked: optimisation - exclusive oplock on file %s\n", fsp_str_dbg(fsp))); + ret = True; } else if ((fsp->oplock_type == LEVEL_II_OPLOCK) && - (lock_type == READ_LOCK)) { - DEBUG(10,("is_locked: optimisation - level II oplock on file %s\n", fsp->fsp_name )); - ret = 0; + (plock->lock_type == READ_LOCK)) { + DEBUG(10,("is_locked: optimisation - level II oplock on file %s\n", fsp_str_dbg(fsp))); + ret = True; } else { - ret = !brl_locktest(fsp->dev, fsp->inode, fsp->fnum, - global_smbpid, procid_self(), conn->cnum, - offset, count, lock_type); + struct byte_range_lock *br_lck; + + br_lck = brl_get_locks_readonly(fsp); + if (!br_lck) { + return True; + } + ret = brl_locktest(br_lck, + plock->context.smblctx, + plock->context.pid, + plock->start, + plock->size, + plock->lock_type, + plock->lock_flav); } } else { - ret = !brl_locktest(fsp->dev, fsp->inode, fsp->fnum, - global_smbpid, procid_self(), conn->cnum, - offset, count, lock_type); - } - - DEBUG(10,("is_locked: brl start=%.0f len=%.0f %s for file %s\n", - (double)offset, (double)count, ret ? "locked" : "unlocked", - fsp->fsp_name )); - - /* - * There is no lock held by an SMB daemon, check to - * see if there is a POSIX lock from a UNIX or NFS process. - */ + struct byte_range_lock *br_lck; - if(!ret && lp_posix_locking(snum)) { - ret = is_posix_locked(fsp, offset, count, lock_type); - - DEBUG(10,("is_locked: posix start=%.0f len=%.0f %s for file %s\n", - (double)offset, (double)count, ret ? "locked" : "unlocked", - fsp->fsp_name )); + br_lck = brl_get_locks_readonly(fsp); + if (!br_lck) { + return True; + } + ret = brl_locktest(br_lck, + plock->context.smblctx, + plock->context.pid, + plock->start, + plock->size, + plock->lock_type, + plock->lock_flav); } + DEBUG(10,("strict_lock_default: flavour = %s brl start=%.0f " + "len=%.0f %s for fnum %d file %s\n", + lock_flav_name(plock->lock_flav), + (double)plock->start, (double)plock->size, + ret ? "unlocked" : "locked", + plock->fnum, fsp_str_dbg(fsp))); + return ret; } +void strict_unlock_default(files_struct *fsp, struct lock_struct *plock) +{ +} + /**************************************************************************** - Utility function called by locking requests. + Find out if a lock could be granted - return who is blocking us if we can't. ****************************************************************************/ -static NTSTATUS do_lock(files_struct *fsp,connection_struct *conn, uint16 lock_pid, - SMB_BIG_UINT count,SMB_BIG_UINT offset,enum brl_type lock_type, BOOL *my_lock_ctx) +NTSTATUS query_lock(files_struct *fsp, + uint64_t *psmblctx, + uint64_t *pcount, + uint64_t *poffset, + enum brl_type *plock_type, + enum brl_flavour lock_flav) { - NTSTATUS status = NT_STATUS_LOCK_NOT_GRANTED; + struct byte_range_lock *br_lck = NULL; + + if (!fsp->can_lock) { + return fsp->is_directory ? NT_STATUS_INVALID_DEVICE_REQUEST : NT_STATUS_INVALID_HANDLE; + } - if (!lp_locking(SNUM(conn))) + if (!lp_locking(fsp->conn->params)) { return NT_STATUS_OK; + } - /* NOTE! 0 byte long ranges ARE allowed and should be stored */ + br_lck = brl_get_locks_readonly(fsp); + if (!br_lck) { + return NT_STATUS_NO_MEMORY; + } - DEBUG(10,("do_lock: lock type %s start=%.0f len=%.0f requested for file %s\n", - lock_type_name(lock_type), (double)offset, (double)count, fsp->fsp_name )); - - if (OPEN_FSP(fsp) && fsp->can_lock && (fsp->conn == conn)) { - status = brl_lock(fsp->dev, fsp->inode, fsp->fnum, - lock_pid, procid_self(), conn->cnum, - offset, count, - lock_type, my_lock_ctx); - - if (NT_STATUS_IS_OK(status) && lp_posix_locking(SNUM(conn))) { - - /* - * Try and get a POSIX lock on this range. - * Note that this is ok if it is a read lock - * overlapping on a different fd. JRA. - */ - - if (!set_posix_lock(fsp, offset, count, lock_type)) { - if (errno == EACCES || errno == EAGAIN) - status = NT_STATUS_FILE_LOCK_CONFLICT; - else - status = map_nt_error_from_unix(errno); - - /* - * We failed to map - we must now remove the brl - * lock entry. - */ - (void)brl_unlock(fsp->dev, fsp->inode, fsp->fnum, - lock_pid, procid_self(), conn->cnum, - offset, count, False, - NULL, NULL); - } - } + return brl_lockquery(br_lck, + psmblctx, + sconn_server_id(fsp->conn->sconn), + poffset, + pcount, + plock_type, + lock_flav); +} + +static void increment_current_lock_count(files_struct *fsp, + enum brl_flavour lock_flav) +{ + if (lock_flav == WINDOWS_LOCK && + fsp->current_lock_count != NO_LOCKING_COUNT) { + /* blocking ie. pending, locks also count here, + * as this is an efficiency counter to avoid checking + * the lock db. on close. JRA. */ + + fsp->current_lock_count++; + } else { + /* Notice that this has had a POSIX lock request. + * We can't count locks after this so forget them. + */ + fsp->current_lock_count = NO_LOCKING_COUNT; } +} - return status; +static void decrement_current_lock_count(files_struct *fsp, + enum brl_flavour lock_flav) +{ + if (lock_flav == WINDOWS_LOCK && + fsp->current_lock_count != NO_LOCKING_COUNT) { + SMB_ASSERT(fsp->current_lock_count > 0); + fsp->current_lock_count--; + } } /**************************************************************************** - Utility function called by locking requests. This is *DISGUSTING*. It also - appears to be "What Windows Does" (tm). Andrew, ever wonder why Windows 2000 - is so slow on the locking tests...... ? This is the reason. Much though I hate - it, we need this. JRA. + Utility function called by locking requests. ****************************************************************************/ -NTSTATUS do_lock_spin(files_struct *fsp,connection_struct *conn, uint16 lock_pid, - SMB_BIG_UINT count,SMB_BIG_UINT offset,enum brl_type lock_type, BOOL *my_lock_ctx) +struct byte_range_lock *do_lock(struct messaging_context *msg_ctx, + files_struct *fsp, + uint64_t smblctx, + uint64_t count, + uint64_t offset, + enum brl_type lock_type, + enum brl_flavour lock_flav, + bool blocking_lock, + NTSTATUS *perr, + uint64_t *psmblctx, + struct blocking_lock_record *blr) { - int j, maxj = lp_lock_spin_count(); - int sleeptime = lp_lock_sleep_time(); - NTSTATUS status, ret; + struct byte_range_lock *br_lck = NULL; - if (maxj <= 0) - maxj = 1; + /* silently return ok on print files as we don't do locking there */ + if (fsp->print_file) { + *perr = NT_STATUS_OK; + return NULL; + } - ret = NT_STATUS_OK; /* to keep dumb compilers happy */ + if (!fsp->can_lock) { + *perr = fsp->is_directory ? NT_STATUS_INVALID_DEVICE_REQUEST : NT_STATUS_INVALID_HANDLE; + return NULL; + } - for (j = 0; j < maxj; j++) { - status = do_lock(fsp, conn, lock_pid, count, offset, lock_type, my_lock_ctx); - if (!NT_STATUS_EQUAL(status, NT_STATUS_LOCK_NOT_GRANTED) && - !NT_STATUS_EQUAL(status, NT_STATUS_FILE_LOCK_CONFLICT)) { - return status; - } - /* if we do fail then return the first error code we got */ - if (j == 0) { - ret = status; - /* Don't spin if we blocked ourselves. */ - if (*my_lock_ctx) - return ret; - } - if (sleeptime) - sys_usleep(sleeptime); + if (!lp_locking(fsp->conn->params)) { + *perr = NT_STATUS_OK; + return NULL; } - return ret; -} -/* Struct passed to brl_unlock. */ -struct posix_unlock_data_struct { - files_struct *fsp; - SMB_BIG_UINT offset; - SMB_BIG_UINT count; -}; + /* NOTE! 0 byte long ranges ARE allowed and should be stored */ -/**************************************************************************** - Function passed to brl_unlock to allow POSIX unlock to be done first. -****************************************************************************/ + DEBUG(10,("do_lock: lock flavour %s lock type %s start=%.0f len=%.0f " + "blocking_lock=%s requested for fnum %d file %s\n", + lock_flav_name(lock_flav), lock_type_name(lock_type), + (double)offset, (double)count, blocking_lock ? "true" : + "false", fsp->fnum, fsp_str_dbg(fsp))); -static void posix_unlock(void *pre_data) -{ - struct posix_unlock_data_struct *pdata = (struct posix_unlock_data_struct *)pre_data; + br_lck = brl_get_locks(talloc_tos(), fsp); + if (!br_lck) { + *perr = NT_STATUS_NO_MEMORY; + return NULL; + } - if (lp_posix_locking(SNUM(pdata->fsp->conn))) - release_posix_lock(pdata->fsp, pdata->offset, pdata->count); + *perr = brl_lock(msg_ctx, + br_lck, + smblctx, + sconn_server_id(fsp->conn->sconn), + offset, + count, + lock_type, + lock_flav, + blocking_lock, + psmblctx, + blr); + + DEBUG(10, ("do_lock: returning status=%s\n", nt_errstr(*perr))); + + increment_current_lock_count(fsp, lock_flav); + return br_lck; } /**************************************************************************** Utility function called by unlocking requests. ****************************************************************************/ -NTSTATUS do_unlock(files_struct *fsp,connection_struct *conn, uint16 lock_pid, - SMB_BIG_UINT count,SMB_BIG_UINT offset) +NTSTATUS do_unlock(struct messaging_context *msg_ctx, + files_struct *fsp, + uint64_t smblctx, + uint64_t count, + uint64_t offset, + enum brl_flavour lock_flav) { - BOOL ok = False; - struct posix_unlock_data_struct posix_data; - - if (!lp_locking(SNUM(conn))) + bool ok = False; + struct byte_range_lock *br_lck = NULL; + + if (!fsp->can_lock) { + return fsp->is_directory ? NT_STATUS_INVALID_DEVICE_REQUEST : NT_STATUS_INVALID_HANDLE; + } + + if (!lp_locking(fsp->conn->params)) { return NT_STATUS_OK; - - if (!OPEN_FSP(fsp) || !fsp->can_lock || (fsp->conn != conn)) { - return NT_STATUS_INVALID_HANDLE; } - - DEBUG(10,("do_unlock: unlock start=%.0f len=%.0f requested for file %s\n", - (double)offset, (double)count, fsp->fsp_name )); - /* - * Remove the existing lock record from the tdb lockdb - * before looking at POSIX locks. If this record doesn't - * match then don't bother looking to remove POSIX locks. - */ + DEBUG(10,("do_unlock: unlock start=%.0f len=%.0f requested for fnum %d file %s\n", + (double)offset, (double)count, fsp->fnum, + fsp_str_dbg(fsp))); + + br_lck = brl_get_locks(talloc_tos(), fsp); + if (!br_lck) { + return NT_STATUS_NO_MEMORY; + } + + ok = brl_unlock(msg_ctx, + br_lck, + smblctx, + sconn_server_id(fsp->conn->sconn), + offset, + count, + lock_flav); - posix_data.fsp = fsp; - posix_data.offset = offset; - posix_data.count = count; + TALLOC_FREE(br_lck); - ok = brl_unlock(fsp->dev, fsp->inode, fsp->fnum, - lock_pid, procid_self(), conn->cnum, offset, count, - False, posix_unlock, (void *)&posix_data); - if (!ok) { DEBUG(10,("do_unlock: returning ERRlock.\n" )); return NT_STATUS_RANGE_NOT_LOCKED; } + + decrement_current_lock_count(fsp, lock_flav); + return NT_STATUS_OK; +} + +/**************************************************************************** + Cancel any pending blocked locks. +****************************************************************************/ + +NTSTATUS do_lock_cancel(files_struct *fsp, + uint64 smblctx, + uint64_t count, + uint64_t offset, + enum brl_flavour lock_flav, + struct blocking_lock_record *blr) +{ + bool ok = False; + struct byte_range_lock *br_lck = NULL; + + if (!fsp->can_lock) { + return fsp->is_directory ? + NT_STATUS_INVALID_DEVICE_REQUEST : NT_STATUS_INVALID_HANDLE; + } + + if (!lp_locking(fsp->conn->params)) { + return NT_STATUS_DOS(ERRDOS, ERRcancelviolation); + } + + DEBUG(10,("do_lock_cancel: cancel start=%.0f len=%.0f requested for fnum %d file %s\n", + (double)offset, (double)count, fsp->fnum, + fsp_str_dbg(fsp))); + + br_lck = brl_get_locks(talloc_tos(), fsp); + if (!br_lck) { + return NT_STATUS_NO_MEMORY; + } + + ok = brl_lock_cancel(br_lck, + smblctx, + sconn_server_id(fsp->conn->sconn), + offset, + count, + lock_flav, + blr); + + TALLOC_FREE(br_lck); + + if (!ok) { + DEBUG(10,("do_lock_cancel: returning ERRcancelviolation.\n" )); + return NT_STATUS_DOS(ERRDOS, ERRcancelviolation); + } + + decrement_current_lock_count(fsp, lock_flav); return NT_STATUS_OK; } @@ -278,26 +402,30 @@ NTSTATUS do_unlock(files_struct *fsp,connection_struct *conn, uint16 lock_pid, Remove any locks on this fd. Called from file_close(). ****************************************************************************/ -void locking_close_file(files_struct *fsp) +void locking_close_file(struct messaging_context *msg_ctx, + files_struct *fsp, + enum file_close_type close_type) { - struct process_id pid = procid_self(); + struct byte_range_lock *br_lck; - if (!lp_locking(SNUM(fsp->conn))) + if (!lp_locking(fsp->conn->params)) { return; + } - /* - * Just release all the brl locks, no need to release individually. + /* If we have not outstanding locks or pending + * locks then we don't need to look in the lock db. */ - brl_close(fsp->dev, fsp->inode, pid, fsp->conn->cnum, fsp->fnum); - - if(lp_posix_locking(SNUM(fsp->conn))) { + if (fsp->current_lock_count == 0) { + return; + } - /* - * Release all the POSIX locks. - */ - posix_locking_close_file(fsp); + br_lck = brl_get_locks(talloc_tos(),fsp); + if (br_lck) { + cancel_pending_lock_requests_by_fid(fsp, br_lck, close_type); + brl_close_fnum(msg_ctx, br_lck); + TALLOC_FREE(br_lck); } } @@ -305,21 +433,19 @@ void locking_close_file(files_struct *fsp) Initialise the locking functions. ****************************************************************************/ -static int open_read_only; - -BOOL locking_init(int read_only) +static bool locking_init_internal(bool read_only) { brl_init(read_only); - if (tdb) + if (lock_db) return True; - tdb = tdb_open_log(lock_path("locking.tdb"), - SMB_OPEN_DATABASE_TDB_HASH_SIZE, TDB_DEFAULT|(read_only?0x0:TDB_CLEAR_IF_FIRST), - read_only?O_RDONLY:O_RDWR|O_CREAT, - 0644); + lock_db = db_open(NULL, lock_path("locking.tdb"), + lp_open_files_db_hash_size(), + TDB_DEFAULT|TDB_VOLATILE|TDB_CLEAR_IF_FIRST|TDB_INCOMPATIBLE_HASH, + read_only?O_RDONLY:O_RDWR|O_CREAT, 0644); - if (!tdb) { + if (!lock_db) { DEBUG(0,("ERROR: Failed to initialise locking database\n")); return False; } @@ -327,73 +453,59 @@ BOOL locking_init(int read_only) if (!posix_locking_init(read_only)) return False; - open_read_only = read_only; - return True; } -/******************************************************************* - Deinitialize the share_mode management. -******************************************************************/ - -BOOL locking_end(void) +bool locking_init(void) { - BOOL ret = True; - - brl_shutdown(open_read_only); - if (tdb) { - if (tdb_close(tdb) != 0) - ret = False; - } + return locking_init_internal(false); +} - return ret; +bool locking_init_readonly(void) +{ + return locking_init_internal(true); } /******************************************************************* - Form a static locking key for a dev/inode pair. + Deinitialize the share_mode management. ******************************************************************/ -/* key and data records in the tdb locking database */ -struct locking_key { - SMB_DEV_T dev; - SMB_INO_T ino; -}; +bool locking_end(void) +{ + brl_shutdown(); + TALLOC_FREE(lock_db); + return true; +} /******************************************************************* Form a static locking key for a dev/inode pair. ******************************************************************/ -static TDB_DATA locking_key(SMB_DEV_T dev, SMB_INO_T inode) +static TDB_DATA locking_key(const struct file_id *id, struct file_id *tmp) { - static struct locking_key key; - TDB_DATA kbuf; - - memset(&key, '\0', sizeof(key)); - key.dev = dev; - key.ino = inode; - kbuf.dptr = (char *)&key; - kbuf.dsize = sizeof(key); - return kbuf; + *tmp = *id; + return make_tdb_data((const uint8_t *)tmp, sizeof(*tmp)); } /******************************************************************* Print out a share mode. ********************************************************************/ -char *share_mode_str(int num, struct share_mode_entry *e) +char *share_mode_str(TALLOC_CTX *ctx, int num, const struct share_mode_entry *e) { - static pstring share_str; - - slprintf(share_str, sizeof(share_str)-1, "share_mode_entry[%d]: " + return talloc_asprintf(ctx, "share_mode_entry[%d]: %s " "pid = %s, share_access = 0x%x, private_options = 0x%x, " - "access_mask = 0x%x, mid = 0x%x, type= 0x%x, file_id = %lu, " - "dev = 0x%x, inode = %.0f", - num, procid_str_static(&e->pid), + "access_mask = 0x%x, mid = 0x%llx, type= 0x%x, gen_id = %lu, " + "uid = %u, flags = %u, file_id %s, name_hash = 0x%x", + num, + e->op_type == UNUSED_SHARE_MODE_ENTRY ? "UNUSED" : "", + procid_str_static(&e->pid), e->share_access, e->private_options, - e->access_mask, e->op_mid, e->op_type, e->share_file_id, - (unsigned int)e->dev, (double)e->inode ); - - return share_str; + e->access_mask, (unsigned long long)e->op_mid, + e->op_type, e->share_file_id, + (unsigned int)e->uid, (unsigned int)e->flags, + file_id_string_tos(&e->id), + (unsigned int)e->name_hash); } /******************************************************************* @@ -408,39 +520,155 @@ static void print_share_mode_table(struct locking_data *data) int i; for (i = 0; i < num_share_modes; i++) { - struct share_mode_entry *entry_p = &shares[i]; - DEBUG(10,("print_share_mode_table: %s\n", - share_mode_str(i, entry_p))); + struct share_mode_entry entry; + char *str; + + /* + * We need to memcpy the entry here due to alignment + * restrictions that are not met when directly accessing + * shares[i] + */ + + memcpy(&entry, &shares[i], sizeof(struct share_mode_entry)); + str = share_mode_str(talloc_tos(), i, &entry); + + DEBUG(10,("print_share_mode_table: %s\n", str ? str : "")); + TALLOC_FREE(str); + } +} + +static int parse_delete_tokens_list(struct share_mode_lock *lck, + struct locking_data *pdata, + const TDB_DATA dbuf) +{ + uint8_t *p = dbuf.dptr + sizeof(struct locking_data) + + (lck->num_share_modes * + sizeof(struct share_mode_entry)); + uint8_t *end_ptr = dbuf.dptr + (dbuf.dsize - 2); + int delete_tokens_size = 0; + int i; + + lck->delete_tokens = NULL; + + for (i = 0; i < pdata->u.s.num_delete_token_entries; i++) { + uint32_t token_len; + struct delete_token_list *pdtl; + + if (end_ptr - p < (sizeof(uint32_t) + sizeof(uint32_t) + + sizeof(uid_t) + sizeof(gid_t))) { + DEBUG(0,("parse_delete_tokens_list: " + "corrupt token list (%u)", + (unsigned int)(end_ptr - p))); + smb_panic("corrupt token list"); + return -1; + } + + memcpy(&token_len, p, sizeof(token_len)); + delete_tokens_size += token_len; + + if (p + token_len > end_ptr || token_len < sizeof(token_len) + + sizeof(pdtl->name_hash) + + sizeof(uid_t) + + sizeof(gid_t)) { + DEBUG(0,("parse_delete_tokens_list: " + "invalid token length (%u)\n", + (unsigned int)token_len )); + smb_panic("invalid token length"); + return -1; + } + + p += sizeof(token_len); + + pdtl = TALLOC_ZERO_P(lck, struct delete_token_list); + if (pdtl == NULL) { + DEBUG(0,("parse_delete_tokens_list: talloc failed")); + return -1; + } + /* Copy out the name_hash. */ + memcpy(&pdtl->name_hash, p, sizeof(pdtl->name_hash)); + p += sizeof(pdtl->name_hash); + + pdtl->delete_token = TALLOC_ZERO_P(pdtl, struct security_unix_token); + if (pdtl->delete_token == NULL) { + DEBUG(0,("parse_delete_tokens_list: talloc failed")); + return -1; + } + + /* Copy out the uid and gid. */ + memcpy(&pdtl->delete_token->uid, p, sizeof(uid_t)); + p += sizeof(uid_t); + memcpy(&pdtl->delete_token->gid, p, sizeof(gid_t)); + p += sizeof(gid_t); + + token_len -= (sizeof(token_len) + sizeof(pdtl->name_hash) + + sizeof(uid_t) + sizeof(gid_t)); + + /* Any supplementary groups ? */ + if (token_len) { + int j; + + if (token_len % sizeof(gid_t) != 0) { + DEBUG(0,("parse_delete_tokens_list: " + "corrupt group list (%u)", + (unsigned int)(token_len % sizeof(gid_t)) )); + smb_panic("corrupt group list"); + return -1; + } + + pdtl->delete_token->ngroups = token_len / sizeof(gid_t); + pdtl->delete_token->groups = talloc_array(pdtl->delete_token, gid_t, + pdtl->delete_token->ngroups); + if (pdtl->delete_token->groups == NULL) { + DEBUG(0,("parse_delete_tokens_list: talloc failed")); + return -1; + } + + for (j = 0; j < pdtl->delete_token->ngroups; j++) { + memcpy(&pdtl->delete_token->groups[j], p, sizeof(gid_t)); + p += sizeof(gid_t); + } + } + /* Add to the list. */ + DLIST_ADD(lck->delete_tokens, pdtl); } + + return delete_tokens_size; } /******************************************************************* Get all share mode entries for a dev/inode pair. ********************************************************************/ -static BOOL parse_share_modes(TDB_DATA dbuf, struct share_mode_lock *lck) +static bool parse_share_modes(const TDB_DATA dbuf, struct share_mode_lock *lck) { - struct locking_data *data; + struct locking_data data; + int delete_tokens_size; int i; if (dbuf.dsize < sizeof(struct locking_data)) { - DEBUG(0, ("parse_share_modes: buffer too short\n")); - return False; + smb_panic("parse_share_modes: buffer too short"); } - data = (struct locking_data *)dbuf.dptr; + memcpy(&data, dbuf.dptr, sizeof(data)); - lck->delete_on_close = data->u.s.delete_on_close; - lck->num_share_modes = data->u.s.num_share_mode_entries; + lck->old_write_time = data.u.s.old_write_time; + lck->changed_write_time = data.u.s.changed_write_time; + lck->num_share_modes = data.u.s.num_share_mode_entries; - DEBUG(10, ("parse_share_modes: delete_on_close: %d, " - "num_share_modes: %d\n", lck->delete_on_close, + DEBUG(10, ("parse_share_modes: owrt: %s, " + "cwrt: %s, ntok: %u, num_share_modes: %d\n", + timestring(talloc_tos(), + convert_timespec_to_time_t(lck->old_write_time)), + timestring(talloc_tos(), + convert_timespec_to_time_t( + lck->changed_write_time)), + (unsigned int)data.u.s.num_delete_token_entries, lck->num_share_modes)); if ((lck->num_share_modes < 0) || (lck->num_share_modes > 1000000)) { DEBUG(0, ("invalid number of share modes: %d\n", lck->num_share_modes)); - return False; + smb_panic("parse_share_modes: invalid number of share modes"); } lck->share_modes = NULL; @@ -450,29 +678,41 @@ static BOOL parse_share_modes(TDB_DATA dbuf, struct share_mode_lock *lck) if (dbuf.dsize < (sizeof(struct locking_data) + (lck->num_share_modes * sizeof(struct share_mode_entry)))) { - DEBUG(0, ("parse_share_modes: buffer too short\n")); - return False; + smb_panic("parse_share_modes: buffer too short"); } - - lck->share_modes = talloc_memdup(lck, dbuf.dptr+sizeof(*data), - lck->num_share_modes * - sizeof(struct share_mode_entry)); + + lck->share_modes = (struct share_mode_entry *) + TALLOC_MEMDUP(lck, + dbuf.dptr+sizeof(struct locking_data), + lck->num_share_modes * + sizeof(struct share_mode_entry)); if (lck->share_modes == NULL) { - DEBUG(0, ("talloc failed\n")); - return False; + smb_panic("parse_share_modes: talloc failed"); } } + /* Get any delete tokens. */ + delete_tokens_size = parse_delete_tokens_list(lck, &data, dbuf); + if (delete_tokens_size < 0) { + smb_panic("parse_share_modes: parse_delete_tokens_list failed"); + } + /* Save off the associated service path and filename. */ - lck->servicepath = talloc_strdup(lck, dbuf.dptr + sizeof(*data) + - (lck->num_share_modes * - sizeof(struct share_mode_entry))); + lck->servicepath = (const char *)dbuf.dptr + sizeof(struct locking_data) + + (lck->num_share_modes * sizeof(struct share_mode_entry)) + + delete_tokens_size; - lck->filename = talloc_strdup(lck, dbuf.dptr + sizeof(*data) + - (lck->num_share_modes * - sizeof(struct share_mode_entry)) + - strlen(lck->servicepath) + 1 ); + lck->base_name = (const char *)dbuf.dptr + sizeof(struct locking_data) + + (lck->num_share_modes * sizeof(struct share_mode_entry)) + + delete_tokens_size + + strlen(lck->servicepath) + 1; + + lck->stream_name = (const char *)dbuf.dptr + sizeof(struct locking_data) + + (lck->num_share_modes * sizeof(struct share_mode_entry)) + + delete_tokens_size + + strlen(lck->servicepath) + 1 + + strlen(lck->base_name) + 1; /* * Ensure that each entry has a real process attached. @@ -480,27 +720,35 @@ static BOOL parse_share_modes(TDB_DATA dbuf, struct share_mode_lock *lck) for (i = 0; i < lck->num_share_modes; i++) { struct share_mode_entry *entry_p = &lck->share_modes[i]; + char *str = NULL; + if (DEBUGLEVEL >= 10) { + str = share_mode_str(NULL, i, entry_p); + } DEBUG(10,("parse_share_modes: %s\n", - share_mode_str(i, entry_p) )); - if (!process_exists(entry_p->pid)) { + str ? str : "")); + if (!serverid_exists(&entry_p->pid)) { DEBUG(10,("parse_share_modes: deleted %s\n", - share_mode_str(i, entry_p) )); + str ? str : "")); entry_p->op_type = UNUSED_SHARE_MODE_ENTRY; lck->modified = True; } + TALLOC_FREE(str); } return True; } -static TDB_DATA unparse_share_modes(struct share_mode_lock *lck) +static TDB_DATA unparse_share_modes(const struct share_mode_lock *lck) { TDB_DATA result; int num_valid = 0; int i; struct locking_data *data; ssize_t offset; - ssize_t sp_len; + ssize_t sp_len, bn_len, sn_len; + uint32_t delete_tokens_size = 0; + struct delete_token_list *pdtl = NULL; + uint32_t num_delete_token_entries = 0; result.dptr = NULL; result.dsize = 0; @@ -516,46 +764,107 @@ static TDB_DATA unparse_share_modes(struct share_mode_lock *lck) } sp_len = strlen(lck->servicepath); + bn_len = strlen(lck->base_name); + sn_len = lck->stream_name != NULL ? strlen(lck->stream_name) : 0; + + for (pdtl = lck->delete_tokens; pdtl; pdtl = pdtl->next) { + num_delete_token_entries++; + delete_tokens_size += (sizeof(uint32_t) + + sizeof(uint32_t) + + sizeof(uid_t) + + sizeof(gid_t) + + pdtl->delete_token->ngroups*sizeof(gid_t)); + } result.dsize = sizeof(*data) + lck->num_share_modes * sizeof(struct share_mode_entry) + + delete_tokens_size + sp_len + 1 + - strlen(lck->filename) + 1; - result.dptr = talloc_size(lck, result.dsize); + bn_len + 1 + + sn_len + 1; + result.dptr = talloc_array(lck, uint8, result.dsize); if (result.dptr == NULL) { - smb_panic("talloc failed\n"); + smb_panic("talloc failed"); } data = (struct locking_data *)result.dptr; ZERO_STRUCTP(data); data->u.s.num_share_mode_entries = lck->num_share_modes; - data->u.s.delete_on_close = lck->delete_on_close; - DEBUG(10, ("unparse_share_modes: del: %d, num: %d\n", - data->u.s.delete_on_close, - data->u.s.num_share_mode_entries)); + data->u.s.old_write_time = lck->old_write_time; + data->u.s.changed_write_time = lck->changed_write_time; + data->u.s.num_delete_token_entries = num_delete_token_entries; + + DEBUG(10,("unparse_share_modes: owrt: %s cwrt: %s, ntok: %u, " + "num: %d\n", + timestring(talloc_tos(), + convert_timespec_to_time_t(lck->old_write_time)), + timestring(talloc_tos(), + convert_timespec_to_time_t( + lck->changed_write_time)), + (unsigned int)data->u.s.num_delete_token_entries, + data->u.s.num_share_mode_entries)); + memcpy(result.dptr + sizeof(*data), lck->share_modes, sizeof(struct share_mode_entry)*lck->num_share_modes); offset = sizeof(*data) + sizeof(struct share_mode_entry)*lck->num_share_modes; - safe_strcpy(result.dptr + offset, lck->servicepath, - result.dsize - offset - 1); + + /* Store any delete on close tokens. */ + for (pdtl = lck->delete_tokens; pdtl; pdtl = pdtl->next) { + struct security_unix_token *pdt = pdtl->delete_token; + uint32_t token_size = sizeof(uint32_t) + + sizeof(uint32_t) + + sizeof(uid_t) + + sizeof(gid_t) + + (pdt->ngroups * sizeof(gid_t)); + uint8_t *p = result.dptr + offset; + + memcpy(p, &token_size, sizeof(uint32_t)); + p += sizeof(uint32_t); + + memcpy(p, &pdtl->name_hash, sizeof(uint32_t)); + p += sizeof(uint32_t); + + memcpy(p, &pdt->uid, sizeof(uid_t)); + p += sizeof(uid_t); + + memcpy(p, &pdt->gid, sizeof(gid_t)); + p += sizeof(gid_t); + + for (i = 0; i < pdt->ngroups; i++) { + memcpy(p, &pdt->groups[i], sizeof(gid_t)); + p += sizeof(gid_t); + } + offset += token_size; + } + + strlcpy((char *)result.dptr + offset, + lck->servicepath ? lck->servicepath : "", + result.dsize - offset); offset += sp_len + 1; - safe_strcpy(result.dptr + offset, lck->filename, - result.dsize - offset - 1); - print_share_mode_table(data); + strlcpy((char *)result.dptr + offset, + lck->base_name ? lck->base_name : "", + result.dsize - offset); + offset += bn_len + 1; + strlcpy((char *)result.dptr + offset, + lck->stream_name ? lck->stream_name : "", + result.dsize - offset); + + if (DEBUGLEVEL >= 10) { + print_share_mode_table(data); + } + return result; } -static int share_mode_lock_destructor(void *p) +static int share_mode_lock_destructor(struct share_mode_lock *lck) { - struct share_mode_lock *lck = - talloc_get_type_abort(p, struct share_mode_lock); - TDB_DATA key = locking_key(lck->dev, lck->ino); + NTSTATUS status; TDB_DATA data; if (!lck->modified) { - goto done; + return 0; } data = unparse_share_modes(lck); @@ -563,90 +872,155 @@ static int share_mode_lock_destructor(void *p) if (data.dptr == NULL) { if (!lck->fresh) { /* There has been an entry before, delete it */ - if (tdb_delete(tdb, key) == -1) { - smb_panic("Could not delete share entry\n"); + + status = lck->record->delete_rec(lck->record); + if (!NT_STATUS_IS_OK(status)) { + char *errmsg; + + DEBUG(0, ("delete_rec returned %s\n", + nt_errstr(status))); + + if (asprintf(&errmsg, "could not delete share " + "entry: %s\n", + nt_errstr(status)) == -1) { + smb_panic("could not delete share" + "entry"); + } + smb_panic(errmsg); } } goto done; } - if (tdb_store(tdb, key, data, TDB_REPLACE) == -1) { - smb_panic("Could not store share mode entry\n"); + status = lck->record->store(lck->record, data, TDB_REPLACE); + if (!NT_STATUS_IS_OK(status)) { + char *errmsg; + + DEBUG(0, ("store returned %s\n", nt_errstr(status))); + + if (asprintf(&errmsg, "could not store share mode entry: %s", + nt_errstr(status)) == -1) { + smb_panic("could not store share mode entry"); + } + smb_panic(errmsg); } done: - tdb_chainunlock(tdb, key); return 0; } -struct share_mode_lock *get_share_mode_lock(TALLOC_CTX *mem_ctx, - SMB_DEV_T dev, SMB_INO_T ino, - const char *servicepath, - const char *fname) +static bool fill_share_mode_lock(struct share_mode_lock *lck, + struct file_id id, + const char *servicepath, + const struct smb_filename *smb_fname, + TDB_DATA share_mode_data, + const struct timespec *old_write_time) { - struct share_mode_lock *lck; - TDB_DATA key = locking_key(dev, ino); - TDB_DATA data; - - lck = TALLOC_P(mem_ctx, struct share_mode_lock); - if (lck == NULL) { - DEBUG(0, ("talloc failed\n")); - return NULL; - } - /* Ensure we set every field here as the destructor must be valid even if parse_share_modes fails. */ lck->servicepath = NULL; - lck->filename = NULL; - lck->dev = dev; - lck->ino = ino; + lck->base_name = NULL; + lck->stream_name = NULL; + lck->id = id; lck->num_share_modes = 0; lck->share_modes = NULL; - lck->delete_on_close = False; + lck->delete_tokens = NULL; + ZERO_STRUCT(lck->old_write_time); + ZERO_STRUCT(lck->changed_write_time); lck->fresh = False; lck->modified = False; - if (tdb_chainlock(tdb, key) != 0) { - DEBUG(3, ("Could not lock share entry\n")); - talloc_free(lck); - return NULL; - } - - /* We must set the destructor immediately after the chainlock - ensure the lock is cleaned up on any of the error return - paths below. */ - - talloc_set_destructor(lck, share_mode_lock_destructor); - - data = tdb_fetch(tdb, key); - lck->fresh = (data.dptr == NULL); + lck->fresh = (share_mode_data.dptr == NULL); if (lck->fresh) { - - if (fname == NULL || servicepath == NULL) { - DEBUG(0, ("New file, but no filename or servicepath supplied\n")); - talloc_free(lck); - return NULL; + bool has_stream; + if (smb_fname == NULL || servicepath == NULL + || old_write_time == NULL) { + return False; } - lck->filename = talloc_strdup(lck, fname); + + has_stream = smb_fname->stream_name != NULL; + + lck->base_name = talloc_strdup(lck, smb_fname->base_name); + lck->stream_name = talloc_strdup(lck, smb_fname->stream_name); lck->servicepath = talloc_strdup(lck, servicepath); - if (lck->filename == NULL || lck->servicepath == NULL) { + if (lck->base_name == NULL || + (has_stream && lck->stream_name == NULL) || + lck->servicepath == NULL) { DEBUG(0, ("talloc failed\n")); - talloc_free(lck); - return NULL; + return False; } + lck->old_write_time = *old_write_time; } else { - if (!parse_share_modes(data, lck)) { + if (!parse_share_modes(share_mode_data, lck)) { DEBUG(0, ("Could not parse share modes\n")); - talloc_free(lck); - SAFE_FREE(data.dptr); - return NULL; + return False; } } - SAFE_FREE(data.dptr); + return True; +} + +struct share_mode_lock *get_share_mode_lock(TALLOC_CTX *mem_ctx, + const struct file_id id, + const char *servicepath, + const struct smb_filename *smb_fname, + const struct timespec *old_write_time) +{ + struct share_mode_lock *lck; + struct file_id tmp; + TDB_DATA key = locking_key(&id, &tmp); + + if (!(lck = TALLOC_P(mem_ctx, struct share_mode_lock))) { + DEBUG(0, ("talloc failed\n")); + return NULL; + } + + if (!(lck->record = lock_db->fetch_locked(lock_db, lck, key))) { + DEBUG(3, ("Could not lock share entry\n")); + TALLOC_FREE(lck); + return NULL; + } + + if (!fill_share_mode_lock(lck, id, servicepath, smb_fname, + lck->record->value, old_write_time)) { + DEBUG(3, ("fill_share_mode_lock failed\n")); + TALLOC_FREE(lck); + return NULL; + } + + talloc_set_destructor(lck, share_mode_lock_destructor); + + return lck; +} + +struct share_mode_lock *fetch_share_mode_unlocked(TALLOC_CTX *mem_ctx, + const struct file_id id) +{ + struct share_mode_lock *lck; + struct file_id tmp; + TDB_DATA key = locking_key(&id, &tmp); + TDB_DATA data; + + if (!(lck = TALLOC_P(mem_ctx, struct share_mode_lock))) { + DEBUG(0, ("talloc failed\n")); + return NULL; + } + + if (lock_db->fetch(lock_db, lck, key, &data) == -1) { + DEBUG(3, ("Could not fetch share entry\n")); + TALLOC_FREE(lck); + return NULL; + } + + if (!fill_share_mode_lock(lck, id, NULL, NULL, data, NULL)) { + DEBUG(10, ("fetch_share_mode_unlocked: no share_mode record " + "around (file not open)\n")); + TALLOC_FREE(lck); + return NULL; + } return lck; } @@ -658,56 +1032,74 @@ struct share_mode_lock *get_share_mode_lock(TALLOC_CTX *mem_ctx, Based on an initial code idea from SATOH Fumiyasu ********************************************************************/ -BOOL rename_share_filename(struct share_mode_lock *lck, +bool rename_share_filename(struct messaging_context *msg_ctx, + struct share_mode_lock *lck, const char *servicepath, - const char *newname) + uint32_t orig_name_hash, + uint32_t new_name_hash, + const struct smb_filename *smb_fname_dst) { - struct file_renamed_message *frm = NULL; size_t sp_len; - size_t fn_len; + size_t bn_len; + size_t sn_len; size_t msg_len; + char *frm = NULL; int i; - - if (!lck) { - return False; - } + bool strip_two_chars = false; + bool has_stream = smb_fname_dst->stream_name != NULL; DEBUG(10, ("rename_share_filename: servicepath %s newname %s\n", - servicepath, newname)); + servicepath, smb_fname_dst->base_name)); /* * rename_internal_fsp() and rename_internals() add './' to * head of newname if newname does not contain a '/'. */ - while (newname[0] && newname[1] && newname[0] == '.' && newname[1] == '/') { - newname += 2; + if (smb_fname_dst->base_name[0] && + smb_fname_dst->base_name[1] && + smb_fname_dst->base_name[0] == '.' && + smb_fname_dst->base_name[1] == '/') { + strip_two_chars = true; } lck->servicepath = talloc_strdup(lck, servicepath); - lck->filename = talloc_strdup(lck, newname); - if (lck->filename == NULL || lck->servicepath == NULL) { + lck->base_name = talloc_strdup(lck, smb_fname_dst->base_name + + (strip_two_chars ? 2 : 0)); + lck->stream_name = talloc_strdup(lck, smb_fname_dst->stream_name); + if (lck->base_name == NULL || + (has_stream && lck->stream_name == NULL) || + lck->servicepath == NULL) { DEBUG(0, ("rename_share_filename: talloc failed\n")); return False; } lck->modified = True; sp_len = strlen(lck->servicepath); - fn_len = strlen(lck->filename); + bn_len = strlen(lck->base_name); + sn_len = has_stream ? strlen(lck->stream_name) : 0; - msg_len = sizeof(*frm) + sp_len + 1 + fn_len + 1; + msg_len = MSG_FILE_RENAMED_MIN_SIZE + sp_len + 1 + bn_len + 1 + + sn_len + 1; /* Set up the name changed message. */ - frm = TALLOC(lck, msg_len); + frm = talloc_array(lck, char, msg_len); if (!frm) { return False; } - frm->dev = lck->dev; - frm->inode = lck->ino; - DEBUG(10,("rename_share_filename: msg_len = %d\n", msg_len )); + push_file_id_24(frm, &lck->id); + + DEBUG(10,("rename_share_filename: msg_len = %u\n", (unsigned int)msg_len )); - safe_strcpy(&frm->names[0], lck->servicepath, sp_len); - safe_strcpy(&frm->names[sp_len + 1], lck->filename, fn_len); + strlcpy(&frm[24], + lck->servicepath ? lck->servicepath : "", + sp_len+1); + strlcpy(&frm[24 + sp_len + 1], + lck->base_name ? lck->base_name : "", + bn_len+1); + strlcpy(&frm[24 + sp_len + 1 + bn_len + 1], + lck->stream_name ? lck->stream_name : "", + sn_len+1); /* Send the messages. */ for (i=0; inum_share_modes; i++) { @@ -715,40 +1107,84 @@ BOOL rename_share_filename(struct share_mode_lock *lck, if (!is_valid_share_mode_entry(se)) { continue; } + + /* If this is a hardlink to the inode + with a different name, skip this. */ + if (se->name_hash != orig_name_hash) { + continue; + } + + se->name_hash = new_name_hash; + /* But not to ourselves... */ if (procid_is_me(&se->pid)) { continue; } - DEBUG(10,("rename_share_filename: sending rename message to pid %u " - "dev %x, inode %.0f sharepath %s newname %s\n", - (unsigned int)procid_to_pid(&se->pid), - (unsigned int)frm->dev, (double)frm->inode, - lck->servicepath, lck->filename )); + DEBUG(10,("rename_share_filename: sending rename message to " + "pid %s file_id %s sharepath %s base_name %s " + "stream_name %s\n", + procid_str_static(&se->pid), + file_id_string_tos(&lck->id), + lck->servicepath, lck->base_name, + has_stream ? lck->stream_name : "")); - message_send_pid(se->pid, MSG_SMB_FILE_RENAME, - frm, msg_len, True); + messaging_send_buf(msg_ctx, se->pid, MSG_SMB_FILE_RENAME, + (uint8 *)frm, msg_len); } return True; } -BOOL get_delete_on_close_flag(SMB_DEV_T dev, SMB_INO_T inode) +void get_file_infos(struct file_id id, + uint32_t name_hash, + bool *delete_on_close, + struct timespec *write_time) { - BOOL result; - struct share_mode_lock *lck = get_share_mode_lock(NULL, dev, inode, NULL, NULL); - if (!lck) { - return False; + struct share_mode_lock *lck; + + if (delete_on_close) { + *delete_on_close = false; } - result = lck->delete_on_close; - talloc_free(lck); - return result; + + if (write_time) { + ZERO_STRUCTP(write_time); + } + + if (!(lck = fetch_share_mode_unlocked(talloc_tos(), id))) { + return; + } + + if (delete_on_close) { + *delete_on_close = is_delete_on_close_set(lck, name_hash); + } + + if (write_time) { + struct timespec wt; + + wt = lck->changed_write_time; + if (null_timespec(wt)) { + wt = lck->old_write_time; + } + + *write_time = wt; + } + + TALLOC_FREE(lck); } -BOOL is_valid_share_mode_entry(const struct share_mode_entry *e) +bool is_valid_share_mode_entry(const struct share_mode_entry *e) { int num_props = 0; + if (e->op_type == UNUSED_SHARE_MODE_ENTRY) { + /* cope with dead entries from the process not + existing. These should not be considered valid, + otherwise we end up doing zero timeout sharing + violation */ + return False; + } + num_props += ((e->op_type == NO_OPLOCK) ? 1 : 0); num_props += (EXCLUSIVE_OPLOCK_TYPE(e->op_type) ? 1 : 0); num_props += (LEVEL_II_OPLOCK_TYPE(e->op_type) ? 1 : 0); @@ -757,12 +1193,12 @@ BOOL is_valid_share_mode_entry(const struct share_mode_entry *e) return (num_props != 0); } -BOOL is_deferred_open_entry(const struct share_mode_entry *e) +bool is_deferred_open_entry(const struct share_mode_entry *e) { return (e->op_type == DEFERRED_OPEN_ENTRY); } -BOOL is_unused_share_mode_entry(const struct share_mode_entry *e) +bool is_unused_share_mode_entry(const struct share_mode_entry *e) { return (e->op_type == UNUSED_SHARE_MODE_ENTRY); } @@ -773,10 +1209,10 @@ BOOL is_unused_share_mode_entry(const struct share_mode_entry *e) static void fill_share_mode_entry(struct share_mode_entry *e, files_struct *fsp, - uint16 mid, uint16 op_type) + uid_t uid, uint64_t mid, uint16 op_type) { ZERO_STRUCTP(e); - e->pid = procid_self(); + e->pid = sconn_server_id(fsp->conn->sconn); e->share_access = fsp->share_access; e->private_options = fsp->fh->private_options; e->access_mask = fsp->access_mask; @@ -784,23 +1220,28 @@ static void fill_share_mode_entry(struct share_mode_entry *e, e->op_type = op_type; e->time.tv_sec = fsp->open_time.tv_sec; e->time.tv_usec = fsp->open_time.tv_usec; - e->share_file_id = fsp->file_id; - e->dev = fsp->dev; - e->inode = fsp->inode; + e->id = fsp->file_id; + e->share_file_id = fsp->fh->gen_id; + e->uid = (uint32)uid; + e->flags = fsp->posix_open ? SHARE_MODE_FLAG_POSIX_OPEN : 0; + e->name_hash = fsp->name_hash; } static void fill_deferred_open_entry(struct share_mode_entry *e, const struct timeval request_time, - SMB_DEV_T dev, SMB_INO_T ino, uint16 mid) + struct file_id id, + struct server_id pid, + uint64_t mid) { ZERO_STRUCTP(e); - e->pid = procid_self(); + e->pid = pid; e->op_mid = mid; e->op_type = DEFERRED_OPEN_ENTRY; e->time.tv_sec = request_time.tv_sec; e->time.tv_usec = request_time.tv_usec; - e->dev = dev; - e->inode = ino; + e->id = id; + e->uid = (uint32)-1; + e->flags = 0; } static void add_share_mode_entry(struct share_mode_lock *lck, @@ -825,58 +1266,47 @@ static void add_share_mode_entry(struct share_mode_lock *lck, } void set_share_mode(struct share_mode_lock *lck, files_struct *fsp, - uint16 mid, uint16 op_type) + uid_t uid, uint64_t mid, uint16 op_type) { struct share_mode_entry entry; - fill_share_mode_entry(&entry, fsp, mid, op_type); + fill_share_mode_entry(&entry, fsp, uid, mid, op_type); add_share_mode_entry(lck, &entry); } -void add_deferred_open(struct share_mode_lock *lck, uint16 mid, +void add_deferred_open(struct share_mode_lock *lck, uint64_t mid, struct timeval request_time, - SMB_DEV_T dev, SMB_INO_T ino) + struct server_id pid, struct file_id id) { struct share_mode_entry entry; - fill_deferred_open_entry(&entry, request_time, dev, ino, mid); + fill_deferred_open_entry(&entry, request_time, id, pid, mid); add_share_mode_entry(lck, &entry); } /******************************************************************* Check if two share mode entries are identical, ignoring oplock - and mid info and desired_access. + and mid info and desired_access. (Removed paranoia test - it's + not automatically a logic error if they are identical. JRA.) ********************************************************************/ -static BOOL share_modes_identical(struct share_mode_entry *e1, +static bool share_modes_identical(struct share_mode_entry *e1, struct share_mode_entry *e2) { -#if 1 /* JRA PARANOIA TEST - REMOVE LATER */ - if (procid_equal(&e1->pid, &e2->pid) && - e1->share_file_id == e2->share_file_id && - e1->dev == e2->dev && - e1->inode == e2->inode && - (e1->share_access) != (e2->share_access)) { - DEBUG(0,("PANIC: share_modes_identical: share_mode " - "mismatch (e1 = 0x%x, e2 = 0x%x). Logic error.\n", - (unsigned int)e1->share_access, - (unsigned int)e2->share_access )); - smb_panic("PANIC: share_modes_identical logic error.\n"); - } -#endif + /* We used to check for e1->share_access == e2->share_access here + as well as the other fields but 2 different DOS or FCB opens + sharing the same share mode entry may validly differ in + fsp->share_access field. */ return (procid_equal(&e1->pid, &e2->pid) && - (e1->share_access) == (e2->share_access) && - e1->dev == e2->dev && - e1->inode == e2->inode && + file_id_equal(&e1->id, &e2->id) && e1->share_file_id == e2->share_file_id ); } -static BOOL deferred_open_identical(struct share_mode_entry *e1, +static bool deferred_open_identical(struct share_mode_entry *e1, struct share_mode_entry *e2) { return (procid_equal(&e1->pid, &e2->pid) && (e1->op_mid == e2->op_mid) && - (e1->dev == e2->dev) && - (e1->inode == e2->inode)); + file_id_equal(&e1->id, &e2->id)); } static struct share_mode_entry *find_share_mode_entry(struct share_mode_lock *lck, @@ -905,11 +1335,12 @@ static struct share_mode_entry *find_share_mode_entry(struct share_mode_lock *lc entries left. ********************************************************************/ -BOOL del_share_mode(struct share_mode_lock *lck, files_struct *fsp) +bool del_share_mode(struct share_mode_lock *lck, files_struct *fsp) { struct share_mode_entry entry, *e; - fill_share_mode_entry(&entry, fsp, 0, 0); + /* Don't care about the pid owner being correct here - just a search. */ + fill_share_mode_entry(&entry, fsp, (uid_t)-1, 0, NO_OPLOCK); e = find_share_mode_entry(lck, &entry); if (e == NULL) { @@ -921,12 +1352,13 @@ BOOL del_share_mode(struct share_mode_lock *lck, files_struct *fsp) return True; } -void del_deferred_open_entry(struct share_mode_lock *lck, uint16 mid) +void del_deferred_open_entry(struct share_mode_lock *lck, uint64_t mid, + struct server_id pid) { struct share_mode_entry entry, *e; fill_deferred_open_entry(&entry, timeval_zero(), - lck->dev, lck->ino, mid); + lck->id, pid, mid); e = find_share_mode_entry(lck, &entry); if (e == NULL) { @@ -941,19 +1373,31 @@ void del_deferred_open_entry(struct share_mode_lock *lck, uint16 mid) Remove an oplock mid and mode entry from a share mode. ********************************************************************/ -BOOL remove_share_oplock(struct share_mode_lock *lck, files_struct *fsp) +bool remove_share_oplock(struct share_mode_lock *lck, files_struct *fsp) { struct share_mode_entry entry, *e; - fill_share_mode_entry(&entry, fsp, 0, 0); + /* Don't care about the pid owner being correct here - just a search. */ + fill_share_mode_entry(&entry, fsp, (uid_t)-1, 0, NO_OPLOCK); e = find_share_mode_entry(lck, &entry); if (e == NULL) { return False; } - e->op_mid = 0; - e->op_type = NO_OPLOCK; + if (EXCLUSIVE_OPLOCK_TYPE(e->op_type)) { + /* + * Going from exclusive or batch, + * we always go through FAKE_LEVEL_II + * first. + */ + if (!EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) { + smb_panic("remove_share_oplock: logic error"); + } + e->op_type = FAKE_LEVEL_II_OPLOCK; + } else { + e->op_type = NO_OPLOCK; + } lck->modified = True; return True; } @@ -962,11 +1406,12 @@ BOOL remove_share_oplock(struct share_mode_lock *lck, files_struct *fsp) Downgrade a oplock type from exclusive to level II. ********************************************************************/ -BOOL downgrade_share_oplock(struct share_mode_lock *lck, files_struct *fsp) +bool downgrade_share_oplock(struct share_mode_lock *lck, files_struct *fsp) { struct share_mode_entry entry, *e; - fill_share_mode_entry(&entry, fsp, 0, 0); + /* Don't care about the pid owner being correct here - just a search. */ + fill_share_mode_entry(&entry, fsp, (uid_t)-1, 0, NO_OPLOCK); e = find_share_mode_entry(lck, &entry); if (e == NULL) { @@ -978,50 +1423,21 @@ BOOL downgrade_share_oplock(struct share_mode_lock *lck, files_struct *fsp) return True; } - -/******************************************************************* - We've just told all the smbd's that our level2 or fake level2 has been - written to. -********************************************************************/ -BOOL remove_all_share_oplocks(struct share_mode_lock *lck, files_struct *fsp) -{ - int i; - for (i=0; inum_share_modes; i++) { - struct share_mode_entry *e = &lck->share_modes[i]; - if (!is_valid_share_mode_entry(e)) { - continue; - } - if (e->op_type == NO_OPLOCK) { - continue; - } - e->op_type = NO_OPLOCK; - lck->modified = True; - } - return True; -} - /**************************************************************************** - Deal with the internal needs of setting the delete on close flag. Note that - as the tdb locking is recursive, it is safe to call this from within - open_file_shared. JRA. + Check if setting delete on close is allowed on this fsp. ****************************************************************************/ -NTSTATUS can_set_delete_on_close(files_struct *fsp, BOOL delete_on_close, - uint32 dosmode) +NTSTATUS can_set_delete_on_close(files_struct *fsp, uint32 dosmode) { - if (!delete_on_close) { - return NT_STATUS_OK; - } - /* * Only allow delete on close for writable files. */ - if ((dosmode & aRONLY) && + if ((dosmode & FILE_ATTRIBUTE_READONLY) && !lp_delete_readonly(SNUM(fsp->conn))) { DEBUG(10,("can_set_delete_on_close: file %s delete on close " "flag set but file attribute is readonly.\n", - fsp->fsp_name )); + fsp_str_dbg(fsp))); return NT_STATUS_CANNOT_DELETE; } @@ -1032,7 +1448,7 @@ NTSTATUS can_set_delete_on_close(files_struct *fsp, BOOL delete_on_close, if (!CAN_WRITE(fsp->conn)) { DEBUG(10,("can_set_delete_on_close: file %s delete on " "close flag set but write access denied on share.\n", - fsp->fsp_name )); + fsp_str_dbg(fsp))); return NT_STATUS_ACCESS_DENIED; } @@ -1044,13 +1460,75 @@ NTSTATUS can_set_delete_on_close(files_struct *fsp, BOOL delete_on_close, if (!(fsp->access_mask & DELETE_ACCESS)) { DEBUG(10,("can_set_delete_on_close: file %s delete on " "close flag set but delete access denied.\n", - fsp->fsp_name )); + fsp_str_dbg(fsp))); return NT_STATUS_ACCESS_DENIED; } + /* Don't allow delete on close for non-empty directories. */ + if (fsp->is_directory) { + SMB_ASSERT(!is_ntfs_stream_smb_fname(fsp->fsp_name)); + return can_delete_directory(fsp->conn, + fsp->fsp_name->base_name); + } + return NT_STATUS_OK; } +/************************************************************************* + Return a talloced copy of a struct security_unix_token. NULL on fail. + (Should this be in locking.c.... ?). +*************************************************************************/ + +static struct security_unix_token *copy_unix_token(TALLOC_CTX *ctx, const struct security_unix_token *tok) +{ + struct security_unix_token *cpy; + + cpy = TALLOC_P(ctx, struct security_unix_token); + if (!cpy) { + return NULL; + } + + cpy->uid = tok->uid; + cpy->gid = tok->gid; + cpy->ngroups = tok->ngroups; + if (tok->ngroups) { + /* Make this a talloc child of cpy. */ + cpy->groups = (gid_t *)talloc_memdup( + cpy, tok->groups, tok->ngroups * sizeof(gid_t)); + if (!cpy->groups) { + TALLOC_FREE(cpy); + return NULL; + } + } + return cpy; +} + +/**************************************************************************** + Adds a delete on close token. +****************************************************************************/ + +static bool add_delete_on_close_token(struct share_mode_lock *lck, + uint32_t name_hash, + const struct security_unix_token *tok) +{ + struct delete_token_list *dtl; + + dtl = TALLOC_ZERO_P(lck, struct delete_token_list); + if (dtl == NULL) { + return false; + } + + dtl->name_hash = name_hash; + dtl->delete_token = copy_unix_token(lck, tok); + if (dtl->delete_token == NULL) { + TALLOC_FREE(dtl); + return false; + } + DLIST_ADD(lck->delete_tokens, dtl); + lck->modified = true; + return true; +} + /**************************************************************************** Sets the delete on close flag over all share modes on this file. Modify the share mode entry for all files open @@ -1058,58 +1536,196 @@ NTSTATUS can_set_delete_on_close(files_struct *fsp, BOOL delete_on_close, changed the delete on close flag. This will be noticed in the close code, the last closer will delete the file if flag is set. + This makes a copy of any struct security_unix_token into the + lck entry. This function is used when the lock is already granted. ****************************************************************************/ -BOOL set_delete_on_close(files_struct *fsp, BOOL delete_on_close) +void set_delete_on_close_lck(files_struct *fsp, + struct share_mode_lock *lck, + bool delete_on_close, + const struct security_unix_token *tok) +{ + struct delete_token_list *dtl; + bool ret; + + if (delete_on_close) { + SMB_ASSERT(tok != NULL); + } else { + SMB_ASSERT(tok == NULL); + } + + for (dtl = lck->delete_tokens; dtl; dtl = dtl->next) { + if (dtl->name_hash == fsp->name_hash) { + lck->modified = true; + if (delete_on_close == false) { + /* Delete this entry. */ + DLIST_REMOVE(lck->delete_tokens, dtl); + TALLOC_FREE(dtl); + return; + } + /* Replace this token with the + given tok. */ + TALLOC_FREE(dtl->delete_token); + dtl->delete_token = copy_unix_token(dtl, tok); + SMB_ASSERT(dtl->delete_token != NULL); + } + } + + if (!delete_on_close) { + /* Nothing to delete - not found. */ + return; + } + + ret = add_delete_on_close_token(lck, fsp->name_hash, tok); + SMB_ASSERT(ret); +} + +bool set_delete_on_close(files_struct *fsp, bool delete_on_close, const struct security_unix_token *tok) { struct share_mode_lock *lck; - + DEBUG(10,("set_delete_on_close: %s delete on close flag for " "fnum = %d, file %s\n", delete_on_close ? "Adding" : "Removing", fsp->fnum, - fsp->fsp_name )); + fsp_str_dbg(fsp))); - if (fsp->is_stat) { - return True; + lck = get_share_mode_lock(talloc_tos(), fsp->file_id, NULL, NULL, + NULL); + if (lck == NULL) { + return False; } - lck = get_share_mode_lock(NULL, fsp->dev, fsp->inode, NULL, NULL); + set_delete_on_close_lck(fsp, lck, delete_on_close, + delete_on_close ? tok : NULL); + + if (fsp->is_directory) { + SMB_ASSERT(!is_ntfs_stream_smb_fname(fsp->fsp_name)); + send_stat_cache_delete_message(fsp->conn->sconn->msg_ctx, + fsp->fsp_name->base_name); + } + + TALLOC_FREE(lck); + + fsp->delete_on_close = delete_on_close; + + return True; +} + +const struct security_unix_token *get_delete_on_close_token(struct share_mode_lock *lck, uint32_t name_hash) +{ + struct delete_token_list *dtl; + + DEBUG(10,("get_delete_on_close_token: name_hash = 0x%x\n", + (unsigned int)name_hash )); + + for (dtl = lck->delete_tokens; dtl; dtl = dtl->next) { + DEBUG(10,("get_delete_on_close_token: dtl->name_hash = 0x%x\n", + (unsigned int)dtl->name_hash )); + if (dtl->name_hash == name_hash) { + return dtl->delete_token; + } + } + return NULL; +} + +bool is_delete_on_close_set(struct share_mode_lock *lck, uint32_t name_hash) +{ + return (get_delete_on_close_token(lck, name_hash) != NULL); +} + +bool set_sticky_write_time(struct file_id fileid, struct timespec write_time) +{ + struct share_mode_lock *lck; + + DEBUG(5,("set_sticky_write_time: %s id=%s\n", + timestring(talloc_tos(), + convert_timespec_to_time_t(write_time)), + file_id_string_tos(&fileid))); + + lck = get_share_mode_lock(NULL, fileid, NULL, NULL, NULL); if (lck == NULL) { return False; } - if (lck->delete_on_close != delete_on_close) { - lck->delete_on_close = delete_on_close; + + if (timespec_compare(&lck->changed_write_time, &write_time) != 0) { lck->modified = True; + lck->changed_write_time = write_time; } - talloc_free(lck); + TALLOC_FREE(lck); return True; } -static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, - void *state) +bool set_write_time(struct file_id fileid, struct timespec write_time) { + struct share_mode_lock *lck; + + DEBUG(5,("set_write_time: %s id=%s\n", + timestring(talloc_tos(), + convert_timespec_to_time_t(write_time)), + file_id_string_tos(&fileid))); + + lck = get_share_mode_lock(NULL, fileid, NULL, NULL, NULL); + if (lck == NULL) { + return False; + } + + if (timespec_compare(&lck->old_write_time, &write_time) != 0) { + lck->modified = True; + lck->old_write_time = write_time; + } + + TALLOC_FREE(lck); + return True; +} + + +struct forall_state { + void (*fn)(const struct share_mode_entry *entry, + const char *sharepath, + const char *fname, + void *private_data); + void *private_data; +}; + +static int traverse_fn(struct db_record *rec, void *_state) +{ + struct forall_state *state = (struct forall_state *)_state; struct locking_data *data; struct share_mode_entry *shares; const char *sharepath; const char *fname; + const char *del_tokens; + uint32_t total_del_token_size = 0; int i; - void (*traverse_callback)(struct share_mode_entry *, const char *, const char *) = state; /* Ensure this is a locking_key record. */ - if (kbuf.dsize != sizeof(struct locking_key)) + if (rec->key.dsize != sizeof(struct file_id)) return 0; - data = (struct locking_data *)dbuf.dptr; - shares = (struct share_mode_entry *)(dbuf.dptr + sizeof(*data)); - sharepath = dbuf.dptr + sizeof(*data) + + data = (struct locking_data *)rec->value.dptr; + shares = (struct share_mode_entry *)(rec->value.dptr + sizeof(*data)); + del_tokens = (const char *)rec->value.dptr + sizeof(*data) + data->u.s.num_share_mode_entries*sizeof(*shares); - fname = dbuf.dptr + sizeof(*data) + + + for (i = 0; i < data->u.s.num_delete_token_entries; i++) { + uint32_t del_token_size; + memcpy(&del_token_size, del_tokens, sizeof(uint32_t)); + total_del_token_size += del_token_size; + del_tokens += del_token_size; + } + + sharepath = (const char *)rec->value.dptr + sizeof(*data) + + data->u.s.num_share_mode_entries*sizeof(*shares) + + total_del_token_size; + fname = (const char *)rec->value.dptr + sizeof(*data) + data->u.s.num_share_mode_entries*sizeof(*shares) + + total_del_token_size + strlen(sharepath) + 1; for (i=0;iu.s.num_share_mode_entries;i++) { - traverse_callback(&shares[i], sharepath, fname); + state->fn(&shares[i], sharepath, fname, + state->private_data); } return 0; } @@ -1119,9 +1735,17 @@ static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, share mode system. ********************************************************************/ -int share_mode_forall(void (*fn)(const struct share_mode_entry *, const char *, const char *)) +int share_mode_forall(void (*fn)(const struct share_mode_entry *, const char *, + const char *, void *), + void *private_data) { - if (tdb == NULL) + struct forall_state state; + + if (lock_db == NULL) return 0; - return tdb_traverse(tdb, traverse_fn, fn); + + state.fn = fn; + state.private_data = private_data; + + return lock_db->traverse_read(lock_db, traverse_fn, (void *)&state); }