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:
commit
4dab354b62
11
howto.txt
11
howto.txt
@ -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.
|
||||
|
@ -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
|
||||
};
|
||||
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
};
|
||||
|
||||
|
||||
|
@ -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 \
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
239
source/ntvfs/posix/pvfs_oplock.c
Normal file
239
source/ntvfs/posix/pvfs_oplock.c
Normal 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;
|
||||
}
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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 =
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user