mirror of
https://github.com/samba-team/samba.git
synced 2025-01-15 23:24:37 +03:00
Merge branch 'v4-0-test' of git://git.samba.org/samba into 4-0-local
(This used to be commit e9567e39106efb7443ed2c5df3492208b1c1d766)
This commit is contained in:
commit
3d9056aa86
@ -283,7 +283,7 @@ static NTSTATUS odb_oplock_break_send(struct odb_context *odb, struct opendb_ent
|
||||
*/
|
||||
static NTSTATUS odb_ctdb_open_file(struct odb_lock *lck,
|
||||
void *file_handle, const char *path,
|
||||
bool allow_level_II_oplock,
|
||||
int *fd, bool allow_level_II_oplock,
|
||||
uint32_t oplock_level, uint32_t *oplock_granted)
|
||||
|
||||
{
|
||||
|
@ -20,6 +20,7 @@ interface opendb
|
||||
uint32 share_access;
|
||||
uint32 access_mask;
|
||||
pointer file_handle;
|
||||
pointer fd;
|
||||
/* we need a per-entry delete on close, as well as a per-file
|
||||
one, to cope with strange semantics on open */
|
||||
boolean8 delete_on_close;
|
||||
|
@ -595,6 +595,13 @@ static NTSTATUS cvfs_rename(struct ntvfs_module_context *ntvfs,
|
||||
|
||||
SETUP_PID;
|
||||
|
||||
if (ren->nttrans.level == RAW_RENAME_NTTRANS) {
|
||||
struct cvfs_file *f;
|
||||
f = ntvfs_handle_get_backend_data(ren->nttrans.in.file.ntvfs, ntvfs);
|
||||
if (!f) return NT_STATUS_INVALID_HANDLE;
|
||||
ren->nttrans.in.file.fnum = f->fnum;
|
||||
}
|
||||
|
||||
if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
|
||||
return smb_raw_rename(private->tree, ren);
|
||||
}
|
||||
|
@ -9,7 +9,10 @@ OBJ_FILES = \
|
||||
opendb.o \
|
||||
opendb_tdb.o \
|
||||
notify.o
|
||||
PUBLIC_DEPENDENCIES = NDR_OPENDB NDR_NOTIFY sys_notify share LIBDBWRAP
|
||||
PUBLIC_DEPENDENCIES = \
|
||||
NDR_OPENDB NDR_NOTIFY \
|
||||
sys_notify sys_lease \
|
||||
share LIBDBWRAP
|
||||
PRIVATE_DEPENDENCIES = brlock_ctdb opendb_ctdb
|
||||
# End LIBRARY ntvfs_common
|
||||
################################################
|
||||
|
@ -97,11 +97,11 @@ _PUBLIC_ DATA_BLOB odb_get_key(TALLOC_CTX *mem_ctx, struct odb_lock *lck)
|
||||
*/
|
||||
_PUBLIC_ NTSTATUS odb_open_file(struct odb_lock *lck,
|
||||
void *file_handle, const char *path,
|
||||
bool allow_level_II_oplock,
|
||||
int *fd, bool allow_level_II_oplock,
|
||||
uint32_t oplock_level, uint32_t *oplock_granted)
|
||||
{
|
||||
return ops->odb_open_file(lck, file_handle, path,
|
||||
allow_level_II_oplock,
|
||||
fd, allow_level_II_oplock,
|
||||
oplock_level, oplock_granted);
|
||||
}
|
||||
|
||||
|
@ -27,7 +27,7 @@ struct opendb_ops {
|
||||
DATA_BLOB (*odb_get_key)(TALLOC_CTX *mem_ctx, struct odb_lock *lck);
|
||||
NTSTATUS (*odb_open_file)(struct odb_lock *lck,
|
||||
void *file_handle, const char *path,
|
||||
bool allow_level_II_oplock,
|
||||
int *fd, bool allow_level_II_oplock,
|
||||
uint32_t oplock_level, uint32_t *oplock_granted);
|
||||
NTSTATUS (*odb_open_file_pending)(struct odb_lock *lck, void *private);
|
||||
NTSTATUS (*odb_close_file)(struct odb_lock *lck, void *file_handle,
|
||||
|
@ -49,11 +49,13 @@
|
||||
#include "ntvfs/common/ntvfs_common.h"
|
||||
#include "cluster/cluster.h"
|
||||
#include "param/param.h"
|
||||
#include "ntvfs/sysdep/sys_lease.h"
|
||||
|
||||
struct odb_context {
|
||||
struct tdb_wrap *w;
|
||||
struct ntvfs_context *ntvfs_ctx;
|
||||
bool oplocks;
|
||||
struct sys_lease_context *lease_ctx;
|
||||
};
|
||||
|
||||
/*
|
||||
@ -72,6 +74,10 @@ struct odb_lock {
|
||||
} can_open;
|
||||
};
|
||||
|
||||
static NTSTATUS odb_oplock_break_send(struct messaging_context *msg_ctx,
|
||||
struct opendb_entry *e,
|
||||
uint8_t level);
|
||||
|
||||
/*
|
||||
Open up the openfiles.tdb database. Close it down using
|
||||
talloc_free(). We need the messaging_ctx to allow for pending open
|
||||
@ -98,6 +104,11 @@ static struct odb_context *odb_tdb_init(TALLOC_CTX *mem_ctx,
|
||||
/* leave oplocks disabled by default until the code is working */
|
||||
odb->oplocks = lp_parm_bool(ntvfs_ctx->lp_ctx, NULL, "opendb", "oplocks", false);
|
||||
|
||||
odb->lease_ctx = sys_lease_context_create(ntvfs_ctx->config, odb,
|
||||
ntvfs_ctx->event_ctx,
|
||||
ntvfs_ctx->msg_ctx,
|
||||
odb_oplock_break_send);
|
||||
|
||||
return odb;
|
||||
}
|
||||
|
||||
@ -442,7 +453,7 @@ static NTSTATUS odb_tdb_open_can_internal(struct odb_context *odb,
|
||||
*/
|
||||
static NTSTATUS odb_tdb_open_file(struct odb_lock *lck,
|
||||
void *file_handle, const char *path,
|
||||
bool allow_level_II_oplock,
|
||||
int *fd, bool allow_level_II_oplock,
|
||||
uint32_t oplock_level, uint32_t *oplock_granted)
|
||||
{
|
||||
struct odb_context *odb = lck->odb;
|
||||
@ -491,22 +502,29 @@ static NTSTATUS odb_tdb_open_file(struct odb_lock *lck,
|
||||
oplock_level = OPLOCK_NONE;
|
||||
}
|
||||
|
||||
lck->can_open.e->file_handle = file_handle;
|
||||
lck->can_open.e->fd = fd;
|
||||
lck->can_open.e->allow_level_II_oplock = allow_level_II_oplock;
|
||||
lck->can_open.e->oplock_level = oplock_level;
|
||||
|
||||
if (odb->lease_ctx && fd) {
|
||||
NTSTATUS status;
|
||||
status = sys_lease_setup(odb->lease_ctx, lck->can_open.e);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
}
|
||||
|
||||
if (oplock_granted) {
|
||||
if (oplock_level == OPLOCK_EXCLUSIVE) {
|
||||
if (lck->can_open.e->oplock_level == OPLOCK_EXCLUSIVE) {
|
||||
*oplock_granted = EXCLUSIVE_OPLOCK_RETURN;
|
||||
} else if (oplock_level == OPLOCK_BATCH) {
|
||||
} else if (lck->can_open.e->oplock_level == OPLOCK_BATCH) {
|
||||
*oplock_granted = BATCH_OPLOCK_RETURN;
|
||||
} else if (oplock_level == OPLOCK_LEVEL_II) {
|
||||
} else if (lck->can_open.e->oplock_level == OPLOCK_LEVEL_II) {
|
||||
*oplock_granted = LEVEL_II_OPLOCK_RETURN;
|
||||
} else {
|
||||
*oplock_granted = NO_OPLOCK_RETURN;
|
||||
}
|
||||
}
|
||||
|
||||
lck->can_open.e->file_handle = file_handle;
|
||||
lck->can_open.e->allow_level_II_oplock = allow_level_II_oplock;
|
||||
lck->can_open.e->oplock_level = oplock_level;
|
||||
|
||||
/* it doesn't conflict, so add it to the end */
|
||||
lck->file.entries = talloc_realloc(lck, lck->file.entries,
|
||||
struct opendb_entry,
|
||||
@ -569,6 +587,11 @@ static NTSTATUS odb_tdb_close_file(struct odb_lock *lck, void *file_handle,
|
||||
if (lck->file.entries[i].delete_on_close) {
|
||||
lck->file.delete_on_close = true;
|
||||
}
|
||||
if (odb->lease_ctx && lck->file.entries[i].fd) {
|
||||
NTSTATUS status;
|
||||
status = sys_lease_remove(odb->lease_ctx, &lck->file.entries[i]);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
}
|
||||
if (i < lck->file.num_entries-1) {
|
||||
memmove(lck->file.entries+i, lck->file.entries+i+1,
|
||||
(lck->file.num_entries - (i+1)) *
|
||||
@ -622,6 +645,13 @@ static NTSTATUS odb_tdb_update_oplock(struct odb_lock *lck, void *file_handle,
|
||||
if (file_handle == lck->file.entries[i].file_handle &&
|
||||
cluster_id_equal(&odb->ntvfs_ctx->server_id, &lck->file.entries[i].server)) {
|
||||
lck->file.entries[i].oplock_level = oplock_level;
|
||||
|
||||
if (odb->lease_ctx && lck->file.entries[i].fd) {
|
||||
NTSTATUS status;
|
||||
status = sys_lease_update(odb->lease_ctx, &lck->file.entries[i]);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -800,6 +830,7 @@ static NTSTATUS odb_tdb_can_open(struct odb_lock *lck,
|
||||
|
||||
lck->can_open.e->server = odb->ntvfs_ctx->server_id;
|
||||
lck->can_open.e->file_handle = NULL;
|
||||
lck->can_open.e->fd = NULL;
|
||||
lck->can_open.e->stream_id = stream_id;
|
||||
lck->can_open.e->share_access = share_access;
|
||||
lck->can_open.e->access_mask = access_mask;
|
||||
@ -831,5 +862,6 @@ static const struct opendb_ops opendb_tdb_ops = {
|
||||
|
||||
void odb_tdb_init_ops(void)
|
||||
{
|
||||
sys_lease_init();
|
||||
odb_set_ops(&opendb_tdb_ops);
|
||||
}
|
||||
|
@ -300,7 +300,7 @@ static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs,
|
||||
|
||||
/* now really mark the file as open */
|
||||
status = odb_open_file(lck, f->handle, name->full_name,
|
||||
false, OPLOCK_NONE, NULL);
|
||||
NULL, false, OPLOCK_NONE, NULL);
|
||||
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
talloc_free(lck);
|
||||
@ -360,7 +360,7 @@ static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs,
|
||||
}
|
||||
|
||||
status = odb_open_file(lck, f->handle, name->full_name,
|
||||
false, OPLOCK_NONE, NULL);
|
||||
NULL, false, OPLOCK_NONE, NULL);
|
||||
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
goto cleanup_delete;
|
||||
@ -600,7 +600,7 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
|
||||
mode = pvfs_fileperms(pvfs, attrib);
|
||||
|
||||
/* create the file */
|
||||
fd = open(name->full_name, flags | O_CREAT | O_EXCL, mode);
|
||||
fd = open(name->full_name, flags | O_CREAT | O_EXCL| O_NONBLOCK, mode);
|
||||
if (fd == -1) {
|
||||
return pvfs_map_errno(pvfs, errno);
|
||||
}
|
||||
@ -688,19 +688,6 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
|
||||
return status;
|
||||
}
|
||||
|
||||
status = odb_open_file(lck, f->handle, name->full_name,
|
||||
allow_level_II_oplock,
|
||||
oplock_level, &oplock_granted);
|
||||
talloc_free(lck);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
/* bad news, we must have hit a race - we don't delete the file
|
||||
here as the most likely scenario is that someone else created
|
||||
the file at the same time */
|
||||
close(fd);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
f->ntvfs = h;
|
||||
f->pvfs = pvfs;
|
||||
f->pending_list = NULL;
|
||||
@ -723,6 +710,18 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
|
||||
f->handle->sticky_write_time = false;
|
||||
f->handle->open_completed = false;
|
||||
|
||||
status = odb_open_file(lck, f->handle, name->full_name,
|
||||
&f->handle->fd, allow_level_II_oplock,
|
||||
oplock_level, &oplock_granted);
|
||||
talloc_free(lck);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
/* bad news, we must have hit a race - we don't delete the file
|
||||
here as the most likely scenario is that someone else created
|
||||
the file at the same time */
|
||||
close(fd);
|
||||
return status;
|
||||
}
|
||||
|
||||
DLIST_ADD(pvfs->files.list, f);
|
||||
|
||||
/* setup a destructor to avoid file descriptor leaks on
|
||||
@ -859,7 +858,13 @@ NTSTATUS pvfs_odb_retry_setup(struct ntvfs_module_context *ntvfs,
|
||||
|
||||
/* setup a pending lock */
|
||||
status = odb_open_file_pending(lck, r);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
if (NT_STATUS_EQUAL(NT_STATUS_OBJECT_NAME_NOT_FOUND,status)) {
|
||||
/*
|
||||
* maybe only a unix application
|
||||
* has the file open
|
||||
*/
|
||||
data_blob_free(&r->odb_locking_key);
|
||||
} else if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
@ -892,8 +897,14 @@ static void pvfs_retry_open_sharing(struct pvfs_odb_retry *r,
|
||||
enum pvfs_wait_notice reason)
|
||||
{
|
||||
union smb_open *io = talloc_get_type(_io, union smb_open);
|
||||
struct timeval *final_timeout = NULL;
|
||||
NTSTATUS status;
|
||||
|
||||
if (private_data) {
|
||||
final_timeout = talloc_get_type(private_data,
|
||||
struct timeval);
|
||||
}
|
||||
|
||||
/* w2k3 ignores SMBntcancel for outstanding open requests. It's probably
|
||||
just a bug in their server, but we better do the same */
|
||||
if (reason == PVFS_WAIT_CANCEL) {
|
||||
@ -901,6 +912,16 @@ static void pvfs_retry_open_sharing(struct pvfs_odb_retry *r,
|
||||
}
|
||||
|
||||
if (reason == PVFS_WAIT_TIMEOUT) {
|
||||
if (final_timeout &&
|
||||
!timeval_expired(final_timeout)) {
|
||||
/*
|
||||
* we need to retry periodictly
|
||||
* after an EAGAIN as there's
|
||||
* no way the kernel tell us
|
||||
* an oplock is released.
|
||||
*/
|
||||
goto retry;
|
||||
}
|
||||
/* if it timed out, then give the failure
|
||||
immediately */
|
||||
talloc_free(r);
|
||||
@ -909,6 +930,7 @@ static void pvfs_retry_open_sharing(struct pvfs_odb_retry *r,
|
||||
return;
|
||||
}
|
||||
|
||||
retry:
|
||||
talloc_free(r);
|
||||
|
||||
/* try the open again, which could trigger another retry setup
|
||||
@ -1028,6 +1050,7 @@ static NTSTATUS pvfs_open_setup_retry(struct ntvfs_module_context *ntvfs,
|
||||
struct pvfs_state *pvfs = ntvfs->private_data;
|
||||
NTSTATUS status;
|
||||
struct timeval end_time;
|
||||
struct timeval *final_timeout = NULL;
|
||||
|
||||
if (io->generic.in.create_options &
|
||||
(NTCREATEX_OPTIONS_PRIVATE_DENY_DOS | NTCREATEX_OPTIONS_PRIVATE_DENY_FCB)) {
|
||||
@ -1048,12 +1071,28 @@ static NTSTATUS pvfs_open_setup_retry(struct ntvfs_module_context *ntvfs,
|
||||
} else if (NT_STATUS_EQUAL(parent_status, NT_STATUS_OPLOCK_NOT_GRANTED)) {
|
||||
end_time = timeval_add(&req->statistics.request_time,
|
||||
pvfs->oplock_break_timeout, 0);
|
||||
} else if (NT_STATUS_EQUAL(parent_status, STATUS_MORE_ENTRIES)) {
|
||||
/*
|
||||
* we got EAGAIN which means a unix application
|
||||
* has an oplock or share mode
|
||||
*
|
||||
* we retry every 4/5 of the sharing violation delay
|
||||
* to see if the unix application
|
||||
* has released the oplock or share mode.
|
||||
*/
|
||||
final_timeout = talloc(req, struct timeval);
|
||||
NT_STATUS_HAVE_NO_MEMORY(final_timeout);
|
||||
*final_timeout = timeval_add(&req->statistics.request_time,
|
||||
pvfs->oplock_break_timeout,
|
||||
0);
|
||||
end_time = timeval_current_ofs(0, (pvfs->sharing_violation_delay*4)/5);
|
||||
end_time = timeval_min(final_timeout, &end_time);
|
||||
} else {
|
||||
return NT_STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
return pvfs_odb_retry_setup(ntvfs, req, lck, end_time, io, NULL,
|
||||
pvfs_retry_open_sharing);
|
||||
return pvfs_odb_retry_setup(ntvfs, req, lck, end_time, io,
|
||||
final_timeout, pvfs_retry_open_sharing);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1297,9 +1336,34 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
|
||||
return status;
|
||||
}
|
||||
|
||||
if (access_mask & (SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA)) {
|
||||
flags |= O_RDWR;
|
||||
} else {
|
||||
flags |= O_RDONLY;
|
||||
}
|
||||
|
||||
/* do the actual open */
|
||||
fd = open(f->handle->name->full_name, flags | O_NONBLOCK);
|
||||
if (fd == -1) {
|
||||
status = pvfs_map_errno(f->pvfs, errno);
|
||||
|
||||
/*
|
||||
* STATUS_MORE_ENTRIES is EAGAIN or EWOULDBLOCK
|
||||
*/
|
||||
if (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES) &&
|
||||
(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
|
||||
return pvfs_open_setup_retry(ntvfs, req, io, f, lck, status);
|
||||
}
|
||||
|
||||
talloc_free(lck);
|
||||
return status;
|
||||
}
|
||||
|
||||
f->handle->fd = fd;
|
||||
|
||||
/* now really mark the file as open */
|
||||
status = odb_open_file(lck, f->handle, name->full_name,
|
||||
allow_level_II_oplock,
|
||||
&f->handle->fd, allow_level_II_oplock,
|
||||
oplock_level, &oplock_granted);
|
||||
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
@ -1319,21 +1383,6 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
|
||||
|
||||
f->handle->have_opendb_entry = true;
|
||||
|
||||
if (access_mask & (SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA)) {
|
||||
flags |= O_RDWR;
|
||||
} else {
|
||||
flags |= O_RDONLY;
|
||||
}
|
||||
|
||||
/* do the actual open */
|
||||
fd = open(f->handle->name->full_name, flags);
|
||||
if (fd == -1) {
|
||||
talloc_free(lck);
|
||||
return pvfs_map_errno(f->pvfs, errno);
|
||||
}
|
||||
|
||||
f->handle->fd = fd;
|
||||
|
||||
stream_existed = name->stream_exists;
|
||||
|
||||
/* if this was a stream create then create the stream as well */
|
||||
|
@ -568,6 +568,9 @@ static NTSTATUS pvfs_rename_nt(struct ntvfs_module_context *ntvfs,
|
||||
NTSTATUS pvfs_rename(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_rename *ren)
|
||||
{
|
||||
struct pvfs_state *pvfs = ntvfs->private_data;
|
||||
struct pvfs_file *f;
|
||||
|
||||
switch (ren->generic.level) {
|
||||
case RAW_RENAME_RENAME:
|
||||
return pvfs_rename_mv(ntvfs, req, ren);
|
||||
@ -576,7 +579,13 @@ NTSTATUS pvfs_rename(struct ntvfs_module_context *ntvfs,
|
||||
return pvfs_rename_nt(ntvfs, req, ren);
|
||||
|
||||
case RAW_RENAME_NTTRANS:
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
f = pvfs_find_fd(pvfs, req, ren->nttrans.in.file.ntvfs);
|
||||
if (!f) {
|
||||
return NT_STATUS_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
/* wk23 ignores the request */
|
||||
return NT_STATUS_OK;
|
||||
|
||||
default:
|
||||
break;
|
||||
|
@ -11,3 +11,13 @@ fi
|
||||
if test x"$ac_cv_header_linux_inotify_h" = x"yes" -a x"$ac_cv_have___NR_inotify_init_decl" = x"yes"; then
|
||||
SMB_ENABLE(sys_notify_inotify, YES)
|
||||
fi
|
||||
|
||||
AC_HAVE_DECL(F_SETLEASE, [#include <fcntl.h>])
|
||||
AC_HAVE_DECL(SA_SIGINFO, [#include <signal.h>])
|
||||
|
||||
SMB_ENABLE(sys_lease_linux, NO)
|
||||
|
||||
if test x"$ac_cv_have_F_SETLEASE_decl" = x"yes" \
|
||||
-a x"$ac_cv_have_SA_SIGINFO_decl" = x"yes"; then
|
||||
SMB_ENABLE(sys_lease_linux, YES)
|
||||
fi
|
||||
|
@ -16,3 +16,21 @@ OBJ_FILES = \
|
||||
PUBLIC_DEPENDENCIES =
|
||||
# End SUBSYSTEM sys_notify
|
||||
################################################
|
||||
|
||||
################################################
|
||||
# Start MODULE sys_lease_linux
|
||||
[MODULE::sys_lease_linux]
|
||||
SUBSYSTEM = sys_lease
|
||||
INIT_FUNCTION = sys_lease_linux_init
|
||||
OBJ_FILES = \
|
||||
sys_lease_linux.o
|
||||
# End MODULE sys_lease_linux
|
||||
################################################
|
||||
|
||||
################################################
|
||||
# Start SUBSYSTEM sys_lease
|
||||
[SUBSYSTEM::sys_lease]
|
||||
OBJ_FILES = \
|
||||
sys_lease.o
|
||||
# End SUBSYSTEM sys_lease
|
||||
################################################
|
||||
|
142
source4/ntvfs/sysdep/sys_lease.c
Normal file
142
source4/ntvfs/sysdep/sys_lease.c
Normal file
@ -0,0 +1,142 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
Copyright (C) Stefan Metzmacher 2008
|
||||
|
||||
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 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
abstract the various kernel interfaces to leases (oplocks) into a
|
||||
single Samba friendly interface
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "system/filesys.h"
|
||||
#include "ntvfs/sysdep/sys_lease.h"
|
||||
#include "lib/events/events.h"
|
||||
#include "lib/util/dlinklist.h"
|
||||
#include "param/param.h"
|
||||
#include "build.h"
|
||||
|
||||
/* list of registered backends */
|
||||
static struct sys_lease_ops *backends;
|
||||
static uint32_t num_backends;
|
||||
|
||||
#define LEASE_BACKEND "lease:backend"
|
||||
|
||||
/*
|
||||
initialise a system change notify backend
|
||||
*/
|
||||
_PUBLIC_ struct sys_lease_context *sys_lease_context_create(struct share_config *scfg,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
struct event_context *ev,
|
||||
struct messaging_context *msg,
|
||||
sys_lease_send_break_fn break_send)
|
||||
{
|
||||
struct sys_lease_context *ctx;
|
||||
const char *bname;
|
||||
int i;
|
||||
NTSTATUS status;
|
||||
|
||||
if (num_backends == 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (ev == NULL) {
|
||||
ev = event_context_find(mem_ctx);
|
||||
}
|
||||
|
||||
ctx = talloc_zero(mem_ctx, struct sys_lease_context);
|
||||
if (ctx == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ctx->event_ctx = ev;
|
||||
ctx->msg_ctx = msg;
|
||||
ctx->break_send = break_send;
|
||||
|
||||
bname = share_string_option(scfg, LEASE_BACKEND, NULL);
|
||||
if (!bname) {
|
||||
talloc_free(ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i=0;i<num_backends;i++) {
|
||||
if (strcasecmp(backends[i].name, bname) == 0) {
|
||||
ctx->ops = &backends[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ctx->ops) {
|
||||
talloc_free(ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
status = ctx->ops->init(ctx);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
talloc_free(ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
/*
|
||||
register a lease backend
|
||||
*/
|
||||
_PUBLIC_ NTSTATUS sys_lease_register(const struct sys_lease_ops *backend)
|
||||
{
|
||||
struct sys_lease_ops *b;
|
||||
b = talloc_realloc(talloc_autofree_context(), backends,
|
||||
struct sys_lease_ops, num_backends+1);
|
||||
NT_STATUS_HAVE_NO_MEMORY(b);
|
||||
backends = b;
|
||||
backends[num_backends] = *backend;
|
||||
num_backends++;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
_PUBLIC_ NTSTATUS sys_lease_init(void)
|
||||
{
|
||||
static bool initialized = false;
|
||||
|
||||
init_module_fn static_init[] = { STATIC_sys_lease_MODULES };
|
||||
|
||||
if (initialized) return NT_STATUS_OK;
|
||||
initialized = true;
|
||||
|
||||
run_init_functions(static_init);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
NTSTATUS sys_lease_setup(struct sys_lease_context *ctx,
|
||||
struct opendb_entry *e)
|
||||
{
|
||||
return ctx->ops->setup(ctx, e);
|
||||
}
|
||||
|
||||
NTSTATUS sys_lease_update(struct sys_lease_context *ctx,
|
||||
struct opendb_entry *e)
|
||||
{
|
||||
return ctx->ops->update(ctx, e);
|
||||
}
|
||||
|
||||
NTSTATUS sys_lease_remove(struct sys_lease_context *ctx,
|
||||
struct opendb_entry *e)
|
||||
{
|
||||
return ctx->ops->remove(ctx, e);
|
||||
}
|
65
source4/ntvfs/sysdep/sys_lease.h
Normal file
65
source4/ntvfs/sysdep/sys_lease.h
Normal file
@ -0,0 +1,65 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
Copyright (C) Stefan Metzmacher 2008
|
||||
|
||||
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 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "param/share.h"
|
||||
|
||||
struct sys_lease_context;
|
||||
struct opendb_entry;
|
||||
struct messaging_context;
|
||||
|
||||
typedef NTSTATUS (*sys_lease_send_break_fn)(struct messaging_context *,
|
||||
struct opendb_entry *,
|
||||
uint8_t level);
|
||||
|
||||
struct sys_lease_ops {
|
||||
const char *name;
|
||||
NTSTATUS (*init)(struct sys_lease_context *ctx);
|
||||
NTSTATUS (*setup)(struct sys_lease_context *ctx,
|
||||
struct opendb_entry *e);
|
||||
NTSTATUS (*update)(struct sys_lease_context *ctx,
|
||||
struct opendb_entry *e);
|
||||
NTSTATUS (*remove)(struct sys_lease_context *ctx,
|
||||
struct opendb_entry *e);
|
||||
};
|
||||
|
||||
struct sys_lease_context {
|
||||
struct event_context *event_ctx;
|
||||
struct messaging_context *msg_ctx;
|
||||
sys_lease_send_break_fn break_send;
|
||||
void *private_data; /* for use of backend */
|
||||
const struct sys_lease_ops *ops;
|
||||
};
|
||||
|
||||
NTSTATUS sys_lease_register(const struct sys_lease_ops *ops);
|
||||
NTSTATUS sys_lease_init(void);
|
||||
|
||||
struct sys_lease_context *sys_lease_context_create(struct share_config *scfg,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
struct event_context *ev,
|
||||
struct messaging_context *msg_ctx,
|
||||
sys_lease_send_break_fn break_send);
|
||||
|
||||
NTSTATUS sys_lease_setup(struct sys_lease_context *ctx,
|
||||
struct opendb_entry *e);
|
||||
|
||||
NTSTATUS sys_lease_update(struct sys_lease_context *ctx,
|
||||
struct opendb_entry *e);
|
||||
|
||||
NTSTATUS sys_lease_remove(struct sys_lease_context *ctx,
|
||||
struct opendb_entry *e);
|
213
source4/ntvfs/sysdep/sys_lease_linux.c
Normal file
213
source4/ntvfs/sysdep/sys_lease_linux.c
Normal file
@ -0,0 +1,213 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
Copyright (C) Stefan Metzmacher 2008
|
||||
|
||||
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 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
lease (oplock) implementation using fcntl F_SETLEASE on linux
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "system/filesys.h"
|
||||
#include "lib/events/events.h"
|
||||
#include "ntvfs/sysdep/sys_lease.h"
|
||||
#include "ntvfs/ntvfs.h"
|
||||
#include "librpc/gen_ndr/ndr_opendb.h"
|
||||
#include "lib/util/dlinklist.h"
|
||||
#include "cluster/cluster.h"
|
||||
|
||||
#define LINUX_LEASE_RT_SIGNAL (SIGRTMIN+1)
|
||||
|
||||
struct linux_lease_pending {
|
||||
struct linux_lease_pending *prev, *next;
|
||||
struct sys_lease_context *ctx;
|
||||
struct opendb_entry e;
|
||||
};
|
||||
|
||||
/* the global linked list of pending leases */
|
||||
static struct linux_lease_pending *leases;
|
||||
|
||||
static void linux_lease_signal_handler(struct event_context *ev_ctx,
|
||||
struct signal_event *se,
|
||||
int signum, int count,
|
||||
void *_info, void *private_data)
|
||||
{
|
||||
struct sys_lease_context *ctx = talloc_get_type(private_data,
|
||||
struct sys_lease_context);
|
||||
siginfo_t *info = (siginfo_t *)_info;
|
||||
struct linux_lease_pending *c;
|
||||
int got_fd = info->si_fd;
|
||||
|
||||
for (c = leases; c; c = c->next) {
|
||||
int *fd = (int *)c->e.fd;
|
||||
|
||||
if (got_fd == *fd) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!c) {
|
||||
return;
|
||||
}
|
||||
|
||||
ctx->break_send(ctx->msg_ctx, &c->e, OPLOCK_BREAK_TO_NONE);
|
||||
}
|
||||
|
||||
static int linux_lease_pending_destructor(struct linux_lease_pending *p)
|
||||
{
|
||||
int ret;
|
||||
int *fd = (int *)p->e.fd;
|
||||
|
||||
DLIST_REMOVE(leases, p);
|
||||
|
||||
if (*fd == -1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = fcntl(*fd, F_SETLEASE, F_UNLCK);
|
||||
if (ret == -1) {
|
||||
DEBUG(0,("%s: failed to remove oplock: %s\n",
|
||||
__FUNCTION__, strerror(errno)));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static NTSTATUS linux_lease_init(struct sys_lease_context *ctx)
|
||||
{
|
||||
struct signal_event *se;
|
||||
|
||||
se = event_add_signal(ctx->event_ctx, ctx,
|
||||
LINUX_LEASE_RT_SIGNAL, SA_SIGINFO,
|
||||
linux_lease_signal_handler, ctx);
|
||||
NT_STATUS_HAVE_NO_MEMORY(se);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static NTSTATUS linux_lease_setup(struct sys_lease_context *ctx,
|
||||
struct opendb_entry *e)
|
||||
{
|
||||
int ret;
|
||||
int *fd = (int *)e->fd;
|
||||
struct linux_lease_pending *p;
|
||||
|
||||
if (e->oplock_level == OPLOCK_NONE) {
|
||||
e->fd = NULL;
|
||||
return NT_STATUS_OK;
|
||||
} else if (e->oplock_level == OPLOCK_LEVEL_II) {
|
||||
/*
|
||||
* the linux kernel doesn't support level2 oplocks
|
||||
* so fix up the granted oplock level
|
||||
*/
|
||||
e->oplock_level = OPLOCK_NONE;
|
||||
e->allow_level_II_oplock = false;
|
||||
e->fd = NULL;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
p = talloc(ctx, struct linux_lease_pending);
|
||||
NT_STATUS_HAVE_NO_MEMORY(p);
|
||||
|
||||
p->ctx = ctx;
|
||||
p->e = *e;
|
||||
|
||||
ret = fcntl(*fd, F_SETSIG, LINUX_LEASE_RT_SIGNAL);
|
||||
if (ret == -1) {
|
||||
talloc_free(p);
|
||||
return map_nt_error_from_unix(errno);
|
||||
}
|
||||
|
||||
ret = fcntl(*fd, F_SETLEASE, F_WRLCK);
|
||||
if (ret == -1) {
|
||||
talloc_free(p);
|
||||
return map_nt_error_from_unix(errno);
|
||||
}
|
||||
|
||||
DLIST_ADD(leases, p);
|
||||
|
||||
talloc_set_destructor(p, linux_lease_pending_destructor);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static NTSTATUS linux_lease_remove(struct sys_lease_context *ctx,
|
||||
struct opendb_entry *e);
|
||||
|
||||
static NTSTATUS linux_lease_update(struct sys_lease_context *ctx,
|
||||
struct opendb_entry *e)
|
||||
{
|
||||
struct linux_lease_pending *c;
|
||||
|
||||
for (c = leases; c; c = c->next) {
|
||||
if (c->e.fd == e->fd) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!c) {
|
||||
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
||||
}
|
||||
|
||||
/*
|
||||
* set the fd pointer to NULL so that the caller
|
||||
* will not call the remove function as the oplock
|
||||
* is already removed
|
||||
*/
|
||||
e->fd = NULL;
|
||||
|
||||
talloc_free(c);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static NTSTATUS linux_lease_remove(struct sys_lease_context *ctx,
|
||||
struct opendb_entry *e)
|
||||
{
|
||||
struct linux_lease_pending *c;
|
||||
|
||||
for (c = leases; c; c = c->next) {
|
||||
if (c->e.fd == e->fd) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!c) {
|
||||
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
||||
}
|
||||
|
||||
talloc_free(c);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static struct sys_lease_ops linux_lease_ops = {
|
||||
.name = "linux",
|
||||
.init = linux_lease_init,
|
||||
.setup = linux_lease_setup,
|
||||
.update = linux_lease_update,
|
||||
.remove = linux_lease_remove
|
||||
};
|
||||
|
||||
/*
|
||||
initialialise the linux lease module
|
||||
*/
|
||||
NTSTATUS sys_lease_linux_init(void)
|
||||
{
|
||||
/* register ourselves as a system lease module */
|
||||
return sys_lease_register(&linux_lease_ops);
|
||||
}
|
@ -2218,7 +2218,7 @@ bool test_nttransrename(struct torture_context *tctx, struct smbcli_state *cli1)
|
||||
NTSTATUS status;
|
||||
bool ret = true;
|
||||
union smb_open io;
|
||||
union smb_fileinfo qfi;
|
||||
union smb_fileinfo qfi, qpi;
|
||||
union smb_rename rn;
|
||||
uint16_t fnum=0;
|
||||
|
||||
@ -2237,7 +2237,7 @@ bool test_nttransrename(struct torture_context *tctx, struct smbcli_state *cli1)
|
||||
*/
|
||||
io.generic.level = RAW_OPEN_NTCREATEX;
|
||||
io.ntcreatex.in.root_fid = 0;
|
||||
io.ntcreatex.in.access_mask = SEC_RIGHTS_FILE_ALL;
|
||||
io.ntcreatex.in.access_mask = 0;/* ask for no access at all */;
|
||||
io.ntcreatex.in.alloc_size = 0;
|
||||
io.ntcreatex.in.file_attr = FILE_ATTRIBUTE_NORMAL;
|
||||
io.ntcreatex.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
|
||||
@ -2256,7 +2256,7 @@ bool test_nttransrename(struct torture_context *tctx, struct smbcli_state *cli1)
|
||||
fnum = io.ntcreatex.out.file.fnum;
|
||||
CHECK_VAL(io.ntcreatex.out.oplock_level, EXCLUSIVE_OPLOCK_RETURN);
|
||||
|
||||
torture_comment(tctx, "nttrans_rename should not trigger a break nor a share mode violation\n");
|
||||
torture_comment(tctx, "nttrans_rename: should not trigger a break nor a share mode violation\n");
|
||||
ZERO_STRUCT(rn);
|
||||
rn.generic.level = RAW_RENAME_NTTRANS;
|
||||
rn.nttrans.in.file.fnum = fnum;
|
||||
@ -2268,25 +2268,60 @@ bool test_nttransrename(struct torture_context *tctx, struct smbcli_state *cli1)
|
||||
CHECK_STATUS(tctx, status, NT_STATUS_OK);
|
||||
CHECK_VAL(break_info.count, 0);
|
||||
|
||||
/* Seems name is only updated on close. */
|
||||
smbcli_close(cli1->tree, fnum);
|
||||
|
||||
/* Check if we can open with the new name. */
|
||||
io.ntcreatex.in.fname = fname2;
|
||||
status = smb_raw_open(cli1->tree, tctx, &io);
|
||||
CHECK_STATUS(tctx, status, NT_STATUS_OK);
|
||||
fnum = io.ntcreatex.out.file.fnum;
|
||||
CHECK_VAL(io.ntcreatex.out.oplock_level, EXCLUSIVE_OPLOCK_RETURN);
|
||||
|
||||
/* w2k3 does nothing, it doesn't rename the file */
|
||||
torture_comment(tctx, "nttrans_rename: the server should have done nothing\n");
|
||||
ZERO_STRUCT(qfi);
|
||||
qfi.generic.level = RAW_FILEINFO_ALL_INFORMATION;
|
||||
qfi.generic.in.file.fnum = fnum;
|
||||
|
||||
status = smb_raw_fileinfo(cli1->tree, tctx, &qfi);
|
||||
CHECK_STATUS(tctx, status, NT_STATUS_OK);
|
||||
CHECK_STRMATCH(qfi.all_info.out.fname.s, fname2);
|
||||
CHECK_STRMATCH(qfi.all_info.out.fname.s, fname1);
|
||||
|
||||
smbcli_close(cli1->tree, fnum);
|
||||
ZERO_STRUCT(qpi);
|
||||
qpi.generic.level = RAW_FILEINFO_ALL_INFORMATION;
|
||||
qpi.generic.in.file.path = fname1;
|
||||
|
||||
status = smb_raw_pathinfo(cli1->tree, tctx, &qpi);
|
||||
CHECK_STATUS(tctx, status, NT_STATUS_OK);
|
||||
CHECK_STRMATCH(qpi.all_info.out.fname.s, fname1);
|
||||
|
||||
ZERO_STRUCT(qpi);
|
||||
qpi.generic.level = RAW_FILEINFO_ALL_INFORMATION;
|
||||
qpi.generic.in.file.path = fname2;
|
||||
|
||||
status = smb_raw_pathinfo(cli1->tree, tctx, &qpi);
|
||||
CHECK_STATUS(tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
|
||||
|
||||
torture_comment(tctx, "nttrans_rename: after closing the file the file is still not renamed\n");
|
||||
status = smbcli_close(cli1->tree, fnum);
|
||||
CHECK_STATUS(tctx, status, NT_STATUS_OK);
|
||||
|
||||
ZERO_STRUCT(qpi);
|
||||
qpi.generic.level = RAW_FILEINFO_ALL_INFORMATION;
|
||||
qpi.generic.in.file.path = fname1;
|
||||
|
||||
status = smb_raw_pathinfo(cli1->tree, tctx, &qpi);
|
||||
CHECK_STATUS(tctx, status, NT_STATUS_OK);
|
||||
CHECK_STRMATCH(qpi.all_info.out.fname.s, fname1);
|
||||
|
||||
ZERO_STRUCT(qpi);
|
||||
qpi.generic.level = RAW_FILEINFO_ALL_INFORMATION;
|
||||
qpi.generic.in.file.path = fname2;
|
||||
|
||||
status = smb_raw_pathinfo(cli1->tree, tctx, &qpi);
|
||||
CHECK_STATUS(tctx, status, NT_STATUS_OBJECT_NAME_NOT_FOUND);
|
||||
|
||||
torture_comment(tctx, "nttrans_rename: rename with an invalid handle gives NT_STATUS_INVALID_HANDLE\n");
|
||||
ZERO_STRUCT(rn);
|
||||
rn.generic.level = RAW_RENAME_NTTRANS;
|
||||
rn.nttrans.in.file.fnum = fnum+1;
|
||||
rn.nttrans.in.flags = 0;
|
||||
rn.nttrans.in.new_name = fname2+strlen(BASEDIR)+1;
|
||||
|
||||
status = smb_raw_rename(cli1->tree, &rn);
|
||||
|
||||
CHECK_STATUS(tctx, status, NT_STATUS_INVALID_HANDLE);
|
||||
|
||||
done:
|
||||
smb_raw_exit(cli1->session);
|
||||
|
@ -356,22 +356,24 @@ static bool test_stream_sharemodes(struct torture_context *tctx,
|
||||
* A different stream does not give a sharing violation
|
||||
*/
|
||||
|
||||
io.ntcreatex.in.fname = sname2;
|
||||
status = smb_raw_open(cli->tree, mem_ctx, &io);
|
||||
CHECK_STATUS(status, NT_STATUS_OK);
|
||||
fnum1 = io.ntcreatex.out.file.fnum;
|
||||
fnum2 = io.ntcreatex.out.file.fnum;
|
||||
|
||||
/*
|
||||
* ... whereas the same stream does with unchanged access/share_access
|
||||
* flags
|
||||
*/
|
||||
|
||||
io.ntcreatex.in.fname = sname1;
|
||||
io.ntcreatex.in.open_disposition = 0;
|
||||
status = smb_raw_open(cli->tree, mem_ctx, &io);
|
||||
CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
|
||||
|
||||
io.ntcreatex.in.fname = sname2;
|
||||
status = smb_raw_open(cli->tree, mem_ctx, &io);
|
||||
CHECK_STATUS(status, NT_STATUS_OK);
|
||||
fnum2 = io.ntcreatex.out.file.fnum;
|
||||
CHECK_STATUS(status, NT_STATUS_SHARING_VIOLATION);
|
||||
|
||||
done:
|
||||
if (fnum1 != -1) smbcli_close(cli->tree, fnum1);
|
||||
@ -558,7 +560,9 @@ bool torture_raw_streams(struct torture_context *torture,
|
||||
}
|
||||
|
||||
ret &= test_stream_io(torture, cli, torture);
|
||||
smb_raw_exit(cli->session);
|
||||
ret &= test_stream_sharemodes(torture, cli, torture);
|
||||
smb_raw_exit(cli->session);
|
||||
if (!torture_setting_bool(torture, "samba4", false)) {
|
||||
ret &= test_stream_delete(torture, cli, torture);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user