1
0
mirror of https://github.com/samba-team/samba.git synced 2024-12-25 23:21:54 +03:00

Merge branch 'v4-0-test' of ssh://git.samba.org/data/git/samba into v4-0-gmake3

This commit is contained in:
Jelmer Vernooij 2008-02-26 13:07:55 +01:00
commit 4dab354b62
22 changed files with 1016 additions and 268 deletions

View File

@ -70,20 +70,17 @@ configure above to change this.
Step 4: provision Samba4
------------------------
The "provision" step sets up a basic user database. Make sure your smbscript
binary is installed in a directory listed in your PATH environment variable.
It is presumed it's available just like any other commands from your shell.
The "provision" step sets up a basic user database.
Must be run as a user with permission to write to the install directory.
::
# cd source
# ./setup/provision --realm=YOUR.REALM --domain=YOURDOM \
# bin/smbpython ./setup/provision --realm=YOUR.REALM --domain=YOURDOM \
# --adminpass=SOMEPASSWORD --server-role='domain controller'
REMINDER: Add the "bin" directory of the path you installed to
(e.g. /usr/local/samba/bin) to your path, or the provision command
will not work.
REMINDER: Use the path to smbpython, as the provision command
will not work with the system python.
'YOURDOM' is the NT4 style domain name. 'YOUR.REALM' is your kerberos
realm, which is typically your DNS domain name.

View File

