wmi-1.3.16 from opsview.com

This commit is contained in:
Are Casilla
2019-02-16 00:16:52 +01:00
parent 163fdd3d1b
commit 17b3af2911
2146 changed files with 678824 additions and 0 deletions
+688
View File
@@ -0,0 +1,688 @@
/*
Unix SMB/CIFS implementation.
generic byte range locking code
Copyright (C) Andrew Tridgell 1992-2004
Copyright (C) Jeremy Allison 1992-2000
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
(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.
*/
/* This module implements a tdb based byte range locking service,
replacing the fcntl() based byte range locking previously
used. This allows us to provide the same semantics as NT */
#include "includes.h"
#include "system/filesys.h"
#include "lib/tdb/include/tdb.h"
#include "messaging/messaging.h"
#include "db_wrap.h"
#include "lib/messaging/irpc.h"
#include "libcli/libcli.h"
/*
in this module a "DATA_BLOB *file_key" is a blob that uniquely identifies
a file. For a local posix filesystem this will usually be a combination
of the device and inode numbers of the file, but it can be anything
that uniquely idetifies a file for locking purposes, as long
as it is applied consistently.
*/
struct brl_context;
/*
the lock context contains the elements that define whether one
lock is the same as another lock
*/
struct lock_context {
uint32_t server;
uint16_t smbpid;
struct brl_context *ctx;
};
/* The data in brlock records is an unsorted linear array of these
records. It is unnecessary to store the count as tdb provides the
size of the record */
struct lock_struct {
struct lock_context context;
struct ntvfs_handle *ntvfs;
uint64_t start;
uint64_t size;
enum brl_type lock_type;
void *notify_ptr;
};
/* this struct is attached to on oprn file handle */
struct brl_handle {
DATA_BLOB key;
struct ntvfs_handle *ntvfs;
struct lock_struct last_lock;
};
/* this struct is typicaly attached to tcon */
struct brl_context {
struct tdb_wrap *w;
uint32_t server;
struct messaging_context *messaging_ctx;
};
/*
Open up the brlock.tdb database. Close it down using
talloc_free(). We need the messaging_ctx to allow for
pending lock notifications.
*/
struct brl_context *brl_init(TALLOC_CTX *mem_ctx, uint32_t server,
struct messaging_context *messaging_ctx)
{
char *path;
struct brl_context *brl;
brl = talloc(mem_ctx, struct brl_context);
if (brl == NULL) {
return NULL;
}
path = smbd_tmp_path(brl, "brlock.tdb");
brl->w = tdb_wrap_open(brl, path, 0,
TDB_DEFAULT, O_RDWR|O_CREAT, 0600);
talloc_free(path);
if (brl->w == NULL) {
talloc_free(brl);
return NULL;
}
brl->server = server;
brl->messaging_ctx = messaging_ctx;
return brl;
}
struct brl_handle *brl_create_handle(TALLOC_CTX *mem_ctx, struct ntvfs_handle *ntvfs, DATA_BLOB *file_key)
{
struct brl_handle *brlh;
brlh = talloc(mem_ctx, struct brl_handle);
if (brlh == NULL) {
return NULL;
}
brlh->key = *file_key;
brlh->ntvfs = ntvfs;
ZERO_STRUCT(brlh->last_lock);
return brlh;
}
/*
see if two locking contexts are equal
*/
static BOOL brl_same_context(struct lock_context *ctx1, struct lock_context *ctx2)
{
return (ctx1->server == ctx2->server &&
ctx1->smbpid == ctx2->smbpid &&
ctx1->ctx == ctx2->ctx);
}
/*
see if lck1 and lck2 overlap
*/
static BOOL brl_overlap(struct lock_struct *lck1,
struct lock_struct *lck2)
{
/* this extra check is not redundent - it copes with locks
that go beyond the end of 64 bit file space */
if (lck1->size != 0 &&
lck1->start == lck2->start &&
lck1->size == lck2->size) {
return True;
}
if (lck1->start >= (lck2->start+lck2->size) ||
lck2->start >= (lck1->start+lck1->size)) {
return False;
}
return True;
}
/*
See if lock2 can be added when lock1 is in place.
*/
static BOOL brl_conflict(struct lock_struct *lck1,
struct lock_struct *lck2)
{
/* pending locks don't conflict with anything */
if (lck1->lock_type >= PENDING_READ_LOCK ||
lck2->lock_type >= PENDING_READ_LOCK) {
return False;
}
if (lck1->lock_type == READ_LOCK && lck2->lock_type == READ_LOCK) {
return False;
}
if (brl_same_context(&lck1->context, &lck2->context) &&
lck2->lock_type == READ_LOCK && lck1->ntvfs == lck2->ntvfs) {
return False;
}
return brl_overlap(lck1, lck2);
}
/*
Check to see if this lock conflicts, but ignore our own locks on the
same fnum only.
*/
static BOOL brl_conflict_other(struct lock_struct *lck1, struct lock_struct *lck2)
{
/* pending locks don't conflict with anything */
if (lck1->lock_type >= PENDING_READ_LOCK ||
lck2->lock_type >= PENDING_READ_LOCK) {
return False;
}
if (lck1->lock_type == READ_LOCK && lck2->lock_type == READ_LOCK)
return False;
/*
* note that incoming write calls conflict with existing READ
* locks even if the context is the same. JRA. See LOCKTEST7
* in smbtorture.
*/
if (brl_same_context(&lck1->context, &lck2->context) &&
lck1->ntvfs == lck2->ntvfs &&
(lck2->lock_type == READ_LOCK || lck1->lock_type == WRITE_LOCK)) {
return False;
}
return brl_overlap(lck1, lck2);
}
/*
amazingly enough, w2k3 "remembers" whether the last lock failure
is the same as this one and changes its error code. I wonder if any
app depends on this?
*/
static NTSTATUS brl_lock_failed(struct brl_handle *brlh, struct lock_struct *lock)
{
/*
* this function is only called for non pending lock!
*/
/*
* if the notify_ptr is non NULL,
* it means that we're at the end of a pending lock
* and the real lock is requested after the timout went by
* In this case we need to remember the last_lock and always
* give FILE_LOCK_CONFLICT
*/
if (lock->notify_ptr) {
brlh->last_lock = *lock;
return NT_STATUS_FILE_LOCK_CONFLICT;
}
/*
* amazing the little things you learn with a test
* suite. Locks beyond this offset (as a 64 bit
* number!) always generate the conflict error code,
* unless the top bit is set
*/
if (lock->start >= 0xEF000000 && (lock->start >> 63) == 0) {
brlh->last_lock = *lock;
return NT_STATUS_FILE_LOCK_CONFLICT;
}
/*
* if the current lock matches the last failed lock on the file handle
* and starts at the same offset, then FILE_LOCK_CONFLICT should be returned
*/
if (lock->context.server == brlh->last_lock.context.server &&
lock->context.ctx == brlh->last_lock.context.ctx &&
lock->ntvfs == brlh->last_lock.ntvfs &&
lock->start == brlh->last_lock.start) {
return NT_STATUS_FILE_LOCK_CONFLICT;
}
brlh->last_lock = *lock;
return NT_STATUS_LOCK_NOT_GRANTED;
}
/*
Lock a range of bytes. The lock_type can be a PENDING_*_LOCK, in
which case a real lock is first tried, and if that fails then a
pending lock is created. When the pending lock is triggered (by
someone else closing an overlapping lock range) a messaging
notification is sent, identified by the notify_ptr
*/
NTSTATUS brl_lock(struct brl_context *brl,
struct brl_handle *brlh,
uint16_t smbpid,
uint64_t start, uint64_t size,
enum brl_type lock_type,
void *notify_ptr)
{
TDB_DATA kbuf, dbuf;
int count=0, i;
struct lock_struct lock, *locks=NULL;
NTSTATUS status;
kbuf.dptr = brlh->key.data;
kbuf.dsize = brlh->key.length;
if (tdb_chainlock(brl->w->tdb, kbuf) != 0) {
return NT_STATUS_INTERNAL_DB_CORRUPTION;
}
/* if this is a pending lock, then with the chainlock held we
try to get the real lock. If we succeed then we don't need
to make it pending. This prevents a possible race condition
where the pending lock gets created after the lock that is
preventing the real lock gets removed */
if (lock_type >= PENDING_READ_LOCK) {
enum brl_type rw = (lock_type==PENDING_READ_LOCK? READ_LOCK : WRITE_LOCK);
/* here we need to force that the last_lock isn't overwritten */
lock = brlh->last_lock;
status = brl_lock(brl, brlh, smbpid, start, size, rw, NULL);
brlh->last_lock = lock;
if (NT_STATUS_IS_OK(status)) {
tdb_chainunlock(brl->w->tdb, kbuf);
return NT_STATUS_OK;
}
}
dbuf = tdb_fetch(brl->w->tdb, kbuf);
lock.context.smbpid = smbpid;
lock.context.server = brl->server;
lock.context.ctx = brl;
lock.ntvfs = brlh->ntvfs;
lock.context.ctx = brl;
lock.start = start;
lock.size = size;
lock.lock_type = lock_type;
lock.notify_ptr = notify_ptr;
if (dbuf.dptr) {
/* there are existing locks - make sure they don't conflict */
locks = (struct lock_struct *)dbuf.dptr;
count = dbuf.dsize / sizeof(*locks);
for (i=0; i<count; i++) {
if (brl_conflict(&locks[i], &lock)) {
status = brl_lock_failed(brlh, &lock);
goto fail;
}
}
}
/* no conflicts - add it to the list of locks */
locks = realloc_p(locks, struct lock_struct, count+1);
if (!locks) {
status = NT_STATUS_NO_MEMORY;
goto fail;
} else {
dbuf.dptr = (uint8_t *)locks;
}
locks[count] = lock;
dbuf.dsize += sizeof(lock);
if (tdb_store(brl->w->tdb, kbuf, dbuf, TDB_REPLACE) != 0) {
status = NT_STATUS_INTERNAL_DB_CORRUPTION;
goto fail;
}
free(dbuf.dptr);
tdb_chainunlock(brl->w->tdb, kbuf);
/* the caller needs to know if the real lock was granted. If
we have reached here then it must be a pending lock that
was granted, so tell them the lock failed */
if (lock_type >= PENDING_READ_LOCK) {
return NT_STATUS_LOCK_NOT_GRANTED;
}
return NT_STATUS_OK;
fail:
free(dbuf.dptr);
tdb_chainunlock(brl->w->tdb, kbuf);
return status;
}
/*
we are removing a lock that might be holding up a pending lock. Scan for pending
locks that cover this range and if we find any then notify the server that it should
retry the lock
*/
static void brl_notify_unlock(struct brl_context *brl,
struct lock_struct *locks, int count,
struct lock_struct *removed_lock)
{
int i, last_notice;
/* the last_notice logic is to prevent stampeding on a lock
range. It prevents us sending hundreds of notifies on the
same range of bytes. It doesn't prevent all possible
stampedes, but it does prevent the most common problem */
last_notice = -1;
for (i=0;i<count;i++) {
if (locks[i].lock_type >= PENDING_READ_LOCK &&
brl_overlap(&locks[i], removed_lock)) {
if (last_notice != -1 && brl_overlap(&locks[i], &locks[last_notice])) {
continue;
}
if (locks[i].lock_type == PENDING_WRITE_LOCK) {
last_notice = i;
}
messaging_send_ptr(brl->messaging_ctx, locks[i].context.server,
MSG_BRL_RETRY, locks[i].notify_ptr);
}
}
}
/*
send notifications for all pending locks - the file is being closed by this
user
*/
static void brl_notify_all(struct brl_context *brl,
struct lock_struct *locks, int count)
{
int i;
for (i=0;i<count;i++) {
if (locks->lock_type >= PENDING_READ_LOCK) {
brl_notify_unlock(brl, locks, count, &locks[i]);
}
}
}
/*
Unlock a range of bytes.
*/
NTSTATUS brl_unlock(struct brl_context *brl,
struct brl_handle *brlh,
uint16_t smbpid,
uint64_t start, uint64_t size)
{
TDB_DATA kbuf, dbuf;
int count, i;
struct lock_struct *locks;
struct lock_context context;
NTSTATUS status;
kbuf.dptr = brlh->key.data;
kbuf.dsize = brlh->key.length;
if (tdb_chainlock(brl->w->tdb, kbuf) != 0) {
return NT_STATUS_INTERNAL_DB_CORRUPTION;
}
dbuf = tdb_fetch(brl->w->tdb, kbuf);
if (!dbuf.dptr) {
tdb_chainunlock(brl->w->tdb, kbuf);
return NT_STATUS_RANGE_NOT_LOCKED;
}
context.smbpid = smbpid;
context.server = brl->server;
context.ctx = brl;
/* there are existing locks - find a match */
locks = (struct lock_struct *)dbuf.dptr;
count = dbuf.dsize / sizeof(*locks);
for (i=0; i<count; i++) {
struct lock_struct *lock = &locks[i];
if (brl_same_context(&lock->context, &context) &&
lock->ntvfs == brlh->ntvfs &&
lock->start == start &&
lock->size == size &&
lock->lock_type < PENDING_READ_LOCK) {
/* found it - delete it */
if (count == 1) {
if (tdb_delete(brl->w->tdb, kbuf) != 0) {
status = NT_STATUS_INTERNAL_DB_CORRUPTION;
goto fail;
}
} else {
struct lock_struct removed_lock = *lock;
if (i < count-1) {
memmove(&locks[i], &locks[i+1],
sizeof(*locks)*((count-1) - i));
}
count--;
/* send notifications for any relevant pending locks */
brl_notify_unlock(brl, locks, count, &removed_lock);
dbuf.dsize = count * sizeof(*locks);
if (tdb_store(brl->w->tdb, kbuf, dbuf, TDB_REPLACE) != 0) {
status = NT_STATUS_INTERNAL_DB_CORRUPTION;
goto fail;
}
}
free(dbuf.dptr);
tdb_chainunlock(brl->w->tdb, kbuf);
return NT_STATUS_OK;
}
}
/* we didn't find it */
status = NT_STATUS_RANGE_NOT_LOCKED;
fail:
free(dbuf.dptr);
tdb_chainunlock(brl->w->tdb, kbuf);
return status;
}
/*
remove a pending lock. This is called when the caller has either
given up trying to establish a lock or when they have succeeded in
getting it. In either case they no longer need to be notified.
*/
NTSTATUS brl_remove_pending(struct brl_context *brl,
struct brl_handle *brlh,
void *notify_ptr)
{
TDB_DATA kbuf, dbuf;
int count, i;
struct lock_struct *locks;
NTSTATUS status;
kbuf.dptr = brlh->key.data;
kbuf.dsize = brlh->key.length;
if (tdb_chainlock(brl->w->tdb, kbuf) != 0) {
return NT_STATUS_INTERNAL_DB_CORRUPTION;
}
dbuf = tdb_fetch(brl->w->tdb, kbuf);
if (!dbuf.dptr) {
tdb_chainunlock(brl->w->tdb, kbuf);
return NT_STATUS_RANGE_NOT_LOCKED;
}
/* there are existing locks - find a match */
locks = (struct lock_struct *)dbuf.dptr;
count = dbuf.dsize / sizeof(*locks);
for (i=0; i<count; i++) {
struct lock_struct *lock = &locks[i];
if (lock->lock_type >= PENDING_READ_LOCK &&
lock->notify_ptr == notify_ptr &&
lock->context.server == brl->server) {
/* found it - delete it */
if (count == 1) {
if (tdb_delete(brl->w->tdb, kbuf) != 0) {
status = NT_STATUS_INTERNAL_DB_CORRUPTION;
goto fail;
}
} else {
if (i < count-1) {
memmove(&locks[i], &locks[i+1],
sizeof(*locks)*((count-1) - i));
}
count--;
dbuf.dsize = count * sizeof(*locks);
if (tdb_store(brl->w->tdb, kbuf, dbuf, TDB_REPLACE) != 0) {
status = NT_STATUS_INTERNAL_DB_CORRUPTION;
goto fail;
}
}
free(dbuf.dptr);
tdb_chainunlock(brl->w->tdb, kbuf);
return NT_STATUS_OK;
}
}
/* we didn't find it */
status = NT_STATUS_RANGE_NOT_LOCKED;
fail:
free(dbuf.dptr);
tdb_chainunlock(brl->w->tdb, kbuf);
return status;
}
/*
Test if we are allowed to perform IO on a region of an open file
*/
NTSTATUS brl_locktest(struct brl_context *brl,
struct brl_handle *brlh,
uint16_t smbpid,
uint64_t start, uint64_t size,
enum brl_type lock_type)
{
TDB_DATA kbuf, dbuf;
int count, i;
struct lock_struct lock, *locks;
kbuf.dptr = brlh->key.data;
kbuf.dsize = brlh->key.length;
dbuf = tdb_fetch(brl->w->tdb, kbuf);
if (dbuf.dptr == NULL) {
return NT_STATUS_OK;
}
lock.context.smbpid = smbpid;
lock.context.server = brl->server;
lock.context.ctx = brl;
lock.ntvfs = brlh->ntvfs;
lock.start = start;
lock.size = size;
lock.lock_type = lock_type;
/* there are existing locks - make sure they don't conflict */
locks = (struct lock_struct *)dbuf.dptr;
count = dbuf.dsize / sizeof(*locks);
for (i=0; i<count; i++) {
if (brl_conflict_other(&locks[i], &lock)) {
free(dbuf.dptr);
return NT_STATUS_FILE_LOCK_CONFLICT;
}
}
free(dbuf.dptr);
return NT_STATUS_OK;
}
/*
Remove any locks associated with a open file.
*/
NTSTATUS brl_close(struct brl_context *brl,
struct brl_handle *brlh)
{
TDB_DATA kbuf, dbuf;
int count, i, dcount=0;
struct lock_struct *locks;
NTSTATUS status;
kbuf.dptr = brlh->key.data;
kbuf.dsize = brlh->key.length;
if (tdb_chainlock(brl->w->tdb, kbuf) != 0) {
return NT_STATUS_INTERNAL_DB_CORRUPTION;
}
dbuf = tdb_fetch(brl->w->tdb, kbuf);
if (!dbuf.dptr) {
tdb_chainunlock(brl->w->tdb, kbuf);
return NT_STATUS_OK;
}
/* there are existing locks - remove any for this fnum */
locks = (struct lock_struct *)dbuf.dptr;
count = dbuf.dsize / sizeof(*locks);
for (i=0; i<count; i++) {
struct lock_struct *lock = &locks[i];
if (lock->context.ctx == brl &&
lock->context.server == brl->server &&
lock->ntvfs == brlh->ntvfs) {
/* found it - delete it */
if (count > 1 && i < count-1) {
memmove(&locks[i], &locks[i+1],
sizeof(*locks)*((count-1) - i));
}
count--;
i--;
dcount++;
}
}
status = NT_STATUS_OK;
if (count == 0) {
if (tdb_delete(brl->w->tdb, kbuf) != 0) {
status = NT_STATUS_INTERNAL_DB_CORRUPTION;
}
} else if (dcount != 0) {
/* tell all pending lock holders for this file that
they have a chance now. This is a bit indiscriminant,
but works OK */
brl_notify_all(brl, locks, count);
dbuf.dsize = count * sizeof(*locks);
if (tdb_store(brl->w->tdb, kbuf, dbuf, TDB_REPLACE) != 0) {
status = NT_STATUS_INTERNAL_DB_CORRUPTION;
}
}
free(dbuf.dptr);
tdb_chainunlock(brl->w->tdb, kbuf);
return status;
}
+12
View File
@@ -0,0 +1,12 @@
################################################
# Start LIBRARY ntvfs_common
[SUBSYSTEM::ntvfs_common]
PRIVATE_PROTO_HEADER = proto.h
OBJ_FILES = \
init.o \
brlock.o \
opendb.o \
notify.o
PUBLIC_DEPENDENCIES = NDR_OPENDB NDR_NOTIFY sys_notify share
# End LIBRARY ntvfs_common
################################################
+33
View File
@@ -0,0 +1,33 @@
/*
Unix SMB/CIFS implementation.
Copyright (C) Stefan Metzmacher 2006
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
(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.
*/
/*
this is the change notify database. It implements mechanisms for
storing current change notify waiters in a tdb, and checking if a
given event matches any of the stored notify waiiters.
*/
#include "includes.h"
#include "ntvfs/sysdep/sys_notify.h"
_PUBLIC_ NTSTATUS ntvfs_common_init(void)
{
return sys_notify_init();
}
+659
View File
@@ -0,0 +1,659 @@
/*
Unix SMB/CIFS implementation.
Copyright (C) Andrew Tridgell 2006
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
(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.
*/
/*
this is the change notify database. It implements mechanisms for
storing current change notify waiters in a tdb, and checking if a
given event matches any of the stored notify waiiters.
*/
#include "includes.h"
#include "system/filesys.h"
#include "lib/tdb/include/tdb.h"
#include "lib/util/util_tdb.h"
#include "messaging/messaging.h"
#include "db_wrap.h"
#include "lib/messaging/irpc.h"
#include "librpc/gen_ndr/ndr_notify.h"
#include "lib/util/dlinklist.h"
#include "ntvfs/sysdep/sys_notify.h"
struct notify_context {
struct tdb_wrap *w;
uint32_t server;
struct messaging_context *messaging_ctx;
struct notify_list *list;
struct notify_array *array;
int seqnum;
struct sys_notify_context *sys_notify_ctx;
};
struct notify_list {
struct notify_list *next, *prev;
void *private;
void (*callback)(void *, const struct notify_event *);
void *sys_notify_handle;
int depth;
};
#define NOTIFY_KEY "notify array"
#define NOTIFY_ENABLE "notify:enable"
#define NOTIFY_ENABLE_DEFAULT True
static NTSTATUS notify_remove_all(struct notify_context *notify);
static void notify_handler(struct messaging_context *msg_ctx, void *private,
uint32_t msg_type, uint32_t server_id, DATA_BLOB *data);
/*
destroy the notify context
*/
static int notify_destructor(struct notify_context *notify)
{
messaging_deregister(notify->messaging_ctx, MSG_PVFS_NOTIFY, notify);
notify_remove_all(notify);
return 0;
}
/*
Open up the notify.tdb database. You should close it down using
talloc_free(). We need the messaging_ctx to allow for notifications
via internal messages
*/
struct notify_context *notify_init(TALLOC_CTX *mem_ctx, uint32_t server,
struct messaging_context *messaging_ctx,
struct event_context *ev,
struct share_config *scfg)
{
char *path;
struct notify_context *notify;
if (share_bool_option(scfg, NOTIFY_ENABLE, NOTIFY_ENABLE_DEFAULT) != True) {
return NULL;
}
notify = talloc(mem_ctx, struct notify_context);
if (notify == NULL) {
return NULL;
}
path = smbd_tmp_path(notify, "notify.tdb");
notify->w = tdb_wrap_open(notify, path, 0,
TDB_SEQNUM,
O_RDWR|O_CREAT, 0600);
talloc_free(path);
if (notify->w == NULL) {
talloc_free(notify);
return NULL;
}
notify->server = server;
notify->messaging_ctx = messaging_ctx;
notify->list = NULL;
notify->array = NULL;
notify->seqnum = tdb_get_seqnum(notify->w->tdb);
talloc_set_destructor(notify, notify_destructor);
/* register with the messaging subsystem for the notify
message type */
messaging_register(notify->messaging_ctx, notify,
MSG_PVFS_NOTIFY, notify_handler);
notify->sys_notify_ctx = sys_notify_context_create(scfg, notify, ev);
return notify;
}
/*
lock the notify db
*/
static NTSTATUS notify_lock(struct notify_context *notify)
{
if (tdb_lock_bystring(notify->w->tdb, NOTIFY_KEY) != 0) {
return NT_STATUS_INTERNAL_DB_CORRUPTION;
}
return NT_STATUS_OK;
}
/*
unlock the notify db
*/
static void notify_unlock(struct notify_context *notify)
{
tdb_unlock_bystring(notify->w->tdb, NOTIFY_KEY);
}
/*
load the notify array
*/
static NTSTATUS notify_load(struct notify_context *notify)
{
TDB_DATA dbuf;
DATA_BLOB blob;
NTSTATUS status;
int seqnum;
seqnum = tdb_get_seqnum(notify->w->tdb);
if (seqnum == notify->seqnum && notify->array != NULL) {
return NT_STATUS_OK;
}
notify->seqnum = seqnum;
talloc_free(notify->array);
notify->array = talloc_zero(notify, struct notify_array);
NT_STATUS_HAVE_NO_MEMORY(notify->array);
dbuf = tdb_fetch_bystring(notify->w->tdb, NOTIFY_KEY);
if (dbuf.dptr == NULL) {
return NT_STATUS_OK;
}
blob.data = dbuf.dptr;
blob.length = dbuf.dsize;
status = ndr_pull_struct_blob(&blob, notify->array, notify->array,
(ndr_pull_flags_fn_t)ndr_pull_notify_array);
free(dbuf.dptr);
return status;
}
/*
compare notify entries for sorting
*/
static int notify_compare(const void *p1, const void *p2)
{
const struct notify_entry *e1 = p1, *e2 = p2;
return strcmp(e1->path, e2->path);
}
/*
save the notify array
*/
static NTSTATUS notify_save(struct notify_context *notify)
{
TDB_DATA dbuf;
DATA_BLOB blob;
NTSTATUS status;
int ret;
TALLOC_CTX *tmp_ctx;
/* if possible, remove some depth arrays */
while (notify->array->num_depths > 0 &&
notify->array->depth[notify->array->num_depths-1].num_entries == 0) {
notify->array->num_depths--;
}
/* we might just be able to delete the record */
if (notify->array->num_depths == 0) {
ret = tdb_delete_bystring(notify->w->tdb, NOTIFY_KEY);
if (ret != 0) {
return NT_STATUS_INTERNAL_DB_CORRUPTION;
}
return NT_STATUS_OK;
}
tmp_ctx = talloc_new(notify);
status = ndr_push_struct_blob(&blob, tmp_ctx, notify->array,
(ndr_push_flags_fn_t)ndr_push_notify_array);
if (!NT_STATUS_IS_OK(status)) {
talloc_free(tmp_ctx);
return status;
}
dbuf.dptr = blob.data;
dbuf.dsize = blob.length;
ret = tdb_store_bystring(notify->w->tdb, NOTIFY_KEY, dbuf, TDB_REPLACE);
talloc_free(tmp_ctx);
if (ret != 0) {
return NT_STATUS_INTERNAL_DB_CORRUPTION;
}
return NT_STATUS_OK;
}
/*
handle incoming notify messages
*/
static void notify_handler(struct messaging_context *msg_ctx, void *private,
uint32_t msg_type, uint32_t server_id, DATA_BLOB *data)
{
struct notify_context *notify = talloc_get_type(private, struct notify_context);
NTSTATUS status;
struct notify_event ev;
TALLOC_CTX *tmp_ctx = talloc_new(notify);
struct notify_list *listel;
status = ndr_pull_struct_blob(data, tmp_ctx, &ev,
(ndr_pull_flags_fn_t)ndr_pull_notify_event);
if (!NT_STATUS_IS_OK(status)) {
talloc_free(tmp_ctx);
return;
}
for (listel=notify->list;listel;listel=listel->next) {
if (listel->private == ev.private) {
listel->callback(listel->private, &ev);
break;
}
}
talloc_free(tmp_ctx);
}
/*
callback from sys_notify telling us about changes from the OS
*/
static void sys_notify_callback(struct sys_notify_context *ctx,
void *ptr, struct notify_event *ev)
{
struct notify_list *listel = talloc_get_type(ptr, struct notify_list);
ev->private = listel;
listel->callback(listel->private, ev);
}
/*
add an entry to the notify array
*/
static NTSTATUS notify_add_array(struct notify_context *notify, struct notify_entry *e,
void *private, int depth)
{
int i;
struct notify_depth *d;
struct notify_entry *ee;
/* possibly expand the depths array */
if (depth >= notify->array->num_depths) {
d = talloc_realloc(notify->array, notify->array->depth,
struct notify_depth, depth+1);
NT_STATUS_HAVE_NO_MEMORY(d);
for (i=notify->array->num_depths;i<=depth;i++) {
ZERO_STRUCT(d[i]);
}
notify->array->depth = d;
notify->array->num_depths = depth+1;
}
d = &notify->array->depth[depth];
/* expand the entries array */
ee = talloc_realloc(notify->array->depth, d->entries, struct notify_entry,
d->num_entries+1);
NT_STATUS_HAVE_NO_MEMORY(ee);
d->entries = ee;
d->entries[d->num_entries] = *e;
d->entries[d->num_entries].private = private;
d->entries[d->num_entries].server = notify->server;
d->entries[d->num_entries].path_len = strlen(e->path);
d->num_entries++;
d->max_mask |= e->filter;
d->max_mask_subdir |= e->subdir_filter;
if (d->num_entries > 1) {
qsort(d->entries, d->num_entries, sizeof(d->entries[0]), notify_compare);
}
/* recalculate the maximum masks */
d->max_mask = 0;
d->max_mask_subdir = 0;
for (i=0;i<d->num_entries;i++) {
d->max_mask |= d->entries[i].filter;
d->max_mask_subdir |= d->entries[i].subdir_filter;
}
return notify_save(notify);
}
/*
add a notify watch. This is called when a notify is first setup on a open
directory handle.
*/
NTSTATUS notify_add(struct notify_context *notify, struct notify_entry *e0,
void (*callback)(void *, const struct notify_event *),
void *private)
{
struct notify_entry e = *e0;
NTSTATUS status;
char *tmp_path = NULL;
struct notify_list *listel;
size_t len;
int depth;
/* see if change notify is enabled at all */
if (notify == NULL) {
return NT_STATUS_NOT_IMPLEMENTED;
}
status = notify_lock(notify);
NT_STATUS_NOT_OK_RETURN(status);
status = notify_load(notify);
if (!NT_STATUS_IS_OK(status)) {
goto done;
}
/* cope with /. on the end of the path */
len = strlen(e.path);
if (len > 1 && e.path[len-1] == '.' && e.path[len-2] == '/') {
tmp_path = talloc_strndup(notify, e.path, len-2);
if (tmp_path == NULL) {
status = NT_STATUS_NO_MEMORY;
goto done;
}
e.path = tmp_path;
}
depth = count_chars(e.path, '/');
listel = talloc_zero(notify, struct notify_list);
if (listel == NULL) {
status = NT_STATUS_NO_MEMORY;
goto done;
}
listel->private = private;
listel->callback = callback;
listel->depth = depth;
DLIST_ADD(notify->list, listel);
/* ignore failures from sys_notify */
if (notify->sys_notify_ctx != NULL) {
/*
this call will modify e.filter and e.subdir_filter
to remove bits handled by the backend
*/
status = sys_notify_watch(notify->sys_notify_ctx, &e,
sys_notify_callback, listel,
&listel->sys_notify_handle);
if (NT_STATUS_IS_OK(status)) {
talloc_steal(listel, listel->sys_notify_handle);
}
}
/* if the system notify handler couldn't handle some of the
filter bits, or couldn't handle a request for recursion
then we need to install it in the array used for the
intra-samba notify handling */
if (e.filter != 0 || e.subdir_filter != 0) {
status = notify_add_array(notify, &e, private, depth);
}
done:
notify_unlock(notify);
talloc_free(tmp_path);
return status;
}
/*
remove a notify watch. Called when the directory handle is closed
*/
NTSTATUS notify_remove(struct notify_context *notify, void *private)
{
NTSTATUS status;
struct notify_list *listel;
int i, depth;
struct notify_depth *d;
/* see if change notify is enabled at all */
if (notify == NULL) {
return NT_STATUS_NOT_IMPLEMENTED;
}
for (listel=notify->list;listel;listel=listel->next) {
if (listel->private == private) {
DLIST_REMOVE(notify->list, listel);
break;
}
}
if (listel == NULL) {
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
}
depth = listel->depth;
talloc_free(listel);
status = notify_lock(notify);
NT_STATUS_NOT_OK_RETURN(status);
status = notify_load(notify);
if (!NT_STATUS_IS_OK(status)) {
notify_unlock(notify);
return status;
}
if (depth >= notify->array->num_depths) {
notify_unlock(notify);
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
}
/* we only have to search at the depth of this element */
d = &notify->array->depth[depth];
for (i=0;i<d->num_entries;i++) {
if (private == d->entries[i].private &&
notify->server == d->entries[i].server) {
break;
}
}
if (i == d->num_entries) {
notify_unlock(notify);
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
}
if (i < d->num_entries-1) {
memmove(&d->entries[i], &d->entries[i+1],
sizeof(d->entries[i])*(d->num_entries-(i+1)));
}
d->num_entries--;
status = notify_save(notify);
notify_unlock(notify);
return status;
}
/*
remove all notify watches for this messaging server
*/
static NTSTATUS notify_remove_all(struct notify_context *notify)
{
NTSTATUS status;
int i, depth, del_count=0;
if (notify->list == NULL) {
return NT_STATUS_OK;
}
status = notify_lock(notify);
NT_STATUS_NOT_OK_RETURN(status);
status = notify_load(notify);
if (!NT_STATUS_IS_OK(status)) {
notify_unlock(notify);
return status;
}
/* we have to search for all entries across all depths, looking for matches
for our server id */
for (depth=0;depth<notify->array->num_depths;depth++) {
struct notify_depth *d = &notify->array->depth[depth];
for (i=0;i<d->num_entries;i++) {
if (notify->server == d->entries[i].server) {
if (i < d->num_entries-1) {
memmove(&d->entries[i], &d->entries[i+1],
sizeof(d->entries[i])*(d->num_entries-(i+1)));
}
i--;
d->num_entries--;
del_count++;
}
}
}
if (del_count > 0) {
status = notify_save(notify);
}
notify_unlock(notify);
return status;
}
/*
send a notify message to another messaging server
*/
static void notify_send(struct notify_context *notify, struct notify_entry *e,
const char *path, uint32_t action)
{
struct notify_event ev;
DATA_BLOB data;
NTSTATUS status;
TALLOC_CTX *tmp_ctx;
ev.action = action;
ev.path = path;
ev.private = e->private;
tmp_ctx = talloc_new(notify);
status = ndr_push_struct_blob(&data, tmp_ctx, &ev,
(ndr_push_flags_fn_t)ndr_push_notify_event);
if (!NT_STATUS_IS_OK(status)) {
talloc_free(tmp_ctx);
return;
}
status = messaging_send(notify->messaging_ctx, e->server,
MSG_PVFS_NOTIFY, &data);
talloc_free(tmp_ctx);
}
/*
trigger a notify message for anyone waiting on a matching event
This function is called a lot, and needs to be very fast. The unusual data structure
and traversal is designed to be fast in the average case, even for large numbers of
notifies
*/
void notify_trigger(struct notify_context *notify,
uint32_t action, uint32_t filter, const char *path)
{
NTSTATUS status;
int depth;
const char *p, *next_p;
/* see if change notify is enabled at all */
if (notify == NULL) {
return;
}
status = notify_load(notify);
if (!NT_STATUS_IS_OK(status)) {
return;
}
/* loop along the given path, working with each directory depth separately */
for (depth=0,p=path;
p && depth < notify->array->num_depths;
p=next_p,depth++) {
int p_len = p - path;
int min_i, max_i, i;
struct notify_depth *d = &notify->array->depth[depth];
next_p = strchr(p+1, '/');
/* see if there are any entries at this depth */
if (d->num_entries == 0) continue;
/* try to skip based on the maximum mask. If next_p is
NULL then we know it will be a 'this directory'
match, otherwise it must be a subdir match */
if (next_p != NULL) {
if (0 == (filter & d->max_mask_subdir)) {
continue;
}
} else {
if (0 == (filter & d->max_mask)) {
continue;
}
}
/* we know there is an entry here worth looking
for. Use a bisection search to find the first entry
with a matching path */
min_i = 0;
max_i = d->num_entries-1;
while (min_i < max_i) {
struct notify_entry *e;
int cmp;
i = (min_i+max_i)/2;
e = &d->entries[i];
cmp = strncmp(path, e->path, p_len);
if (cmp == 0) {
if (p_len == e->path_len) {
max_i = i;
} else {
max_i = i-1;
}
} else if (cmp < 0) {
max_i = i-1;
} else {
min_i = i+1;
}
}
if (min_i != max_i) {
/* none match */
continue;
}
/* we now know that the entries start at min_i */
for (i=min_i;i<d->num_entries;i++) {
struct notify_entry *e = &d->entries[i];
if (p_len != e->path_len ||
strncmp(path, e->path, p_len) != 0) break;
if (next_p != NULL) {
if (0 == (filter & e->subdir_filter)) {
continue;
}
} else {
if (0 == (filter & e->filter)) {
continue;
}
}
notify_send(notify, e, path + e->path_len + 1, action);
}
}
}
+605
View File
@@ -0,0 +1,605 @@
/*
Unix SMB/CIFS implementation.
Copyright (C) Andrew Tridgell 2004
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 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.
*/
/*
this is the open files database. It implements shared storage of
what files are open between server instances, and implements the rules
of shared access to files.
The caller needs to provide a file_key, which specifies what file
they are talking about. This needs to be a unique key across all
filesystems, and is usually implemented in terms of a device/inode
pair.
Before any operations can be performed the caller needs to establish
a lock on the record associated with file_key. That is done by
calling odb_lock(). The caller releases this lock by calling
talloc_free() on the returned handle.
All other operations on a record are done by passing the odb_lock()
handle back to this module. The handle contains internal
information about what file_key is being operated on.
*/
#include "includes.h"
#include "system/filesys.h"
#include "lib/tdb/include/tdb.h"
#include "messaging/messaging.h"
#include "db_wrap.h"
#include "lib/messaging/irpc.h"
#include "librpc/gen_ndr/ndr_opendb.h"
#include "ntvfs/ntvfs.h"
struct odb_context {
struct tdb_wrap *w;
struct ntvfs_context *ntvfs_ctx;
BOOL oplocks;
};
/*
an odb lock handle. You must obtain one of these using odb_lock() before doing
any other operations.
*/
struct odb_lock {
struct odb_context *odb;
TDB_DATA key;
};
/*
Open up the openfiles.tdb database. Close it down using
talloc_free(). We need the messaging_ctx to allow for pending open
notifications.
*/
_PUBLIC_ struct odb_context *odb_init(TALLOC_CTX *mem_ctx,
struct ntvfs_context *ntvfs_ctx)
{
char *path;
struct odb_context *odb;
odb = talloc(mem_ctx, struct odb_context);
if (odb == NULL) {
return NULL;
}
path = smbd_tmp_path(odb, "openfiles.tdb");
odb->w = tdb_wrap_open(odb, path, 0,
TDB_DEFAULT,
O_RDWR|O_CREAT, 0600);
talloc_free(path);
if (odb->w == NULL) {
talloc_free(odb);
return NULL;
}
odb->ntvfs_ctx = ntvfs_ctx;
/* leave oplocks disabled by default until the code is working */
odb->oplocks = lp_parm_bool(-1, "opendb", "oplocks", False);
return odb;
}
/*
destroy a lock on the database
*/
static int odb_lock_destructor(struct odb_lock *lck)
{
tdb_chainunlock(lck->odb->w->tdb, lck->key);
return 0;
}
/*
get a lock on a entry in the odb. This call returns a lock handle,
which the caller should unlock using talloc_free().
*/
_PUBLIC_ struct odb_lock *odb_lock(TALLOC_CTX *mem_ctx,
struct odb_context *odb, DATA_BLOB *file_key)
{
struct odb_lock *lck;
lck = talloc(mem_ctx, struct odb_lock);
if (lck == NULL) {
return NULL;
}
lck->odb = talloc_reference(lck, odb);
lck->key.dptr = talloc_memdup(lck, file_key->data, file_key->length);
lck->key.dsize = file_key->length;
if (lck->key.dptr == NULL) {
talloc_free(lck);
return NULL;
}
if (tdb_chainlock(odb->w->tdb, lck->key) != 0) {
talloc_free(lck);
return NULL;
}
talloc_set_destructor(lck, odb_lock_destructor);
return lck;
}
/*
determine if two odb_entry structures conflict
return NT_STATUS_OK on no conflict
*/
static NTSTATUS share_conflict(struct opendb_entry *e1, struct opendb_entry *e2)
{
/* if either open involves no read.write or delete access then
it can't conflict */
if (!(e1->access_mask & (SEC_FILE_WRITE_DATA |
SEC_FILE_APPEND_DATA |
SEC_FILE_READ_DATA |
SEC_FILE_EXECUTE |
SEC_STD_DELETE))) {
return NT_STATUS_OK;
}
if (!(e2->access_mask & (SEC_FILE_WRITE_DATA |
SEC_FILE_APPEND_DATA |
SEC_FILE_READ_DATA |
SEC_FILE_EXECUTE |
SEC_STD_DELETE))) {
return NT_STATUS_OK;
}
/* data IO access masks. This is skipped if the two open handles
are on different streams (as in that case the masks don't
interact) */
if (e1->stream_id != e2->stream_id) {
return NT_STATUS_OK;
}
#define CHECK_MASK(am, right, sa, share) \
if (((am) & (right)) && !((sa) & (share))) return NT_STATUS_SHARING_VIOLATION
CHECK_MASK(e1->access_mask, SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA,
e2->share_access, NTCREATEX_SHARE_ACCESS_WRITE);
CHECK_MASK(e2->access_mask, SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA,
e1->share_access, NTCREATEX_SHARE_ACCESS_WRITE);
CHECK_MASK(e1->access_mask, SEC_FILE_READ_DATA | SEC_FILE_EXECUTE,
e2->share_access, NTCREATEX_SHARE_ACCESS_READ);
CHECK_MASK(e2->access_mask, SEC_FILE_READ_DATA | SEC_FILE_EXECUTE,
e1->share_access, NTCREATEX_SHARE_ACCESS_READ);
CHECK_MASK(e1->access_mask, SEC_STD_DELETE,
e2->share_access, NTCREATEX_SHARE_ACCESS_DELETE);
CHECK_MASK(e2->access_mask, SEC_STD_DELETE,
e1->share_access, NTCREATEX_SHARE_ACCESS_DELETE);
return NT_STATUS_OK;
}
/*
pull a record, translating from the db format to the opendb_file structure defined
in opendb.idl
*/
static NTSTATUS odb_pull_record(struct odb_lock *lck, struct opendb_file *file)
{
struct odb_context *odb = lck->odb;
TDB_DATA dbuf;
DATA_BLOB blob;
NTSTATUS status;
dbuf = tdb_fetch(odb->w->tdb, lck->key);
if (dbuf.dptr == NULL) {
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
}
blob.data = dbuf.dptr;
blob.length = dbuf.dsize;
status = ndr_pull_struct_blob(&blob, lck, file, (ndr_pull_flags_fn_t)ndr_pull_opendb_file);
free(dbuf.dptr);
return status;
}
/*
push a record, translating from the opendb_file structure defined in opendb.idl
*/
static NTSTATUS odb_push_record(struct odb_lock *lck, struct opendb_file *file)
{
struct odb_context *odb = lck->odb;
TDB_DATA dbuf;
DATA_BLOB blob;
NTSTATUS status;
int ret;
if (file->num_entries == 0) {
ret = tdb_delete(odb->w->tdb, lck->key);
if (ret != 0) {
return NT_STATUS_INTERNAL_DB_CORRUPTION;
}
return NT_STATUS_OK;
}
status = ndr_push_struct_blob(&blob, lck, file, (ndr_push_flags_fn_t)ndr_push_opendb_file);
NT_STATUS_NOT_OK_RETURN(status);
dbuf.dptr = blob.data;
dbuf.dsize = blob.length;
ret = tdb_store(odb->w->tdb, lck->key, dbuf, TDB_REPLACE);
data_blob_free(&blob);
if (ret != 0) {
return NT_STATUS_INTERNAL_DB_CORRUPTION;
}
return NT_STATUS_OK;
}
/*
send an oplock break to a client
*/
static NTSTATUS odb_oplock_break_send(struct odb_context *odb, struct opendb_entry *e)
{
/* tell the server handling this open file about the need to send the client
a break */
return messaging_send_ptr(odb->ntvfs_ctx->msg_ctx, e->server,
MSG_NTVFS_OPLOCK_BREAK, e->file_handle);
}
/*
register an open file in the open files database. This implements the share_access
rules
Note that the path is only used by the delete on close logic, not
for comparing with other filenames
*/
_PUBLIC_ NTSTATUS odb_open_file(struct odb_lock *lck, void *file_handle,
uint32_t stream_id, uint32_t share_access,
uint32_t access_mask, BOOL delete_on_close,
const char *path,
uint32_t oplock_level, uint32_t *oplock_granted)
{
struct odb_context *odb = lck->odb;
struct opendb_entry e;
int i;
struct opendb_file file;
NTSTATUS status;
if (odb->oplocks == False) {
oplock_level = OPLOCK_NONE;
}
status = odb_pull_record(lck, &file);
if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
/* initialise a blank structure */
ZERO_STRUCT(file);
file.path = path;
} else {
NT_STATUS_NOT_OK_RETURN(status);
}
/* see if it conflicts */
e.server = odb->ntvfs_ctx->server_id;
e.file_handle = file_handle;
e.stream_id = stream_id;
e.share_access = share_access;
e.access_mask = access_mask;
e.delete_on_close = delete_on_close;
e.oplock_level = OPLOCK_NONE;
/* see if anyone has an oplock, which we need to break */
for (i=0;i<file.num_entries;i++) {
if (file.entries[i].oplock_level == OPLOCK_BATCH) {
/* a batch oplock caches close calls, which
means the client application might have
already closed the file. We have to allow
this close to propogate by sending a oplock
break request and suspending this call
until the break is acknowledged or the file
is closed */
odb_oplock_break_send(odb, &file.entries[i]);
return NT_STATUS_OPLOCK_NOT_GRANTED;
}
}
if (file.delete_on_close ||
(file.num_entries != 0 && delete_on_close)) {
/* while delete on close is set, no new opens are allowed */
return NT_STATUS_DELETE_PENDING;
}
/* check for sharing violations */
for (i=0;i<file.num_entries;i++) {
status = share_conflict(&file.entries[i], &e);
NT_STATUS_NOT_OK_RETURN(status);
}
/* we now know the open could succeed, but we need to check
for any exclusive oplocks. We can't grant a second open
till these are broken. Note that we check for batch oplocks
before checking for sharing violations, and check for
exclusive oplocks afterwards. */
for (i=0;i<file.num_entries;i++) {
if (file.entries[i].oplock_level == OPLOCK_EXCLUSIVE) {
odb_oplock_break_send(odb, &file.entries[i]);
return NT_STATUS_OPLOCK_NOT_GRANTED;
}
}
/*
possibly grant an exclusive or batch oplock if this is the only client
with the file open. We don't yet grant levelII oplocks.
*/
if (oplock_granted != NULL) {
if ((oplock_level == OPLOCK_BATCH ||
oplock_level == OPLOCK_EXCLUSIVE) &&
file.num_entries == 0) {
(*oplock_granted) = oplock_level;
} else {
(*oplock_granted) = OPLOCK_NONE;
}
e.oplock_level = (*oplock_granted);
}
/* it doesn't conflict, so add it to the end */
file.entries = talloc_realloc(lck, file.entries, struct opendb_entry,
file.num_entries+1);
NT_STATUS_HAVE_NO_MEMORY(file.entries);
file.entries[file.num_entries] = e;
file.num_entries++;
return odb_push_record(lck, &file);
}
/*
register a pending open file in the open files database
*/
_PUBLIC_ NTSTATUS odb_open_file_pending(struct odb_lock *lck, void *private)
{
struct odb_context *odb = lck->odb;
struct opendb_file file;
NTSTATUS status;
status = odb_pull_record(lck, &file);
NT_STATUS_NOT_OK_RETURN(status);
file.pending = talloc_realloc(lck, file.pending, struct opendb_pending,
file.num_pending+1);
NT_STATUS_HAVE_NO_MEMORY(file.pending);
file.pending[file.num_pending].server = odb->ntvfs_ctx->server_id;
file.pending[file.num_pending].notify_ptr = private;
file.num_pending++;
return odb_push_record(lck, &file);
}
/*
remove a opendb entry
*/
_PUBLIC_ NTSTATUS odb_close_file(struct odb_lock *lck, void *file_handle)
{
struct odb_context *odb = lck->odb;
struct opendb_file file;
int i;
NTSTATUS status;
status = odb_pull_record(lck, &file);
NT_STATUS_NOT_OK_RETURN(status);
/* find the entry, and delete it */
for (i=0;i<file.num_entries;i++) {
if (file_handle == file.entries[i].file_handle &&
odb->ntvfs_ctx->server_id == file.entries[i].server) {
if (file.entries[i].delete_on_close) {
file.delete_on_close = True;
}
if (i < file.num_entries-1) {
memmove(file.entries+i, file.entries+i+1,
(file.num_entries - (i+1)) *
sizeof(struct opendb_entry));
}
break;
}
}
if (i == file.num_entries) {
return NT_STATUS_UNSUCCESSFUL;
}
/* send any pending notifications, removing them once sent */
for (i=0;i<file.num_pending;i++) {
messaging_send_ptr(odb->ntvfs_ctx->msg_ctx, file.pending[i].server,
MSG_PVFS_RETRY_OPEN,
file.pending[i].notify_ptr);
}
file.num_pending = 0;
file.num_entries--;
return odb_push_record(lck, &file);
}
/*
remove a pending opendb entry
*/
_PUBLIC_ NTSTATUS odb_remove_pending(struct odb_lock *lck, void *private)
{
struct odb_context *odb = lck->odb;
int i;
NTSTATUS status;
struct opendb_file file;
status = odb_pull_record(lck, &file);
NT_STATUS_NOT_OK_RETURN(status);
/* find the entry, and delete it */
for (i=0;i<file.num_pending;i++) {
if (private == file.pending[i].notify_ptr &&
odb->ntvfs_ctx->server_id == file.pending[i].server) {
if (i < file.num_pending-1) {
memmove(file.pending+i, file.pending+i+1,
(file.num_pending - (i+1)) *
sizeof(struct opendb_pending));
}
break;
}
}
if (i == file.num_pending) {
return NT_STATUS_UNSUCCESSFUL;
}
file.num_pending--;
return odb_push_record(lck, &file);
}
/*
rename the path in a open file
*/
_PUBLIC_ NTSTATUS odb_rename(struct odb_lock *lck, const char *path)
{
struct opendb_file file;
NTSTATUS status;
status = odb_pull_record(lck, &file);
if (NT_STATUS_EQUAL(NT_STATUS_OBJECT_NAME_NOT_FOUND, status)) {
/* not having the record at all is OK */
return NT_STATUS_OK;
}
NT_STATUS_NOT_OK_RETURN(status);
file.path = path;
return odb_push_record(lck, &file);
}
/*
update delete on close flag on an open file
*/
_PUBLIC_ NTSTATUS odb_set_delete_on_close(struct odb_lock *lck, BOOL del_on_close)
{
NTSTATUS status;
struct opendb_file file;
status = odb_pull_record(lck, &file);
NT_STATUS_NOT_OK_RETURN(status);
file.delete_on_close = del_on_close;
return odb_push_record(lck, &file);
}
/*
return the current value of the delete_on_close bit, and how many
people still have the file open
*/
_PUBLIC_ NTSTATUS odb_get_delete_on_close(struct odb_context *odb,
DATA_BLOB *key, BOOL *del_on_close,
int *open_count, char **path)
{
NTSTATUS status;
struct opendb_file file;
struct odb_lock *lck;
lck = odb_lock(odb, odb, key);
NT_STATUS_HAVE_NO_MEMORY(lck);
status = odb_pull_record(lck, &file);
if (NT_STATUS_EQUAL(NT_STATUS_OBJECT_NAME_NOT_FOUND, status)) {
talloc_free(lck);
(*del_on_close) = False;
return NT_STATUS_OK;
}
if (!NT_STATUS_IS_OK(status)) {
talloc_free(lck);
return status;
}
(*del_on_close) = file.delete_on_close;
if (open_count != NULL) {
(*open_count) = file.num_entries;
}
if (path != NULL) {
*path = talloc_strdup(odb, file.path);
NT_STATUS_HAVE_NO_MEMORY(*path);
if (file.num_entries == 1 && file.entries[0].delete_on_close) {
(*del_on_close) = True;
}
}
talloc_free(lck);
return NT_STATUS_OK;
}
/*
determine if a file can be opened with the given share_access,
create_options and access_mask
*/
_PUBLIC_ NTSTATUS odb_can_open(struct odb_lock *lck,
uint32_t share_access, uint32_t create_options,
uint32_t access_mask)
{
struct odb_context *odb = lck->odb;
NTSTATUS status;
struct opendb_file file;
struct opendb_entry e;
int i;
status = odb_pull_record(lck, &file);
if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
return NT_STATUS_OK;
}
NT_STATUS_NOT_OK_RETURN(status);
if ((create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
file.num_entries != 0) {
return NT_STATUS_SHARING_VIOLATION;
}
if (file.delete_on_close) {
return NT_STATUS_DELETE_PENDING;
}
e.server = odb->ntvfs_ctx->server_id;
e.file_handle = NULL;
e.stream_id = 0;
e.share_access = share_access;
e.access_mask = access_mask;
for (i=0;i<file.num_entries;i++) {
status = share_conflict(&file.entries[i], &e);
if (!NT_STATUS_IS_OK(status)) {
/* note that we discard the error code
here. We do this as unless we are actually
doing an open (which comes via a different
function), we need to return a sharing
violation */
return NT_STATUS_SHARING_VIOLATION;
}
}
return NT_STATUS_OK;
}