wmi-1.3.16 from opsview.com
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
################################################
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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 = ¬ify->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 = ¬ify->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 = ¬ify->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 = ¬ify->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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user