@ -279,11 +279,13 @@ static NTSTATUS odb_oplock_break_send(struct odb_context *odb, struct opendb_ent
Note that the path is only used by the delete on close logic, not
for comparing with other filenames
*/
static NTSTATUS odb_ctdb_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)
static NTSTATUS odb_ctdb_open_file(struct odb_lock *lck,
void *file_handle, const char *path,
uint32_t stream_id, uint32_t share_access,
uint32_t access_mask, bool delete_on_close,
uint32_t open_disposition, bool break_to_none,
uint32_t oplock_level, uint32_t *oplock_granted)
{
struct odb_context *odb = lck->odb;
struct opendb_entry e;
@ -464,6 +466,16 @@ static NTSTATUS odb_ctdb_update_oplock(struct odb_lock *lck, void *file_handle,
return NT_STATUS_FOOBAR;
}
static NTSTATUS odb_ctdb_break_oplocks(struct odb_lock *lck)
{
/*
* as this file will went away and isn't used yet,
* copy the implementation from the tdb backend
* --metze
*/
return NT_STATUS_FOOBAR;
}
/*
remove a pending opendb entry
*/
@ -584,8 +596,9 @@ static NTSTATUS odb_ctdb_get_delete_on_close(struct odb_context *odb,
create_options and access_mask
*/
static NTSTATUS odb_ctdb_can_open(struct odb_lock *lck,
uint32_t share_access, uint32_t create_options,
uint32_t access_mask)
uint32_t stream_id, uint32_t share_access,
uint32_t access_mask, bool delete_on_close,
uint32_t open_disposition, bool break_to_none)
{
struct odb_context *odb = lck->odb;
NTSTATUS status;
@ -599,7 +612,7 @@ static NTSTATUS odb_ctdb_can_open(struct odb_lock *lck,
}
NT_STATUS_NOT_OK_RETURN(status);
if ((create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
if (delete_on_close &&
file.num_entries != 0) {
return NT_STATUS_SHARING_VIOLATION;
}
@ -642,7 +655,8 @@ static const struct opendb_ops opendb_ctdb_ops = {
.odb_set_delete_on_close = odb_ctdb_set_delete_on_close,
.odb_get_delete_on_close = odb_ctdb_get_delete_on_close,
.odb_can_open = odb_ctdb_can_open,
.odb_update_oplock = odb_ctdb_update_oplock
.odb_update_oplock = odb_ctdb_update_oplock,
.odb_break_oplocks = odb_ctdb_break_oplocks
};

View File

@ -42,9 +42,9 @@ The main features that separate ldb from other solutions are:
Currently ldb is completely lacking in programmer or user
documentation. This is your opportunity to make a contribution! Start
with the public functions declared in <a
href="http://samba.org/ftp/unpacked/samba4/source/lib/ldb/include/ldb.h">ldb.h</a>
href="http://samba.org/ftp/unpacked/ldb/include/ldb.h">ldb.h</a>
and the example code in the <a
href="http://samba.org/ftp/unpacked/samba4/source/lib/ldb/tools/">tools
href="http://samba.org/ftp/unpacked/ldb/tools/">tools
directory</a>. Documentation in the same docbook format used by Samba
would be preferred.
@ -53,21 +53,17 @@ would be preferred.
ldb does not currently have its own mailing list or bug tracking
system. For now, please use the <a
href="https://lists.samba.org/mailman/listinfo/samba-technical">samba-technical</a>
mailing list, and the <a href="http://bugzilla.samba.org/">Samba
bugzilla</a> bug tracking system.
mailing list or the <a href="https://lists.samba.org/mailman/listinfo/ldb">ldb</a>
mailing list, and the <a href="http://bugzilla.samba.org/">Samba bugzilla</a> bug tracking system.
<h2>Download</h2>
You can download the latest release either via rsync or anonymous
svn. To fetch via svn use the following commands:
<pre>
svn co svn://svnanon.samba.org/samba/branches/SAMBA_4_0/source/lib/ldb ldb
svn co svn://svnanon.samba.org/samba/branches/SAMBA_4_0/source/lib/tdb tdb
svn co svn://svnanon.samba.org/samba/branches/SAMBA_4_0/source/lib/talloc talloc
svn co svn://svnanon.samba.org/samba/branches/SAMBA_4_0/source/lib/replace libreplace
</pre>
You can download the latest release either via rsync or thtough git.<br>
<br>
To fetch via git see the following guide:<br>
<a href="http://wiki.samba.org/index.php/Using_Git_for_Samba_Development">Using Git for Samba Development</a><br>
Once you have cloned the tree switch to the v4-0-test branch and cd into the source/lib/ldb directory.<br>
<br>
To fetch via rsync use these commands:
<pre>

View File

@ -83,6 +83,11 @@
#include <stropts.h>
#endif
#ifndef HAVE_SOCKLEN_T
#define HAVE_SOCKLEN_T
typedef int socklen_t;
#endif
#ifdef REPLACE_INET_NTOA
/* define is in "replace.h" */
char *rep_inet_ntoa(struct in_addr ip);
@ -245,11 +250,6 @@ void rep_freeifaddrs(struct ifaddrs *);
#define HOST_NAME_MAX 256
#endif
#ifndef HAVE_SOCKLEN_T
#define HAVE_SOCKLEN_T
typedef int socklen_t;
#endif
#ifndef HAVE_SA_FAMILY_T
#define HAVE_SA_FAMILY_T
typedef unsigned short int sa_family_t;

View File

@ -12,7 +12,7 @@ destructors. It is the core memory allocator used in Samba4, and has
made a huge difference in many aspects of Samba4 development.<p>
To get started with talloc, I would recommend you read the <a
href="http://samba.org/ftp/unpacked/samba_4_0_test/source/lib/talloc/talloc_guide.txt">talloc guide</a>.
href="http://samba.org/ftp/unpacked/talloc/talloc_guide.txt">talloc guide</a>.
<h2>Discussion and bug reports</h2>
@ -24,20 +24,16 @@ bugzilla</a> bug tracking system.
<h2>Download</h2>
You can download the latest release either via rsync or git.
To fetch via git use the following command:
<pre>
git-clone git://git.samba.org/samba.git samba
cd samba
git checkout -b samba4 origin/v4-0-test
</pre>
You can download the latest release either via rsync or git.<br>
<br>
To fetch via git see the following guide:<br>
<a href="http://wiki.samba.org/index.php/Using_Git_for_Samba_Development">Using Git for Samba Development</a><br>
Once you have cloned the tree switch to the v4-0-test branch and cd into the source/lib/talloc directory.<br>
<br>
To fetch via rsync use this command:
<pre>
rsync -Pavz samba.org::ftp/unpacked/samba_4_0_test/source/lib/talloc .
rsync -Pavz samba.org::ftp/unpacked/samba_4_0_test/source/lib/libreplace .
rsync -Pavz samba.org::ftp/unpacked/talloc .
</pre>
<hr>

View File

@ -22,14 +22,12 @@ bugzilla</a> bug tracking system.
<h2>Download</h2>
You can download the latest release either via rsync or anonymous
svn. To fetch via svn use the following commands:
<pre>
svn co svn://svnanon.samba.org/samba/branches/SAMBA_4_0/source/lib/tdb tdb
svn co svn://svnanon.samba.org/samba/branches/SAMBA_4_0/source/lib/replace libreplace
</pre>
You can download the latest release either via rsync or git.<br>
<br>
To fetch via git see the following guide:<br>
<a href="http://wiki.samba.org/index.php/Using_Git_for_Samba_Development">Using Git for Samba Development</a><br>
Once you have cloned the tree switch to the v4-0-test branch and cd into the source/lib/tdb directory.<br>
<br>
To fetch via rsync use these commands:
<pre>

View File

@ -93,15 +93,16 @@ _PUBLIC_ DATA_BLOB odb_get_key(TALLOC_CTX *mem_ctx, struct odb_lock *lck)
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,
_PUBLIC_ NTSTATUS odb_open_file(struct odb_lock *lck,
void *file_handle, const char *path,
uint32_t stream_id, uint32_t share_access,
uint32_t access_mask, bool delete_on_close,
const char *path,
uint32_t open_disposition, bool break_to_none,
uint32_t oplock_level, uint32_t *oplock_granted)
{
return ops->odb_open_file(lck, file_handle, stream_id, share_access,
access_mask, delete_on_close, path, oplock_level,
oplock_granted);
return ops->odb_open_file(lck, file_handle, path, stream_id, share_access,
access_mask, delete_on_close, open_disposition,
break_to_none, oplock_level, oplock_granted);
}
@ -165,10 +166,12 @@ _PUBLIC_ NTSTATUS odb_get_delete_on_close(struct odb_context *odb,
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)
uint32_t stream_id, uint32_t share_access,
uint32_t access_mask, bool delete_on_close,
uint32_t open_disposition, bool break_to_none)
{
return ops->odb_can_open(lck, share_access, create_options, access_mask);
return ops->odb_can_open(lck, stream_id, share_access, access_mask,
delete_on_close, open_disposition, break_to_none);
}
_PUBLIC_ NTSTATUS odb_update_oplock(struct odb_lock *lck, void *file_handle,
@ -176,3 +179,8 @@ _PUBLIC_ NTSTATUS odb_update_oplock(struct odb_lock *lck, void *file_handle,
{
return ops->odb_update_oplock(lck, file_handle, oplock_level);
}
_PUBLIC_ NTSTATUS odb_break_oplocks(struct odb_lock *lck)
{
return ops->odb_break_oplocks(lck);
}

View File

@ -25,10 +25,11 @@ struct opendb_ops {
struct odb_lock *(*odb_lock)(TALLOC_CTX *mem_ctx,
struct odb_context *odb, DATA_BLOB *file_key);
DATA_BLOB (*odb_get_key)(TALLOC_CTX *mem_ctx, struct odb_lock *lck);
NTSTATUS (*odb_open_file)(struct odb_lock *lck, void *file_handle,
uint32_t stream_id, uint32_t share_access,
NTSTATUS (*odb_open_file)(struct odb_lock *lck,
void *file_handle, const char *path,
uint32_t stream_id, uint32_t share_access,
uint32_t access_mask, bool delete_on_close,
const char *path,
uint32_t open_disposition, bool break_to_none,
uint32_t oplock_level, uint32_t *oplock_granted);
NTSTATUS (*odb_open_file_pending)(struct odb_lock *lck, void *private);
NTSTATUS (*odb_close_file)(struct odb_lock *lck, void *file_handle);
@ -39,10 +40,12 @@ struct opendb_ops {
DATA_BLOB *key, bool *del_on_close,
int *open_count, char **path);
NTSTATUS (*odb_can_open)(struct odb_lock *lck,
uint32_t share_access, uint32_t create_options,
uint32_t access_mask);
uint32_t stream_id, uint32_t share_access,
uint32_t access_mask, bool delete_on_close,
uint32_t open_disposition, bool break_to_none);
NTSTATUS (*odb_update_oplock)(struct odb_lock *lck, void *file_handle,
uint32_t oplock_level);
NTSTATUS (*odb_break_oplocks)(struct odb_lock *lck);
};
struct opendb_oplock_break {

View File

@ -146,7 +146,10 @@ static DATA_BLOB odb_tdb_get_key(TALLOC_CTX *mem_ctx, struct odb_lock *lck)
return NT_STATUS_OK on no conflict
*/
static NTSTATUS share_conflict(struct opendb_entry *e1, struct opendb_entry *e2)
static NTSTATUS share_conflict(struct opendb_entry *e1,
uint32_t stream_id,
uint32_t share_access,
uint32_t access_mask)
{
/* if either open involves no read.write or delete access then
it can't conflict */
@ -157,18 +160,18 @@ static NTSTATUS share_conflict(struct opendb_entry *e1, struct opendb_entry *e2)
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))) {
if (!(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) {
if (e1->stream_id != stream_id) {
return NT_STATUS_OK;
}
@ -176,20 +179,20 @@ static NTSTATUS share_conflict(struct opendb_entry *e1, struct opendb_entry *e2)
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,
share_access, NTCREATEX_SHARE_ACCESS_WRITE);
CHECK_MASK(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,
share_access, NTCREATEX_SHARE_ACCESS_READ);
CHECK_MASK(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,
share_access, NTCREATEX_SHARE_ACCESS_DELETE);
CHECK_MASK(access_mask, SEC_STD_DELETE,
e1->share_access, NTCREATEX_SHARE_ACCESS_DELETE);
#undef CHECK_MASK
return NT_STATUS_OK;
}
@ -284,6 +287,100 @@ static NTSTATUS odb_oplock_break_send(struct odb_context *odb,
return NT_STATUS_OK;
}
static bool access_attributes_only(uint32_t access_mask,
uint32_t open_disposition)
{
switch (open_disposition) {
case NTCREATEX_DISP_SUPERSEDE:
case NTCREATEX_DISP_OVERWRITE_IF:
case NTCREATEX_DISP_OVERWRITE:
return false;
default:
break;
}
#define CHECK_MASK(m,g) ((m) && (((m) & ~(g))==0) && (((m) & (g)) != 0))
return CHECK_MASK(access_mask,
SEC_STD_SYNCHRONIZE |
SEC_FILE_READ_ATTRIBUTE |
SEC_FILE_WRITE_ATTRIBUTE);
#undef CHECK_MASK
}
static NTSTATUS odb_tdb_open_can_internal(struct odb_context *odb,
const struct opendb_file *file,
uint32_t stream_id, uint32_t share_access,
uint32_t access_mask, bool delete_on_close,
uint32_t open_disposition, bool break_to_none,
bool *_attrs_only)
{
NTSTATUS status;
uint32_t i;
bool attrs_only = false;
/* 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) {
bool oplock_return = OPLOCK_BREAK_TO_LEVEL_II;
/* if this is an attribute only access
* it doesn't conflict with a BACTCH oplock
* but we'll not grant the oplock below
*/
attrs_only = access_attributes_only(access_mask,
open_disposition);
if (attrs_only) {
break;
}
/* 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 */
if (break_to_none) {
oplock_return = OPLOCK_BREAK_TO_NONE;
}
odb_oplock_break_send(odb, &file->entries[i],
oplock_return);
return NT_STATUS_OPLOCK_NOT_GRANTED;
}
}
if (file->delete_on_close) {
/* while delete on close is set, no new opens are allowed */
return NT_STATUS_DELETE_PENDING;
}
if (file->num_entries != 0 && delete_on_close) {
return NT_STATUS_SHARING_VIOLATION;
}
/* check for sharing violations */
for (i=0;i<file->num_entries;i++) {
status = share_conflict(&file->entries[i], stream_id,
share_access, access_mask);
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],
OPLOCK_BREAK_TO_NONE);
return NT_STATUS_OPLOCK_NOT_GRANTED;
}
}
if (_attrs_only) {
*_attrs_only = attrs_only;
}
return NT_STATUS_OK;
}
/*
register an open file in the open files database. This implements the share_access
rules
@ -291,17 +388,18 @@ static NTSTATUS odb_oplock_break_send(struct odb_context *odb,
Note that the path is only used by the delete on close logic, not
for comparing with other filenames
*/
static NTSTATUS odb_tdb_open_file(struct odb_lock *lck, void *file_handle,
uint32_t stream_id, uint32_t share_access,
static NTSTATUS odb_tdb_open_file(struct odb_lock *lck,
void *file_handle, const char *path,
uint32_t stream_id, uint32_t share_access,
uint32_t access_mask, bool delete_on_close,
const char *path,
uint32_t open_disposition, bool break_to_none,
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;
bool attrs_only = false;
if (odb->oplocks == false) {
oplock_level = OPLOCK_NONE;
@ -316,6 +414,13 @@ static NTSTATUS odb_tdb_open_file(struct odb_lock *lck, void *file_handle,
NT_STATUS_NOT_OK_RETURN(status);
}
/* see if it conflicts */
status = odb_tdb_open_can_internal(odb, &file, stream_id,
share_access, access_mask,
delete_on_close, open_disposition,
break_to_none, &attrs_only);
NT_STATUS_NOT_OK_RETURN(status);
/* see if it conflicts */
e.server = odb->ntvfs_ctx->server_id;
e.file_handle = file_handle;
@ -324,61 +429,37 @@ static NTSTATUS odb_tdb_open_file(struct odb_lock *lck, void *file_handle,
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],
OPLOCK_BREAK_TO_LEVEL_II/*TODO*/);
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],
OPLOCK_BREAK_TO_NONE/*TODO*/);
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.
possibly grant an exclusive, batch or level2 oplock
*/
if (oplock_granted != NULL) {
if ((oplock_level == OPLOCK_BATCH ||
oplock_level == OPLOCK_EXCLUSIVE) &&
file.num_entries == 0) {
(*oplock_granted) = oplock_level;
if (oplock_granted) {
if (attrs_only) {
e.oplock_level = OPLOCK_NONE;
*oplock_granted = NO_OPLOCK_RETURN;
} else if (oplock_level == OPLOCK_EXCLUSIVE) {
if (file.num_entries == 0) {
e.oplock_level = OPLOCK_EXCLUSIVE;
*oplock_granted = EXCLUSIVE_OPLOCK_RETURN;
} else {
e.oplock_level = OPLOCK_NONE;
*oplock_granted = NO_OPLOCK_RETURN;
}
} else if (oplock_level == OPLOCK_BATCH) {
if (file.num_entries == 0) {
e.oplock_level = OPLOCK_BATCH;
*oplock_granted = BATCH_OPLOCK_RETURN;
} else {
e.oplock_level = OPLOCK_LEVEL_II;
*oplock_granted = LEVEL_II_OPLOCK_RETURN;
}
} else if (oplock_level == OPLOCK_LEVEL_II) {
e.oplock_level = OPLOCK_LEVEL_II;
*oplock_granted = LEVEL_II_OPLOCK_RETURN;
} else {
(*oplock_granted) = OPLOCK_NONE;
e.oplock_level = OPLOCK_NONE;
*oplock_granted = NO_OPLOCK_RETURN;
}
e.oplock_level = (*oplock_granted);
}
/* it doesn't conflict, so add it to the end */
@ -503,6 +584,43 @@ static NTSTATUS odb_tdb_update_oplock(struct odb_lock *lck, void *file_handle,
return odb_push_record(lck, &file);
}
/*
send oplocks breaks to none to all level2 holders
*/
static NTSTATUS odb_tdb_break_oplocks(struct odb_lock *lck)
{
struct odb_context *odb = lck->odb;
NTSTATUS status;
struct opendb_file file;
int i;
bool modified = true;
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);
/* 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_LEVEL_II) {
/*
* there could be multiple level2 oplocks
* and we just send a break to none to all of them
* without waiting for a release
*/
odb_oplock_break_send(odb, &file.entries[i],
OPLOCK_BREAK_TO_NONE);
file.entries[i].oplock_level = OPLOCK_NONE;
modified = true;
}
}
if (modified) {
return odb_push_record(lck, &file);
}
return NT_STATUS_OK;
}
/*
remove a pending opendb entry
@ -624,14 +742,14 @@ static NTSTATUS odb_tdb_get_delete_on_close(struct odb_context *odb,
create_options and access_mask
*/
static NTSTATUS odb_tdb_can_open(struct odb_lock *lck,
uint32_t share_access, uint32_t create_options,
uint32_t access_mask)
uint32_t stream_id, uint32_t share_access,
uint32_t access_mask, bool delete_on_close,
uint32_t open_disposition, bool break_to_none)
{
struct odb_context *odb = lck->odb;
NTSTATUS status;
struct opendb_file file;
struct opendb_entry e;
int i;
bool attrs_only = false;
status = odb_pull_record(lck, &file);
if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
@ -639,32 +757,11 @@ static NTSTATUS odb_tdb_can_open(struct odb_lock *lck,
}
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;
}
}
status = odb_tdb_open_can_internal(odb, &file, stream_id,
share_access, access_mask,
delete_on_close, open_disposition,
break_to_none, &attrs_only);
NT_STATUS_NOT_OK_RETURN(status);
return NT_STATUS_OK;
}
@ -682,7 +779,8 @@ static const struct opendb_ops opendb_tdb_ops = {
.odb_set_delete_on_close = odb_tdb_set_delete_on_close,
.odb_get_delete_on_close = odb_tdb_get_delete_on_close,
.odb_can_open = odb_tdb_can_open,
.odb_update_oplock = odb_tdb_update_oplock
.odb_update_oplock = odb_tdb_update_oplock,
.odb_break_oplocks = odb_tdb_break_oplocks
};

