X-Git-Url: http://git.samba.org/samba.git/?p=sfrench%2Fsamba-autobuild%2F.git;a=blobdiff_plain;f=source%2Flocking%2Flocking.c;h=28a7fab97d3fd2c0a30fc783adba92df404b77c2;hp=9a707f80a42068df18ff54489a0e27a543fe4beb;hb=bbb508414683eeddd2ee0d2d36fe620118180bbb;hpb=dbe2572d1c713f46b0d1d0c405f88456c9336297 diff --git a/source/locking/locking.c b/source/locking/locking.c index 9a707f80a42..28a7fab97d3 100644 --- a/source/locking/locking.c +++ b/source/locking/locking.c @@ -2,7 +2,7 @@ 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 @@ -33,10 +33,10 @@ 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; #undef DBGC_CLASS #define DBGC_CLASS DBGC_LOCKING @@ -45,218 +45,292 @@ uint16 global_smbpid; static TDB_CONTEXT *tdb; /**************************************************************************** - 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) { - return (lock_type == READ_LOCK) ? "READ" : "WRITE"; + 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_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) +BOOL is_locked(files_struct *fsp, + uint32 smbpid, + SMB_BIG_UINT count, + SMB_BIG_UINT offset, + enum brl_type lock_type) { - int snum = SNUM(conn); - int strict_locking = lp_strict_locking(snum); - BOOL ret; + int strict_locking = lp_strict_locking(fsp->conn->params); + enum brl_flavour lock_flav = lp_posix_cifsu_locktype(fsp); + BOOL ret = True; - if (count == 0) - return(False); + if (count == 0) { + return False; + } - if (!lp_locking(snum) || !strict_locking) - return(False); + if (!lp_locking(fsp->conn->params) || !strict_locking) { + return False; + } 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; + ret = False; } 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; + ret = False; } 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 = brl_get_locks_readonly(NULL, fsp); + if (!br_lck) { + return False; + } + ret = !brl_locktest(br_lck, + smbpid, + procid_self(), + offset, + count, + lock_type, + lock_flav); + TALLOC_FREE(br_lck); } } 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 = brl_get_locks_readonly(NULL, fsp); + if (!br_lck) { + return False; + } + ret = !brl_locktest(br_lck, + smbpid, + procid_self(), + offset, + count, + lock_type, + lock_flav); + TALLOC_FREE(br_lck); } - DEBUG(10,("is_locked: brl start=%.0f len=%.0f %s for file %s\n", + DEBUG(10,("is_locked: flavour = %s brl start=%.0f len=%.0f %s for fnum %d file %s\n", + lock_flav_name(lock_flav), (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. - */ - - 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 )); - } + fsp->fnum, fsp->fsp_name )); return ret; } /**************************************************************************** - 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, + uint32 *psmbpid, + SMB_BIG_UINT *pcount, + SMB_BIG_UINT *poffset, + enum brl_type *plock_type, + enum brl_flavour lock_flav) { + struct byte_range_lock *br_lck = NULL; NTSTATUS status = NT_STATUS_LOCK_NOT_GRANTED; - if (!lp_locking(SNUM(conn))) - return NT_STATUS_OK; + if (!fsp->can_lock) { + return fsp->is_directory ? NT_STATUS_INVALID_DEVICE_REQUEST : NT_STATUS_INVALID_HANDLE; + } - /* NOTE! 0 byte long ranges ARE allowed and should be stored */ + if (!lp_locking(fsp->conn->params)) { + return NT_STATUS_OK; + } - 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); - } - } + br_lck = brl_get_locks_readonly(NULL, fsp); + if (!br_lck) { + return NT_STATUS_NO_MEMORY; } + status = brl_lockquery(br_lck, + psmbpid, + procid_self(), + poffset, + pcount, + plock_type, + lock_flav); + + TALLOC_FREE(br_lck); return status; } /**************************************************************************** - 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, + uint32 lock_pid, + SMB_BIG_UINT count, + SMB_BIG_UINT offset, + enum brl_type lock_type, + enum brl_flavour lock_flav, + BOOL blocking_lock, + NTSTATUS *perr) { - 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; + if (!fsp->can_lock) { + *perr = fsp->is_directory ? NT_STATUS_INVALID_DEVICE_REQUEST : NT_STATUS_INVALID_HANDLE; + return NULL; + } - ret = NT_STATUS_OK; /* to keep dumb compilers happy */ + if (!lp_locking(fsp->conn->params)) { + *perr = NT_STATUS_OK; + 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); + /* NOTE! 0 byte long ranges ARE allowed and should be stored */ + + DEBUG(10,("do_lock: lock flavour %s lock type %s start=%.0f len=%.0f requested for fnum %d file %s\n", + lock_flav_name(lock_flav), lock_type_name(lock_type), + (double)offset, (double)count, fsp->fnum, fsp->fsp_name )); + + br_lck = brl_get_locks(NULL, fsp); + if (!br_lck) { + *perr = NT_STATUS_NO_MEMORY; + 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; -}; + *perr = brl_lock(msg_ctx, + br_lck, + lock_pid, + procid_self(), + offset, + count, + lock_type, + lock_flav, + blocking_lock); -/**************************************************************************** - Function passed to brl_unlock to allow POSIX unlock to be done first. -****************************************************************************/ + /* blocking ie. pending, locks also count here, + * as this is an efficiency counter to avoid checking + * the lock db. on close. JRA. */ -static void posix_unlock(void *pre_data) -{ - struct posix_unlock_data_struct *pdata = (struct posix_unlock_data_struct *)pre_data; + fsp->current_lock_count++; - if (lp_posix_locking(SNUM(pdata->fsp->conn))) - release_posix_lock(pdata->fsp, pdata->offset, pdata->count); + 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, + uint32 lock_pid, + SMB_BIG_UINT count, + SMB_BIG_UINT offset, + enum brl_flavour lock_flav) { BOOL ok = False; - struct posix_unlock_data_struct posix_data; + struct byte_range_lock *br_lck = NULL; - if (!lp_locking(SNUM(conn))) - return NT_STATUS_OK; + if (!fsp->can_lock) { + return fsp->is_directory ? NT_STATUS_INVALID_DEVICE_REQUEST : NT_STATUS_INVALID_HANDLE; + } - if (!OPEN_FSP(fsp) || !fsp->can_lock || (fsp->conn != conn)) { - return NT_STATUS_INVALID_HANDLE; + if (!lp_locking(fsp->conn->params)) { + return NT_STATUS_OK; } - 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->fsp_name )); - posix_data.fsp = fsp; - posix_data.offset = offset; - posix_data.count = count; + br_lck = brl_get_locks(NULL, fsp); + if (!br_lck) { + return NT_STATUS_NO_MEMORY; + } - ok = brl_unlock(fsp->dev, fsp->inode, fsp->fnum, - lock_pid, procid_self(), conn->cnum, offset, count, - False, posix_unlock, (void *)&posix_data); + ok = brl_unlock(msg_ctx, + br_lck, + lock_pid, + procid_self(), + offset, + count, + lock_flav); + TALLOC_FREE(br_lck); + if (!ok) { DEBUG(10,("do_unlock: returning ERRlock.\n" )); return NT_STATUS_RANGE_NOT_LOCKED; } + + SMB_ASSERT(fsp->current_lock_count > 0); + fsp->current_lock_count--; + + return NT_STATUS_OK; +} + +/**************************************************************************** + Cancel any pending blocked locks. +****************************************************************************/ + +NTSTATUS do_lock_cancel(files_struct *fsp, + uint32 lock_pid, + SMB_BIG_UINT count, + SMB_BIG_UINT offset, + enum brl_flavour lock_flav) +{ + 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->fsp_name )); + + br_lck = brl_get_locks(NULL, fsp); + if (!br_lck) { + return NT_STATUS_NO_MEMORY; + } + + ok = brl_lock_cancel(br_lck, + lock_pid, + procid_self(), + offset, + count, + lock_flav); + + TALLOC_FREE(br_lck); + + if (!ok) { + DEBUG(10,("do_lock_cancel: returning ERRcancelviolation.\n" )); + return NT_STATUS_DOS(ERRDOS, ERRcancelviolation); + } + + SMB_ASSERT(fsp->current_lock_count > 0); + fsp->current_lock_count--; + return NT_STATUS_OK; } @@ -264,26 +338,29 @@ 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) { - 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(NULL,fsp); + if (br_lck) { + cancel_pending_lock_requests_by_fid(fsp, br_lck); + brl_close_fnum(msg_ctx, br_lck); + TALLOC_FREE(br_lck); } } @@ -301,7 +378,8 @@ BOOL locking_init(int read_only) 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), + lp_open_files_db_hash_size(), + TDB_DEFAULT|(read_only?0x0:TDB_CLEAR_IF_FIRST), read_only?O_RDONLY:O_RDWR|O_CREAT, 0644); @@ -310,6 +388,9 @@ BOOL locking_init(int read_only) return False; } + /* Activate the per-hashchain freelist */ + tdb_set_max_dead(tdb, 5); + if (!posix_locking_init(read_only)) return False; @@ -357,7 +438,7 @@ static TDB_DATA locking_key(SMB_DEV_T dev, SMB_INO_T inode) memset(&key, '\0', sizeof(key)); key.dev = dev; key.ino = inode; - kbuf.dptr = (char *)&key; + kbuf.dptr = (uint8 *)&key; kbuf.dsize = sizeof(key); return kbuf; } @@ -373,12 +454,13 @@ char *share_mode_str(int num, struct share_mode_entry *e) slprintf(share_str, sizeof(share_str)-1, "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", + "uid = %u, flags = %u, dev = 0x%x, inode = %.0f", 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->uid, (unsigned int)e->flags, (unsigned int)e->dev, (double)e->inode ); return share_str; @@ -420,14 +502,11 @@ static BOOL parse_share_modes(TDB_DATA dbuf, struct share_mode_lock *lck) data = (struct locking_data *)dbuf.dptr; lck->delete_on_close = data->u.s.delete_on_close; - lck->initial_delete_on_close = data->u.s.initial_delete_on_close; lck->num_share_modes = data->u.s.num_share_mode_entries; DEBUG(10, ("parse_share_modes: delete_on_close: %d, " - "initial_delete_on_close: %d, " "num_share_modes: %d\n", lck->delete_on_close, - lck->initial_delete_on_close, lck->num_share_modes)); if ((lck->num_share_modes < 0) || (lck->num_share_modes > 1000000)) { @@ -446,9 +525,10 @@ static BOOL parse_share_modes(TDB_DATA dbuf, struct share_mode_lock *lck) smb_panic("PANIC: parse_share_modes: buffer too short.\n"); } - 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(*data), + lck->num_share_modes * + sizeof(struct share_mode_entry)); if (lck->share_modes == NULL) { smb_panic("talloc failed\n"); @@ -457,13 +537,12 @@ static BOOL parse_share_modes(TDB_DATA dbuf, struct share_mode_lock *lck) /* Get any delete token. */ if (data->u.s.delete_token_size) { - /* Each uid/gid is stored as a 4 byte value. */ - uint32 val; - uint32 *p = (uint32 *)(dbuf.dptr + sizeof(*data) + + uint8 *p = dbuf.dptr + sizeof(*data) + (lck->num_share_modes * - sizeof(struct share_mode_entry))); + sizeof(struct share_mode_entry)); - if ((data->u.s.delete_token_size < 8) || (data->u.s.delete_token_size % 4) != 0) { + if ((data->u.s.delete_token_size < sizeof(uid_t) + sizeof(gid_t)) || + ((data->u.s.delete_token_size - sizeof(uid_t)) % sizeof(gid_t)) != 0) { DEBUG(0, ("parse_share_modes: invalid token size %d\n", data->u.s.delete_token_size)); smb_panic("parse_share_modes: invalid token size\n"); @@ -475,14 +554,16 @@ static BOOL parse_share_modes(TDB_DATA dbuf, struct share_mode_lock *lck) } /* Copy out the uid and gid. */ - memcpy(&val, p++, 4); - lck->delete_token->uid = (uid_t)val; - memcpy(&val, p++, 4); - lck->delete_token->gid = (gid_t)val; + memcpy(&lck->delete_token->uid, p, sizeof(uid_t)); + p += sizeof(uid_t); + memcpy(&lck->delete_token->gid, p, sizeof(gid_t)); + p += sizeof(gid_t); /* Any supplementary groups ? */ - lck->delete_token->ngroups = (data->u.s.delete_token_size > 8) ? - ((data->u.s.delete_token_size - 8)/4) : 0; + lck->delete_token->ngroups = (data->u.s.delete_token_size > (sizeof(uid_t) + sizeof(gid_t))) ? + ((data->u.s.delete_token_size - + (sizeof(uid_t) + sizeof(gid_t)))/sizeof(gid_t)) : 0; + if (lck->delete_token->ngroups) { /* Make this a talloc child of lck->delete_token. */ lck->delete_token->groups = TALLOC_ARRAY(lck->delete_token, gid_t, @@ -492,8 +573,8 @@ static BOOL parse_share_modes(TDB_DATA dbuf, struct share_mode_lock *lck) } for (i = 0; i < lck->delete_token->ngroups; i++) { - memcpy(&val, p++, 4); - lck->delete_token->groups[i] = (gid_t)val; + memcpy(&lck->delete_token->groups[i], p, sizeof(gid_t)); + p += sizeof(gid_t); } } @@ -502,16 +583,22 @@ static BOOL parse_share_modes(TDB_DATA dbuf, struct share_mode_lock *lck) } /* Save off the associated service path and filename. */ - lck->servicepath = talloc_strdup(lck, dbuf.dptr + sizeof(*data) + + lck->servicepath = talloc_strdup(lck, (const char *)dbuf.dptr + sizeof(*data) + (lck->num_share_modes * sizeof(struct share_mode_entry)) + data->u.s.delete_token_size ); + if (lck->servicepath == NULL) { + smb_panic("talloc_strdup failed\n"); + } - lck->filename = talloc_strdup(lck, dbuf.dptr + sizeof(*data) + + lck->filename = talloc_strdup(lck, (const char *)dbuf.dptr + sizeof(*data) + (lck->num_share_modes * sizeof(struct share_mode_entry)) + data->u.s.delete_token_size + strlen(lck->servicepath) + 1 ); + if (lck->filename == NULL) { + smb_panic("talloc_strdup failed\n"); + } /* * Ensure that each entry has a real process attached. @@ -557,14 +644,14 @@ static TDB_DATA unparse_share_modes(struct share_mode_lock *lck) sp_len = strlen(lck->servicepath); delete_token_size = (lck->delete_token ? - (8 + (lck->delete_token->ngroups*4)) : 0); + (sizeof(uid_t) + sizeof(gid_t) + (lck->delete_token->ngroups*sizeof(gid_t))) : 0); result.dsize = sizeof(*data) + lck->num_share_modes * sizeof(struct share_mode_entry) + delete_token_size + sp_len + 1 + strlen(lck->filename) + 1; - result.dptr = talloc_size(lck, result.dsize); + result.dptr = TALLOC_ARRAY(lck, uint8, result.dsize); if (result.dptr == NULL) { smb_panic("talloc failed\n"); @@ -574,11 +661,9 @@ static TDB_DATA unparse_share_modes(struct share_mode_lock *lck) ZERO_STRUCTP(data); data->u.s.num_share_mode_entries = lck->num_share_modes; data->u.s.delete_on_close = lck->delete_on_close; - data->u.s.initial_delete_on_close = lck->initial_delete_on_close; data->u.s.delete_token_size = delete_token_size; - DEBUG(10, ("unparse_share_modes: del: %d, initial del %d, tok = %u, num: %d\n", + DEBUG(10, ("unparse_share_modes: del: %d, tok = %u, num: %d\n", data->u.s.delete_on_close, - data->u.s.initial_delete_on_close, (unsigned int)data->u.s.delete_token_size, data->u.s.num_share_mode_entries)); memcpy(result.dptr + sizeof(*data), lck->share_modes, @@ -588,26 +673,25 @@ static TDB_DATA unparse_share_modes(struct share_mode_lock *lck) /* Store any delete on close token. */ if (lck->delete_token) { - uint32 val; - uint32 *p = (uint32 *)(result.dptr + offset); + uint8 *p = result.dptr + offset; - val = (uint32)lck->delete_token->uid; - memcpy(p++, &val, 4); + memcpy(p, &lck->delete_token->uid, sizeof(uid_t)); + p += sizeof(uid_t); - val = (uint32)lck->delete_token->uid; - memcpy(p++, &val, 4); + memcpy(p, &lck->delete_token->gid, sizeof(gid_t)); + p += sizeof(gid_t); for (i = 0; i < lck->delete_token->ngroups; i++) { - val = (uint32)lck->delete_token->groups[i]; - memcpy(p++, &val, 4); + memcpy(p, &lck->delete_token->groups[i], sizeof(gid_t)); + p += sizeof(gid_t); } - offset = ((char *)p - result.dptr); + offset = p - result.dptr; } - safe_strcpy(result.dptr + offset, lck->servicepath, + safe_strcpy((char *)result.dptr + offset, lck->servicepath, result.dsize - offset - 1); offset += sp_len + 1; - safe_strcpy(result.dptr + offset, lck->filename, + safe_strcpy((char *)result.dptr + offset, lck->filename, result.dsize - offset - 1); if (DEBUGLEVEL >= 10) { @@ -617,10 +701,8 @@ static TDB_DATA unparse_share_modes(struct share_mode_lock *lck) 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); TDB_DATA data; @@ -676,13 +758,12 @@ struct share_mode_lock *get_share_mode_lock(TALLOC_CTX *mem_ctx, lck->share_modes = NULL; lck->delete_token = NULL; lck->delete_on_close = False; - lck->initial_delete_on_close = False; lck->fresh = False; lck->modified = False; if (tdb_chainlock(tdb, key) != 0) { DEBUG(3, ("Could not lock share entry\n")); - talloc_free(lck); + TALLOC_FREE(lck); return NULL; } @@ -698,20 +779,20 @@ struct share_mode_lock *get_share_mode_lock(TALLOC_CTX *mem_ctx, if (lck->fresh) { if (fname == NULL || servicepath == NULL) { - talloc_free(lck); + TALLOC_FREE(lck); return NULL; } lck->filename = talloc_strdup(lck, fname); lck->servicepath = talloc_strdup(lck, servicepath); if (lck->filename == NULL || lck->servicepath == NULL) { DEBUG(0, ("talloc failed\n")); - talloc_free(lck); + TALLOC_FREE(lck); return NULL; } } else { if (!parse_share_modes(data, lck)) { DEBUG(0, ("Could not parse share modes\n")); - talloc_free(lck); + TALLOC_FREE(lck); SAFE_FREE(data.dptr); return NULL; } @@ -729,7 +810,8 @@ 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) { @@ -738,6 +820,7 @@ BOOL rename_share_filename(struct share_mode_lock *lck, size_t msg_len; char *frm = NULL; int i; + DATA_BLOB msg; if (!lck) { return False; @@ -768,7 +851,7 @@ BOOL rename_share_filename(struct share_mode_lock *lck, msg_len = MSG_FILE_RENAMED_MIN_SIZE + sp_len + 1 + fn_len + 1; /* Set up the name changed message. */ - frm = TALLOC(lck, msg_len); + frm = TALLOC_ARRAY(lck, char, msg_len); if (!frm) { return False; } @@ -776,11 +859,13 @@ BOOL rename_share_filename(struct share_mode_lock *lck, SDEV_T_VAL(frm,0,lck->dev); SINO_T_VAL(frm,8,lck->ino); - DEBUG(10,("rename_share_filename: msg_len = %d\n", msg_len )); + DEBUG(10,("rename_share_filename: msg_len = %u\n", (unsigned int)msg_len )); safe_strcpy(&frm[16], lck->servicepath, sp_len); safe_strcpy(&frm[16 + sp_len + 1], lck->filename, fn_len); + msg = data_blob_const(frm, msg_len); + /* Send the messages. */ for (i=0; inum_share_modes; i++) { struct share_mode_entry *se = &lck->share_modes[i]; @@ -792,30 +877,41 @@ BOOL rename_share_filename(struct share_mode_lock *lck, continue; } - DEBUG(10,("rename_share_filename: sending rename message to pid %u " + DEBUG(10,("rename_share_filename: sending rename message to pid %s " "dev %x, inode %.0f sharepath %s newname %s\n", - (unsigned int)procid_to_pid(&se->pid), + procid_str_static(&se->pid), (unsigned int)lck->dev, (double)lck->ino, lck->servicepath, lck->filename )); - become_root(); - message_send_pid(se->pid, MSG_SMB_FILE_RENAME, - frm, msg_len, True); - unbecome_root(); + messaging_send(msg_ctx, se->pid, MSG_SMB_FILE_RENAME, &msg); } return True; } -BOOL get_delete_on_close_flag(SMB_DEV_T dev, SMB_INO_T inode) +static int pull_delete_on_close_flag(TDB_DATA key, TDB_DATA dbuf, + void *private_data) { - BOOL result; - struct share_mode_lock *lck = get_share_mode_lock(NULL, dev, inode, NULL, NULL); - if (!lck) { - return False; + BOOL *result = (BOOL *)private_data; + struct locking_data *data; + + if (dbuf.dsize < sizeof(struct locking_data)) { + smb_panic("PANIC: parse_share_modes: buffer too short.\n"); } - result = lck->delete_on_close; - talloc_free(lck); + + data = (struct locking_data *)dbuf.dptr; + + *result = data->u.s.delete_on_close; + return 0; +} + +BOOL get_delete_on_close_flag(SMB_DEV_T dev, SMB_INO_T inode) +{ + TDB_DATA key = locking_key(dev, inode); + BOOL result = False; + + tdb_parse_record(tdb, key, pull_delete_on_close_flag, + (void *)&result); return result; } @@ -847,7 +943,7 @@ 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, uint16 mid, uint16 op_type) { ZERO_STRUCTP(e); e->pid = procid_self(); @@ -858,9 +954,11 @@ 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->share_file_id = fsp->fh->file_id; + e->uid = (uint32)uid; + e->flags = fsp->posix_open ? SHARE_MODE_FLAG_POSIX_OPEN : 0; } static void fill_deferred_open_entry(struct share_mode_entry *e, @@ -875,6 +973,8 @@ static void fill_deferred_open_entry(struct share_mode_entry *e, e->time.tv_usec = request_time.tv_usec; e->dev = dev; e->inode = ino; + e->uid = (uint32)-1; + e->flags = 0; } static void add_share_mode_entry(struct share_mode_lock *lck, @@ -899,10 +999,10 @@ 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, uint16 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); } @@ -917,28 +1017,19 @@ void add_deferred_open(struct share_mode_lock *lck, uint16 mid, /******************************************************************* 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, 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 && e1->share_file_id == e2->share_file_id ); @@ -983,7 +1074,8 @@ 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) { @@ -1019,7 +1111,8 @@ 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) { @@ -1040,7 +1133,8 @@ 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) { @@ -1055,7 +1149,7 @@ BOOL downgrade_share_oplock(struct share_mode_lock *lck, files_struct *fsp) /**************************************************************************** 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. + open_file_ntcreate. JRA. ****************************************************************************/ NTSTATUS can_set_delete_on_close(files_struct *fsp, BOOL delete_on_close, @@ -1100,6 +1194,11 @@ NTSTATUS can_set_delete_on_close(files_struct *fsp, BOOL delete_on_close, return NT_STATUS_ACCESS_DENIED; } + /* Don't allow delete on close for non-empty directories. */ + if (fsp->is_directory) { + return can_delete_directory(fsp->conn, fsp->fsp_name); + } + return NT_STATUS_OK; } @@ -1143,7 +1242,7 @@ void set_delete_on_close_token(struct share_mode_lock *lck, UNIX_USER_TOKEN *tok { /* Ensure there's no token. */ if (lck->delete_token) { - talloc_free(lck->delete_token); /* Also deletes groups... */ + TALLOC_FREE(lck->delete_token); /* Also deletes groups... */ lck->delete_token = NULL; } @@ -1159,11 +1258,22 @@ void set_delete_on_close_token(struct share_mode_lock *lck, UNIX_USER_TOKEN *tok 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. - Note that setting this to any value clears the initial_delete_on_close flag. - If delete_on_close is True this makes a copy of any UNIX_USER_TOKEN into the - lck entry. + This makes a copy of any UNIX_USER_TOKEN into the + lck entry. This function is used when the lock is already granted. ****************************************************************************/ +void set_delete_on_close_lck(struct share_mode_lock *lck, BOOL delete_on_close, UNIX_USER_TOKEN *tok) +{ + if (lck->delete_on_close != delete_on_close) { + set_delete_on_close_token(lck, tok); + lck->delete_on_close = delete_on_close; + if (delete_on_close) { + SMB_ASSERT(lck->delete_token != NULL); + } + lck->modified = True; + } +} + BOOL set_delete_on_close(files_struct *fsp, BOOL delete_on_close, UNIX_USER_TOKEN *tok) { struct share_mode_lock *lck; @@ -1182,33 +1292,33 @@ BOOL set_delete_on_close(files_struct *fsp, BOOL delete_on_close, UNIX_USER_TOKE return False; } - if (lck->delete_on_close != delete_on_close) { - set_delete_on_close_token(lck, tok); - lck->delete_on_close = delete_on_close; - if (delete_on_close) { - SMB_ASSERT(lck->delete_token != NULL); - } - lck->modified = True; - } + set_delete_on_close_lck(lck, delete_on_close, tok); - if (lck->initial_delete_on_close) { - lck->initial_delete_on_close = False; - lck->modified = True; + if (fsp->is_directory) { + send_stat_cache_delete_message(fsp->fsp_name); } - talloc_free(lck); + 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(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, - void *state) + 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; 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)) @@ -1216,16 +1326,17 @@ static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA kbuf, TDB_DATA dbuf, data = (struct locking_data *)dbuf.dptr; shares = (struct share_mode_entry *)(dbuf.dptr + sizeof(*data)); - sharepath = dbuf.dptr + sizeof(*data) + + sharepath = (const char *)dbuf.dptr + sizeof(*data) + data->u.s.num_share_mode_entries*sizeof(*shares) + data->u.s.delete_token_size; - fname = dbuf.dptr + sizeof(*data) + + fname = (const char *)dbuf.dptr + sizeof(*data) + data->u.s.num_share_mode_entries*sizeof(*shares) + data->u.s.delete_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; } @@ -1235,9 +1346,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) { + struct forall_state state; + if (tdb == NULL) return 0; - return tdb_traverse(tdb, traverse_fn, fn); + + state.fn = fn; + state.private_data = private_data; + + return tdb_traverse(tdb, traverse_fn, (void *)&state); }