View File

@ -53,6 +53,7 @@ OBJ_FILES = \
pvfs_resolve.o \
pvfs_shortname.o \
pvfs_lock.o \
pvfs_oplock.o \
pvfs_wait.o \
pvfs_seek.o \
pvfs_ioctl.o \

View File

@ -294,6 +294,10 @@ NTSTATUS pvfs_lock(struct ntvfs_module_context *ntvfs,
return ntvfs_map_lock(ntvfs, req, lck);
}
if (lck->lockx.in.mode & LOCKING_ANDX_OPLOCK_RELEASE) {
return pvfs_oplock_release(ntvfs, req, lck);
}
f = pvfs_find_fd(pvfs, req, lck->lockx.in.file.ntvfs);
if (!f) {
return NT_STATUS_INVALID_HANDLE;
@ -303,6 +307,9 @@ NTSTATUS pvfs_lock(struct ntvfs_module_context *ntvfs,
return NT_STATUS_FILE_IS_A_DIRECTORY;
}
status = pvfs_break_level2_oplocks(f);
NT_STATUS_NOT_OK_RETURN(status);
if (lck->lockx.in.timeout != 0 &&
(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
pending = talloc(f, struct pvfs_pending_lock);
@ -338,13 +345,6 @@ NTSTATUS pvfs_lock(struct ntvfs_module_context *ntvfs,
return NT_STATUS_DOS(ERRDOS, ERRnoatomiclocks);
}
if (lck->lockx.in.mode & LOCKING_ANDX_OPLOCK_RELEASE) {
DEBUG(0,("received unexpected oplock break\n"));
talloc_free(pending);
return NT_STATUS_NOT_IMPLEMENTED;
}
/* the unlocks happen first */
locks = lck->lockx.in.locks;

View File

@ -268,6 +268,7 @@ static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs,
f->handle->seek_offset = 0;
f->handle->position = 0;
f->handle->mode = 0;
f->handle->oplock = NULL;
f->handle->sticky_write_time = false;
f->handle->open_completed = false;
@ -296,9 +297,10 @@ static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs,
}
/* see if we are allowed to open at the same time as existing opens */
status = odb_open_file(lck, f->handle, f->handle->name->stream_id,
status = odb_open_file(lck, f->handle, name->full_name, name->stream_id,
share_access, access_mask, del_on_close,
name->full_name, OPLOCK_NONE, NULL);
io->generic.in.open_disposition,
false, OPLOCK_NONE, NULL);
if (!NT_STATUS_IS_OK(status)) {
talloc_free(lck);
@ -349,9 +351,10 @@ static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs,
return NT_STATUS_INTERNAL_DB_CORRUPTION;
}
status = odb_open_file(lck, f->handle, f->handle->name->stream_id,
status = odb_open_file(lck, f->handle, name->full_name, name->stream_id,
share_access, access_mask, del_on_close,
name->full_name, OPLOCK_NONE, NULL);
io->generic.in.open_disposition,
false, OPLOCK_NONE, NULL);
if (!NT_STATUS_IS_OK(status)) {
goto cleanup_delete;
@ -669,9 +672,10 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
oplock_level = OPLOCK_EXCLUSIVE;
}
status = odb_open_file(lck, f->handle, name->stream_id,
status = odb_open_file(lck, f->handle, name->full_name, name->stream_id,
share_access, access_mask, del_on_close,
name->full_name, oplock_level, &oplock_granted);
io->generic.in.open_disposition,
false, oplock_level, &oplock_granted);
talloc_free(lck);
if (!NT_STATUS_IS_OK(status)) {
/* bad news, we must have hit a race - we don't delete the file
@ -681,9 +685,6 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
return status;
}
if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
oplock_granted = OPLOCK_BATCH;
}
f->ntvfs = h;
f->pvfs = pvfs;
@ -702,12 +703,23 @@ static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
f->handle->seek_offset = 0;
f->handle->position = 0;
f->handle->mode = 0;
f->handle->oplock = NULL;
f->handle->have_opendb_entry = true;
f->handle->sticky_write_time = false;
f->handle->open_completed = false;
DLIST_ADD(pvfs->files.list, f);
if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
oplock_granted = OPLOCK_BATCH;
} else if (oplock_granted != OPLOCK_NONE) {
status = pvfs_setup_oplock(f, oplock_granted);
if (!NT_STATUS_IS_OK(status)) {
talloc_free(lck);
return status;
}
}
/* setup a destructor to avoid file descriptor leaks on
abnormal termination */
talloc_set_destructor(f, pvfs_fnum_destructor);
@ -748,20 +760,25 @@ cleanup_delete:
return status;
}
/*
state of a pending open retry
state of a pending retry
*/
struct pvfs_open_retry {
struct pvfs_odb_retry {
struct ntvfs_module_context *ntvfs;
struct ntvfs_request *req;
union smb_open *io;
struct pvfs_wait *wait_handle;
DATA_BLOB odb_locking_key;
void *io;
void *private_data;
void (*callback)(struct pvfs_odb_retry *r,
struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req,
void *io,
void *private_data,
enum pvfs_wait_notice reason);
};
/* destroy a pending open request */
static int pvfs_retry_destructor(struct pvfs_open_retry *r)
/* destroy a pending request */
static int pvfs_odb_retry_destructor(struct pvfs_odb_retry *r)
{
struct pvfs_state *pvfs = r->ntvfs->private_data;
if (r->odb_locking_key.data) {
@ -775,15 +792,92 @@ static int pvfs_retry_destructor(struct pvfs_open_retry *r)
return 0;
}
/*
retry an open
*/
static void pvfs_open_retry(void *private, enum pvfs_wait_notice reason)
static void pvfs_odb_retry_callback(void *_r, enum pvfs_wait_notice reason)
{
struct pvfs_open_retry *r = private;
struct ntvfs_module_context *ntvfs = r->ntvfs;
struct ntvfs_request *req = r->req;
union smb_open *io = r->io;
struct pvfs_odb_retry *r = talloc_get_type(_r, struct pvfs_odb_retry);
if (reason == PVFS_WAIT_EVENT) {
/*
* The pending odb entry is already removed.
* We use a null locking key to indicate this
* to the destructor.
*/
data_blob_free(&r->odb_locking_key);
}
r->callback(r, r->ntvfs, r->req, r->io, r->private_data, reason);
}
/*
setup for a retry of a request that was rejected
by odb_open_file() or odb_can_open()
*/
NTSTATUS pvfs_odb_retry_setup(struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req,
struct odb_lock *lck,
struct timeval end_time,
void *io,
void *private_data,
void (*callback)(struct pvfs_odb_retry *r,
struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req,
void *io,
void *private_data,
enum pvfs_wait_notice reason))
{
struct pvfs_state *pvfs = ntvfs->private_data;
struct pvfs_odb_retry *r;
struct pvfs_wait *wait_handle;
NTSTATUS status;
r = talloc(req, struct pvfs_odb_retry);
NT_STATUS_HAVE_NO_MEMORY(r);
r->ntvfs = ntvfs;
r->req = req;
r->io = io;
r->private_data = private_data;
r->callback = callback;
r->odb_locking_key = odb_get_key(r, lck);
if (r->odb_locking_key.data == NULL) {
return NT_STATUS_NO_MEMORY;
}
/* setup a pending lock */
status = odb_open_file_pending(lck, r);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
talloc_free(lck);
talloc_set_destructor(r, pvfs_odb_retry_destructor);
wait_handle = pvfs_wait_message(pvfs, req,
MSG_PVFS_RETRY_OPEN, end_time,
pvfs_odb_retry_callback, r);
if (wait_handle == NULL) {
return NT_STATUS_NO_MEMORY;
}
talloc_steal(r, wait_handle);
talloc_steal(pvfs, r);
return NT_STATUS_OK;
}
/*
retry an open after a sharing violation
*/
static void pvfs_retry_open_sharing(struct pvfs_odb_retry *r,
struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req,
void *_io,
void *private_data,
enum pvfs_wait_notice reason)
{
union smb_open *io = talloc_get_type(_io, union smb_open);
NTSTATUS status;
/* w2k3 ignores SMBntcancel for outstanding open requests. It's probably
@ -792,8 +886,6 @@ static void pvfs_open_retry(void *private, enum pvfs_wait_notice reason)
return;
}
talloc_free(r->wait_handle);
if (reason == PVFS_WAIT_TIMEOUT) {
/* if it timed out, then give the failure
immediately */
@ -803,9 +895,6 @@ static void pvfs_open_retry(void *private, enum pvfs_wait_notice reason)
return;
}
/* the pending odb entry is already removed. We use a null locking
key to indicate this */
data_blob_free(&r->odb_locking_key);
talloc_free(r);
/* try the open again, which could trigger another retry setup
@ -919,10 +1008,10 @@ static NTSTATUS pvfs_open_setup_retry(struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req,
union smb_open *io,
struct pvfs_file *f,
struct odb_lock *lck)
struct odb_lock *lck,
NTSTATUS parent_status)
{
struct pvfs_state *pvfs = ntvfs->private_data;
struct pvfs_open_retry *r;
NTSTATUS status;
struct timeval end_time;
@ -936,40 +1025,21 @@ static NTSTATUS pvfs_open_setup_retry(struct ntvfs_module_context *ntvfs,
}
}
r = talloc(req, struct pvfs_open_retry);
if (r == NULL) {
return NT_STATUS_NO_MEMORY;
}
r->ntvfs = ntvfs;
r->req = req;
r->io = io;
r->odb_locking_key = data_blob_talloc(r,
f->handle->odb_locking_key.data,
f->handle->odb_locking_key.length);
end_time = timeval_add(&req->statistics.request_time, 0, pvfs->sharing_violation_delay);
/* setup a pending lock */
status = odb_open_file_pending(lck, r);
if (!NT_STATUS_IS_OK(status)) {
return status;
}
talloc_free(lck);
/* the retry should allocate a new file handle */
talloc_free(f);
talloc_set_destructor(r, pvfs_retry_destructor);
r->wait_handle = pvfs_wait_message(pvfs, req, MSG_PVFS_RETRY_OPEN, end_time,
pvfs_open_retry, r);
if (r->wait_handle == NULL) {
return NT_STATUS_NO_MEMORY;
if (NT_STATUS_EQUAL(parent_status, NT_STATUS_SHARING_VIOLATION)) {
end_time = timeval_add(&req->statistics.request_time,
0, pvfs->sharing_violation_delay);
} else if (NT_STATUS_EQUAL(parent_status, NT_STATUS_OPLOCK_NOT_GRANTED)) {
end_time = timeval_add(&req->statistics.request_time,
pvfs->oplock_break_timeout, 0);
} else {
return NT_STATUS_INTERNAL_ERROR;
}
talloc_steal(pvfs, r);
return NT_STATUS_OK;
return pvfs_odb_retry_setup(ntvfs, req, lck, end_time, io, NULL,
pvfs_retry_open_sharing);
}
/*
@ -1133,6 +1203,7 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
f->handle->seek_offset = 0;
f->handle->position = 0;
f->handle->mode = 0;
f->handle->oplock = NULL;
f->handle->have_opendb_entry = false;
f->handle->sticky_write_time = false;
f->handle->open_completed = false;
@ -1186,15 +1257,21 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
}
/* see if we are allowed to open at the same time as existing opens */
status = odb_open_file(lck, f->handle, f->handle->name->stream_id,
status = odb_open_file(lck, f->handle, name->full_name, name->stream_id,
share_access, access_mask, del_on_close,
name->full_name, oplock_level, &oplock_granted);
io->generic.in.open_disposition,
false, oplock_level, &oplock_granted);
/* on a sharing violation we need to retry when the file is closed by
the other user, or after 1 second */
if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) &&
/*
* on a sharing violation we need to retry when the file is closed by
* the other user, or after 1 second
* on a non granted oplock we need to retry when the file is closed by
* the other user, or after 30 seconds
*/
if ((NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) ||
NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) &&
(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
return pvfs_open_setup_retry(ntvfs, req, io, f, lck);
return pvfs_open_setup_retry(ntvfs, req, io, f, lck, status);
}
if (!NT_STATUS_IS_OK(status)) {
@ -1204,6 +1281,12 @@ NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
oplock_granted = OPLOCK_BATCH;
} else if (oplock_granted != OPLOCK_NONE) {
status = pvfs_setup_oplock(f, oplock_granted);
if (!NT_STATUS_IS_OK(status)) {
talloc_free(lck);
return status;
}
}
f->handle->have_opendb_entry = true;
@ -1424,6 +1507,9 @@ NTSTATUS pvfs_can_delete(struct pvfs_state *pvfs,
NTSTATUS status;
DATA_BLOB key;
struct odb_lock *lck;
uint32_t share_access;
uint32_t access_mask;
bool delete_on_close;
status = pvfs_locking_key(name, name, &key);
if (!NT_STATUS_IS_OK(status)) {
@ -1436,15 +1522,18 @@ NTSTATUS pvfs_can_delete(struct pvfs_state *pvfs,
return NT_STATUS_INTERNAL_DB_CORRUPTION;
}
status = odb_can_open(lck,
NTCREATEX_SHARE_ACCESS_READ |
NTCREATEX_SHARE_ACCESS_WRITE |
NTCREATEX_SHARE_ACCESS_DELETE,
NTCREATEX_OPTIONS_DELETE_ON_CLOSE,
SEC_STD_DELETE);
share_access = NTCREATEX_SHARE_ACCESS_READ |
NTCREATEX_SHARE_ACCESS_WRITE |
NTCREATEX_SHARE_ACCESS_DELETE;
access_mask = SEC_STD_DELETE;
delete_on_close = true;
status = odb_can_open(lck, name->stream_id,
share_access, access_mask, delete_on_close,
0, false);
if (NT_STATUS_IS_OK(status)) {
status = pvfs_access_check_simple(pvfs, req, name, SEC_STD_DELETE);
status = pvfs_access_check_simple(pvfs, req, name, access_mask);
}
/*
@ -1483,6 +1572,9 @@ NTSTATUS pvfs_can_rename(struct pvfs_state *pvfs,
NTSTATUS status;
DATA_BLOB key;
struct odb_lock *lck;
uint32_t share_access;
uint32_t access_mask;
bool delete_on_close;
status = pvfs_locking_key(name, name, &key);
if (!NT_STATUS_IS_OK(status)) {
@ -1495,11 +1587,76 @@ NTSTATUS pvfs_can_rename(struct pvfs_state *pvfs,
return NT_STATUS_INTERNAL_DB_CORRUPTION;
}
status = odb_can_open(lck,
NTCREATEX_SHARE_ACCESS_READ |
NTCREATEX_SHARE_ACCESS_WRITE,
0,
SEC_STD_DELETE);
share_access = NTCREATEX_SHARE_ACCESS_READ |
NTCREATEX_SHARE_ACCESS_WRITE;
access_mask = SEC_STD_DELETE;
delete_on_close = false;
status = odb_can_open(lck, name->stream_id,
share_access, access_mask, delete_on_close,
0, false);
/*
* if it's a sharing violation or we got no oplock
* only keep the lock if the caller requested access
* to the lock
*/
if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) ||
NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) {
if (lckp) {
*lckp = lck;
} else {
talloc_free(lck);
}
} else if (!NT_STATUS_IS_OK(status)) {
talloc_free(lck);
if (lckp) {
*lckp = NULL;
}
} else if (lckp) {
*lckp = lck;
}
return status;
}
/*
determine if the file size of a file can be changed,
or if it is prevented by an already open file
*/
NTSTATUS pvfs_can_update_file_size(struct pvfs_state *pvfs,
struct ntvfs_request *req,
struct pvfs_filename *name,
struct odb_lock **lckp)
{
NTSTATUS status;
DATA_BLOB key;
struct odb_lock *lck;
uint32_t share_access;
uint32_t access_mask;
bool break_to_none;
bool delete_on_close;
status = pvfs_locking_key(name, name, &key);
if (!NT_STATUS_IS_OK(status)) {
return NT_STATUS_NO_MEMORY;
}
lck = odb_lock(req, pvfs->odb_context, &key);
if (lck == NULL) {
DEBUG(0,("Unable to lock opendb for can_stat\n"));
return NT_STATUS_INTERNAL_DB_CORRUPTION;
}
/* TODO: this may needs some more flags */
share_access = NTCREATEX_SHARE_ACCESS_WRITE;
access_mask = 0;
delete_on_close = false;
break_to_none = true;
status = odb_can_open(lck, name->stream_id,
share_access, access_mask, delete_on_close,
0, break_to_none);
/*
* if it's a sharing violation or we got no oplock
@ -1536,6 +1693,9 @@ NTSTATUS pvfs_can_stat(struct pvfs_state *pvfs,
NTSTATUS status;
DATA_BLOB key;
struct odb_lock *lck;
uint32_t share_access;
uint32_t access_mask;
bool delete_on_close;
status = pvfs_locking_key(name, name, &key);
if (!NT_STATUS_IS_OK(status)) {
@ -1548,10 +1708,14 @@ NTSTATUS pvfs_can_stat(struct pvfs_state *pvfs,
return NT_STATUS_INTERNAL_DB_CORRUPTION;
}
status = odb_can_open(lck,
NTCREATEX_SHARE_ACCESS_READ |
NTCREATEX_SHARE_ACCESS_WRITE,
0, 0);
share_access = NTCREATEX_SHARE_ACCESS_READ |
NTCREATEX_SHARE_ACCESS_WRITE;
access_mask = 0;
delete_on_close = false;
status = odb_can_open(lck, name->stream_id,
share_access, access_mask, delete_on_close,
0, false);
if (!NT_STATUS_IS_OK(status)) {
talloc_free(lck);

View File

@ -0,0 +1,239 @@
/*
Unix SMB/CIFS implementation.
POSIX NTVFS backend - oplock handling
Copyright (C) Stefan Metzmacher 2008
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "includes.h"
#include "lib/messaging/messaging.h"
#include "lib/messaging/irpc.h"
#include "vfs_posix.h"
struct pvfs_oplock {
struct pvfs_file_handle *handle;
struct pvfs_file *file;
uint32_t level;
struct messaging_context *msg_ctx;
};
/*
receive oplock breaks and forward them to the client
*/
static void pvfs_oplock_break(struct pvfs_oplock *opl, uint8_t level)
{
NTSTATUS status;
struct pvfs_file *f = opl->file;
struct pvfs_file_handle *h = opl->handle;
struct pvfs_state *pvfs = h->pvfs;
DEBUG(10,("pvfs_oplock_break: sending oplock break level %d for '%s' %p\n",
level, h->name->original_name, h));
status = ntvfs_send_oplock_break(pvfs->ntvfs, f->ntvfs, level);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(0,("pvfs_oplock_break: sending oplock break failed: %s\n",
nt_errstr(status)));
}
}
static void pvfs_oplock_break_dispatch(struct messaging_context *msg,
void *private_data, uint32_t msg_type,
struct server_id src, DATA_BLOB *data)
{
struct pvfs_oplock *opl = talloc_get_type(private_data,
struct pvfs_oplock);
struct opendb_oplock_break opb;
ZERO_STRUCT(opb);
/* we need to check that this one is for us. See
messaging_send_ptr() for the other side of this.
*/
if (data->length == sizeof(struct opendb_oplock_break)) {
struct opendb_oplock_break *p;
p = (struct opendb_oplock_break *)data->data;
opb = *p;
} else {
DEBUG(0,("%s: ignore oplock break with length[%u]\n",
__location__, data->length));
return;
}
if (opb.file_handle != opl->handle) {
return;
}
/*
* maybe we should use ntvfs_setup_async()
*/
pvfs_oplock_break(opl, opb.level);
}
static int pvfs_oplock_destructor(struct pvfs_oplock *opl)
{
messaging_deregister(opl->msg_ctx, MSG_NTVFS_OPLOCK_BREAK, opl);
return 0;
}
NTSTATUS pvfs_setup_oplock(struct pvfs_file *f, uint32_t oplock_granted)
{
NTSTATUS status;
struct pvfs_oplock *opl;
uint32_t level = OPLOCK_NONE;
f->handle->oplock = NULL;
switch (oplock_granted) {
case EXCLUSIVE_OPLOCK_RETURN:
level = OPLOCK_EXCLUSIVE;
break;
case BATCH_OPLOCK_RETURN:
level = OPLOCK_BATCH;
break;
case LEVEL_II_OPLOCK_RETURN:
level = OPLOCK_LEVEL_II;
break;
}
if (level == OPLOCK_NONE) {
return NT_STATUS_OK;
}
opl = talloc(f->handle, struct pvfs_oplock);
NT_STATUS_HAVE_NO_MEMORY(opl);
opl->handle = f->handle;
opl->file = f;
opl->level = level;
opl->msg_ctx = f->pvfs->ntvfs->ctx->msg_ctx;
status = messaging_register(opl->msg_ctx,
opl,
MSG_NTVFS_OPLOCK_BREAK,
pvfs_oplock_break_dispatch);
NT_STATUS_NOT_OK_RETURN(status);
/* destructor */
talloc_set_destructor(opl, pvfs_oplock_destructor);
f->handle->oplock = opl;
return NT_STATUS_OK;
}
NTSTATUS pvfs_oplock_release(struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req, union smb_lock *lck)
{
struct pvfs_state *pvfs = ntvfs->private_data;
struct pvfs_file *f;
struct pvfs_file_handle *h;
struct odb_lock *olck;
uint8_t oplock_break;
NTSTATUS status;
f = pvfs_find_fd(pvfs, req, lck->lockx.in.file.ntvfs);
if (!f) {
return NT_STATUS_INVALID_HANDLE;
}
h = f->handle;
if (h->fd == -1) {
return NT_STATUS_FILE_IS_A_DIRECTORY;
}
if (!h->have_opendb_entry) {
return NT_STATUS_FOOBAR;
}
if (!h->oplock) {
return NT_STATUS_FOOBAR;
}
olck = odb_lock(h, h->pvfs->odb_context, &h->odb_locking_key);
if (olck == NULL) {
DEBUG(0,("Unable to lock opendb for oplock update\n"));
return NT_STATUS_FOOBAR;
}
oplock_break = (lck->lockx.in.mode >> 8) & 0xFF;
if (oplock_break == OPLOCK_BREAK_TO_NONE) {
h->oplock->level = OPLOCK_NONE;
} else if (oplock_break == OPLOCK_BREAK_TO_LEVEL_II) {
h->oplock->level = OPLOCK_LEVEL_II;
} else {
/* fallback to level II in case of a invalid value */
DEBUG(1,("unexpected oplock break level[0x%02X]\n", oplock_break));
h->oplock->level = OPLOCK_LEVEL_II;
}
status = odb_update_oplock(olck, h, h->oplock->level);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(0,("Unable to update oplock level for '%s' - %s\n",
h->name->full_name, nt_errstr(status)));
talloc_free(olck);
return status;
}
talloc_free(olck);
/* after a break to none, we no longer have an oplock attached */
if (h->oplock->level == OPLOCK_NONE) {
talloc_free(h->oplock);
h->oplock = NULL;
}
return NT_STATUS_OK;
}
NTSTATUS pvfs_break_level2_oplocks(struct pvfs_file *f)
{
struct pvfs_file_handle *h = f->handle;
struct odb_lock *olck;
NTSTATUS status;
if (h->oplock && h->oplock->level == OPLOCK_EXCLUSIVE) {
return NT_STATUS_OK;
}
olck = odb_lock(h, h->pvfs->odb_context, &h->odb_locking_key);
if (olck == NULL) {
DEBUG(0,("Unable to lock opendb for oplock update\n"));
return NT_STATUS_FOOBAR;
}
if (h->oplock && h->oplock->level == OPLOCK_BATCH) {
status = odb_update_oplock(olck, h, OPLOCK_LEVEL_II);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(0,("Unable to update oplock level for '%s' - %s\n",
h->name->full_name, nt_errstr(status)));
talloc_free(olck);
return status;
}
}
status = odb_break_oplocks(olck);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(0,("Unable to break level2 oplocks to none for '%s' - %s\n",
h->name->full_name, nt_errstr(status)));
talloc_free(olck);
return status;
}
talloc_free(olck);
return NT_STATUS_OK;
}

View File

@ -152,6 +152,9 @@ static NTSTATUS pvfs_setfileinfo_rename(struct pvfs_state *pvfs,
}
status = pvfs_can_delete(pvfs, req, name2, NULL);
if (NT_STATUS_EQUAL(status, NT_STATUS_DELETE_PENDING)) {
return NT_STATUS_ACCESS_DENIED;
}
if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION)) {
return NT_STATUS_ACCESS_DENIED;
}
@ -352,6 +355,9 @@ NTSTATUS pvfs_setfileinfo(struct ntvfs_module_context *ntvfs,
case RAW_SFILEINFO_ALLOCATION_INFO:
case RAW_SFILEINFO_ALLOCATION_INFORMATION:
status = pvfs_break_level2_oplocks(f);
NT_STATUS_NOT_OK_RETURN(status);
newstats.dos.alloc_size = info->allocation_info.in.alloc_size;
if (newstats.dos.alloc_size < newstats.st.st_size) {
newstats.st.st_size = newstats.dos.alloc_size;
@ -362,6 +368,9 @@ NTSTATUS pvfs_setfileinfo(struct ntvfs_module_context *ntvfs,
case RAW_SFILEINFO_END_OF_FILE_INFO:
case RAW_SFILEINFO_END_OF_FILE_INFORMATION:
status = pvfs_break_level2_oplocks(f);
NT_STATUS_NOT_OK_RETURN(status);
newstats.st.st_size = info->end_of_file_info.in.size;
break;
@ -463,6 +472,84 @@ NTSTATUS pvfs_setfileinfo(struct ntvfs_module_context *ntvfs,
return pvfs_dosattrib_save(pvfs, h->name, h->fd);
}
/*
retry an open after a sharing violation
*/
static void pvfs_retry_setpathinfo(struct pvfs_odb_retry *r,
struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req,
void *_info,
void *private_data,
enum pvfs_wait_notice reason)
{
union smb_setfileinfo *info = talloc_get_type(_info,
union smb_setfileinfo);
NTSTATUS status = NT_STATUS_INTERNAL_ERROR;
talloc_free(r);
switch (reason) {
case PVFS_WAIT_CANCEL:
/*TODO*/
status = NT_STATUS_CANCELLED;
break;
case PVFS_WAIT_TIMEOUT:
/* if it timed out, then give the failure
immediately */
/*TODO*/
status = NT_STATUS_SHARING_VIOLATION;
break;
case PVFS_WAIT_EVENT:
/* try the open again, which could trigger another retry setup
if it wants to, so we have to unmark the async flag so we
will know if it does a second async reply */
req->async_states->state &= ~NTVFS_ASYNC_STATE_ASYNC;
status = pvfs_setpathinfo(ntvfs, req, info);
if (req->async_states->state & NTVFS_ASYNC_STATE_ASYNC) {
/* the 2nd try also replied async, so we don't send
the reply yet */
return;
}
/* re-mark it async, just in case someone up the chain does
paranoid checking */
req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
break;
}
/* send the reply up the chain */
req->async_states->status = status;
req->async_states->send_fn(req);
}
/*
setup for a unlink retry after a sharing violation
or a non granted oplock
*/
static NTSTATUS pvfs_setpathinfo_setup_retry(struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req,
union smb_setfileinfo *info,
struct odb_lock *lck,
NTSTATUS status)
{
struct pvfs_state *pvfs = ntvfs->private_data;
struct timeval end_time;
if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION)) {
end_time = timeval_add(&req->statistics.request_time,
0, pvfs->sharing_violation_delay);
} else if (NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) {
end_time = timeval_add(&req->statistics.request_time,
pvfs->oplock_break_timeout, 0);
} else {
return NT_STATUS_INTERNAL_ERROR;
}
return pvfs_odb_retry_setup(ntvfs, req, lck, end_time, info, NULL,
pvfs_retry_setpathinfo);
}
/*
set info on a pathname
@ -477,6 +564,7 @@ NTSTATUS pvfs_setpathinfo(struct ntvfs_module_context *ntvfs,
struct utimbuf unix_times;
uint32_t access_needed;
uint32_t change_mask = 0;
struct odb_lock *lck = NULL;
/* resolve the cifs name to a posix name */
status = pvfs_resolve_name(pvfs, req, info->generic.in.file.path,
@ -551,6 +639,20 @@ NTSTATUS pvfs_setpathinfo(struct ntvfs_module_context *ntvfs,
case RAW_SFILEINFO_ALLOCATION_INFO:
case RAW_SFILEINFO_ALLOCATION_INFORMATION:
status = pvfs_can_update_file_size(pvfs, req, name, &lck);
/*
* on a sharing violation we need to retry when the file is closed by
* the other user, or after 1 second
* on a non granted oplock we need to retry when the file is closed by
* the other user, or after 30 seconds
*/
if ((NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) ||
NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) &&
(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
return pvfs_setpathinfo_setup_retry(pvfs->ntvfs, req, info, lck, status);
}
NT_STATUS_NOT_OK_RETURN(status);
if (info->allocation_info.in.alloc_size > newstats.dos.alloc_size) {
/* strange. Increasing the allocation size via setpathinfo
should be silently ignored */
@ -566,6 +668,20 @@ NTSTATUS pvfs_setpathinfo(struct ntvfs_module_context *ntvfs,
case RAW_SFILEINFO_END_OF_FILE_INFO:
case RAW_SFILEINFO_END_OF_FILE_INFORMATION:
status = pvfs_can_update_file_size(pvfs, req, name, &lck);
/*
* on a sharing violation we need to retry when the file is closed by
* the other user, or after 1 second
* on a non granted oplock we need to retry when the file is closed by
* the other user, or after 30 seconds
*/
if ((NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) ||
NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) &&
(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
return pvfs_setpathinfo_setup_retry(pvfs->ntvfs, req, info, lck, status);
}
NT_STATUS_NOT_OK_RETURN(status);
newstats.st.st_size = info->end_of_file_info.in.size;
break;

View File

@ -23,6 +23,84 @@
#include "vfs_posix.h"
#include "system/dir.h"
/*
retry an open after a sharing violation
*/
static void pvfs_retry_unlink(struct pvfs_odb_retry *r,
struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req,
void *_io,
void *private_data,
enum pvfs_wait_notice reason)
{
union smb_unlink *io = talloc_get_type(_io, union smb_unlink);
NTSTATUS status = NT_STATUS_INTERNAL_ERROR;
talloc_free(r);
switch (reason) {
case PVFS_WAIT_CANCEL:
/*TODO*/
status = NT_STATUS_CANCELLED;
break;
case PVFS_WAIT_TIMEOUT:
/* if it timed out, then give the failure
immediately */
/*TODO*/
status = NT_STATUS_SHARING_VIOLATION;
break;
case PVFS_WAIT_EVENT:
/* try the open again, which could trigger another retry setup
if it wants to, so we have to unmark the async flag so we
will know if it does a second async reply */
req->async_states->state &= ~NTVFS_ASYNC_STATE_ASYNC;
status = pvfs_unlink(ntvfs, req, io);
if (req->async_states->state & NTVFS_ASYNC_STATE_ASYNC) {
/* the 2nd try also replied async, so we don't send
the reply yet */
return;
}
/* re-mark it async, just in case someone up the chain does
paranoid checking */
req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
break;
}
/* send the reply up the chain */
req->async_states->status = status;
req->async_states->send_fn(req);
}
/*
setup for a unlink retry after a sharing violation
or a non granted oplock
*/
static NTSTATUS pvfs_unlink_setup_retry(struct ntvfs_module_context *ntvfs,
struct ntvfs_request *req,
union smb_unlink *io,
struct odb_lock *lck,
NTSTATUS status)
{
struct pvfs_state *pvfs = ntvfs->private_data;
struct timeval end_time;
if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION)) {
end_time = timeval_add(&req->statistics.request_time,
0, pvfs->sharing_violation_delay);
} else if (NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) {
end_time = timeval_add(&req->statistics.request_time,
pvfs->oplock_break_timeout, 0);
} else {
return NT_STATUS_INTERNAL_ERROR;
}
return pvfs_odb_retry_setup(ntvfs, req, lck, end_time, io, NULL,
pvfs_retry_unlink);
}
/*
unlink a file
@ -67,6 +145,7 @@ static NTSTATUS pvfs_unlink_one(struct pvfs_state *pvfs,
struct pvfs_filename *name)
{
NTSTATUS status;
struct odb_lock *lck = NULL;
/* make sure its matches the given attributes */
status = pvfs_match_attrib(pvfs, name,
@ -75,7 +154,20 @@ static NTSTATUS pvfs_unlink_one(struct pvfs_state *pvfs,
return status;
}
status = pvfs_can_delete(pvfs, req, name, NULL);
status = pvfs_can_delete(pvfs, req, name, &lck);
/*
* on a sharing violation we need to retry when the file is closed by
* the other user, or after 1 second
* on a non granted oplock we need to retry when the file is closed by
* the other user, or after 30 seconds
*/
if ((NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION) ||
NT_STATUS_EQUAL(status, NT_STATUS_OPLOCK_NOT_GRANTED)) &&
(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
return pvfs_unlink_setup_retry(pvfs->ntvfs, req, unl, lck, status);
}
if (!NT_STATUS_IS_OK(status)) {
return status;
}

View File

@ -57,7 +57,10 @@ NTSTATUS pvfs_write(struct ntvfs_module_context *ntvfs,
wr->writex.in.count,
WRITE_LOCK);
NT_STATUS_NOT_OK_RETURN(status);
status = pvfs_break_level2_oplocks(f);
NT_STATUS_NOT_OK_RETURN(status);
if (f->handle->name->stream_name) {
ret = pvfs_stream_write(pvfs,
f->handle,

View File

@ -91,6 +91,10 @@ static void pvfs_setup_options(struct pvfs_state *pvfs)
PVFS_SHARE_DELAY,
PVFS_SHARE_DELAY_DEFAULT);
pvfs->oplock_break_timeout = share_int_option(scfg,
PVFS_OPLOCK_TIMEOUT,
PVFS_OPLOCK_TIMEOUT_DEFAULT);
pvfs->share_name = talloc_strdup(pvfs, scfg->name);
pvfs->fs_attribs =

View File

@ -29,6 +29,7 @@
#include "dsdb/samdb/samdb.h"
struct pvfs_wait;
struct pvfs_oplock;
/* this is the private structure for the posix vfs backend. It is used
to hold per-connection (per tree connect) state information */
@ -51,9 +52,12 @@ struct pvfs_state {
ntcancel */
struct pvfs_wait *wait_list;
/* the sharing violation timeout */
/* the sharing violation timeout (nsecs) */
uint_t sharing_violation_delay;
/* the oplock break timeout (secs) */
uint_t oplock_break_timeout;
/* filesystem attributes (see FS_ATTR_*) */
uint32_t fs_attribs;
@ -154,6 +158,13 @@ struct pvfs_file_handle {
bool have_opendb_entry;
/*
* we need to wait for oplock break requests from other processes,
* and we need to remember the pvfs_file so we can correctly
* forward the oplock break to the client
*/
struct pvfs_oplock *oplock;
/* we need this hook back to our parent for lock destruction */
struct pvfs_state *pvfs;
@ -230,10 +241,16 @@ struct pvfs_dir;
/* types of notification for pvfs wait events */
enum pvfs_wait_notice {PVFS_WAIT_EVENT, PVFS_WAIT_TIMEOUT, PVFS_WAIT_CANCEL};
/*
state of a pending retry
*/
struct pvfs_odb_retry;
#define PVFS_EADB "posix:eadb"
#define PVFS_XATTR "posix:xattr"
#define PVFS_FAKE_OPLOCKS "posix:fakeoplocks"
#define PVFS_SHARE_DELAY "posix:sharedelay"
#define PVFS_OPLOCK_TIMEOUT "posix:oplocktimeout"
#define PVFS_ALLOCATION_ROUNDING "posix:allocationrounding"
#define PVFS_SEARCH_INACTIVITY "posix:searchinactivity"
#define PVFS_ACL "posix:acl"
@ -241,7 +258,8 @@ enum pvfs_wait_notice {PVFS_WAIT_EVENT, PVFS_WAIT_TIMEOUT, PVFS_WAIT_CANCEL};
#define PVFS_XATTR_DEFAULT true
#define PVFS_FAKE_OPLOCKS_DEFAULT false
#define PVFS_SHARE_DELAY_DEFAULT 1000000
#define PVFS_SHARE_DELAY_DEFAULT 1000000 /* nsecs */
#define PVFS_OPLOCK_TIMEOUT_DEFAULT 30 /* secs */
#define PVFS_ALLOCATION_ROUNDING_DEFAULT 512
#define PVFS_SEARCH_INACTIVITY_DEFAULT 300

View File

@ -3,7 +3,6 @@ local.iconv.*.next_codepoint()
base.delaywrite.finfo update on close
base.delete.*.deltest20a
base.delete.*.deltest20b
raw.oplock.*.OPLOCK
rpc.winreg
local.registry.*.security # Not implemented yet
rpc.wkssvc

View File

@ -1,12 +1,10 @@
base.delaywrite
raw.composite
raw.oplock
base.iometer
base.casetable
base.nttrans
.*base.bench.holdcon.* # Very slow
base.scan.maxfid
raw.bench.oplock
raw.hold.oplock
raw.ping.pong
rpc.samr_accessmask
@ -25,9 +23,7 @@ ntvfs.cifs.base.scan-maxfid
ntvfs.cifs.base.utable
ntvfs.cifs.base.smb
ntvfs.cifs.raw.composite
ntvfs.cifs.raw.oplock
ntvfs.cifs.raw.notify
ntvfs.cifs.raw.bench-oplock
ntvfs.cifs.raw.scan-eamax
ntvfs.cifs.raw.context
ntvfs.cifs.raw.qfileinfo.ipc

View File

@ -562,12 +562,16 @@ sub provision($$$$$$)
gensec:require_pac = true
log level = $smbd_loglevel
# this is a global option
opendb:oplocks = yes
[tmp]
path = $tmpdir
read only = no
ntvfs handler = posix
posix:sharedelay = 100000
posix:eadb = $lockdir/eadb.tdb
posix:oplocktimeout = 3
[test1]
path = $tmpdir/test1
@ -575,6 +579,7 @@ sub provision($$$$$$)
ntvfs handler = posix
posix:sharedelay = 100000
posix:eadb = $lockdir/eadb.tdb
posix:oplocktimeout = 3
[test2]
path = $tmpdir/test2
@ -582,6 +587,7 @@ sub provision($$$$$$)
ntvfs handler = posix
posix:sharedelay = 100000
posix:eadb = $lockdir/eadb.tdb
posix:oplocktimeout = 3
[cifs]
read only = no

View File

@ -664,7 +664,7 @@ static bool run_deferopen(struct torture_context *tctx, struct smbcli_state *cli
}
if (NT_STATUS_EQUAL(smbcli_nt_error(cli->tree),NT_STATUS_SHARING_VIOLATION)) {
double e = timeval_elapsed(&tv);
if (e < (0.5 * sec) || e > (1.5 * sec)) {
if (e < (0.5 * sec) || e > ((1.5 * sec) + 1)) {
torture_comment(tctx,"Timing incorrect %.2f violation 1 sec == %.2f\n",
e, sec);
return false;