summaryrefslogtreecommitdiffstats
path: root/source4/ntvfs
diff options
context:
space:
mode:
Diffstat (limited to 'source4/ntvfs')
-rw-r--r--source4/ntvfs/README26
-rw-r--r--source4/ntvfs/cifs/README40
-rw-r--r--source4/ntvfs/cifs/vfs_cifs.c1263
-rw-r--r--source4/ntvfs/common/brlock.c136
-rw-r--r--source4/ntvfs/common/brlock.h55
-rw-r--r--source4/ntvfs/common/brlock_tdb.c775
-rw-r--r--source4/ntvfs/common/init.c34
-rw-r--r--source4/ntvfs/common/notify.c687
-rw-r--r--source4/ntvfs/common/ntvfs_common.h32
-rw-r--r--source4/ntvfs/common/opendb.c200
-rw-r--r--source4/ntvfs/common/opendb.h59
-rw-r--r--source4/ntvfs/common/opendb_tdb.c886
-rw-r--r--source4/ntvfs/common/wscript_build9
-rw-r--r--source4/ntvfs/ipc/README5
-rw-r--r--source4/ntvfs/ipc/ipc_rap.c511
-rw-r--r--source4/ntvfs/ipc/rap_server.c95
-rw-r--r--source4/ntvfs/ipc/vfs_ipc.c1356
-rw-r--r--source4/ntvfs/ntvfs.h338
-rw-r--r--source4/ntvfs/ntvfs_base.c249
-rw-r--r--source4/ntvfs/ntvfs_generic.c1648
-rw-r--r--source4/ntvfs/ntvfs_interface.c702
-rw-r--r--source4/ntvfs/ntvfs_util.c197
-rw-r--r--source4/ntvfs/posix/posix_eadb.c299
-rw-r--r--source4/ntvfs/posix/posix_eadb.h20
-rw-r--r--source4/ntvfs/posix/pvfs_acl.c1083
-rw-r--r--source4/ntvfs/posix/pvfs_acl_nfs4.c199
-rw-r--r--source4/ntvfs/posix/pvfs_acl_xattr.c104
-rw-r--r--source4/ntvfs/posix/pvfs_dirlist.c411
-rw-r--r--source4/ntvfs/posix/pvfs_fileinfo.c158
-rw-r--r--source4/ntvfs/posix/pvfs_flush.c80
-rw-r--r--source4/ntvfs/posix/pvfs_fsinfo.c224
-rw-r--r--source4/ntvfs/posix/pvfs_ioctl.c82
-rw-r--r--source4/ntvfs/posix/pvfs_lock.c404
-rw-r--r--source4/ntvfs/posix/pvfs_mkdir.c196
-rw-r--r--source4/ntvfs/posix/pvfs_notify.c300
-rw-r--r--source4/ntvfs/posix/pvfs_open.c2094
-rw-r--r--source4/ntvfs/posix/pvfs_oplock.c307
-rw-r--r--source4/ntvfs/posix/pvfs_qfileinfo.c468
-rw-r--r--source4/ntvfs/posix/pvfs_read.c103
-rw-r--r--source4/ntvfs/posix/pvfs_rename.c675
-rw-r--r--source4/ntvfs/posix/pvfs_resolve.c826
-rw-r--r--source4/ntvfs/posix/pvfs_search.c865
-rw-r--r--source4/ntvfs/posix/pvfs_seek.c65
-rw-r--r--source4/ntvfs/posix/pvfs_setfileinfo.c884
-rw-r--r--source4/ntvfs/posix/pvfs_shortname.c699
-rw-r--r--source4/ntvfs/posix/pvfs_streams.c556
-rw-r--r--source4/ntvfs/posix/pvfs_sys.c662
-rw-r--r--source4/ntvfs/posix/pvfs_unlink.c276
-rw-r--r--source4/ntvfs/posix/pvfs_util.c206
-rw-r--r--source4/ntvfs/posix/pvfs_wait.c212
-rw-r--r--source4/ntvfs/posix/pvfs_write.c145
-rw-r--r--source4/ntvfs/posix/pvfs_xattr.c488
-rw-r--r--source4/ntvfs/posix/python/pyposix_eadb.c140
-rw-r--r--source4/ntvfs/posix/python/pyxattr_native.c129
-rw-r--r--source4/ntvfs/posix/python/pyxattr_tdb.c177
-rw-r--r--source4/ntvfs/posix/vfs_posix.c426
-rw-r--r--source4/ntvfs/posix/vfs_posix.h290
-rw-r--r--source4/ntvfs/posix/wscript_build61
-rw-r--r--source4/ntvfs/posix/xattr_system.c145
-rw-r--r--source4/ntvfs/simple/README10
-rw-r--r--source4/ntvfs/simple/svfs.h38
-rw-r--r--source4/ntvfs/simple/svfs_util.c189
-rw-r--r--source4/ntvfs/simple/vfs_simple.c1112
-rw-r--r--source4/ntvfs/sysdep/README5
-rw-r--r--source4/ntvfs/sysdep/inotify.c398
-rw-r--r--source4/ntvfs/sysdep/sys_lease.c152
-rw-r--r--source4/ntvfs/sysdep/sys_lease.h66
-rw-r--r--source4/ntvfs/sysdep/sys_lease_linux.c215
-rw-r--r--source4/ntvfs/sysdep/sys_notify.c151
-rw-r--r--source4/ntvfs/sysdep/sys_notify.h54
-rw-r--r--source4/ntvfs/sysdep/wscript_build31
-rw-r--r--source4/ntvfs/sysdep/wscript_configure13
-rw-r--r--source4/ntvfs/unixuid/vfs_unixuid.c724
-rw-r--r--source4/ntvfs/unixuid/wscript_build9
-rw-r--r--source4/ntvfs/wscript_build40
75 files changed, 26969 insertions, 0 deletions
diff --git a/source4/ntvfs/README b/source4/ntvfs/README
new file mode 100644
index 0000000..c86c9a0
--- /dev/null
+++ b/source4/ntvfs/README
@@ -0,0 +1,26 @@
+This is the base of the new NTVFS subsystem for Samba. The model for
+NTVFS backends is quite different than for the older style VFS
+backends, in particular:
+
+- the NTVFS backends receive windows style file names, although they
+ are in the unix charset (usually UTF8). This means the backend is
+ responsible for mapping windows filename conventions to unix
+ filename conventions if necessary
+
+- the NTVFS backends are responsible for changing effective UID before
+ calling any OS local filesystem operations (if needed). The
+ become_*() functions are provided to make this easier.
+
+- the NTVFS backends are responsible for resolving DFS paths
+
+- each NTVFS backend handles either disk, printer or IPC$ shares,
+ rather than one backend handling all types
+
+- the entry points of the NTVFS backends correspond closely with basic
+ SMB operations, wheres the old VFS was modelled directly on the
+ POSIX filesystem interface.
+
+- the NTVFS backends are responsible for all semantic mappings, such
+ as mapping dos file attributes, ACLs, file ownership and file times
+
+
diff --git a/source4/ntvfs/cifs/README b/source4/ntvfs/cifs/README
new file mode 100644
index 0000000..f9ab50c
--- /dev/null
+++ b/source4/ntvfs/cifs/README
@@ -0,0 +1,40 @@
+This is the 'CIFS on CIFS' backend for Samba. It provides a NTVFS
+backend that talks to a remote CIFS server. The primary aim of this
+backend is for debugging and development, although some people may
+find it useful as a CIFS gateway.
+
+There are two modes of operation: Password specified and delegated
+credentials.
+
+Password specified:
+-------------------
+
+This uses a static username/password in the config file, example:
+
+[myshare]
+ ntvfs handler = cifs
+ cifs:server = myserver
+ cifs:user = tridge
+ cifs:password = mypass
+ cifs:domain = TESTDOM
+ cifs:share = test
+
+
+Delegated credentials:
+----------------------
+
+If your incoming user is authenticated with Kerberos, and the machine
+account for this Samba4 proxy server is 'trusted for delegation', then
+the Samba4 proxy can forward the client's credentials to the target.
+
+You must be joined to the domain (net join <domain> member).
+
+To set 'trusted for delegation' with MMC, see the checkbox in the
+Computer account property page under Users and Computers.
+
+[myshare]
+ ntvfs handler = cifs
+ cifs:server = myserver
+ cifs:share = test
+
+
diff --git a/source4/ntvfs/cifs/vfs_cifs.c b/source4/ntvfs/cifs/vfs_cifs.c
new file mode 100644
index 0000000..121ff57
--- /dev/null
+++ b/source4/ntvfs/cifs/vfs_cifs.c
@@ -0,0 +1,1263 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ CIFS-on-CIFS NTVFS filesystem backend
+
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) James J Myers 2003 <myersjj@samba.org>
+
+ 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/>.
+*/
+/*
+ this implements a CIFS->CIFS NTVFS filesystem backend.
+
+*/
+
+#include "includes.h"
+#include "libcli/raw/libcliraw.h"
+#include "libcli/raw/raw_proto.h"
+#include "libcli/smb_composite/smb_composite.h"
+#include "auth/auth.h"
+#include "auth/credentials/credentials.h"
+#include "ntvfs/ntvfs.h"
+#include "../lib/util/dlinklist.h"
+#include "param/param.h"
+#include "libcli/resolve/resolve.h"
+#include "../libcli/smb/smbXcli_base.h"
+
+struct cvfs_file {
+ struct cvfs_file *prev, *next;
+ uint16_t fnum;
+ struct ntvfs_handle *h;
+};
+
+/* this is stored in ntvfs_private */
+struct cvfs_private {
+ struct smbcli_tree *tree;
+ struct smbcli_transport *transport;
+ struct ntvfs_module_context *ntvfs;
+ struct async_info *pending;
+ struct cvfs_file *files;
+ bool map_generic;
+ bool map_trans2;
+};
+
+
+/* a structure used to pass information to an async handler */
+struct async_info {
+ struct async_info *next, *prev;
+ struct cvfs_private *cvfs;
+ struct ntvfs_request *req;
+ struct smbcli_request *c_req;
+ struct cvfs_file *f;
+ void *parms;
+};
+
+NTSTATUS ntvfs_cifs_init(TALLOC_CTX *);
+
+#define CHECK_UPSTREAM_OPEN do { \
+ if (!smbXcli_conn_is_connected(p->transport->conn)) { \
+ req->async_states->state|=NTVFS_ASYNC_STATE_CLOSE; \
+ return NT_STATUS_CONNECTION_DISCONNECTED; \
+ } \
+} while(0)
+
+#define SETUP_PID do { \
+ p->tree->session->pid = req->smbpid; \
+ CHECK_UPSTREAM_OPEN; \
+} while(0)
+
+#define SETUP_FILE_HERE(f) do { \
+ f = ntvfs_handle_get_backend_data(io->generic.in.file.ntvfs, ntvfs); \
+ if (!f) return NT_STATUS_INVALID_HANDLE; \
+ io->generic.in.file.fnum = f->fnum; \
+} while (0)
+
+#define SETUP_FILE do { \
+ struct cvfs_file *f; \
+ SETUP_FILE_HERE(f); \
+} while (0)
+
+#define SETUP_PID_AND_FILE do { \
+ SETUP_PID; \
+ SETUP_FILE; \
+} while (0)
+
+#define CIFS_SERVER "cifs:server"
+#define CIFS_USER "cifs:user"
+#define CIFS_PASSWORD "cifs:password"
+#define CIFS_DOMAIN "cifs:domain"
+#define CIFS_SHARE "cifs:share"
+#define CIFS_USE_MACHINE_ACCT "cifs:use-machine-account"
+#define CIFS_USE_S4U2PROXY "cifs:use-s4u2proxy"
+#define CIFS_MAP_GENERIC "cifs:map-generic"
+#define CIFS_MAP_TRANS2 "cifs:map-trans2"
+
+#define CIFS_USE_MACHINE_ACCT_DEFAULT false
+#define CIFS_USE_S4U2PROXY_DEFAULT false
+#define CIFS_MAP_GENERIC_DEFAULT false
+#define CIFS_MAP_TRANS2_DEFAULT true
+
+/*
+ a handler for oplock break events from the server - these need to be passed
+ along to the client
+ */
+static bool oplock_handler(struct smbcli_transport *transport, uint16_t tid, uint16_t fnum, uint8_t level, void *p_private)
+{
+ struct cvfs_private *p = p_private;
+ NTSTATUS status;
+ struct ntvfs_handle *h = NULL;
+ struct cvfs_file *f;
+
+ for (f=p->files; f; f=f->next) {
+ if (f->fnum != fnum) continue;
+ h = f->h;
+ break;
+ }
+
+ if (!h) {
+ DEBUG(5,("vfs_cifs: ignoring oplock break level %d for fnum %d\n", level, fnum));
+ return true;
+ }
+
+ DEBUG(5,("vfs_cifs: sending oplock break level %d for fnum %d\n", level, fnum));
+ status = ntvfs_send_oplock_break(p->ntvfs, h, level);
+ if (!NT_STATUS_IS_OK(status)) return false;
+ return true;
+}
+
+/*
+ connect to a share - used when a tree_connect operation comes in.
+*/
+static NTSTATUS cvfs_connect(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_tcon *tcon)
+{
+ NTSTATUS status;
+ struct cvfs_private *p;
+ const char *host, *user, *pass, *domain, *remote_share;
+ struct smb_composite_connect io;
+ struct composite_context *creq;
+ struct share_config *scfg = ntvfs->ctx->config;
+
+ struct cli_credentials *credentials;
+ bool machine_account;
+ bool s4u2proxy;
+ const char* sharename;
+ TALLOC_CTX *tmp_ctx;
+
+ tmp_ctx = talloc_new(req);
+ if (tmp_ctx == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ switch (tcon->generic.level) {
+ case RAW_TCON_TCON:
+ sharename = tcon->tcon.in.service;
+ break;
+ case RAW_TCON_TCONX:
+ sharename = tcon->tconx.in.path;
+ break;
+ case RAW_TCON_SMB2:
+ sharename = tcon->smb2.in.path;
+ break;
+ default:
+ status = NT_STATUS_INVALID_LEVEL;
+ goto out;
+ }
+
+ if (strncmp(sharename, "\\\\", 2) == 0) {
+ char *str = strchr(sharename+2, '\\');
+ if (str) {
+ sharename = str + 1;
+ }
+ }
+
+ /* Here we need to determine which server to connect to.
+ * For now we use parametric options, type cifs.
+ */
+ host = share_string_option(tmp_ctx, scfg, CIFS_SERVER, NULL);
+ user = share_string_option(tmp_ctx, scfg, CIFS_USER, NULL);
+ pass = share_string_option(tmp_ctx, scfg, CIFS_PASSWORD, NULL);
+ domain = share_string_option(tmp_ctx, scfg, CIFS_DOMAIN, NULL);
+ remote_share = share_string_option(tmp_ctx, scfg, CIFS_SHARE, NULL);
+ if (!remote_share) {
+ remote_share = sharename;
+ }
+
+ machine_account = share_bool_option(scfg, CIFS_USE_MACHINE_ACCT, CIFS_USE_MACHINE_ACCT_DEFAULT);
+ s4u2proxy = share_bool_option(scfg, CIFS_USE_S4U2PROXY, CIFS_USE_S4U2PROXY_DEFAULT);
+
+ p = talloc_zero(ntvfs, struct cvfs_private);
+ if (!p) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ ntvfs->private_data = p;
+
+ if (!host) {
+ DEBUG(1,("CIFS backend: You must supply server\n"));
+ status = NT_STATUS_INVALID_PARAMETER;
+ goto out;
+ }
+
+ if (user && pass) {
+ DEBUG(5, ("CIFS backend: Using specified password\n"));
+ credentials = cli_credentials_init(p);
+ if (!credentials) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+ cli_credentials_set_conf(credentials, ntvfs->ctx->lp_ctx);
+ cli_credentials_set_username(credentials, user, CRED_SPECIFIED);
+ if (domain) {
+ cli_credentials_set_domain(credentials, domain, CRED_SPECIFIED);
+ }
+ cli_credentials_set_password(credentials, pass, CRED_SPECIFIED);
+ } else if (machine_account) {
+ DEBUG(5, ("CIFS backend: Using machine account\n"));
+ credentials = cli_credentials_init_server(p,
+ ntvfs->ctx->lp_ctx);
+ if (credentials == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+ } else if (req->session_info->credentials) {
+ DEBUG(5, ("CIFS backend: Using delegated credentials\n"));
+ credentials = req->session_info->credentials;
+ } else if (s4u2proxy) {
+ struct ccache_container *ccc = NULL;
+ const char *err_str = NULL;
+ int ret;
+ char *impersonate_principal;
+ char *self_service;
+ char *target_service;
+
+ impersonate_principal = talloc_asprintf(req, "%s@%s",
+ req->session_info->info->account_name,
+ req->session_info->info->domain_name);
+
+ self_service = talloc_asprintf(req, "cifs/%s",
+ lpcfg_netbios_name(ntvfs->ctx->lp_ctx));
+
+ target_service = talloc_asprintf(req, "cifs/%s", host);
+
+ DEBUG(5, ("CIFS backend: Using S4U2Proxy credentials\n"));
+
+ credentials = cli_credentials_init_server(p,
+ ntvfs->ctx->lp_ctx);
+ if (credentials == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+ cli_credentials_invalidate_ccache(credentials, CRED_SPECIFIED);
+ cli_credentials_set_impersonate_principal(credentials,
+ impersonate_principal,
+ self_service);
+ cli_credentials_set_target_service(credentials, target_service);
+ ret = cli_credentials_get_ccache(credentials,
+ ntvfs->ctx->event_ctx,
+ ntvfs->ctx->lp_ctx,
+ &ccc,
+ &err_str);
+ if (ret != 0) {
+ status = NT_STATUS_CROSSREALM_DELEGATION_FAILURE;
+ DEBUG(1,("S4U2Proxy: cli_credentials_get_ccache() gave: ret[%d] str[%s] - %s\n",
+ ret, err_str, nt_errstr(status)));
+ goto out;
+ }
+
+ } else {
+ DEBUG(1,("CIFS backend: NO delegated credentials found: You must supply server, user and password or the client must supply delegated credentials\n"));
+ status = NT_STATUS_INTERNAL_ERROR;
+ goto out;
+ }
+
+ /* connect to the server, using the smbd event context */
+ io.in.dest_host = host;
+ io.in.dest_ports = lpcfg_smb_ports(ntvfs->ctx->lp_ctx);
+ io.in.socket_options = lpcfg_socket_options(ntvfs->ctx->lp_ctx);
+ io.in.called_name = host;
+ io.in.existing_conn = NULL;
+ io.in.credentials = credentials;
+ io.in.fallback_to_anonymous = false;
+ io.in.workgroup = lpcfg_workgroup(ntvfs->ctx->lp_ctx);
+ io.in.service = remote_share;
+ io.in.service_type = "?????";
+ io.in.gensec_settings = lpcfg_gensec_settings(p, ntvfs->ctx->lp_ctx);
+ lpcfg_smbcli_options(ntvfs->ctx->lp_ctx, &io.in.options);
+ lpcfg_smbcli_session_options(ntvfs->ctx->lp_ctx, &io.in.session_options);
+
+ if (!(ntvfs->ctx->client_caps & NTVFS_CLIENT_CAP_LEVEL_II_OPLOCKS)) {
+ io.in.options.use_level2_oplocks = false;
+ }
+
+ creq = smb_composite_connect_send(&io, p,
+ lpcfg_resolve_context(ntvfs->ctx->lp_ctx),
+ ntvfs->ctx->event_ctx);
+ status = smb_composite_connect_recv(creq, p);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto out;
+ }
+
+ p->tree = io.out.tree;
+
+ p->transport = p->tree->session->transport;
+ SETUP_PID;
+ p->ntvfs = ntvfs;
+
+ ntvfs->ctx->fs_type = talloc_strdup(ntvfs->ctx, "NTFS");
+ if (ntvfs->ctx->fs_type == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+ ntvfs->ctx->dev_type = talloc_strdup(ntvfs->ctx, "A:");
+ if (ntvfs->ctx->dev_type == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto out;
+ }
+
+ if (tcon->generic.level == RAW_TCON_TCONX) {
+ tcon->tconx.out.fs_type = ntvfs->ctx->fs_type;
+ tcon->tconx.out.dev_type = ntvfs->ctx->dev_type;
+ }
+
+ /* we need to receive oplock break requests from the server */
+ smbcli_oplock_handler(p->transport, oplock_handler, p);
+
+ p->map_generic = share_bool_option(scfg, CIFS_MAP_GENERIC, CIFS_MAP_GENERIC_DEFAULT);
+
+ p->map_trans2 = share_bool_option(scfg, CIFS_MAP_TRANS2, CIFS_MAP_TRANS2_DEFAULT);
+
+ status = NT_STATUS_OK;
+
+out:
+ TALLOC_FREE(tmp_ctx);
+ return status;
+}
+
+/*
+ disconnect from a share
+*/
+static NTSTATUS cvfs_disconnect(struct ntvfs_module_context *ntvfs)
+{
+ struct cvfs_private *p = ntvfs->private_data;
+ struct async_info *a, *an;
+
+ /* first cleanup pending requests */
+ for (a=p->pending; a; a = an) {
+ an = a->next;
+ smbcli_request_destroy(a->c_req);
+ talloc_free(a);
+ }
+
+ talloc_free(p);
+ ntvfs->private_data = NULL;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ destroy an async info structure
+*/
+static int async_info_destructor(struct async_info *async)
+{
+ DLIST_REMOVE(async->cvfs->pending, async);
+ return 0;
+}
+
+/*
+ a handler for simple async replies
+ this handler can only be used for functions that don't return any
+ parameters (those that just return a status code)
+ */
+static void async_simple(struct smbcli_request *c_req)
+{
+ struct async_info *async = c_req->async.private_data;
+ struct ntvfs_request *req = async->req;
+ req->async_states->status = smbcli_request_simple_recv(c_req);
+ talloc_free(async);
+ req->async_states->send_fn(req);
+}
+
+
+/* save some typing for the simple functions */
+#define ASYNC_RECV_TAIL_F(io, async_fn, file) do { \
+ if (!c_req) return NT_STATUS_UNSUCCESSFUL; \
+ { \
+ struct async_info *async; \
+ async = talloc(req, struct async_info); \
+ if (!async) return NT_STATUS_NO_MEMORY; \
+ async->parms = io; \
+ async->req = req; \
+ async->f = file; \
+ async->cvfs = p; \
+ async->c_req = c_req; \
+ DLIST_ADD(p->pending, async); \
+ c_req->async.private_data = async; \
+ talloc_set_destructor(async, async_info_destructor); \
+ } \
+ c_req->async.fn = async_fn; \
+ req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC; \
+ return NT_STATUS_OK; \
+} while (0)
+
+#define ASYNC_RECV_TAIL(io, async_fn) ASYNC_RECV_TAIL_F(io, async_fn, NULL)
+
+#define SIMPLE_ASYNC_TAIL ASYNC_RECV_TAIL(NULL, async_simple)
+
+/*
+ delete a file - the dirtype specifies the file types to include in the search.
+ The name can contain CIFS wildcards, but rarely does (except with OS/2 clients)
+*/
+static NTSTATUS cvfs_unlink(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_unlink *unl)
+{
+ struct cvfs_private *p = ntvfs->private_data;
+ struct smbcli_request *c_req;
+
+ SETUP_PID;
+
+ /* see if the front end will allow us to perform this
+ function asynchronously. */
+ if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
+ return smb_raw_unlink(p->tree, unl);
+ }
+
+ c_req = smb_raw_unlink_send(p->tree, unl);
+
+ SIMPLE_ASYNC_TAIL;
+}
+
+/*
+ a handler for async ioctl replies
+ */
+static void async_ioctl(struct smbcli_request *c_req)
+{
+ struct async_info *async = c_req->async.private_data;
+ struct ntvfs_request *req = async->req;
+ req->async_states->status = smb_raw_ioctl_recv(c_req, req, async->parms);
+ talloc_free(async);
+ req->async_states->send_fn(req);
+}
+
+/*
+ ioctl interface
+*/
+static NTSTATUS cvfs_ioctl(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_ioctl *io)
+{
+ struct cvfs_private *p = ntvfs->private_data;
+ struct smbcli_request *c_req;
+
+ SETUP_PID_AND_FILE;
+
+ /* see if the front end will allow us to perform this
+ function asynchronously. */
+ if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
+ return smb_raw_ioctl(p->tree, req, io);
+ }
+
+ c_req = smb_raw_ioctl_send(p->tree, io);
+
+ ASYNC_RECV_TAIL(io, async_ioctl);
+}
+
+/*
+ check if a directory exists
+*/
+static NTSTATUS cvfs_chkpath(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_chkpath *cp)
+{
+ struct cvfs_private *p = ntvfs->private_data;
+ struct smbcli_request *c_req;
+
+ SETUP_PID;
+
+ if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
+ return smb_raw_chkpath(p->tree, cp);
+ }
+
+ c_req = smb_raw_chkpath_send(p->tree, cp);
+
+ SIMPLE_ASYNC_TAIL;
+}
+
+/*
+ a handler for async qpathinfo replies
+ */
+static void async_qpathinfo(struct smbcli_request *c_req)
+{
+ struct async_info *async = c_req->async.private_data;
+ struct ntvfs_request *req = async->req;
+ req->async_states->status = smb_raw_pathinfo_recv(c_req, req, async->parms);
+ talloc_free(async);
+ req->async_states->send_fn(req);
+}
+
+/*
+ return info on a pathname
+*/
+static NTSTATUS cvfs_qpathinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_fileinfo *info)
+{
+ struct cvfs_private *p = ntvfs->private_data;
+ struct smbcli_request *c_req;
+
+ SETUP_PID;
+
+ if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
+ return smb_raw_pathinfo(p->tree, req, info);
+ }
+
+ c_req = smb_raw_pathinfo_send(p->tree, info);
+
+ ASYNC_RECV_TAIL(info, async_qpathinfo);
+}
+
+/*
+ a handler for async qfileinfo replies
+ */
+static void async_qfileinfo(struct smbcli_request *c_req)
+{
+ struct async_info *async = c_req->async.private_data;
+ struct ntvfs_request *req = async->req;
+ req->async_states->status = smb_raw_fileinfo_recv(c_req, req, async->parms);
+ talloc_free(async);
+ req->async_states->send_fn(req);
+}
+
+/*
+ query info on a open file
+*/
+static NTSTATUS cvfs_qfileinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_fileinfo *io)
+{
+ struct cvfs_private *p = ntvfs->private_data;
+ struct smbcli_request *c_req;
+
+ SETUP_PID_AND_FILE;
+
+ if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
+ return smb_raw_fileinfo(p->tree, req, io);
+ }
+
+ c_req = smb_raw_fileinfo_send(p->tree, io);
+
+ ASYNC_RECV_TAIL(io, async_qfileinfo);
+}
+
+
+/*
+ set info on a pathname
+*/
+static NTSTATUS cvfs_setpathinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_setfileinfo *st)
+{
+ struct cvfs_private *p = ntvfs->private_data;
+ struct smbcli_request *c_req;
+
+ SETUP_PID;
+
+ if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
+ return smb_raw_setpathinfo(p->tree, st);
+ }
+
+ c_req = smb_raw_setpathinfo_send(p->tree, st);
+
+ SIMPLE_ASYNC_TAIL;
+}
+
+
+/*
+ a handler for async open replies
+ */
+static void async_open(struct smbcli_request *c_req)
+{
+ struct async_info *async = c_req->async.private_data;
+ struct cvfs_private *cvfs = async->cvfs;
+ struct ntvfs_request *req = async->req;
+ struct cvfs_file *f = async->f;
+ union smb_open *io = async->parms;
+ union smb_handle *file;
+ if (f == NULL) {
+ goto failed;
+ }
+ talloc_free(async);
+ req->async_states->status = smb_raw_open_recv(c_req, req, io);
+ SMB_OPEN_OUT_FILE(io, file);
+ if (file == NULL) {
+ goto failed;
+ }
+ f->fnum = file->fnum;
+ file->ntvfs = NULL;
+ if (!NT_STATUS_IS_OK(req->async_states->status)) goto failed;
+ req->async_states->status = ntvfs_handle_set_backend_data(f->h, cvfs->ntvfs, f);
+ if (!NT_STATUS_IS_OK(req->async_states->status)) goto failed;
+ file->ntvfs = f->h;
+ DLIST_ADD(cvfs->files, f);
+failed:
+ req->async_states->send_fn(req);
+}
+
+/*
+ open a file
+*/
+static NTSTATUS cvfs_open(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_open *io)
+{
+ struct cvfs_private *p = ntvfs->private_data;
+ struct smbcli_request *c_req;
+ struct ntvfs_handle *h;
+ struct cvfs_file *f;
+ NTSTATUS status;
+
+ SETUP_PID;
+
+ if (io->generic.level != RAW_OPEN_GENERIC &&
+ p->map_generic) {
+ return ntvfs_map_open(ntvfs, req, io);
+ }
+
+ status = ntvfs_handle_new(ntvfs, req, &h);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ f = talloc_zero(h, struct cvfs_file);
+ NT_STATUS_HAVE_NO_MEMORY(f);
+ f->h = h;
+
+ if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
+ union smb_handle *file;
+
+ status = smb_raw_open(p->tree, req, io);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ SMB_OPEN_OUT_FILE(io, file);
+ if (file == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ f->fnum = file->fnum;
+ file->ntvfs = NULL;
+ status = ntvfs_handle_set_backend_data(f->h, p->ntvfs, f);
+ NT_STATUS_NOT_OK_RETURN(status);
+ file->ntvfs = f->h;
+ DLIST_ADD(p->files, f);
+
+ return NT_STATUS_OK;
+ }
+
+ c_req = smb_raw_open_send(p->tree, io);
+
+ ASYNC_RECV_TAIL_F(io, async_open, f);
+}
+
+/*
+ create a directory
+*/
+static NTSTATUS cvfs_mkdir(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_mkdir *md)
+{
+ struct cvfs_private *p = ntvfs->private_data;
+ struct smbcli_request *c_req;
+
+ SETUP_PID;
+
+ if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
+ return smb_raw_mkdir(p->tree, md);
+ }
+
+ c_req = smb_raw_mkdir_send(p->tree, md);
+
+ SIMPLE_ASYNC_TAIL;
+}
+
+/*
+ remove a directory
+*/
+static NTSTATUS cvfs_rmdir(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, struct smb_rmdir *rd)
+{
+ struct cvfs_private *p = ntvfs->private_data;
+ struct smbcli_request *c_req;
+
+ SETUP_PID;
+
+ if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
+ return smb_raw_rmdir(p->tree, rd);
+ }
+ c_req = smb_raw_rmdir_send(p->tree, rd);
+
+ SIMPLE_ASYNC_TAIL;
+}
+
+/*
+ rename a set of files
+*/
+static NTSTATUS cvfs_rename(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_rename *ren)
+{
+ struct cvfs_private *p = ntvfs->private_data;
+ struct smbcli_request *c_req;
+
+ SETUP_PID;
+
+ if (ren->nttrans.level == RAW_RENAME_NTTRANS) {
+ struct cvfs_file *f;
+ f = ntvfs_handle_get_backend_data(ren->nttrans.in.file.ntvfs, ntvfs);
+ if (!f) return NT_STATUS_INVALID_HANDLE;
+ ren->nttrans.in.file.fnum = f->fnum;
+ }
+
+ if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
+ return smb_raw_rename(p->tree, ren);
+ }
+
+ c_req = smb_raw_rename_send(p->tree, ren);
+
+ SIMPLE_ASYNC_TAIL;
+}
+
+/*
+ copy a set of files
+*/
+static NTSTATUS cvfs_copy(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, struct smb_copy *cp)
+{
+ return NT_STATUS_NOT_SUPPORTED;
+}
+
+/*
+ a handler for async read replies
+ */
+static void async_read(struct smbcli_request *c_req)
+{
+ struct async_info *async = c_req->async.private_data;
+ struct ntvfs_request *req = async->req;
+ req->async_states->status = smb_raw_read_recv(c_req, async->parms);
+ talloc_free(async);
+ req->async_states->send_fn(req);
+}
+
+/*
+ read from a file
+*/
+static NTSTATUS cvfs_read(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_read *io)
+{
+ struct cvfs_private *p = ntvfs->private_data;
+ struct smbcli_request *c_req;
+
+ SETUP_PID;
+
+ if (io->generic.level != RAW_READ_GENERIC &&
+ p->map_generic) {
+ return ntvfs_map_read(ntvfs, req, io);
+ }
+
+ SETUP_FILE;
+
+ if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
+ return smb_raw_read(p->tree, io);
+ }
+
+ c_req = smb_raw_read_send(p->tree, io);
+
+ ASYNC_RECV_TAIL(io, async_read);
+}
+
+/*
+ a handler for async write replies
+ */
+static void async_write(struct smbcli_request *c_req)
+{
+ struct async_info *async = c_req->async.private_data;
+ struct ntvfs_request *req = async->req;
+ req->async_states->status = smb_raw_write_recv(c_req, async->parms);
+ talloc_free(async);
+ req->async_states->send_fn(req);
+}
+
+/*
+ write to a file
+*/
+static NTSTATUS cvfs_write(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_write *io)
+{
+ struct cvfs_private *p = ntvfs->private_data;
+ struct smbcli_request *c_req;
+
+ SETUP_PID;
+
+ if (io->generic.level != RAW_WRITE_GENERIC &&
+ p->map_generic) {
+ return ntvfs_map_write(ntvfs, req, io);
+ }
+ SETUP_FILE;
+
+ if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
+ return smb_raw_write(p->tree, io);
+ }
+
+ c_req = smb_raw_write_send(p->tree, io);
+
+ ASYNC_RECV_TAIL(io, async_write);
+}
+
+/*
+ a handler for async seek replies
+ */
+static void async_seek(struct smbcli_request *c_req)
+{
+ struct async_info *async = c_req->async.private_data;
+ struct ntvfs_request *req = async->req;
+ req->async_states->status = smb_raw_seek_recv(c_req, async->parms);
+ talloc_free(async);
+ req->async_states->send_fn(req);
+}
+
+/*
+ seek in a file
+*/
+static NTSTATUS cvfs_seek(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_seek *io)
+{
+ struct cvfs_private *p = ntvfs->private_data;
+ struct smbcli_request *c_req;
+
+ SETUP_PID_AND_FILE;
+
+ if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
+ return smb_raw_seek(p->tree, io);
+ }
+
+ c_req = smb_raw_seek_send(p->tree, io);
+
+ ASYNC_RECV_TAIL(io, async_seek);
+}
+
+/*
+ flush a file
+*/
+static NTSTATUS cvfs_flush(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_flush *io)
+{
+ struct cvfs_private *p = ntvfs->private_data;
+ struct smbcli_request *c_req;
+
+ SETUP_PID;
+ switch (io->generic.level) {
+ case RAW_FLUSH_FLUSH:
+ SETUP_FILE;
+ break;
+ case RAW_FLUSH_ALL:
+ io->generic.in.file.fnum = 0xFFFF;
+ break;
+ case RAW_FLUSH_SMB2:
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
+ return smb_raw_flush(p->tree, io);
+ }
+
+ c_req = smb_raw_flush_send(p->tree, io);
+
+ SIMPLE_ASYNC_TAIL;
+}
+
+/*
+ close a file
+*/
+static NTSTATUS cvfs_close(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_close *io)
+{
+ struct cvfs_private *p = ntvfs->private_data;
+ struct smbcli_request *c_req;
+ struct cvfs_file *f;
+ union smb_close io2;
+
+ SETUP_PID;
+
+ if (io->generic.level != RAW_CLOSE_GENERIC &&
+ p->map_generic) {
+ return ntvfs_map_close(ntvfs, req, io);
+ }
+
+ if (io->generic.level == RAW_CLOSE_GENERIC) {
+ ZERO_STRUCT(io2);
+ io2.close.level = RAW_CLOSE_CLOSE;
+ io2.close.in.file = io->generic.in.file;
+ io2.close.in.write_time = io->generic.in.write_time;
+ io = &io2;
+ }
+
+ SETUP_FILE_HERE(f);
+ /* Note, we aren't free-ing f, or it's h here. Should we?
+ even if file-close fails, we'll remove it from the list,
+ what else would we do? Maybe we should not remove until
+ after the proxied call completes? */
+ DLIST_REMOVE(p->files, f);
+
+ if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
+ return smb_raw_close(p->tree, io);
+ }
+
+ c_req = smb_raw_close_send(p->tree, io);
+
+ SIMPLE_ASYNC_TAIL;
+}
+
+/*
+ exit - closing files open by the pid
+*/
+static NTSTATUS cvfs_exit(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req)
+{
+ struct cvfs_private *p = ntvfs->private_data;
+ struct smbcli_request *c_req;
+
+ SETUP_PID;
+
+ if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
+ return smb_raw_exit(p->tree->session);
+ }
+
+ c_req = smb_raw_exit_send(p->tree->session);
+
+ SIMPLE_ASYNC_TAIL;
+}
+
+/*
+ logoff - closing files open by the user
+*/
+static NTSTATUS cvfs_logoff(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req)
+{
+ /* we can't do this right in the cifs backend .... */
+ return NT_STATUS_OK;
+}
+
+/*
+ setup for an async call - nothing to do yet
+*/
+static NTSTATUS cvfs_async_setup(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ void *private_data)
+{
+ return NT_STATUS_OK;
+}
+
+/*
+ cancel an async call
+*/
+static NTSTATUS cvfs_cancel(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req)
+{
+ struct cvfs_private *p = ntvfs->private_data;
+ struct async_info *a;
+
+ /* find the matching request */
+ for (a=p->pending;a;a=a->next) {
+ if (a->req == req) {
+ break;
+ }
+ }
+
+ if (a == NULL) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ return smb_raw_ntcancel(a->c_req);
+}
+
+/*
+ lock a byte range
+*/
+static NTSTATUS cvfs_lock(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_lock *io)
+{
+ struct cvfs_private *p = ntvfs->private_data;
+ struct smbcli_request *c_req;
+
+ SETUP_PID;
+
+ if (io->generic.level != RAW_LOCK_GENERIC &&
+ p->map_generic) {
+ return ntvfs_map_lock(ntvfs, req, io);
+ }
+ SETUP_FILE;
+
+ if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
+ return smb_raw_lock(p->tree, io);
+ }
+
+ c_req = smb_raw_lock_send(p->tree, io);
+ SIMPLE_ASYNC_TAIL;
+}
+
+/*
+ set info on a open file
+*/
+static NTSTATUS cvfs_setfileinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_setfileinfo *io)
+{
+ struct cvfs_private *p = ntvfs->private_data;
+ struct smbcli_request *c_req;
+
+ SETUP_PID_AND_FILE;
+
+ if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
+ return smb_raw_setfileinfo(p->tree, io);
+ }
+ c_req = smb_raw_setfileinfo_send(p->tree, io);
+
+ SIMPLE_ASYNC_TAIL;
+}
+
+
+/*
+ a handler for async fsinfo replies
+ */
+static void async_fsinfo(struct smbcli_request *c_req)
+{
+ struct async_info *async = c_req->async.private_data;
+ struct ntvfs_request *req = async->req;
+ req->async_states->status = smb_raw_fsinfo_recv(c_req, req, async->parms);
+ talloc_free(async);
+ req->async_states->send_fn(req);
+}
+
+/*
+ return filesystem space info
+*/
+static NTSTATUS cvfs_fsinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_fsinfo *fs)
+{
+ struct cvfs_private *p = ntvfs->private_data;
+ struct smbcli_request *c_req;
+
+ SETUP_PID;
+
+ if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
+ return smb_raw_fsinfo(p->tree, req, fs);
+ }
+
+ c_req = smb_raw_fsinfo_send(p->tree, req, fs);
+
+ ASYNC_RECV_TAIL(fs, async_fsinfo);
+}
+
+/*
+ return print queue info
+*/
+static NTSTATUS cvfs_lpq(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_lpq *lpq)
+{
+ return NT_STATUS_NOT_SUPPORTED;
+}
+
+/*
+ list files in a directory matching a wildcard pattern
+*/
+static NTSTATUS cvfs_search_first(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_search_first *io,
+ void *search_private,
+ bool (*callback)(void *, const union smb_search_data *))
+{
+ struct cvfs_private *p = ntvfs->private_data;
+
+ SETUP_PID;
+
+ return smb_raw_search_first(p->tree, req, io, search_private, callback);
+}
+
+/* continue a search */
+static NTSTATUS cvfs_search_next(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_search_next *io,
+ void *search_private,
+ bool (*callback)(void *, const union smb_search_data *))
+{
+ struct cvfs_private *p = ntvfs->private_data;
+
+ SETUP_PID;
+
+ return smb_raw_search_next(p->tree, req, io, search_private, callback);
+}
+
+/* close a search */
+static NTSTATUS cvfs_search_close(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_search_close *io)
+{
+ struct cvfs_private *p = ntvfs->private_data;
+
+ SETUP_PID;
+
+ return smb_raw_search_close(p->tree, io);
+}
+
+/*
+ a handler for async trans2 replies
+ */
+static void async_trans2(struct smbcli_request *c_req)
+{
+ struct async_info *async = c_req->async.private_data;
+ struct ntvfs_request *req = async->req;
+ req->async_states->status = smb_raw_trans2_recv(c_req, req, async->parms);
+ talloc_free(async);
+ req->async_states->send_fn(req);
+}
+
+/* raw trans2 */
+static NTSTATUS cvfs_trans2(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ struct smb_trans2 *trans2)
+{
+ struct cvfs_private *p = ntvfs->private_data;
+ struct smbcli_request *c_req;
+
+ if (p->map_trans2) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ SETUP_PID;
+
+ if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
+ return smb_raw_trans2(p->tree, req, trans2);
+ }
+
+ c_req = smb_raw_trans2_send(p->tree, trans2);
+
+ ASYNC_RECV_TAIL(trans2, async_trans2);
+}
+
+
+/* SMBtrans - not used on file shares */
+static NTSTATUS cvfs_trans(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ struct smb_trans2 *trans2)
+{
+ return NT_STATUS_ACCESS_DENIED;
+}
+
+/*
+ a handler for async change notify replies
+ */
+static void async_changenotify(struct smbcli_request *c_req)
+{
+ struct async_info *async = c_req->async.private_data;
+ struct ntvfs_request *req = async->req;
+ req->async_states->status = smb_raw_changenotify_recv(c_req, req, async->parms);
+ talloc_free(async);
+ req->async_states->send_fn(req);
+}
+
+/* change notify request - always async */
+static NTSTATUS cvfs_notify(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_notify *io)
+{
+ struct cvfs_private *p = ntvfs->private_data;
+ struct smbcli_request *c_req;
+ int saved_timeout = p->transport->options.request_timeout;
+ struct cvfs_file *f;
+
+ if (io->nttrans.level != RAW_NOTIFY_NTTRANS) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ SETUP_PID;
+
+ f = ntvfs_handle_get_backend_data(io->nttrans.in.file.ntvfs, ntvfs);
+ if (!f) return NT_STATUS_INVALID_HANDLE;
+ io->nttrans.in.file.fnum = f->fnum;
+
+ /* this request doesn't make sense unless its async */
+ if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* we must not timeout on notify requests - they wait
+ forever */
+ p->transport->options.request_timeout = 0;
+
+ c_req = smb_raw_changenotify_send(p->tree, io);
+
+ p->transport->options.request_timeout = saved_timeout;
+
+ ASYNC_RECV_TAIL(io, async_changenotify);
+}
+
+/*
+ initialise the CIFS->CIFS backend, registering ourselves with the ntvfs subsystem
+ */
+NTSTATUS ntvfs_cifs_init(TALLOC_CTX *ctx)
+{
+ NTSTATUS ret;
+ struct ntvfs_ops ops;
+ NTVFS_CURRENT_CRITICAL_SIZES(vers);
+
+ ZERO_STRUCT(ops);
+
+ /* fill in the name and type */
+ ops.name = "cifs";
+ ops.type = NTVFS_DISK;
+
+ /* fill in all the operations */
+ ops.connect_fn = cvfs_connect;
+ ops.disconnect_fn = cvfs_disconnect;
+ ops.unlink_fn = cvfs_unlink;
+ ops.chkpath_fn = cvfs_chkpath;
+ ops.qpathinfo_fn = cvfs_qpathinfo;
+ ops.setpathinfo_fn = cvfs_setpathinfo;
+ ops.open_fn = cvfs_open;
+ ops.mkdir_fn = cvfs_mkdir;
+ ops.rmdir_fn = cvfs_rmdir;
+ ops.rename_fn = cvfs_rename;
+ ops.copy_fn = cvfs_copy;
+ ops.ioctl_fn = cvfs_ioctl;
+ ops.read_fn = cvfs_read;
+ ops.write_fn = cvfs_write;
+ ops.seek_fn = cvfs_seek;
+ ops.flush_fn = cvfs_flush;
+ ops.close_fn = cvfs_close;
+ ops.exit_fn = cvfs_exit;
+ ops.lock_fn = cvfs_lock;
+ ops.setfileinfo_fn = cvfs_setfileinfo;
+ ops.qfileinfo_fn = cvfs_qfileinfo;
+ ops.fsinfo_fn = cvfs_fsinfo;
+ ops.lpq_fn = cvfs_lpq;
+ ops.search_first_fn = cvfs_search_first;
+ ops.search_next_fn = cvfs_search_next;
+ ops.search_close_fn = cvfs_search_close;
+ ops.trans_fn = cvfs_trans;
+ ops.logoff_fn = cvfs_logoff;
+ ops.async_setup_fn = cvfs_async_setup;
+ ops.cancel_fn = cvfs_cancel;
+ ops.notify_fn = cvfs_notify;
+ ops.trans2_fn = cvfs_trans2;
+
+ /* register ourselves with the NTVFS subsystem. We register
+ under the name 'cifs'. */
+ ret = ntvfs_register(&ops, &vers);
+
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(0,("Failed to register CIFS backend!\n"));
+ }
+
+ return ret;
+}
diff --git a/source4/ntvfs/common/brlock.c b/source4/ntvfs/common/brlock.c
new file mode 100644
index 0000000..d99cc4e
--- /dev/null
+++ b/source4/ntvfs/common/brlock.c
@@ -0,0 +1,136 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ generic byte range locking code
+
+ Copyright (C) Andrew Tridgell 1992-2004
+ Copyright (C) Jeremy Allison 1992-2000
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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/>.
+*/
+
+/* This module implements a tdb based byte range locking service,
+ replacing the fcntl() based byte range locking previously
+ used. This allows us to provide the same semantics as NT */
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "messaging/messaging.h"
+#include "lib/messaging/irpc.h"
+#include "libcli/libcli.h"
+#include "cluster/cluster.h"
+#include "ntvfs/common/ntvfs_common.h"
+
+static const struct brlock_ops *ops;
+
+/*
+ set the brl backend ops
+*/
+void brlock_set_ops(const struct brlock_ops *new_ops)
+{
+ ops = new_ops;
+}
+
+/*
+ Open up the brlock database. Close it down using talloc_free(). We
+ need the imessaging_ctx to allow for pending lock notifications.
+*/
+struct brl_context *brlock_init(TALLOC_CTX *mem_ctx, struct server_id server,
+ struct loadparm_context *lp_ctx,
+ struct imessaging_context *imessaging_ctx)
+{
+ if (ops == NULL) {
+ brl_tdb_init_ops();
+ }
+ return ops->brl_init(mem_ctx, server, lp_ctx, imessaging_ctx);
+}
+
+struct brl_handle *brlock_create_handle(TALLOC_CTX *mem_ctx, struct ntvfs_handle *ntvfs, DATA_BLOB *file_key)
+{
+ return ops->brl_create_handle(mem_ctx, ntvfs, file_key);
+}
+
+/*
+ Lock a range of bytes. The lock_type can be a PENDING_*_LOCK, in
+ which case a real lock is first tried, and if that fails then a
+ pending lock is created. When the pending lock is triggered (by
+ someone else closing an overlapping lock range) a messaging
+ notification is sent, identified by the notify_ptr
+*/
+NTSTATUS brlock_lock(struct brl_context *brl,
+ struct brl_handle *brlh,
+ uint32_t smbpid,
+ uint64_t start, uint64_t size,
+ enum brl_type lock_type,
+ void *notify_ptr)
+{
+ return ops->brl_lock(brl, brlh, smbpid, start, size, lock_type, notify_ptr);
+}
+
+
+/*
+ Unlock a range of bytes.
+*/
+NTSTATUS brlock_unlock(struct brl_context *brl,
+ struct brl_handle *brlh,
+ uint32_t smbpid,
+ uint64_t start, uint64_t size)
+{
+ return ops->brl_unlock(brl, brlh, smbpid, start, size);
+}
+
+/*
+ remove a pending lock. This is called when the caller has either
+ given up trying to establish a lock or when they have succeeded in
+ getting it. In either case they no longer need to be notified.
+*/
+NTSTATUS brlock_remove_pending(struct brl_context *brl,
+ struct brl_handle *brlh,
+ void *notify_ptr)
+{
+ return ops->brl_remove_pending(brl, brlh, notify_ptr);
+}
+
+
+/*
+ Test if we are allowed to perform IO on a region of an open file
+*/
+NTSTATUS brlock_locktest(struct brl_context *brl,
+ struct brl_handle *brlh,
+ uint32_t smbpid,
+ uint64_t start, uint64_t size,
+ enum brl_type lock_type)
+{
+ return ops->brl_locktest(brl, brlh, smbpid, start, size, lock_type);
+}
+
+
+/*
+ Remove any locks associated with a open file.
+*/
+NTSTATUS brlock_close(struct brl_context *brl,
+ struct brl_handle *brlh)
+{
+ return ops->brl_close(brl, brlh);
+}
+
+/*
+ Get a number of locks associated with a open file.
+*/
+NTSTATUS brlock_count(struct brl_context *brl,
+ struct brl_handle *brlh,
+ int *count)
+{
+ return ops->brl_count(brl, brlh, count);
+}
diff --git a/source4/ntvfs/common/brlock.h b/source4/ntvfs/common/brlock.h
new file mode 100644
index 0000000..650136b
--- /dev/null
+++ b/source4/ntvfs/common/brlock.h
@@ -0,0 +1,55 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ generic byte range locking code - common include
+
+ Copyright (C) Andrew Tridgell 2006
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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 "libcli/libcli.h"
+
+struct brlock_ops {
+ struct brl_context *(*brl_init)(TALLOC_CTX *, struct server_id ,
+ struct loadparm_context *lp_ctx,
+ struct imessaging_context *);
+ struct brl_handle *(*brl_create_handle)(TALLOC_CTX *, struct ntvfs_handle *, DATA_BLOB *);
+ NTSTATUS (*brl_lock)(struct brl_context *,
+ struct brl_handle *,
+ uint32_t ,
+ uint64_t , uint64_t ,
+ enum brl_type ,
+ void *);
+ NTSTATUS (*brl_unlock)(struct brl_context *,
+ struct brl_handle *,
+ uint32_t ,
+ uint64_t , uint64_t );
+ NTSTATUS (*brl_remove_pending)(struct brl_context *,
+ struct brl_handle *,
+ void *);
+ NTSTATUS (*brl_locktest)(struct brl_context *,
+ struct brl_handle *,
+ uint32_t ,
+ uint64_t , uint64_t ,
+ enum brl_type );
+ NTSTATUS (*brl_close)(struct brl_context *,
+ struct brl_handle *);
+ NTSTATUS (*brl_count)(struct brl_context *,
+ struct brl_handle *,
+ int *count);
+};
+
+void brlock_set_ops(const struct brlock_ops *new_ops);
+void brl_tdb_init_ops(void);
diff --git a/source4/ntvfs/common/brlock_tdb.c b/source4/ntvfs/common/brlock_tdb.c
new file mode 100644
index 0000000..c4cd76b
--- /dev/null
+++ b/source4/ntvfs/common/brlock_tdb.c
@@ -0,0 +1,775 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ generic byte range locking code - tdb backend
+
+ Copyright (C) Andrew Tridgell 1992-2006
+ Copyright (C) Jeremy Allison 1992-2000
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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/>.
+*/
+
+/* This module implements a tdb based byte range locking service,
+ replacing the fcntl() based byte range locking previously
+ used. This allows us to provide the same semantics as NT */
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "messaging/messaging.h"
+#include "lib/messaging/irpc.h"
+#include "libcli/libcli.h"
+#include "cluster/cluster.h"
+#include "ntvfs/common/brlock.h"
+#include "ntvfs/ntvfs.h"
+#include "param/param.h"
+#include "dbwrap/dbwrap.h"
+
+/*
+ in this module a "DATA_BLOB *file_key" is a blob that uniquely identifies
+ a file. For a local posix filesystem this will usually be a combination
+ of the device and inode numbers of the file, but it can be anything
+ that uniquely identifies a file for locking purposes, as long
+ as it is applied consistently.
+*/
+
+/* this struct is typically attached to tcon */
+struct brl_context {
+ struct db_context *db;
+ struct server_id server;
+ struct imessaging_context *imessaging_ctx;
+};
+
+/*
+ the lock context contains the elements that define whether one
+ lock is the same as another lock
+*/
+struct lock_context {
+ struct server_id server;
+ uint32_t smbpid;
+ struct brl_context *ctx;
+};
+
+/* The data in brlock records is an unsorted linear array of these
+ records. It is unnecessary to store the count as tdb provides the
+ size of the record */
+struct lock_struct {
+ struct lock_context context;
+ struct ntvfs_handle *ntvfs;
+ uint64_t start;
+ uint64_t size;
+ enum brl_type lock_type;
+ void *notify_ptr;
+};
+
+/* this struct is attached to on oprn file handle */
+struct brl_handle {
+ DATA_BLOB key;
+ struct ntvfs_handle *ntvfs;
+ struct lock_struct last_lock;
+};
+
+/* see if we have wrapped locks, which are no longer allowed (windows
+ * changed this in win7 */
+static bool brl_invalid_lock_range(uint64_t start, uint64_t size)
+{
+ return (size > 1 && (start + size < start));
+}
+
+/*
+ Open up the brlock.tdb database. Close it down using
+ talloc_free(). We need the imessaging_ctx to allow for
+ pending lock notifications.
+*/
+static struct brl_context *brl_tdb_init(TALLOC_CTX *mem_ctx, struct server_id server,
+ struct loadparm_context *lp_ctx,
+ struct imessaging_context *imessaging_ctx)
+{
+ struct brl_context *brl;
+
+ brl = talloc(mem_ctx, struct brl_context);
+ if (brl == NULL) {
+ return NULL;
+ }
+
+ brl->db = cluster_db_tmp_open(brl, lp_ctx, "brlock", TDB_DEFAULT);
+ if (brl->db == NULL) {
+ talloc_free(brl);
+ return NULL;
+ }
+
+ brl->server = server;
+ brl->imessaging_ctx = imessaging_ctx;
+
+ return brl;
+}
+
+static struct brl_handle *brl_tdb_create_handle(TALLOC_CTX *mem_ctx, struct ntvfs_handle *ntvfs,
+ DATA_BLOB *file_key)
+{
+ struct brl_handle *brlh;
+
+ brlh = talloc(mem_ctx, struct brl_handle);
+ if (brlh == NULL) {
+ return NULL;
+ }
+
+ brlh->key = *file_key;
+ brlh->ntvfs = ntvfs;
+ ZERO_STRUCT(brlh->last_lock);
+
+ return brlh;
+}
+
+/*
+ see if two locking contexts are equal
+*/
+static bool brl_tdb_same_context(struct lock_context *ctx1, struct lock_context *ctx2)
+{
+ return (cluster_id_equal(&ctx1->server, &ctx2->server) &&
+ ctx1->smbpid == ctx2->smbpid &&
+ ctx1->ctx == ctx2->ctx);
+}
+
+/*
+ see if lck1 and lck2 overlap
+
+ lck1 is the existing lock. lck2 is the new lock we are
+ looking at adding
+*/
+static bool brl_tdb_overlap(struct lock_struct *lck1,
+ struct lock_struct *lck2)
+{
+ /* this extra check is not redundant - it copes with locks
+ that go beyond the end of 64 bit file space */
+ if (lck1->size != 0 &&
+ lck1->start == lck2->start &&
+ lck1->size == lck2->size) {
+ return true;
+ }
+
+ if (lck1->start >= (lck2->start+lck2->size) ||
+ lck2->start >= (lck1->start+lck1->size)) {
+ return false;
+ }
+
+ /* we have a conflict. Now check to see if lck1 really still
+ * exists, which involves checking if the process still
+ * exists. We leave this test to last as its the most
+ * expensive test, especially when we are clustered */
+ /* TODO: need to do this via a server_id_exists() call, which
+ * hasn't been written yet. When clustered this will need to
+ * call into ctdb */
+
+ return true;
+}
+
+/*
+ See if lock2 can be added when lock1 is in place.
+*/
+static bool brl_tdb_conflict(struct lock_struct *lck1,
+ struct lock_struct *lck2)
+{
+ /* pending locks don't conflict with anything */
+ if (lck1->lock_type >= PENDING_READ_LOCK ||
+ lck2->lock_type >= PENDING_READ_LOCK) {
+ return false;
+ }
+
+ if (lck1->lock_type == READ_LOCK && lck2->lock_type == READ_LOCK) {
+ return false;
+ }
+
+ if (brl_tdb_same_context(&lck1->context, &lck2->context) &&
+ lck2->lock_type == READ_LOCK && lck1->ntvfs == lck2->ntvfs) {
+ return false;
+ }
+
+ return brl_tdb_overlap(lck1, lck2);
+}
+
+
+/*
+ Check to see if this lock conflicts, but ignore our own locks on the
+ same fnum only.
+*/
+static bool brl_tdb_conflict_other(struct lock_struct *lck1, struct lock_struct *lck2)
+{
+ /* pending locks don't conflict with anything */
+ if (lck1->lock_type >= PENDING_READ_LOCK ||
+ lck2->lock_type >= PENDING_READ_LOCK) {
+ return false;
+ }
+
+ if (lck1->lock_type == READ_LOCK && lck2->lock_type == READ_LOCK)
+ return false;
+
+ /*
+ * note that incoming write calls conflict with existing READ
+ * locks even if the context is the same. JRA. See LOCKTEST7
+ * in smbtorture.
+ */
+ if (brl_tdb_same_context(&lck1->context, &lck2->context) &&
+ lck1->ntvfs == lck2->ntvfs &&
+ (lck2->lock_type == READ_LOCK || lck1->lock_type == WRITE_LOCK)) {
+ return false;
+ }
+
+ return brl_tdb_overlap(lck1, lck2);
+}
+
+
+/*
+ amazingly enough, w2k3 "remembers" whether the last lock failure
+ is the same as this one and changes its error code. I wonder if any
+ app depends on this?
+*/
+static NTSTATUS brl_tdb_lock_failed(struct brl_handle *brlh, struct lock_struct *lock)
+{
+ /*
+ * this function is only called for non pending lock!
+ */
+
+ /* in SMB2 mode always return NT_STATUS_LOCK_NOT_GRANTED! */
+ if (lock->ntvfs->ctx->protocol >= PROTOCOL_SMB2_02) {
+ return NT_STATUS_LOCK_NOT_GRANTED;
+ }
+
+ /*
+ * if the notify_ptr is non NULL,
+ * it means that we're at the end of a pending lock
+ * and the real lock is requested after the timeout went by
+ * In this case we need to remember the last_lock and always
+ * give FILE_LOCK_CONFLICT
+ */
+ if (lock->notify_ptr) {
+ brlh->last_lock = *lock;
+ return NT_STATUS_FILE_LOCK_CONFLICT;
+ }
+
+ /*
+ * amazing the little things you learn with a test
+ * suite. Locks beyond this offset (as a 64 bit
+ * number!) always generate the conflict error code,
+ * unless the top bit is set
+ */
+ if (lock->start >= 0xEF000000 && (lock->start >> 63) == 0) {
+ brlh->last_lock = *lock;
+ return NT_STATUS_FILE_LOCK_CONFLICT;
+ }
+
+ /*
+ * if the current lock matches the last failed lock on the file handle
+ * and starts at the same offset, then FILE_LOCK_CONFLICT should be returned
+ */
+ if (cluster_id_equal(&lock->context.server, &brlh->last_lock.context.server) &&
+ lock->context.ctx == brlh->last_lock.context.ctx &&
+ lock->ntvfs == brlh->last_lock.ntvfs &&
+ lock->start == brlh->last_lock.start) {
+ return NT_STATUS_FILE_LOCK_CONFLICT;
+ }
+
+ brlh->last_lock = *lock;
+ return NT_STATUS_LOCK_NOT_GRANTED;
+}
+
+/*
+ Lock a range of bytes. The lock_type can be a PENDING_*_LOCK, in
+ which case a real lock is first tried, and if that fails then a
+ pending lock is created. When the pending lock is triggered (by
+ someone else closing an overlapping lock range) a messaging
+ notification is sent, identified by the notify_ptr
+*/
+static NTSTATUS brl_tdb_lock(struct brl_context *brl,
+ struct brl_handle *brlh,
+ uint32_t smbpid,
+ uint64_t start, uint64_t size,
+ enum brl_type lock_type,
+ void *notify_ptr)
+{
+ TDB_DATA kbuf, dbuf;
+ int count=0, i;
+ struct lock_struct lock, *locks=NULL;
+ NTSTATUS status;
+ struct db_record *locked;
+
+ kbuf.dptr = brlh->key.data;
+ kbuf.dsize = brlh->key.length;
+
+ if (brl_invalid_lock_range(start, size)) {
+ return NT_STATUS_INVALID_LOCK_RANGE;
+ }
+
+ locked = dbwrap_fetch_locked(brl->db, brl, kbuf);
+ if (!locked) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ /* if this is a pending lock, then with the chainlock held we
+ try to get the real lock. If we succeed then we don't need
+ to make it pending. This prevents a possible race condition
+ where the pending lock gets created after the lock that is
+ preventing the real lock gets removed */
+ if (lock_type >= PENDING_READ_LOCK) {
+ enum brl_type rw = (lock_type==PENDING_READ_LOCK? READ_LOCK : WRITE_LOCK);
+
+ /* here we need to force that the last_lock isn't overwritten */
+ lock = brlh->last_lock;
+ status = brl_tdb_lock(brl, brlh, smbpid, start, size, rw, NULL);
+ brlh->last_lock = lock;
+
+ if (NT_STATUS_IS_OK(status)) {
+ talloc_free(locked);
+ return NT_STATUS_OK;
+ }
+ }
+
+ dbuf = dbwrap_record_get_value(locked);
+
+ lock.context.smbpid = smbpid;
+ lock.context.server = brl->server;
+ lock.context.ctx = brl;
+ lock.ntvfs = brlh->ntvfs;
+ lock.context.ctx = brl;
+ lock.start = start;
+ lock.size = size;
+ lock.lock_type = lock_type;
+ lock.notify_ptr = notify_ptr;
+
+ if (dbuf.dptr) {
+ /* there are existing locks - make sure they don't conflict */
+ locks = (struct lock_struct *)dbuf.dptr;
+ count = dbuf.dsize / sizeof(*locks);
+ for (i=0; i<count; i++) {
+ if (brl_tdb_conflict(&locks[i], &lock)) {
+ status = brl_tdb_lock_failed(brlh, &lock);
+ goto fail;
+ }
+ }
+ }
+
+ /* no conflicts - add it to the list of locks */
+ /* FIXME: a dbwrap_record_append() would help here! */
+ locks = talloc_array(locked, struct lock_struct, count+1);
+ if (!locks) {
+ status = NT_STATUS_NO_MEMORY;
+ goto fail;
+ }
+ if (dbuf.dsize > 0) {
+ memcpy(locks, dbuf.dptr, dbuf.dsize);
+ }
+ locks[count] = lock;
+
+ dbuf.dptr = (unsigned char *)locks;
+ dbuf.dsize += sizeof(lock);
+
+ status = dbwrap_record_store(locked, dbuf, TDB_REPLACE);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+
+ talloc_free(locked);
+
+ /* the caller needs to know if the real lock was granted. If
+ we have reached here then it must be a pending lock that
+ was granted, so tell them the lock failed */
+ if (lock_type >= PENDING_READ_LOCK) {
+ return NT_STATUS_LOCK_NOT_GRANTED;
+ }
+
+ return NT_STATUS_OK;
+
+ fail:
+ talloc_free(locked);
+ return status;
+}
+
+
+/*
+ we are removing a lock that might be holding up a pending lock. Scan for pending
+ locks that cover this range and if we find any then notify the server that it should
+ retry the lock
+*/
+static void brl_tdb_notify_unlock(struct brl_context *brl,
+ struct lock_struct *locks, int count,
+ struct lock_struct *removed_lock)
+{
+ int i, last_notice;
+
+ /* the last_notice logic is to prevent stampeding on a lock
+ range. It prevents us sending hundreds of notifies on the
+ same range of bytes. It doesn't prevent all possible
+ stampedes, but it does prevent the most common problem */
+ last_notice = -1;
+
+ for (i=0;i<count;i++) {
+ if (locks[i].lock_type >= PENDING_READ_LOCK &&
+ brl_tdb_overlap(&locks[i], removed_lock)) {
+ if (last_notice != -1 && brl_tdb_overlap(&locks[i], &locks[last_notice])) {
+ continue;
+ }
+ if (locks[i].lock_type == PENDING_WRITE_LOCK) {
+ last_notice = i;
+ }
+ imessaging_send_ptr(brl->imessaging_ctx, locks[i].context.server,
+ MSG_BRL_RETRY, locks[i].notify_ptr);
+ }
+ }
+}
+
+
+/*
+ send notifications for all pending locks - the file is being closed by this
+ user
+*/
+static void brl_tdb_notify_all(struct brl_context *brl,
+ struct lock_struct *locks, int count)
+{
+ int i;
+ for (i=0;i<count;i++) {
+ if (locks->lock_type >= PENDING_READ_LOCK) {
+ brl_tdb_notify_unlock(brl, locks, count, &locks[i]);
+ }
+ }
+}
+
+
+
+/*
+ Unlock a range of bytes.
+*/
+static NTSTATUS brl_tdb_unlock(struct brl_context *brl,
+ struct brl_handle *brlh,
+ uint32_t smbpid,
+ uint64_t start, uint64_t size)
+{
+ TDB_DATA kbuf, dbuf;
+ int count, i;
+ struct lock_struct *locks, *lock = NULL;
+ struct lock_context context;
+ struct db_record *locked;
+ NTSTATUS status;
+
+ kbuf.dptr = brlh->key.data;
+ kbuf.dsize = brlh->key.length;
+
+ if (brl_invalid_lock_range(start, size)) {
+ return NT_STATUS_INVALID_LOCK_RANGE;
+ }
+
+ locked = dbwrap_fetch_locked(brl->db, brl, kbuf);
+ if (!locked) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ dbuf = dbwrap_record_get_value(locked);
+
+ context.smbpid = smbpid;
+ context.server = brl->server;
+ context.ctx = brl;
+
+ /* there are existing locks - find a match */
+ locks = (struct lock_struct *)dbuf.dptr;
+ count = dbuf.dsize / sizeof(*locks);
+
+ for (i=0; i<count; i++) {
+ lock = &locks[i];
+ if (brl_tdb_same_context(&lock->context, &context) &&
+ lock->ntvfs == brlh->ntvfs &&
+ lock->start == start &&
+ lock->size == size &&
+ lock->lock_type == WRITE_LOCK) {
+ break;
+ }
+ }
+ if (i < count) goto found;
+
+ for (i=0; i<count; i++) {
+ lock = &locks[i];
+ if (brl_tdb_same_context(&lock->context, &context) &&
+ lock->ntvfs == brlh->ntvfs &&
+ lock->start == start &&
+ lock->size == size &&
+ lock->lock_type < PENDING_READ_LOCK) {
+ break;
+ }
+ }
+
+found:
+ if (i < count) {
+ /* found it - delete it */
+ if (count == 1) {
+ status = dbwrap_record_delete(locked);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+ } else {
+ struct lock_struct removed_lock = *lock;
+ if (i < count-1) {
+ memmove(&locks[i], &locks[i+1],
+ sizeof(*locks)*((count-1) - i));
+ }
+ count--;
+
+ /* send notifications for any relevant pending locks */
+ brl_tdb_notify_unlock(brl, locks, count, &removed_lock);
+
+ dbuf.dsize = count * sizeof(*locks);
+
+ status = dbwrap_record_store(locked, dbuf, TDB_REPLACE);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+ }
+
+ talloc_free(locked);
+ return NT_STATUS_OK;
+ }
+
+ /* we didn't find it */
+ status = NT_STATUS_RANGE_NOT_LOCKED;
+
+ fail:
+ talloc_free(locked);
+ return status;
+}
+
+
+/*
+ remove a pending lock. This is called when the caller has either
+ given up trying to establish a lock or when they have succeeded in
+ getting it. In either case they no longer need to be notified.
+*/
+static NTSTATUS brl_tdb_remove_pending(struct brl_context *brl,
+ struct brl_handle *brlh,
+ void *notify_ptr)
+{
+ TDB_DATA kbuf, dbuf;
+ int count, i;
+ struct lock_struct *locks;
+ NTSTATUS status;
+ struct db_record *locked;
+
+ kbuf.dptr = brlh->key.data;
+ kbuf.dsize = brlh->key.length;
+
+ locked = dbwrap_fetch_locked(brl->db, brl, kbuf);
+ if (!locked) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ dbuf = dbwrap_record_get_value(locked);
+ if (!dbuf.dptr) {
+ talloc_free(locked);
+ return NT_STATUS_RANGE_NOT_LOCKED;
+ }
+
+ /* there are existing locks - find a match */
+ locks = (struct lock_struct *)dbuf.dptr;
+ count = dbuf.dsize / sizeof(*locks);
+
+ for (i=0; i<count; i++) {
+ struct lock_struct *lock = &locks[i];
+
+ if (lock->lock_type >= PENDING_READ_LOCK &&
+ lock->notify_ptr == notify_ptr &&
+ cluster_id_equal(&lock->context.server, &brl->server)) {
+ /* found it - delete it */
+ if (count == 1) {
+ status = dbwrap_record_delete(locked);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+ } else {
+ if (i < count-1) {
+ memmove(&locks[i], &locks[i+1],
+ sizeof(*locks)*((count-1) - i));
+ }
+ count--;
+ dbuf.dsize = count * sizeof(*locks);
+ status = dbwrap_record_store(locked, dbuf,
+ TDB_REPLACE);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto fail;
+ }
+ }
+
+ talloc_free(locked);
+ return NT_STATUS_OK;
+ }
+ }
+
+ /* we didn't find it */
+ status = NT_STATUS_RANGE_NOT_LOCKED;
+
+ fail:
+ talloc_free(locked);
+ return status;
+}
+
+
+/*
+ Test if we are allowed to perform IO on a region of an open file
+*/
+static NTSTATUS brl_tdb_locktest(struct brl_context *brl,
+ struct brl_handle *brlh,
+ uint32_t smbpid,
+ uint64_t start, uint64_t size,
+ enum brl_type lock_type)
+{
+ TDB_DATA kbuf, dbuf;
+ int count, i;
+ struct lock_struct lock, *locks;
+ NTSTATUS status;
+
+ kbuf.dptr = brlh->key.data;
+ kbuf.dsize = brlh->key.length;
+
+ if (brl_invalid_lock_range(start, size)) {
+ return NT_STATUS_INVALID_LOCK_RANGE;
+ }
+
+ status = dbwrap_fetch(brl->db, brl, kbuf, &dbuf);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+ return NT_STATUS_OK;
+ } else if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ lock.context.smbpid = smbpid;
+ lock.context.server = brl->server;
+ lock.context.ctx = brl;
+ lock.ntvfs = brlh->ntvfs;
+ lock.start = start;
+ lock.size = size;
+ lock.lock_type = lock_type;
+
+ /* there are existing locks - make sure they don't conflict */
+ locks = (struct lock_struct *)dbuf.dptr;
+ count = dbuf.dsize / sizeof(*locks);
+
+ for (i=0; i<count; i++) {
+ if (brl_tdb_conflict_other(&locks[i], &lock)) {
+ talloc_free(dbuf.dptr);
+ return NT_STATUS_FILE_LOCK_CONFLICT;
+ }
+ }
+
+ talloc_free(dbuf.dptr);
+ return NT_STATUS_OK;
+}
+
+
+/*
+ Remove any locks associated with a open file.
+*/
+static NTSTATUS brl_tdb_close(struct brl_context *brl,
+ struct brl_handle *brlh)
+{
+ TDB_DATA kbuf, dbuf;
+ int count, i, dcount=0;
+ struct lock_struct *locks;
+ struct db_record *locked;
+ NTSTATUS status;
+
+ kbuf.dptr = brlh->key.data;
+ kbuf.dsize = brlh->key.length;
+
+ locked = dbwrap_fetch_locked(brl->db, brl, kbuf);
+ if (!locked) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+ dbuf = dbwrap_record_get_value(locked);
+ if (!dbuf.dptr) {
+ talloc_free(locked);
+ return NT_STATUS_OK;
+ }
+
+ /* there are existing locks - remove any for this fnum */
+ locks = (struct lock_struct *)dbuf.dptr;
+ count = dbuf.dsize / sizeof(*locks);
+
+ for (i=0; i<count; i++) {
+ struct lock_struct *lock = &locks[i];
+
+ if (lock->context.ctx == brl &&
+ cluster_id_equal(&lock->context.server, &brl->server) &&
+ lock->ntvfs == brlh->ntvfs) {
+ /* found it - delete it */
+ if (count > 1 && i < count-1) {
+ memmove(&locks[i], &locks[i+1],
+ sizeof(*locks)*((count-1) - i));
+ }
+ count--;
+ i--;
+ dcount++;
+ }
+ }
+
+ status = NT_STATUS_OK;
+
+ if (count == 0) {
+ status = dbwrap_record_delete(locked);
+ } else if (dcount != 0) {
+ /* tell all pending lock holders for this file that
+ they have a chance now. This is a bit indiscriminant,
+ but works OK */
+ brl_tdb_notify_all(brl, locks, count);
+
+ dbuf.dsize = count * sizeof(*locks);
+
+ status = dbwrap_record_store(locked, dbuf, TDB_REPLACE);
+ }
+ talloc_free(locked);
+
+ return status;
+}
+
+static NTSTATUS brl_tdb_count(struct brl_context *brl, struct brl_handle *brlh,
+ int *count)
+{
+ TDB_DATA kbuf, dbuf;
+ NTSTATUS status;
+
+ kbuf.dptr = brlh->key.data;
+ kbuf.dsize = brlh->key.length;
+ *count = 0;
+
+ status = dbwrap_fetch(brl->db, brl, kbuf, &dbuf);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+ return NT_STATUS_OK;
+ } else if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ *count = dbuf.dsize / sizeof(struct lock_struct);
+
+ talloc_free(dbuf.dptr);
+
+ return NT_STATUS_OK;
+}
+
+static const struct brlock_ops brlock_tdb_ops = {
+ .brl_init = brl_tdb_init,
+ .brl_create_handle = brl_tdb_create_handle,
+ .brl_lock = brl_tdb_lock,
+ .brl_unlock = brl_tdb_unlock,
+ .brl_remove_pending = brl_tdb_remove_pending,
+ .brl_locktest = brl_tdb_locktest,
+ .brl_close = brl_tdb_close,
+ .brl_count = brl_tdb_count
+};
+
+
+void brl_tdb_init_ops(void)
+{
+ brlock_set_ops(&brlock_tdb_ops);
+}
diff --git a/source4/ntvfs/common/init.c b/source4/ntvfs/common/init.c
new file mode 100644
index 0000000..f8f8e27
--- /dev/null
+++ b/source4/ntvfs/common/init.c
@@ -0,0 +1,34 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Stefan Metzmacher 2006
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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/>.
+*/
+
+/*
+ this is the change notify database. It implements mechanisms for
+ storing current change notify waiters in a tdb, and checking if a
+ given event matches any of the stored notify waiiters.
+*/
+
+#include "includes.h"
+#include "ntvfs/sysdep/sys_notify.h"
+
+NTSTATUS ntvfs_common_init(void);
+
+NTSTATUS ntvfs_common_init(void)
+{
+ return sys_notify_init();
+}
diff --git a/source4/ntvfs/common/notify.c b/source4/ntvfs/common/notify.c
new file mode 100644
index 0000000..a434fab
--- /dev/null
+++ b/source4/ntvfs/common/notify.c
@@ -0,0 +1,687 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Andrew Tridgell 2006
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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/>.
+*/
+
+/*
+ this is the change notify database. It implements mechanisms for
+ storing current change notify waiters in a tdb, and checking if a
+ given event matches any of the stored notify waiiters.
+*/
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "messaging/messaging.h"
+#include "lib/messaging/irpc.h"
+#include "librpc/gen_ndr/ndr_notify.h"
+#include "../lib/util/dlinklist.h"
+#include "ntvfs/common/ntvfs_common.h"
+#include "ntvfs/sysdep/sys_notify.h"
+#include "cluster/cluster.h"
+#include "param/param.h"
+#include "lib/util/tsort.h"
+#include "lib/dbwrap/dbwrap.h"
+#include "../lib/util/util_tdb.h"
+
+struct notify_context {
+ struct db_context *db;
+ struct server_id server;
+ struct imessaging_context *imessaging_ctx;
+ struct notify_list *list;
+ struct notify_array *array;
+ int64_t seqnum;
+ struct sys_notify_context *sys_notify_ctx;
+};
+
+
+struct notify_list {
+ struct notify_list *next, *prev;
+ void *private_data;
+ void (*callback)(void *, const struct notify_event *);
+ void *sys_notify_handle;
+ int depth;
+};
+
+#define NOTIFY_KEY "notify array"
+
+#define NOTIFY_ENABLE "notify:enable"
+#define NOTIFY_ENABLE_DEFAULT true
+
+static NTSTATUS notify_remove_all(struct notify_context *notify);
+static void notify_handler(struct imessaging_context *msg_ctx,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id server_id,
+ size_t num_fds,
+ int *fds,
+ DATA_BLOB *data);
+
+/*
+ destroy the notify context
+*/
+static int notify_destructor(struct notify_context *notify)
+{
+ imessaging_deregister(notify->imessaging_ctx, MSG_PVFS_NOTIFY, notify);
+ notify_remove_all(notify);
+ return 0;
+}
+
+/*
+ Open up the notify.tdb database. You should close it down using
+ talloc_free(). We need the imessaging_ctx to allow for notifications
+ via internal messages
+*/
+struct notify_context *notify_init(TALLOC_CTX *mem_ctx, struct server_id server,
+ struct imessaging_context *imessaging_ctx,
+ struct loadparm_context *lp_ctx,
+ struct tevent_context *ev,
+ struct share_config *scfg)
+{
+ struct notify_context *notify;
+
+ if (share_bool_option(scfg, NOTIFY_ENABLE, NOTIFY_ENABLE_DEFAULT) != true) {
+ return NULL;
+ }
+
+ if (ev == NULL) {
+ return NULL;
+ }
+
+ notify = talloc(mem_ctx, struct notify_context);
+ if (notify == NULL) {
+ return NULL;
+ }
+
+ notify->db = cluster_db_tmp_open(notify, lp_ctx, "notify", TDB_SEQNUM);
+ if (notify->db == NULL) {
+ talloc_free(notify);
+ return NULL;
+ }
+
+ notify->server = server;
+ notify->imessaging_ctx = imessaging_ctx;
+ notify->list = NULL;
+ notify->array = NULL;
+ notify->seqnum = dbwrap_get_seqnum(notify->db);
+
+ talloc_set_destructor(notify, notify_destructor);
+
+ /* register with the messaging subsystem for the notify
+ message type */
+ imessaging_register(notify->imessaging_ctx, notify,
+ MSG_PVFS_NOTIFY, notify_handler);
+
+ notify->sys_notify_ctx = sys_notify_context_create(scfg, notify, ev);
+
+ return notify;
+}
+
+
+/*
+ lock the notify db
+*/
+static struct db_record *notify_lock(struct notify_context *notify)
+{
+ TDB_DATA key = string_term_tdb_data(NOTIFY_KEY);
+
+ return dbwrap_fetch_locked(notify->db, notify, key);
+}
+
+static void notify_unlock(struct db_record *lock)
+{
+ talloc_free(lock);
+}
+
+/*
+ load the notify array
+*/
+static NTSTATUS notify_load(struct notify_context *notify)
+{
+ TDB_DATA dbuf;
+ DATA_BLOB blob;
+ enum ndr_err_code ndr_err;
+ int seqnum;
+ NTSTATUS status;
+
+ seqnum = dbwrap_get_seqnum(notify->db);
+
+ if (seqnum == notify->seqnum && notify->array != NULL) {
+ return NT_STATUS_OK;
+ }
+
+ notify->seqnum = seqnum;
+
+ talloc_free(notify->array);
+ notify->array = talloc_zero(notify, struct notify_array);
+ NT_STATUS_HAVE_NO_MEMORY(notify->array);
+
+ status = dbwrap_fetch_bystring(notify->db, notify, NOTIFY_KEY, &dbuf);
+ if (!NT_STATUS_IS_OK(status)) {
+ return NT_STATUS_OK;
+ }
+
+ blob.data = dbuf.dptr;
+ blob.length = dbuf.dsize;
+
+ ndr_err = ndr_pull_struct_blob(&blob, notify->array, notify->array,
+ (ndr_pull_flags_fn_t)ndr_pull_notify_array);
+ talloc_free(dbuf.dptr);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ compare notify entries for sorting
+*/
+static int notify_compare(const void *p1, const void *p2)
+{
+ const struct notify_entry *e1 = p1, *e2 = p2;
+ return strcmp(e1->path, e2->path);
+}
+
+/*
+ save the notify array
+*/
+static NTSTATUS notify_save(struct notify_context *notify)
+{
+ TDB_DATA dbuf;
+ DATA_BLOB blob;
+ enum ndr_err_code ndr_err;
+ TALLOC_CTX *tmp_ctx;
+ NTSTATUS status;
+
+ /* if possible, remove some depth arrays */
+ while (notify->array->num_depths > 0 &&
+ notify->array->depth[notify->array->num_depths-1].num_entries == 0) {
+ notify->array->num_depths--;
+ }
+
+ /* we might just be able to delete the record */
+ if (notify->array->num_depths == 0) {
+ return dbwrap_delete_bystring(notify->db, NOTIFY_KEY);
+ }
+
+ tmp_ctx = talloc_new(notify);
+ NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
+
+ ndr_err = ndr_push_struct_blob(&blob, tmp_ctx, notify->array,
+ (ndr_push_flags_fn_t)ndr_push_notify_array);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ talloc_free(tmp_ctx);
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ dbuf.dptr = blob.data;
+ dbuf.dsize = blob.length;
+
+ status = dbwrap_store_bystring(notify->db, NOTIFY_KEY, dbuf,
+ TDB_REPLACE);
+ talloc_free(tmp_ctx);
+ return status;
+}
+
+
+/*
+ handle incoming notify messages
+*/
+static void notify_handler(struct imessaging_context *msg_ctx,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id server_id,
+ size_t num_fds,
+ int *fds,
+ DATA_BLOB *data)
+{
+ struct notify_context *notify = talloc_get_type(private_data, struct notify_context);
+ enum ndr_err_code ndr_err;
+ struct notify_event ev;
+ TALLOC_CTX *tmp_ctx = talloc_new(notify);
+ struct notify_list *listel;
+
+ if (num_fds != 0) {
+ DBG_WARNING("Received %zu fds, ignoring message\n", num_fds);
+ return;
+ }
+
+ if (tmp_ctx == NULL) {
+ return;
+ }
+
+ ndr_err = ndr_pull_struct_blob(data, tmp_ctx, &ev,
+ (ndr_pull_flags_fn_t)ndr_pull_notify_event);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ talloc_free(tmp_ctx);
+ return;
+ }
+
+ for (listel=notify->list;listel;listel=listel->next) {
+ if (listel->private_data == ev.private_data) {
+ listel->callback(listel->private_data, &ev);
+ break;
+ }
+ }
+
+ talloc_free(tmp_ctx);
+}
+
+/*
+ callback from sys_notify telling us about changes from the OS
+*/
+static void sys_notify_callback(struct sys_notify_context *ctx,
+ void *ptr, struct notify_event *ev)
+{
+ struct notify_list *listel = talloc_get_type(ptr, struct notify_list);
+ ev->private_data = listel;
+ listel->callback(listel->private_data, ev);
+}
+
+/*
+ add an entry to the notify array
+*/
+static NTSTATUS notify_add_array(struct notify_context *notify, struct notify_entry *e,
+ void *private_data, int depth)
+{
+ int i;
+ struct notify_depth *d;
+ struct notify_entry *ee;
+
+ /* possibly expand the depths array */
+ if (depth >= notify->array->num_depths) {
+ d = talloc_realloc(notify->array, notify->array->depth,
+ struct notify_depth, depth+1);
+ NT_STATUS_HAVE_NO_MEMORY(d);
+ for (i=notify->array->num_depths;i<=depth;i++) {
+ ZERO_STRUCT(d[i]);
+ }
+ notify->array->depth = d;
+ notify->array->num_depths = depth+1;
+ }
+ d = &notify->array->depth[depth];
+
+ /* expand the entries array */
+ ee = talloc_realloc(notify->array->depth, d->entries, struct notify_entry,
+ d->num_entries+1);
+ NT_STATUS_HAVE_NO_MEMORY(ee);
+ d->entries = ee;
+
+ d->entries[d->num_entries] = *e;
+ d->entries[d->num_entries].private_data = private_data;
+ d->entries[d->num_entries].server = notify->server;
+ d->entries[d->num_entries].path_len = strlen(e->path);
+ d->num_entries++;
+
+ d->max_mask |= e->filter;
+ d->max_mask_subdir |= e->subdir_filter;
+
+ TYPESAFE_QSORT(d->entries, d->num_entries, notify_compare);
+
+ /* recalculate the maximum masks */
+ d->max_mask = 0;
+ d->max_mask_subdir = 0;
+
+ for (i=0;i<d->num_entries;i++) {
+ d->max_mask |= d->entries[i].filter;
+ d->max_mask_subdir |= d->entries[i].subdir_filter;
+ }
+
+ return notify_save(notify);
+}
+
+/*
+ add a notify watch. This is called when a notify is first setup on a open
+ directory handle.
+*/
+NTSTATUS notify_add(struct notify_context *notify, struct notify_entry *e0,
+ void (*callback)(void *, const struct notify_event *),
+ void *private_data)
+{
+ struct notify_entry e = *e0;
+ NTSTATUS status;
+ char *tmp_path = NULL;
+ struct notify_list *listel;
+ size_t len;
+ int depth;
+ struct db_record *locked;
+
+ /* see if change notify is enabled at all */
+ if (notify == NULL) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ locked = notify_lock(notify);
+ if (!locked) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ status = notify_load(notify);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ /* cope with /. on the end of the path */
+ len = strlen(e.path);
+ if (len > 1 && e.path[len-1] == '.' && e.path[len-2] == '/') {
+ tmp_path = talloc_strndup(notify, e.path, len-2);
+ if (tmp_path == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+ e.path = tmp_path;
+ }
+
+ depth = count_chars(e.path, '/');
+
+ listel = talloc_zero(notify, struct notify_list);
+ if (listel == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ listel->private_data = private_data;
+ listel->callback = callback;
+ listel->depth = depth;
+ DLIST_ADD(notify->list, listel);
+
+ /* ignore failures from sys_notify */
+ if (notify->sys_notify_ctx != NULL) {
+ /*
+ this call will modify e.filter and e.subdir_filter
+ to remove bits handled by the backend
+ */
+ status = sys_notify_watch(notify->sys_notify_ctx, &e,
+ sys_notify_callback, listel,
+ &listel->sys_notify_handle);
+ if (NT_STATUS_IS_OK(status)) {
+ talloc_steal(listel, listel->sys_notify_handle);
+ }
+ }
+
+ /* if the system notify handler couldn't handle some of the
+ filter bits, or couldn't handle a request for recursion
+ then we need to install it in the array used for the
+ intra-samba notify handling */
+ if (e.filter != 0 || e.subdir_filter != 0) {
+ status = notify_add_array(notify, &e, private_data, depth);
+ }
+
+done:
+ notify_unlock(locked);
+ talloc_free(tmp_path);
+
+ return status;
+}
+
+/*
+ remove a notify watch. Called when the directory handle is closed
+*/
+NTSTATUS notify_remove(struct notify_context *notify, void *private_data)
+{
+ NTSTATUS status;
+ struct notify_list *listel;
+ int i, depth;
+ struct notify_depth *d;
+ struct db_record *locked;
+
+ /* see if change notify is enabled at all */
+ if (notify == NULL) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+
+ for (listel=notify->list;listel;listel=listel->next) {
+ if (listel->private_data == private_data) {
+ DLIST_REMOVE(notify->list, listel);
+ break;
+ }
+ }
+ if (listel == NULL) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ depth = listel->depth;
+
+ talloc_free(listel);
+
+ locked = notify_lock(notify);
+ if (!locked) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ status = notify_load(notify);
+ if (!NT_STATUS_IS_OK(status)) {
+ notify_unlock(locked);
+ return status;
+ }
+
+ if (depth >= notify->array->num_depths) {
+ notify_unlock(locked);
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ /* we only have to search at the depth of this element */
+ d = &notify->array->depth[depth];
+
+ for (i=0;i<d->num_entries;i++) {
+ if (private_data == d->entries[i].private_data &&
+ cluster_id_equal(&notify->server, &d->entries[i].server)) {
+ break;
+ }
+ }
+ if (i == d->num_entries) {
+ notify_unlock(locked);
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ if (i < d->num_entries-1) {
+ memmove(&d->entries[i], &d->entries[i+1],
+ sizeof(d->entries[i])*(d->num_entries-(i+1)));
+ }
+ d->num_entries--;
+
+ status = notify_save(notify);
+
+ notify_unlock(locked);
+
+ return status;
+}
+
+/*
+ remove all notify watches for this messaging server
+*/
+static NTSTATUS notify_remove_all(struct notify_context *notify)
+{
+ NTSTATUS status;
+ int i, depth, del_count=0;
+ struct db_record *locked;
+
+ if (notify->list == NULL) {
+ return NT_STATUS_OK;
+ }
+
+ locked = notify_lock(notify);
+ if (!locked) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ status = notify_load(notify);
+ if (!NT_STATUS_IS_OK(status)) {
+ notify_unlock(locked);
+ return status;
+ }
+
+ /* we have to search for all entries across all depths, looking for matches
+ for our server id */
+ for (depth=0;depth<notify->array->num_depths;depth++) {
+ struct notify_depth *d = &notify->array->depth[depth];
+ for (i=0;i<d->num_entries;i++) {
+ if (cluster_id_equal(&notify->server, &d->entries[i].server)) {
+ if (i < d->num_entries-1) {
+ memmove(&d->entries[i], &d->entries[i+1],
+ sizeof(d->entries[i])*(d->num_entries-(i+1)));
+ }
+ i--;
+ d->num_entries--;
+ del_count++;
+ }
+ }
+ }
+
+ if (del_count > 0) {
+ status = notify_save(notify);
+ }
+
+ notify_unlock(locked);
+
+ return status;
+}
+
+
+/*
+ send a notify message to another messaging server
+*/
+static void notify_send(struct notify_context *notify, struct notify_entry *e,
+ const char *path, uint32_t action)
+{
+ struct notify_event ev;
+ DATA_BLOB data;
+ NTSTATUS status;
+ enum ndr_err_code ndr_err;
+ TALLOC_CTX *tmp_ctx;
+
+ ev.action = action;
+ ev.dir = discard_const_p(char, "");
+ ev.path = path;
+ ev.private_data = e->private_data;
+
+ tmp_ctx = talloc_new(notify);
+
+ ndr_err = ndr_push_struct_blob(&data, tmp_ctx, &ev, (ndr_push_flags_fn_t)ndr_push_notify_event);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ talloc_free(tmp_ctx);
+ return;
+ }
+
+ status = imessaging_send(notify->imessaging_ctx, e->server,
+ MSG_PVFS_NOTIFY, &data);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(tmp_ctx);
+ return;
+ }
+
+ talloc_free(tmp_ctx);
+}
+
+
+/*
+ trigger a notify message for anyone waiting on a matching event
+
+ This function is called a lot, and needs to be very fast. The unusual data structure
+ and traversal is designed to be fast in the average case, even for large numbers of
+ notifies
+*/
+void notify_trigger(struct notify_context *notify,
+ uint32_t action, uint32_t filter, const char *path)
+{
+ NTSTATUS status;
+ int depth;
+ const char *p, *next_p;
+
+ /* see if change notify is enabled at all */
+ if (notify == NULL) {
+ return;
+ }
+
+ status = notify_load(notify);
+ if (!NT_STATUS_IS_OK(status)) {
+ return;
+ }
+
+ /* loop along the given path, working with each directory depth separately */
+ for (depth=0,p=path;
+ p && depth < notify->array->num_depths;
+ p=next_p,depth++) {
+ int p_len = p - path;
+ int min_i, max_i, i;
+ struct notify_depth *d = &notify->array->depth[depth];
+ next_p = strchr(p+1, '/');
+
+ /* see if there are any entries at this depth */
+ if (d->num_entries == 0) continue;
+
+ /* try to skip based on the maximum mask. If next_p is
+ NULL then we know it will be a 'this directory'
+ match, otherwise it must be a subdir match */
+ if (next_p != NULL) {
+ if (0 == (filter & d->max_mask_subdir)) {
+ continue;
+ }
+ } else {
+ if (0 == (filter & d->max_mask)) {
+ continue;
+ }
+ }
+
+ /* we know there is an entry here worth looking
+ for. Use a bisection search to find the first entry
+ with a matching path */
+ min_i = 0;
+ max_i = d->num_entries-1;
+
+ while (min_i < max_i) {
+ struct notify_entry *e;
+ int cmp;
+ i = (min_i+max_i)/2;
+ e = &d->entries[i];
+ cmp = strncmp(path, e->path, p_len);
+ if (cmp == 0) {
+ if (p_len == e->path_len) {
+ max_i = i;
+ } else {
+ max_i = i-1;
+ }
+ } else if (cmp < 0) {
+ max_i = i-1;
+ } else {
+ min_i = i+1;
+ }
+ }
+
+ if (min_i != max_i) {
+ /* none match */
+ continue;
+ }
+
+ /* we now know that the entries start at min_i */
+ for (i=min_i;i<d->num_entries;i++) {
+ struct notify_entry *e = &d->entries[i];
+ if (p_len != e->path_len ||
+ strncmp(path, e->path, p_len) != 0) break;
+ if (next_p != NULL) {
+ if (0 == (filter & e->subdir_filter)) {
+ continue;
+ }
+ } else {
+ if (0 == (filter & e->filter)) {
+ continue;
+ }
+ }
+ notify_send(notify, e, path + e->path_len + 1, action);
+ }
+ }
+}
diff --git a/source4/ntvfs/common/ntvfs_common.h b/source4/ntvfs/common/ntvfs_common.h
new file mode 100644
index 0000000..37dd553
--- /dev/null
+++ b/source4/ntvfs/common/ntvfs_common.h
@@ -0,0 +1,32 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Stefan Metzmacher 2007
+
+ 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/>.
+*/
+
+#ifndef _NTVFS_COMMON_H_
+#define _NTVFS_COMMON_H_
+
+#include "ntvfs/ntvfs.h"
+
+struct notify_event;
+struct notify_entry;
+
+#include "ntvfs/common/brlock.h"
+#include "ntvfs/common/opendb.h"
+#include "ntvfs/common/proto.h"
+
+#endif /* _NTVFS_COMMON_H_ */
diff --git a/source4/ntvfs/common/opendb.c b/source4/ntvfs/common/opendb.c
new file mode 100644
index 0000000..29081ef
--- /dev/null
+++ b/source4/ntvfs/common/opendb.c
@@ -0,0 +1,200 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Andrew Tridgell 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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/>.
+*/
+
+/*
+ this is the open files database. It implements shared storage of
+ what files are open between server instances, and implements the rules
+ of shared access to files.
+
+ The caller needs to provide a file_key, which specifies what file
+ they are talking about. This needs to be a unique key across all
+ filesystems, and is usually implemented in terms of a device/inode
+ pair.
+
+ Before any operations can be performed the caller needs to establish
+ a lock on the record associated with file_key. That is done by
+ calling odb_lock(). The caller releases this lock by calling
+ talloc_free() on the returned handle.
+
+ All other operations on a record are done by passing the odb_lock()
+ handle back to this module. The handle contains internal
+ information about what file_key is being operated on.
+*/
+
+#include "includes.h"
+#include "ntvfs/ntvfs.h"
+#include "ntvfs/common/ntvfs_common.h"
+#include "cluster/cluster.h"
+#include "param/param.h"
+
+static const struct opendb_ops *ops;
+
+/*
+ set the odb backend ops
+*/
+void odb_set_ops(const struct opendb_ops *new_ops)
+{
+ ops = new_ops;
+}
+
+/*
+ Open up the openfiles.tdb database. Close it down using
+ talloc_free(). We need the imessaging_ctx to allow for pending open
+ notifications.
+*/
+struct odb_context *odb_init(TALLOC_CTX *mem_ctx,
+ struct ntvfs_context *ntvfs_ctx)
+{
+ if (ops == NULL) {
+ odb_tdb_init_ops();
+ }
+ return ops->odb_init(mem_ctx, ntvfs_ctx);
+}
+
+/*
+ get a lock on a entry in the odb. This call returns a lock handle,
+ which the caller should unlock using talloc_free().
+*/
+struct odb_lock *odb_lock(TALLOC_CTX *mem_ctx,
+ struct odb_context *odb, DATA_BLOB *file_key)
+{
+ return ops->odb_lock(mem_ctx, odb, file_key);
+}
+
+DATA_BLOB odb_get_key(TALLOC_CTX *mem_ctx, struct odb_lock *lck)
+{
+ return ops->odb_get_key(mem_ctx, lck);
+}
+
+/*
+ register an open file in the open files database.
+ The share_access rules are implemented by odb_can_open()
+ and it's needed to call odb_can_open() before
+ odb_open_file() otherwise NT_STATUS_INTERNAL_ERROR is returned
+
+ Note that the path is only used by the delete on close logic, not
+ for comparing with other filenames
+*/
+NTSTATUS odb_open_file(struct odb_lock *lck,
+ void *file_handle, const char *path,
+ int *fd, NTTIME open_write_time,
+ bool allow_level_II_oplock,
+ uint32_t oplock_level, uint32_t *oplock_granted)
+{
+ return ops->odb_open_file(lck, file_handle, path,
+ fd, open_write_time,
+ allow_level_II_oplock,
+ oplock_level, oplock_granted);
+}
+
+
+/*
+ register a pending open file in the open files database
+*/
+NTSTATUS odb_open_file_pending(struct odb_lock *lck, void *private_data)
+{
+ return ops->odb_open_file_pending(lck, private_data);
+}
+
+
+/*
+ remove a opendb entry
+*/
+NTSTATUS odb_close_file(struct odb_lock *lck, void *file_handle,
+ const char **delete_path)
+{
+ return ops->odb_close_file(lck, file_handle, delete_path);
+}
+
+
+/*
+ remove a pending opendb entry
+*/
+NTSTATUS odb_remove_pending(struct odb_lock *lck, void *private_data)
+{
+ return ops->odb_remove_pending(lck, private_data);
+}
+
+
+/*
+ rename the path in a open file
+*/
+NTSTATUS odb_rename(struct odb_lock *lck, const char *path)
+{
+ return ops->odb_rename(lck, path);
+}
+
+/*
+ get back the path of an open file
+*/
+NTSTATUS odb_get_path(struct odb_lock *lck, const char **path)
+{
+ return ops->odb_get_path(lck, path);
+}
+
+/*
+ update delete on close flag on an open file
+*/
+NTSTATUS odb_set_delete_on_close(struct odb_lock *lck, bool del_on_close)
+{
+ return ops->odb_set_delete_on_close(lck, del_on_close);
+}
+
+/*
+ update the write time on an open file
+*/
+NTSTATUS odb_set_write_time(struct odb_lock *lck,
+ NTTIME write_time, bool force)
+{
+ return ops->odb_set_write_time(lck, write_time, force);
+}
+
+/*
+ return the current value of the delete_on_close bit,
+ and the current write time.
+*/
+NTSTATUS odb_get_file_infos(struct odb_context *odb, DATA_BLOB *key,
+ bool *del_on_close, NTTIME *write_time)
+{
+ return ops->odb_get_file_infos(odb, key, del_on_close, write_time);
+}
+
+/*
+ determine if a file can be opened with the given share_access,
+ create_options and access_mask
+*/
+NTSTATUS odb_can_open(struct odb_lock *lck,
+ 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, stream_id, share_access, access_mask,
+ delete_on_close, open_disposition, break_to_none);
+}
+
+NTSTATUS odb_update_oplock(struct odb_lock *lck, void *file_handle,
+ uint32_t oplock_level)
+{
+ return ops->odb_update_oplock(lck, file_handle, oplock_level);
+}
+
+NTSTATUS odb_break_oplocks(struct odb_lock *lck)
+{
+ return ops->odb_break_oplocks(lck);
+}
diff --git a/source4/ntvfs/common/opendb.h b/source4/ntvfs/common/opendb.h
new file mode 100644
index 0000000..1bfc6aa
--- /dev/null
+++ b/source4/ntvfs/common/opendb.h
@@ -0,0 +1,59 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ open database code - common include
+
+ Copyright (C) Andrew Tridgell 2007
+
+ 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/>.
+*/
+
+struct opendb_ops {
+ struct odb_context *(*odb_init)(TALLOC_CTX *mem_ctx,
+ struct ntvfs_context *ntvfs_ctx);
+ 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, const char *path,
+ int *fd, NTTIME open_write_time,
+ bool allow_level_II_oplock,
+ uint32_t oplock_level, uint32_t *oplock_granted);
+ NTSTATUS (*odb_open_file_pending)(struct odb_lock *lck, void *private_data);
+ NTSTATUS (*odb_close_file)(struct odb_lock *lck, void *file_handle,
+ const char **delete_path);
+ NTSTATUS (*odb_remove_pending)(struct odb_lock *lck, void *private_data);
+ NTSTATUS (*odb_rename)(struct odb_lock *lck, const char *path);
+ NTSTATUS (*odb_get_path)(struct odb_lock *lck, const char **path);
+ NTSTATUS (*odb_set_delete_on_close)(struct odb_lock *lck, bool del_on_close);
+ NTSTATUS (*odb_set_write_time)(struct odb_lock *lck,
+ NTTIME write_time, bool force);
+ NTSTATUS (*odb_get_file_infos)(struct odb_context *odb, DATA_BLOB *key,
+ bool *del_on_close, NTTIME *write_time);
+ NTSTATUS (*odb_can_open)(struct odb_lock *lck,
+ 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 {
+ void *file_handle;
+ uint8_t level;
+};
+
+void odb_set_ops(const struct opendb_ops *new_ops);
+void odb_tdb_init_ops(void);
diff --git a/source4/ntvfs/common/opendb_tdb.c b/source4/ntvfs/common/opendb_tdb.c
new file mode 100644
index 0000000..9fe0b26
--- /dev/null
+++ b/source4/ntvfs/common/opendb_tdb.c
@@ -0,0 +1,886 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Andrew Tridgell 2004
+ 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/>.
+*/
+
+/*
+ this is the open files database, tdb backend. It implements shared
+ storage of what files are open between server instances, and
+ implements the rules of shared access to files.
+
+ The caller needs to provide a file_key, which specifies what file
+ they are talking about. This needs to be a unique key across all
+ filesystems, and is usually implemented in terms of a device/inode
+ pair.
+
+ Before any operations can be performed the caller needs to establish
+ a lock on the record associated with file_key. That is done by
+ calling odb_lock(). The caller releases this lock by calling
+ talloc_free() on the returned handle.
+
+ All other operations on a record are done by passing the odb_lock()
+ handle back to this module. The handle contains internal
+ information about what file_key is being operated on.
+*/
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "lib/dbwrap/dbwrap.h"
+#include "messaging/messaging.h"
+#include "lib/messaging/irpc.h"
+#include "librpc/gen_ndr/ndr_opendb.h"
+#include "ntvfs/ntvfs.h"
+#include "ntvfs/common/ntvfs_common.h"
+#include "cluster/cluster.h"
+#include "param/param.h"
+#include "ntvfs/sysdep/sys_lease.h"
+
+struct odb_context {
+ struct db_context *db;
+ struct ntvfs_context *ntvfs_ctx;
+ bool oplocks;
+ struct sys_lease_context *lease_ctx;
+};
+
+/*
+ an odb lock handle. You must obtain one of these using odb_lock() before doing
+ any other operations.
+*/
+struct odb_lock {
+ struct odb_context *odb;
+ struct db_record *locked;
+
+ struct opendb_file file;
+
+ struct {
+ struct opendb_entry *e;
+ bool attrs_only;
+ } can_open;
+};
+
+static NTSTATUS odb_oplock_break_send(struct imessaging_context *msg_ctx,
+ struct opendb_entry *e,
+ uint8_t level);
+
+/*
+ Open up the openfiles.tdb database. Close it down using
+ talloc_free(). We need the imessaging_ctx to allow for pending open
+ notifications.
+*/
+static struct odb_context *odb_tdb_init(TALLOC_CTX *mem_ctx,
+ struct ntvfs_context *ntvfs_ctx)
+{
+ struct odb_context *odb;
+
+ odb = talloc(mem_ctx, struct odb_context);
+ if (odb == NULL) {
+ return NULL;
+ }
+
+ odb->db = cluster_db_tmp_open(odb, ntvfs_ctx->lp_ctx,
+ "openfiles", TDB_DEFAULT);
+ if (odb->db == NULL) {
+ talloc_free(odb);
+ return NULL;
+ }
+
+ odb->ntvfs_ctx = ntvfs_ctx;
+
+ odb->oplocks = share_bool_option(ntvfs_ctx->config, SHARE_OPLOCKS, SHARE_OPLOCKS_DEFAULT);
+
+ odb->lease_ctx = sys_lease_context_create(ntvfs_ctx->config, odb,
+ ntvfs_ctx->event_ctx,
+ ntvfs_ctx->msg_ctx,
+ odb_oplock_break_send);
+
+ return odb;
+}
+
+static NTSTATUS odb_pull_record(struct odb_lock *lck, struct opendb_file *file);
+
+/*
+ get a lock on a entry in the odb. This call returns a lock handle,
+ which the caller should unlock using talloc_free().
+*/
+static struct odb_lock *odb_tdb_lock(TALLOC_CTX *mem_ctx,
+ struct odb_context *odb, DATA_BLOB *file_key)
+{
+ struct odb_lock *lck;
+ NTSTATUS status;
+ TDB_DATA key;
+
+ lck = talloc(mem_ctx, struct odb_lock);
+ if (lck == NULL) {
+ return NULL;
+ }
+
+ lck->odb = talloc_reference(lck, odb);
+ key.dptr = talloc_memdup(lck, file_key->data, file_key->length);
+ key.dsize = file_key->length;
+ if (key.dptr == NULL) {
+ talloc_free(lck);
+ return NULL;
+ }
+
+ lck->locked = dbwrap_fetch_locked(odb->db, lck, key);
+ if (!lck->locked) {
+ talloc_free(lck);
+ return NULL;
+ }
+
+ ZERO_STRUCT(lck->can_open);
+
+ status = odb_pull_record(lck, &lck->file);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+ /* initialise a blank structure */
+ ZERO_STRUCT(lck->file);
+ } else if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(lck);
+ return NULL;
+ }
+
+ return lck;
+}
+
+static DATA_BLOB odb_tdb_get_key(TALLOC_CTX *mem_ctx, struct odb_lock *lck)
+{
+ TDB_DATA key = dbwrap_record_get_key(lck->locked);
+ return data_blob_talloc(mem_ctx, key.dptr, key.dsize);
+}
+
+
+/*
+ determine if two odb_entry structures conflict
+
+ return NT_STATUS_OK on no conflict
+*/
+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 */
+ if (!(e1->access_mask & (SEC_FILE_WRITE_DATA |
+ SEC_FILE_APPEND_DATA |
+ SEC_FILE_READ_DATA |
+ SEC_FILE_EXECUTE |
+ SEC_STD_DELETE))) {
+ return NT_STATUS_OK;
+ }
+ if (!(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 != stream_id) {
+ return NT_STATUS_OK;
+ }
+
+#define CHECK_MASK(am, right, sa, share) \
+ if (((am) & (right)) && !((sa) & (share))) return NT_STATUS_SHARING_VIOLATION
+
+ CHECK_MASK(e1->access_mask, SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA,
+ 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,
+ 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,
+ 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;
+}
+
+/*
+ pull a record, translating from the db format to the opendb_file structure defined
+ in opendb.idl
+*/
+static NTSTATUS odb_pull_record(struct odb_lock *lck, struct opendb_file *file)
+{
+ TDB_DATA dbuf;
+ DATA_BLOB blob;
+ enum ndr_err_code ndr_err;
+
+ dbuf = dbwrap_record_get_value(lck->locked);
+ if (!dbuf.dptr) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ blob.data = dbuf.dptr;
+ blob.length = dbuf.dsize;
+
+ ndr_err = ndr_pull_struct_blob(&blob, lck, file, (ndr_pull_flags_fn_t)ndr_pull_opendb_file);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ push a record, translating from the opendb_file structure defined in opendb.idl
+*/
+static NTSTATUS odb_push_record(struct odb_lock *lck, struct opendb_file *file)
+{
+ TDB_DATA dbuf;
+ DATA_BLOB blob;
+ enum ndr_err_code ndr_err;
+ NTSTATUS status;
+
+ if (file->num_entries == 0) {
+ return dbwrap_record_delete(lck->locked);
+ }
+
+ ndr_err = ndr_push_struct_blob(&blob, lck, file, (ndr_push_flags_fn_t)ndr_push_opendb_file);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ dbuf.dptr = blob.data;
+ dbuf.dsize = blob.length;
+
+ status = dbwrap_record_store(lck->locked, dbuf, TDB_REPLACE);
+ data_blob_free(&blob);
+ return status;
+}
+
+/*
+ send an oplock break to a client
+*/
+static NTSTATUS odb_oplock_break_send(struct imessaging_context *msg_ctx,
+ struct opendb_entry *e,
+ uint8_t level)
+{
+ NTSTATUS status;
+ struct opendb_oplock_break op_break;
+ DATA_BLOB blob;
+
+ ZERO_STRUCT(op_break);
+
+ /* tell the server handling this open file about the need to send the client
+ a break */
+ op_break.file_handle = e->file_handle;
+ op_break.level = level;
+
+ blob = data_blob_const(&op_break, sizeof(op_break));
+
+ status = imessaging_send(msg_ctx, e->server,
+ MSG_NTVFS_OPLOCK_BREAK, &blob);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ return NT_STATUS_OK;
+}
+
+static bool access_attributes_only(uint32_t access_mask,
+ uint32_t open_disposition,
+ bool break_to_none)
+{
+ switch (open_disposition) {
+ case NTCREATEX_DISP_SUPERSEDE:
+ case NTCREATEX_DISP_OVERWRITE_IF:
+ case NTCREATEX_DISP_OVERWRITE:
+ return false;
+ default:
+ break;
+ }
+
+ if (break_to_none) {
+ return false;
+ }
+
+#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,
+ break_to_none);
+ 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 propagate by sending a oplock
+ break request and suspending this call
+ until the break is acknowledged or the file
+ is closed */
+ if (break_to_none ||
+ !file->entries[i].allow_level_II_oplock) {
+ oplock_return = OPLOCK_BREAK_TO_NONE;
+ }
+ odb_oplock_break_send(odb->ntvfs_ctx->msg_ctx,
+ &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) {
+ bool oplock_return = OPLOCK_BREAK_TO_LEVEL_II;
+ /* if this is an attribute only access
+ * it doesn't conflict with an EXCLUSIVE oplock
+ * but we'll not grant the oplock below
+ */
+ attrs_only = access_attributes_only(access_mask,
+ open_disposition,
+ break_to_none);
+ if (attrs_only) {
+ break;
+ }
+ /*
+ * send an oplock break to the holder of the
+ * oplock and tell caller to retry later
+ */
+ if (break_to_none ||
+ !file->entries[i].allow_level_II_oplock) {
+ oplock_return = OPLOCK_BREAK_TO_NONE;
+ }
+ odb_oplock_break_send(odb->ntvfs_ctx->msg_ctx,
+ &file->entries[i],
+ oplock_return);
+ 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.
+ The share_access rules are implemented by odb_can_open()
+ and it's needed to call odb_can_open() before
+ odb_open_file() otherwise NT_STATUS_INTERNAL_ERROR is returned
+
+ 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, const char *path,
+ int *fd, NTTIME open_write_time,
+ bool allow_level_II_oplock,
+ uint32_t oplock_level, uint32_t *oplock_granted)
+{
+ struct odb_context *odb = lck->odb;
+
+ if (!lck->can_open.e) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ if (odb->oplocks == false) {
+ oplock_level = OPLOCK_NONE;
+ }
+
+ if (!oplock_granted) {
+ oplock_level = OPLOCK_NONE;
+ }
+
+ if (lck->file.path == NULL) {
+ lck->file.path = talloc_strdup(lck, path);
+ NT_STATUS_HAVE_NO_MEMORY(lck->file.path);
+ }
+
+ if (lck->file.open_write_time == 0) {
+ lck->file.open_write_time = open_write_time;
+ }
+
+ /*
+ possibly grant an exclusive, batch or level2 oplock
+ */
+ if (lck->can_open.attrs_only) {
+ oplock_level = OPLOCK_NONE;
+ } else if (oplock_level == OPLOCK_EXCLUSIVE) {
+ if (lck->file.num_entries == 0) {
+ oplock_level = OPLOCK_EXCLUSIVE;
+ } else if (allow_level_II_oplock) {
+ oplock_level = OPLOCK_LEVEL_II;
+ } else {
+ oplock_level = OPLOCK_NONE;
+ }
+ } else if (oplock_level == OPLOCK_BATCH) {
+ if (lck->file.num_entries == 0) {
+ oplock_level = OPLOCK_BATCH;
+ } else if (allow_level_II_oplock) {
+ oplock_level = OPLOCK_LEVEL_II;
+ } else {
+ oplock_level = OPLOCK_NONE;
+ }
+ } else if (oplock_level == OPLOCK_LEVEL_II) {
+ oplock_level = OPLOCK_LEVEL_II;
+ } else {
+ oplock_level = OPLOCK_NONE;
+ }
+
+ lck->can_open.e->file_handle = file_handle;
+ lck->can_open.e->fd = fd;
+ lck->can_open.e->allow_level_II_oplock = allow_level_II_oplock;
+ lck->can_open.e->oplock_level = oplock_level;
+
+ if (odb->lease_ctx && fd) {
+ NTSTATUS status;
+ status = sys_lease_setup(odb->lease_ctx, lck->can_open.e);
+ NT_STATUS_NOT_OK_RETURN(status);
+ }
+
+ if (oplock_granted) {
+ if (lck->can_open.e->oplock_level == OPLOCK_EXCLUSIVE) {
+ *oplock_granted = EXCLUSIVE_OPLOCK_RETURN;
+ } else if (lck->can_open.e->oplock_level == OPLOCK_BATCH) {
+ *oplock_granted = BATCH_OPLOCK_RETURN;
+ } else if (lck->can_open.e->oplock_level == OPLOCK_LEVEL_II) {
+ *oplock_granted = LEVEL_II_OPLOCK_RETURN;
+ } else {
+ *oplock_granted = NO_OPLOCK_RETURN;
+ }
+ }
+
+ /* it doesn't conflict, so add it to the end */
+ lck->file.entries = talloc_realloc(lck, lck->file.entries,
+ struct opendb_entry,
+ lck->file.num_entries+1);
+ NT_STATUS_HAVE_NO_MEMORY(lck->file.entries);
+
+ lck->file.entries[lck->file.num_entries] = *lck->can_open.e;
+ lck->file.num_entries++;
+
+ talloc_free(lck->can_open.e);
+ lck->can_open.e = NULL;
+
+ return odb_push_record(lck, &lck->file);
+}
+
+
+/*
+ register a pending open file in the open files database
+*/
+static NTSTATUS odb_tdb_open_file_pending(struct odb_lock *lck, void *private_data)
+{
+ struct odb_context *odb = lck->odb;
+
+ if (lck->file.path == NULL) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ lck->file.pending = talloc_realloc(lck, lck->file.pending,
+ struct opendb_pending,
+ lck->file.num_pending+1);
+ NT_STATUS_HAVE_NO_MEMORY(lck->file.pending);
+
+ lck->file.pending[lck->file.num_pending].server = odb->ntvfs_ctx->server_id;
+ lck->file.pending[lck->file.num_pending].notify_ptr = private_data;
+
+ lck->file.num_pending++;
+
+ return odb_push_record(lck, &lck->file);
+}
+
+
+/*
+ remove a opendb entry
+*/
+static NTSTATUS odb_tdb_close_file(struct odb_lock *lck, void *file_handle,
+ const char **_delete_path)
+{
+ struct odb_context *odb = lck->odb;
+ const char *delete_path = NULL;
+ int i;
+
+ if (lck->file.path == NULL) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ /* find the entry, and delete it */
+ for (i=0;i<lck->file.num_entries;i++) {
+ if (file_handle == lck->file.entries[i].file_handle &&
+ cluster_id_equal(&odb->ntvfs_ctx->server_id, &lck->file.entries[i].server)) {
+ if (lck->file.entries[i].delete_on_close) {
+ lck->file.delete_on_close = true;
+ }
+ if (odb->lease_ctx && lck->file.entries[i].fd) {
+ NTSTATUS status;
+ status = sys_lease_remove(odb->lease_ctx, &lck->file.entries[i]);
+ NT_STATUS_NOT_OK_RETURN(status);
+ }
+ if (i < lck->file.num_entries-1) {
+ memmove(lck->file.entries+i, lck->file.entries+i+1,
+ (lck->file.num_entries - (i+1)) *
+ sizeof(struct opendb_entry));
+ }
+ break;
+ }
+ }
+
+ if (i == lck->file.num_entries) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* send any pending notifications, removing them once sent */
+ for (i=0;i<lck->file.num_pending;i++) {
+ imessaging_send_ptr(odb->ntvfs_ctx->msg_ctx,
+ lck->file.pending[i].server,
+ MSG_PVFS_RETRY_OPEN,
+ lck->file.pending[i].notify_ptr);
+ }
+ lck->file.num_pending = 0;
+
+ lck->file.num_entries--;
+
+ if (lck->file.num_entries == 0 && lck->file.delete_on_close) {
+ delete_path = lck->file.path;
+ }
+
+ if (_delete_path) {
+ *_delete_path = delete_path;
+ }
+
+ return odb_push_record(lck, &lck->file);
+}
+
+/*
+ update the oplock level of the client
+*/
+static NTSTATUS odb_tdb_update_oplock(struct odb_lock *lck, void *file_handle,
+ uint32_t oplock_level)
+{
+ struct odb_context *odb = lck->odb;
+ int i;
+
+ if (lck->file.path == NULL) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ /* find the entry, and update it */
+ for (i=0;i<lck->file.num_entries;i++) {
+ if (file_handle == lck->file.entries[i].file_handle &&
+ cluster_id_equal(&odb->ntvfs_ctx->server_id, &lck->file.entries[i].server)) {
+ lck->file.entries[i].oplock_level = oplock_level;
+
+ if (odb->lease_ctx && lck->file.entries[i].fd) {
+ NTSTATUS status;
+ status = sys_lease_update(odb->lease_ctx, &lck->file.entries[i]);
+ NT_STATUS_NOT_OK_RETURN(status);
+ }
+
+ break;
+ }
+ }
+
+ if (i == lck->file.num_entries) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ /* send any pending notifications, removing them once sent */
+ for (i=0;i<lck->file.num_pending;i++) {
+ imessaging_send_ptr(odb->ntvfs_ctx->msg_ctx,
+ lck->file.pending[i].server,
+ MSG_PVFS_RETRY_OPEN,
+ lck->file.pending[i].notify_ptr);
+ }
+ lck->file.num_pending = 0;
+
+ return odb_push_record(lck, &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;
+ int i;
+ bool modified = false;
+
+ /* see if anyone has an oplock, which we need to break */
+ for (i=0;i<lck->file.num_entries;i++) {
+ if (lck->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->ntvfs_ctx->msg_ctx,
+ &lck->file.entries[i],
+ OPLOCK_BREAK_TO_NONE);
+ lck->file.entries[i].oplock_level = OPLOCK_NONE;
+ modified = true;
+ }
+ }
+
+ if (modified) {
+ return odb_push_record(lck, &lck->file);
+ }
+ return NT_STATUS_OK;
+}
+
+/*
+ remove a pending opendb entry
+*/
+static NTSTATUS odb_tdb_remove_pending(struct odb_lock *lck, void *private_data)
+{
+ struct odb_context *odb = lck->odb;
+ int i;
+
+ if (lck->file.path == NULL) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ /* find the entry, and delete it */
+ for (i=0;i<lck->file.num_pending;i++) {
+ if (private_data == lck->file.pending[i].notify_ptr &&
+ cluster_id_equal(&odb->ntvfs_ctx->server_id, &lck->file.pending[i].server)) {
+ if (i < lck->file.num_pending-1) {
+ memmove(lck->file.pending+i, lck->file.pending+i+1,
+ (lck->file.num_pending - (i+1)) *
+ sizeof(struct opendb_pending));
+ }
+ break;
+ }
+ }
+
+ if (i == lck->file.num_pending) {
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ lck->file.num_pending--;
+
+ return odb_push_record(lck, &lck->file);
+}
+
+
+/*
+ rename the path in a open file
+*/
+static NTSTATUS odb_tdb_rename(struct odb_lock *lck, const char *path)
+{
+ if (lck->file.path == NULL) {
+ /* not having the record at all is OK */
+ return NT_STATUS_OK;
+ }
+
+ lck->file.path = talloc_strdup(lck, path);
+ NT_STATUS_HAVE_NO_MEMORY(lck->file.path);
+
+ return odb_push_record(lck, &lck->file);
+}
+
+/*
+ get the path of an open file
+*/
+static NTSTATUS odb_tdb_get_path(struct odb_lock *lck, const char **path)
+{
+ *path = NULL;
+
+ /* we don't ignore NT_STATUS_OBJECT_NAME_NOT_FOUND here */
+ if (lck->file.path == NULL) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ *path = lck->file.path;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ update delete on close flag on an open file
+*/
+static NTSTATUS odb_tdb_set_delete_on_close(struct odb_lock *lck, bool del_on_close)
+{
+ if (lck->file.path == NULL) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ lck->file.delete_on_close = del_on_close;
+
+ return odb_push_record(lck, &lck->file);
+}
+
+/*
+ update the write time on an open file
+*/
+static NTSTATUS odb_tdb_set_write_time(struct odb_lock *lck,
+ NTTIME write_time, bool force)
+{
+ if (lck->file.path == NULL) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ if (lck->file.changed_write_time != 0 && !force) {
+ return NT_STATUS_OK;
+ }
+
+ lck->file.changed_write_time = write_time;
+
+ return odb_push_record(lck, &lck->file);
+}
+
+/*
+ return the current value of the delete_on_close bit, and how many
+ people still have the file open
+*/
+static NTSTATUS odb_tdb_get_file_infos(struct odb_context *odb, DATA_BLOB *key,
+ bool *del_on_close, NTTIME *write_time)
+{
+ struct odb_lock *lck;
+
+ if (del_on_close) {
+ *del_on_close = false;
+ }
+ if (write_time) {
+ *write_time = 0;
+ }
+
+ lck = odb_lock(odb, odb, key);
+ NT_STATUS_HAVE_NO_MEMORY(lck);
+
+ if (del_on_close) {
+ *del_on_close = lck->file.delete_on_close;
+ }
+ if (write_time) {
+ if (lck->file.changed_write_time == 0) {
+ *write_time = lck->file.open_write_time;
+ } else {
+ *write_time = lck->file.changed_write_time;
+ }
+ }
+
+ talloc_free(lck);
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ determine if a file can be opened with the given share_access,
+ create_options and access_mask
+*/
+static NTSTATUS odb_tdb_can_open(struct odb_lock *lck,
+ 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;
+
+ status = odb_tdb_open_can_internal(odb, &lck->file, stream_id,
+ share_access, access_mask,
+ delete_on_close, open_disposition,
+ break_to_none, &lck->can_open.attrs_only);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ lck->can_open.e = talloc(lck, struct opendb_entry);
+ NT_STATUS_HAVE_NO_MEMORY(lck->can_open.e);
+
+ lck->can_open.e->server = odb->ntvfs_ctx->server_id;
+ lck->can_open.e->file_handle = NULL;
+ lck->can_open.e->fd = NULL;
+ lck->can_open.e->stream_id = stream_id;
+ lck->can_open.e->share_access = share_access;
+ lck->can_open.e->access_mask = access_mask;
+ lck->can_open.e->delete_on_close = delete_on_close;
+ lck->can_open.e->allow_level_II_oplock = false;
+ lck->can_open.e->oplock_level = OPLOCK_NONE;
+
+ return NT_STATUS_OK;
+}
+
+
+static const struct opendb_ops opendb_tdb_ops = {
+ .odb_init = odb_tdb_init,
+ .odb_lock = odb_tdb_lock,
+ .odb_get_key = odb_tdb_get_key,
+ .odb_open_file = odb_tdb_open_file,
+ .odb_open_file_pending = odb_tdb_open_file_pending,
+ .odb_close_file = odb_tdb_close_file,
+ .odb_remove_pending = odb_tdb_remove_pending,
+ .odb_rename = odb_tdb_rename,
+ .odb_get_path = odb_tdb_get_path,
+ .odb_set_delete_on_close = odb_tdb_set_delete_on_close,
+ .odb_set_write_time = odb_tdb_set_write_time,
+ .odb_get_file_infos = odb_tdb_get_file_infos,
+ .odb_can_open = odb_tdb_can_open,
+ .odb_update_oplock = odb_tdb_update_oplock,
+ .odb_break_oplocks = odb_tdb_break_oplocks
+};
+
+
+void odb_tdb_init_ops(void)
+{
+ sys_lease_init();
+ odb_set_ops(&opendb_tdb_ops);
+}
diff --git a/source4/ntvfs/common/wscript_build b/source4/ntvfs/common/wscript_build
new file mode 100644
index 0000000..b144472
--- /dev/null
+++ b/source4/ntvfs/common/wscript_build
@@ -0,0 +1,9 @@
+#!/usr/bin/env python
+
+bld.SAMBA_SUBSYSTEM('ntvfs_common',
+ source='init.c brlock.c brlock_tdb.c opendb.c opendb_tdb.c notify.c',
+ autoproto='proto.h',
+ deps='util_tdb tdb-wrap',
+ public_deps='NDR_OPENDB NDR_NOTIFY sys_notify sys_lease share'
+ )
+
diff --git a/source4/ntvfs/ipc/README b/source4/ntvfs/ipc/README
new file mode 100644
index 0000000..059a714
--- /dev/null
+++ b/source4/ntvfs/ipc/README
@@ -0,0 +1,5 @@
+This is the IPC$ backend for Samba. NTVFS operations that are made on
+IPC$ shares are directed here by default. Most file operations
+are not supported on IPC$ shares.
+
+
diff --git a/source4/ntvfs/ipc/ipc_rap.c b/source4/ntvfs/ipc/ipc_rap.c
new file mode 100644
index 0000000..9ddde5e
--- /dev/null
+++ b/source4/ntvfs/ipc/ipc_rap.c
@@ -0,0 +1,511 @@
+/*
+ Unix SMB/CIFS implementation.
+ RAP handlers
+
+ Copyright (C) Volker Lendecke 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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 "libcli/raw/interfaces.h"
+#include "../librpc/gen_ndr/rap.h"
+#include "events/events.h"
+#include "ntvfs/ipc/proto.h"
+#include "librpc/ndr/libndr.h"
+#include "param/param.h"
+
+#define NDR_RETURN(call) do { \
+ enum ndr_err_code _ndr_err; \
+ _ndr_err = call; \
+ if (!NDR_ERR_CODE_IS_SUCCESS(_ndr_err)) { \
+ return ndr_map_error2ntstatus(_ndr_err); \
+ } \
+} while (0)
+
+#define RAP_GOTO(call) do { \
+ result = call; \
+ if (NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL)) {\
+ goto buffer_overflow; \
+ } \
+ if (!NT_STATUS_IS_OK(result)) { \
+ goto done; \
+ } \
+} while (0)
+
+#define NDR_GOTO(call) do { \
+ enum ndr_err_code _ndr_err; \
+ _ndr_err = call; \
+ if (!NDR_ERR_CODE_IS_SUCCESS(_ndr_err)) { \
+ RAP_GOTO(ndr_map_error2ntstatus(_ndr_err)); \
+ } \
+} while (0)
+
+
+#define NERR_notsupported 50
+
+struct rap_string_heap {
+ TALLOC_CTX *mem_ctx;
+ int offset;
+ int num_strings;
+ const char **strings;
+};
+
+struct rap_heap_save {
+ int offset, num_strings;
+};
+
+static void rap_heap_save(struct rap_string_heap *heap,
+ struct rap_heap_save *save)
+{
+ save->offset = heap->offset;
+ save->num_strings = heap->num_strings;
+}
+
+static void rap_heap_restore(struct rap_string_heap *heap,
+ struct rap_heap_save *save)
+{
+ heap->offset = save->offset;
+ heap->num_strings = save->num_strings;
+}
+
+struct rap_call {
+ struct loadparm_context *lp_ctx;
+
+ TALLOC_CTX *mem_ctx;
+ uint16_t callno;
+ const char *paramdesc;
+ const char *datadesc;
+
+ uint16_t status;
+ uint16_t convert;
+
+ uint16_t rcv_paramlen, rcv_datalen;
+
+ struct ndr_push *ndr_push_param;
+ struct ndr_push *ndr_push_data;
+ struct rap_string_heap *heap;
+
+ struct ndr_pull *ndr_pull_param;
+ struct ndr_pull *ndr_pull_data;
+
+ struct tevent_context *event_ctx;
+};
+
+#define RAPNDR_FLAGS (LIBNDR_FLAG_NOALIGN|LIBNDR_FLAG_STR_ASCII|LIBNDR_FLAG_STR_NULLTERM);
+
+static struct rap_call *new_rap_srv_call(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev_ctx,
+ struct loadparm_context *lp_ctx,
+ struct smb_trans2 *trans)
+{
+ struct rap_call *call;
+
+ call = talloc(mem_ctx, struct rap_call);
+
+ if (call == NULL)
+ return NULL;
+
+ ZERO_STRUCTP(call);
+
+ call->lp_ctx = talloc_reference(call, lp_ctx);
+ call->event_ctx = ev_ctx;
+
+ call->mem_ctx = mem_ctx;
+
+ call->ndr_pull_param = ndr_pull_init_blob(&trans->in.params, mem_ctx);
+ call->ndr_pull_param->flags = RAPNDR_FLAGS;
+
+ call->ndr_pull_data = ndr_pull_init_blob(&trans->in.data, mem_ctx);
+ call->ndr_pull_data->flags = RAPNDR_FLAGS;
+
+ call->heap = talloc(mem_ctx, struct rap_string_heap);
+
+ if (call->heap == NULL)
+ return NULL;
+
+ ZERO_STRUCTP(call->heap);
+
+ call->heap->mem_ctx = mem_ctx;
+
+ return call;
+}
+
+static NTSTATUS rap_srv_pull_word(struct rap_call *call, uint16_t *result)
+{
+ enum ndr_err_code ndr_err;
+
+ if (*call->paramdesc++ != 'W')
+ return NT_STATUS_INVALID_PARAMETER;
+
+ ndr_err = ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, result);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS rap_srv_pull_dword(struct rap_call *call, uint32_t *result)
+{
+ enum ndr_err_code ndr_err;
+
+ if (*call->paramdesc++ != 'D')
+ return NT_STATUS_INVALID_PARAMETER;
+
+ ndr_err = ndr_pull_uint32(call->ndr_pull_param, NDR_SCALARS, result);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS rap_srv_pull_string(struct rap_call *call, const char **result)
+{
+ enum ndr_err_code ndr_err;
+ char paramdesc = *call->paramdesc++;
+
+ if (paramdesc == 'O') {
+ *result = NULL;
+ return NT_STATUS_OK;
+ }
+
+ if (paramdesc != 'z')
+ return NT_STATUS_INVALID_PARAMETER;
+
+ ndr_err = ndr_pull_string(call->ndr_pull_param, NDR_SCALARS, result);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS rap_srv_pull_bufsize(struct rap_call *call, uint16_t *bufsize)
+{
+ enum ndr_err_code ndr_err;
+
+ if ( (*call->paramdesc++ != 'r') || (*call->paramdesc++ != 'L') )
+ return NT_STATUS_INVALID_PARAMETER;
+
+ ndr_err = ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, bufsize);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ call->heap->offset = *bufsize;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS rap_srv_pull_expect_multiple(struct rap_call *call)
+{
+ if ( (*call->paramdesc++ != 'e') || (*call->paramdesc++ != 'h') )
+ return NT_STATUS_INVALID_PARAMETER;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS rap_push_string(struct ndr_push *data_push,
+ struct rap_string_heap *heap,
+ const char *str)
+{
+ size_t space;
+
+ if (str == NULL)
+ str = "";
+
+ space = strlen(str)+1;
+
+ if (heap->offset < space)
+ return NT_STATUS_BUFFER_TOO_SMALL;
+
+ heap->offset -= space;
+
+ NDR_RETURN(ndr_push_uint16(data_push, NDR_SCALARS, heap->offset));
+ NDR_RETURN(ndr_push_uint16(data_push, NDR_SCALARS, 0));
+
+ heap->strings = talloc_realloc(heap->mem_ctx,
+ heap->strings,
+ const char *,
+ heap->num_strings + 1);
+
+ if (heap->strings == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ heap->strings[heap->num_strings] = str;
+ heap->num_strings += 1;
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS _rap_netshareenum(struct rap_call *call)
+{
+ struct rap_NetShareEnum r;
+ NTSTATUS result;
+ uint32_t offset_save = 0;
+ struct rap_heap_save heap_save = {0};
+
+ RAP_GOTO(rap_srv_pull_word(call, &r.in.level));
+ RAP_GOTO(rap_srv_pull_bufsize(call, &r.in.bufsize));
+ RAP_GOTO(rap_srv_pull_expect_multiple(call));
+
+ switch(r.in.level) {
+ case 0:
+ if (strcmp(call->datadesc, "B13") != 0)
+ return NT_STATUS_INVALID_PARAMETER;
+ break;
+ case 1:
+ if (strcmp(call->datadesc, "B13BWz") != 0)
+ return NT_STATUS_INVALID_PARAMETER;
+ break;
+ default:
+ return NT_STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ result = rap_netshareenum(call, call->event_ctx, call->lp_ctx, &r);
+
+ if (!NT_STATUS_IS_OK(result))
+ return result;
+
+ for (r.out.count = 0; r.out.count < r.out.available; r.out.count++) {
+
+ int i = r.out.count;
+
+ offset_save = call->ndr_push_data->offset;
+ rap_heap_save(call->heap, &heap_save);
+
+ switch(r.in.level) {
+ case 0:
+ NDR_GOTO(ndr_push_bytes(call->ndr_push_data,
+ (const uint8_t *)r.out.info[i].info0.share_name,
+ sizeof(r.out.info[i].info0.share_name)));
+ break;
+ case 1:
+ NDR_GOTO(ndr_push_bytes(call->ndr_push_data,
+ (const uint8_t *)r.out.info[i].info1.share_name,
+ sizeof(r.out.info[i].info1.share_name)));
+ NDR_GOTO(ndr_push_uint8(call->ndr_push_data,
+ NDR_SCALARS, r.out.info[i].info1.reserved1));
+ NDR_GOTO(ndr_push_uint16(call->ndr_push_data,
+ NDR_SCALARS, r.out.info[i].info1.share_type));
+
+ RAP_GOTO(rap_push_string(call->ndr_push_data,
+ call->heap,
+ r.out.info[i].info1.comment));
+
+ break;
+ }
+
+ if (call->ndr_push_data->offset > call->heap->offset) {
+
+ buffer_overflow:
+
+ call->ndr_push_data->offset = offset_save;
+ rap_heap_restore(call->heap, &heap_save);
+ break;
+ }
+ }
+
+ call->status = r.out.status;
+
+ NDR_RETURN(ndr_push_uint16(call->ndr_push_param, NDR_SCALARS, r.out.count));
+ NDR_RETURN(ndr_push_uint16(call->ndr_push_param, NDR_SCALARS, r.out.available));
+
+ result = NT_STATUS_OK;
+
+ done:
+ return result;
+}
+
+static NTSTATUS _rap_netserverenum2(struct rap_call *call)
+{
+ struct rap_NetServerEnum2 r;
+ NTSTATUS result;
+ uint32_t offset_save = 0;
+ struct rap_heap_save heap_save = {0};
+
+ RAP_GOTO(rap_srv_pull_word(call, &r.in.level));
+ RAP_GOTO(rap_srv_pull_bufsize(call, &r.in.bufsize));
+ RAP_GOTO(rap_srv_pull_expect_multiple(call));
+ RAP_GOTO(rap_srv_pull_dword(call, &r.in.servertype));
+ RAP_GOTO(rap_srv_pull_string(call, &r.in.domain));
+
+ switch(r.in.level) {
+ case 0:
+ if (strcmp(call->datadesc, "B16") != 0)
+ return NT_STATUS_INVALID_PARAMETER;
+ break;
+ case 1:
+ if (strcmp(call->datadesc, "B16BBDz") != 0)
+ return NT_STATUS_INVALID_PARAMETER;
+ break;
+ default:
+ return NT_STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ result = rap_netserverenum2(call, call->lp_ctx, &r);
+
+ if (!NT_STATUS_IS_OK(result))
+ return result;
+
+ for (r.out.count = 0; r.out.count < r.out.available; r.out.count++) {
+
+ int i = r.out.count;
+
+ offset_save = call->ndr_push_data->offset;
+ rap_heap_save(call->heap, &heap_save);
+
+ switch(r.in.level) {
+ case 0:
+ NDR_GOTO(ndr_push_bytes(call->ndr_push_data,
+ (const uint8_t *)r.out.info[i].info0.name,
+ sizeof(r.out.info[i].info0.name)));
+ break;
+ case 1:
+ NDR_GOTO(ndr_push_bytes(call->ndr_push_data,
+ (const uint8_t *)r.out.info[i].info1.name,
+ sizeof(r.out.info[i].info1.name)));
+ NDR_GOTO(ndr_push_uint8(call->ndr_push_data,
+ NDR_SCALARS, r.out.info[i].info1.version_major));
+ NDR_GOTO(ndr_push_uint8(call->ndr_push_data,
+ NDR_SCALARS, r.out.info[i].info1.version_minor));
+ NDR_GOTO(ndr_push_uint32(call->ndr_push_data,
+ NDR_SCALARS, r.out.info[i].info1.servertype));
+
+ RAP_GOTO(rap_push_string(call->ndr_push_data,
+ call->heap,
+ r.out.info[i].info1.comment));
+
+ break;
+ }
+
+ if (call->ndr_push_data->offset > call->heap->offset) {
+
+ buffer_overflow:
+
+ call->ndr_push_data->offset = offset_save;
+ rap_heap_restore(call->heap, &heap_save);
+ break;
+ }
+ }
+
+ call->status = r.out.status;
+
+ NDR_RETURN(ndr_push_uint16(call->ndr_push_param, NDR_SCALARS, r.out.count));
+ NDR_RETURN(ndr_push_uint16(call->ndr_push_param, NDR_SCALARS, r.out.available));
+
+ result = NT_STATUS_OK;
+
+ done:
+ return result;
+}
+
+static NTSTATUS api_Unsupported(struct rap_call *call)
+{
+ call->status = NERR_notsupported;
+ call->convert = 0;
+ return NT_STATUS_OK;
+}
+
+static const struct
+{
+ const char *name;
+ int id;
+ NTSTATUS (*fn)(struct rap_call *call);
+} api_commands[] = {
+ {"NetShareEnum", RAP_WshareEnum, _rap_netshareenum },
+ {"NetServerEnum2", RAP_NetServerEnum2, _rap_netserverenum2 },
+ {NULL, -1, api_Unsupported}
+};
+
+NTSTATUS ipc_rap_call(TALLOC_CTX *mem_ctx, struct tevent_context *event_ctx, struct loadparm_context *lp_ctx,
+ struct smb_trans2 *trans)
+{
+ int i;
+ NTSTATUS result;
+ struct rap_call *call;
+ DATA_BLOB result_param, result_data;
+ struct ndr_push *final_param;
+ struct ndr_push *final_data;
+
+ call = new_rap_srv_call(mem_ctx, event_ctx, lp_ctx, trans);
+
+ if (call == NULL)
+ return NT_STATUS_NO_MEMORY;
+
+ NDR_RETURN(ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, &call->callno));
+ NDR_RETURN(ndr_pull_string(call->ndr_pull_param, NDR_SCALARS,
+ &call->paramdesc));
+ NDR_RETURN(ndr_pull_string(call->ndr_pull_param, NDR_SCALARS,
+ &call->datadesc));
+
+ call->ndr_push_param = ndr_push_init_ctx(call);
+ call->ndr_push_data = ndr_push_init_ctx(call);
+
+ if ((call->ndr_push_param == NULL) || (call->ndr_push_data == NULL))
+ return NT_STATUS_NO_MEMORY;
+
+ call->ndr_push_param->flags = RAPNDR_FLAGS;
+ call->ndr_push_data->flags = RAPNDR_FLAGS;
+
+ result = NT_STATUS_INVALID_SYSTEM_SERVICE;
+
+ for (i=0; api_commands[i].name != NULL; i++) {
+ if (api_commands[i].id == call->callno) {
+ DEBUG(5, ("Running RAP call %s\n",
+ api_commands[i].name));
+ result = api_commands[i].fn(call);
+ break;
+ }
+ }
+
+ if (!NT_STATUS_IS_OK(result))
+ return result;
+
+ result_param = ndr_push_blob(call->ndr_push_param);
+ result_data = ndr_push_blob(call->ndr_push_data);
+
+ final_param = ndr_push_init_ctx(call);
+ final_data = ndr_push_init_ctx(call);
+
+ if ((final_param == NULL) || (final_data == NULL))
+ return NT_STATUS_NO_MEMORY;
+
+ final_param->flags = RAPNDR_FLAGS;
+ final_data->flags = RAPNDR_FLAGS;
+
+ NDR_RETURN(ndr_push_uint16(final_param, NDR_SCALARS, call->status));
+ NDR_RETURN(ndr_push_uint16(final_param,
+ NDR_SCALARS, call->heap->offset - result_data.length));
+ NDR_RETURN(ndr_push_bytes(final_param, result_param.data,
+ result_param.length));
+
+ NDR_RETURN(ndr_push_bytes(final_data, result_data.data,
+ result_data.length));
+
+ for (i=call->heap->num_strings-1; i>=0; i--)
+ NDR_RETURN(ndr_push_string(final_data, NDR_SCALARS,
+ call->heap->strings[i]));
+
+ trans->out.setup_count = 0;
+ trans->out.setup = NULL;
+ trans->out.params = ndr_push_blob(final_param);
+ trans->out.data = ndr_push_blob(final_data);
+
+ return result;
+}
diff --git a/source4/ntvfs/ipc/rap_server.c b/source4/ntvfs/ipc/rap_server.c
new file mode 100644
index 0000000..4c4beca
--- /dev/null
+++ b/source4/ntvfs/ipc/rap_server.c
@@ -0,0 +1,95 @@
+/*
+ Unix SMB/CIFS implementation.
+ RAP handlers
+
+ Copyright (C) Volker Lendecke 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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 "param/share.h"
+#include "../librpc/gen_ndr/rap.h"
+#include "libcli/raw/interfaces.h"
+#include "librpc/gen_ndr/srvsvc.h"
+#include "librpc/gen_ndr/dcerpc.h"
+#include "rpc_server/common/common.h"
+#include "rpc_server/common/share.h"
+#include "param/param.h"
+#include "ntvfs/ipc/proto.h"
+
+/* At this moment these are just dummy functions, but you might get the
+ * idea. */
+
+NTSTATUS rap_netshareenum(TALLOC_CTX *mem_ctx,
+ struct tevent_context *event_ctx,
+ struct loadparm_context *lp_ctx,
+ struct rap_NetShareEnum *r)
+{
+ NTSTATUS nterr;
+ const char **snames;
+ struct share_context *sctx;
+ struct share_config *scfg;
+ int i, j, count;
+
+ r->out.status = 0;
+ r->out.available = 0;
+ r->out.info = NULL;
+
+ nterr = share_get_context(mem_ctx, lp_ctx, &sctx);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ return nterr;
+ }
+
+ nterr = share_list_all(mem_ctx, sctx, &count, &snames);
+ if (!NT_STATUS_IS_OK(nterr)) {
+ return nterr;
+ }
+
+ r->out.available = count;
+ r->out.info = talloc_array(mem_ctx,
+ union rap_share_info, r->out.available);
+
+ for (i = 0, j = 0; i < r->out.available; i++) {
+ size_t sname_len;
+
+ if (!NT_STATUS_IS_OK(share_get_config(mem_ctx, sctx, snames[i], &scfg))) {
+ DEBUG(3, ("WARNING: Service [%s] disappeared after enumeration!\n", snames[i]));
+ continue;
+ }
+ /* Make sure we have NUL-termination */
+ sname_len = MIN(strlen(snames[i]),
+ sizeof(r->out.info[j].info1.share_name));
+ strlcpy((char *)r->out.info[j].info1.share_name,
+ snames[i],
+ sname_len);
+ r->out.info[i].info1.reserved1 = 0;
+ r->out.info[i].info1.share_type = dcesrv_common_get_share_type(mem_ctx, NULL, scfg);
+ r->out.info[i].info1.comment = share_string_option(mem_ctx, scfg, SHARE_COMMENT, "");
+ talloc_free(scfg);
+ j++;
+ }
+ r->out.available = j;
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS rap_netserverenum2(TALLOC_CTX *mem_ctx,
+ struct loadparm_context *lp_ctx,
+ struct rap_NetServerEnum2 *r)
+{
+ r->out.status = 0;
+ r->out.available = 0;
+ return NT_STATUS_OK;
+}
diff --git a/source4/ntvfs/ipc/vfs_ipc.c b/source4/ntvfs/ipc/vfs_ipc.c
new file mode 100644
index 0000000..01e2a5d
--- /dev/null
+++ b/source4/ntvfs/ipc/vfs_ipc.c
@@ -0,0 +1,1356 @@
+/*
+ Unix SMB/CIFS implementation.
+ default IPC$ NTVFS backend
+
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) Stefan (metze) Metzmacher 2004-2005
+
+ 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/>.
+*/
+/*
+ this implements the IPC$ backend, called by the NTVFS subsystem to
+ handle requests on IPC$ shares
+*/
+
+
+#include "includes.h"
+#include "../lib/util/dlinklist.h"
+#include "ntvfs/ntvfs.h"
+#include "../librpc/gen_ndr/rap.h"
+#include "ntvfs/ipc/proto.h"
+#include "../libcli/smb/smb_constants.h"
+#include "param/param.h"
+#include "../lib/tsocket/tsocket.h"
+#include "../libcli/named_pipe_auth/npa_tstream.h"
+#include "auth/auth.h"
+#include "auth/auth_sam_reply.h"
+#include "lib/socket/socket.h"
+#include "auth/credentials/credentials.h"
+#include "auth/credentials/credentials_krb5.h"
+#include "system/kerberos.h"
+#include "system/gssapi.h"
+#include "system/locale.h"
+#include "system/filesys.h"
+
+#undef strncasecmp
+
+/* this is the private structure used to keep the state of an open
+ ipc$ connection. It needs to keep information about all open
+ pipes */
+struct ipc_private {
+ struct ntvfs_module_context *ntvfs;
+
+ /* a list of open pipes */
+ struct pipe_state {
+ struct pipe_state *next, *prev;
+ struct ipc_private *ipriv;
+ const char *pipe_name;
+ struct ntvfs_handle *handle;
+ struct tstream_context *npipe;
+ uint16_t file_type;
+ uint16_t device_state;
+ uint64_t allocation_size;
+ struct tevent_queue *write_queue;
+ struct tevent_queue *read_queue;
+ } *pipe_list;
+};
+
+
+/*
+ find a open pipe give a file handle
+*/
+static struct pipe_state *pipe_state_find(struct ipc_private *ipriv, struct ntvfs_handle *handle)
+{
+ struct pipe_state *s;
+ void *p;
+
+ p = ntvfs_handle_get_backend_data(handle, ipriv->ntvfs);
+ if (!p) return NULL;
+
+ s = talloc_get_type(p, struct pipe_state);
+ if (!s) return NULL;
+
+ return s;
+}
+
+/*
+ find a open pipe give a wire fnum
+*/
+static struct pipe_state *pipe_state_find_key(struct ipc_private *ipriv, struct ntvfs_request *req, const DATA_BLOB *key)
+{
+ struct ntvfs_handle *h;
+
+ h = ntvfs_handle_search_by_wire_key(ipriv->ntvfs, req, key);
+ if (!h) return NULL;
+
+ return pipe_state_find(ipriv, h);
+}
+
+
+/*
+ connect to a share - always works
+*/
+static NTSTATUS ipc_connect(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_tcon* tcon)
+{
+ struct ipc_private *ipriv;
+ const char *sharename;
+
+ switch (tcon->generic.level) {
+ case RAW_TCON_TCON:
+ sharename = tcon->tcon.in.service;
+ break;
+ case RAW_TCON_TCONX:
+ sharename = tcon->tconx.in.path;
+ break;
+ case RAW_TCON_SMB2:
+ sharename = tcon->smb2.in.path;
+ break;
+ default:
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ if (strncmp(sharename, "\\\\", 2) == 0) {
+ char *p = strchr(sharename+2, '\\');
+ if (p) {
+ sharename = p + 1;
+ }
+ }
+
+ ntvfs->ctx->fs_type = talloc_strdup(ntvfs->ctx, "IPC");
+ NT_STATUS_HAVE_NO_MEMORY(ntvfs->ctx->fs_type);
+
+ ntvfs->ctx->dev_type = talloc_strdup(ntvfs->ctx, "IPC");
+ NT_STATUS_HAVE_NO_MEMORY(ntvfs->ctx->dev_type);
+
+ if (tcon->generic.level == RAW_TCON_TCONX) {
+ tcon->tconx.out.fs_type = ntvfs->ctx->fs_type;
+ tcon->tconx.out.dev_type = ntvfs->ctx->dev_type;
+ }
+
+ /* prepare the private state for this connection */
+ ipriv = talloc(ntvfs, struct ipc_private);
+ NT_STATUS_HAVE_NO_MEMORY(ipriv);
+
+ ntvfs->private_data = ipriv;
+
+ ipriv->ntvfs = ntvfs;
+ ipriv->pipe_list = NULL;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ disconnect from a share
+*/
+static NTSTATUS ipc_disconnect(struct ntvfs_module_context *ntvfs)
+{
+ return NT_STATUS_OK;
+}
+
+/*
+ delete a file
+*/
+static NTSTATUS ipc_unlink(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_unlink *unl)
+{
+ return NT_STATUS_ACCESS_DENIED;
+}
+
+/*
+ check if a directory exists
+*/
+static NTSTATUS ipc_chkpath(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_chkpath *cp)
+{
+ return NT_STATUS_ACCESS_DENIED;
+}
+
+/*
+ return info on a pathname
+*/
+static NTSTATUS ipc_qpathinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_fileinfo *info)
+{
+ switch (info->generic.level) {
+ case RAW_FILEINFO_GENERIC:
+ return NT_STATUS_INVALID_DEVICE_REQUEST;
+ case RAW_FILEINFO_GETATTR:
+ return NT_STATUS_ACCESS_DENIED;
+ default:
+ return ntvfs_map_qpathinfo(ntvfs, req, info);
+ }
+}
+
+/*
+ set info on a pathname
+*/
+static NTSTATUS ipc_setpathinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_setfileinfo *st)
+{
+ return NT_STATUS_ACCESS_DENIED;
+}
+
+
+/*
+ destroy a open pipe structure
+*/
+static int ipc_fd_destructor(struct pipe_state *p)
+{
+ DLIST_REMOVE(p->ipriv->pipe_list, p);
+ ntvfs_handle_remove_backend_data(p->handle, p->ipriv->ntvfs);
+ return 0;
+}
+
+struct ipc_open_state {
+ struct ipc_private *ipriv;
+ struct pipe_state *p;
+ struct ntvfs_request *req;
+ union smb_open *oi;
+ struct auth_session_info_transport *session_info_transport;
+};
+
+static void ipc_open_done(struct tevent_req *subreq);
+
+/*
+ check the pipename is valid
+ */
+static NTSTATUS validate_pipename(const char *name)
+{
+ while (*name) {
+ if (!isalnum(*name) && *name != '_') {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ name++;
+ }
+ return NT_STATUS_OK;
+}
+
+/*
+ open a file - used for MSRPC pipes
+*/
+static NTSTATUS ipc_open(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_open *oi)
+{
+ NTSTATUS status;
+ struct pipe_state *p;
+ struct ipc_private *ipriv = talloc_get_type_abort(ntvfs->private_data,
+ struct ipc_private);
+ struct ntvfs_handle *h;
+ struct ipc_open_state *state;
+ struct tevent_req *subreq;
+ const char *fname;
+ const char *directory;
+ const struct tsocket_address *remote_client_addr;
+ const struct tsocket_address *local_server_addr;
+
+ switch (oi->generic.level) {
+ case RAW_OPEN_NTCREATEX:
+ case RAW_OPEN_NTTRANS_CREATE:
+ fname = oi->ntcreatex.in.fname;
+ while (fname[0] == '\\') fname++;
+ break;
+ case RAW_OPEN_OPENX:
+ fname = oi->openx.in.fname;
+ while (fname[0] == '\\') fname++;
+ if (strncasecmp(fname, "PIPE\\", 5) != 0) {
+ return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
+ }
+ while (fname[0] == '\\') fname++;
+ break;
+ case RAW_OPEN_SMB2:
+ fname = oi->smb2.in.fname;
+ break;
+ default:
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ directory = talloc_asprintf(req, "%s/np",
+ lpcfg_ncalrpc_dir(ipriv->ntvfs->ctx->lp_ctx));
+ NT_STATUS_HAVE_NO_MEMORY(directory);
+
+ state = talloc(req, struct ipc_open_state);
+ NT_STATUS_HAVE_NO_MEMORY(state);
+
+ status = ntvfs_handle_new(ntvfs, req, &h);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ p = talloc(h, struct pipe_state);
+ NT_STATUS_HAVE_NO_MEMORY(p);
+
+ /* check for valid characters in name */
+ fname = strlower_talloc(p, fname);
+
+ status = validate_pipename(fname);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ p->pipe_name = talloc_asprintf(p, "\\pipe\\%s", fname);
+ NT_STATUS_HAVE_NO_MEMORY(p->pipe_name);
+
+ p->handle = h;
+ p->ipriv = ipriv;
+
+ p->write_queue = tevent_queue_create(p, "ipc_write_queue");
+ NT_STATUS_HAVE_NO_MEMORY(p->write_queue);
+
+ p->read_queue = tevent_queue_create(p, "ipc_read_queue");
+ NT_STATUS_HAVE_NO_MEMORY(p->read_queue);
+
+ state->ipriv = ipriv;
+ state->p = p;
+ state->req = req;
+ state->oi = oi;
+
+ status = auth_session_info_transport_from_session(state,
+ req->session_info,
+ ipriv->ntvfs->ctx->event_ctx,
+ ipriv->ntvfs->ctx->lp_ctx,
+ &state->session_info_transport);
+
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ local_server_addr = ntvfs_get_local_address(ipriv->ntvfs);
+ remote_client_addr = ntvfs_get_remote_address(ipriv->ntvfs);
+
+ subreq = tstream_npa_connect_send(p,
+ ipriv->ntvfs->ctx->event_ctx,
+ directory,
+ fname,
+ NCACN_NP,
+ remote_client_addr,
+ NULL,
+ local_server_addr,
+ NULL,
+ state->session_info_transport);
+ NT_STATUS_HAVE_NO_MEMORY(subreq);
+ tevent_req_set_callback(subreq, ipc_open_done, state);
+
+ req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
+ return NT_STATUS_OK;
+}
+
+static void ipc_open_done(struct tevent_req *subreq)
+{
+ struct ipc_open_state *state = tevent_req_callback_data(subreq,
+ struct ipc_open_state);
+ struct ipc_private *ipriv = state->ipriv;
+ struct pipe_state *p = state->p;
+ struct ntvfs_request *req = state->req;
+ union smb_open *oi = state->oi;
+ int ret;
+ int sys_errno;
+ NTSTATUS status;
+
+ ret = tstream_npa_connect_recv(subreq, &sys_errno,
+ p, &p->npipe,
+ &p->file_type,
+ &p->device_state,
+ &p->allocation_size);
+ TALLOC_FREE(subreq);
+ if (ret == -1) {
+ status = map_nt_error_from_unix_common(sys_errno);
+ goto reply;
+ }
+
+ DLIST_ADD(ipriv->pipe_list, p);
+ talloc_set_destructor(p, ipc_fd_destructor);
+
+ status = ntvfs_handle_set_backend_data(p->handle, ipriv->ntvfs, p);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto reply;
+ }
+
+ switch (oi->generic.level) {
+ case RAW_OPEN_NTCREATEX:
+ ZERO_STRUCT(oi->ntcreatex.out);
+ oi->ntcreatex.out.file.ntvfs = p->handle;
+ oi->ntcreatex.out.oplock_level = 0;
+ oi->ntcreatex.out.create_action = NTCREATEX_ACTION_EXISTED;
+ oi->ntcreatex.out.create_time = 0;
+ oi->ntcreatex.out.access_time = 0;
+ oi->ntcreatex.out.write_time = 0;
+ oi->ntcreatex.out.change_time = 0;
+ oi->ntcreatex.out.attrib = FILE_ATTRIBUTE_NORMAL;
+ oi->ntcreatex.out.alloc_size = p->allocation_size;
+ oi->ntcreatex.out.size = 0;
+ oi->ntcreatex.out.file_type = p->file_type;
+ oi->ntcreatex.out.ipc_state = p->device_state;
+ oi->ntcreatex.out.is_directory = 0;
+ break;
+ case RAW_OPEN_OPENX:
+ ZERO_STRUCT(oi->openx.out);
+ oi->openx.out.file.ntvfs = p->handle;
+ oi->openx.out.attrib = FILE_ATTRIBUTE_NORMAL;
+ oi->openx.out.write_time = 0;
+ oi->openx.out.size = 0;
+ oi->openx.out.access = 0;
+ oi->openx.out.ftype = p->file_type;
+ oi->openx.out.devstate = p->device_state;
+ oi->openx.out.action = 0;
+ oi->openx.out.unique_fid = 0;
+ oi->openx.out.access_mask = 0;
+ oi->openx.out.unknown = 0;
+ break;
+ case RAW_OPEN_SMB2:
+ ZERO_STRUCT(oi->smb2.out);
+ oi->smb2.out.file.ntvfs = p->handle;
+ oi->smb2.out.oplock_level = oi->smb2.in.oplock_level;
+ oi->smb2.out.create_action = NTCREATEX_ACTION_EXISTED;
+ oi->smb2.out.create_time = 0;
+ oi->smb2.out.access_time = 0;
+ oi->smb2.out.write_time = 0;
+ oi->smb2.out.change_time = 0;
+ oi->smb2.out.alloc_size = p->allocation_size;
+ oi->smb2.out.size = 0;
+ oi->smb2.out.file_attr = FILE_ATTRIBUTE_NORMAL;
+ oi->smb2.out.reserved2 = 0;
+ break;
+ default:
+ break;
+ }
+
+reply:
+ req->async_states->status = status;
+ req->async_states->send_fn(req);
+}
+
+/*
+ create a directory
+*/
+static NTSTATUS ipc_mkdir(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_mkdir *md)
+{
+ return NT_STATUS_ACCESS_DENIED;
+}
+
+/*
+ remove a directory
+*/
+static NTSTATUS ipc_rmdir(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, struct smb_rmdir *rd)
+{
+ return NT_STATUS_ACCESS_DENIED;
+}
+
+/*
+ rename a set of files
+*/
+static NTSTATUS ipc_rename(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_rename *ren)
+{
+ return NT_STATUS_ACCESS_DENIED;
+}
+
+/*
+ copy a set of files
+*/
+static NTSTATUS ipc_copy(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, struct smb_copy *cp)
+{
+ return NT_STATUS_ACCESS_DENIED;
+}
+
+struct ipc_readv_next_vector_state {
+ uint8_t *buf;
+ size_t len;
+ off_t ofs;
+ size_t remaining;
+};
+
+static void ipc_readv_next_vector_init(struct ipc_readv_next_vector_state *s,
+ uint8_t *buf, size_t len)
+{
+ ZERO_STRUCTP(s);
+
+ s->buf = buf;
+ s->len = MIN(len, UINT16_MAX);
+}
+
+static int ipc_readv_next_vector(struct tstream_context *stream,
+ void *private_data,
+ TALLOC_CTX *mem_ctx,
+ struct iovec **_vector,
+ size_t *count)
+{
+ struct ipc_readv_next_vector_state *state =
+ (struct ipc_readv_next_vector_state *)private_data;
+ struct iovec *vector;
+ ssize_t pending;
+ size_t wanted;
+
+ if (state->ofs == state->len) {
+ *_vector = NULL;
+ *count = 0;
+ return 0;
+ }
+
+ pending = tstream_pending_bytes(stream);
+ if (pending == -1) {
+ return -1;
+ }
+
+ if (pending == 0 && state->ofs != 0) {
+ /* return a short read */
+ *_vector = NULL;
+ *count = 0;
+ return 0;
+ }
+
+ if (pending == 0) {
+ /* we want at least one byte and recheck again */
+ wanted = 1;
+ } else {
+ size_t missing = state->len - state->ofs;
+ if (pending > missing) {
+ /* there's more available */
+ state->remaining = pending - missing;
+ wanted = missing;
+ } else {
+ /* read what we can get and recheck in the next cycle */
+ wanted = pending;
+ }
+ }
+
+ vector = talloc_array(mem_ctx, struct iovec, 1);
+ if (!vector) {
+ return -1;
+ }
+
+ vector[0].iov_base = (char *) (state->buf + state->ofs);
+ vector[0].iov_len = wanted;
+
+ state->ofs += wanted;
+
+ *_vector = vector;
+ *count = 1;
+ return 0;
+}
+
+struct ipc_read_state {
+ struct ipc_private *ipriv;
+ struct pipe_state *p;
+ struct ntvfs_request *req;
+ union smb_read *rd;
+ struct ipc_readv_next_vector_state next_vector;
+};
+
+static void ipc_read_done(struct tevent_req *subreq);
+
+/*
+ read from a file
+*/
+static NTSTATUS ipc_read(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_read *rd)
+{
+ struct ipc_private *ipriv = talloc_get_type_abort(ntvfs->private_data,
+ struct ipc_private);
+ struct pipe_state *p;
+ struct ipc_read_state *state;
+ struct tevent_req *subreq;
+
+ if (rd->generic.level != RAW_READ_GENERIC) {
+ return ntvfs_map_read(ntvfs, req, rd);
+ }
+
+ p = pipe_state_find(ipriv, rd->readx.in.file.ntvfs);
+ if (!p) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ state = talloc(req, struct ipc_read_state);
+ NT_STATUS_HAVE_NO_MEMORY(state);
+
+ state->ipriv = ipriv;
+ state->p = p;
+ state->req = req;
+ state->rd = rd;
+
+ /* rd->readx.out.data is already allocated */
+ ipc_readv_next_vector_init(&state->next_vector,
+ rd->readx.out.data,
+ rd->readx.in.maxcnt);
+
+ subreq = tstream_readv_pdu_queue_send(req,
+ ipriv->ntvfs->ctx->event_ctx,
+ p->npipe,
+ p->read_queue,
+ ipc_readv_next_vector,
+ &state->next_vector);
+ NT_STATUS_HAVE_NO_MEMORY(subreq);
+ tevent_req_set_callback(subreq, ipc_read_done, state);
+
+ req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
+ return NT_STATUS_OK;
+}
+
+static void ipc_read_done(struct tevent_req *subreq)
+{
+ struct ipc_read_state *state =
+ tevent_req_callback_data(subreq,
+ struct ipc_read_state);
+ struct ntvfs_request *req = state->req;
+ union smb_read *rd = state->rd;
+ int ret;
+ int sys_errno;
+ NTSTATUS status;
+
+ ret = tstream_readv_pdu_queue_recv(subreq, &sys_errno);
+ TALLOC_FREE(subreq);
+ if (ret == -1) {
+ status = map_nt_error_from_unix_common(sys_errno);
+ goto reply;
+ }
+
+ status = NT_STATUS_OK;
+ if (state->next_vector.remaining > 0) {
+ status = STATUS_BUFFER_OVERFLOW;
+ }
+
+ rd->readx.out.remaining = state->next_vector.remaining;
+ rd->readx.out.compaction_mode = 0;
+ rd->readx.out.nread = ret;
+
+reply:
+ req->async_states->status = status;
+ req->async_states->send_fn(req);
+}
+
+struct ipc_write_state {
+ struct ipc_private *ipriv;
+ struct pipe_state *p;
+ struct ntvfs_request *req;
+ union smb_write *wr;
+ struct iovec iov;
+};
+
+static void ipc_write_done(struct tevent_req *subreq);
+
+/*
+ write to a file
+*/
+static NTSTATUS ipc_write(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_write *wr)
+{
+ struct ipc_private *ipriv = talloc_get_type_abort(ntvfs->private_data,
+ struct ipc_private);
+ struct pipe_state *p;
+ struct tevent_req *subreq;
+ struct ipc_write_state *state;
+
+ if (wr->generic.level != RAW_WRITE_GENERIC) {
+ return ntvfs_map_write(ntvfs, req, wr);
+ }
+
+ p = pipe_state_find(ipriv, wr->writex.in.file.ntvfs);
+ if (!p) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ state = talloc(req, struct ipc_write_state);
+ NT_STATUS_HAVE_NO_MEMORY(state);
+
+ state->ipriv = ipriv;
+ state->p = p;
+ state->req = req;
+ state->wr = wr;
+ state->iov.iov_base = discard_const_p(void, wr->writex.in.data);
+ state->iov.iov_len = wr->writex.in.count;
+
+ subreq = tstream_writev_queue_send(state,
+ ipriv->ntvfs->ctx->event_ctx,
+ p->npipe,
+ p->write_queue,
+ &state->iov, 1);
+ NT_STATUS_HAVE_NO_MEMORY(subreq);
+ tevent_req_set_callback(subreq, ipc_write_done, state);
+
+ req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
+ return NT_STATUS_OK;
+}
+
+static void ipc_write_done(struct tevent_req *subreq)
+{
+ struct ipc_write_state *state =
+ tevent_req_callback_data(subreq,
+ struct ipc_write_state);
+ struct ntvfs_request *req = state->req;
+ union smb_write *wr = state->wr;
+ int ret;
+ int sys_errno;
+ NTSTATUS status;
+
+ ret = tstream_writev_queue_recv(subreq, &sys_errno);
+ TALLOC_FREE(subreq);
+ if (ret == -1) {
+ status = map_nt_error_from_unix_common(sys_errno);
+ goto reply;
+ }
+
+ status = NT_STATUS_OK;
+
+ wr->writex.out.nwritten = ret;
+ wr->writex.out.remaining = 0;
+
+reply:
+ req->async_states->status = status;
+ req->async_states->send_fn(req);
+}
+
+/*
+ seek in a file
+*/
+static NTSTATUS ipc_seek(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_seek *io)
+{
+ return NT_STATUS_ACCESS_DENIED;
+}
+
+/*
+ flush a file
+*/
+static NTSTATUS ipc_flush(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_flush *io)
+{
+ return NT_STATUS_ACCESS_DENIED;
+}
+
+/*
+ close a file
+*/
+static NTSTATUS ipc_close(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_close *io)
+{
+ struct ipc_private *ipriv = talloc_get_type_abort(ntvfs->private_data,
+ struct ipc_private);
+ struct pipe_state *p;
+
+ if (io->generic.level != RAW_CLOSE_GENERIC) {
+ return ntvfs_map_close(ntvfs, req, io);
+ }
+
+ ZERO_STRUCT(io->generic.out);
+
+ p = pipe_state_find(ipriv, io->generic.in.file.ntvfs);
+ if (!p) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ talloc_free(p);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ exit - closing files
+*/
+static NTSTATUS ipc_exit(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req)
+{
+ struct ipc_private *ipriv = talloc_get_type_abort(ntvfs->private_data,
+ struct ipc_private);
+ struct pipe_state *p, *next;
+
+ for (p=ipriv->pipe_list; p; p=next) {
+ next = p->next;
+ if (p->handle->session_info == req->session_info &&
+ p->handle->smbpid == req->smbpid) {
+ talloc_free(p);
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ logoff - closing files open by the user
+*/
+static NTSTATUS ipc_logoff(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req)
+{
+ struct ipc_private *ipriv = talloc_get_type_abort(ntvfs->private_data,
+ struct ipc_private);
+ struct pipe_state *p, *next;
+
+ for (p=ipriv->pipe_list; p; p=next) {
+ next = p->next;
+ if (p->handle->session_info == req->session_info) {
+ talloc_free(p);
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ setup for an async call
+*/
+static NTSTATUS ipc_async_setup(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ void *private_data)
+{
+ return NT_STATUS_OK;
+}
+
+/*
+ cancel an async call
+*/
+static NTSTATUS ipc_cancel(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req)
+{
+ return NT_STATUS_UNSUCCESSFUL;
+}
+
+/*
+ lock a byte range
+*/
+static NTSTATUS ipc_lock(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_lock *lck)
+{
+ return NT_STATUS_ACCESS_DENIED;
+}
+
+/*
+ set info on a open file
+*/
+static NTSTATUS ipc_setfileinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_setfileinfo *info)
+{
+ return NT_STATUS_ACCESS_DENIED;
+}
+
+/*
+ query info on a open file
+*/
+static NTSTATUS ipc_qfileinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_fileinfo *info)
+{
+ struct ipc_private *ipriv = talloc_get_type_abort(ntvfs->private_data,
+ struct ipc_private);
+ struct pipe_state *p = pipe_state_find(ipriv, info->generic.in.file.ntvfs);
+ if (!p) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+ switch (info->generic.level) {
+ case RAW_FILEINFO_GENERIC:
+ {
+ ZERO_STRUCT(info->generic.out);
+ info->generic.out.attrib = FILE_ATTRIBUTE_NORMAL;
+ info->generic.out.fname.s = strrchr(p->pipe_name, '\\');
+ info->generic.out.alloc_size = 4096;
+ info->generic.out.nlink = 1;
+ /* What the heck? Match Win2k3: IPC$ pipes are delete pending */
+ info->generic.out.delete_pending = 1;
+ return NT_STATUS_OK;
+ }
+ case RAW_FILEINFO_ALT_NAME_INFO:
+ case RAW_FILEINFO_ALT_NAME_INFORMATION:
+ case RAW_FILEINFO_STREAM_INFO:
+ case RAW_FILEINFO_STREAM_INFORMATION:
+ case RAW_FILEINFO_COMPRESSION_INFO:
+ case RAW_FILEINFO_COMPRESSION_INFORMATION:
+ case RAW_FILEINFO_NETWORK_OPEN_INFORMATION:
+ case RAW_FILEINFO_ATTRIBUTE_TAG_INFORMATION:
+ return NT_STATUS_INVALID_PARAMETER;
+ case RAW_FILEINFO_ALL_EAS:
+ return NT_STATUS_ACCESS_DENIED;
+ default:
+ return ntvfs_map_qfileinfo(ntvfs, req, info);
+ }
+}
+
+
+/*
+ return filesystem info
+*/
+static NTSTATUS ipc_fsinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_fsinfo *fs)
+{
+ return NT_STATUS_ACCESS_DENIED;
+}
+
+/*
+ return print queue info
+*/
+static NTSTATUS ipc_lpq(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_lpq *lpq)
+{
+ return NT_STATUS_ACCESS_DENIED;
+}
+
+/*
+ list files in a directory matching a wildcard pattern
+*/
+static NTSTATUS ipc_search_first(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_search_first *io,
+ void *search_private,
+ bool (*callback)(void *, const union smb_search_data *))
+{
+ return NT_STATUS_ACCESS_DENIED;
+}
+
+/*
+ continue listing files in a directory
+*/
+static NTSTATUS ipc_search_next(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_search_next *io,
+ void *search_private,
+ bool (*callback)(void *, const union smb_search_data *))
+{
+ return NT_STATUS_ACCESS_DENIED;
+}
+
+/*
+ end listing files in a directory
+*/
+static NTSTATUS ipc_search_close(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_search_close *io)
+{
+ return NT_STATUS_ACCESS_DENIED;
+}
+
+struct ipc_trans_state {
+ struct ipc_private *ipriv;
+ struct pipe_state *p;
+ struct ntvfs_request *req;
+ struct smb_trans2 *trans;
+ struct iovec writev_iov;
+ struct ipc_readv_next_vector_state next_vector;
+};
+
+static void ipc_trans_writev_done(struct tevent_req *subreq);
+static void ipc_trans_readv_done(struct tevent_req *subreq);
+
+/* SMBtrans - handle a DCERPC command */
+static NTSTATUS ipc_dcerpc_cmd(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, struct smb_trans2 *trans)
+{
+ struct ipc_private *ipriv = talloc_get_type_abort(ntvfs->private_data,
+ struct ipc_private);
+ struct pipe_state *p;
+ DATA_BLOB fnum_key;
+ uint16_t fnum;
+ struct ipc_trans_state *state;
+ struct tevent_req *subreq;
+
+ /*
+ * the fnum is in setup[1], a 16 bit value
+ * the setup[*] values are already in host byteorder
+ * but ntvfs_handle_search_by_wire_key() expects
+ * network byteorder
+ */
+ SSVAL(&fnum, 0, trans->in.setup[1]);
+ fnum_key = data_blob_const(&fnum, 2);
+
+ p = pipe_state_find_key(ipriv, req, &fnum_key);
+ if (!p) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ /*
+ * Trans requests are only allowed
+ * if no other Trans or Read is active
+ */
+ if (tevent_queue_length(p->read_queue) > 0) {
+ return NT_STATUS_PIPE_BUSY;
+ }
+
+ state = talloc(req, struct ipc_trans_state);
+ NT_STATUS_HAVE_NO_MEMORY(state);
+
+ trans->out.setup_count = 0;
+ trans->out.setup = NULL;
+ trans->out.params = data_blob(NULL, 0);
+ trans->out.data = data_blob_talloc(req, NULL, trans->in.max_data);
+ NT_STATUS_HAVE_NO_MEMORY(trans->out.data.data);
+
+ state->ipriv = ipriv;
+ state->p = p;
+ state->req = req;
+ state->trans = trans;
+ state->writev_iov.iov_base = (char *) trans->in.data.data;
+ state->writev_iov.iov_len = trans->in.data.length;
+
+ ipc_readv_next_vector_init(&state->next_vector,
+ trans->out.data.data,
+ trans->out.data.length);
+
+ subreq = tstream_writev_queue_send(state,
+ ipriv->ntvfs->ctx->event_ctx,
+ p->npipe,
+ p->write_queue,
+ &state->writev_iov, 1);
+ NT_STATUS_HAVE_NO_MEMORY(subreq);
+ tevent_req_set_callback(subreq, ipc_trans_writev_done, state);
+
+ req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
+ return NT_STATUS_OK;
+}
+
+static void ipc_trans_writev_done(struct tevent_req *subreq)
+{
+ struct ipc_trans_state *state =
+ tevent_req_callback_data(subreq,
+ struct ipc_trans_state);
+ struct ipc_private *ipriv = state->ipriv;
+ struct pipe_state *p = state->p;
+ struct ntvfs_request *req = state->req;
+ int ret;
+ int sys_errno;
+ NTSTATUS status;
+
+ ret = tstream_writev_queue_recv(subreq, &sys_errno);
+ TALLOC_FREE(subreq);
+ if (ret == 0) {
+ status = NT_STATUS_PIPE_DISCONNECTED;
+ goto reply;
+ } else if (ret == -1) {
+ status = map_nt_error_from_unix_common(sys_errno);
+ goto reply;
+ }
+
+ subreq = tstream_readv_pdu_queue_send(state,
+ ipriv->ntvfs->ctx->event_ctx,
+ p->npipe,
+ p->read_queue,
+ ipc_readv_next_vector,
+ &state->next_vector);
+ if (!subreq) {
+ status = NT_STATUS_NO_MEMORY;
+ goto reply;
+ }
+ tevent_req_set_callback(subreq, ipc_trans_readv_done, state);
+ return;
+
+reply:
+ req->async_states->status = status;
+ req->async_states->send_fn(req);
+}
+
+static void ipc_trans_readv_done(struct tevent_req *subreq)
+{
+ struct ipc_trans_state *state =
+ tevent_req_callback_data(subreq,
+ struct ipc_trans_state);
+ struct ntvfs_request *req = state->req;
+ struct smb_trans2 *trans = state->trans;
+ int ret;
+ int sys_errno;
+ NTSTATUS status;
+
+ ret = tstream_readv_pdu_queue_recv(subreq, &sys_errno);
+ TALLOC_FREE(subreq);
+ if (ret == -1) {
+ status = map_nt_error_from_unix_common(sys_errno);
+ goto reply;
+ }
+
+ status = NT_STATUS_OK;
+ if (state->next_vector.remaining > 0) {
+ status = STATUS_BUFFER_OVERFLOW;
+ }
+
+ trans->out.data.length = ret;
+
+reply:
+ req->async_states->status = status;
+ req->async_states->send_fn(req);
+}
+
+/* SMBtrans - set named pipe state */
+static NTSTATUS ipc_set_nm_pipe_state(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, struct smb_trans2 *trans)
+{
+ struct ipc_private *ipriv = talloc_get_type_abort(ntvfs->private_data,
+ struct ipc_private);
+ struct pipe_state *p;
+ DATA_BLOB fnum_key;
+
+ /* the fnum is in setup[1] */
+ fnum_key = data_blob_const(&trans->in.setup[1], sizeof(trans->in.setup[1]));
+
+ p = pipe_state_find_key(ipriv, req, &fnum_key);
+ if (!p) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ if (trans->in.params.length != 2) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /*
+ * TODO: pass this to the tstream_npa logic
+ */
+ p->device_state = SVAL(trans->in.params.data, 0);
+
+ trans->out.setup_count = 0;
+ trans->out.setup = NULL;
+ trans->out.params = data_blob(NULL, 0);
+ trans->out.data = data_blob(NULL, 0);
+
+ return NT_STATUS_OK;
+}
+
+
+/* SMBtrans - used to provide access to SMB pipes */
+static NTSTATUS ipc_trans(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, struct smb_trans2 *trans)
+{
+ NTSTATUS status;
+
+ if (strequal(trans->in.trans_name, "\\PIPE\\LANMAN"))
+ return ipc_rap_call(req, ntvfs->ctx->event_ctx, ntvfs->ctx->lp_ctx, trans);
+
+ if (trans->in.setup_count != 2) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ switch (trans->in.setup[0]) {
+ case TRANSACT_SETNAMEDPIPEHANDLESTATE:
+ status = ipc_set_nm_pipe_state(ntvfs, req, trans);
+ break;
+ case TRANSACT_DCERPCCMD:
+ status = ipc_dcerpc_cmd(ntvfs, req, trans);
+ break;
+ default:
+ status = NT_STATUS_INVALID_PARAMETER;
+ break;
+ }
+
+ return status;
+}
+
+struct ipc_ioctl_state {
+ struct ipc_private *ipriv;
+ struct pipe_state *p;
+ struct ntvfs_request *req;
+ union smb_ioctl *io;
+ struct iovec writev_iov;
+ struct ipc_readv_next_vector_state next_vector;
+};
+
+static void ipc_ioctl_writev_done(struct tevent_req *subreq);
+static void ipc_ioctl_readv_done(struct tevent_req *subreq);
+
+static NTSTATUS ipc_ioctl_smb2(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_ioctl *io)
+{
+ struct ipc_private *ipriv = talloc_get_type_abort(ntvfs->private_data,
+ struct ipc_private);
+ struct pipe_state *p;
+ struct ipc_ioctl_state *state;
+ struct tevent_req *subreq;
+
+ switch (io->smb2.in.function) {
+ case FSCTL_NAMED_PIPE_READ_WRITE:
+ break;
+
+ default:
+ return NT_STATUS_FS_DRIVER_REQUIRED;
+ }
+
+ p = pipe_state_find(ipriv, io->smb2.in.file.ntvfs);
+ if (!p) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ /*
+ * Trans requests are only allowed
+ * if no other Trans or Read is active
+ */
+ if (tevent_queue_length(p->read_queue) > 0) {
+ return NT_STATUS_PIPE_BUSY;
+ }
+
+ state = talloc(req, struct ipc_ioctl_state);
+ NT_STATUS_HAVE_NO_MEMORY(state);
+
+ io->smb2.out.reserved = 0;
+ io->smb2.out.function = io->smb2.in.function;
+ io->smb2.out.flags = 0;
+ io->smb2.out.reserved2 = 0;
+ io->smb2.out.in = data_blob_null;
+ io->smb2.out.out = data_blob_talloc(req, NULL, io->smb2.in.max_output_response);
+ NT_STATUS_HAVE_NO_MEMORY(io->smb2.out.out.data);
+
+ state->ipriv = ipriv;
+ state->p = p;
+ state->req = req;
+ state->io = io;
+ state->writev_iov.iov_base = (char *) io->smb2.in.out.data;
+ state->writev_iov.iov_len = io->smb2.in.out.length;
+
+ ipc_readv_next_vector_init(&state->next_vector,
+ io->smb2.out.out.data,
+ io->smb2.out.out.length);
+
+ subreq = tstream_writev_queue_send(state,
+ ipriv->ntvfs->ctx->event_ctx,
+ p->npipe,
+ p->write_queue,
+ &state->writev_iov, 1);
+ NT_STATUS_HAVE_NO_MEMORY(subreq);
+ tevent_req_set_callback(subreq, ipc_ioctl_writev_done, state);
+
+ req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
+ return NT_STATUS_OK;
+}
+
+static void ipc_ioctl_writev_done(struct tevent_req *subreq)
+{
+ struct ipc_ioctl_state *state =
+ tevent_req_callback_data(subreq,
+ struct ipc_ioctl_state);
+ struct ipc_private *ipriv = state->ipriv;
+ struct pipe_state *p = state->p;
+ struct ntvfs_request *req = state->req;
+ int ret;
+ int sys_errno;
+ NTSTATUS status;
+
+ ret = tstream_writev_queue_recv(subreq, &sys_errno);
+ TALLOC_FREE(subreq);
+ if (ret == -1) {
+ status = map_nt_error_from_unix_common(sys_errno);
+ goto reply;
+ }
+
+ subreq = tstream_readv_pdu_queue_send(state,
+ ipriv->ntvfs->ctx->event_ctx,
+ p->npipe,
+ p->read_queue,
+ ipc_readv_next_vector,
+ &state->next_vector);
+ if (!subreq) {
+ status = NT_STATUS_NO_MEMORY;
+ goto reply;
+ }
+ tevent_req_set_callback(subreq, ipc_ioctl_readv_done, state);
+ return;
+
+reply:
+ req->async_states->status = status;
+ req->async_states->send_fn(req);
+}
+
+static void ipc_ioctl_readv_done(struct tevent_req *subreq)
+{
+ struct ipc_ioctl_state *state =
+ tevent_req_callback_data(subreq,
+ struct ipc_ioctl_state);
+ struct ntvfs_request *req = state->req;
+ union smb_ioctl *io = state->io;
+ int ret;
+ int sys_errno;
+ NTSTATUS status;
+
+ ret = tstream_readv_pdu_queue_recv(subreq, &sys_errno);
+ TALLOC_FREE(subreq);
+ if (ret == -1) {
+ status = map_nt_error_from_unix_common(sys_errno);
+ goto reply;
+ }
+
+ status = NT_STATUS_OK;
+ if (state->next_vector.remaining > 0) {
+ status = STATUS_BUFFER_OVERFLOW;
+ }
+
+ io->smb2.out.out.length = ret;
+
+reply:
+ req->async_states->status = status;
+ req->async_states->send_fn(req);
+}
+
+/*
+ ioctl interface
+*/
+static NTSTATUS ipc_ioctl(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_ioctl *io)
+{
+ switch (io->generic.level) {
+ case RAW_IOCTL_SMB2:
+ return ipc_ioctl_smb2(ntvfs, req, io);
+
+ case RAW_IOCTL_SMB2_NO_HANDLE:
+ return NT_STATUS_FS_DRIVER_REQUIRED;
+
+ default:
+ return NT_STATUS_ACCESS_DENIED;
+ }
+}
+
+
+/*
+ initialise the IPC backend, registering ourselves with the ntvfs subsystem
+ */
+NTSTATUS ntvfs_ipc_init(TALLOC_CTX *ctx)
+{
+ NTSTATUS ret;
+ struct ntvfs_ops ops;
+ NTVFS_CURRENT_CRITICAL_SIZES(vers);
+
+ ZERO_STRUCT(ops);
+
+ /* fill in the name and type */
+ ops.name = "default";
+ ops.type = NTVFS_IPC;
+
+ /* fill in all the operations */
+ ops.connect_fn = ipc_connect;
+ ops.disconnect_fn = ipc_disconnect;
+ ops.unlink_fn = ipc_unlink;
+ ops.chkpath_fn = ipc_chkpath;
+ ops.qpathinfo_fn = ipc_qpathinfo;
+ ops.setpathinfo_fn = ipc_setpathinfo;
+ ops.open_fn = ipc_open;
+ ops.mkdir_fn = ipc_mkdir;
+ ops.rmdir_fn = ipc_rmdir;
+ ops.rename_fn = ipc_rename;
+ ops.copy_fn = ipc_copy;
+ ops.ioctl_fn = ipc_ioctl;
+ ops.read_fn = ipc_read;
+ ops.write_fn = ipc_write;
+ ops.seek_fn = ipc_seek;
+ ops.flush_fn = ipc_flush;
+ ops.close_fn = ipc_close;
+ ops.exit_fn = ipc_exit;
+ ops.lock_fn = ipc_lock;
+ ops.setfileinfo_fn = ipc_setfileinfo;
+ ops.qfileinfo_fn = ipc_qfileinfo;
+ ops.fsinfo_fn = ipc_fsinfo;
+ ops.lpq_fn = ipc_lpq;
+ ops.search_first_fn = ipc_search_first;
+ ops.search_next_fn = ipc_search_next;
+ ops.search_close_fn = ipc_search_close;
+ ops.trans_fn = ipc_trans;
+ ops.logoff_fn = ipc_logoff;
+ ops.async_setup_fn = ipc_async_setup;
+ ops.cancel_fn = ipc_cancel;
+
+ /* register ourselves with the NTVFS subsystem. */
+ ret = ntvfs_register(&ops, &vers);
+
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(0,("Failed to register IPC backend!\n"));
+ return ret;
+ }
+
+ return ret;
+}
diff --git a/source4/ntvfs/ntvfs.h b/source4/ntvfs/ntvfs.h
new file mode 100644
index 0000000..b459579
--- /dev/null
+++ b/source4/ntvfs/ntvfs.h
@@ -0,0 +1,338 @@
+/*
+ Unix SMB/CIFS implementation.
+ NTVFS structures and defines
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) Stefan Metzmacher 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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/>.
+*/
+
+#ifndef _NTVFS_H_
+#define _NTVFS_H_
+
+#include "libcli/raw/interfaces.h"
+#include "param/share.h"
+#include "librpc/gen_ndr/security.h"
+#include "librpc/gen_ndr/server_id.h"
+
+/* modules can use the following to determine if the interface has changed */
+/* version 1 -> 0 - make module stacking easier -- metze */
+#define NTVFS_INTERFACE_VERSION 0
+
+struct ntvfs_module_context;
+struct ntvfs_request;
+
+/* each backend has to be one one of the following 3 basic types. In
+ earlier versions of Samba backends needed to handle all types, now
+ we implement them separately.
+ The values 1..3 match the SMB2 SMB2_SHARE_TYPE_* values
+ */
+enum ntvfs_type {NTVFS_DISK=1, NTVFS_IPC=2, NTVFS_PRINT=3};
+
+/* the ntvfs operations structure - contains function pointers to
+ the backend implementations of each operation */
+struct ntvfs_ops {
+ const char *name;
+ enum ntvfs_type type;
+
+ /* initial setup */
+ NTSTATUS (*connect_fn)(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_tcon *tcon);
+ NTSTATUS (*disconnect_fn)(struct ntvfs_module_context *ntvfs);
+
+ /* async_setup - called when a backend is processing a async request */
+ NTSTATUS (*async_setup_fn)(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ void *private_data);
+
+ /* filesystem operations */
+ NTSTATUS (*fsinfo_fn)(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_fsinfo *fs);
+
+ /* path operations */
+ NTSTATUS (*unlink_fn)(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_unlink *unl);
+ NTSTATUS (*chkpath_fn)(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_chkpath *cp);
+ NTSTATUS (*qpathinfo_fn)(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_fileinfo *st);
+ NTSTATUS (*setpathinfo_fn)(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_setfileinfo *st);
+ NTSTATUS (*mkdir_fn)(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_mkdir *md);
+ NTSTATUS (*rmdir_fn)(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ struct smb_rmdir *rd);
+ NTSTATUS (*rename_fn)(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_rename *ren);
+ NTSTATUS (*copy_fn)(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ struct smb_copy *cp);
+ NTSTATUS (*open_fn)(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_open *oi);
+
+ /* directory search */
+ NTSTATUS (*search_first_fn)(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_search_first *io, void *private_data,
+ bool (*callback_fn)(void *private_data, const union smb_search_data *file));
+ NTSTATUS (*search_next_fn)(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_search_next *io, void *private_data,
+ bool (*callback_fn)(void *private_data, const union smb_search_data *file));
+ NTSTATUS (*search_close_fn)(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_search_close *io);
+
+ /* operations on open files */
+ NTSTATUS (*ioctl_fn)(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_ioctl *io);
+ NTSTATUS (*read_fn)(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_read *io);
+ NTSTATUS (*write_fn)(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_write *io);
+ NTSTATUS (*seek_fn)(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_seek *io);
+ NTSTATUS (*flush_fn)(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_flush *flush);
+ NTSTATUS (*lock_fn)(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_lock *lck);
+ NTSTATUS (*qfileinfo_fn)(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_fileinfo *info);
+ NTSTATUS (*setfileinfo_fn)(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_setfileinfo *info);
+ NTSTATUS (*close_fn)(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_close *io);
+
+ /* trans interface - used by IPC backend for pipes and RAP calls */
+ NTSTATUS (*trans_fn)(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ struct smb_trans2 *trans);
+
+ /* trans2 interface - only used by CIFS backend to prover complete passthru for testing */
+ NTSTATUS (*trans2_fn)(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ struct smb_trans2 *trans2);
+
+ /* change notify request */
+ NTSTATUS (*notify_fn)(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_notify *info);
+
+ /* cancel - cancels any pending async request */
+ NTSTATUS (*cancel_fn)(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req);
+
+ /* printing specific operations */
+ NTSTATUS (*lpq_fn)(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_lpq *lpq);
+
+ /* logoff - called when a vuid is closed */
+ NTSTATUS (*logoff_fn)(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req);
+ NTSTATUS (*exit_fn)(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req);
+};
+
+struct ntvfs_module_context {
+ struct ntvfs_module_context *prev, *next;
+ struct ntvfs_context *ctx;
+ int depth;
+ const struct ntvfs_ops *ops;
+ void *private_data;
+};
+
+struct ntvfs_context {
+ enum ntvfs_type type;
+
+ /* the reported filesystem type */
+ char *fs_type;
+
+ /* the reported device type */
+ char *dev_type;
+
+ enum protocol_types protocol;
+
+ /*
+ * client capabilities
+ * this field doesn't use protocol specific
+ * values!
+ */
+#define NTVFS_CLIENT_CAP_LEVEL_II_OPLOCKS 0x0000000000000001LLU
+ uint64_t client_caps;
+
+ /*
+ * linked list of module contexts
+ */
+ struct ntvfs_module_context *modules;
+
+ struct share_config *config;
+
+ struct server_id server_id;
+ struct loadparm_context *lp_ctx;
+ struct tevent_context *event_ctx;
+ struct imessaging_context *msg_ctx;
+
+ struct {
+ void *private_data;
+ NTSTATUS (*handler)(void *private_data, struct ntvfs_handle *handle, uint8_t level);
+ } oplock;
+
+ struct {
+ const struct tsocket_address *local_address;
+ const struct tsocket_address *remote_address;
+ } client;
+
+ struct {
+ void *private_data;
+ NTSTATUS (*create_new)(void *private_data, struct ntvfs_request *req, struct ntvfs_handle **h);
+ NTSTATUS (*make_valid)(void *private_data, struct ntvfs_handle *h);
+ void (*destroy)(void *private_data, struct ntvfs_handle *h);
+ struct ntvfs_handle *(*search_by_wire_key)(void *private_data, struct ntvfs_request *req, const DATA_BLOB *key);
+ DATA_BLOB (*get_wire_key)(void *private_data, struct ntvfs_handle *handle, TALLOC_CTX *mem_ctx);
+ } handles;
+};
+
+/* a set of flags to control handling of request structures */
+#define NTVFS_ASYNC_STATE_ASYNC (1<<1) /* the backend will answer this one later */
+#define NTVFS_ASYNC_STATE_MAY_ASYNC (1<<2) /* the backend is allowed to answer async */
+#define NTVFS_ASYNC_STATE_CLOSE (1<<3) /* the backend session should be closed */
+
+/* the ntvfs_async_state structure allows backend functions to
+ delay replying to requests. To use this, the front end must
+ set send_fn to a function to be called by the backend
+ when the reply is finally ready to be sent. The backend
+ must set status to the status it wants in the
+ reply. The backend must set the NTVFS_ASYNC_STATE_ASYNC
+ control_flag on the request to indicate that it wishes to
+ delay the reply
+
+ If NTVFS_ASYNC_STATE_MAY_ASYNC is not set then the backend cannot
+ ask for a delayed reply for this request
+
+ note that the private_data pointer is private to the layer which alloced this struct
+*/
+struct ntvfs_async_state {
+ struct ntvfs_async_state *prev, *next;
+ /* the async handling infos */
+ unsigned int state;
+ void *private_data;
+ void (*send_fn)(struct ntvfs_request *);
+ NTSTATUS status;
+
+ /* the passthru module's per session private data */
+ struct ntvfs_module_context *ntvfs;
+};
+
+struct ntvfs_request {
+ /* the ntvfs_context this requests belongs to */
+ struct ntvfs_context *ctx;
+
+ /* ntvfs per request async states */
+ struct ntvfs_async_state *async_states;
+
+ /* the session_info, with security_token and maybe delegated credentials */
+ struct auth_session_info *session_info;
+
+ /* the smb pid is needed for locking contexts */
+ uint32_t smbpid;
+
+ /*
+ * client capabilities
+ * this field doesn't use protocol specific
+ * values!
+ * see NTVFS_CLIENT_CAP_*
+ */
+ uint64_t client_caps;
+
+ /* some statistics for the management tools */
+ struct {
+ /* the system time when the request arrived */
+ struct timeval request_time;
+ } statistics;
+
+ struct {
+ void *private_data;
+ } frontend_data;
+};
+
+struct ntvfs_handle {
+ struct ntvfs_context *ctx;
+
+ struct auth_session_info *session_info;
+
+ uint16_t smbpid;
+
+ struct ntvfs_handle_data {
+ struct ntvfs_handle_data *prev, *next;
+ struct ntvfs_module_context *owner;
+ void *private_data;/* this must be a valid talloc pointer */
+ } *backend_data;
+
+ struct {
+ void *private_data;
+ } frontend_data;
+};
+
+/* this structure is used by backends to determine the size of some critical types */
+struct ntvfs_critical_sizes {
+ int interface_version;
+ int sizeof_ntvfs_critical_sizes;
+ int sizeof_ntvfs_context;
+ int sizeof_ntvfs_module_context;
+ int sizeof_ntvfs_ops;
+ int sizeof_ntvfs_async_state;
+ int sizeof_ntvfs_request;
+ int sizeof_ntvfs_handle;
+ int sizeof_ntvfs_handle_data;
+};
+
+#define NTVFS_CURRENT_CRITICAL_SIZES(c) \
+ struct ntvfs_critical_sizes c = { \
+ .interface_version = NTVFS_INTERFACE_VERSION, \
+ .sizeof_ntvfs_critical_sizes = sizeof(struct ntvfs_critical_sizes), \
+ .sizeof_ntvfs_context = sizeof(struct ntvfs_context), \
+ .sizeof_ntvfs_module_context = sizeof(struct ntvfs_module_context), \
+ .sizeof_ntvfs_ops = sizeof(struct ntvfs_ops), \
+ .sizeof_ntvfs_async_state = sizeof(struct ntvfs_async_state), \
+ .sizeof_ntvfs_request = sizeof(struct ntvfs_request), \
+ .sizeof_ntvfs_handle = sizeof(struct ntvfs_handle), \
+ .sizeof_ntvfs_handle_data = sizeof(struct ntvfs_handle_data), \
+ }
+
+struct imessaging_context;
+#include "librpc/gen_ndr/security.h"
+#include "librpc/gen_ndr/notify.h"
+#include "ntvfs/ntvfs_proto.h"
+
+#endif /* _NTVFS_H_ */
diff --git a/source4/ntvfs/ntvfs_base.c b/source4/ntvfs/ntvfs_base.c
new file mode 100644
index 0000000..5c438bb
--- /dev/null
+++ b/source4/ntvfs/ntvfs_base.c
@@ -0,0 +1,249 @@
+/*
+ Unix SMB/CIFS implementation.
+ NTVFS base code
+
+ Copyright (C) Andrew Tridgell 2003
+ Copyright (C) Stefan (metze) Metzmacher 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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/>.
+*/
+/*
+ this implements the core code for all NTVFS modules. Backends register themselves here.
+*/
+
+#include "includes.h"
+#include "../lib/util/dlinklist.h"
+#include "ntvfs/ntvfs.h"
+#include "param/param.h"
+#include "lib/util/samba_modules.h"
+
+/* the list of currently registered NTVFS backends, note that there
+ * can be more than one backend with the same name, as long as they
+ * have different typesx */
+static struct ntvfs_backend {
+ const struct ntvfs_ops *ops;
+} *backends = NULL;
+static int num_backends;
+
+/*
+ register a NTVFS backend.
+
+ The 'name' can be later used by other backends to find the operations
+ structure for this backend.
+
+ The 'type' is used to specify whether this is for a disk, printer or IPC$ share
+*/
+NTSTATUS ntvfs_register(const struct ntvfs_ops *ops,
+ const struct ntvfs_critical_sizes *const sizes)
+{
+ struct ntvfs_ops *new_ops;
+
+ if (ntvfs_interface_differs(sizes)) {
+ DEBUG(0, ("NTVFS backend '%s' for type %d "
+ "failed version check\n",
+ ops->name, (int)ops->type));
+ return NT_STATUS_BAD_FUNCTION_TABLE;
+ }
+
+ if (ntvfs_backend_byname(ops->name, ops->type) != NULL) {
+ /* its already registered! */
+ DEBUG(0,("NTVFS backend '%s' for type %d already registered\n",
+ ops->name, (int)ops->type));
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+
+ backends = realloc_p(backends, struct ntvfs_backend, num_backends+1);
+ if (!backends) {
+ smb_panic("out of memory in ntvfs_register");
+ }
+
+ new_ops = smb_xmemdup(ops, sizeof(*ops));
+ new_ops->name = smb_xstrdup(ops->name);
+
+ backends[num_backends].ops = new_ops;
+
+ num_backends++;
+
+ DEBUG(3,("NTVFS backend '%s' for type %d registered\n",
+ ops->name,ops->type));
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ return the operations structure for a named backend of the specified type
+*/
+const struct ntvfs_ops *ntvfs_backend_byname(const char *name, enum ntvfs_type type)
+{
+ int i;
+
+ for (i=0;i<num_backends;i++) {
+ if (backends[i].ops->type == type &&
+ strcmp(backends[i].ops->name, name) == 0) {
+ return backends[i].ops;
+ }
+ }
+
+ return NULL;
+}
+
+
+/*
+ return the NTVFS interface version, and the size of some critical types
+ This can be used by backends to either detect compilation errors, or provide
+ multiple implementations for different smbd compilation options in one module
+*/
+
+static const NTVFS_CURRENT_CRITICAL_SIZES(critical_sizes);
+
+const struct ntvfs_critical_sizes *ntvfs_interface_version(void)
+{
+ return &critical_sizes;
+}
+
+bool ntvfs_interface_differs(const struct ntvfs_critical_sizes *const iface)
+{
+ /* The comparison would be easier with memcmp, but compiler-interset
+ * alignment padding is not guaranteed to be zeroed.
+ */
+
+#define FIELD_DIFFERS(field) (iface->field != critical_sizes.field)
+
+ if (FIELD_DIFFERS(interface_version))
+ return true;
+
+ if (FIELD_DIFFERS(sizeof_ntvfs_critical_sizes))
+ return true;
+
+ if (FIELD_DIFFERS(sizeof_ntvfs_context))
+ return true;
+
+ if (FIELD_DIFFERS(sizeof_ntvfs_module_context))
+ return true;
+
+ if (FIELD_DIFFERS(sizeof_ntvfs_ops))
+ return true;
+
+ if (FIELD_DIFFERS(sizeof_ntvfs_async_state))
+ return true;
+
+ if (FIELD_DIFFERS(sizeof_ntvfs_request))
+ return true;
+
+ /* Versions match. */
+ return false;
+
+#undef FIELD_DIFFERS
+}
+
+/*
+ initialise a connection structure to point at a NTVFS backend
+*/
+NTSTATUS ntvfs_init_connection(TALLOC_CTX *mem_ctx, struct share_config *scfg, enum ntvfs_type type,
+ enum protocol_types protocol,
+ uint64_t ntvfs_client_caps,
+ struct tevent_context *ev, struct imessaging_context *msg,
+ struct loadparm_context *lp_ctx,
+ struct server_id server_id, struct ntvfs_context **_ctx)
+{
+ const char **handlers = share_string_list_option(mem_ctx, scfg, SHARE_NTVFS_HANDLER);
+ int i;
+ struct ntvfs_context *ctx;
+
+ if (!handlers) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ ctx = talloc_zero(mem_ctx, struct ntvfs_context);
+ NT_STATUS_HAVE_NO_MEMORY(ctx);
+ ctx->protocol = protocol;
+ ctx->client_caps = ntvfs_client_caps;
+ ctx->type = type;
+ ctx->config = talloc_steal(ctx, scfg);
+ ctx->event_ctx = ev;
+ ctx->msg_ctx = msg;
+ ctx->server_id = server_id;
+ ctx->lp_ctx = lp_ctx;
+
+ for (i=0; handlers[i]; i++) {
+ struct ntvfs_module_context *ntvfs;
+
+ ntvfs = talloc_zero(ctx, struct ntvfs_module_context);
+ NT_STATUS_HAVE_NO_MEMORY(ntvfs);
+ ntvfs->ctx = ctx;
+ ntvfs->ops = ntvfs_backend_byname(handlers[i], ctx->type);
+ if (!ntvfs->ops) {
+ DEBUG(1,("ntvfs_init_connection: failed to find backend=%s, type=%d\n",
+ handlers[i], ctx->type));
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ ntvfs->depth = i;
+ DLIST_ADD_END(ctx->modules, ntvfs);
+ }
+
+ if (!ctx->modules) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ *_ctx = ctx;
+ return NT_STATUS_OK;
+}
+
+/*
+ adds the IPC$ share, needed for RPC calls
+ */
+static NTSTATUS ntvfs_add_ipc_share(struct loadparm_context *lp_ctx)
+{
+ struct loadparm_service *ipc;
+
+ if (lpcfg_service(lp_ctx, "IPC$")) {
+ /* it has already been defined in smb.conf or elsewhere */
+ return NT_STATUS_OK;
+ }
+
+ ipc = lpcfg_add_service(lp_ctx, NULL, "IPC$");
+ NT_STATUS_HAVE_NO_MEMORY(ipc);
+
+ lpcfg_do_service_parameter(lp_ctx, ipc, "comment", "IPC Service");
+ lpcfg_do_service_parameter(lp_ctx, ipc, "path", "/dev/null");
+ lpcfg_do_service_parameter(lp_ctx, ipc, "ntvfs handler", "default");
+ lpcfg_do_service_parameter(lp_ctx, ipc, "browseable", "No");
+ lpcfg_do_service_parameter(lp_ctx, ipc, "fstype", "IPC");
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS ntvfs_init(struct loadparm_context *lp_ctx)
+{
+ static bool initialized = false;
+#define _MODULE_PROTO(init) extern NTSTATUS init(TALLOC_CTX *);
+ STATIC_ntvfs_MODULES_PROTO;
+ init_module_fn static_init[] = { STATIC_ntvfs_MODULES };
+ init_module_fn *shared_init;
+
+ if (initialized) return NT_STATUS_OK;
+ initialized = true;
+
+ shared_init = load_samba_modules(NULL, "ntvfs");
+
+ run_init_functions(NULL, static_init);
+ run_init_functions(NULL, shared_init);
+
+ talloc_free(shared_init);
+
+ ntvfs_add_ipc_share(lp_ctx);
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/ntvfs/ntvfs_generic.c b/source4/ntvfs/ntvfs_generic.c
new file mode 100644
index 0000000..de0ae2c
--- /dev/null
+++ b/source4/ntvfs/ntvfs_generic.c
@@ -0,0 +1,1648 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ NTVFS generic level mapping code
+
+ Copyright (C) Andrew Tridgell 2003-2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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/>.
+*/
+/*
+ this implements mappings between info levels for NTVFS backend calls
+
+ the idea is that each of these functions implements one of the NTVFS
+ backend calls in terms of the 'generic' call. All backends that use
+ these functions must supply the generic call, but can if it wants to
+ also implement other levels if the need arises
+
+ this allows backend writers to only implement one variant of each
+ call unless they need fine grained control of the calls.
+*/
+
+#include "includes.h"
+#include "ntvfs/ntvfs.h"
+#include "libcli/smb2/smb2.h"
+#include "libcli/smb2/smb2_calls.h"
+
+#undef strcasecmp
+
+/* a second stage function converts from the out parameters of the generic
+ call onto the out parameters of the specific call made */
+typedef NTSTATUS (*second_stage_t)(struct ntvfs_module_context *,
+ struct ntvfs_request *,
+ void *, void *, NTSTATUS);
+
+/*
+ this structure holds the async state for pending mapped async calls
+*/
+struct ntvfs_map_async {
+ struct ntvfs_module_context *ntvfs;
+ void *io, *io2;
+ second_stage_t fn;
+};
+
+/*
+ this is a async wrapper, called from the backend when it has completed
+ a function that it has decided to reply to in an async fashion
+*/
+static void ntvfs_map_async_send(struct ntvfs_request *req)
+{
+ struct ntvfs_map_async *m = talloc_get_type(req->async_states->private_data,
+ struct ntvfs_map_async);
+
+ ntvfs_async_state_pop(req);
+
+ /* call the _finish function setup in ntvfs_map_async_setup() */
+ req->async_states->status = m->fn(m->ntvfs, req, m->io, m->io2, req->async_states->status);
+
+ /* call the send function from the next module up */
+ req->async_states->send_fn(req);
+}
+
+/*
+ prepare for calling a ntvfs backend with async support
+ io is the original call structure
+ io2 is the new call structure for the mapped call
+ fn is a second stage function for processing the out arguments
+*/
+static NTSTATUS ntvfs_map_async_setup(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ void *io, void *io2,
+ second_stage_t fn)
+{
+ struct ntvfs_map_async *m;
+ m = talloc(req, struct ntvfs_map_async);
+ if (m == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ m->ntvfs = ntvfs;
+ m->io = io;
+ m->io2 = io2;
+ m->fn = fn;
+ return ntvfs_async_state_push(ntvfs, req, m, ntvfs_map_async_send);
+}
+
+/*
+ called when first stage processing is complete.
+*/
+static NTSTATUS ntvfs_map_async_finish(struct ntvfs_request *req, NTSTATUS status)
+{
+ struct ntvfs_map_async *m;
+
+ /* if the backend has decided to reply in an async fashion then
+ we don't need to do any work here */
+ if (req->async_states->state & NTVFS_ASYNC_STATE_ASYNC) {
+ return status;
+ }
+
+ /* the backend is replying immediately. call the 2nd stage function after popping our local
+ async state */
+ m = talloc_get_type(req->async_states->private_data,
+ struct ntvfs_map_async);
+
+ ntvfs_async_state_pop(req);
+
+ return m->fn(m->ntvfs, req, m->io, m->io2, status);
+}
+
+/*
+ see if a filename ends in EXE COM DLL or SYM. This is needed for the
+ DENY_DOS mapping for OpenX
+*/
+bool is_exe_filename(const char *fname)
+{
+ char *p;
+ p = strrchr(fname, '.');
+ if (!p) {
+ return false;
+ }
+ p++;
+ if (strcasecmp(p, "EXE") == 0 ||
+ strcasecmp(p, "COM") == 0 ||
+ strcasecmp(p, "DLL") == 0 ||
+ strcasecmp(p, "SYM") == 0) {
+ return true;
+ }
+ return false;
+}
+
+
+/*
+ NTVFS openx to ntcreatex mapper
+*/
+static NTSTATUS ntvfs_map_open_finish(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_open *io,
+ union smb_open *io2,
+ NTSTATUS status)
+{
+ time_t write_time = 0;
+ uint32_t set_size = 0;
+ union smb_setfileinfo *sf;
+ unsigned int state;
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ switch (io->generic.level) {
+ case RAW_OPEN_OPEN:
+ io->openold.out.file.ntvfs = io2->generic.out.file.ntvfs;
+ io->openold.out.attrib = io2->generic.out.attrib;
+ io->openold.out.write_time = nt_time_to_unix(io2->generic.out.write_time);
+ io->openold.out.size = io2->generic.out.size;
+ io->openold.out.rmode = io->openold.in.open_mode;
+ break;
+
+ case RAW_OPEN_OPENX:
+ io->openx.out.file.ntvfs = io2->generic.out.file.ntvfs;
+ io->openx.out.attrib = io2->generic.out.attrib;
+ io->openx.out.write_time = nt_time_to_unix(io2->generic.out.write_time);
+ io->openx.out.size = io2->generic.out.size;
+ io->openx.out.access = (io->openx.in.open_mode & OPENX_MODE_ACCESS_MASK);
+ io->openx.out.ftype = 0;
+ io->openx.out.devstate = 0;
+ io->openx.out.action = io2->generic.out.create_action;
+ io->openx.out.unique_fid = 0;
+ io->openx.out.access_mask = SEC_STD_ALL;
+ io->openx.out.unknown = 0;
+
+ /* we need to extend the file to the requested size if
+ it was newly created */
+ if (io2->generic.out.create_action == NTCREATEX_ACTION_CREATED) {
+ set_size = io->openx.in.size;
+ }
+ break;
+
+ case RAW_OPEN_T2OPEN:
+ io->t2open.out.file.ntvfs = io2->generic.out.file.ntvfs;
+ io->t2open.out.attrib = io2->generic.out.attrib;
+ io->t2open.out.write_time = nt_time_to_unix(io2->generic.out.write_time);
+ io->t2open.out.size = io2->generic.out.size;
+ io->t2open.out.access = io->t2open.in.open_mode;
+ io->t2open.out.ftype = 0;
+ io->t2open.out.devstate = 0;
+ io->t2open.out.action = io2->generic.out.create_action;
+ io->t2open.out.file_id = 0;
+ break;
+
+ case RAW_OPEN_MKNEW:
+ case RAW_OPEN_CREATE:
+ io->mknew.out.file.ntvfs= io2->generic.out.file.ntvfs;
+ write_time = io->mknew.in.write_time;
+ break;
+
+ case RAW_OPEN_CTEMP:
+ io->ctemp.out.file.ntvfs= io2->generic.out.file.ntvfs;
+ io->ctemp.out.name = talloc_strdup(req, io2->generic.in.fname +
+ strlen(io->ctemp.in.directory) + 1);
+ NT_STATUS_HAVE_NO_MEMORY(io->ctemp.out.name);
+ break;
+
+ case RAW_OPEN_SMB2:
+ ZERO_STRUCT(io->smb2.out);
+ io->smb2.out.file.ntvfs = io2->generic.out.file.ntvfs;
+ switch (io2->generic.out.oplock_level) {
+ case BATCH_OPLOCK_RETURN:
+ io->smb2.out.oplock_level = SMB2_OPLOCK_LEVEL_BATCH;
+ break;
+ case EXCLUSIVE_OPLOCK_RETURN:
+ io->smb2.out.oplock_level = SMB2_OPLOCK_LEVEL_EXCLUSIVE;
+ break;
+ case LEVEL_II_OPLOCK_RETURN:
+ io->smb2.out.oplock_level = SMB2_OPLOCK_LEVEL_II;
+ break;
+ default:
+ io->smb2.out.oplock_level = SMB2_OPLOCK_LEVEL_NONE;
+ break;
+ }
+ io->smb2.out.reserved = 0;
+ io->smb2.out.create_action = io2->generic.out.create_action;
+ io->smb2.out.create_time = io2->generic.out.create_time;
+ io->smb2.out.access_time = io2->generic.out.access_time;
+ io->smb2.out.write_time = io2->generic.out.write_time;
+ io->smb2.out.change_time = io2->generic.out.change_time;
+ io->smb2.out.alloc_size = io2->generic.out.alloc_size;
+ io->smb2.out.size = io2->generic.out.size;
+ io->smb2.out.file_attr = io2->generic.out.attrib;
+ io->smb2.out.reserved2 = 0;
+ io->smb2.out.maximal_access = io2->generic.out.maximal_access;
+ memcpy(io->smb2.out.on_disk_id, io2->generic.out.on_disk_id,
+ sizeof(io2->generic.out.on_disk_id));
+ break;
+
+ default:
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ /* doing a secondary request async is more trouble than its
+ worth */
+ state = req->async_states->state;
+ req->async_states->state &= ~NTVFS_ASYNC_STATE_MAY_ASYNC;
+
+ if (write_time != 0) {
+ sf = talloc(req, union smb_setfileinfo);
+ NT_STATUS_HAVE_NO_MEMORY(sf);
+ sf->generic.level = RAW_SFILEINFO_STANDARD;
+ sf->generic.in.file.ntvfs = io2->generic.out.file.ntvfs;
+ sf->standard.in.create_time = 0;
+ sf->standard.in.write_time = write_time;
+ sf->standard.in.access_time = 0;
+ status = ntvfs->ops->setfileinfo_fn(ntvfs, req, sf);
+ }
+
+ if (set_size != 0) {
+ sf = talloc(req, union smb_setfileinfo);
+ NT_STATUS_HAVE_NO_MEMORY(sf);
+ sf->generic.level = RAW_SFILEINFO_END_OF_FILE_INFORMATION;
+ sf->generic.in.file.ntvfs = io2->generic.out.file.ntvfs;
+ sf->end_of_file_info.in.size = set_size;
+ status = ntvfs->ops->setfileinfo_fn(ntvfs, req, sf);
+ if (NT_STATUS_IS_OK(status)) {
+ io->openx.out.size = io->openx.in.size;
+ }
+ }
+
+ req->async_states->state = state;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ the core of the mapping between openx style parameters and ntcreatex
+ parameters
+*/
+static NTSTATUS map_openx_open(uint16_t flags, uint16_t open_mode,
+ uint16_t open_func, const char *fname,
+ union smb_open *io2)
+{
+ io2->generic.in.create_options = NTCREATEX_OPTIONS_NON_DIRECTORY_FILE;
+ io2->generic.in.private_flags = 0;
+
+ if (flags & OPENX_FLAGS_REQUEST_OPLOCK) {
+ io2->generic.in.flags |= NTCREATEX_FLAGS_REQUEST_OPLOCK;
+ }
+ if (flags & OPENX_FLAGS_REQUEST_BATCH_OPLOCK) {
+ io2->generic.in.flags |= NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK;
+ }
+
+ switch (open_mode & OPENX_MODE_ACCESS_MASK) {
+ case OPENX_MODE_ACCESS_READ:
+ case OPENX_MODE_ACCESS_EXEC:
+ io2->generic.in.access_mask = SEC_RIGHTS_FILE_READ;
+ break;
+ case OPENX_MODE_ACCESS_WRITE:
+ io2->generic.in.access_mask = SEC_RIGHTS_FILE_WRITE;
+ break;
+ case OPENX_MODE_ACCESS_RDWR:
+ case OPENX_MODE_ACCESS_FCB:
+ io2->generic.in.access_mask =
+ SEC_RIGHTS_FILE_READ |
+ SEC_RIGHTS_FILE_WRITE;
+ break;
+ default:
+ return NT_STATUS_DOS(ERRDOS, ERRbadaccess);
+ }
+
+ switch (open_mode & OPENX_MODE_DENY_MASK) {
+ case OPENX_MODE_DENY_READ:
+ io2->generic.in.share_access = NTCREATEX_SHARE_ACCESS_WRITE;
+ break;
+ case OPENX_MODE_DENY_WRITE:
+ io2->generic.in.share_access = NTCREATEX_SHARE_ACCESS_READ;
+ break;
+ case OPENX_MODE_DENY_ALL:
+ io2->generic.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
+ break;
+ case OPENX_MODE_DENY_NONE:
+ io2->generic.in.share_access =
+ NTCREATEX_SHARE_ACCESS_READ |
+ NTCREATEX_SHARE_ACCESS_WRITE;
+ break;
+ case OPENX_MODE_DENY_DOS:
+ /* DENY_DOS is quite strange - it depends on the filename! */
+ io2->generic.in.private_flags |=
+ NTCREATEX_FLAG_DENY_DOS;
+ if (is_exe_filename(fname)) {
+ io2->generic.in.share_access =
+ NTCREATEX_SHARE_ACCESS_READ |
+ NTCREATEX_SHARE_ACCESS_WRITE;
+ } else {
+ if ((open_mode & OPENX_MODE_ACCESS_MASK) == OPENX_MODE_ACCESS_READ) {
+ io2->generic.in.share_access = NTCREATEX_SHARE_ACCESS_READ;
+ } else {
+ io2->generic.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
+ }
+ }
+ break;
+ case OPENX_MODE_DENY_FCB:
+ io2->generic.in.private_flags |= NTCREATEX_FLAG_DENY_FCB;
+ io2->generic.in.share_access = NTCREATEX_SHARE_ACCESS_NONE;
+ break;
+ default:
+ return NT_STATUS_DOS(ERRDOS, ERRbadaccess);
+ }
+
+ switch (open_func) {
+ case (OPENX_OPEN_FUNC_OPEN):
+ io2->generic.in.open_disposition = NTCREATEX_DISP_OPEN;
+ break;
+ case (OPENX_OPEN_FUNC_TRUNC):
+ io2->generic.in.open_disposition = NTCREATEX_DISP_OVERWRITE;
+ break;
+ case (OPENX_OPEN_FUNC_FAIL | OPENX_OPEN_FUNC_CREATE):
+ io2->generic.in.open_disposition = NTCREATEX_DISP_CREATE;
+ break;
+ case (OPENX_OPEN_FUNC_OPEN | OPENX_OPEN_FUNC_CREATE):
+ io2->generic.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
+ break;
+ case (OPENX_OPEN_FUNC_TRUNC | OPENX_OPEN_FUNC_CREATE):
+ io2->generic.in.open_disposition = NTCREATEX_DISP_OVERWRITE_IF;
+ break;
+ default:
+ /* this one is very strange */
+ if ((open_mode & OPENX_MODE_ACCESS_MASK) == OPENX_MODE_ACCESS_EXEC) {
+ io2->generic.in.open_disposition = NTCREATEX_DISP_CREATE;
+ break;
+ }
+ return NT_STATUS_DOS(ERRDOS, ERRbadaccess);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ NTVFS open generic to any mapper
+*/
+NTSTATUS ntvfs_map_open(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_open *io)
+{
+ NTSTATUS status;
+ union smb_open *io2;
+
+ io2 = talloc_zero(req, union smb_open);
+ if (io2 == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = ntvfs_map_async_setup(ntvfs, req,
+ io, io2,
+ (second_stage_t)ntvfs_map_open_finish);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ io2->generic.level = RAW_OPEN_GENERIC;
+
+ switch (io->generic.level) {
+ case RAW_OPEN_OPENX:
+ status = map_openx_open(io->openx.in.flags,
+ io->openx.in.open_mode,
+ io->openx.in.open_func,
+ io->openx.in.fname,
+ io2);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ io2->generic.in.file_attr = io->openx.in.file_attrs;
+ io2->generic.in.fname = io->openx.in.fname;
+
+ status = ntvfs->ops->open_fn(ntvfs, req, io2);
+ break;
+
+
+ case RAW_OPEN_OPEN:
+ status = map_openx_open(0,
+ io->openold.in.open_mode,
+ OPENX_OPEN_FUNC_OPEN,
+ io->openold.in.fname,
+ io2);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ io2->generic.in.file_attr = io->openold.in.search_attrs;
+ io2->generic.in.fname = io->openold.in.fname;
+
+ status = ntvfs->ops->open_fn(ntvfs, req, io2);
+ break;
+
+ case RAW_OPEN_T2OPEN:
+ io2->generic.level = RAW_OPEN_NTTRANS_CREATE;
+
+ if (io->t2open.in.open_func == 0) {
+ status = NT_STATUS_OBJECT_NAME_COLLISION;
+ goto done;
+ }
+
+ status = map_openx_open(io->t2open.in.flags,
+ io->t2open.in.open_mode,
+ io->t2open.in.open_func,
+ io->t2open.in.fname,
+ io2);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto done;
+ }
+
+ io2->generic.in.file_attr = io->t2open.in.file_attrs;
+ io2->generic.in.fname = io->t2open.in.fname;
+ io2->generic.in.ea_list = talloc(io2, struct smb_ea_list);
+ io2->generic.in.ea_list->num_eas = io->t2open.in.num_eas;
+ io2->generic.in.ea_list->eas = io->t2open.in.eas;
+
+ status = ntvfs->ops->open_fn(ntvfs, req, io2);
+ break;
+
+ case RAW_OPEN_MKNEW:
+ io2->generic.in.file_attr = io->mknew.in.attrib;
+ io2->generic.in.fname = io->mknew.in.fname;
+ io2->generic.in.open_disposition = NTCREATEX_DISP_CREATE;
+ io2->generic.in.access_mask =
+ SEC_RIGHTS_FILE_READ |
+ SEC_RIGHTS_FILE_WRITE;
+ io2->generic.in.share_access =
+ NTCREATEX_SHARE_ACCESS_READ |
+ NTCREATEX_SHARE_ACCESS_WRITE;
+ status = ntvfs->ops->open_fn(ntvfs, req, io2);
+ break;
+
+ case RAW_OPEN_CREATE:
+ io2->generic.in.file_attr = io->mknew.in.attrib;
+ io2->generic.in.fname = io->mknew.in.fname;
+ io2->generic.in.open_disposition = NTCREATEX_DISP_OPEN_IF;
+ io2->generic.in.access_mask =
+ SEC_RIGHTS_FILE_READ |
+ SEC_RIGHTS_FILE_WRITE;
+ io2->generic.in.share_access =
+ NTCREATEX_SHARE_ACCESS_READ |
+ NTCREATEX_SHARE_ACCESS_WRITE;
+ status = ntvfs->ops->open_fn(ntvfs, req, io2);
+ break;
+
+ case RAW_OPEN_CTEMP:
+ io2->generic.in.file_attr = io->ctemp.in.attrib;
+ io2->generic.in.fname =
+ talloc_asprintf(io2, "%s\\SRV%s",
+ io->ctemp.in.directory,
+ generate_random_str_list(io2, 5, "0123456789"));
+ io2->generic.in.open_disposition = NTCREATEX_DISP_CREATE;
+ io2->generic.in.access_mask =
+ SEC_RIGHTS_FILE_READ |
+ SEC_RIGHTS_FILE_WRITE;
+ io2->generic.in.share_access =
+ NTCREATEX_SHARE_ACCESS_READ |
+ NTCREATEX_SHARE_ACCESS_WRITE;
+ status = ntvfs->ops->open_fn(ntvfs, req, io2);
+ break;
+ case RAW_OPEN_SMB2:
+ switch (io->smb2.in.oplock_level) {
+ case SMB2_OPLOCK_LEVEL_BATCH:
+ io2->generic.in.flags = NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK |
+ NTCREATEX_FLAGS_REQUEST_OPLOCK;
+ break;
+ case SMB2_OPLOCK_LEVEL_EXCLUSIVE:
+ io2->generic.in.flags = NTCREATEX_FLAGS_REQUEST_OPLOCK;
+ break;
+ default:
+ io2->generic.in.flags = 0;
+ break;
+ }
+ io2->generic.in.root_fid.fnum = 0;
+ io2->generic.in.access_mask = io->smb2.in.desired_access;
+ io2->generic.in.alloc_size = io->smb2.in.alloc_size;
+ io2->generic.in.file_attr = io->smb2.in.file_attributes;
+ io2->generic.in.share_access = io->smb2.in.share_access;
+ io2->generic.in.open_disposition= io->smb2.in.create_disposition;
+ io2->generic.in.create_options = io->smb2.in.create_options;
+ io2->generic.in.impersonation = io->smb2.in.impersonation_level;
+ io2->generic.in.security_flags = 0;
+ io2->generic.in.fname = io->smb2.in.fname;
+ io2->generic.in.sec_desc = io->smb2.in.sec_desc;
+ io2->generic.in.ea_list = &io->smb2.in.eas;
+ io2->generic.in.query_maximal_access = io->smb2.in.query_maximal_access;
+ io2->generic.in.query_on_disk_id = io->smb2.in.query_on_disk_id;
+ io2->generic.in.private_flags = 0;
+
+ /* we don't support timewarp yet */
+ if (io->smb2.in.timewarp != 0) {
+ status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ break;
+ }
+
+ /* we need to check these bits before we check the private mask */
+ if (io2->generic.in.create_options & SMB2_CREATE_OPTIONS_NOT_SUPPORTED_MASK) {
+ DEBUG(2,(__location__ " create_options 0x%x not supported\n",
+ io2->generic.in.create_options));
+ status = NT_STATUS_NOT_SUPPORTED;
+ break;
+ }
+
+ /* TODO: find out why only SMB2 ignores these */
+ io2->generic.in.create_options &= ~NTCREATEX_OPTIONS_SYNC_ALERT;
+ io2->generic.in.create_options &= ~NTCREATEX_OPTIONS_ASYNC_ALERT;
+
+ status = ntvfs->ops->open_fn(ntvfs, req, io2);
+ break;
+
+ default:
+ status = NT_STATUS_INVALID_LEVEL;
+ break;
+ }
+done:
+ return ntvfs_map_async_finish(req, status);
+}
+
+
+/*
+ NTVFS any to fsinfo mapper
+*/
+static NTSTATUS ntvfs_map_fsinfo_finish(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_fsinfo *fs,
+ union smb_fsinfo *fs2,
+ NTSTATUS status)
+{
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* and convert it to the required level */
+ switch (fs->generic.level) {
+ case RAW_QFS_DSKATTR: {
+ /* map from generic to DSKATTR */
+ unsigned int bpunit = 64;
+
+ /* we need to scale the sizes to fit */
+ for (bpunit=64; bpunit<0x10000; bpunit *= 2) {
+ if (fs2->generic.out.blocks_total * (double)fs2->generic.out.block_size < bpunit * 512 * 65535.0) {
+ break;
+ }
+ }
+
+ fs->dskattr.out.blocks_per_unit = bpunit;
+ fs->dskattr.out.block_size = 512;
+ fs->dskattr.out.units_total =
+ (fs2->generic.out.blocks_total * (double)fs2->generic.out.block_size) / (bpunit * 512);
+ fs->dskattr.out.units_free =
+ (fs2->generic.out.blocks_free * (double)fs2->generic.out.block_size) / (bpunit * 512);
+
+ /* we must return a maximum of 2G to old DOS systems, or they get very confused */
+ if (bpunit > 64 && req->ctx->protocol <= PROTOCOL_LANMAN2) {
+ fs->dskattr.out.blocks_per_unit = 64;
+ fs->dskattr.out.units_total = 0xFFFF;
+ fs->dskattr.out.units_free = 0xFFFF;
+ }
+ return NT_STATUS_OK;
+ }
+
+ case RAW_QFS_ALLOCATION:
+ fs->allocation.out.fs_id = fs2->generic.out.fs_id;
+ fs->allocation.out.total_alloc_units = fs2->generic.out.blocks_total;
+ fs->allocation.out.avail_alloc_units = fs2->generic.out.blocks_free;
+ fs->allocation.out.sectors_per_unit = 1;
+ fs->allocation.out.bytes_per_sector = fs2->generic.out.block_size;
+ return NT_STATUS_OK;
+
+ case RAW_QFS_VOLUME:
+ fs->volume.out.serial_number = fs2->generic.out.serial_number;
+ fs->volume.out.volume_name.s = fs2->generic.out.volume_name;
+ return NT_STATUS_OK;
+
+ case RAW_QFS_VOLUME_INFO:
+ case RAW_QFS_VOLUME_INFORMATION:
+ fs->volume_info.out.create_time = fs2->generic.out.create_time;
+ fs->volume_info.out.serial_number = fs2->generic.out.serial_number;
+ fs->volume_info.out.volume_name.s = fs2->generic.out.volume_name;
+ return NT_STATUS_OK;
+
+ case RAW_QFS_SIZE_INFO:
+ case RAW_QFS_SIZE_INFORMATION:
+ fs->size_info.out.total_alloc_units = fs2->generic.out.blocks_total;
+ fs->size_info.out.avail_alloc_units = fs2->generic.out.blocks_free;
+ fs->size_info.out.sectors_per_unit = 1;
+ fs->size_info.out.bytes_per_sector = fs2->generic.out.block_size;
+ return NT_STATUS_OK;
+
+ case RAW_QFS_DEVICE_INFO:
+ case RAW_QFS_DEVICE_INFORMATION:
+ fs->device_info.out.device_type = fs2->generic.out.device_type;
+ fs->device_info.out.characteristics = fs2->generic.out.device_characteristics;
+ return NT_STATUS_OK;
+
+ case RAW_QFS_ATTRIBUTE_INFO:
+ case RAW_QFS_ATTRIBUTE_INFORMATION:
+ fs->attribute_info.out.fs_attr = fs2->generic.out.fs_attr;
+ fs->attribute_info.out.max_file_component_length = fs2->generic.out.max_file_component_length;
+ fs->attribute_info.out.fs_type.s = fs2->generic.out.fs_type;
+ return NT_STATUS_OK;
+
+ case RAW_QFS_QUOTA_INFORMATION:
+ ZERO_STRUCT(fs->quota_information.out.unknown);
+ fs->quota_information.out.quota_soft = fs2->generic.out.quota_soft;
+ fs->quota_information.out.quota_hard = fs2->generic.out.quota_hard;
+ fs->quota_information.out.quota_flags = fs2->generic.out.quota_flags;
+ return NT_STATUS_OK;
+
+ case RAW_QFS_FULL_SIZE_INFORMATION:
+ fs->full_size_information.out.total_alloc_units = fs2->generic.out.blocks_total;
+ fs->full_size_information.out.call_avail_alloc_units = fs2->generic.out.blocks_free;
+ fs->full_size_information.out.actual_avail_alloc_units = fs2->generic.out.blocks_free;
+ fs->full_size_information.out.sectors_per_unit = 1;
+ fs->full_size_information.out.bytes_per_sector = fs2->generic.out.block_size;
+ return NT_STATUS_OK;
+
+ case RAW_QFS_OBJECTID_INFORMATION:
+ fs->objectid_information.out.guid = fs2->generic.out.guid;
+ ZERO_STRUCT(fs->objectid_information.out.unknown);
+ return NT_STATUS_OK;
+
+ case RAW_QFS_SECTOR_SIZE_INFORMATION:
+ fs->sector_size_info.out.logical_bytes_per_sector
+ = fs2->generic.out.block_size;
+ fs->sector_size_info.out.phys_bytes_per_sector_atomic
+ = fs2->generic.out.block_size;
+ fs->sector_size_info.out.phys_bytes_per_sector_perf
+ = fs2->generic.out.block_size;
+ fs->sector_size_info.out.fs_effective_phys_bytes_per_sector_atomic
+ = fs2->generic.out.block_size;
+ fs->sector_size_info.out.flags
+ = QFS_SSINFO_FLAGS_ALIGNED_DEVICE
+ | QFS_SSINFO_FLAGS_PARTITION_ALIGNED_ON_DEVICE;
+ fs->sector_size_info.out.byte_off_sector_align = 0;
+ fs->sector_size_info.out.byte_off_partition_align = 0;
+ return NT_STATUS_OK;
+
+ case RAW_QFS_GENERIC:
+ case RAW_QFS_UNIX_INFO:
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ return NT_STATUS_INVALID_LEVEL;
+}
+
+/*
+ NTVFS fsinfo any to generic mapper
+*/
+NTSTATUS ntvfs_map_fsinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_fsinfo *fs)
+{
+ NTSTATUS status;
+ union smb_fsinfo *fs2;
+
+ fs2 = talloc(req, union smb_fsinfo);
+ if (fs2 == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (fs->generic.level == RAW_QFS_GENERIC) {
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ status = ntvfs_map_async_setup(ntvfs, req, fs, fs2,
+ (second_stage_t)ntvfs_map_fsinfo_finish);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* ask the backend for the generic info */
+ fs2->generic.level = RAW_QFS_GENERIC;
+
+ status = ntvfs->ops->fsinfo_fn(ntvfs, req, fs2);
+ return ntvfs_map_async_finish(req, status);
+}
+
+
+/*
+ NTVFS fileinfo generic to any mapper
+*/
+NTSTATUS ntvfs_map_fileinfo(TALLOC_CTX *mem_ctx,
+ union smb_fileinfo *info,
+ union smb_fileinfo *info2)
+{
+ int i;
+ /* and convert it to the required level using results in info2 */
+ switch (info->generic.level) {
+ case RAW_FILEINFO_GETATTR:
+ info->getattr.out.attrib = info2->generic.out.attrib & 0xff;
+ info->getattr.out.size = info2->generic.out.size;
+ info->getattr.out.write_time = nt_time_to_unix(info2->generic.out.write_time);
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_GETATTRE:
+ info->getattre.out.attrib = info2->generic.out.attrib;
+ info->getattre.out.size = info2->generic.out.size;
+ info->getattre.out.write_time = nt_time_to_unix(info2->generic.out.write_time);
+ info->getattre.out.create_time = nt_time_to_unix(info2->generic.out.create_time);
+ info->getattre.out.access_time = nt_time_to_unix(info2->generic.out.access_time);
+ info->getattre.out.alloc_size = info2->generic.out.alloc_size;
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_NETWORK_OPEN_INFORMATION:
+ info->network_open_information.out.create_time = info2->generic.out.create_time;
+ info->network_open_information.out.access_time = info2->generic.out.access_time;
+ info->network_open_information.out.write_time = info2->generic.out.write_time;
+ info->network_open_information.out.change_time = info2->generic.out.change_time;
+ info->network_open_information.out.alloc_size = info2->generic.out.alloc_size;
+ info->network_open_information.out.size = info2->generic.out.size;
+ info->network_open_information.out.attrib = info2->generic.out.attrib;
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_ALL_INFO:
+ case RAW_FILEINFO_ALL_INFORMATION:
+ info->all_info.out.create_time = info2->generic.out.create_time;
+ info->all_info.out.access_time = info2->generic.out.access_time;
+ info->all_info.out.write_time = info2->generic.out.write_time;
+ info->all_info.out.change_time = info2->generic.out.change_time;
+ info->all_info.out.attrib = info2->generic.out.attrib;
+ info->all_info.out.alloc_size = info2->generic.out.alloc_size;
+ info->all_info.out.size = info2->generic.out.size;
+ info->all_info.out.nlink = info2->generic.out.nlink;
+ info->all_info.out.delete_pending = info2->generic.out.delete_pending;
+ info->all_info.out.directory = info2->generic.out.directory;
+ info->all_info.out.ea_size = info2->generic.out.ea_size;
+ info->all_info.out.fname.s = info2->generic.out.fname.s;
+ info->all_info.out.fname.private_length = info2->generic.out.fname.private_length;
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_BASIC_INFO:
+ case RAW_FILEINFO_BASIC_INFORMATION:
+ info->basic_info.out.create_time = info2->generic.out.create_time;
+ info->basic_info.out.access_time = info2->generic.out.access_time;
+ info->basic_info.out.write_time = info2->generic.out.write_time;
+ info->basic_info.out.change_time = info2->generic.out.change_time;
+ info->basic_info.out.attrib = info2->generic.out.attrib;
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_STANDARD:
+ info->standard.out.create_time = nt_time_to_unix(info2->generic.out.create_time);
+ info->standard.out.access_time = nt_time_to_unix(info2->generic.out.access_time);
+ info->standard.out.write_time = nt_time_to_unix(info2->generic.out.write_time);
+ info->standard.out.size = info2->generic.out.size;
+ info->standard.out.alloc_size = info2->generic.out.alloc_size;
+ info->standard.out.attrib = info2->generic.out.attrib;
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_EA_SIZE:
+ info->ea_size.out.create_time = nt_time_to_unix(info2->generic.out.create_time);
+ info->ea_size.out.access_time = nt_time_to_unix(info2->generic.out.access_time);
+ info->ea_size.out.write_time = nt_time_to_unix(info2->generic.out.write_time);
+ info->ea_size.out.size = info2->generic.out.size;
+ info->ea_size.out.alloc_size = info2->generic.out.alloc_size;
+ info->ea_size.out.attrib = info2->generic.out.attrib;
+ info->ea_size.out.ea_size = info2->generic.out.ea_size;
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_STANDARD_INFO:
+ case RAW_FILEINFO_STANDARD_INFORMATION:
+ info->standard_info.out.alloc_size = info2->generic.out.alloc_size;
+ info->standard_info.out.size = info2->generic.out.size;
+ info->standard_info.out.nlink = info2->generic.out.nlink;
+ info->standard_info.out.delete_pending = info2->generic.out.delete_pending;
+ info->standard_info.out.directory = info2->generic.out.directory;
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_INTERNAL_INFORMATION:
+ info->internal_information.out.file_id = info2->generic.out.file_id;
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_EA_INFO:
+ case RAW_FILEINFO_EA_INFORMATION:
+ info->ea_info.out.ea_size = info2->generic.out.ea_size;
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_ATTRIBUTE_TAG_INFORMATION:
+ info->attribute_tag_information.out.attrib = info2->generic.out.attrib;
+ info->attribute_tag_information.out.reparse_tag = info2->generic.out.reparse_tag;
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_STREAM_INFO:
+ case RAW_FILEINFO_STREAM_INFORMATION:
+ info->stream_info.out.num_streams = info2->generic.out.num_streams;
+ if (info->stream_info.out.num_streams > 0) {
+ info->stream_info.out.streams =
+ talloc_array(mem_ctx,
+ struct stream_struct,
+ info->stream_info.out.num_streams);
+ if (!info->stream_info.out.streams) {
+ DEBUG(2,("ntvfs_map_fileinfo: no memory for %d streams\n",
+ info->stream_info.out.num_streams));
+ return NT_STATUS_NO_MEMORY;
+ }
+ for (i=0; i < info->stream_info.out.num_streams; i++) {
+ info->stream_info.out.streams[i] = info2->generic.out.streams[i];
+ info->stream_info.out.streams[i].stream_name.s =
+ talloc_strdup(info->stream_info.out.streams,
+ info2->generic.out.streams[i].stream_name.s);
+ if (!info->stream_info.out.streams[i].stream_name.s) {
+ DEBUG(2,("ntvfs_map_fileinfo: no memory for stream_name\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+ }
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_NAME_INFO:
+ case RAW_FILEINFO_NAME_INFORMATION:
+ info->name_info.out.fname.s = talloc_strdup(mem_ctx, info2->generic.out.fname.s);
+ NT_STATUS_HAVE_NO_MEMORY(info->name_info.out.fname.s);
+ info->name_info.out.fname.private_length = info2->generic.out.fname.private_length;
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_ALT_NAME_INFO:
+ case RAW_FILEINFO_ALT_NAME_INFORMATION:
+ case RAW_FILEINFO_SMB2_ALT_NAME_INFORMATION:
+ info->alt_name_info.out.fname.s = talloc_strdup(mem_ctx, info2->generic.out.alt_fname.s);
+ NT_STATUS_HAVE_NO_MEMORY(info->alt_name_info.out.fname.s);
+ info->alt_name_info.out.fname.private_length = info2->generic.out.alt_fname.private_length;
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_POSITION_INFORMATION:
+ info->position_information.out.position = info2->generic.out.position;
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_ALL_EAS:
+ info->all_eas.out.num_eas = info2->generic.out.num_eas;
+ if (info->all_eas.out.num_eas > 0) {
+ info->all_eas.out.eas = talloc_array(mem_ctx,
+ struct ea_struct,
+ info->all_eas.out.num_eas);
+ if (!info->all_eas.out.eas) {
+ DEBUG(2,("ntvfs_map_fileinfo: no memory for %d eas\n",
+ info->all_eas.out.num_eas));
+ return NT_STATUS_NO_MEMORY;
+ }
+ for (i = 0; i < info->all_eas.out.num_eas; i++) {
+ info->all_eas.out.eas[i] = info2->generic.out.eas[i];
+ info->all_eas.out.eas[i].name.s =
+ talloc_strdup(info->all_eas.out.eas,
+ info2->generic.out.eas[i].name.s);
+ if (!info->all_eas.out.eas[i].name.s) {
+ DEBUG(2,("ntvfs_map_fileinfo: no memory for stream_name\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+ info->all_eas.out.eas[i].value.data =
+ (uint8_t *)talloc_memdup(info->all_eas.out.eas,
+ info2->generic.out.eas[i].value.data,
+ info2->generic.out.eas[i].value.length);
+ if (!info->all_eas.out.eas[i].value.data) {
+ DEBUG(2,("ntvfs_map_fileinfo: no memory for stream_name\n"));
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+ }
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_IS_NAME_VALID:
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_COMPRESSION_INFO:
+ case RAW_FILEINFO_COMPRESSION_INFORMATION:
+ info->compression_info.out.compressed_size = info2->generic.out.compressed_size;
+ info->compression_info.out.format = info2->generic.out.format;
+ info->compression_info.out.unit_shift = info2->generic.out.unit_shift;
+ info->compression_info.out.chunk_shift = info2->generic.out.chunk_shift;
+ info->compression_info.out.cluster_shift = info2->generic.out.cluster_shift;
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_ACCESS_INFORMATION:
+ info->access_information.out.access_flags = info2->generic.out.access_flags;
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_MODE_INFORMATION:
+ info->mode_information.out.mode = info2->generic.out.mode;
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_ALIGNMENT_INFORMATION:
+ info->alignment_information.out.alignment_requirement =
+ info2->generic.out.alignment_requirement;
+ return NT_STATUS_OK;
+ case RAW_FILEINFO_UNIX_BASIC:
+#if 1
+ return NT_STATUS_INVALID_LEVEL;
+#else
+ info->unix_basic_info.out.end_of_file = info2->generic.out.end_of_file;
+ info->unix_basic_info.out.num_bytes = info2->generic.out.size;
+ info->unix_basic_info.out.status_change_time = info2->generic.out.change_time;
+ info->unix_basic_info.out.access_time = info2->generic.out.access_time;
+ info->unix_basic_info.out.change_time = info2->generic.out.change_time;
+ info->unix_basic_info.out.uid = info2->generic.out.uid;
+ info->unix_basic_info.out.gid = info2->generic.out.gid;
+ info->unix_basic_info.out.file_type = info2->generic.out.file_type;
+ info->unix_basic_info.out.dev_major = info2->generic.out.device;
+ info->unix_basic_info.out.dev_minor = info2->generic.out.device;
+ info->unix_basic_info.out.unique_id = info2->generic.out.inode;
+ info->unix_basic_info.out.permissions = info2->generic.out.permissions;
+ info->unix_basic_info.out.nlink = info2->generic.out.nlink;
+ return NT_STATUS_OK;
+#endif
+ case RAW_FILEINFO_UNIX_LINK:
+#if 1
+ return NT_STATUS_INVALID_LEVEL;
+#else
+ info->unix_link_info.out.link_dest = info2->generic.out.link_dest;
+ return NT_STATUS_OK;
+#endif
+ case RAW_FILEINFO_GENERIC:
+ case RAW_FILEINFO_SEC_DESC:
+ case RAW_FILEINFO_EA_LIST:
+ case RAW_FILEINFO_UNIX_INFO2:
+ case RAW_FILEINFO_SMB2_ALL_EAS:
+ case RAW_FILEINFO_SMB2_ALL_INFORMATION:
+ return NT_STATUS_INVALID_LEVEL;
+ case RAW_FILEINFO_NORMALIZED_NAME_INFORMATION:
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ return NT_STATUS_INVALID_LEVEL;
+}
+
+/*
+ NTVFS any to fileinfo mapper
+*/
+static NTSTATUS ntvfs_map_qfileinfo_finish(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_fileinfo *info,
+ union smb_fileinfo *info2,
+ NTSTATUS status)
+{
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return ntvfs_map_fileinfo(req, info, info2);
+}
+
+/*
+ NTVFS fileinfo generic to any mapper
+*/
+NTSTATUS ntvfs_map_qfileinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_fileinfo *info)
+{
+ NTSTATUS status;
+ union smb_fileinfo *info2;
+
+ info2 = talloc(req, union smb_fileinfo);
+ if (info2 == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (info->generic.level == RAW_FILEINFO_GENERIC) {
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ status = ntvfs_map_async_setup(ntvfs, req, info, info2,
+ (second_stage_t)ntvfs_map_qfileinfo_finish);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* ask the backend for the generic info */
+ info2->generic.level = RAW_FILEINFO_GENERIC;
+ info2->generic.in.file.ntvfs= info->generic.in.file.ntvfs;
+
+ status = ntvfs->ops->qfileinfo_fn(ntvfs, req, info2);
+ return ntvfs_map_async_finish(req, status);
+}
+
+/*
+ NTVFS any to fileinfo mapper
+*/
+static NTSTATUS ntvfs_map_qpathinfo_finish(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_fileinfo *info,
+ union smb_fileinfo *info2,
+ NTSTATUS status)
+{
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return ntvfs_map_fileinfo(req, info, info2);
+}
+
+/*
+ NTVFS pathinfo generic to any mapper
+*/
+NTSTATUS ntvfs_map_qpathinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_fileinfo *info)
+{
+ NTSTATUS status;
+ union smb_fileinfo *info2;
+
+ info2 = talloc(req, union smb_fileinfo);
+ if (info2 == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (info->generic.level == RAW_FILEINFO_GENERIC) {
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ status = ntvfs_map_async_setup(ntvfs, req, info, info2,
+ (second_stage_t)ntvfs_map_qpathinfo_finish);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* ask the backend for the generic info */
+ info2->generic.level = RAW_FILEINFO_GENERIC;
+ info2->generic.in.file.path = info->generic.in.file.path;
+
+ status = ntvfs->ops->qpathinfo_fn(ntvfs, req, info2);
+ return ntvfs_map_async_finish(req, status);
+}
+
+
+/*
+ NTVFS lock generic to any mapper
+*/
+NTSTATUS ntvfs_map_lock(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_lock *lck)
+{
+ union smb_lock *lck2;
+ struct smb_lock_entry *locks;
+
+ lck2 = talloc(req, union smb_lock);
+ if (lck2 == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ locks = talloc_array(lck2, struct smb_lock_entry, 1);
+ if (locks == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ switch (lck->generic.level) {
+ case RAW_LOCK_LOCKX:
+ return NT_STATUS_INVALID_LEVEL;
+
+ case RAW_LOCK_LOCK:
+ lck2->generic.level = RAW_LOCK_GENERIC;
+ lck2->generic.in.file.ntvfs= lck->lock.in.file.ntvfs;
+ lck2->generic.in.mode = 0;
+ lck2->generic.in.timeout = 0;
+ lck2->generic.in.ulock_cnt = 0;
+ lck2->generic.in.lock_cnt = 1;
+ lck2->generic.in.locks = locks;
+ locks->pid = req->smbpid;
+ locks->offset = lck->lock.in.offset;
+ locks->count = lck->lock.in.count;
+ break;
+
+ case RAW_LOCK_UNLOCK:
+ lck2->generic.level = RAW_LOCK_GENERIC;
+ lck2->generic.in.file.ntvfs= lck->unlock.in.file.ntvfs;
+ lck2->generic.in.mode = 0;
+ lck2->generic.in.timeout = 0;
+ lck2->generic.in.ulock_cnt = 1;
+ lck2->generic.in.lock_cnt = 0;
+ lck2->generic.in.locks = locks;
+ locks->pid = req->smbpid;
+ locks->offset = lck->unlock.in.offset;
+ locks->count = lck->unlock.in.count;
+ break;
+
+ case RAW_LOCK_SMB2: {
+ /* this is only approximate! We need to change the
+ generic structure to fix this properly */
+ int i;
+ bool isunlock;
+ if (lck->smb2.in.lock_count < 1) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ lck2->generic.level = RAW_LOCK_GENERIC;
+ lck2->generic.in.file.ntvfs= lck->smb2.in.file.ntvfs;
+ lck2->generic.in.timeout = UINT32_MAX;
+ lck2->generic.in.mode = 0;
+ lck2->generic.in.lock_cnt = 0;
+ lck2->generic.in.ulock_cnt = 0;
+ lck2->generic.in.locks = talloc_zero_array(lck2, struct smb_lock_entry,
+ lck->smb2.in.lock_count);
+ if (lck2->generic.in.locks == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ /* only the first lock gives the UNLOCK bit - see
+ MS-SMB2 3.3.5.14 */
+ if (lck->smb2.in.locks[0].flags & SMB2_LOCK_FLAG_UNLOCK) {
+ if (lck->smb2.in.locks[0].flags & SMB2_LOCK_FLAG_FAIL_IMMEDIATELY) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ lck2->generic.in.ulock_cnt = lck->smb2.in.lock_count;
+ isunlock = true;
+ } else {
+ lck2->generic.in.lock_cnt = lck->smb2.in.lock_count;
+ isunlock = false;
+ }
+ for (i=0;i<lck->smb2.in.lock_count;i++) {
+ if (!isunlock &&
+ lck->smb2.in.locks[i].flags == SMB2_LOCK_FLAG_NONE) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (lck->smb2.in.locks[i].flags & ~SMB2_LOCK_FLAG_ALL_MASK) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (isunlock &&
+ (lck->smb2.in.locks[i].flags &
+ (SMB2_LOCK_FLAG_SHARED|SMB2_LOCK_FLAG_EXCLUSIVE))) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ if (!isunlock &&
+ (lck->smb2.in.locks[i].flags & SMB2_LOCK_FLAG_UNLOCK)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ lck2->generic.in.locks[i].pid = req->smbpid;
+ lck2->generic.in.locks[i].offset = lck->smb2.in.locks[i].offset;
+ lck2->generic.in.locks[i].count = lck->smb2.in.locks[i].length;
+ if (!(lck->smb2.in.locks[i].flags & SMB2_LOCK_FLAG_EXCLUSIVE)) {
+ lck2->generic.in.mode = LOCKING_ANDX_SHARED_LOCK;
+ }
+ if (lck->smb2.in.locks[i].flags & SMB2_LOCK_FLAG_FAIL_IMMEDIATELY) {
+ lck2->generic.in.timeout = 0;
+ }
+ }
+ /* initialize output value */
+ lck->smb2.out.reserved = 0;
+ break;
+ }
+
+ case RAW_LOCK_SMB2_BREAK:
+ lck2->generic.level = RAW_LOCK_GENERIC;
+ lck2->generic.in.file.ntvfs = lck->smb2_break.in.file.ntvfs;
+ lck2->generic.in.mode = LOCKING_ANDX_OPLOCK_RELEASE |
+ ((lck->smb2_break.in.oplock_level << 8) & 0xFF00);
+ lck2->generic.in.timeout = 0;
+ lck2->generic.in.ulock_cnt = 0;
+ lck2->generic.in.lock_cnt = 0;
+ lck2->generic.in.locks = NULL;
+
+ /* initialize output value */
+ lck->smb2_break.out.oplock_level= lck->smb2_break.in.oplock_level;
+ lck->smb2_break.out.reserved = lck->smb2_break.in.reserved;
+ lck->smb2_break.out.reserved2 = lck->smb2_break.in.reserved2;
+ lck->smb2_break.out.file = lck->smb2_break.in.file;
+ break;
+ }
+
+ /*
+ * we don't need to call ntvfs_map_async_setup() here,
+ * as lock() doesn't have any output fields
+ */
+
+ return ntvfs->ops->lock_fn(ntvfs, req, lck2);
+}
+
+
+/*
+ NTVFS write generic to any mapper
+*/
+static NTSTATUS ntvfs_map_write_finish(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_write *wr,
+ union smb_write *wr2,
+ NTSTATUS status)
+{
+ union smb_lock *lck;
+ union smb_close *cl;
+ unsigned int state;
+
+ if (NT_STATUS_IS_ERR(status)) {
+ return status;
+ }
+
+ switch (wr->generic.level) {
+ case RAW_WRITE_WRITE:
+ wr->write.out.nwritten = wr2->generic.out.nwritten;
+ break;
+
+ case RAW_WRITE_WRITEUNLOCK:
+ wr->writeunlock.out.nwritten = wr2->generic.out.nwritten;
+
+ lck = talloc(wr2, union smb_lock);
+ if (lck == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ lck->unlock.level = RAW_LOCK_UNLOCK;
+ lck->unlock.in.file.ntvfs = wr->writeunlock.in.file.ntvfs;
+ lck->unlock.in.count = wr->writeunlock.in.count;
+ lck->unlock.in.offset = wr->writeunlock.in.offset;
+
+ if (lck->unlock.in.count != 0) {
+ /* do the lock sync for now */
+ state = req->async_states->state;
+ req->async_states->state &= ~NTVFS_ASYNC_STATE_MAY_ASYNC;
+ status = ntvfs->ops->lock_fn(ntvfs, req, lck);
+ req->async_states->state = state;
+ }
+ break;
+
+ case RAW_WRITE_WRITECLOSE:
+ wr->writeclose.out.nwritten = wr2->generic.out.nwritten;
+
+ cl = talloc(wr2, union smb_close);
+ if (cl == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ cl->close.level = RAW_CLOSE_CLOSE;
+ cl->close.in.file.ntvfs = wr->writeclose.in.file.ntvfs;
+ cl->close.in.write_time = wr->writeclose.in.mtime;
+
+ if (wr2->generic.in.count != 0) {
+ /* do the close sync for now */
+ state = req->async_states->state;
+ req->async_states->state &= ~NTVFS_ASYNC_STATE_MAY_ASYNC;
+ status = ntvfs->ops->close_fn(ntvfs, req, cl);
+ req->async_states->state = state;
+ }
+ break;
+
+ case RAW_WRITE_SPLWRITE:
+ break;
+
+ case RAW_WRITE_SMB2:
+ wr->smb2.out._pad = 0;
+ wr->smb2.out.nwritten = wr2->generic.out.nwritten;
+ wr->smb2.out.unknown1 = 0;
+ break;
+
+ default:
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ return status;
+}
+
+
+/*
+ NTVFS write generic to any mapper
+*/
+NTSTATUS ntvfs_map_write(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_write *wr)
+{
+ union smb_write *wr2;
+ NTSTATUS status;
+
+ wr2 = talloc(req, union smb_write);
+ if (wr2 == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = ntvfs_map_async_setup(ntvfs, req, wr, wr2,
+ (second_stage_t)ntvfs_map_write_finish);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ wr2->writex.level = RAW_WRITE_GENERIC;
+
+ switch (wr->generic.level) {
+ case RAW_WRITE_WRITEX:
+ status = NT_STATUS_INVALID_LEVEL;
+ break;
+
+ case RAW_WRITE_WRITE:
+ wr2->writex.in.file.ntvfs= wr->write.in.file.ntvfs;
+ wr2->writex.in.offset = wr->write.in.offset;
+ wr2->writex.in.wmode = 0;
+ wr2->writex.in.remaining = wr->write.in.remaining;
+ wr2->writex.in.count = wr->write.in.count;
+ wr2->writex.in.data = wr->write.in.data;
+ status = ntvfs->ops->write_fn(ntvfs, req, wr2);
+ break;
+
+ case RAW_WRITE_WRITEUNLOCK:
+ wr2->writex.in.file.ntvfs= wr->writeunlock.in.file.ntvfs;
+ wr2->writex.in.offset = wr->writeunlock.in.offset;
+ wr2->writex.in.wmode = 0;
+ wr2->writex.in.remaining = wr->writeunlock.in.remaining;
+ wr2->writex.in.count = wr->writeunlock.in.count;
+ wr2->writex.in.data = wr->writeunlock.in.data;
+ status = ntvfs->ops->write_fn(ntvfs, req, wr2);
+ break;
+
+ case RAW_WRITE_WRITECLOSE:
+ wr2->writex.in.file.ntvfs= wr->writeclose.in.file.ntvfs;
+ wr2->writex.in.offset = wr->writeclose.in.offset;
+ wr2->writex.in.wmode = 0;
+ wr2->writex.in.remaining = 0;
+ wr2->writex.in.count = wr->writeclose.in.count;
+ wr2->writex.in.data = wr->writeclose.in.data;
+ status = ntvfs->ops->write_fn(ntvfs, req, wr2);
+ break;
+
+ case RAW_WRITE_SPLWRITE:
+ wr2->writex.in.file.ntvfs= wr->splwrite.in.file.ntvfs;
+ wr2->writex.in.offset = 0;
+ wr2->writex.in.wmode = 0;
+ wr2->writex.in.remaining = 0;
+ wr2->writex.in.count = wr->splwrite.in.count;
+ wr2->writex.in.data = wr->splwrite.in.data;
+ status = ntvfs->ops->write_fn(ntvfs, req, wr2);
+ break;
+
+ case RAW_WRITE_SMB2:
+ wr2->writex.in.file.ntvfs= wr->smb2.in.file.ntvfs;
+ wr2->writex.in.offset = wr->smb2.in.offset;
+ wr2->writex.in.wmode = 0;
+ wr2->writex.in.remaining = 0;
+ wr2->writex.in.count = wr->smb2.in.data.length;
+ wr2->writex.in.data = wr->smb2.in.data.data;
+ status = ntvfs->ops->write_fn(ntvfs, req, wr2);
+ }
+
+ return ntvfs_map_async_finish(req, status);
+}
+
+
+/*
+ NTVFS read generic to any mapper - finish the out mapping
+*/
+static NTSTATUS ntvfs_map_read_finish(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_read *rd,
+ union smb_read *rd2,
+ NTSTATUS status)
+{
+ switch (rd->generic.level) {
+ case RAW_READ_READ:
+ rd->read.out.nread = rd2->generic.out.nread;
+ break;
+ case RAW_READ_READBRAW:
+ rd->readbraw.out.nread = rd2->generic.out.nread;
+ break;
+ case RAW_READ_LOCKREAD:
+ rd->lockread.out.nread = rd2->generic.out.nread;
+ break;
+ case RAW_READ_SMB2:
+ rd->smb2.out.data.length= rd2->generic.out.nread;
+ rd->smb2.out.remaining = 0;
+ rd->smb2.out.reserved = 0;
+ break;
+ default:
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ return status;
+}
+
+/*
+ NTVFS read* to readx mapper
+*/
+NTSTATUS ntvfs_map_read(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_read *rd)
+{
+ union smb_read *rd2;
+ union smb_lock *lck;
+ NTSTATUS status;
+ unsigned int state;
+
+ rd2 = talloc(req, union smb_read);
+ if (rd2 == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = ntvfs_map_async_setup(ntvfs, req, rd, rd2,
+ (second_stage_t)ntvfs_map_read_finish);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ rd2->readx.level = RAW_READ_READX;
+ rd2->readx.in.read_for_execute = false;
+
+ switch (rd->generic.level) {
+ case RAW_READ_READX:
+ status = NT_STATUS_INVALID_LEVEL;
+ break;
+
+ case RAW_READ_READ:
+ rd2->readx.in.file.ntvfs= rd->read.in.file.ntvfs;
+ rd2->readx.in.offset = rd->read.in.offset;
+ rd2->readx.in.mincnt = rd->read.in.count;
+ rd2->readx.in.maxcnt = rd->read.in.count;
+ rd2->readx.in.remaining = rd->read.in.remaining;
+ rd2->readx.out.data = rd->read.out.data;
+ status = ntvfs->ops->read_fn(ntvfs, req, rd2);
+ break;
+
+ case RAW_READ_READBRAW:
+ rd2->readx.in.file.ntvfs= rd->readbraw.in.file.ntvfs;
+ rd2->readx.in.offset = rd->readbraw.in.offset;
+ rd2->readx.in.mincnt = rd->readbraw.in.mincnt;
+ rd2->readx.in.maxcnt = rd->readbraw.in.maxcnt;
+ rd2->readx.in.remaining = 0;
+ rd2->readx.out.data = rd->readbraw.out.data;
+ status = ntvfs->ops->read_fn(ntvfs, req, rd2);
+ break;
+
+ case RAW_READ_LOCKREAD:
+ /* do the initial lock sync for now */
+ state = req->async_states->state;
+ req->async_states->state &= ~NTVFS_ASYNC_STATE_MAY_ASYNC;
+
+ lck = talloc(rd2, union smb_lock);
+ if (lck == NULL) {
+ status = NT_STATUS_NO_MEMORY;
+ goto done;
+ }
+ lck->lock.level = RAW_LOCK_LOCK;
+ lck->lock.in.file.ntvfs = rd->lockread.in.file.ntvfs;
+ lck->lock.in.count = rd->lockread.in.count;
+ lck->lock.in.offset = rd->lockread.in.offset;
+ status = ntvfs->ops->lock_fn(ntvfs, req, lck);
+ req->async_states->state = state;
+
+ rd2->readx.in.file.ntvfs= rd->lockread.in.file.ntvfs;
+ rd2->readx.in.offset = rd->lockread.in.offset;
+ rd2->readx.in.mincnt = rd->lockread.in.count;
+ rd2->readx.in.maxcnt = rd->lockread.in.count;
+ rd2->readx.in.remaining = rd->lockread.in.remaining;
+ rd2->readx.out.data = rd->lockread.out.data;
+
+ if (NT_STATUS_IS_OK(status)) {
+ status = ntvfs->ops->read_fn(ntvfs, req, rd2);
+ }
+ break;
+
+ case RAW_READ_SMB2:
+ rd2->readx.in.file.ntvfs= rd->smb2.in.file.ntvfs;
+ rd2->readx.in.offset = rd->smb2.in.offset;
+ rd2->readx.in.mincnt = rd->smb2.in.min_count;
+ rd2->readx.in.maxcnt = rd->smb2.in.length;
+ rd2->readx.in.remaining = 0;
+ rd2->readx.out.data = rd->smb2.out.data.data;
+ status = ntvfs->ops->read_fn(ntvfs, req, rd2);
+ break;
+ }
+
+done:
+ return ntvfs_map_async_finish(req, status);
+}
+
+
+/*
+ NTVFS close generic to any mapper
+*/
+static NTSTATUS ntvfs_map_close_finish(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_close *cl,
+ union smb_close *cl2,
+ NTSTATUS status)
+{
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ switch (cl->generic.level) {
+ case RAW_CLOSE_SMB2:
+ cl->smb2.out.flags = cl2->generic.out.flags;
+ cl->smb2.out._pad = 0;
+ cl->smb2.out.create_time = cl2->generic.out.create_time;
+ cl->smb2.out.access_time = cl2->generic.out.access_time;
+ cl->smb2.out.write_time = cl2->generic.out.write_time;
+ cl->smb2.out.change_time = cl2->generic.out.change_time;
+ cl->smb2.out.alloc_size = cl2->generic.out.alloc_size;
+ cl->smb2.out.size = cl2->generic.out.size;
+ cl->smb2.out.file_attr = cl2->generic.out.file_attr;
+ break;
+ default:
+ break;
+ }
+
+ return status;
+}
+
+/*
+ NTVFS close generic to any mapper
+*/
+NTSTATUS ntvfs_map_close(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_close *cl)
+{
+ union smb_close *cl2;
+ NTSTATUS status;
+
+ cl2 = talloc(req, union smb_close);
+ if (cl2 == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ switch (cl->generic.level) {
+ case RAW_CLOSE_GENERIC:
+ return NT_STATUS_INVALID_LEVEL;
+
+ case RAW_CLOSE_CLOSE:
+ cl2->generic.level = RAW_CLOSE_GENERIC;
+ cl2->generic.in.file = cl->close.in.file;
+ cl2->generic.in.write_time = cl->close.in.write_time;
+ cl2->generic.in.flags = 0;
+ break;
+
+ case RAW_CLOSE_SPLCLOSE:
+ cl2->generic.level = RAW_CLOSE_GENERIC;
+ cl2->generic.in.file = cl->splclose.in.file;
+ cl2->generic.in.write_time = 0;
+ cl2->generic.in.flags = 0;
+ break;
+
+ case RAW_CLOSE_SMB2:
+ cl2->generic.level = RAW_CLOSE_GENERIC;
+ cl2->generic.in.file = cl->smb2.in.file;
+ cl2->generic.in.write_time = 0;
+ cl2->generic.in.flags = cl->smb2.in.flags;
+ break;
+ }
+
+ status = ntvfs_map_async_setup(ntvfs, req, cl, cl2,
+ (second_stage_t)ntvfs_map_close_finish);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ status = ntvfs->ops->close_fn(ntvfs, req, cl2);
+
+ return ntvfs_map_async_finish(req, status);
+}
+
+/*
+ NTVFS notify generic to any mapper
+*/
+static NTSTATUS ntvfs_map_notify_finish(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_notify *nt,
+ union smb_notify *nt2,
+ NTSTATUS status)
+{
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ switch (nt->nttrans.level) {
+ case RAW_NOTIFY_SMB2:
+ if (nt2->nttrans.out.num_changes == 0) {
+ return NT_STATUS_NOTIFY_ENUM_DIR;
+ }
+ nt->smb2.out.num_changes = nt2->nttrans.out.num_changes;
+ nt->smb2.out.changes = talloc_steal(req, nt2->nttrans.out.changes);
+ break;
+
+ default:
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ return status;
+}
+
+
+/*
+ NTVFS notify generic to any mapper
+*/
+NTSTATUS ntvfs_map_notify(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_notify *nt)
+{
+ union smb_notify *nt2;
+ NTSTATUS status;
+
+ nt2 = talloc(req, union smb_notify);
+ NT_STATUS_HAVE_NO_MEMORY(nt2);
+
+ status = ntvfs_map_async_setup(ntvfs, req, nt, nt2,
+ (second_stage_t)ntvfs_map_notify_finish);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ nt2->nttrans.level = RAW_NOTIFY_NTTRANS;
+
+ switch (nt->nttrans.level) {
+ case RAW_NOTIFY_NTTRANS:
+ status = NT_STATUS_INVALID_LEVEL;
+ break;
+
+ case RAW_NOTIFY_SMB2:
+ nt2->nttrans.in.file.ntvfs = nt->smb2.in.file.ntvfs;
+ nt2->nttrans.in.buffer_size = nt->smb2.in.buffer_size;
+ nt2->nttrans.in.completion_filter = nt->smb2.in.completion_filter;
+ nt2->nttrans.in.recursive = nt->smb2.in.recursive;
+ status = ntvfs->ops->notify_fn(ntvfs, req, nt2);
+ break;
+ }
+
+ return ntvfs_map_async_finish(req, status);
+}
diff --git a/source4/ntvfs/ntvfs_interface.c b/source4/ntvfs/ntvfs_interface.c
new file mode 100644
index 0000000..a9e3101
--- /dev/null
+++ b/source4/ntvfs/ntvfs_interface.c
@@ -0,0 +1,702 @@
+/*
+ Unix SMB/CIFS implementation.
+ NTVFS interface functions
+
+ Copyright (C) Stefan (metze) Metzmacher 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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 "ntvfs/ntvfs.h"
+#include "lib/tsocket/tsocket.h"
+
+/* connect/disconnect */
+NTSTATUS ntvfs_connect(struct ntvfs_request *req, union smb_tcon *tcon)
+{
+ struct ntvfs_module_context *ntvfs = req->ctx->modules;
+ if (!ntvfs->ops->connect_fn) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->ops->connect_fn(ntvfs, req, tcon);
+}
+
+NTSTATUS ntvfs_disconnect(struct ntvfs_context *ntvfs_ctx)
+{
+ struct ntvfs_module_context *ntvfs;
+ if (ntvfs_ctx == NULL) {
+ return NT_STATUS_INVALID_CONNECTION;
+ }
+ ntvfs = ntvfs_ctx->modules;
+ if (!ntvfs->ops->disconnect_fn) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->ops->disconnect_fn(ntvfs);
+}
+
+/* async setup - called by a backend that wants to setup any state for
+ a async request */
+NTSTATUS ntvfs_async_setup(struct ntvfs_request *req, void *private_data)
+{
+ struct ntvfs_module_context *ntvfs = req->ctx->modules;
+ if (!ntvfs->ops->async_setup_fn) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->ops->async_setup_fn(ntvfs, req, private_data);
+}
+
+/* filesystem operations */
+NTSTATUS ntvfs_fsinfo(struct ntvfs_request *req, union smb_fsinfo *fs)
+{
+ struct ntvfs_module_context *ntvfs = req->ctx->modules;
+ if (!ntvfs->ops->fsinfo_fn) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->ops->fsinfo_fn(ntvfs, req, fs);
+}
+
+/* path operations */
+NTSTATUS ntvfs_unlink(struct ntvfs_request *req, union smb_unlink *unl)
+{
+ struct ntvfs_module_context *ntvfs = req->ctx->modules;
+ if (!ntvfs->ops->unlink_fn) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->ops->unlink_fn(ntvfs, req, unl);
+}
+
+NTSTATUS ntvfs_chkpath(struct ntvfs_request *req, union smb_chkpath *cp)
+{
+ struct ntvfs_module_context *ntvfs = req->ctx->modules;
+ if (!ntvfs->ops->chkpath_fn) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->ops->chkpath_fn(ntvfs, req, cp);
+}
+
+NTSTATUS ntvfs_qpathinfo(struct ntvfs_request *req, union smb_fileinfo *st)
+{
+ struct ntvfs_module_context *ntvfs = req->ctx->modules;
+ if (!ntvfs->ops->qpathinfo_fn) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->ops->qpathinfo_fn(ntvfs, req, st);
+}
+
+NTSTATUS ntvfs_setpathinfo(struct ntvfs_request *req, union smb_setfileinfo *st)
+{
+ struct ntvfs_module_context *ntvfs = req->ctx->modules;
+ if (!ntvfs->ops->setpathinfo_fn) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->ops->setpathinfo_fn(ntvfs, req, st);
+}
+
+NTSTATUS ntvfs_open(struct ntvfs_request *req, union smb_open *oi)
+{
+ struct ntvfs_module_context *ntvfs = req->ctx->modules;
+ if (!ntvfs->ops->open_fn) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->ops->open_fn(ntvfs, req, oi);
+}
+
+NTSTATUS ntvfs_mkdir(struct ntvfs_request *req, union smb_mkdir *md)
+{
+ struct ntvfs_module_context *ntvfs = req->ctx->modules;
+ if (!ntvfs->ops->mkdir_fn) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->ops->mkdir_fn(ntvfs, req, md);
+}
+
+NTSTATUS ntvfs_rmdir(struct ntvfs_request *req, struct smb_rmdir *rd)
+{
+ struct ntvfs_module_context *ntvfs = req->ctx->modules;
+ if (!ntvfs->ops->rmdir_fn) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->ops->rmdir_fn(ntvfs, req, rd);
+}
+
+NTSTATUS ntvfs_rename(struct ntvfs_request *req, union smb_rename *ren)
+{
+ struct ntvfs_module_context *ntvfs = req->ctx->modules;
+ if (!ntvfs->ops->rename_fn) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->ops->rename_fn(ntvfs, req, ren);
+}
+
+NTSTATUS ntvfs_copy(struct ntvfs_request *req, struct smb_copy *cp)
+{
+ struct ntvfs_module_context *ntvfs = req->ctx->modules;
+ if (!ntvfs->ops->copy_fn) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->ops->copy_fn(ntvfs, req, cp);
+}
+
+/* directory search */
+NTSTATUS ntvfs_search_first(struct ntvfs_request *req, union smb_search_first *io, void *private_data,
+ bool ntvfs_callback(void *private_data, const union smb_search_data *file))
+{
+ struct ntvfs_module_context *ntvfs = req->ctx->modules;
+ if (!ntvfs->ops->search_first_fn) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->ops->search_first_fn(ntvfs, req, io, private_data, ntvfs_callback);
+}
+
+NTSTATUS ntvfs_search_next(struct ntvfs_request *req, union smb_search_next *io, void *private_data,
+ bool ntvfs_callback(void *private_data, const union smb_search_data *file))
+{
+ struct ntvfs_module_context *ntvfs = req->ctx->modules;
+ if (!ntvfs->ops->search_next_fn) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->ops->search_next_fn(ntvfs, req, io, private_data, ntvfs_callback);
+}
+
+NTSTATUS ntvfs_search_close(struct ntvfs_request *req, union smb_search_close *io)
+{
+ struct ntvfs_module_context *ntvfs = req->ctx->modules;
+ if (!ntvfs->ops->search_close_fn) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->ops->search_close_fn(ntvfs, req, io);
+}
+
+/* operations on open files */
+NTSTATUS ntvfs_ioctl(struct ntvfs_request *req, union smb_ioctl *io)
+{
+ struct ntvfs_module_context *ntvfs = req->ctx->modules;
+ if (!ntvfs->ops->ioctl_fn) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->ops->ioctl_fn(ntvfs, req, io);
+}
+
+NTSTATUS ntvfs_read(struct ntvfs_request *req, union smb_read *io)
+{
+ struct ntvfs_module_context *ntvfs = req->ctx->modules;
+ if (!ntvfs->ops->read_fn) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->ops->read_fn(ntvfs, req, io);
+}
+
+NTSTATUS ntvfs_write(struct ntvfs_request *req, union smb_write *io)
+{
+ struct ntvfs_module_context *ntvfs = req->ctx->modules;
+ if (!ntvfs->ops->write_fn) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->ops->write_fn(ntvfs, req, io);
+}
+
+NTSTATUS ntvfs_seek(struct ntvfs_request *req, union smb_seek *io)
+{
+ struct ntvfs_module_context *ntvfs = req->ctx->modules;
+ if (!ntvfs->ops->seek_fn) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->ops->seek_fn(ntvfs, req, io);
+}
+
+NTSTATUS ntvfs_flush(struct ntvfs_request *req,
+ union smb_flush *flush)
+{
+ struct ntvfs_module_context *ntvfs = req->ctx->modules;
+ if (!ntvfs->ops->flush_fn) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->ops->flush_fn(ntvfs, req, flush);
+}
+
+NTSTATUS ntvfs_lock(struct ntvfs_request *req, union smb_lock *lck)
+{
+ struct ntvfs_module_context *ntvfs = req->ctx->modules;
+ if (!ntvfs->ops->lock_fn) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->ops->lock_fn(ntvfs, req, lck);
+}
+
+NTSTATUS ntvfs_qfileinfo(struct ntvfs_request *req, union smb_fileinfo *info)
+{
+ struct ntvfs_module_context *ntvfs = req->ctx->modules;
+ if (!ntvfs->ops->qfileinfo_fn) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->ops->qfileinfo_fn(ntvfs, req, info);
+}
+
+NTSTATUS ntvfs_setfileinfo(struct ntvfs_request *req, union smb_setfileinfo *info)
+{
+ struct ntvfs_module_context *ntvfs = req->ctx->modules;
+ if (!ntvfs->ops->setfileinfo_fn) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->ops->setfileinfo_fn(ntvfs, req, info);
+}
+
+NTSTATUS ntvfs_close(struct ntvfs_request *req, union smb_close *io)
+{
+ struct ntvfs_module_context *ntvfs = req->ctx->modules;
+ if (!ntvfs->ops->close_fn) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->ops->close_fn(ntvfs, req, io);
+}
+
+/* trans interface - used by IPC backend for pipes and RAP calls */
+NTSTATUS ntvfs_trans(struct ntvfs_request *req, struct smb_trans2 *trans)
+{
+ struct ntvfs_module_context *ntvfs = req->ctx->modules;
+ if (!ntvfs->ops->trans_fn) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->ops->trans_fn(ntvfs, req, trans);
+}
+
+/* trans2 interface - only used by CIFS backend to prover complete passthru for testing */
+NTSTATUS ntvfs_trans2(struct ntvfs_request *req, struct smb_trans2 *trans2)
+{
+ struct ntvfs_module_context *ntvfs = req->ctx->modules;
+ if (!ntvfs->ops->trans2_fn) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->ops->trans2_fn(ntvfs, req, trans2);
+}
+
+/* printing specific operations */
+NTSTATUS ntvfs_lpq(struct ntvfs_request *req, union smb_lpq *lpq)
+{
+ struct ntvfs_module_context *ntvfs = req->ctx->modules;
+ if (!ntvfs->ops->lpq_fn) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->ops->lpq_fn(ntvfs, req, lpq);
+}
+
+/* logoff - called when a vuid is closed */
+NTSTATUS ntvfs_logoff(struct ntvfs_request *req)
+{
+ struct ntvfs_module_context *ntvfs = req->ctx->modules;
+ if (!ntvfs->ops->logoff_fn) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->ops->logoff_fn(ntvfs, req);
+}
+
+NTSTATUS ntvfs_exit(struct ntvfs_request *req)
+{
+ struct ntvfs_module_context *ntvfs = req->ctx->modules;
+ if (!ntvfs->ops->exit_fn) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->ops->exit_fn(ntvfs, req);
+}
+
+/*
+ change notify request
+*/
+NTSTATUS ntvfs_notify(struct ntvfs_request *req, union smb_notify *info)
+{
+ struct ntvfs_module_context *ntvfs = req->ctx->modules;
+ if (!ntvfs->ops->notify_fn) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->ops->notify_fn(ntvfs, req, info);
+}
+
+/*
+ cancel an outstanding async request
+*/
+NTSTATUS ntvfs_cancel(struct ntvfs_request *req)
+{
+ struct ntvfs_module_context *ntvfs = req->ctx->modules;
+ if (!ntvfs->ops->cancel_fn) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->ops->cancel_fn(ntvfs, req);
+}
+
+/* initial setup */
+NTSTATUS ntvfs_next_connect(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_tcon *tcon)
+{
+ if (!ntvfs->next || !ntvfs->next->ops->connect_fn) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->next->ops->connect_fn(ntvfs->next, req, tcon);
+}
+
+NTSTATUS ntvfs_next_disconnect(struct ntvfs_module_context *ntvfs)
+{
+ if (!ntvfs->next || !ntvfs->next->ops->disconnect_fn) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->next->ops->disconnect_fn(ntvfs->next);
+}
+
+/* async_setup - called when setting up for a async request */
+NTSTATUS ntvfs_next_async_setup(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ void *private_data)
+{
+ if (!ntvfs->next || !ntvfs->next->ops->async_setup_fn) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->next->ops->async_setup_fn(ntvfs->next, req, private_data);
+}
+
+/* filesystem operations */
+NTSTATUS ntvfs_next_fsinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_fsinfo *fs)
+{
+ if (!ntvfs->next || !ntvfs->next->ops->fsinfo_fn) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->next->ops->fsinfo_fn(ntvfs->next, req, fs);
+}
+
+/* path operations */
+NTSTATUS ntvfs_next_unlink(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_unlink *unl)
+{
+ if (!ntvfs->next || !ntvfs->next->ops->unlink_fn) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->next->ops->unlink_fn(ntvfs->next, req, unl);
+}
+
+NTSTATUS ntvfs_next_chkpath(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_chkpath *cp)
+{
+ if (!ntvfs->next || !ntvfs->next->ops->chkpath_fn) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->next->ops->chkpath_fn(ntvfs->next, req, cp);
+}
+
+NTSTATUS ntvfs_next_qpathinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_fileinfo *st)
+{
+ if (!ntvfs->next || !ntvfs->next->ops->qpathinfo_fn) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->next->ops->qpathinfo_fn(ntvfs->next, req, st);
+}
+
+NTSTATUS ntvfs_next_setpathinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_setfileinfo *st)
+{
+ if (!ntvfs->next || !ntvfs->next->ops->setpathinfo_fn) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->next->ops->setpathinfo_fn(ntvfs->next, req, st);
+}
+
+NTSTATUS ntvfs_next_mkdir(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_mkdir *md)
+{
+ if (!ntvfs->next || !ntvfs->next->ops->mkdir_fn) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->next->ops->mkdir_fn(ntvfs->next, req, md);
+}
+
+NTSTATUS ntvfs_next_rmdir(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ struct smb_rmdir *rd)
+{
+ if (!ntvfs->next || !ntvfs->next->ops->rmdir_fn) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->next->ops->rmdir_fn(ntvfs->next, req, rd);
+}
+
+NTSTATUS ntvfs_next_rename(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_rename *ren)
+{
+ if (!ntvfs->next || !ntvfs->next->ops->rename_fn) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->next->ops->rename_fn(ntvfs->next, req, ren);
+}
+
+NTSTATUS ntvfs_next_copy(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ struct smb_copy *cp)
+{
+ if (!ntvfs->next || !ntvfs->next->ops->copy_fn) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->next->ops->copy_fn(ntvfs->next, req, cp);
+}
+
+NTSTATUS ntvfs_next_open(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_open *oi)
+{
+ if (!ntvfs->next || !ntvfs->next->ops->open_fn) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->next->ops->open_fn(ntvfs->next, req, oi);
+}
+
+
+/* directory search */
+NTSTATUS ntvfs_next_search_first(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_search_first *io, void *private_data,
+ bool (*callback)(void *private_data, const union smb_search_data *file))
+{
+ if (!ntvfs->next || !ntvfs->next->ops->search_first_fn) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->next->ops->search_first_fn(ntvfs->next, req, io, private_data, callback);
+}
+
+NTSTATUS ntvfs_next_search_next(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_search_next *io, void *private_data,
+ bool (*callback)(void *private_data, const union smb_search_data *file))
+{
+ if (!ntvfs->next || !ntvfs->next->ops->search_next_fn) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->next->ops->search_next_fn(ntvfs->next, req, io, private_data, callback);
+}
+
+NTSTATUS ntvfs_next_search_close(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_search_close *io)
+{
+ if (!ntvfs->next || !ntvfs->next->ops->search_close_fn) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->next->ops->search_close_fn(ntvfs->next, req, io);
+}
+
+/* operations on open files */
+NTSTATUS ntvfs_next_ioctl(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_ioctl *io)
+{
+ if (!ntvfs->next || !ntvfs->next->ops->ioctl_fn) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->next->ops->ioctl_fn(ntvfs->next, req, io);
+}
+
+NTSTATUS ntvfs_next_read(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_read *io)
+{
+ if (!ntvfs->next || !ntvfs->next->ops->read_fn) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->next->ops->read_fn(ntvfs->next, req, io);
+}
+
+NTSTATUS ntvfs_next_write(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_write *io)
+{
+ if (!ntvfs->next || !ntvfs->next->ops->write_fn) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->next->ops->write_fn(ntvfs->next, req, io);
+}
+
+NTSTATUS ntvfs_next_seek(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_seek *io)
+{
+ if (!ntvfs->next || !ntvfs->next->ops->seek_fn) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->next->ops->seek_fn(ntvfs->next, req, io);
+}
+
+NTSTATUS ntvfs_next_flush(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_flush *flush)
+{
+ if (!ntvfs->next || !ntvfs->next->ops->flush_fn) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->next->ops->flush_fn(ntvfs->next, req, flush);
+}
+
+NTSTATUS ntvfs_next_lock(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_lock *lck)
+{
+ if (!ntvfs->next || !ntvfs->next->ops->lock_fn) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->next->ops->lock_fn(ntvfs->next, req, lck);
+}
+
+NTSTATUS ntvfs_next_qfileinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_fileinfo *info)
+{
+ if (!ntvfs->next || !ntvfs->next->ops->qfileinfo_fn) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->next->ops->qfileinfo_fn(ntvfs->next, req, info);
+}
+
+NTSTATUS ntvfs_next_setfileinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_setfileinfo *info)
+{
+ if (!ntvfs->next || !ntvfs->next->ops->setfileinfo_fn) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->next->ops->setfileinfo_fn(ntvfs->next, req, info);
+}
+
+NTSTATUS ntvfs_next_close(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_close *io)
+{
+ if (!ntvfs->next || !ntvfs->next->ops->close_fn) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->next->ops->close_fn(ntvfs->next, req, io);
+}
+
+/* trans interface - used by IPC backend for pipes and RAP calls */
+NTSTATUS ntvfs_next_trans(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ struct smb_trans2 *trans)
+{
+ if (!ntvfs->next || !ntvfs->next->ops->trans_fn) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->next->ops->trans_fn(ntvfs->next, req, trans);
+}
+
+/*
+ change notify request
+*/
+NTSTATUS ntvfs_next_notify(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_notify *info)
+{
+ if (!ntvfs->next || !ntvfs->next->ops->notify_fn) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->next->ops->notify_fn(ntvfs->next, req, info);
+}
+
+/* cancel - called to cancel an outstanding async request */
+NTSTATUS ntvfs_next_cancel(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req)
+{
+ if (!ntvfs->next || !ntvfs->next->ops->cancel_fn) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->next->ops->cancel_fn(ntvfs->next, req);
+}
+
+/* printing specific operations */
+NTSTATUS ntvfs_next_lpq(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_lpq *lpq)
+{
+ if (!ntvfs->next || !ntvfs->next->ops->lpq_fn) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->next->ops->lpq_fn(ntvfs->next, req, lpq);
+}
+
+
+/* logoff - called when a vuid is closed */
+NTSTATUS ntvfs_next_logoff(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req)
+{
+ if (!ntvfs->next || !ntvfs->next->ops->logoff_fn) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->next->ops->logoff_fn(ntvfs->next, req);
+}
+
+NTSTATUS ntvfs_next_exit(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req)
+{
+ if (!ntvfs->next || !ntvfs->next->ops->exit_fn) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->next->ops->exit_fn(ntvfs->next, req);
+}
+
+/* client connection callback */
+NTSTATUS ntvfs_set_addresses(struct ntvfs_context *ntvfs,
+ const struct tsocket_address *local_address,
+ const struct tsocket_address *remote_address)
+{
+ ntvfs->client.local_address = tsocket_address_copy(local_address, ntvfs);
+ NT_STATUS_HAVE_NO_MEMORY(ntvfs->client.local_address);
+
+ ntvfs->client.remote_address = tsocket_address_copy(remote_address, ntvfs);
+ NT_STATUS_HAVE_NO_MEMORY(ntvfs->client.remote_address);
+
+ return NT_STATUS_OK;
+}
+
+const struct tsocket_address *ntvfs_get_local_address(struct ntvfs_module_context *ntvfs)
+{
+ return ntvfs->ctx->client.local_address;
+}
+
+const struct tsocket_address *ntvfs_get_remote_address(struct ntvfs_module_context *ntvfs)
+{
+ return ntvfs->ctx->client.remote_address;
+}
+
+/* oplock helpers */
+NTSTATUS ntvfs_set_oplock_handler(struct ntvfs_context *ntvfs,
+ NTSTATUS (*handler)(void *private_data, struct ntvfs_handle *handle, uint8_t level),
+ void *private_data)
+{
+ ntvfs->oplock.handler = handler;
+ ntvfs->oplock.private_data = private_data;
+ return NT_STATUS_OK;
+}
+
+NTSTATUS ntvfs_send_oplock_break(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_handle *handle, uint8_t level)
+{
+ if (!ntvfs->ctx->oplock.handler) {
+ return NT_STATUS_OK;
+ }
+
+ return ntvfs->ctx->oplock.handler(ntvfs->ctx->oplock.private_data, handle, level);
+}
+
diff --git a/source4/ntvfs/ntvfs_util.c b/source4/ntvfs/ntvfs_util.c
new file mode 100644
index 0000000..2888ef4
--- /dev/null
+++ b/source4/ntvfs/ntvfs_util.c
@@ -0,0 +1,197 @@
+/*
+ Unix SMB/CIFS implementation.
+ NTVFS utility code
+ Copyright (C) Stefan Metzmacher 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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/>.
+*/
+/*
+ this implements common utility functions that many NTVFS backends may wish to use
+*/
+
+#include "includes.h"
+#include "../lib/util/dlinklist.h"
+#include "ntvfs/ntvfs.h"
+
+
+struct ntvfs_request *ntvfs_request_create(struct ntvfs_context *ctx, TALLOC_CTX *mem_ctx,
+ struct auth_session_info *session_info,
+ uint16_t smbpid,
+ struct timeval request_time,
+ void *private_data,
+ void (*send_fn)(struct ntvfs_request *),
+ uint32_t state)
+{
+ struct ntvfs_request *req;
+ struct ntvfs_async_state *async;
+
+ req = talloc(mem_ctx, struct ntvfs_request);
+ if (!req) return NULL;
+ req->ctx = ctx;
+ req->async_states = NULL;
+ req->session_info = session_info;
+ req->smbpid = smbpid;
+ req->client_caps = ctx->client_caps;
+ req->statistics.request_time = request_time;
+
+ async = talloc(req, struct ntvfs_async_state);
+ if (!async) goto failed;
+
+ async->state = state;
+ async->private_data = private_data;
+ async->send_fn = send_fn;
+ async->status = NT_STATUS_INTERNAL_ERROR;
+ async->ntvfs = NULL;
+
+ DLIST_ADD(req->async_states, async);
+
+ return req;
+failed:
+ talloc_free(req);
+ return NULL;
+}
+
+NTSTATUS ntvfs_async_state_push(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ void *private_data,
+ void (*send_fn)(struct ntvfs_request *))
+{
+ struct ntvfs_async_state *async;
+
+ async = talloc(req, struct ntvfs_async_state);
+ NT_STATUS_HAVE_NO_MEMORY(async);
+
+ async->state = req->async_states->state;
+ async->private_data = private_data;
+ async->send_fn = send_fn;
+ async->status = NT_STATUS_INTERNAL_ERROR;
+
+ async->ntvfs = ntvfs;
+
+ DLIST_ADD(req->async_states, async);
+
+ return NT_STATUS_OK;
+}
+
+void ntvfs_async_state_pop(struct ntvfs_request *req)
+{
+ struct ntvfs_async_state *async;
+
+ async = req->async_states;
+
+ DLIST_REMOVE(req->async_states, async);
+
+ req->async_states->state = async->state;
+ req->async_states->status = async->status;
+
+ talloc_free(async);
+}
+
+NTSTATUS ntvfs_handle_new(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ struct ntvfs_handle **h)
+{
+ if (!ntvfs->ctx->handles.create_new) {
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return ntvfs->ctx->handles.create_new(ntvfs->ctx->handles.private_data, req, h);
+}
+
+NTSTATUS ntvfs_handle_set_backend_data(struct ntvfs_handle *h,
+ struct ntvfs_module_context *ntvfs,
+ TALLOC_CTX *private_data)
+{
+ struct ntvfs_handle_data *d;
+ bool first_time = h->backend_data?false:true;
+
+ for (d=h->backend_data; d; d = d->next) {
+ if (d->owner != ntvfs) continue;
+ d->private_data = talloc_steal(d, private_data);
+ return NT_STATUS_OK;
+ }
+
+ d = talloc(h, struct ntvfs_handle_data);
+ NT_STATUS_HAVE_NO_MEMORY(d);
+ d->owner = ntvfs;
+ d->private_data = talloc_steal(d, private_data);
+
+ DLIST_ADD(h->backend_data, d);
+
+ if (first_time) {
+ NTSTATUS status;
+ status = h->ctx->handles.make_valid(h->ctx->handles.private_data, h);
+ NT_STATUS_NOT_OK_RETURN(status);
+ }
+
+ return NT_STATUS_OK;
+}
+
+void *ntvfs_handle_get_backend_data(struct ntvfs_handle *h,
+ struct ntvfs_module_context *ntvfs)
+{
+ struct ntvfs_handle_data *d;
+
+ for (d=h->backend_data; d; d = d->next) {
+ if (d->owner != ntvfs) continue;
+ return d->private_data;
+ }
+
+ return NULL;
+}
+
+void ntvfs_handle_remove_backend_data(struct ntvfs_handle *h,
+ struct ntvfs_module_context *ntvfs)
+{
+ struct ntvfs_handle_data *d,*n;
+
+ for (d=h->backend_data; d; d = n) {
+ n = d->next;
+ if (d->owner != ntvfs) continue;
+ DLIST_REMOVE(h->backend_data, d);
+ talloc_free(d);
+ d = NULL;
+ }
+
+ if (h->backend_data) return;
+
+ /* if there's no backend_data anymore, destroy the handle */
+ h->ctx->handles.destroy(h->ctx->handles.private_data, h);
+}
+
+struct ntvfs_handle *ntvfs_handle_search_by_wire_key(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ const DATA_BLOB *key)
+{
+ if (!ntvfs->ctx->handles.search_by_wire_key) {
+ return NULL;
+ }
+ return ntvfs->ctx->handles.search_by_wire_key(ntvfs->ctx->handles.private_data, req, key);
+}
+
+NTSTATUS ntvfs_set_handle_callbacks(struct ntvfs_context *ntvfs,
+ NTSTATUS (*create_new)(void *private_data, struct ntvfs_request *req, struct ntvfs_handle **h),
+ NTSTATUS (*make_valid)(void *private_data, struct ntvfs_handle *h),
+ void (*destroy)(void *private_data, struct ntvfs_handle *h),
+ struct ntvfs_handle *(*search_by_wire_key)(void *private_data, struct ntvfs_request *req, const DATA_BLOB *key),
+ DATA_BLOB (*get_wire_key)(void *private_data, struct ntvfs_handle *handle, TALLOC_CTX *mem_ctx),
+ void *private_data)
+{
+ ntvfs->handles.create_new = create_new;
+ ntvfs->handles.make_valid = make_valid;
+ ntvfs->handles.destroy = destroy;
+ ntvfs->handles.search_by_wire_key = search_by_wire_key;
+ ntvfs->handles.get_wire_key = get_wire_key;
+ ntvfs->handles.private_data = private_data;
+ return NT_STATUS_OK;
+}
diff --git a/source4/ntvfs/posix/posix_eadb.c b/source4/ntvfs/posix/posix_eadb.c
new file mode 100644
index 0000000..2d181fd
--- /dev/null
+++ b/source4/ntvfs/posix/posix_eadb.c
@@ -0,0 +1,299 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ POSIX NTVFS backend - xattr support using a tdb
+
+ Copyright (C) Andrew Tridgell 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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/tdb_wrap/tdb_wrap.h"
+#ifdef WITH_NTVFS_FILESERVER
+#include "vfs_posix.h"
+#endif
+#include "posix_eadb.h"
+
+#define XATTR_LIST_ATTR ".xattr_list"
+
+/*
+ we need to maintain a list of attributes on each file, so that unlink
+ can automatically clean them up
+*/
+static NTSTATUS posix_eadb_add_list(struct tdb_wrap *ea_tdb, TALLOC_CTX *ctx, const char *attr_name,
+ const char *fname, int fd)
+{
+ DATA_BLOB blob;
+ TALLOC_CTX *mem_ctx;
+ NTSTATUS status;
+ size_t len;
+
+ if (strcmp(attr_name, XATTR_LIST_ATTR) == 0) {
+ return NT_STATUS_OK;
+ }
+
+ mem_ctx = talloc_new(ctx);
+
+ status = pull_xattr_blob_tdb_raw(ea_tdb, mem_ctx, XATTR_LIST_ATTR,
+ fname, fd, 100, &blob);
+ if (NT_STATUS_IS_OK(status)) {
+ const char *s;
+
+ for (s = (const char *)blob.data;
+ s < (const char *)(blob.data + blob.length);
+ s += strlen(s) + 1) {
+ if (strcmp(attr_name, s) == 0) {
+ talloc_free(mem_ctx);
+ return NT_STATUS_OK;
+ }
+ }
+ } else {
+ blob = data_blob(NULL, 0);
+ /* No need to parse an empty blob */
+ }
+
+ len = strlen(attr_name) + 1;
+
+ blob.data = talloc_realloc(mem_ctx, blob.data, uint8_t, blob.length + len);
+ if (blob.data == NULL) {
+ talloc_free(mem_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+ memcpy(blob.data + blob.length, attr_name, len);
+ blob.length += len;
+
+ status = push_xattr_blob_tdb_raw(ea_tdb, XATTR_LIST_ATTR, fname, fd, &blob);
+ talloc_free(mem_ctx);
+
+ return status;
+}
+
+/*
+ form a key for using in the ea_tdb
+*/
+static NTSTATUS get_ea_tdb_key(TALLOC_CTX *mem_ctx,
+ const char *attr_name,
+ const char *fname, int fd,
+ TDB_DATA *key)
+{
+ struct stat st;
+ size_t len = strlen(attr_name);
+
+ if (fd == -1) {
+ if (stat(fname, &st) == -1) {
+ return NT_STATUS_NOT_FOUND;
+ }
+ } else {
+ if (fstat(fd, &st) == -1) {
+ return NT_STATUS_NOT_FOUND;
+ }
+ }
+
+ key->dptr = talloc_array(mem_ctx, uint8_t, 16 + len);
+ if (key->dptr == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ key->dsize = 16 + len;
+
+ SBVAL(key->dptr, 0, st.st_dev);
+ SBVAL(key->dptr, 8, st.st_ino);
+ memcpy(key->dptr+16, attr_name, len);
+
+ return NT_STATUS_OK;
+}
+
+
+
+/*
+ pull a xattr as a blob, using the ea_tdb_context tdb
+*/
+NTSTATUS pull_xattr_blob_tdb_raw(struct tdb_wrap *ea_tdb,
+ TALLOC_CTX *mem_ctx,
+ const char *attr_name,
+ const char *fname,
+ int fd,
+ size_t estimated_size,
+ DATA_BLOB *blob)
+{
+ TDB_DATA tkey, tdata;
+ NTSTATUS status;
+
+ status = get_ea_tdb_key(mem_ctx, attr_name, fname, fd, &tkey);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ tdata = tdb_fetch(ea_tdb->tdb, tkey);
+ if (tdata.dptr == NULL) {
+ return NT_STATUS_NOT_FOUND;
+ }
+
+ *blob = data_blob_talloc(mem_ctx, tdata.dptr, tdata.dsize);
+ free(tdata.dptr);
+ if (blob->data == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ push a xattr as a blob, using ea_tdb
+*/
+NTSTATUS push_xattr_blob_tdb_raw(struct tdb_wrap *ea_tdb,
+ const char *attr_name,
+ const char *fname,
+ int fd,
+ const DATA_BLOB *blob)
+{
+ TDB_DATA tkey, tdata;
+ NTSTATUS status;
+ TALLOC_CTX *mem_ctx = talloc_new(ea_tdb);
+ if (!mem_ctx) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = get_ea_tdb_key(mem_ctx, attr_name, fname, fd, &tkey);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(mem_ctx);
+ return status;
+ }
+
+ tdata.dptr = blob->data;
+ tdata.dsize = blob->length;
+
+ if (tdb_chainlock(ea_tdb->tdb, tkey) != 0) {
+ talloc_free(mem_ctx);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ status = posix_eadb_add_list(ea_tdb,mem_ctx, attr_name, fname, fd);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(mem_ctx);
+ goto done;
+ }
+
+ if (tdb_store(ea_tdb->tdb, tkey, tdata, TDB_REPLACE) != 0) {
+ status = NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+done:
+ tdb_chainunlock(ea_tdb->tdb, tkey);
+ talloc_free(mem_ctx);
+ return status;
+}
+
+
+/*
+ delete a xattr
+*/
+NTSTATUS delete_posix_eadb_raw(struct tdb_wrap *ea_tdb, const char *attr_name,
+ const char *fname, int fd)
+{
+ TDB_DATA tkey;
+ NTSTATUS status;
+
+ status = get_ea_tdb_key(NULL, attr_name, fname, fd, &tkey);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (tdb_delete(ea_tdb->tdb, tkey) != 0) {
+ talloc_free(tkey.dptr);
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ talloc_free(tkey.dptr);
+ return NT_STATUS_OK;
+}
+
+
+/*
+ delete all xattrs for a file
+*/
+NTSTATUS unlink_posix_eadb_raw(struct tdb_wrap *ea_tdb, const char *fname, int fd)
+{
+ TALLOC_CTX *mem_ctx = talloc_new(ea_tdb);
+ DATA_BLOB blob;
+ const char *s;
+ NTSTATUS status;
+
+ status = pull_xattr_blob_tdb_raw(ea_tdb, mem_ctx, XATTR_LIST_ATTR,
+ fname, fd, 100, &blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(mem_ctx);
+ return NT_STATUS_OK;
+ }
+
+ for (s=(const char *)blob.data; s < (const char *)(blob.data+blob.length); s += strlen(s) + 1) {
+ delete_posix_eadb_raw(ea_tdb, s, fname, -1);
+ }
+
+ status = delete_posix_eadb_raw(ea_tdb, XATTR_LIST_ATTR, fname, fd);
+ talloc_free(mem_ctx);
+ return status;
+}
+
+/*
+ list all xattrs for a file
+*/
+NTSTATUS list_posix_eadb_raw(struct tdb_wrap *ea_tdb, TALLOC_CTX *mem_ctx,
+ const char *fname, int fd,
+ DATA_BLOB *list)
+{
+ return pull_xattr_blob_tdb_raw(ea_tdb, mem_ctx, XATTR_LIST_ATTR,
+ fname, fd, 100, list);
+}
+
+#ifdef WITH_NTVFS_FILESERVER
+NTSTATUS pull_xattr_blob_tdb(struct pvfs_state *pvfs_state,
+ TALLOC_CTX *mem_ctx,
+ const char *attr_name,
+ const char *fname,
+ int fd,
+ size_t estimated_size,
+ DATA_BLOB *blob)
+{
+ return pull_xattr_blob_tdb_raw(pvfs_state->ea_db,mem_ctx,attr_name,fname,fd,estimated_size,blob);
+}
+
+NTSTATUS push_xattr_blob_tdb(struct pvfs_state *pvfs_state,
+ const char *attr_name,
+ const char *fname,
+ int fd,
+ const DATA_BLOB *blob)
+{
+ return push_xattr_blob_tdb_raw(pvfs_state->ea_db, attr_name, fname, fd, blob);
+}
+
+/*
+ delete a xattr
+*/
+NTSTATUS delete_posix_eadb(struct pvfs_state *pvfs_state, const char *attr_name,
+ const char *fname, int fd)
+{
+ return delete_posix_eadb_raw(pvfs_state->ea_db,
+ attr_name, fname, fd);
+}
+
+/*
+ delete all xattrs for a file
+*/
+NTSTATUS unlink_posix_eadb(struct pvfs_state *pvfs_state, const char *fname)
+{
+ return unlink_posix_eadb_raw(pvfs_state->ea_db, fname, -1);
+}
+
+#endif
diff --git a/source4/ntvfs/posix/posix_eadb.h b/source4/ntvfs/posix/posix_eadb.h
new file mode 100644
index 0000000..14b9439
--- /dev/null
+++ b/source4/ntvfs/posix/posix_eadb.h
@@ -0,0 +1,20 @@
+/*
+ Unix SMB/CIFS implementation. Xattr manipulation bindings.
+ Copyright (C) Andrew Bartlett 2011
+
+ 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/>.
+*/
+
+struct pvfs_state;
+#include "source4/ntvfs/posix/posix_eadb_proto.h"
diff --git a/source4/ntvfs/posix/pvfs_acl.c b/source4/ntvfs/posix/pvfs_acl.c
new file mode 100644
index 0000000..e643293
--- /dev/null
+++ b/source4/ntvfs/posix/pvfs_acl.c
@@ -0,0 +1,1083 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ POSIX NTVFS backend - ACL support
+
+ Copyright (C) Andrew Tridgell 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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 "system/passwd.h"
+#include "auth/auth.h"
+#include "vfs_posix.h"
+#include "librpc/gen_ndr/xattr.h"
+#include "libcli/security/security.h"
+#include "param/param.h"
+#include "../lib/util/unix_privs.h"
+#include "lib/util/samba_modules.h"
+
+/* the list of currently registered ACL backends */
+static struct pvfs_acl_backend {
+ const struct pvfs_acl_ops *ops;
+} *backends = NULL;
+static int num_backends;
+
+/*
+ register a pvfs acl backend.
+
+ The 'name' can be later used by other backends to find the operations
+ structure for this backend.
+*/
+NTSTATUS pvfs_acl_register(TALLOC_CTX *ctx, const struct pvfs_acl_ops *ops)
+{
+ struct pvfs_acl_ops *new_ops;
+
+ if (pvfs_acl_backend_byname(ops->name) != NULL) {
+ DEBUG(0,("pvfs acl backend '%s' already registered\n", ops->name));
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+
+ backends = talloc_realloc(ctx, backends,
+ struct pvfs_acl_backend, num_backends+1);
+ NT_STATUS_HAVE_NO_MEMORY(backends);
+
+ new_ops = (struct pvfs_acl_ops *)talloc_memdup(backends, ops, sizeof(*ops));
+ new_ops->name = talloc_strdup(new_ops, ops->name);
+
+ backends[num_backends].ops = new_ops;
+
+ num_backends++;
+
+ DEBUG(3,("NTVFS backend '%s' registered\n", ops->name));
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ return the operations structure for a named backend
+*/
+const struct pvfs_acl_ops *pvfs_acl_backend_byname(const char *name)
+{
+ int i;
+
+ for (i=0;i<num_backends;i++) {
+ if (strcmp(backends[i].ops->name, name) == 0) {
+ return backends[i].ops;
+ }
+ }
+
+ return NULL;
+}
+
+NTSTATUS pvfs_acl_init(void)
+{
+ static bool initialized = false;
+#define _MODULE_PROTO(init) extern NTSTATUS init(TALLOC_CTX *);
+ STATIC_pvfs_acl_MODULES_PROTO;
+ init_module_fn static_init[] = { STATIC_pvfs_acl_MODULES };
+ init_module_fn *shared_init;
+
+ if (initialized) return NT_STATUS_OK;
+ initialized = true;
+
+ shared_init = load_samba_modules(NULL, "pvfs_acl");
+
+ run_init_functions(NULL, static_init);
+ run_init_functions(NULL, shared_init);
+
+ talloc_free(shared_init);
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ map a single access_mask from generic to specific bits for files/dirs
+*/
+static uint32_t pvfs_translate_mask(uint32_t access_mask)
+{
+ if (access_mask & SEC_MASK_GENERIC) {
+ if (access_mask & SEC_GENERIC_READ) access_mask |= SEC_RIGHTS_FILE_READ;
+ if (access_mask & SEC_GENERIC_WRITE) access_mask |= SEC_RIGHTS_FILE_WRITE;
+ if (access_mask & SEC_GENERIC_EXECUTE) access_mask |= SEC_RIGHTS_FILE_EXECUTE;
+ if (access_mask & SEC_GENERIC_ALL) access_mask |= SEC_RIGHTS_FILE_ALL;
+ access_mask &= ~SEC_MASK_GENERIC;
+ }
+ return access_mask;
+}
+
+
+/*
+ map any generic access bits in the given acl
+ this relies on the fact that the mappings for files and directories
+ are the same
+*/
+static void pvfs_translate_generic_bits(struct security_acl *acl)
+{
+ unsigned i;
+
+ if (!acl) return;
+
+ for (i=0;i<acl->num_aces;i++) {
+ struct security_ace *ace = &acl->aces[i];
+ ace->access_mask = pvfs_translate_mask(ace->access_mask);
+ }
+}
+
+
+/*
+ setup a default ACL for a file
+*/
+static NTSTATUS pvfs_default_acl(struct pvfs_state *pvfs,
+ struct ntvfs_request *req,
+ struct pvfs_filename *name, int fd,
+ struct security_descriptor **psd)
+{
+ struct security_descriptor *sd;
+ NTSTATUS status;
+ struct security_ace ace = {};
+ mode_t mode;
+ struct id_map *ids;
+
+ *psd = security_descriptor_initialise(req);
+ if (*psd == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ sd = *psd;
+
+ ids = talloc_zero_array(sd, struct id_map, 2);
+ NT_STATUS_HAVE_NO_MEMORY(ids);
+
+ ids[0].xid.id = name->st.st_uid;
+ ids[0].xid.type = ID_TYPE_UID;
+ ids[0].sid = NULL;
+
+ ids[1].xid.id = name->st.st_gid;
+ ids[1].xid.type = ID_TYPE_GID;
+ ids[1].sid = NULL;
+
+ status = wbc_xids_to_sids(ids, 2);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ sd->owner_sid = talloc_steal(sd, ids[0].sid);
+ sd->group_sid = talloc_steal(sd, ids[1].sid);
+
+ talloc_free(ids);
+ sd->type |= SEC_DESC_DACL_PRESENT;
+
+ mode = name->st.st_mode;
+
+ /*
+ we provide up to 4 ACEs
+ - Owner
+ - Group
+ - Everyone
+ - Administrator
+ */
+
+
+ /* setup owner ACE */
+ ace.type = SEC_ACE_TYPE_ACCESS_ALLOWED;
+ ace.flags = 0;
+ ace.trustee = *sd->owner_sid;
+ ace.access_mask = 0;
+
+ if (mode & S_IRUSR) {
+ if (mode & S_IWUSR) {
+ ace.access_mask |= SEC_RIGHTS_FILE_ALL;
+ } else {
+ ace.access_mask |= SEC_RIGHTS_FILE_READ | SEC_FILE_EXECUTE;
+ }
+ }
+ if (mode & S_IWUSR) {
+ ace.access_mask |= SEC_RIGHTS_FILE_WRITE | SEC_STD_DELETE;
+ }
+ if (ace.access_mask) {
+ security_descriptor_dacl_add(sd, &ace);
+ }
+
+
+ /* setup group ACE */
+ ace.trustee = *sd->group_sid;
+ ace.access_mask = 0;
+ if (mode & S_IRGRP) {
+ ace.access_mask |= SEC_RIGHTS_FILE_READ | SEC_FILE_EXECUTE;
+ }
+ if (mode & S_IWGRP) {
+ /* note that delete is not granted - this matches posix behaviour */
+ ace.access_mask |= SEC_RIGHTS_FILE_WRITE;
+ }
+ if (ace.access_mask) {
+ security_descriptor_dacl_add(sd, &ace);
+ }
+
+ /* setup other ACE */
+ ace.trustee = global_sid_World;
+ ace.access_mask = 0;
+ if (mode & S_IROTH) {
+ ace.access_mask |= SEC_RIGHTS_FILE_READ | SEC_FILE_EXECUTE;
+ }
+ if (mode & S_IWOTH) {
+ ace.access_mask |= SEC_RIGHTS_FILE_WRITE;
+ }
+ if (ace.access_mask) {
+ security_descriptor_dacl_add(sd, &ace);
+ }
+
+ /* setup system ACE */
+ ace.trustee = global_sid_System;
+ ace.access_mask = SEC_RIGHTS_FILE_ALL;
+ security_descriptor_dacl_add(sd, &ace);
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ omit any security_descriptor elements not specified in the given
+ secinfo flags
+*/
+static void normalise_sd_flags(struct security_descriptor *sd, uint32_t secinfo_flags)
+{
+ if (!(secinfo_flags & SECINFO_OWNER)) {
+ sd->owner_sid = NULL;
+ }
+ if (!(secinfo_flags & SECINFO_GROUP)) {
+ sd->group_sid = NULL;
+ }
+ if (!(secinfo_flags & SECINFO_DACL)) {
+ sd->dacl = NULL;
+ }
+ if (!(secinfo_flags & SECINFO_SACL)) {
+ sd->sacl = NULL;
+ }
+}
+
+static bool pvfs_privileged_access(uid_t uid)
+{
+ uid_t euid;
+
+ if (uid_wrapper_enabled()) {
+ setenv("UID_WRAPPER_MYUID", "1", 1);
+ }
+
+ euid = geteuid();
+
+ if (uid_wrapper_enabled()) {
+ unsetenv("UID_WRAPPER_MYUID");
+ }
+
+ return (uid == euid);
+}
+
+/*
+ answer a setfileinfo for an ACL
+*/
+NTSTATUS pvfs_acl_set(struct pvfs_state *pvfs,
+ struct ntvfs_request *req,
+ struct pvfs_filename *name, int fd,
+ uint32_t access_mask,
+ union smb_setfileinfo *info)
+{
+ uint32_t secinfo_flags = info->set_secdesc.in.secinfo_flags;
+ struct security_descriptor *new_sd, *sd, orig_sd;
+ NTSTATUS status = NT_STATUS_NOT_FOUND;
+ uid_t old_uid = -1;
+ gid_t old_gid = -1;
+ uid_t new_uid = -1;
+ gid_t new_gid = -1;
+ struct id_map *ids;
+
+ if (pvfs->acl_ops != NULL) {
+ status = pvfs->acl_ops->acl_load(pvfs, name, fd, req, &sd);
+ }
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+ status = pvfs_default_acl(pvfs, req, name, fd, &sd);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ ids = talloc(req, struct id_map);
+ NT_STATUS_HAVE_NO_MEMORY(ids);
+ ZERO_STRUCT(ids->xid);
+ ids->sid = NULL;
+ ids->status = ID_UNKNOWN;
+
+ new_sd = info->set_secdesc.in.sd;
+ orig_sd = *sd;
+
+ old_uid = name->st.st_uid;
+ old_gid = name->st.st_gid;
+
+ /* only set the elements that have been specified */
+ if (secinfo_flags & SECINFO_OWNER) {
+ if (!(access_mask & SEC_STD_WRITE_OWNER)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ if (!dom_sid_equal(sd->owner_sid, new_sd->owner_sid)) {
+ ids->sid = new_sd->owner_sid;
+ status = wbc_sids_to_xids(ids, 1);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ if (ids->xid.type == ID_TYPE_BOTH ||
+ ids->xid.type == ID_TYPE_UID) {
+ new_uid = ids->xid.id;
+ }
+ }
+ sd->owner_sid = new_sd->owner_sid;
+ }
+
+ if (secinfo_flags & SECINFO_GROUP) {
+ if (!(access_mask & SEC_STD_WRITE_OWNER)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ if (!dom_sid_equal(sd->group_sid, new_sd->group_sid)) {
+ ids->sid = new_sd->group_sid;
+ status = wbc_sids_to_xids(ids, 1);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ if (ids->xid.type == ID_TYPE_BOTH ||
+ ids->xid.type == ID_TYPE_GID) {
+ new_gid = ids->xid.id;
+ }
+
+ }
+ sd->group_sid = new_sd->group_sid;
+ }
+
+ if (secinfo_flags & SECINFO_DACL) {
+ if (!(access_mask & SEC_STD_WRITE_DAC)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ sd->dacl = new_sd->dacl;
+ pvfs_translate_generic_bits(sd->dacl);
+ sd->type |= SEC_DESC_DACL_PRESENT;
+ }
+
+ if (secinfo_flags & SECINFO_SACL) {
+ if (!(access_mask & SEC_FLAG_SYSTEM_SECURITY)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ sd->sacl = new_sd->sacl;
+ pvfs_translate_generic_bits(sd->sacl);
+ sd->type |= SEC_DESC_SACL_PRESENT;
+ }
+
+ if (secinfo_flags & SECINFO_PROTECTED_DACL) {
+ if (new_sd->type & SEC_DESC_DACL_PROTECTED) {
+ sd->type |= SEC_DESC_DACL_PROTECTED;
+ } else {
+ sd->type &= ~SEC_DESC_DACL_PROTECTED;
+ }
+ }
+
+ if (secinfo_flags & SECINFO_PROTECTED_SACL) {
+ if (new_sd->type & SEC_DESC_SACL_PROTECTED) {
+ sd->type |= SEC_DESC_SACL_PROTECTED;
+ } else {
+ sd->type &= ~SEC_DESC_SACL_PROTECTED;
+ }
+ }
+
+ if (new_uid == old_uid) {
+ new_uid = -1;
+ }
+
+ if (new_gid == old_gid) {
+ new_gid = -1;
+ }
+
+ /* if there's something to change try it */
+ if (new_uid != -1 || new_gid != -1) {
+ int ret;
+ if (fd == -1) {
+ ret = chown(name->full_name, new_uid, new_gid);
+ } else {
+ ret = fchown(fd, new_uid, new_gid);
+ }
+ if (errno == EPERM) {
+ if (pvfs_privileged_access(name->st.st_uid)) {
+ ret = 0;
+ } else {
+ /* try again as root if we have SEC_PRIV_RESTORE or
+ SEC_PRIV_TAKE_OWNERSHIP */
+ if (security_token_has_privilege(req->session_info->security_token,
+ SEC_PRIV_RESTORE) ||
+ security_token_has_privilege(req->session_info->security_token,
+ SEC_PRIV_TAKE_OWNERSHIP)) {
+ void *privs;
+ privs = root_privileges();
+ if (fd == -1) {
+ ret = chown(name->full_name, new_uid, new_gid);
+ } else {
+ ret = fchown(fd, new_uid, new_gid);
+ }
+ talloc_free(privs);
+ }
+ }
+ }
+ if (ret == -1) {
+ return pvfs_map_errno(pvfs, errno);
+ }
+ }
+
+ /* we avoid saving if the sd is the same. This means when clients
+ copy files and end up copying the default sd that we don't
+ needlessly use xattrs */
+ if (!security_descriptor_equal(sd, &orig_sd) && pvfs->acl_ops) {
+ status = pvfs->acl_ops->acl_save(pvfs, name, fd, sd);
+ }
+
+ return status;
+}
+
+
+/*
+ answer a fileinfo query for the ACL
+*/
+NTSTATUS pvfs_acl_query(struct pvfs_state *pvfs,
+ struct ntvfs_request *req,
+ struct pvfs_filename *name, int fd,
+ union smb_fileinfo *info)
+{
+ NTSTATUS status = NT_STATUS_NOT_FOUND;
+ struct security_descriptor *sd;
+
+ if (pvfs->acl_ops) {
+ status = pvfs->acl_ops->acl_load(pvfs, name, fd, req, &sd);
+ }
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+ status = pvfs_default_acl(pvfs, req, name, fd, &sd);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ normalise_sd_flags(sd, info->query_secdesc.in.secinfo_flags);
+
+ info->query_secdesc.out.sd = sd;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ check the read only bit against any of the write access bits
+*/
+static bool pvfs_read_only(struct pvfs_state *pvfs, uint32_t access_mask)
+{
+ if ((pvfs->flags & PVFS_FLAG_READONLY) &&
+ (access_mask & (SEC_FILE_WRITE_DATA |
+ SEC_FILE_APPEND_DATA |
+ SEC_FILE_WRITE_EA |
+ SEC_FILE_WRITE_ATTRIBUTE |
+ SEC_STD_DELETE |
+ SEC_STD_WRITE_DAC |
+ SEC_STD_WRITE_OWNER |
+ SEC_DIR_DELETE_CHILD))) {
+ return true;
+ }
+ return false;
+}
+
+/*
+ see if we are a member of the appropriate unix group
+ */
+static bool pvfs_group_member(struct pvfs_state *pvfs, gid_t gid)
+{
+ int i, ngroups;
+ gid_t *groups;
+ if (getegid() == gid) {
+ return true;
+ }
+ ngroups = getgroups(0, NULL);
+ if (ngroups <= 0) {
+ return false;
+ }
+ groups = talloc_array(pvfs, gid_t, ngroups);
+ if (groups == NULL) {
+ return false;
+ }
+ if (getgroups(ngroups, groups) != ngroups) {
+ talloc_free(groups);
+ return false;
+ }
+ for (i=0; i<ngroups; i++) {
+ if (groups[i] == gid) break;
+ }
+ talloc_free(groups);
+ return i < ngroups;
+}
+
+/*
+ default access check function based on unix permissions
+ doing this saves on building a full security descriptor
+ for the common case of access check on files with no
+ specific NT ACL
+
+ If name is NULL then treat as a new file creation
+*/
+static NTSTATUS pvfs_access_check_unix(struct pvfs_state *pvfs,
+ struct ntvfs_request *req,
+ struct pvfs_filename *name,
+ uint32_t *access_mask)
+{
+ uint32_t max_bits = 0;
+ struct security_token *token = req->session_info->security_token;
+
+ if (pvfs_read_only(pvfs, *access_mask)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (name == NULL) {
+ max_bits |= SEC_RIGHTS_FILE_ALL | SEC_STD_ALL;
+ } else if (pvfs_privileged_access(name->st.st_uid)) {
+ /* use the IxUSR bits */
+ if ((name->st.st_mode & S_IWUSR)) {
+ max_bits |= SEC_RIGHTS_FILE_ALL | SEC_STD_ALL;
+ } else if ((name->st.st_mode & (S_IRUSR | S_IXUSR))) {
+ max_bits |= SEC_RIGHTS_FILE_READ | SEC_RIGHTS_FILE_EXECUTE | SEC_STD_ALL;
+ }
+ } else if (pvfs_group_member(pvfs, name->st.st_gid)) {
+ /* use the IxGRP bits */
+ if ((name->st.st_mode & S_IWGRP)) {
+ max_bits |= SEC_RIGHTS_FILE_ALL | SEC_STD_ALL;
+ } else if ((name->st.st_mode & (S_IRGRP | S_IXGRP))) {
+ max_bits |= SEC_RIGHTS_FILE_READ | SEC_RIGHTS_FILE_EXECUTE | SEC_STD_ALL;
+ }
+ } else {
+ /* use the IxOTH bits */
+ if ((name->st.st_mode & S_IWOTH)) {
+ max_bits |= SEC_RIGHTS_FILE_ALL | SEC_STD_ALL;
+ } else if ((name->st.st_mode & (S_IROTH | S_IXOTH))) {
+ max_bits |= SEC_RIGHTS_FILE_READ | SEC_RIGHTS_FILE_EXECUTE | SEC_STD_ALL;
+ }
+ }
+
+ if (*access_mask & SEC_FLAG_MAXIMUM_ALLOWED) {
+ *access_mask |= max_bits;
+ *access_mask &= ~SEC_FLAG_MAXIMUM_ALLOWED;
+ }
+
+ if ((*access_mask & SEC_FLAG_SYSTEM_SECURITY) &&
+ security_token_has_privilege(token, SEC_PRIV_SECURITY)) {
+ max_bits |= SEC_FLAG_SYSTEM_SECURITY;
+ }
+
+ if (((*access_mask & ~max_bits) & SEC_RIGHTS_PRIV_RESTORE) &&
+ security_token_has_privilege(token, SEC_PRIV_RESTORE)) {
+ max_bits |= ~(SEC_RIGHTS_PRIV_RESTORE);
+ }
+ if (((*access_mask & ~max_bits) & SEC_RIGHTS_PRIV_BACKUP) &&
+ security_token_has_privilege(token, SEC_PRIV_BACKUP)) {
+ max_bits |= ~(SEC_RIGHTS_PRIV_BACKUP);
+ }
+
+ if (*access_mask & ~max_bits) {
+ DEBUG(5,(__location__ " denied access to '%s' - wanted 0x%08x but got 0x%08x (missing 0x%08x)\n",
+ name?name->full_name:"(new file)", *access_mask, max_bits, *access_mask & ~max_bits));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (pvfs->ntvfs->ctx->protocol < PROTOCOL_SMB2_02) {
+ /* on SMB, this bit is always granted, even if not
+ asked for */
+ *access_mask |= SEC_FILE_READ_ATTRIBUTE;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ check the security descriptor on a file, if any
+
+ *access_mask is modified with the access actually granted
+*/
+NTSTATUS pvfs_access_check(struct pvfs_state *pvfs,
+ struct ntvfs_request *req,
+ struct pvfs_filename *name,
+ uint32_t *access_mask)
+{
+ struct security_token *token = req->session_info->security_token;
+ struct xattr_NTACL *acl;
+ NTSTATUS status;
+ struct security_descriptor *sd;
+ bool allow_delete = false;
+
+ /* on SMB2 a blank access mask is always denied */
+ if (pvfs->ntvfs->ctx->protocol >= PROTOCOL_SMB2_02 &&
+ *access_mask == 0) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (pvfs_read_only(pvfs, *access_mask)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (*access_mask & SEC_FLAG_MAXIMUM_ALLOWED ||
+ *access_mask & SEC_STD_DELETE) {
+ status = pvfs_access_check_parent(pvfs, req,
+ name, SEC_DIR_DELETE_CHILD);
+ if (NT_STATUS_IS_OK(status)) {
+ allow_delete = true;
+ *access_mask &= ~SEC_STD_DELETE;
+ }
+ }
+
+ acl = talloc(req, struct xattr_NTACL);
+ if (acl == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* expand the generic access bits to file specific bits */
+ *access_mask = pvfs_translate_mask(*access_mask);
+ if (pvfs->ntvfs->ctx->protocol < PROTOCOL_SMB2_02) {
+ *access_mask &= ~SEC_FILE_READ_ATTRIBUTE;
+ }
+
+ status = pvfs_acl_load(pvfs, name, -1, acl);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+ talloc_free(acl);
+ status = pvfs_access_check_unix(pvfs, req, name, access_mask);
+ goto done;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ switch (acl->version) {
+ case 1:
+ sd = acl->info.sd;
+ break;
+ default:
+ return NT_STATUS_INVALID_ACL;
+ }
+
+ /* check the acl against the required access mask */
+ status = se_file_access_check(sd, token, false, *access_mask, access_mask);
+ talloc_free(acl);
+
+ /* if we used a NT acl, then allow access override if the
+ share allows for posix permission override
+ */
+ if (NT_STATUS_IS_OK(status)) {
+ name->allow_override = (pvfs->flags & PVFS_FLAG_PERM_OVERRIDE) != 0;
+ }
+
+done:
+ if (pvfs->ntvfs->ctx->protocol < PROTOCOL_SMB2_02) {
+ /* on SMB, this bit is always granted, even if not
+ asked for */
+ *access_mask |= SEC_FILE_READ_ATTRIBUTE;
+ }
+
+ if (allow_delete) {
+ *access_mask |= SEC_STD_DELETE;
+ }
+
+ return status;
+}
+
+
+/*
+ a simplified interface to access check, designed for calls that
+ do not take or return an access check mask
+*/
+NTSTATUS pvfs_access_check_simple(struct pvfs_state *pvfs,
+ struct ntvfs_request *req,
+ struct pvfs_filename *name,
+ uint32_t access_needed)
+{
+ if (access_needed == 0) {
+ return NT_STATUS_OK;
+ }
+ return pvfs_access_check(pvfs, req, name, &access_needed);
+}
+
+/*
+ access check for creating a new file/directory
+*/
+NTSTATUS pvfs_access_check_create(struct pvfs_state *pvfs,
+ struct ntvfs_request *req,
+ struct pvfs_filename *name,
+ uint32_t *access_mask,
+ bool container,
+ struct security_descriptor **sd)
+{
+ struct pvfs_filename *parent;
+ NTSTATUS status;
+ uint32_t parent_mask;
+ bool allow_delete = false;
+
+ if (pvfs_read_only(pvfs, *access_mask)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ status = pvfs_resolve_parent(pvfs, req, name, &parent);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ if (container) {
+ parent_mask = SEC_DIR_ADD_SUBDIR;
+ } else {
+ parent_mask = SEC_DIR_ADD_FILE;
+ }
+ if (*access_mask & SEC_FLAG_MAXIMUM_ALLOWED ||
+ *access_mask & SEC_STD_DELETE) {
+ parent_mask |= SEC_DIR_DELETE_CHILD;
+ }
+
+ status = pvfs_access_check(pvfs, req, parent, &parent_mask);
+ if (NT_STATUS_IS_OK(status)) {
+ if (parent_mask & SEC_DIR_DELETE_CHILD) {
+ allow_delete = true;
+ }
+ } else if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
+ /*
+ * on ACCESS_DENIED we get the rejected bits
+ * remove the non critical SEC_DIR_DELETE_CHILD
+ * and check if something else was rejected.
+ */
+ parent_mask &= ~SEC_DIR_DELETE_CHILD;
+ if (parent_mask != 0) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ status = NT_STATUS_OK;
+ } else {
+ return status;
+ }
+
+ if (*sd == NULL) {
+ status = pvfs_acl_inherited_sd(pvfs, req, req, parent, container, sd);
+ }
+
+ talloc_free(parent);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* expand the generic access bits to file specific bits */
+ *access_mask = pvfs_translate_mask(*access_mask);
+
+ if (*access_mask & SEC_FLAG_MAXIMUM_ALLOWED) {
+ *access_mask |= SEC_RIGHTS_FILE_ALL;
+ *access_mask &= ~SEC_FLAG_MAXIMUM_ALLOWED;
+ }
+
+ if (pvfs->ntvfs->ctx->protocol < PROTOCOL_SMB2_02) {
+ /* on SMB, this bit is always granted, even if not
+ asked for */
+ *access_mask |= SEC_FILE_READ_ATTRIBUTE;
+ }
+
+ if (allow_delete) {
+ *access_mask |= SEC_STD_DELETE;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ access check for creating a new file/directory - no access mask supplied
+*/
+NTSTATUS pvfs_access_check_parent(struct pvfs_state *pvfs,
+ struct ntvfs_request *req,
+ struct pvfs_filename *name,
+ uint32_t access_mask)
+{
+ struct pvfs_filename *parent;
+ NTSTATUS status;
+
+ status = pvfs_resolve_parent(pvfs, req, name, &parent);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = pvfs_access_check_simple(pvfs, req, parent, access_mask);
+ if (NT_STATUS_IS_OK(status) && parent->allow_override) {
+ name->allow_override = true;
+ }
+ return status;
+}
+
+
+/*
+ determine if an ACE is inheritable
+*/
+static bool pvfs_inheritable_ace(struct pvfs_state *pvfs,
+ const struct security_ace *ace,
+ bool container)
+{
+ if (!container) {
+ return (ace->flags & SEC_ACE_FLAG_OBJECT_INHERIT) != 0;
+ }
+
+ if (ace->flags & SEC_ACE_FLAG_CONTAINER_INHERIT) {
+ return true;
+ }
+
+ if ((ace->flags & SEC_ACE_FLAG_OBJECT_INHERIT) &&
+ !(ace->flags & SEC_ACE_FLAG_NO_PROPAGATE_INHERIT)) {
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ this is the core of ACL inheritance. It copies any inheritable
+ aces from the parent SD to the child SD. Note that the algorithm
+ depends on whether the child is a container or not
+*/
+static NTSTATUS pvfs_acl_inherit_aces(struct pvfs_state *pvfs,
+ struct security_descriptor *parent_sd,
+ struct security_descriptor *sd,
+ bool container)
+{
+ int i;
+
+ for (i=0;i<parent_sd->dacl->num_aces;i++) {
+ struct security_ace ace = parent_sd->dacl->aces[i];
+ NTSTATUS status;
+ const struct dom_sid *creator = NULL, *new_id = NULL;
+ uint32_t orig_flags;
+
+ if (!pvfs_inheritable_ace(pvfs, &ace, container)) {
+ continue;
+ }
+
+ orig_flags = ace.flags;
+
+ /* see the RAW-ACLS inheritance test for details on these rules */
+ if (!container) {
+ ace.flags = 0;
+ } else {
+ ace.flags &= ~SEC_ACE_FLAG_INHERIT_ONLY;
+
+ if (!(ace.flags & SEC_ACE_FLAG_CONTAINER_INHERIT)) {
+ ace.flags |= SEC_ACE_FLAG_INHERIT_ONLY;
+ }
+ if (ace.flags & SEC_ACE_FLAG_NO_PROPAGATE_INHERIT) {
+ ace.flags = 0;
+ }
+ }
+
+ /* the CREATOR sids are special when inherited */
+ if (dom_sid_equal(&ace.trustee, pvfs->sid_cache.creator_owner)) {
+ creator = pvfs->sid_cache.creator_owner;
+ new_id = sd->owner_sid;
+ } else if (dom_sid_equal(&ace.trustee, pvfs->sid_cache.creator_group)) {
+ creator = pvfs->sid_cache.creator_group;
+ new_id = sd->group_sid;
+ } else {
+ new_id = &ace.trustee;
+ }
+
+ if (creator && container &&
+ (ace.flags & SEC_ACE_FLAG_CONTAINER_INHERIT)) {
+ uint32_t flags = ace.flags;
+
+ ace.trustee = *new_id;
+ ace.flags = 0;
+ status = security_descriptor_dacl_add(sd, &ace);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ ace.trustee = *creator;
+ ace.flags = flags | SEC_ACE_FLAG_INHERIT_ONLY;
+ status = security_descriptor_dacl_add(sd, &ace);
+ } else if (container &&
+ !(orig_flags & SEC_ACE_FLAG_NO_PROPAGATE_INHERIT)) {
+ status = security_descriptor_dacl_add(sd, &ace);
+ } else {
+ ace.trustee = *new_id;
+ status = security_descriptor_dacl_add(sd, &ace);
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+
+/*
+ calculate the ACL on a new file/directory based on the inherited ACL
+ from the parent. If there is no inherited ACL then return a NULL
+ ACL, which means the default ACL should be used
+*/
+NTSTATUS pvfs_acl_inherited_sd(struct pvfs_state *pvfs,
+ TALLOC_CTX *mem_ctx,
+ struct ntvfs_request *req,
+ struct pvfs_filename *parent,
+ bool container,
+ struct security_descriptor **ret_sd)
+{
+ struct xattr_NTACL *acl;
+ NTSTATUS status;
+ struct security_descriptor *parent_sd, *sd;
+ struct id_map *ids;
+ TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx);
+
+ *ret_sd = NULL;
+
+ acl = talloc(req, struct xattr_NTACL);
+ if (acl == NULL) {
+ TALLOC_FREE(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = pvfs_acl_load(pvfs, parent, -1, acl);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+ talloc_free(tmp_ctx);
+ return NT_STATUS_OK;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(tmp_ctx);
+ return status;
+ }
+
+ switch (acl->version) {
+ case 1:
+ parent_sd = acl->info.sd;
+ break;
+ default:
+ talloc_free(tmp_ctx);
+ return NT_STATUS_INVALID_ACL;
+ }
+
+ if (parent_sd == NULL ||
+ parent_sd->dacl == NULL ||
+ parent_sd->dacl->num_aces == 0) {
+ /* go with the default ACL */
+ talloc_free(tmp_ctx);
+ return NT_STATUS_OK;
+ }
+
+ /* create the new sd */
+ sd = security_descriptor_initialise(req);
+ if (sd == NULL) {
+ TALLOC_FREE(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ids = talloc_array(sd, struct id_map, 2);
+ if (ids == NULL) {
+ TALLOC_FREE(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ids[0].xid.id = geteuid();
+ ids[0].xid.type = ID_TYPE_UID;
+ ids[0].sid = NULL;
+ ids[0].status = ID_UNKNOWN;
+
+ ids[1].xid.id = getegid();
+ ids[1].xid.type = ID_TYPE_GID;
+ ids[1].sid = NULL;
+ ids[1].status = ID_UNKNOWN;
+
+ status = wbc_xids_to_sids(ids, 2);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(tmp_ctx);
+ return status;
+ }
+
+ sd->owner_sid = talloc_steal(sd, ids[0].sid);
+ sd->group_sid = talloc_steal(sd, ids[1].sid);
+
+ sd->type |= SEC_DESC_DACL_PRESENT;
+
+ /* fill in the aces from the parent */
+ status = pvfs_acl_inherit_aces(pvfs, parent_sd, sd, container);
+ if (!NT_STATUS_IS_OK(status)) {
+ TALLOC_FREE(tmp_ctx);
+ return status;
+ }
+
+ /* if there is nothing to inherit then we fallback to the
+ default acl */
+ if (sd->dacl == NULL || sd->dacl->num_aces == 0) {
+ talloc_free(tmp_ctx);
+ return NT_STATUS_OK;
+ }
+
+ *ret_sd = talloc_steal(mem_ctx, sd);
+
+ talloc_free(tmp_ctx);
+ return NT_STATUS_OK;
+}
+
+
+/*
+ setup an ACL on a new file/directory based on the inherited ACL from
+ the parent. If there is no inherited ACL then we don't set anything,
+ as the default ACL applies anyway
+*/
+NTSTATUS pvfs_acl_inherit(struct pvfs_state *pvfs,
+ struct ntvfs_request *req,
+ struct pvfs_filename *name,
+ int fd)
+{
+ struct xattr_NTACL acl;
+ NTSTATUS status;
+ struct security_descriptor *sd;
+ struct pvfs_filename *parent;
+ bool container;
+
+ /* form the parents path */
+ status = pvfs_resolve_parent(pvfs, req, name, &parent);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ container = (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) ? true:false;
+
+ status = pvfs_acl_inherited_sd(pvfs, req, req, parent, container, &sd);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(parent);
+ return status;
+ }
+
+ if (sd == NULL) {
+ return NT_STATUS_OK;
+ }
+
+ acl.version = 1;
+ acl.info.sd = sd;
+
+ status = pvfs_acl_save(pvfs, name, fd, &acl);
+ talloc_free(sd);
+ talloc_free(parent);
+
+ return status;
+}
+
+/*
+ return the maximum allowed access mask
+*/
+NTSTATUS pvfs_access_maximal_allowed(struct pvfs_state *pvfs,
+ struct ntvfs_request *req,
+ struct pvfs_filename *name,
+ uint32_t *maximal_access)
+{
+ *maximal_access = SEC_FLAG_MAXIMUM_ALLOWED;
+ return pvfs_access_check(pvfs, req, name, maximal_access);
+}
diff --git a/source4/ntvfs/posix/pvfs_acl_nfs4.c b/source4/ntvfs/posix/pvfs_acl_nfs4.c
new file mode 100644
index 0000000..3ddc1b6
--- /dev/null
+++ b/source4/ntvfs/posix/pvfs_acl_nfs4.c
@@ -0,0 +1,199 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ POSIX NTVFS backend - NT ACLs mapped to NFS4 ACLs, as per
+ http://www.suse.de/~agruen/nfs4acl/
+
+ Copyright (C) Andrew Tridgell 2006
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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 "vfs_posix.h"
+#include "../lib/util/unix_privs.h"
+#include "librpc/gen_ndr/ndr_nfs4acl.h"
+#include "libcli/security/security.h"
+
+NTSTATUS pvfs_acl_nfs4_init(TALLOC_CTX *);
+
+#define ACE4_IDENTIFIER_GROUP 0x40
+
+/*
+ load the current ACL from system.nfs4acl
+*/
+static NTSTATUS pvfs_acl_load_nfs4(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd,
+ TALLOC_CTX *mem_ctx,
+ struct security_descriptor **psd)
+{
+ NTSTATUS status;
+ struct nfs4acl *acl;
+ struct security_descriptor *sd;
+ int i, num_ids;
+ struct id_map *ids;
+
+ acl = talloc_zero(mem_ctx, struct nfs4acl);
+ NT_STATUS_HAVE_NO_MEMORY(acl);
+
+ status = pvfs_xattr_ndr_load(pvfs, mem_ctx, name->full_name, fd,
+ NFS4ACL_NDR_XATTR_NAME,
+ acl, (void *) ndr_pull_nfs4acl);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(acl);
+ return status;
+ }
+
+ *psd = security_descriptor_initialise(mem_ctx);
+ NT_STATUS_HAVE_NO_MEMORY(*psd);
+
+ sd = *psd;
+
+ sd->type |= acl->a_flags;
+
+ /* the number of ids to map is the acl count plus uid and gid */
+ num_ids = acl->a_count +2;
+ ids = talloc_array(sd, struct id_map, num_ids);
+ NT_STATUS_HAVE_NO_MEMORY(ids);
+
+ ids[0].xid.id = name->st.st_uid;
+ ids[0].xid.type = ID_TYPE_UID;
+ ids[0].sid = NULL;
+ ids[0].status = ID_UNKNOWN;
+
+ ids[1].xid.id = name->st.st_gid;
+ ids[1].xid.type = ID_TYPE_GID;
+ ids[1].sid = NULL;
+ ids[1].status = ID_UNKNOWN;
+
+ for (i=0;i<acl->a_count;i++) {
+ struct nfs4ace *a = &acl->ace[i];
+ ids[i+2].xid.id = a->e_id;
+ if (a->e_flags & ACE4_IDENTIFIER_GROUP) {
+ ids[i+2].xid.type = ID_TYPE_GID;
+ } else {
+ ids[i+2].xid.type = ID_TYPE_UID;
+ }
+ ids[i+2].sid = NULL;
+ ids[i+2].status = ID_UNKNOWN;
+ }
+
+ /* Allocate memory for the sids from the security descriptor to be on
+ * the safe side. */
+ status = wbc_xids_to_sids(ids, num_ids);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ sd->owner_sid = talloc_steal(sd, ids[0].sid);
+ sd->group_sid = talloc_steal(sd, ids[1].sid);
+
+ for (i=0;i<acl->a_count;i++) {
+ struct nfs4ace *a = &acl->ace[i];
+ struct security_ace ace = {};
+ ace.type = a->e_type;
+ ace.flags = a->e_flags;
+ ace.access_mask = a->e_mask;
+ ace.trustee = *ids[i+2].sid;
+ security_descriptor_dacl_add(sd, &ace);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ save the acl for a file into system.nfs4acl
+*/
+static NTSTATUS pvfs_acl_save_nfs4(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd,
+ struct security_descriptor *sd)
+{
+ NTSTATUS status;
+ void *privs;
+ struct nfs4acl acl;
+ int i;
+ TALLOC_CTX *tmp_ctx;
+ struct id_map *ids;
+
+ tmp_ctx = talloc_new(pvfs);
+ NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
+
+ acl.a_version = 0;
+ acl.a_flags = sd->type;
+ acl.a_count = sd->dacl?sd->dacl->num_aces:0;
+ acl.a_owner_mask = 0;
+ acl.a_group_mask = 0;
+ acl.a_other_mask = 0;
+
+ acl.ace = talloc_array(tmp_ctx, struct nfs4ace, acl.a_count);
+ if (!acl.ace) {
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ids = talloc_array(tmp_ctx, struct id_map, acl.a_count);
+ if (ids == NULL) {
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (i=0;i<acl.a_count;i++) {
+ struct security_ace *ace = &sd->dacl->aces[i];
+ ZERO_STRUCT(ids[i].xid);
+ ids[i].sid = dom_sid_dup(ids, &ace->trustee);
+ if (ids[i].sid == NULL) {
+ talloc_free(tmp_ctx);
+ return NT_STATUS_NO_MEMORY;
+ }
+ ids[i].status = ID_UNKNOWN;
+ }
+
+ status = wbc_sids_to_xids(ids, acl.a_count);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(tmp_ctx);
+ return status;
+ }
+
+ for (i=0;i<acl.a_count;i++) {
+ struct nfs4ace *a = &acl.ace[i];
+ struct security_ace *ace = &sd->dacl->aces[i];
+ a->e_type = ace->type;
+ a->e_flags = ace->flags;
+ a->e_mask = ace->access_mask;
+ if (ids[i].xid.type != ID_TYPE_UID) {
+ a->e_flags |= ACE4_IDENTIFIER_GROUP;
+ }
+ a->e_id = ids[i].xid.id;
+ a->e_who = "";
+ }
+
+ privs = root_privileges();
+ status = pvfs_xattr_ndr_save(pvfs, name->full_name, fd,
+ NFS4ACL_NDR_XATTR_NAME,
+ &acl, (void *) ndr_push_nfs4acl);
+ talloc_free(privs);
+
+ talloc_free(tmp_ctx);
+ return status;
+}
+
+
+/*
+ initialise pvfs acl NFS4 backend
+*/
+NTSTATUS pvfs_acl_nfs4_init(TALLOC_CTX *ctx)
+{
+ struct pvfs_acl_ops ops = {
+ .name = "nfs4acl",
+ .acl_load = pvfs_acl_load_nfs4,
+ .acl_save = pvfs_acl_save_nfs4
+ };
+ return pvfs_acl_register(ctx, &ops);
+}
diff --git a/source4/ntvfs/posix/pvfs_acl_xattr.c b/source4/ntvfs/posix/pvfs_acl_xattr.c
new file mode 100644
index 0000000..1f569ca
--- /dev/null
+++ b/source4/ntvfs/posix/pvfs_acl_xattr.c
@@ -0,0 +1,104 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ POSIX NTVFS backend - NT ACLs in xattrs
+
+ Copyright (C) Andrew Tridgell 2006
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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 "vfs_posix.h"
+#include "../lib/util/unix_privs.h"
+#include "librpc/gen_ndr/ndr_xattr.h"
+
+NTSTATUS pvfs_acl_xattr_init(TALLOC_CTX *);
+
+/*
+ load the current ACL from extended attributes
+*/
+static NTSTATUS pvfs_acl_load_xattr(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd,
+ TALLOC_CTX *mem_ctx,
+ struct security_descriptor **sd)
+{
+ NTSTATUS status;
+ struct xattr_NTACL *acl;
+
+ if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
+ return NT_STATUS_NOT_FOUND;
+ }
+
+ acl = talloc_zero(mem_ctx, struct xattr_NTACL);
+ NT_STATUS_HAVE_NO_MEMORY(acl);
+
+ status = pvfs_xattr_ndr_load(pvfs, mem_ctx, name->full_name, fd,
+ XATTR_NTACL_NAME,
+ acl, (void *) ndr_pull_xattr_NTACL);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(acl);
+ return status;
+ }
+
+ if (acl->version != 1) {
+ talloc_free(acl);
+ return NT_STATUS_INVALID_ACL;
+ }
+
+ *sd = talloc_steal(mem_ctx, acl->info.sd);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ save the acl for a file into filesystem xattr
+*/
+static NTSTATUS pvfs_acl_save_xattr(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd,
+ struct security_descriptor *sd)
+{
+ NTSTATUS status;
+ void *privs;
+ struct xattr_NTACL acl;
+
+ if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
+ return NT_STATUS_OK;
+ }
+
+ acl.version = 1;
+ acl.info.sd = sd;
+
+ /* this xattr is in the "system" namespace, so we need
+ admin privileges to set it */
+ privs = root_privileges();
+ status = pvfs_xattr_ndr_save(pvfs, name->full_name, fd,
+ XATTR_NTACL_NAME,
+ &acl, (void *) ndr_push_xattr_NTACL);
+ talloc_free(privs);
+ return status;
+}
+
+
+/*
+ initialise pvfs acl xattr backend
+*/
+NTSTATUS pvfs_acl_xattr_init(TALLOC_CTX *ctx)
+{
+ struct pvfs_acl_ops ops = {
+ .name = "xattr",
+ .acl_load = pvfs_acl_load_xattr,
+ .acl_save = pvfs_acl_save_xattr
+ };
+ return pvfs_acl_register(ctx, &ops);
+}
diff --git a/source4/ntvfs/posix/pvfs_dirlist.c b/source4/ntvfs/posix/pvfs_dirlist.c
new file mode 100644
index 0000000..1207287
--- /dev/null
+++ b/source4/ntvfs/posix/pvfs_dirlist.c
@@ -0,0 +1,411 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Andrew Tridgell 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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/>.
+*/
+/*
+ directory listing functions for posix backend
+*/
+
+#include "includes.h"
+#include "vfs_posix.h"
+#include "system/dir.h"
+
+#define NAME_CACHE_SIZE 100
+
+struct name_cache_entry {
+ char *name;
+ off_t offset;
+};
+
+struct pvfs_dir {
+ struct pvfs_state *pvfs;
+ bool no_wildcard;
+ char *single_name;
+ const char *pattern;
+ off_t offset;
+ DIR *dir;
+ const char *unix_path;
+ bool end_of_search;
+ struct name_cache_entry *name_cache;
+ uint32_t name_cache_index;
+};
+
+/* these three numbers are chosen to minimise the chances of a bad
+ interaction with the OS value for 'end of directory'. On IRIX
+ telldir() returns 0xFFFFFFFF at the end of a directory, and that
+ caused an infinite loop with the original values of 0,1,2
+
+ On XFS on linux telldir returns 0x7FFFFFFF at the end of a
+ directory. Thus the change from 0x80000002, as otherwise
+ 0x7FFFFFFF+0x80000002==1==DIR_OFFSET_DOTDOT
+*/
+#define DIR_OFFSET_DOT 0
+#define DIR_OFFSET_DOTDOT 1
+#define DIR_OFFSET_BASE 0x80000022
+
+/*
+ a special directory listing case where the pattern has no wildcard. We can just do a single stat()
+ thus avoiding the more expensive directory scan
+*/
+static NTSTATUS pvfs_list_no_wildcard(struct pvfs_state *pvfs, struct pvfs_filename *name,
+ const char *pattern, struct pvfs_dir *dir)
+{
+ if (!name->exists) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ dir->pvfs = pvfs;
+ dir->no_wildcard = true;
+ dir->end_of_search = false;
+ dir->unix_path = talloc_strdup(dir, name->full_name);
+ if (!dir->unix_path) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ dir->single_name = talloc_strdup(dir, pattern);
+ if (!dir->single_name) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ dir->dir = NULL;
+ dir->offset = 0;
+ dir->pattern = NULL;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ destroy an open search
+*/
+static int pvfs_dirlist_destructor(struct pvfs_dir *dir)
+{
+ if (dir->dir) closedir(dir->dir);
+ return 0;
+}
+
+/*
+ start to read a directory
+
+ if the pattern matches no files then we return NT_STATUS_OK, with dir->count = 0
+*/
+NTSTATUS pvfs_list_start(struct pvfs_state *pvfs, struct pvfs_filename *name,
+ TALLOC_CTX *mem_ctx, struct pvfs_dir **dirp)
+{
+ char *pattern;
+ struct pvfs_dir *dir;
+
+ (*dirp) = talloc_zero(mem_ctx, struct pvfs_dir);
+ if (*dirp == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ dir = *dirp;
+
+ /* split the unix path into a directory + pattern */
+ pattern = strrchr(name->full_name, '/');
+ if (!pattern) {
+ /* this should not happen, as pvfs_unix_path is supposed to
+ return an absolute path */
+ return NT_STATUS_UNSUCCESSFUL;
+ }
+
+ *pattern++ = 0;
+
+ if (!name->has_wildcard) {
+ return pvfs_list_no_wildcard(pvfs, name, pattern, dir);
+ }
+
+ dir->unix_path = talloc_strdup(dir, name->full_name);
+ if (!dir->unix_path) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ dir->pattern = talloc_strdup(dir, pattern);
+ if (dir->pattern == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ dir->dir = opendir(name->full_name);
+ if (!dir->dir) {
+ return pvfs_map_errno(pvfs, errno);
+ }
+
+ dir->pvfs = pvfs;
+ dir->no_wildcard = false;
+ dir->end_of_search = false;
+ dir->offset = DIR_OFFSET_DOT;
+ dir->name_cache = talloc_zero_array(dir,
+ struct name_cache_entry,
+ NAME_CACHE_SIZE);
+ if (dir->name_cache == NULL) {
+ talloc_free(dir);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ talloc_set_destructor(dir, pvfs_dirlist_destructor);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ add an entry to the local cache
+*/
+static void dcache_add(struct pvfs_dir *dir, const char *name)
+{
+ struct name_cache_entry *e;
+
+ dir->name_cache_index = (dir->name_cache_index+1) % NAME_CACHE_SIZE;
+ e = &dir->name_cache[dir->name_cache_index];
+
+ if (e->name) talloc_free(e->name);
+
+ e->name = talloc_strdup(dir->name_cache, name);
+ e->offset = dir->offset;
+}
+
+/*
+ return the next entry
+*/
+const char *pvfs_list_next(struct pvfs_dir *dir, off_t *ofs)
+{
+ struct dirent *de;
+ enum protocol_types protocol = dir->pvfs->ntvfs->ctx->protocol;
+
+ /* non-wildcard searches are easy */
+ if (dir->no_wildcard) {
+ dir->end_of_search = true;
+ if (*ofs != 0) return NULL;
+ (*ofs)++;
+ return dir->single_name;
+ }
+
+ /* . and .. are handled separately as some unix systems will
+ not return them first in a directory, but windows client
+ may assume that these entries always appear first */
+ if (*ofs == DIR_OFFSET_DOT) {
+ (*ofs) = DIR_OFFSET_DOTDOT;
+ dir->offset = *ofs;
+ if (ms_fnmatch_protocol(dir->pattern, ".", protocol,
+ false) == 0) {
+ dcache_add(dir, ".");
+ return ".";
+ }
+ }
+
+ if (*ofs == DIR_OFFSET_DOTDOT) {
+ (*ofs) = DIR_OFFSET_BASE;
+ dir->offset = *ofs;
+ if (ms_fnmatch_protocol(dir->pattern, "..", protocol,
+ false) == 0) {
+ dcache_add(dir, "..");
+ return "..";
+ }
+ }
+
+ if (*ofs == DIR_OFFSET_BASE) {
+ rewinddir(dir->dir);
+ } else if (*ofs != dir->offset) {
+ seekdir(dir->dir, (*ofs) - DIR_OFFSET_BASE);
+ }
+ dir->offset = *ofs;
+
+ while ((de = readdir(dir->dir))) {
+ const char *dname = de->d_name;
+
+ if (ISDOT(dname) || ISDOTDOT(dname)) {
+ continue;
+ }
+
+ if (ms_fnmatch_protocol(dir->pattern, dname, protocol,
+ false) != 0) {
+ char *short_name = pvfs_short_name_component(dir->pvfs, dname);
+ if (short_name == NULL ||
+ ms_fnmatch_protocol(dir->pattern, short_name,
+ protocol, false) != 0) {
+ talloc_free(short_name);
+ continue;
+ }
+ talloc_free(short_name);
+ }
+
+ /* Casting is necessary to avoid signed integer overflow. */
+ dir->offset = (unsigned long)telldir(dir->dir) + (unsigned long)DIR_OFFSET_BASE;
+ (*ofs) = dir->offset;
+
+ dcache_add(dir, dname);
+
+ return dname;
+ }
+
+ dir->end_of_search = true;
+ return NULL;
+}
+
+/*
+ return unix directory of an open search
+*/
+const char *pvfs_list_unix_path(struct pvfs_dir *dir)
+{
+ return dir->unix_path;
+}
+
+/*
+ return true if end of search has been reached
+*/
+bool pvfs_list_eos(struct pvfs_dir *dir, off_t ofs)
+{
+ return dir->end_of_search;
+}
+
+/*
+ seek to the given name
+*/
+NTSTATUS pvfs_list_seek(struct pvfs_dir *dir, const char *name, off_t *ofs)
+{
+ struct dirent *de;
+ int i;
+
+ dir->end_of_search = false;
+
+ if (ISDOT(name)) {
+ dir->offset = DIR_OFFSET_DOTDOT;
+ *ofs = dir->offset;
+ return NT_STATUS_OK;
+ }
+
+ if (ISDOTDOT(name)) {
+ dir->offset = DIR_OFFSET_BASE;
+ *ofs = dir->offset;
+ return NT_STATUS_OK;
+ }
+
+ for (i=dir->name_cache_index;i>=0;i--) {
+ struct name_cache_entry *e = &dir->name_cache[i];
+ if (e->name && strcasecmp_m(name, e->name) == 0) {
+ *ofs = e->offset;
+ return NT_STATUS_OK;
+ }
+ }
+ for (i=NAME_CACHE_SIZE-1;i>dir->name_cache_index;i--) {
+ struct name_cache_entry *e = &dir->name_cache[i];
+ if (e->name && strcasecmp_m(name, e->name) == 0) {
+ *ofs = e->offset;
+ return NT_STATUS_OK;
+ }
+ }
+
+ rewinddir(dir->dir);
+
+ while ((de = readdir(dir->dir))) {
+ if (strcasecmp_m(name, de->d_name) == 0) {
+ /* Casting is necessary to avoid signed integer overflow. */
+ dir->offset = (unsigned long)telldir(dir->dir) + (unsigned long)DIR_OFFSET_BASE;
+ *ofs = dir->offset;
+ return NT_STATUS_OK;
+ }
+ }
+
+ dir->end_of_search = true;
+
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+}
+
+/*
+ seek to the given offset
+*/
+NTSTATUS pvfs_list_seek_ofs(struct pvfs_dir *dir, uint32_t resume_key, off_t *ofs)
+{
+ struct dirent *de;
+ int i;
+
+ dir->end_of_search = false;
+
+ if (resume_key == DIR_OFFSET_DOT) {
+ *ofs = DIR_OFFSET_DOTDOT;
+ return NT_STATUS_OK;
+ }
+
+ if (resume_key == DIR_OFFSET_DOTDOT) {
+ *ofs = DIR_OFFSET_BASE;
+ return NT_STATUS_OK;
+ }
+
+ if (resume_key == DIR_OFFSET_BASE) {
+ rewinddir(dir->dir);
+ if ((de=readdir(dir->dir)) == NULL) {
+ dir->end_of_search = true;
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+ /* Casting is necessary to avoid signed integer overflow. */
+ *ofs = (unsigned long)telldir(dir->dir) + (unsigned long)DIR_OFFSET_BASE;
+ dir->offset = *ofs;
+ return NT_STATUS_OK;
+ }
+
+ for (i=dir->name_cache_index;i>=0;i--) {
+ struct name_cache_entry *e = &dir->name_cache[i];
+ if (resume_key == (uint32_t)e->offset) {
+ *ofs = e->offset;
+ return NT_STATUS_OK;
+ }
+ }
+ for (i=NAME_CACHE_SIZE-1;i>dir->name_cache_index;i--) {
+ struct name_cache_entry *e = &dir->name_cache[i];
+ if (resume_key == (uint32_t)e->offset) {
+ *ofs = e->offset;
+ return NT_STATUS_OK;
+ }
+ }
+
+ rewinddir(dir->dir);
+
+ while ((de = readdir(dir->dir))) {
+ /* Casting is necessary to avoid signed integer overflow. */
+ dir->offset = (unsigned long)telldir(dir->dir) + (unsigned long)DIR_OFFSET_BASE;
+ if (resume_key == (uint32_t)dir->offset) {
+ *ofs = dir->offset;
+ return NT_STATUS_OK;
+ }
+ }
+
+ dir->end_of_search = true;
+
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+}
+
+
+/*
+ see if a directory is empty
+*/
+bool pvfs_directory_empty(struct pvfs_state *pvfs, struct pvfs_filename *name)
+{
+ struct dirent *de;
+ DIR *dir = opendir(name->full_name);
+ if (dir == NULL) {
+ return true;
+ }
+
+ while ((de = readdir(dir))) {
+ if (!ISDOT(de->d_name) && !ISDOTDOT(de->d_name)) {
+ closedir(dir);
+ return false;
+ }
+ }
+
+ closedir(dir);
+ return true;
+}
diff --git a/source4/ntvfs/posix/pvfs_fileinfo.c b/source4/ntvfs/posix/pvfs_fileinfo.c
new file mode 100644
index 0000000..977ea4f
--- /dev/null
+++ b/source4/ntvfs/posix/pvfs_fileinfo.c
@@ -0,0 +1,158 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ POSIX NTVFS backend -
+
+ Copyright (C) Andrew Tridgell 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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 "vfs_posix.h"
+#include "lib/util/time.h"
+
+/****************************************************************************
+ Change a unix mode to a dos mode.
+****************************************************************************/
+static uint32_t dos_mode_from_stat(struct pvfs_state *pvfs, struct stat *st)
+{
+ int result = 0;
+
+ if ((st->st_mode & S_IWUSR) == 0)
+ result |= FILE_ATTRIBUTE_READONLY;
+
+ if ((pvfs->flags & PVFS_FLAG_MAP_ARCHIVE) && ((st->st_mode & S_IXUSR) != 0))
+ result |= FILE_ATTRIBUTE_ARCHIVE;
+
+ if ((pvfs->flags & PVFS_FLAG_MAP_SYSTEM) && ((st->st_mode & S_IXGRP) != 0))
+ result |= FILE_ATTRIBUTE_SYSTEM;
+
+ if ((pvfs->flags & PVFS_FLAG_MAP_HIDDEN) && ((st->st_mode & S_IXOTH) != 0))
+ result |= FILE_ATTRIBUTE_HIDDEN;
+
+ if (S_ISDIR(st->st_mode))
+ result = FILE_ATTRIBUTE_DIRECTORY | (result & FILE_ATTRIBUTE_READONLY);
+
+ return result;
+}
+
+
+
+/*
+ fill in the dos file attributes for a file
+*/
+NTSTATUS pvfs_fill_dos_info(struct pvfs_state *pvfs, struct pvfs_filename *name,
+ unsigned int flags, int fd)
+{
+ NTSTATUS status;
+ DATA_BLOB lkey;
+ NTTIME write_time;
+
+ /* make directories appear as size 0 with 1 link */
+ if (S_ISDIR(name->st.st_mode)) {
+ name->st.st_size = 0;
+ name->st.st_nlink = 1;
+ } else if (name->stream_id == 0) {
+ name->stream_name = NULL;
+ }
+
+ /* for now just use the simple samba mapping */
+ unix_to_nt_time(&name->dos.create_time, name->st.st_ctime);
+ unix_to_nt_time(&name->dos.access_time, name->st.st_atime);
+ unix_to_nt_time(&name->dos.write_time, name->st.st_mtime);
+ unix_to_nt_time(&name->dos.change_time, name->st.st_ctime);
+ name->dos.create_time += get_ctimensec(&name->st) / 100;
+ name->dos.access_time += get_atimensec(&name->st) / 100;
+ name->dos.write_time += get_mtimensec(&name->st) / 100;
+ name->dos.change_time += get_ctimensec(&name->st) / 100;
+ name->dos.attrib = dos_mode_from_stat(pvfs, &name->st);
+ name->dos.alloc_size = pvfs_round_alloc_size(pvfs, name->st.st_size);
+ name->dos.nlink = name->st.st_nlink;
+ name->dos.ea_size = 4; /* TODO: Fill this in without hitting the stream bad in pvfs_doseas_load() */
+ if (pvfs->ntvfs->ctx->protocol >= PROTOCOL_SMB2_02) {
+ /* SMB2 represents a null EA with zero bytes */
+ name->dos.ea_size = 0;
+ }
+
+ name->dos.file_id = (((uint64_t)name->st.st_dev)<<32) | name->st.st_ino;
+ name->dos.flags = 0;
+
+ status = pvfs_dosattrib_load(pvfs, name, fd);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ if (flags & PVFS_RESOLVE_NO_OPENDB) {
+ return NT_STATUS_OK;
+ }
+
+ status = pvfs_locking_key(name, name, &lkey);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ status = odb_get_file_infos(pvfs->odb_context, &lkey,
+ NULL, &write_time);
+ data_blob_free(&lkey);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1,("WARNING: odb_get_file_infos: %s\n", nt_errstr(status)));
+ return status;
+ }
+
+ if (!null_time(write_time)) {
+ name->dos.write_time = write_time;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ return a set of unix file permissions for a new file or directory
+*/
+mode_t pvfs_fileperms(struct pvfs_state *pvfs, uint32_t attrib)
+{
+ mode_t mode = (S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH);
+
+ if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE) &&
+ (attrib & FILE_ATTRIBUTE_READONLY)) {
+ mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
+ }
+
+ if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
+ if ((attrib & FILE_ATTRIBUTE_ARCHIVE) &&
+ (pvfs->flags & PVFS_FLAG_MAP_ARCHIVE)) {
+ mode |= S_IXUSR;
+ }
+ if ((attrib & FILE_ATTRIBUTE_SYSTEM) &&
+ (pvfs->flags & PVFS_FLAG_MAP_SYSTEM)) {
+ mode |= S_IXGRP;
+ }
+ if ((attrib & FILE_ATTRIBUTE_HIDDEN) &&
+ (pvfs->flags & PVFS_FLAG_MAP_HIDDEN)) {
+ mode |= S_IXOTH;
+ }
+ }
+
+ if (attrib & FILE_ATTRIBUTE_DIRECTORY) {
+ mode |= (S_IFDIR | S_IWUSR);
+ mode |= (S_IXUSR | S_IXGRP | S_IXOTH);
+ mode &= pvfs->options.dir_mask;
+ mode |= pvfs->options.force_dir_mode;
+ } else {
+ mode &= pvfs->options.create_mask;
+ mode |= pvfs->options.force_create_mode;
+ }
+
+ return mode;
+}
+
+
diff --git a/source4/ntvfs/posix/pvfs_flush.c b/source4/ntvfs/posix/pvfs_flush.c
new file mode 100644
index 0000000..f425fcc
--- /dev/null
+++ b/source4/ntvfs/posix/pvfs_flush.c
@@ -0,0 +1,80 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ POSIX NTVFS backend - flush
+
+ Copyright (C) Andrew Tridgell 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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 "vfs_posix.h"
+
+/*
+ flush a single open file
+*/
+static void pvfs_flush_file(struct pvfs_state *pvfs, struct pvfs_file *f)
+{
+ if (f->handle->fd == -1) {
+ return;
+ }
+ if (pvfs->flags & PVFS_FLAG_STRICT_SYNC) {
+ fsync(f->handle->fd);
+ }
+}
+
+/*
+ flush a fnum
+*/
+NTSTATUS pvfs_flush(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_flush *io)
+{
+ struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ struct pvfs_file *f;
+
+ switch (io->generic.level) {
+ case RAW_FLUSH_FLUSH:
+ case RAW_FLUSH_SMB2:
+ /* TODO: take care of io->smb2.in.unknown */
+ f = pvfs_find_fd(pvfs, req, io->generic.in.file.ntvfs);
+ if (!f) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+ pvfs_flush_file(pvfs, f);
+ io->smb2.out.reserved = 0;
+ return NT_STATUS_OK;
+
+ case RAW_FLUSH_ALL:
+ if (!(pvfs->flags & PVFS_FLAG_STRICT_SYNC)) {
+ return NT_STATUS_OK;
+ }
+
+ /*
+ * they are asking to flush all open files
+ * for the given SMBPID
+ */
+ for (f=pvfs->files.list;f;f=f->next) {
+ if (f->ntvfs->smbpid != req->smbpid) continue;
+
+ pvfs_flush_file(pvfs, f);
+ }
+
+ return NT_STATUS_OK;
+ }
+
+ return NT_STATUS_INVALID_LEVEL;
+}
diff --git a/source4/ntvfs/posix/pvfs_fsinfo.c b/source4/ntvfs/posix/pvfs_fsinfo.c
new file mode 100644
index 0000000..c355c19
--- /dev/null
+++ b/source4/ntvfs/posix/pvfs_fsinfo.c
@@ -0,0 +1,224 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ POSIX NTVFS backend - fsinfo
+
+ Copyright (C) Andrew Tridgell 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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 "vfs_posix.h"
+#include "librpc/gen_ndr/xattr.h"
+#include "librpc/ndr/libndr.h"
+
+/* We use libblkid out of e2fsprogs to identify UUID of a volume */
+#ifdef HAVE_LIBBLKID
+#include <blkid/blkid.h>
+#endif
+
+static NTSTATUS pvfs_blkid_fs_uuid(struct pvfs_state *pvfs, struct stat *st, struct GUID *uuid)
+{
+#ifdef HAVE_LIBBLKID
+ NTSTATUS status;
+ char *uuid_value = NULL;
+ char *devname = NULL;
+
+ devname = blkid_devno_to_devname(st->st_dev);
+ if (!devname) {
+ ZERO_STRUCTP(uuid);
+ return NT_STATUS_OK;
+ }
+
+ uuid_value = blkid_get_tag_value(NULL, "UUID", devname);
+ free(devname);
+ if (!uuid_value) {
+ ZERO_STRUCTP(uuid);
+ return NT_STATUS_OK;
+ }
+
+ status = GUID_from_string(uuid_value, uuid);
+ free(uuid_value);
+ if (!NT_STATUS_IS_OK(status)) {
+ ZERO_STRUCTP(uuid);
+ return NT_STATUS_OK;
+ }
+ return NT_STATUS_OK;
+#else
+ ZERO_STRUCTP(uuid);
+ return NT_STATUS_OK;
+#endif
+}
+
+static NTSTATUS pvfs_cache_base_fs_uuid(struct pvfs_state *pvfs, struct stat *st)
+{
+ NTSTATUS status;
+ struct GUID uuid;
+
+ if (pvfs->base_fs_uuid) return NT_STATUS_OK;
+
+ status = pvfs_blkid_fs_uuid(pvfs, st, &uuid);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ pvfs->base_fs_uuid = talloc(pvfs, struct GUID);
+ NT_STATUS_HAVE_NO_MEMORY(pvfs->base_fs_uuid);
+ *pvfs->base_fs_uuid = uuid;
+
+ return NT_STATUS_OK;
+}
+/*
+ return filesystem space info
+*/
+NTSTATUS pvfs_fsinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_fsinfo *fs)
+{
+ NTSTATUS status;
+ struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ uint64_t blocks_free, blocks_total;
+ unsigned int bpunit;
+ struct stat st;
+ const uint16_t block_size = 512;
+
+ /* only some levels need the expensive sys_fsusage() call */
+ switch (fs->generic.level) {
+ case RAW_QFS_DSKATTR:
+ case RAW_QFS_ALLOCATION:
+ case RAW_QFS_SIZE_INFO:
+ case RAW_QFS_SIZE_INFORMATION:
+ case RAW_QFS_FULL_SIZE_INFORMATION:
+ if (sys_fsusage(pvfs->base_directory, &blocks_free, &blocks_total) == -1) {
+ return pvfs_map_errno(pvfs, errno);
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (stat(pvfs->base_directory, &st) != 0) {
+ return NT_STATUS_DISK_CORRUPT_ERROR;
+ }
+
+ /* now fill in the out fields */
+ switch (fs->generic.level) {
+ case RAW_QFS_GENERIC:
+ return NT_STATUS_INVALID_LEVEL;
+
+ case RAW_QFS_DSKATTR:
+ /* we need to scale the sizes to fit */
+ for (bpunit=64; bpunit<0x10000; bpunit *= 2) {
+ if (blocks_total * (double)block_size < bpunit * 512 * 65535.0) {
+ break;
+ }
+ }
+ fs->dskattr.out.blocks_per_unit = bpunit;
+ fs->dskattr.out.block_size = block_size;
+ fs->dskattr.out.units_total = (blocks_total * (double)block_size) / (bpunit * 512);
+ fs->dskattr.out.units_free = (blocks_free * (double)block_size) / (bpunit * 512);
+
+ /* we must return a maximum of 2G to old DOS systems, or they get very confused */
+ if (bpunit > 64 && req->ctx->protocol <= PROTOCOL_LANMAN2) {
+ fs->dskattr.out.blocks_per_unit = 64;
+ fs->dskattr.out.units_total = 0xFFFF;
+ fs->dskattr.out.units_free = 0xFFFF;
+ }
+ return NT_STATUS_OK;
+
+ case RAW_QFS_ALLOCATION:
+ fs->allocation.out.fs_id = st.st_dev;
+ fs->allocation.out.total_alloc_units = blocks_total;
+ fs->allocation.out.avail_alloc_units = blocks_free;
+ fs->allocation.out.sectors_per_unit = 1;
+ fs->allocation.out.bytes_per_sector = block_size;
+ return NT_STATUS_OK;
+
+ case RAW_QFS_VOLUME:
+ fs->volume.out.serial_number = st.st_ino;
+ fs->volume.out.volume_name.s = pvfs->share_name;
+ return NT_STATUS_OK;
+
+ case RAW_QFS_VOLUME_INFO:
+ case RAW_QFS_VOLUME_INFORMATION:
+ unix_to_nt_time(&fs->volume_info.out.create_time, st.st_ctime);
+ fs->volume_info.out.serial_number = st.st_ino;
+ fs->volume_info.out.volume_name.s = pvfs->share_name;
+ return NT_STATUS_OK;
+
+ case RAW_QFS_SIZE_INFO:
+ case RAW_QFS_SIZE_INFORMATION:
+ fs->size_info.out.total_alloc_units = blocks_total;
+ fs->size_info.out.avail_alloc_units = blocks_free;
+ fs->size_info.out.sectors_per_unit = 1;
+ fs->size_info.out.bytes_per_sector = block_size;
+ return NT_STATUS_OK;
+
+ case RAW_QFS_DEVICE_INFO:
+ case RAW_QFS_DEVICE_INFORMATION:
+ fs->device_info.out.device_type = 0;
+ fs->device_info.out.characteristics = 0;
+ return NT_STATUS_OK;
+
+ case RAW_QFS_ATTRIBUTE_INFO:
+ case RAW_QFS_ATTRIBUTE_INFORMATION:
+ fs->attribute_info.out.fs_attr = pvfs->fs_attribs;
+ fs->attribute_info.out.max_file_component_length = 255;
+ fs->attribute_info.out.fs_type.s = ntvfs->ctx->fs_type;
+ return NT_STATUS_OK;
+
+ case RAW_QFS_QUOTA_INFORMATION:
+ ZERO_STRUCT(fs->quota_information.out.unknown);
+ fs->quota_information.out.quota_soft = 0;
+ fs->quota_information.out.quota_hard = 0;
+ fs->quota_information.out.quota_flags = 0;
+ return NT_STATUS_OK;
+
+ case RAW_QFS_FULL_SIZE_INFORMATION:
+ fs->full_size_information.out.total_alloc_units = blocks_total;
+ fs->full_size_information.out.call_avail_alloc_units = blocks_free;
+ fs->full_size_information.out.actual_avail_alloc_units = blocks_free;
+ fs->full_size_information.out.sectors_per_unit = 1;
+ fs->full_size_information.out.bytes_per_sector = block_size;
+ return NT_STATUS_OK;
+
+ case RAW_QFS_OBJECTID_INFORMATION:
+ ZERO_STRUCT(fs->objectid_information.out.guid);
+ ZERO_STRUCT(fs->objectid_information.out.unknown);
+
+ status = pvfs_cache_base_fs_uuid(pvfs, &st);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ fs->objectid_information.out.guid = *pvfs->base_fs_uuid;
+ return NT_STATUS_OK;
+
+ case RAW_QFS_SECTOR_SIZE_INFORMATION:
+ fs->sector_size_info.out.logical_bytes_per_sector = block_size;
+ fs->sector_size_info.out.phys_bytes_per_sector_atomic
+ = block_size;
+ fs->sector_size_info.out.phys_bytes_per_sector_perf
+ = block_size;
+ fs->sector_size_info.out.fs_effective_phys_bytes_per_sector_atomic
+ = block_size;
+ fs->sector_size_info.out.flags
+ = QFS_SSINFO_FLAGS_ALIGNED_DEVICE
+ | QFS_SSINFO_FLAGS_PARTITION_ALIGNED_ON_DEVICE;
+ fs->sector_size_info.out.byte_off_sector_align = 0;
+ fs->sector_size_info.out.byte_off_partition_align = 0;
+ return NT_STATUS_OK;
+
+ default:
+ break;
+ }
+ return NT_STATUS_INVALID_LEVEL;
+}
diff --git a/source4/ntvfs/posix/pvfs_ioctl.c b/source4/ntvfs/posix/pvfs_ioctl.c
new file mode 100644
index 0000000..1d5e8fd
--- /dev/null
+++ b/source4/ntvfs/posix/pvfs_ioctl.c
@@ -0,0 +1,82 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ POSIX NTVFS backend - open and close
+
+ Copyright (C) Andrew Tridgell 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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 "vfs_posix.h"
+#include "../libcli/smb/smb_constants.h"
+
+/*
+ old ioctl interface
+*/
+static NTSTATUS pvfs_ioctl_old(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_ioctl *io)
+{
+ return NT_STATUS_DOS(ERRSRV, ERRerror);
+}
+
+/*
+ nt ioctl interface
+*/
+static NTSTATUS pvfs_ntioctl(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_ioctl *io)
+{
+ struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ struct pvfs_file *f;
+
+ f = pvfs_find_fd(pvfs, req, io->ntioctl.in.file.ntvfs);
+ if (!f) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ switch (io->ntioctl.in.function) {
+ case FSCTL_SET_SPARSE:
+ /* maybe some posix systems have a way of marking
+ a file non-sparse? */
+ io->ntioctl.out.blob = data_blob(NULL, 0);
+ return NT_STATUS_OK;
+ }
+
+ return NT_STATUS_NOT_SUPPORTED;
+}
+
+/*
+ ioctl interface
+*/
+NTSTATUS pvfs_ioctl(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_ioctl *io)
+{
+ switch (io->generic.level) {
+ case RAW_IOCTL_IOCTL:
+ return pvfs_ioctl_old(ntvfs, req, io);
+
+ case RAW_IOCTL_NTIOCTL:
+ return pvfs_ntioctl(ntvfs, req, io);
+
+ case RAW_IOCTL_SMB2:
+ case RAW_IOCTL_SMB2_NO_HANDLE:
+ /* see WSPP SMB2 test 46 */
+ return NT_STATUS_INVALID_DEVICE_REQUEST;
+ }
+
+ return NT_STATUS_INVALID_LEVEL;
+}
diff --git a/source4/ntvfs/posix/pvfs_lock.c b/source4/ntvfs/posix/pvfs_lock.c
new file mode 100644
index 0000000..0802ded
--- /dev/null
+++ b/source4/ntvfs/posix/pvfs_lock.c
@@ -0,0 +1,404 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ POSIX NTVFS backend - locking
+
+ Copyright (C) Andrew Tridgell 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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 "vfs_posix.h"
+#include "system/time.h"
+#include "../lib/util/dlinklist.h"
+#include "messaging/messaging.h"
+
+
+/*
+ check if we can perform IO on a range that might be locked
+*/
+NTSTATUS pvfs_check_lock(struct pvfs_state *pvfs,
+ struct pvfs_file *f,
+ uint32_t smbpid,
+ uint64_t offset, uint64_t count,
+ enum brl_type rw)
+{
+ if (!(pvfs->flags & PVFS_FLAG_STRICT_LOCKING)) {
+ return NT_STATUS_OK;
+ }
+
+ return brlock_locktest(pvfs->brl_context,
+ f->brl_handle,
+ smbpid,
+ offset, count, rw);
+}
+
+/* this state structure holds information about a lock we are waiting on */
+struct pvfs_pending_lock {
+ struct pvfs_pending_lock *next, *prev;
+ struct pvfs_state *pvfs;
+ union smb_lock *lck;
+ struct pvfs_file *f;
+ struct ntvfs_request *req;
+ int pending_lock;
+ struct pvfs_wait *wait_handle;
+ struct timeval end_time;
+};
+
+/*
+ a secondary attempt to setup a lock has failed - back out
+ the locks we did get and send an error
+*/
+static void pvfs_lock_async_failed(struct pvfs_state *pvfs,
+ struct ntvfs_request *req,
+ struct pvfs_file *f,
+ struct smb_lock_entry *locks,
+ int i,
+ NTSTATUS status)
+{
+ /* undo the locks we just did */
+ for (i--;i>=0;i--) {
+ brlock_unlock(pvfs->brl_context,
+ f->brl_handle,
+ locks[i].pid,
+ locks[i].offset,
+ locks[i].count);
+ f->lock_count--;
+ }
+ req->async_states->status = status;
+ req->async_states->send_fn(req);
+}
+
+
+/*
+ called when we receive a pending lock notification. It means that
+ either our lock timed out or someone else has unlocked a overlapping
+ range, so we should try the lock again. Note that on timeout we
+ do retry the lock, giving it a last chance.
+*/
+static void pvfs_pending_lock_continue(void *private_data, enum pvfs_wait_notice reason)
+{
+ struct pvfs_pending_lock *pending = talloc_get_type(private_data,
+ struct pvfs_pending_lock);
+ struct pvfs_state *pvfs = pending->pvfs;
+ struct pvfs_file *f = pending->f;
+ struct ntvfs_request *req = pending->req;
+ union smb_lock *lck = pending->lck;
+ struct smb_lock_entry *locks;
+ enum brl_type rw;
+ NTSTATUS status;
+ int i;
+ bool timed_out;
+
+ timed_out = (reason != PVFS_WAIT_EVENT);
+
+ locks = lck->lockx.in.locks + lck->lockx.in.ulock_cnt;
+
+ if (lck->lockx.in.mode & LOCKING_ANDX_SHARED_LOCK) {
+ rw = READ_LOCK;
+ } else {
+ rw = WRITE_LOCK;
+ }
+
+ DLIST_REMOVE(f->pending_list, pending);
+
+ /* we don't retry on a cancel */
+ if (reason == PVFS_WAIT_CANCEL) {
+ if (pvfs->ntvfs->ctx->protocol < PROTOCOL_SMB2_02) {
+ status = NT_STATUS_FILE_LOCK_CONFLICT;
+ } else {
+ status = NT_STATUS_CANCELLED;
+ }
+ } else {
+ /*
+ * here it's important to pass the pending pointer
+ * because with this we'll get the correct error code
+ * FILE_LOCK_CONFLICT in the error case
+ */
+ status = brlock_lock(pvfs->brl_context,
+ f->brl_handle,
+ locks[pending->pending_lock].pid,
+ locks[pending->pending_lock].offset,
+ locks[pending->pending_lock].count,
+ rw, pending);
+ }
+ if (NT_STATUS_IS_OK(status)) {
+ f->lock_count++;
+ timed_out = false;
+ }
+
+ /* if we have failed and timed out, or succeeded, then we
+ don't need the pending lock any more */
+ if (NT_STATUS_IS_OK(status) || timed_out) {
+ NTSTATUS status2;
+ status2 = brlock_remove_pending(pvfs->brl_context,
+ f->brl_handle, pending);
+ if (!NT_STATUS_IS_OK(status2)) {
+ DEBUG(0,("pvfs_lock: failed to remove pending lock - %s\n", nt_errstr(status2)));
+ }
+ talloc_free(pending->wait_handle);
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ if (timed_out) {
+ /* no more chances */
+ pvfs_lock_async_failed(pvfs, req, f, locks, pending->pending_lock, status);
+ talloc_free(pending);
+ } else {
+ /* we can try again */
+ DLIST_ADD(f->pending_list, pending);
+ }
+ return;
+ }
+
+ /* if we haven't timed out yet, then we can do more pending locks */
+ if (rw == READ_LOCK) {
+ rw = PENDING_READ_LOCK;
+ } else {
+ rw = PENDING_WRITE_LOCK;
+ }
+
+ /* we've now got the pending lock. try and get the rest, which might
+ lead to more pending locks */
+ for (i=pending->pending_lock+1;i<lck->lockx.in.lock_cnt;i++) {
+ pending->pending_lock = i;
+
+ status = brlock_lock(pvfs->brl_context,
+ f->brl_handle,
+ locks[i].pid,
+ locks[i].offset,
+ locks[i].count,
+ rw, pending);
+ if (!NT_STATUS_IS_OK(status)) {
+ /* a timed lock failed - setup a wait message to handle
+ the pending lock notification or a timeout */
+ pending->wait_handle = pvfs_wait_message(pvfs, req, MSG_BRL_RETRY,
+ pending->end_time,
+ pvfs_pending_lock_continue,
+ pending);
+ if (pending->wait_handle == NULL) {
+ pvfs_lock_async_failed(pvfs, req, f, locks, i, NT_STATUS_NO_MEMORY);
+ talloc_free(pending);
+ } else {
+ talloc_steal(pending, pending->wait_handle);
+ DLIST_ADD(f->pending_list, pending);
+ }
+ return;
+ }
+
+ f->lock_count++;
+ }
+
+ /* we've managed to get all the locks. Tell the client */
+ req->async_states->status = NT_STATUS_OK;
+ req->async_states->send_fn(req);
+ talloc_free(pending);
+}
+
+
+/*
+ called when we close a file that might have locks
+*/
+void pvfs_lock_close(struct pvfs_state *pvfs, struct pvfs_file *f)
+{
+ struct pvfs_pending_lock *p, *next;
+
+ if (f->lock_count || f->pending_list) {
+ DEBUG(5,("pvfs_lock: removing %.0f locks on close\n",
+ (double)f->lock_count));
+ brlock_close(f->pvfs->brl_context, f->brl_handle);
+ f->lock_count = 0;
+ }
+
+ /* reply to all the pending lock requests, telling them the
+ lock failed */
+ for (p=f->pending_list;p;p=next) {
+ next = p->next;
+ DLIST_REMOVE(f->pending_list, p);
+ p->req->async_states->status = NT_STATUS_RANGE_NOT_LOCKED;
+ p->req->async_states->send_fn(p->req);
+ }
+}
+
+
+/*
+ cancel a set of locks
+*/
+static NTSTATUS pvfs_lock_cancel(struct pvfs_state *pvfs, struct ntvfs_request *req, union smb_lock *lck,
+ struct pvfs_file *f)
+{
+ struct pvfs_pending_lock *p;
+
+ for (p=f->pending_list;p;p=p->next) {
+ /* check if the lock request matches exactly - you can only cancel with exact matches */
+ if (p->lck->lockx.in.ulock_cnt == lck->lockx.in.ulock_cnt &&
+ p->lck->lockx.in.lock_cnt == lck->lockx.in.lock_cnt &&
+ p->lck->lockx.in.file.ntvfs== lck->lockx.in.file.ntvfs &&
+ p->lck->lockx.in.mode == (lck->lockx.in.mode & ~LOCKING_ANDX_CANCEL_LOCK)) {
+ int i;
+
+ for (i=0;i<lck->lockx.in.ulock_cnt + lck->lockx.in.lock_cnt;i++) {
+ if (p->lck->lockx.in.locks[i].pid != lck->lockx.in.locks[i].pid ||
+ p->lck->lockx.in.locks[i].offset != lck->lockx.in.locks[i].offset ||
+ p->lck->lockx.in.locks[i].count != lck->lockx.in.locks[i].count) {
+ break;
+ }
+ }
+ if (i < lck->lockx.in.ulock_cnt) continue;
+
+ /* an exact match! we can cancel it, which is equivalent
+ to triggering the timeout early */
+ pvfs_pending_lock_continue(p, PVFS_WAIT_TIMEOUT);
+ return NT_STATUS_OK;
+ }
+ }
+
+ return NT_STATUS_DOS(ERRDOS, ERRcancelviolation);
+}
+
+
+/*
+ lock or unlock a byte range
+*/
+NTSTATUS pvfs_lock(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_lock *lck)
+{
+ struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ struct pvfs_file *f;
+ struct smb_lock_entry *locks;
+ int i;
+ enum brl_type rw;
+ struct pvfs_pending_lock *pending = NULL;
+ NTSTATUS status;
+
+ if (lck->generic.level != RAW_LOCK_GENERIC) {
+ 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;
+ }
+
+ if (f->handle->fd == -1) {
+ 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);
+ if (pending == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ pending->pvfs = pvfs;
+ pending->lck = lck;
+ pending->f = f;
+ pending->req = req;
+
+ pending->end_time =
+ timeval_current_ofs_msec(lck->lockx.in.timeout);
+ }
+
+ if (lck->lockx.in.mode & LOCKING_ANDX_SHARED_LOCK) {
+ rw = pending? PENDING_READ_LOCK : READ_LOCK;
+ } else {
+ rw = pending? PENDING_WRITE_LOCK : WRITE_LOCK;
+ }
+
+ if (lck->lockx.in.mode & LOCKING_ANDX_CANCEL_LOCK) {
+ talloc_free(pending);
+ return pvfs_lock_cancel(pvfs, req, lck, f);
+ }
+
+ if (lck->lockx.in.mode & LOCKING_ANDX_CHANGE_LOCKTYPE) {
+ /* this seems to not be supported by any windows server,
+ or used by any clients */
+ talloc_free(pending);
+ return NT_STATUS_DOS(ERRDOS, ERRnoatomiclocks);
+ }
+
+ /* the unlocks happen first */
+ locks = lck->lockx.in.locks;
+
+ for (i=0;i<lck->lockx.in.ulock_cnt;i++) {
+ status = brlock_unlock(pvfs->brl_context,
+ f->brl_handle,
+ locks[i].pid,
+ locks[i].offset,
+ locks[i].count);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(pending);
+ return status;
+ }
+ f->lock_count--;
+ }
+
+ locks += i;
+
+ for (i=0;i<lck->lockx.in.lock_cnt;i++) {
+ if (pending) {
+ pending->pending_lock = i;
+ }
+
+ status = brlock_lock(pvfs->brl_context,
+ f->brl_handle,
+ locks[i].pid,
+ locks[i].offset,
+ locks[i].count,
+ rw, pending);
+ if (!NT_STATUS_IS_OK(status)) {
+ if (pending) {
+ /* a timed lock failed - setup a wait message to handle
+ the pending lock notification or a timeout */
+ pending->wait_handle = pvfs_wait_message(pvfs, req, MSG_BRL_RETRY,
+ pending->end_time,
+ pvfs_pending_lock_continue,
+ pending);
+ if (pending->wait_handle == NULL) {
+ talloc_free(pending);
+ return NT_STATUS_NO_MEMORY;
+ }
+ talloc_steal(pending, pending->wait_handle);
+ DLIST_ADD(f->pending_list, pending);
+ return NT_STATUS_OK;
+ }
+
+ /* undo the locks we just did */
+ for (i--;i>=0;i--) {
+ brlock_unlock(pvfs->brl_context,
+ f->brl_handle,
+ locks[i].pid,
+ locks[i].offset,
+ locks[i].count);
+ f->lock_count--;
+ }
+ talloc_free(pending);
+ return status;
+ }
+ f->lock_count++;
+ }
+
+ talloc_free(pending);
+ return NT_STATUS_OK;
+}
diff --git a/source4/ntvfs/posix/pvfs_mkdir.c b/source4/ntvfs/posix/pvfs_mkdir.c
new file mode 100644
index 0000000..2cf43ab
--- /dev/null
+++ b/source4/ntvfs/posix/pvfs_mkdir.c
@@ -0,0 +1,196 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ POSIX NTVFS backend - mkdir and rmdir
+
+ Copyright (C) Andrew Tridgell 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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 "system/dir.h"
+#include "vfs_posix.h"
+#include "librpc/gen_ndr/security.h"
+
+/*
+ create a directory with EAs
+*/
+static NTSTATUS pvfs_t2mkdir(struct pvfs_state *pvfs,
+ struct ntvfs_request *req, union smb_mkdir *md)
+{
+ NTSTATUS status;
+ struct pvfs_filename *name;
+ mode_t mode;
+
+ /* resolve the cifs name to a posix name */
+ status = pvfs_resolve_name(pvfs, req, md->t2mkdir.in.path, 0, &name);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (name->exists) {
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+
+ status = pvfs_access_check_parent(pvfs, req, name, SEC_DIR_ADD_FILE);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ mode = pvfs_fileperms(pvfs, FILE_ATTRIBUTE_DIRECTORY);
+
+ if (pvfs_sys_mkdir(pvfs, name->full_name, mode, name->allow_override) == -1) {
+ return pvfs_map_errno(pvfs, errno);
+ }
+
+ pvfs_xattr_unlink_hook(pvfs, name->full_name);
+
+ status = pvfs_resolve_name(pvfs, req, md->t2mkdir.in.path, 0, &name);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ if (!name->exists ||
+ !(name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ /* setup an inherited acl from the parent */
+ status = pvfs_acl_inherit(pvfs, req, name, -1);
+ if (!NT_STATUS_IS_OK(status)) {
+ pvfs_sys_rmdir(pvfs, name->full_name, name->allow_override);
+ return status;
+ }
+
+ /* setup any EAs that were asked for */
+ status = pvfs_setfileinfo_ea_set(pvfs, name, -1,
+ md->t2mkdir.in.num_eas,
+ md->t2mkdir.in.eas);
+ if (!NT_STATUS_IS_OK(status)) {
+ pvfs_sys_rmdir(pvfs, name->full_name, name->allow_override);
+ return status;
+ }
+
+ notify_trigger(pvfs->notify_context,
+ NOTIFY_ACTION_ADDED,
+ FILE_NOTIFY_CHANGE_DIR_NAME,
+ name->full_name);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ create a directory
+*/
+NTSTATUS pvfs_mkdir(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_mkdir *md)
+{
+ struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ NTSTATUS status;
+ struct pvfs_filename *name;
+ mode_t mode;
+
+ if (md->generic.level == RAW_MKDIR_T2MKDIR) {
+ return pvfs_t2mkdir(pvfs, req, md);
+ }
+
+ if (md->generic.level != RAW_MKDIR_MKDIR) {
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ /* resolve the cifs name to a posix name */
+ status = pvfs_resolve_name(pvfs, req, md->mkdir.in.path, 0, &name);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (name->exists) {
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+
+ status = pvfs_access_check_parent(pvfs, req, name, SEC_DIR_ADD_FILE);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ mode = pvfs_fileperms(pvfs, FILE_ATTRIBUTE_DIRECTORY);
+
+ if (pvfs_sys_mkdir(pvfs, name->full_name, mode, name->allow_override) == -1) {
+ return pvfs_map_errno(pvfs, errno);
+ }
+
+ pvfs_xattr_unlink_hook(pvfs, name->full_name);
+
+ /* setup an inherited acl from the parent */
+ status = pvfs_acl_inherit(pvfs, req, name, -1);
+ if (!NT_STATUS_IS_OK(status)) {
+ pvfs_sys_rmdir(pvfs, name->full_name, name->allow_override);
+ return status;
+ }
+
+ notify_trigger(pvfs->notify_context,
+ NOTIFY_ACTION_ADDED,
+ FILE_NOTIFY_CHANGE_DIR_NAME,
+ name->full_name);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ remove a directory
+*/
+NTSTATUS pvfs_rmdir(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, struct smb_rmdir *rd)
+{
+ struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ NTSTATUS status;
+ struct pvfs_filename *name;
+
+ /* resolve the cifs name to a posix name */
+ status = pvfs_resolve_name(pvfs, req, rd->in.path, 0, &name);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (!name->exists) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ status = pvfs_access_check_simple(pvfs, req, name, SEC_STD_DELETE);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = pvfs_xattr_unlink_hook(pvfs, name->full_name);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (pvfs_sys_rmdir(pvfs, name->full_name, name->allow_override) == -1) {
+ /* some olders systems don't return ENOTEMPTY to rmdir() */
+ if (errno == EEXIST) {
+ return NT_STATUS_DIRECTORY_NOT_EMPTY;
+ }
+ return pvfs_map_errno(pvfs, errno);
+ }
+
+ notify_trigger(pvfs->notify_context,
+ NOTIFY_ACTION_REMOVED,
+ FILE_NOTIFY_CHANGE_DIR_NAME,
+ name->full_name);
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/ntvfs/posix/pvfs_notify.c b/source4/ntvfs/posix/pvfs_notify.c
new file mode 100644
index 0000000..91a151b
--- /dev/null
+++ b/source4/ntvfs/posix/pvfs_notify.c
@@ -0,0 +1,300 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ POSIX NTVFS backend - notify
+
+ Copyright (C) Andrew Tridgell 2006
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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 "vfs_posix.h"
+#include "lib/messaging/irpc.h"
+#include "messaging/messaging.h"
+#include "../lib/util/dlinklist.h"
+#include "lib/events/events.h"
+
+/* pending notifies buffer, hung off struct pvfs_file for open directories
+ that have used change notify */
+struct pvfs_notify_buffer {
+ struct pvfs_file *f;
+ uint32_t num_changes;
+ struct notify_changes *changes;
+ uint32_t max_buffer_size;
+ uint32_t current_buffer_size;
+ bool overflowed;
+
+ /* a list of requests waiting for events on this handle */
+ struct notify_pending {
+ struct notify_pending *next, *prev;
+ struct ntvfs_request *req;
+ union smb_notify *info;
+ } *pending;
+};
+
+/*
+ send a notify on the next event run.
+*/
+static void pvfs_notify_send_next(struct tevent_context *ev, struct tevent_timer *te,
+ struct timeval t, void *ptr)
+{
+ struct ntvfs_request *req = talloc_get_type(ptr, struct ntvfs_request);
+ req->async_states->send_fn(req);
+}
+
+
+/*
+ send a reply to a pending notify request
+*/
+static void pvfs_notify_send(struct pvfs_notify_buffer *notify_buffer,
+ NTSTATUS status, bool immediate)
+{
+ struct notify_pending *pending = notify_buffer->pending;
+ struct ntvfs_request *req;
+ union smb_notify *info;
+
+ if (notify_buffer->current_buffer_size > notify_buffer->max_buffer_size &&
+ notify_buffer->num_changes != 0) {
+ /* on buffer overflow return no changes and destroys the notify buffer */
+ notify_buffer->num_changes = 0;
+ while (notify_buffer->pending) {
+ pvfs_notify_send(notify_buffer, NT_STATUS_OK, immediate);
+ }
+ notify_buffer->overflowed = true;
+ return;
+ }
+
+ /* see if there is anyone waiting */
+ if (notify_buffer->pending == NULL) {
+ return;
+ }
+
+ DLIST_REMOVE(notify_buffer->pending, pending);
+
+ req = pending->req;
+ info = pending->info;
+
+ info->nttrans.out.num_changes = notify_buffer->num_changes;
+ info->nttrans.out.changes = talloc_steal(req, notify_buffer->changes);
+ notify_buffer->num_changes = 0;
+ notify_buffer->overflowed = false;
+ notify_buffer->changes = NULL;
+ notify_buffer->current_buffer_size = 0;
+
+ talloc_free(pending);
+
+ if (info->nttrans.out.num_changes != 0) {
+ status = NT_STATUS_OK;
+ }
+
+ req->async_states->status = status;
+
+ if (immediate) {
+ req->async_states->send_fn(req);
+ return;
+ }
+
+ /* we can't call pvfs_notify_send() directly here, as that
+ would free the request, and the ntvfs modules above us
+ could use it, so call it on the next event */
+ tevent_add_timer(req->ctx->event_ctx,
+ req, timeval_zero(), pvfs_notify_send_next, req);
+}
+
+/*
+ destroy a notify buffer. Called when the handle is closed
+ */
+static int pvfs_notify_destructor(struct pvfs_notify_buffer *n)
+{
+ notify_remove(n->f->pvfs->notify_context, n);
+ n->f->notify_buffer = NULL;
+ pvfs_notify_send(n, NT_STATUS_OK, true);
+ return 0;
+}
+
+
+/*
+ called when a async notify event comes in
+*/
+static void pvfs_notify_callback(void *private_data, const struct notify_event *ev)
+{
+ struct pvfs_notify_buffer *n = talloc_get_type(private_data, struct pvfs_notify_buffer);
+ size_t len;
+ struct notify_changes *n2;
+ char *new_path;
+
+ if (n->overflowed) {
+ return;
+ }
+
+ n2 = talloc_realloc(n, n->changes, struct notify_changes, n->num_changes+1);
+ if (n2 == NULL) {
+ /* nothing much we can do for this */
+ return;
+ }
+ n->changes = n2;
+
+ new_path = talloc_strdup(n->changes, ev->path);
+ if (new_path == NULL) {
+ return;
+ }
+ string_replace(new_path, '/', '\\');
+
+ n->changes[n->num_changes].action = ev->action;
+ n->changes[n->num_changes].name.s = new_path;
+ n->num_changes++;
+
+ /*
+ work out how much room this will take in the buffer
+ */
+ len = 12 + strlen_m(ev->path)*2;
+ if (len & 3) {
+ len += 4 - (len & 3);
+ }
+ n->current_buffer_size += len;
+
+ /* send what we have, unless its the first part of a rename */
+ if (ev->action != NOTIFY_ACTION_OLD_NAME) {
+ pvfs_notify_send(n, NT_STATUS_OK, true);
+ }
+}
+
+/*
+ setup a notify buffer on a directory handle
+*/
+static NTSTATUS pvfs_notify_setup(struct pvfs_state *pvfs, struct pvfs_file *f,
+ uint32_t buffer_size, uint32_t filter, bool recursive)
+{
+ NTSTATUS status;
+ struct notify_entry e;
+
+ /* We may not fill in all the elements in this entry -
+ * structure may in future be shared with Samba3 */
+ ZERO_STRUCT(e);
+
+ /* We may not fill in all the elements in this entry -
+ * structure may in future be shared with Samba3 */
+ ZERO_STRUCT(e);
+
+ f->notify_buffer = talloc_zero(f, struct pvfs_notify_buffer);
+ NT_STATUS_HAVE_NO_MEMORY(f->notify_buffer);
+
+ f->notify_buffer->max_buffer_size = buffer_size;
+ f->notify_buffer->f = f;
+
+ e.filter = filter;
+ e.path = f->handle->name->full_name;
+ if (recursive) {
+ e.subdir_filter = filter;
+ } else {
+ e.subdir_filter = 0;
+ }
+
+ status = notify_add(pvfs->notify_context, &e,
+ pvfs_notify_callback, f->notify_buffer);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ talloc_set_destructor(f->notify_buffer, pvfs_notify_destructor);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ called from the pvfs_wait code when either an event has come in, or
+ the notify request has been cancelled
+*/
+static void pvfs_notify_end(void *private_data, enum pvfs_wait_notice reason)
+{
+ struct pvfs_notify_buffer *notify_buffer = talloc_get_type(private_data,
+ struct pvfs_notify_buffer);
+ if (reason == PVFS_WAIT_CANCEL) {
+ pvfs_notify_send(notify_buffer, NT_STATUS_CANCELLED, false);
+ } else {
+ pvfs_notify_send(notify_buffer, NT_STATUS_OK, true);
+ }
+}
+
+/* change notify request - always async. This request blocks until the
+ event buffer is non-empty */
+NTSTATUS pvfs_notify(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_notify *info)
+{
+ struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ struct pvfs_file *f;
+ NTSTATUS status;
+ struct notify_pending *pending;
+
+ if (info->nttrans.level != RAW_NOTIFY_NTTRANS) {
+ return ntvfs_map_notify(ntvfs, req, info);
+ }
+
+ f = pvfs_find_fd(pvfs, req, info->nttrans.in.file.ntvfs);
+ if (!f) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ /* this request doesn't make sense unless its async */
+ if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* its only valid for directories */
+ if (f->handle->fd != -1) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* if the handle doesn't currently have a notify buffer then
+ create one */
+ if (f->notify_buffer == NULL) {
+ status = pvfs_notify_setup(pvfs, f,
+ info->nttrans.in.buffer_size,
+ info->nttrans.in.completion_filter,
+ info->nttrans.in.recursive);
+ NT_STATUS_NOT_OK_RETURN(status);
+ }
+
+ /* we update the max_buffer_size on each call, but we do not
+ update the recursive flag or filter */
+ f->notify_buffer->max_buffer_size = info->nttrans.in.buffer_size;
+
+ pending = talloc(f->notify_buffer, struct notify_pending);
+ NT_STATUS_HAVE_NO_MEMORY(pending);
+
+ pending->req = talloc_reference(pending, req);
+ NT_STATUS_HAVE_NO_MEMORY(pending->req);
+ pending->info = info;
+
+ DLIST_ADD_END(f->notify_buffer->pending, pending);
+
+ /* if the buffer is empty then start waiting */
+ if (f->notify_buffer->num_changes == 0 &&
+ !f->notify_buffer->overflowed) {
+ struct pvfs_wait *wait_handle;
+ wait_handle = pvfs_wait_message(pvfs, req, -1,
+ timeval_zero(),
+ pvfs_notify_end,
+ f->notify_buffer);
+ NT_STATUS_HAVE_NO_MEMORY(wait_handle);
+ talloc_steal(req, wait_handle);
+ return NT_STATUS_OK;
+ }
+
+ req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
+ pvfs_notify_send(f->notify_buffer, NT_STATUS_OK, false);
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/ntvfs/posix/pvfs_open.c b/source4/ntvfs/posix/pvfs_open.c
new file mode 100644
index 0000000..860861d
--- /dev/null
+++ b/source4/ntvfs/posix/pvfs_open.c
@@ -0,0 +1,2094 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ POSIX NTVFS backend - open and close
+
+ Copyright (C) Andrew Tridgell 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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 "vfs_posix.h"
+#include "system/dir.h"
+#include "system/time.h"
+#include "../lib/util/dlinklist.h"
+#include "messaging/messaging.h"
+#include "librpc/gen_ndr/xattr.h"
+
+/*
+ find open file handle given fnum
+*/
+struct pvfs_file *pvfs_find_fd(struct pvfs_state *pvfs,
+ struct ntvfs_request *req, struct ntvfs_handle *h)
+{
+ void *p;
+ struct pvfs_file *f;
+
+ p = ntvfs_handle_get_backend_data(h, pvfs->ntvfs);
+ if (!p) return NULL;
+
+ f = talloc_get_type(p, struct pvfs_file);
+ if (!f) return NULL;
+
+ return f;
+}
+
+/*
+ cleanup a open directory handle
+*/
+static int pvfs_dir_handle_destructor(struct pvfs_file_handle *h)
+{
+ if (h->have_opendb_entry) {
+ struct odb_lock *lck;
+ NTSTATUS status;
+ const char *delete_path = NULL;
+
+ lck = odb_lock(h, h->pvfs->odb_context, &h->odb_locking_key);
+ if (lck == NULL) {
+ DEBUG(0,("Unable to lock opendb for close\n"));
+ return 0;
+ }
+
+ status = odb_close_file(lck, h, &delete_path);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("Unable to remove opendb entry for '%s' - %s\n",
+ h->name->full_name, nt_errstr(status)));
+ }
+
+ if (h->name->stream_name == NULL && delete_path) {
+ status = pvfs_xattr_unlink_hook(h->pvfs, delete_path);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("Warning: xattr unlink hook failed for '%s' - %s\n",
+ delete_path, nt_errstr(status)));
+ }
+ if (pvfs_sys_rmdir(h->pvfs, delete_path, h->name->allow_override) != 0) {
+ DEBUG(0,("pvfs_dir_handle_destructor: failed to rmdir '%s' - %s\n",
+ delete_path, strerror(errno)));
+ }
+ }
+
+ talloc_free(lck);
+ }
+
+ return 0;
+}
+
+/*
+ cleanup a open directory fnum
+*/
+static int pvfs_dir_fnum_destructor(struct pvfs_file *f)
+{
+ DLIST_REMOVE(f->pvfs->files.list, f);
+ ntvfs_handle_remove_backend_data(f->ntvfs, f->pvfs->ntvfs);
+
+ return 0;
+}
+
+/*
+ setup any EAs and the ACL on newly created files/directories
+*/
+static NTSTATUS pvfs_open_setup_eas_acl(struct pvfs_state *pvfs,
+ struct ntvfs_request *req,
+ struct pvfs_filename *name,
+ int fd, struct pvfs_file *f,
+ union smb_open *io,
+ struct security_descriptor *sd)
+{
+ NTSTATUS status = NT_STATUS_OK;
+
+ /* setup any EAs that were asked for */
+ if (io->ntcreatex.in.ea_list) {
+ status = pvfs_setfileinfo_ea_set(pvfs, name, fd,
+ io->ntcreatex.in.ea_list->num_eas,
+ io->ntcreatex.in.ea_list->eas);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+
+ /* setup an initial sec_desc if requested */
+ if (sd && (sd->type & SEC_DESC_DACL_PRESENT)) {
+ union smb_setfileinfo set;
+/*
+ * TODO: set the full ACL!
+ * - vista denies the creation of the file with NT_STATUS_PRIVILEGE_NOT_HELD,
+ * when a SACL is present on the sd,
+ * but the user doesn't have SeSecurityPrivilege
+ * - w2k3 allows it
+ */
+ set.set_secdesc.in.file.ntvfs = f->ntvfs;
+ set.set_secdesc.in.secinfo_flags = SECINFO_DACL;
+ set.set_secdesc.in.sd = sd;
+
+ status = pvfs_acl_set(pvfs, req, name, fd, SEC_STD_WRITE_DAC, &set);
+ }
+
+ return status;
+}
+
+/*
+ form the lock context used for opendb locking. Note that we must
+ zero here to take account of possible padding on some architectures
+*/
+NTSTATUS pvfs_locking_key(struct pvfs_filename *name,
+ TALLOC_CTX *mem_ctx, DATA_BLOB *key)
+{
+ struct {
+ dev_t device;
+ ino_t inode;
+ } lock_context;
+ ZERO_STRUCT(lock_context);
+
+ lock_context.device = name->st.st_dev;
+ lock_context.inode = name->st.st_ino;
+
+ *key = data_blob_talloc(mem_ctx, &lock_context, sizeof(lock_context));
+ if (key->data == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ open a directory
+*/
+static NTSTATUS pvfs_open_directory(struct pvfs_state *pvfs,
+ struct ntvfs_request *req,
+ struct pvfs_filename *name,
+ union smb_open *io)
+{
+ struct pvfs_file *f;
+ struct ntvfs_handle *h;
+ NTSTATUS status;
+ uint32_t create_action;
+ uint32_t access_mask = io->generic.in.access_mask;
+ struct odb_lock *lck;
+ bool del_on_close;
+ uint32_t create_options;
+ uint32_t share_access;
+ bool forced;
+ struct security_descriptor *sd = NULL;
+
+ create_options = io->generic.in.create_options;
+ share_access = io->generic.in.share_access;
+
+ forced = (io->generic.in.create_options & NTCREATEX_OPTIONS_DIRECTORY)?true:false;
+
+ if (name->stream_name) {
+ if (forced) {
+ return NT_STATUS_NOT_A_DIRECTORY;
+ } else {
+ return NT_STATUS_FILE_IS_A_DIRECTORY;
+ }
+ }
+
+ /* if the client says it must be a directory, and it isn't,
+ then fail */
+ if (name->exists && !(name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) {
+ return NT_STATUS_NOT_A_DIRECTORY;
+ }
+
+ /* found with gentest */
+ if (io->ntcreatex.in.access_mask == SEC_FLAG_MAXIMUM_ALLOWED &&
+ (io->ntcreatex.in.create_options & NTCREATEX_OPTIONS_DIRECTORY) &&
+ (io->ntcreatex.in.create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
+ DEBUG(3,(__location__ ": Invalid access_mask/create_options 0x%08x 0x%08x for %s\n",
+ io->ntcreatex.in.access_mask, io->ntcreatex.in.create_options, name->original_name));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ switch (io->generic.in.open_disposition) {
+ case NTCREATEX_DISP_OPEN_IF:
+ break;
+
+ case NTCREATEX_DISP_OPEN:
+ if (!name->exists) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+ break;
+
+ case NTCREATEX_DISP_CREATE:
+ if (name->exists) {
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+ break;
+
+ case NTCREATEX_DISP_OVERWRITE_IF:
+ case NTCREATEX_DISP_OVERWRITE:
+ case NTCREATEX_DISP_SUPERSEDE:
+ default:
+ DEBUG(3,(__location__ ": Invalid open disposition 0x%08x for %s\n",
+ io->generic.in.open_disposition, name->original_name));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ status = ntvfs_handle_new(pvfs->ntvfs, req, &h);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ f = talloc(h, struct pvfs_file);
+ if (f == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ f->handle = talloc(f, struct pvfs_file_handle);
+ if (f->handle == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (name->exists) {
+ /* check the security descriptor */
+ status = pvfs_access_check(pvfs, req, name, &access_mask);
+ } else {
+ sd = io->ntcreatex.in.sec_desc;
+ status = pvfs_access_check_create(pvfs, req, name, &access_mask, true, &sd);
+ }
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ if (io->generic.in.query_maximal_access) {
+ status = pvfs_access_maximal_allowed(pvfs, req, name,
+ &io->generic.out.maximal_access);
+ NT_STATUS_NOT_OK_RETURN(status);
+ }
+
+ f->ntvfs = h;
+ f->pvfs = pvfs;
+ f->pending_list = NULL;
+ f->lock_count = 0;
+ f->share_access = io->generic.in.share_access;
+ f->impersonation = io->generic.in.impersonation;
+ f->access_mask = access_mask;
+ f->brl_handle = NULL;
+ f->notify_buffer = NULL;
+ f->search = NULL;
+
+ f->handle->pvfs = pvfs;
+ f->handle->name = talloc_steal(f->handle, name);
+ f->handle->fd = -1;
+ f->handle->odb_locking_key = data_blob(NULL, 0);
+ f->handle->create_options = io->generic.in.create_options;
+ f->handle->private_flags = io->generic.in.private_flags;
+ f->handle->seek_offset = 0;
+ f->handle->position = 0;
+ f->handle->mode = 0;
+ f->handle->oplock = NULL;
+ ZERO_STRUCT(f->handle->write_time);
+ f->handle->open_completed = false;
+
+ if ((create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
+ pvfs_directory_empty(pvfs, f->handle->name)) {
+ del_on_close = true;
+ } else {
+ del_on_close = false;
+ }
+
+ if (name->exists) {
+ /* form the lock context used for opendb locking */
+ status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* get a lock on this file before the actual open */
+ lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
+ if (lck == NULL) {
+ DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
+ name->full_name));
+ /* we were supposed to do a blocking lock, so something
+ is badly wrong! */
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ /* see if we are allowed to open at the same time as existing opens */
+ status = odb_can_open(lck, name->stream_id,
+ share_access, access_mask, del_on_close,
+ io->generic.in.open_disposition, false);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(lck);
+ return status;
+ }
+
+ /* now really mark the file as open */
+ status = odb_open_file(lck, f->handle, name->full_name,
+ NULL, name->dos.write_time,
+ false, OPLOCK_NONE, NULL);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(lck);
+ return status;
+ }
+
+ f->handle->have_opendb_entry = true;
+ }
+
+ DLIST_ADD(pvfs->files.list, f);
+
+ /* setup destructors to avoid leaks on abnormal termination */
+ talloc_set_destructor(f->handle, pvfs_dir_handle_destructor);
+ talloc_set_destructor(f, pvfs_dir_fnum_destructor);
+
+ if (!name->exists) {
+ uint32_t attrib = io->generic.in.file_attr | FILE_ATTRIBUTE_DIRECTORY;
+ mode_t mode = pvfs_fileperms(pvfs, attrib);
+
+ if (pvfs_sys_mkdir(pvfs, name->full_name, mode, name->allow_override) == -1) {
+ return pvfs_map_errno(pvfs,errno);
+ }
+
+ pvfs_xattr_unlink_hook(pvfs, name->full_name);
+
+ status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname, 0, &name);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto cleanup_delete;
+ }
+
+ status = pvfs_open_setup_eas_acl(pvfs, req, name, -1, f, io, sd);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto cleanup_delete;
+ }
+
+ /* form the lock context used for opendb locking */
+ status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
+ if (lck == NULL) {
+ DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
+ name->full_name));
+ /* we were supposed to do a blocking lock, so something
+ is badly wrong! */
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ status = odb_can_open(lck, name->stream_id,
+ share_access, access_mask, del_on_close,
+ io->generic.in.open_disposition, false);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto cleanup_delete;
+ }
+
+ status = odb_open_file(lck, f->handle, name->full_name,
+ NULL, name->dos.write_time,
+ false, OPLOCK_NONE, NULL);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ goto cleanup_delete;
+ }
+
+ f->handle->have_opendb_entry = true;
+
+ create_action = NTCREATEX_ACTION_CREATED;
+
+ notify_trigger(pvfs->notify_context,
+ NOTIFY_ACTION_ADDED,
+ FILE_NOTIFY_CHANGE_DIR_NAME,
+ name->full_name);
+ } else {
+ create_action = NTCREATEX_ACTION_EXISTED;
+ }
+
+ if (!name->exists) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ if (io->generic.in.query_on_disk_id) {
+ ZERO_ARRAY(io->generic.out.on_disk_id);
+ SBVAL(io->generic.out.on_disk_id, 0, name->st.st_ino);
+ SBVAL(io->generic.out.on_disk_id, 8, name->st.st_dev);
+ }
+
+ /* the open succeeded, keep this handle permanently */
+ status = ntvfs_handle_set_backend_data(h, pvfs->ntvfs, f);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto cleanup_delete;
+ }
+
+ f->handle->open_completed = true;
+
+ io->generic.out.oplock_level = OPLOCK_NONE;
+ io->generic.out.file.ntvfs = h;
+ io->generic.out.create_action = create_action;
+ io->generic.out.create_time = name->dos.create_time;
+ io->generic.out.access_time = name->dos.access_time;
+ io->generic.out.write_time = name->dos.write_time;
+ io->generic.out.change_time = name->dos.change_time;
+ io->generic.out.attrib = name->dos.attrib;
+ io->generic.out.alloc_size = name->dos.alloc_size;
+ io->generic.out.size = name->st.st_size;
+ io->generic.out.file_type = FILE_TYPE_DISK;
+ io->generic.out.ipc_state = 0;
+ io->generic.out.is_directory = 1;
+
+ return NT_STATUS_OK;
+
+cleanup_delete:
+ pvfs_sys_rmdir(pvfs, name->full_name, name->allow_override);
+ return status;
+}
+
+/*
+ destroy a struct pvfs_file_handle
+*/
+static int pvfs_handle_destructor(struct pvfs_file_handle *h)
+{
+ talloc_free(h->write_time.update_event);
+ h->write_time.update_event = NULL;
+
+ if ((h->create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
+ h->name->stream_name) {
+ NTSTATUS status;
+ status = pvfs_stream_delete(h->pvfs, h->name, h->fd);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("Failed to delete stream '%s' on close of '%s'\n",
+ h->name->stream_name, h->name->full_name));
+ }
+ }
+
+ if (h->fd != -1) {
+ if (close(h->fd) != 0) {
+ DEBUG(0,("pvfs_handle_destructor: close(%d) failed for %s - %s\n",
+ h->fd, h->name->full_name, strerror(errno)));
+ }
+ h->fd = -1;
+ }
+
+ if (!h->write_time.update_forced &&
+ h->write_time.update_on_close &&
+ h->write_time.close_time == 0) {
+ struct timeval tv;
+ tv = timeval_current();
+ h->write_time.close_time = timeval_to_nttime(&tv);
+ }
+
+ if (h->have_opendb_entry) {
+ struct odb_lock *lck;
+ NTSTATUS status;
+ const char *delete_path = NULL;
+
+ lck = odb_lock(h, h->pvfs->odb_context, &h->odb_locking_key);
+ if (lck == NULL) {
+ DEBUG(0,("Unable to lock opendb for close\n"));
+ return 0;
+ }
+
+ if (h->write_time.update_forced) {
+ status = odb_get_file_infos(h->pvfs->odb_context,
+ &h->odb_locking_key,
+ NULL,
+ &h->write_time.close_time);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("Unable get write time for '%s' - %s\n",
+ h->name->full_name, nt_errstr(status)));
+ }
+
+ h->write_time.update_forced = false;
+ h->write_time.update_on_close = true;
+ } else if (h->write_time.update_on_close) {
+ status = odb_set_write_time(lck, h->write_time.close_time, true);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("Unable set write time for '%s' - %s\n",
+ h->name->full_name, nt_errstr(status)));
+ }
+ }
+
+ status = odb_close_file(lck, h, &delete_path);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("Unable to remove opendb entry for '%s' - %s\n",
+ h->name->full_name, nt_errstr(status)));
+ }
+
+ if (h->name->stream_name == NULL &&
+ h->open_completed && delete_path) {
+ status = pvfs_xattr_unlink_hook(h->pvfs, delete_path);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("Warning: xattr unlink hook failed for '%s' - %s\n",
+ delete_path, nt_errstr(status)));
+ }
+ if (pvfs_sys_unlink(h->pvfs, delete_path, h->name->allow_override) != 0) {
+ DEBUG(0,("pvfs_close: failed to delete '%s' - %s\n",
+ delete_path, strerror(errno)));
+ } else {
+ notify_trigger(h->pvfs->notify_context,
+ NOTIFY_ACTION_REMOVED,
+ FILE_NOTIFY_CHANGE_FILE_NAME,
+ delete_path);
+ }
+ h->write_time.update_on_close = false;
+ }
+
+ talloc_free(lck);
+ }
+
+ if (h->write_time.update_on_close) {
+ struct timeval tv[2];
+
+ nttime_to_timeval(&tv[0], h->name->dos.access_time);
+ nttime_to_timeval(&tv[1], h->write_time.close_time);
+
+ if (!timeval_is_zero(&tv[0]) || !timeval_is_zero(&tv[1])) {
+ if (utimes(h->name->full_name, tv) == -1) {
+ DEBUG(3,("pvfs_handle_destructor: utimes() failed '%s' - %s\n",
+ h->name->full_name, strerror(errno)));
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+/*
+ destroy a struct pvfs_file
+*/
+static int pvfs_fnum_destructor(struct pvfs_file *f)
+{
+ DLIST_REMOVE(f->pvfs->files.list, f);
+ pvfs_lock_close(f->pvfs, f);
+ ntvfs_handle_remove_backend_data(f->ntvfs, f->pvfs->ntvfs);
+
+ return 0;
+}
+
+
+/*
+ form the lock context used for byte range locking. This is separate
+ from the locking key used for opendb locking as it needs to take
+ account of file streams (each stream is a separate byte range
+ locking space)
+*/
+static NTSTATUS pvfs_brl_locking_handle(TALLOC_CTX *mem_ctx,
+ struct pvfs_filename *name,
+ struct ntvfs_handle *ntvfs,
+ struct brl_handle **_h)
+{
+ DATA_BLOB odb_key, key;
+ NTSTATUS status;
+ struct brl_handle *h;
+
+ status = pvfs_locking_key(name, mem_ctx, &odb_key);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ if (name->stream_name == NULL) {
+ key = odb_key;
+ } else {
+ key = data_blob_talloc(mem_ctx, NULL,
+ odb_key.length + strlen(name->stream_name) + 1);
+ NT_STATUS_HAVE_NO_MEMORY(key.data);
+ memcpy(key.data, odb_key.data, odb_key.length);
+ memcpy(key.data + odb_key.length,
+ name->stream_name, strlen(name->stream_name) + 1);
+ data_blob_free(&odb_key);
+ }
+
+ h = brlock_create_handle(mem_ctx, ntvfs, &key);
+ NT_STATUS_HAVE_NO_MEMORY(h);
+
+ *_h = h;
+ return NT_STATUS_OK;
+}
+
+/*
+ create a new file
+*/
+static NTSTATUS pvfs_create_file(struct pvfs_state *pvfs,
+ struct ntvfs_request *req,
+ struct pvfs_filename *name,
+ union smb_open *io)
+{
+ struct pvfs_file *f;
+ NTSTATUS status;
+ struct ntvfs_handle *h;
+ int flags, fd;
+ struct odb_lock *lck;
+ uint32_t create_options = io->generic.in.create_options;
+ uint32_t share_access = io->generic.in.share_access;
+ uint32_t access_mask = io->generic.in.access_mask;
+ mode_t mode;
+ uint32_t attrib;
+ bool del_on_close;
+ struct pvfs_filename *parent;
+ uint32_t oplock_level = OPLOCK_NONE, oplock_granted;
+ bool allow_level_II_oplock = false;
+ struct security_descriptor *sd = NULL;
+
+ if (io->ntcreatex.in.file_attr & ~FILE_ATTRIBUTE_ALL_MASK) {
+ DEBUG(3,(__location__ ": Invalid file_attr 0x%08x for %s\n",
+ io->ntcreatex.in.file_attr, name->original_name));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (io->ntcreatex.in.file_attr & FILE_ATTRIBUTE_ENCRYPTED) {
+ DEBUG(3,(__location__ ": Invalid encryption request for %s\n",
+ name->original_name));
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if ((io->ntcreatex.in.file_attr & FILE_ATTRIBUTE_READONLY) &&
+ (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
+ DEBUG(4,(__location__ ": Invalid delete on close for readonly file %s\n",
+ name->original_name));
+ return NT_STATUS_CANNOT_DELETE;
+ }
+
+ sd = io->ntcreatex.in.sec_desc;
+ status = pvfs_access_check_create(pvfs, req, name, &access_mask, false, &sd);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ /* check that the parent isn't opened with delete on close set */
+ status = pvfs_resolve_parent(pvfs, req, name, &parent);
+ if (NT_STATUS_IS_OK(status)) {
+ DATA_BLOB locking_key;
+ status = pvfs_locking_key(parent, req, &locking_key);
+ NT_STATUS_NOT_OK_RETURN(status);
+ status = odb_get_file_infos(pvfs->odb_context, &locking_key,
+ &del_on_close, NULL);
+ NT_STATUS_NOT_OK_RETURN(status);
+ if (del_on_close) {
+ return NT_STATUS_DELETE_PENDING;
+ }
+ }
+
+ if (access_mask & (SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA)) {
+ flags = O_RDWR;
+ } else {
+ flags = O_RDONLY;
+ }
+
+ status = ntvfs_handle_new(pvfs->ntvfs, req, &h);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ f = talloc(h, struct pvfs_file);
+ NT_STATUS_HAVE_NO_MEMORY(f);
+
+ f->handle = talloc(f, struct pvfs_file_handle);
+ NT_STATUS_HAVE_NO_MEMORY(f->handle);
+
+ attrib = io->ntcreatex.in.file_attr | FILE_ATTRIBUTE_ARCHIVE;
+ mode = pvfs_fileperms(pvfs, attrib);
+
+ /* create the file */
+ fd = pvfs_sys_open(pvfs, name->full_name, flags | O_CREAT | O_EXCL| O_NONBLOCK, mode, name->allow_override);
+ if (fd == -1) {
+ return pvfs_map_errno(pvfs, errno);
+ }
+
+ pvfs_xattr_unlink_hook(pvfs, name->full_name);
+
+ /* if this was a stream create then create the stream as well */
+ if (name->stream_name) {
+ status = pvfs_stream_create(pvfs, name, fd);
+ if (!NT_STATUS_IS_OK(status)) {
+ close(fd);
+ return status;
+ }
+ }
+
+ /* re-resolve the open fd */
+ status = pvfs_resolve_name_fd(pvfs, fd, name, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ close(fd);
+ return status;
+ }
+
+ /* support initial alloc sizes */
+ name->dos.alloc_size = io->ntcreatex.in.alloc_size;
+ name->dos.attrib = attrib;
+ status = pvfs_dosattrib_save(pvfs, name, fd);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto cleanup_delete;
+ }
+
+
+ status = pvfs_open_setup_eas_acl(pvfs, req, name, fd, f, io, sd);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto cleanup_delete;
+ }
+
+ if (io->generic.in.query_maximal_access) {
+ status = pvfs_access_maximal_allowed(pvfs, req, name,
+ &io->generic.out.maximal_access);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto cleanup_delete;
+ }
+ }
+
+ if (io->generic.in.query_on_disk_id) {
+ ZERO_ARRAY(io->generic.out.on_disk_id);
+ SBVAL(io->generic.out.on_disk_id, 0, name->st.st_ino);
+ SBVAL(io->generic.out.on_disk_id, 8, name->st.st_dev);
+ }
+
+ /* form the lock context used for byte range locking and
+ opendb locking */
+ status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto cleanup_delete;
+ }
+
+ status = pvfs_brl_locking_handle(f, name, h, &f->brl_handle);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto cleanup_delete;
+ }
+
+ /* grab a lock on the open file record */
+ lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
+ if (lck == NULL) {
+ DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
+ name->full_name));
+ /* we were supposed to do a blocking lock, so something
+ is badly wrong! */
+ status = NT_STATUS_INTERNAL_DB_CORRUPTION;
+ goto cleanup_delete;
+ }
+
+ if (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) {
+ del_on_close = true;
+ } else {
+ del_on_close = false;
+ }
+
+ if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
+ oplock_level = OPLOCK_NONE;
+ } else if (io->ntcreatex.in.flags & NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK) {
+ oplock_level = OPLOCK_BATCH;
+ } else if (io->ntcreatex.in.flags & NTCREATEX_FLAGS_REQUEST_OPLOCK) {
+ oplock_level = OPLOCK_EXCLUSIVE;
+ }
+
+ if (req->client_caps & NTVFS_CLIENT_CAP_LEVEL_II_OPLOCKS) {
+ allow_level_II_oplock = true;
+ }
+
+ status = odb_can_open(lck, name->stream_id,
+ share_access, access_mask, del_on_close,
+ io->generic.in.open_disposition, false);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(lck);
+ /* bad news, we must have hit a race - we don't delete the file
+ here as the most likely scenario is that someone else created
+ the file at the same time */
+ close(fd);
+ return status;
+ }
+
+ f->ntvfs = h;
+ f->pvfs = pvfs;
+ f->pending_list = NULL;
+ f->lock_count = 0;
+ f->share_access = io->generic.in.share_access;
+ f->access_mask = access_mask;
+ f->impersonation = io->generic.in.impersonation;
+ f->notify_buffer = NULL;
+ f->search = NULL;
+
+ f->handle->pvfs = pvfs;
+ f->handle->name = talloc_steal(f->handle, name);
+ f->handle->fd = fd;
+ f->handle->create_options = io->generic.in.create_options;
+ f->handle->private_flags = io->generic.in.private_flags;
+ f->handle->seek_offset = 0;
+ f->handle->position = 0;
+ f->handle->mode = 0;
+ f->handle->oplock = NULL;
+ f->handle->have_opendb_entry = true;
+ ZERO_STRUCT(f->handle->write_time);
+ f->handle->open_completed = false;
+
+ status = odb_open_file(lck, f->handle, name->full_name,
+ &f->handle->fd, name->dos.write_time,
+ allow_level_II_oplock,
+ oplock_level, &oplock_granted);
+ talloc_free(lck);
+ if (!NT_STATUS_IS_OK(status)) {
+ /* bad news, we must have hit a race - we don't delete the file
+ here as the most likely scenario is that someone else created
+ the file at the same time */
+ close(fd);
+ return status;
+ }
+
+ DLIST_ADD(pvfs->files.list, f);
+
+ /* setup a destructor to avoid file descriptor leaks on
+ abnormal termination */
+ talloc_set_destructor(f, pvfs_fnum_destructor);
+ talloc_set_destructor(f->handle, pvfs_handle_destructor);
+
+ 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)) {
+ return status;
+ }
+ }
+
+ io->generic.out.oplock_level = oplock_granted;
+ io->generic.out.file.ntvfs = f->ntvfs;
+ io->generic.out.create_action = NTCREATEX_ACTION_CREATED;
+ io->generic.out.create_time = name->dos.create_time;
+ io->generic.out.access_time = name->dos.access_time;
+ io->generic.out.write_time = name->dos.write_time;
+ io->generic.out.change_time = name->dos.change_time;
+ io->generic.out.attrib = name->dos.attrib;
+ io->generic.out.alloc_size = name->dos.alloc_size;
+ io->generic.out.size = name->st.st_size;
+ io->generic.out.file_type = FILE_TYPE_DISK;
+ io->generic.out.ipc_state = 0;
+ io->generic.out.is_directory = 0;
+
+ /* success - keep the file handle */
+ status = ntvfs_handle_set_backend_data(h, pvfs->ntvfs, f);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto cleanup_delete;
+ }
+
+ f->handle->open_completed = true;
+
+ notify_trigger(pvfs->notify_context,
+ NOTIFY_ACTION_ADDED,
+ FILE_NOTIFY_CHANGE_FILE_NAME,
+ name->full_name);
+
+ return NT_STATUS_OK;
+
+cleanup_delete:
+ close(fd);
+ pvfs_sys_unlink(pvfs, name->full_name, name->allow_override);
+ return status;
+}
+
+/*
+ state of a pending retry
+*/
+struct pvfs_odb_retry {
+ struct ntvfs_module_context *ntvfs;
+ struct ntvfs_request *req;
+ 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 request */
+static int pvfs_odb_retry_destructor(struct pvfs_odb_retry *r)
+{
+ struct pvfs_state *pvfs = talloc_get_type(r->ntvfs->private_data,
+ struct pvfs_state);
+ if (r->odb_locking_key.data) {
+ struct odb_lock *lck;
+ lck = odb_lock(r->req, pvfs->odb_context, &r->odb_locking_key);
+ if (lck != NULL) {
+ odb_remove_pending(lck, r);
+ }
+ talloc_free(lck);
+ }
+ return 0;
+}
+
+static void pvfs_odb_retry_callback(void *_r, enum pvfs_wait_notice reason)
+{
+ 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_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 = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ 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_EQUAL(NT_STATUS_OBJECT_NAME_NOT_FOUND,status)) {
+ /*
+ * maybe only a unix application
+ * has the file open
+ */
+ data_blob_free(&r->odb_locking_key);
+ } else if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ 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);
+
+ 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);
+ struct timeval *final_timeout = NULL;
+ NTSTATUS status;
+
+ if (private_data) {
+ final_timeout = talloc_get_type(private_data,
+ struct timeval);
+ }
+
+ /* w2k3 ignores SMBntcancel for outstanding open requests. It's probably
+ just a bug in their server, but we better do the same */
+ if (reason == PVFS_WAIT_CANCEL) {
+ return;
+ }
+
+ if (reason == PVFS_WAIT_TIMEOUT) {
+ if (final_timeout &&
+ !timeval_expired(final_timeout)) {
+ /*
+ * we need to retry periodictly
+ * after an EAGAIN as there's
+ * no way the kernel tell us
+ * an oplock is released.
+ */
+ goto retry;
+ }
+ /* if it timed out, then give the failure
+ immediately */
+ talloc_free(r);
+ req->async_states->status = NT_STATUS_SHARING_VIOLATION;
+ req->async_states->send_fn(req);
+ return;
+ }
+
+retry:
+ talloc_free(r);
+
+ /* 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_open(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;
+
+ /* send the reply up the chain */
+ req->async_states->status = status;
+ req->async_states->send_fn(req);
+}
+
+
+/*
+ special handling for openx DENY_DOS semantics
+
+ This function attempts a reference open using an existing handle. If its allowed,
+ then it returns NT_STATUS_OK, otherwise it returns any other code and normal
+ open processing continues.
+*/
+static NTSTATUS pvfs_open_deny_dos(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_open *io,
+ struct pvfs_file *f, struct odb_lock *lck)
+{
+ struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ struct pvfs_file *f2;
+ struct pvfs_filename *name;
+ NTSTATUS status;
+
+ /* search for an existing open with the right parameters. Note
+ the magic ntcreatex options flag, which is set in the
+ generic mapping code. This might look ugly, but its
+ actually pretty much now w2k does it internally as well.
+
+ If you look at the BASE-DENYDOS test you will see that a
+ DENY_DOS is a very special case, and in the right
+ circumstances you actually get the _same_ handle back
+ twice, rather than a new handle.
+ */
+ for (f2=pvfs->files.list;f2;f2=f2->next) {
+ if (f2 != f &&
+ f2->ntvfs->session_info == req->session_info &&
+ f2->ntvfs->smbpid == req->smbpid &&
+ (f2->handle->private_flags &
+ (NTCREATEX_FLAG_DENY_DOS |
+ NTCREATEX_FLAG_DENY_FCB)) &&
+ (f2->access_mask & SEC_FILE_WRITE_DATA) &&
+ strcasecmp_m(f2->handle->name->original_name,
+ io->generic.in.fname)==0) {
+ break;
+ }
+ }
+
+ if (!f2) {
+ return NT_STATUS_SHARING_VIOLATION;
+ }
+
+ /* quite an insane set of semantics ... */
+ if (is_exe_filename(io->generic.in.fname) &&
+ (f2->handle->private_flags & NTCREATEX_FLAG_DENY_DOS)) {
+ return NT_STATUS_SHARING_VIOLATION;
+ }
+
+ /*
+ setup a reference to the existing handle
+ */
+ talloc_free(f->handle);
+ f->handle = talloc_reference(f, f2->handle);
+
+ talloc_free(lck);
+
+ name = f->handle->name;
+
+ io->generic.out.oplock_level = OPLOCK_NONE;
+ io->generic.out.file.ntvfs = f->ntvfs;
+ io->generic.out.create_action = NTCREATEX_ACTION_EXISTED;
+ io->generic.out.create_time = name->dos.create_time;
+ io->generic.out.access_time = name->dos.access_time;
+ io->generic.out.write_time = name->dos.write_time;
+ io->generic.out.change_time = name->dos.change_time;
+ io->generic.out.attrib = name->dos.attrib;
+ io->generic.out.alloc_size = name->dos.alloc_size;
+ io->generic.out.size = name->st.st_size;
+ io->generic.out.file_type = FILE_TYPE_DISK;
+ io->generic.out.ipc_state = 0;
+ io->generic.out.is_directory = 0;
+
+ status = ntvfs_handle_set_backend_data(f->ntvfs, ntvfs, f);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ return NT_STATUS_OK;
+}
+
+
+
+/*
+ setup for a open retry after a sharing violation
+*/
+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,
+ NTSTATUS parent_status)
+{
+ struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ NTSTATUS status;
+ struct timeval end_time;
+ struct timeval *final_timeout = NULL;
+
+ if (io->generic.in.private_flags &
+ (NTCREATEX_FLAG_DENY_DOS | NTCREATEX_FLAG_DENY_FCB)) {
+ /* see if we can satisfy the request using the special DENY_DOS
+ code */
+ status = pvfs_open_deny_dos(ntvfs, req, io, f, lck);
+ if (NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+
+ /* the retry should allocate a new file handle */
+ talloc_free(f);
+
+ 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 if (NT_STATUS_EQUAL(parent_status, STATUS_MORE_ENTRIES)) {
+ /*
+ * we got EAGAIN which means a unix application
+ * has an oplock or share mode
+ *
+ * we retry every 4/5 of the sharing violation delay
+ * to see if the unix application
+ * has released the oplock or share mode.
+ */
+ final_timeout = talloc(req, struct timeval);
+ NT_STATUS_HAVE_NO_MEMORY(final_timeout);
+ *final_timeout = timeval_add(&req->statistics.request_time,
+ pvfs->oplock_break_timeout,
+ 0);
+ end_time = timeval_current_ofs_usec((pvfs->sharing_violation_delay*4)/5);
+ end_time = timeval_min(final_timeout, &end_time);
+ } else {
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ return pvfs_odb_retry_setup(ntvfs, req, lck, end_time, io,
+ final_timeout, pvfs_retry_open_sharing);
+}
+
+/*
+ open a file
+*/
+NTSTATUS pvfs_open(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_open *io)
+{
+ struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ int flags = 0;
+ struct pvfs_filename *name;
+ struct pvfs_file *f;
+ struct ntvfs_handle *h;
+ NTSTATUS status;
+ int fd, count;
+ struct odb_lock *lck;
+ uint32_t create_options;
+ uint32_t create_options_must_ignore_mask;
+ uint32_t share_access;
+ uint32_t access_mask;
+ uint32_t create_action = NTCREATEX_ACTION_EXISTED;
+ bool del_on_close;
+ bool stream_existed, stream_truncate=false;
+ uint32_t oplock_level = OPLOCK_NONE, oplock_granted;
+ bool allow_level_II_oplock = false;
+
+ /* use the generic mapping code to avoid implementing all the
+ different open calls. */
+ if (io->generic.level != RAW_OPEN_GENERIC &&
+ io->generic.level != RAW_OPEN_NTTRANS_CREATE) {
+ return ntvfs_map_open(ntvfs, req, io);
+ }
+
+ ZERO_STRUCT(io->generic.out);
+
+ create_options = io->generic.in.create_options;
+ share_access = io->generic.in.share_access;
+ access_mask = io->generic.in.access_mask;
+
+ if (share_access & ~NTCREATEX_SHARE_ACCESS_MASK) {
+ DEBUG(3,(__location__ ": Invalid share_access 0x%08x for %s\n",
+ share_access, io->ntcreatex.in.fname));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /*
+ * These options are ignored,
+ * but we reuse some of them as private values for the generic mapping
+ */
+ create_options_must_ignore_mask = NTCREATEX_OPTIONS_MUST_IGNORE_MASK;
+ create_options &= ~create_options_must_ignore_mask;
+
+ if (create_options & NTCREATEX_OPTIONS_NOT_SUPPORTED_MASK) {
+ DEBUG(2,(__location__ " create_options 0x%x not supported\n",
+ create_options));
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ if (create_options & NTCREATEX_OPTIONS_INVALID_PARAM_MASK) {
+ DEBUG(3,(__location__ ": Invalid create_options 0x%08x for %s\n",
+ create_options, io->ntcreatex.in.fname));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* TODO: When we implement HSM, add a hook here not to pull
+ * the actual file off tape, when this option is passed from
+ * the client */
+ if (create_options & NTCREATEX_OPTIONS_NO_RECALL) {
+ /* no-op */
+ }
+
+ /* TODO: If (unlikely) Linux does a good compressed
+ * filesystem, we might need an ioctl call for this */
+ if (create_options & NTCREATEX_OPTIONS_NO_COMPRESSION) {
+ /* no-op */
+ }
+
+ if (create_options & NTCREATEX_OPTIONS_NO_INTERMEDIATE_BUFFERING) {
+ create_options |= NTCREATEX_OPTIONS_WRITE_THROUGH;
+ }
+
+ /* Open the file with sync, if they asked for it, but
+ 'strict sync = no' turns this client request into a no-op */
+ if (create_options & (NTCREATEX_OPTIONS_WRITE_THROUGH) && pvfs->flags & PVFS_FLAG_STRICT_SYNC) {
+ flags |= O_SYNC;
+ }
+
+
+ /* other create options are not allowed */
+ if ((create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
+ !(access_mask & SEC_STD_DELETE)) {
+ DEBUG(3,(__location__ ": Invalid delete_on_close option 0x%08x with access_mask 0x%08x for %s\n",
+ create_options, access_mask, io->ntcreatex.in.fname));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (access_mask & SEC_MASK_INVALID) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ /* what does this bit really mean?? */
+ if (req->ctx->protocol >= PROTOCOL_SMB2_02 &&
+ access_mask == SEC_STD_SYNCHRONIZE) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ /* cope with non-zero root_fid */
+ if (io->ntcreatex.in.root_fid.ntvfs != NULL) {
+ f = pvfs_find_fd(pvfs, req, io->ntcreatex.in.root_fid.ntvfs);
+ if (f == NULL) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+ if (f->handle->fd != -1) {
+ return NT_STATUS_INVALID_DEVICE_REQUEST;
+ }
+ io->ntcreatex.in.fname = talloc_asprintf(req, "%s\\%s",
+ f->handle->name->original_name,
+ io->ntcreatex.in.fname);
+ NT_STATUS_HAVE_NO_MEMORY(io->ntcreatex.in.fname);
+ }
+
+ if (io->ntcreatex.in.file_attr & (FILE_ATTRIBUTE_DEVICE|
+ FILE_ATTRIBUTE_VOLUME|
+ (~FILE_ATTRIBUTE_ALL_MASK))) {
+ DEBUG(3,(__location__ ": Invalid file_attr 0x%08x for %s\n",
+ io->ntcreatex.in.file_attr, io->ntcreatex.in.fname));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* we ignore some file_attr bits */
+ io->ntcreatex.in.file_attr &= ~(FILE_ATTRIBUTE_NONINDEXED |
+ FILE_ATTRIBUTE_COMPRESSED |
+ FILE_ATTRIBUTE_REPARSE_POINT |
+ FILE_ATTRIBUTE_SPARSE |
+ FILE_ATTRIBUTE_NORMAL);
+
+ /* resolve the cifs name to a posix name */
+ status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname,
+ PVFS_RESOLVE_STREAMS, &name);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* if the client specified that it must not be a directory then
+ check that it isn't */
+ if (name->exists && (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) &&
+ (io->generic.in.create_options & NTCREATEX_OPTIONS_NON_DIRECTORY_FILE)) {
+ return NT_STATUS_FILE_IS_A_DIRECTORY;
+ }
+
+ /* if the client specified that it must be a directory then
+ check that it is */
+ if (name->exists && !(name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) &&
+ (io->generic.in.create_options & NTCREATEX_OPTIONS_DIRECTORY)) {
+ return NT_STATUS_NOT_A_DIRECTORY;
+ }
+
+ /* directory opens are handled separately */
+ if ((name->exists && (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) ||
+ (io->generic.in.create_options & NTCREATEX_OPTIONS_DIRECTORY)) {
+ return pvfs_open_directory(pvfs, req, name, io);
+ }
+
+ /* FILE_ATTRIBUTE_DIRECTORY is ignored if the above test for directory
+ open doesn't match */
+ io->generic.in.file_attr &= ~FILE_ATTRIBUTE_DIRECTORY;
+
+ switch (io->generic.in.open_disposition) {
+ case NTCREATEX_DISP_SUPERSEDE:
+ case NTCREATEX_DISP_OVERWRITE_IF:
+ if (name->stream_name == NULL) {
+ flags |= O_TRUNC;
+ } else {
+ stream_truncate = true;
+ }
+ create_action = NTCREATEX_ACTION_TRUNCATED;
+ break;
+
+ case NTCREATEX_DISP_OPEN:
+ if (!name->stream_exists) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+ break;
+
+ case NTCREATEX_DISP_OVERWRITE:
+ if (!name->stream_exists) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+ if (name->stream_name == NULL) {
+ flags |= O_TRUNC;
+ } else {
+ stream_truncate = true;
+ }
+ create_action = NTCREATEX_ACTION_TRUNCATED;
+ break;
+
+ case NTCREATEX_DISP_CREATE:
+ if (name->stream_exists) {
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+ break;
+
+ case NTCREATEX_DISP_OPEN_IF:
+ break;
+
+ default:
+ DEBUG(3,(__location__ ": Invalid open disposition 0x%08x for %s\n",
+ io->generic.in.open_disposition, name->original_name));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* handle creating a new file separately */
+ if (!name->exists) {
+ status = pvfs_create_file(pvfs, req, name, io);
+ if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_COLLISION)) {
+ return status;
+ }
+
+ /* we've hit a race - the file was created during this call */
+ if (io->generic.in.open_disposition == NTCREATEX_DISP_CREATE) {
+ return status;
+ }
+
+ /* try re-resolving the name */
+ status = pvfs_resolve_name(pvfs, req, io->ntcreatex.in.fname, 0, &name);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ /* fall through to a normal open */
+ }
+
+ if ((name->dos.attrib & FILE_ATTRIBUTE_READONLY) &&
+ (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE)) {
+ return NT_STATUS_CANNOT_DELETE;
+ }
+
+ /* check the security descriptor */
+ status = pvfs_access_check(pvfs, req, name, &access_mask);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ if (io->generic.in.query_maximal_access) {
+ status = pvfs_access_maximal_allowed(pvfs, req, name,
+ &io->generic.out.maximal_access);
+ NT_STATUS_NOT_OK_RETURN(status);
+ }
+
+ if (io->generic.in.query_on_disk_id) {
+ ZERO_ARRAY(io->generic.out.on_disk_id);
+ SBVAL(io->generic.out.on_disk_id, 0, name->st.st_ino);
+ SBVAL(io->generic.out.on_disk_id, 8, name->st.st_dev);
+ }
+
+ status = ntvfs_handle_new(pvfs->ntvfs, req, &h);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ f = talloc(h, struct pvfs_file);
+ if (f == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ f->handle = talloc(f, struct pvfs_file_handle);
+ if (f->handle == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ f->ntvfs = h;
+ f->pvfs = pvfs;
+ f->pending_list = NULL;
+ f->lock_count = 0;
+ f->share_access = io->generic.in.share_access;
+ f->access_mask = access_mask;
+ f->impersonation = io->generic.in.impersonation;
+ f->notify_buffer = NULL;
+ f->search = NULL;
+
+ f->handle->pvfs = pvfs;
+ f->handle->fd = -1;
+ f->handle->name = talloc_steal(f->handle, name);
+ f->handle->create_options = io->generic.in.create_options;
+ f->handle->private_flags = io->generic.in.private_flags;
+ f->handle->seek_offset = 0;
+ f->handle->position = 0;
+ f->handle->mode = 0;
+ f->handle->oplock = NULL;
+ f->handle->have_opendb_entry = false;
+ ZERO_STRUCT(f->handle->write_time);
+ f->handle->open_completed = false;
+
+ /* form the lock context used for byte range locking and
+ opendb locking */
+ status = pvfs_locking_key(name, f->handle, &f->handle->odb_locking_key);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = pvfs_brl_locking_handle(f, name, h, &f->brl_handle);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* get a lock on this file before the actual open */
+ lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
+ if (lck == NULL) {
+ DEBUG(0,("pvfs_open: failed to lock file '%s' in opendb\n",
+ name->full_name));
+ /* we were supposed to do a blocking lock, so something
+ is badly wrong! */
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ DLIST_ADD(pvfs->files.list, f);
+
+ /* setup a destructor to avoid file descriptor leaks on
+ abnormal termination */
+ talloc_set_destructor(f, pvfs_fnum_destructor);
+ talloc_set_destructor(f->handle, pvfs_handle_destructor);
+
+ /*
+ * Only SMB2 takes care of the delete_on_close,
+ * on existing files
+ */
+ if (create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE &&
+ req->ctx->protocol >= PROTOCOL_SMB2_02) {
+ del_on_close = true;
+ } else {
+ del_on_close = false;
+ }
+
+ if (pvfs->flags & PVFS_FLAG_FAKE_OPLOCKS) {
+ oplock_level = OPLOCK_NONE;
+ } else if (io->ntcreatex.in.flags & NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK) {
+ oplock_level = OPLOCK_BATCH;
+ } else if (io->ntcreatex.in.flags & NTCREATEX_FLAGS_REQUEST_OPLOCK) {
+ oplock_level = OPLOCK_EXCLUSIVE;
+ }
+
+ if (req->client_caps & NTVFS_CLIENT_CAP_LEVEL_II_OPLOCKS) {
+ allow_level_II_oplock = true;
+ }
+
+ /* see if we are allowed to open at the same time as existing opens */
+ status = odb_can_open(lck, name->stream_id,
+ share_access, access_mask, del_on_close,
+ io->generic.in.open_disposition, false);
+
+ /*
+ * 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, status);
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(lck);
+ return status;
+ }
+
+ if (access_mask & (SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA)) {
+ flags |= O_RDWR;
+ } else {
+ flags |= O_RDONLY;
+ }
+
+ /* do the actual open */
+ fd = pvfs_sys_open(pvfs, f->handle->name->full_name, flags | O_NONBLOCK, 0, name->allow_override);
+ if (fd == -1) {
+ status = pvfs_map_errno(f->pvfs, errno);
+
+ DEBUG(0,(__location__ " mapped errno %s for %s (was %d)\n",
+ nt_errstr(status), f->handle->name->full_name, errno));
+ /*
+ * STATUS_MORE_ENTRIES is EAGAIN or EWOULDBLOCK
+ */
+ if (NT_STATUS_EQUAL(status, STATUS_MORE_ENTRIES) &&
+ (req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
+ return pvfs_open_setup_retry(ntvfs, req, io, f, lck, status);
+ }
+
+ talloc_free(lck);
+ return status;
+ }
+
+ f->handle->fd = fd;
+
+ status = brlock_count(f->pvfs->brl_context, f->brl_handle, &count);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(lck);
+ return status;
+ }
+
+ if (count != 0) {
+ oplock_level = OPLOCK_NONE;
+ }
+
+ /* now really mark the file as open */
+ status = odb_open_file(lck, f->handle, name->full_name,
+ &f->handle->fd, name->dos.write_time,
+ allow_level_II_oplock,
+ oplock_level, &oplock_granted);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(lck);
+ return status;
+ }
+
+ f->handle->have_opendb_entry = true;
+
+ 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;
+ }
+ }
+
+ stream_existed = name->stream_exists;
+
+ /* if this was a stream create then create the stream as well */
+ if (!name->stream_exists) {
+ status = pvfs_stream_create(pvfs, f->handle->name, fd);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(lck);
+ return status;
+ }
+ if (stream_truncate) {
+ status = pvfs_stream_truncate(pvfs, f->handle->name, fd, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(lck);
+ return status;
+ }
+ }
+ }
+
+ /* re-resolve the open fd */
+ status = pvfs_resolve_name_fd(f->pvfs, fd, f->handle->name, PVFS_RESOLVE_NO_OPENDB);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(lck);
+ return status;
+ }
+
+ if (f->handle->name->stream_id == 0 &&
+ (io->generic.in.open_disposition == NTCREATEX_DISP_OVERWRITE ||
+ io->generic.in.open_disposition == NTCREATEX_DISP_OVERWRITE_IF)) {
+ /* for overwrite we may need to replace file permissions */
+ uint32_t attrib = io->ntcreatex.in.file_attr | FILE_ATTRIBUTE_ARCHIVE;
+ mode_t mode = pvfs_fileperms(pvfs, attrib);
+ if (f->handle->name->st.st_mode != mode &&
+ f->handle->name->dos.attrib != attrib &&
+ pvfs_sys_fchmod(pvfs, fd, mode, name->allow_override) == -1) {
+ talloc_free(lck);
+ return pvfs_map_errno(pvfs, errno);
+ }
+ name->dos.alloc_size = io->ntcreatex.in.alloc_size;
+ name->dos.attrib = attrib;
+ status = pvfs_dosattrib_save(pvfs, name, fd);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(lck);
+ return status;
+ }
+ }
+
+ talloc_free(lck);
+
+ status = ntvfs_handle_set_backend_data(h, ntvfs, f);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ /* mark the open as having completed fully, so delete on close
+ can now be used */
+ f->handle->open_completed = true;
+
+ io->generic.out.oplock_level = oplock_granted;
+ io->generic.out.file.ntvfs = h;
+ io->generic.out.create_action = stream_existed?
+ create_action:NTCREATEX_ACTION_CREATED;
+
+ io->generic.out.create_time = name->dos.create_time;
+ io->generic.out.access_time = name->dos.access_time;
+ io->generic.out.write_time = name->dos.write_time;
+ io->generic.out.change_time = name->dos.change_time;
+ io->generic.out.attrib = name->dos.attrib;
+ io->generic.out.alloc_size = name->dos.alloc_size;
+ io->generic.out.size = name->st.st_size;
+ io->generic.out.file_type = FILE_TYPE_DISK;
+ io->generic.out.ipc_state = 0;
+ io->generic.out.is_directory = 0;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ close a file
+*/
+NTSTATUS pvfs_close(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_close *io)
+{
+ struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ struct pvfs_file *f;
+
+ if (io->generic.level == RAW_CLOSE_SPLCLOSE) {
+ return NT_STATUS_DOS(ERRSRV, ERRerror);
+ }
+
+ if (io->generic.level != RAW_CLOSE_GENERIC) {
+ return ntvfs_map_close(ntvfs, req, io);
+ }
+
+ f = pvfs_find_fd(pvfs, req, io->generic.in.file.ntvfs);
+ if (!f) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ if (!null_time(io->generic.in.write_time)) {
+ f->handle->write_time.update_forced = false;
+ f->handle->write_time.update_on_close = true;
+ unix_to_nt_time(&f->handle->write_time.close_time, io->generic.in.write_time);
+ }
+
+ if (io->generic.in.flags & SMB2_CLOSE_FLAGS_FULL_INFORMATION) {
+ struct pvfs_filename *name;
+ NTSTATUS status;
+ struct pvfs_file_handle *h = f->handle;
+
+ status = pvfs_resolve_name_handle(pvfs, h);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ name = h->name;
+
+ io->generic.out.flags = SMB2_CLOSE_FLAGS_FULL_INFORMATION;
+ io->generic.out.create_time = name->dos.create_time;
+ io->generic.out.access_time = name->dos.access_time;
+ io->generic.out.write_time = name->dos.write_time;
+ io->generic.out.change_time = name->dos.change_time;
+ io->generic.out.alloc_size = name->dos.alloc_size;
+ io->generic.out.size = name->st.st_size;
+ io->generic.out.file_attr = name->dos.attrib;
+ } else {
+ ZERO_STRUCT(io->generic.out);
+ }
+
+ talloc_free(f);
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ logoff - close all file descriptors open by a vuid
+*/
+NTSTATUS pvfs_logoff(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req)
+{
+ struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ struct pvfs_file *f, *next;
+
+ /* If pvfs is NULL, we never logged on, and no files are open. */
+ if(pvfs == NULL) {
+ return NT_STATUS_OK;
+ }
+
+ for (f=pvfs->files.list;f;f=next) {
+ next = f->next;
+ if (f->ntvfs->session_info == req->session_info) {
+ talloc_free(f);
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ exit - close files for the current pid
+*/
+NTSTATUS pvfs_exit(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req)
+{
+ struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ struct pvfs_file *f, *next;
+
+ for (f=pvfs->files.list;f;f=next) {
+ next = f->next;
+ if (f->ntvfs->session_info == req->session_info &&
+ f->ntvfs->smbpid == req->smbpid) {
+ talloc_free(f);
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ change the delete on close flag on an already open file
+*/
+NTSTATUS pvfs_set_delete_on_close(struct pvfs_state *pvfs,
+ struct ntvfs_request *req,
+ struct pvfs_file *f, bool del_on_close)
+{
+ struct odb_lock *lck;
+ NTSTATUS status;
+
+ if ((f->handle->name->dos.attrib & FILE_ATTRIBUTE_READONLY) && del_on_close) {
+ return NT_STATUS_CANNOT_DELETE;
+ }
+
+ if ((f->handle->name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) &&
+ !pvfs_directory_empty(pvfs, f->handle->name)) {
+ return NT_STATUS_DIRECTORY_NOT_EMPTY;
+ }
+
+ if (del_on_close) {
+ f->handle->create_options |= NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
+ } else {
+ f->handle->create_options &= ~NTCREATEX_OPTIONS_DELETE_ON_CLOSE;
+ }
+
+ lck = odb_lock(req, pvfs->odb_context, &f->handle->odb_locking_key);
+ if (lck == NULL) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ status = odb_set_delete_on_close(lck, del_on_close);
+
+ talloc_free(lck);
+
+ return status;
+}
+
+
+/*
+ determine if a file can be deleted, or if it is prevented by an
+ already open file
+*/
+NTSTATUS pvfs_can_delete(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 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_delete\n"));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ 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,
+ NTCREATEX_DISP_OPEN, false);
+
+ if (NT_STATUS_IS_OK(status)) {
+ status = pvfs_access_check_simple(pvfs, req, name, access_mask);
+ }
+
+ /*
+ * 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 a file can be renamed, or if it is prevented by an
+ already open file
+*/
+NTSTATUS pvfs_can_rename(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 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;
+ }
+
+ 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,
+ NTCREATEX_DISP_OPEN, 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;
+ }
+
+ share_access = NTCREATEX_SHARE_ACCESS_READ |
+ NTCREATEX_SHARE_ACCESS_WRITE |
+ NTCREATEX_SHARE_ACCESS_DELETE;
+ /*
+ * this code previous set only SEC_FILE_WRITE_ATTRIBUTE, with
+ * a comment that this seemed to be wrong, but matched windows
+ * behaviour. It now appears that this windows behaviour is
+ * just a bug.
+ */
+ access_mask = SEC_FILE_WRITE_ATTRIBUTE | SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA;
+ delete_on_close = false;
+ break_to_none = true;
+
+ status = odb_can_open(lck, name->stream_id,
+ share_access, access_mask, delete_on_close,
+ NTCREATEX_DISP_OPEN, break_to_none);
+
+ /*
+ * 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 file meta data can be accessed, or if it is prevented by an
+ already open file
+*/
+NTSTATUS pvfs_can_stat(struct pvfs_state *pvfs,
+ struct ntvfs_request *req,
+ struct pvfs_filename *name)
+{
+ 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)) {
+ 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;
+ }
+
+ share_access = NTCREATEX_SHARE_ACCESS_READ |
+ NTCREATEX_SHARE_ACCESS_WRITE;
+ access_mask = SEC_FILE_READ_ATTRIBUTE;
+ delete_on_close = false;
+
+ status = odb_can_open(lck, name->stream_id,
+ share_access, access_mask, delete_on_close,
+ NTCREATEX_DISP_OPEN, false);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(lck);
+ }
+
+ return status;
+}
+
+
+/*
+ determine if delete on close is set on
+*/
+bool pvfs_delete_on_close_set(struct pvfs_state *pvfs, struct pvfs_file_handle *h)
+{
+ NTSTATUS status;
+ bool del_on_close;
+
+ status = odb_get_file_infos(pvfs->odb_context, &h->odb_locking_key,
+ &del_on_close, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(1,("WARNING: unable to determine delete on close status for open file\n"));
+ return false;
+ }
+
+ return del_on_close;
+}
diff --git a/source4/ntvfs/posix/pvfs_oplock.c b/source4/ntvfs/posix/pvfs_oplock.c
new file mode 100644
index 0000000..ba5d25d
--- /dev/null
+++ b/source4/ntvfs/posix/pvfs_oplock.c
@@ -0,0 +1,307 @@
+/*
+ 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 "system/time.h"
+#include "vfs_posix.h"
+
+
+struct pvfs_oplock {
+ struct pvfs_file_handle *handle;
+ struct pvfs_file *file;
+ uint32_t level;
+ struct timeval break_to_level_II;
+ struct timeval break_to_none;
+ struct imessaging_context *msg_ctx;
+};
+
+static NTSTATUS pvfs_oplock_release_internal(struct pvfs_file_handle *h,
+ uint8_t oplock_break)
+{
+ struct odb_lock *olck;
+ NTSTATUS status;
+
+ 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;
+ }
+
+ 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;
+}
+
+/*
+ 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;
+ struct timeval cur = timeval_current();
+ struct timeval *last = NULL;
+ struct timeval end;
+
+ switch (level) {
+ case OPLOCK_BREAK_TO_LEVEL_II:
+ last = &opl->break_to_level_II;
+ break;
+ case OPLOCK_BREAK_TO_NONE:
+ last = &opl->break_to_none;
+ break;
+ }
+
+ if (!last) {
+ DEBUG(0,("%s: got unexpected level[0x%02X]\n",
+ __FUNCTION__, level));
+ return;
+ }
+
+ if (timeval_is_zero(last)) {
+ /*
+ * this is the first break we for this level
+ * remember the time
+ */
+ *last = cur;
+
+ DEBUG(5,("%s: sending oplock break level %d for '%s' %p\n",
+ __FUNCTION__, level, h->name->original_name, h));
+ status = ntvfs_send_oplock_break(pvfs->ntvfs, f->ntvfs, level);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("%s: sending oplock break failed: %s\n",
+ __FUNCTION__, nt_errstr(status)));
+ }
+ return;
+ }
+
+ end = timeval_add(last, pvfs->oplock_break_timeout, 0);
+
+ if (timeval_compare(&cur, &end) < 0) {
+ /*
+ * If it's not expired just ignore the break
+ * as we already sent the break request to the client
+ */
+ DEBUG(0,("%s: do not resend oplock break level %d for '%s' %p\n",
+ __FUNCTION__, level, h->name->original_name, h));
+ return;
+ }
+
+ /*
+ * If the client did not send a release within the
+ * oplock break timeout time frame we auto release
+ * the oplock
+ */
+ DEBUG(0,("%s: auto release oplock level %d for '%s' %p\n",
+ __FUNCTION__, level, h->name->original_name, h));
+ status = pvfs_oplock_release_internal(h, level);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("%s: failed to auto release the oplock[0x%02X]: %s\n",
+ __FUNCTION__, level, nt_errstr(status)));
+ }
+}
+
+static void pvfs_oplock_break_dispatch(struct imessaging_context *msg,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id src,
+ size_t num_fds,
+ int *fds,
+ DATA_BLOB *data)
+{
+ struct pvfs_oplock *opl = talloc_get_type(private_data,
+ struct pvfs_oplock);
+ struct opendb_oplock_break opb;
+
+ if (num_fds != 0) {
+ DBG_WARNING("Received %zu fds, ignoring message\n", num_fds);
+ return;
+ }
+
+ ZERO_STRUCT(opb);
+
+ /* we need to check that this one is for us. See
+ imessaging_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__, (unsigned)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)
+{
+ imessaging_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_zero(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 = imessaging_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 = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ struct pvfs_file *f;
+ uint8_t oplock_break;
+ NTSTATUS status;
+
+ f = pvfs_find_fd(pvfs, req, lck->lockx.in.file.ntvfs);
+ if (!f) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ oplock_break = (lck->lockx.in.mode >> 8) & 0xFF;
+
+ status = pvfs_oplock_release_internal(f->handle, oplock_break);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("%s: failed to release the oplock[0x%02X]: %s\n",
+ __FUNCTION__, oplock_break, nt_errstr(status)));
+ return status;
+ }
+
+ 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_LEVEL_II) {
+ 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;
+ }
+
+ 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;
+}
diff --git a/source4/ntvfs/posix/pvfs_qfileinfo.c b/source4/ntvfs/posix/pvfs_qfileinfo.c
new file mode 100644
index 0000000..be601a2
--- /dev/null
+++ b/source4/ntvfs/posix/pvfs_qfileinfo.c
@@ -0,0 +1,468 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ POSIX NTVFS backend - read
+
+ Copyright (C) Andrew Tridgell 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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 "vfs_posix.h"
+#include "librpc/gen_ndr/xattr.h"
+
+
+/*
+ determine what access bits are needed for a call
+*/
+static uint32_t pvfs_fileinfo_access(union smb_fileinfo *info)
+{
+ uint32_t needed;
+
+ switch (info->generic.level) {
+ case RAW_FILEINFO_EA_LIST:
+ case RAW_FILEINFO_ALL_EAS:
+ needed = SEC_FILE_READ_EA;
+ break;
+
+ case RAW_FILEINFO_IS_NAME_VALID:
+ needed = 0;
+ break;
+
+ case RAW_FILEINFO_ACCESS_INFORMATION:
+ needed = 0;
+ break;
+
+ case RAW_FILEINFO_STREAM_INFO:
+ case RAW_FILEINFO_STREAM_INFORMATION:
+ needed = 0;
+ break;
+
+ case RAW_FILEINFO_SEC_DESC:
+ needed = 0;
+ if (info->query_secdesc.in.secinfo_flags & (SECINFO_OWNER|SECINFO_GROUP)) {
+ needed |= SEC_STD_READ_CONTROL;
+ }
+ if (info->query_secdesc.in.secinfo_flags & SECINFO_DACL) {
+ needed |= SEC_STD_READ_CONTROL;
+ }
+ if (info->query_secdesc.in.secinfo_flags & SECINFO_SACL) {
+ needed |= SEC_FLAG_SYSTEM_SECURITY;
+ }
+ break;
+
+ default:
+ needed = SEC_FILE_READ_ATTRIBUTE;
+ break;
+ }
+
+ return needed;
+}
+
+/*
+ reply to a RAW_FILEINFO_EA_LIST call
+*/
+NTSTATUS pvfs_query_ea_list(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx,
+ struct pvfs_filename *name, int fd,
+ unsigned int num_names,
+ struct ea_name *names,
+ struct smb_ea_list *eas)
+{
+ NTSTATUS status;
+ int i;
+ struct xattr_DosEAs *ealist = talloc(mem_ctx, struct xattr_DosEAs);
+
+ ZERO_STRUCTP(eas);
+ status = pvfs_doseas_load(pvfs, name, fd, ealist);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ eas->eas = talloc_array(mem_ctx, struct ea_struct, num_names);
+ if (eas->eas == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ eas->num_eas = num_names;
+ for (i=0;i<num_names;i++) {
+ int j;
+ eas->eas[i].flags = 0;
+ eas->eas[i].name.s = names[i].name.s;
+ eas->eas[i].value = data_blob(NULL, 0);
+ for (j=0;j<ealist->num_eas;j++) {
+ if (strcasecmp_m(eas->eas[i].name.s,
+ ealist->eas[j].name) == 0) {
+ if (ealist->eas[j].value.length == 0) {
+ continue;
+ }
+ eas->eas[i].value = ealist->eas[j].value;
+ break;
+ }
+ }
+ }
+ return NT_STATUS_OK;
+}
+
+/*
+ reply to a RAW_FILEINFO_ALL_EAS call
+*/
+static NTSTATUS pvfs_query_all_eas(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx,
+ struct pvfs_filename *name, int fd,
+ struct smb_ea_list *eas)
+{
+ NTSTATUS status;
+ int i;
+ struct xattr_DosEAs *ealist = talloc(mem_ctx, struct xattr_DosEAs);
+
+ ZERO_STRUCTP(eas);
+ status = pvfs_doseas_load(pvfs, name, fd, ealist);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ eas->eas = talloc_array(mem_ctx, struct ea_struct, ealist->num_eas);
+ if (eas->eas == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ eas->num_eas = 0;
+ for (i=0;i<ealist->num_eas;i++) {
+ eas->eas[eas->num_eas].flags = 0;
+ eas->eas[eas->num_eas].name.s = ealist->eas[i].name;
+ if (ealist->eas[i].value.length == 0) {
+ continue;
+ }
+ eas->eas[eas->num_eas].value = ealist->eas[i].value;
+ eas->num_eas++;
+ }
+ return NT_STATUS_OK;
+}
+
+/*
+ approximately map a struct pvfs_filename to a generic fileinfo struct
+*/
+static NTSTATUS pvfs_map_fileinfo(struct pvfs_state *pvfs,
+ struct ntvfs_request *req,
+ struct pvfs_filename *name, union smb_fileinfo *info,
+ int fd)
+{
+ switch (info->generic.level) {
+ case RAW_FILEINFO_GETATTR:
+ info->getattr.out.attrib = name->dos.attrib;
+ info->getattr.out.size = name->st.st_size;
+ info->getattr.out.write_time = nt_time_to_unix(name->dos.write_time);
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_GETATTRE:
+ case RAW_FILEINFO_STANDARD:
+ info->standard.out.create_time = nt_time_to_unix(name->dos.create_time);
+ info->standard.out.access_time = nt_time_to_unix(name->dos.access_time);
+ info->standard.out.write_time = nt_time_to_unix(name->dos.write_time);
+ info->standard.out.size = name->st.st_size;
+ info->standard.out.alloc_size = name->dos.alloc_size;
+ info->standard.out.attrib = name->dos.attrib;
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_EA_SIZE:
+ info->ea_size.out.create_time = nt_time_to_unix(name->dos.create_time);
+ info->ea_size.out.access_time = nt_time_to_unix(name->dos.access_time);
+ info->ea_size.out.write_time = nt_time_to_unix(name->dos.write_time);
+ info->ea_size.out.size = name->st.st_size;
+ info->ea_size.out.alloc_size = name->dos.alloc_size;
+ info->ea_size.out.attrib = name->dos.attrib;
+ info->ea_size.out.ea_size = name->dos.ea_size;
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_EA_LIST:
+ return pvfs_query_ea_list(pvfs, req, name, fd,
+ info->ea_list.in.num_names,
+ info->ea_list.in.ea_names,
+ &info->ea_list.out);
+
+ case RAW_FILEINFO_ALL_EAS:
+ return pvfs_query_all_eas(pvfs, req, name, fd, &info->all_eas.out);
+
+ case RAW_FILEINFO_SMB2_ALL_EAS: {
+ NTSTATUS status = pvfs_query_all_eas(pvfs, req, name, fd, &info->all_eas.out);
+ if (NT_STATUS_IS_OK(status) &&
+ info->all_eas.out.num_eas == 0) {
+ return NT_STATUS_NO_EAS_ON_FILE;
+ }
+ return status;
+ }
+
+ case RAW_FILEINFO_IS_NAME_VALID:
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_BASIC_INFO:
+ case RAW_FILEINFO_BASIC_INFORMATION:
+ info->basic_info.out.create_time = name->dos.create_time;
+ info->basic_info.out.access_time = name->dos.access_time;
+ info->basic_info.out.write_time = name->dos.write_time;
+ info->basic_info.out.change_time = name->dos.change_time;
+ info->basic_info.out.attrib = name->dos.attrib;
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_STANDARD_INFO:
+ case RAW_FILEINFO_STANDARD_INFORMATION:
+ info->standard_info.out.alloc_size = name->dos.alloc_size;
+ info->standard_info.out.size = name->st.st_size;
+ info->standard_info.out.nlink = name->dos.nlink;
+ info->standard_info.out.delete_pending = 0; /* only for qfileinfo */
+ info->standard_info.out.directory =
+ (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)? 1 : 0;
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_EA_INFO:
+ case RAW_FILEINFO_EA_INFORMATION:
+ info->ea_info.out.ea_size = name->dos.ea_size;
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_NAME_INFO:
+ case RAW_FILEINFO_NAME_INFORMATION:
+ if (req->ctx->protocol >= PROTOCOL_SMB2_02) {
+ /* strange that SMB2 doesn't have this */
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+ info->name_info.out.fname.s = name->original_name;
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_ALL_INFO:
+ case RAW_FILEINFO_ALL_INFORMATION:
+ info->all_info.out.create_time = name->dos.create_time;
+ info->all_info.out.access_time = name->dos.access_time;
+ info->all_info.out.write_time = name->dos.write_time;
+ info->all_info.out.change_time = name->dos.change_time;
+ info->all_info.out.attrib = name->dos.attrib;
+ info->all_info.out.alloc_size = name->dos.alloc_size;
+ info->all_info.out.size = name->st.st_size;
+ info->all_info.out.nlink = name->dos.nlink;
+ info->all_info.out.delete_pending = 0; /* only set by qfileinfo */
+ info->all_info.out.directory =
+ (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)? 1 : 0;
+ info->all_info.out.ea_size = name->dos.ea_size;
+ info->all_info.out.fname.s = name->original_name;
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_ALT_NAME_INFO:
+ case RAW_FILEINFO_ALT_NAME_INFORMATION:
+ case RAW_FILEINFO_SMB2_ALT_NAME_INFORMATION:
+ info->name_info.out.fname.s = pvfs_short_name(pvfs, name, name);
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_STREAM_INFO:
+ case RAW_FILEINFO_STREAM_INFORMATION:
+ return pvfs_stream_information(pvfs, req, name, fd, &info->stream_info.out);
+
+ case RAW_FILEINFO_COMPRESSION_INFO:
+ case RAW_FILEINFO_COMPRESSION_INFORMATION:
+ info->compression_info.out.compressed_size = name->st.st_size;
+ info->compression_info.out.format = 0;
+ info->compression_info.out.unit_shift = 0;
+ info->compression_info.out.chunk_shift = 0;
+ info->compression_info.out.cluster_shift = 0;
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_INTERNAL_INFORMATION:
+ info->internal_information.out.file_id = name->dos.file_id;
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_ACCESS_INFORMATION:
+ info->access_information.out.access_flags = 0; /* only set by qfileinfo */
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_POSITION_INFORMATION:
+ info->position_information.out.position = 0; /* only set by qfileinfo */
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_MODE_INFORMATION:
+ info->mode_information.out.mode = 0; /* only set by qfileinfo */
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_ALIGNMENT_INFORMATION:
+ info->alignment_information.out.alignment_requirement = 0;
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_NETWORK_OPEN_INFORMATION:
+ info->network_open_information.out.create_time = name->dos.create_time;
+ info->network_open_information.out.access_time = name->dos.access_time;
+ info->network_open_information.out.write_time = name->dos.write_time;
+ info->network_open_information.out.change_time = name->dos.change_time;
+ info->network_open_information.out.alloc_size = name->dos.alloc_size;
+ info->network_open_information.out.size = name->st.st_size;
+ info->network_open_information.out.attrib = name->dos.attrib;
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_ATTRIBUTE_TAG_INFORMATION:
+ info->attribute_tag_information.out.attrib = name->dos.attrib;
+ info->attribute_tag_information.out.reparse_tag = 0;
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_SEC_DESC:
+ return pvfs_acl_query(pvfs, req, name, fd, info);
+
+ case RAW_FILEINFO_SMB2_ALL_INFORMATION:
+ info->all_info2.out.create_time = name->dos.create_time;
+ info->all_info2.out.access_time = name->dos.access_time;
+ info->all_info2.out.write_time = name->dos.write_time;
+ info->all_info2.out.change_time = name->dos.change_time;
+ info->all_info2.out.attrib = name->dos.attrib;
+ info->all_info2.out.unknown1 = 0;
+ info->all_info2.out.alloc_size = name->dos.alloc_size;
+ info->all_info2.out.size = name->st.st_size;
+ info->all_info2.out.nlink = name->dos.nlink;
+ info->all_info2.out.delete_pending = 0; /* only set by qfileinfo */
+ info->all_info2.out.directory =
+ (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)? 1 : 0;
+ info->all_info2.out.file_id = name->dos.file_id;
+ info->all_info2.out.ea_size = name->dos.ea_size;
+ info->all_info2.out.access_mask = 0; /* only set by qfileinfo */
+ info->all_info2.out.position = 0; /* only set by qfileinfo */
+ info->all_info2.out.mode = 0; /* only set by qfileinfo */
+ info->all_info2.out.alignment_requirement = 0;
+ /* windows wants the full path on disk for this
+ result, but I really don't want to expose that on
+ the wire, so I'll give the path with a share
+ prefix, which is a good approximation */
+ info->all_info2.out.fname.s = talloc_asprintf(req, "\\%s\\%s",
+ pvfs->share_name,
+ name->original_name);
+ NT_STATUS_HAVE_NO_MEMORY(info->all_info2.out.fname.s);
+ return NT_STATUS_OK;
+
+ case RAW_FILEINFO_GENERIC:
+ case RAW_FILEINFO_UNIX_BASIC:
+ case RAW_FILEINFO_UNIX_INFO2:
+ case RAW_FILEINFO_UNIX_LINK:
+ return NT_STATUS_INVALID_LEVEL;
+ case RAW_FILEINFO_NORMALIZED_NAME_INFORMATION:
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ return NT_STATUS_INVALID_LEVEL;
+}
+
+/*
+ return info on a pathname
+*/
+NTSTATUS pvfs_qpathinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_fileinfo *info)
+{
+ struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ struct pvfs_filename *name;
+ NTSTATUS status;
+
+ /* resolve the cifs name to a posix name */
+ status = pvfs_resolve_name(pvfs, req, info->generic.in.file.path, PVFS_RESOLVE_STREAMS, &name);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (!name->stream_exists) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ status = pvfs_can_stat(pvfs, req, name);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = pvfs_access_check_simple(pvfs, req, name,
+ pvfs_fileinfo_access(info));
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = pvfs_map_fileinfo(pvfs, req, name, info, -1);
+
+ return status;
+}
+
+/*
+ query info on a open file
+*/
+NTSTATUS pvfs_qfileinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_fileinfo *info)
+{
+ struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ struct pvfs_file *f;
+ struct pvfs_file_handle *h;
+ NTSTATUS status;
+ uint32_t access_needed;
+
+ f = pvfs_find_fd(pvfs, req, info->generic.in.file.ntvfs);
+ if (!f) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+ h = f->handle;
+
+ access_needed = pvfs_fileinfo_access(info);
+ if ((f->access_mask & access_needed) != access_needed) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ /* update the file information */
+ status = pvfs_resolve_name_handle(pvfs, h);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = pvfs_map_fileinfo(pvfs, req, h->name, info, h->fd);
+
+ /* a qfileinfo can fill in a bit more info than a qpathinfo -
+ now modify the levels that need to be fixed up */
+ switch (info->generic.level) {
+ case RAW_FILEINFO_STANDARD_INFO:
+ case RAW_FILEINFO_STANDARD_INFORMATION:
+ if (pvfs_delete_on_close_set(pvfs, h)) {
+ info->standard_info.out.delete_pending = 1;
+ info->standard_info.out.nlink--;
+ }
+ break;
+
+ case RAW_FILEINFO_ALL_INFO:
+ case RAW_FILEINFO_ALL_INFORMATION:
+ if (pvfs_delete_on_close_set(pvfs, h)) {
+ info->all_info.out.delete_pending = 1;
+ info->all_info.out.nlink--;
+ }
+ break;
+
+ case RAW_FILEINFO_POSITION_INFORMATION:
+ info->position_information.out.position = h->position;
+ break;
+
+ case RAW_FILEINFO_ACCESS_INFORMATION:
+ info->access_information.out.access_flags = f->access_mask;
+ break;
+
+ case RAW_FILEINFO_MODE_INFORMATION:
+ info->mode_information.out.mode = h->mode;
+ break;
+
+ case RAW_FILEINFO_SMB2_ALL_INFORMATION:
+ if (pvfs_delete_on_close_set(pvfs, h)) {
+ info->all_info2.out.delete_pending = 1;
+ info->all_info2.out.nlink--;
+ }
+ info->all_info2.out.position = h->position;
+ info->all_info2.out.access_mask = f->access_mask;
+ info->all_info2.out.mode = h->mode;
+ break;
+
+ default:
+ break;
+ }
+
+ return status;
+}
diff --git a/source4/ntvfs/posix/pvfs_read.c b/source4/ntvfs/posix/pvfs_read.c
new file mode 100644
index 0000000..09eedd5
--- /dev/null
+++ b/source4/ntvfs/posix/pvfs_read.c
@@ -0,0 +1,103 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ POSIX NTVFS backend - read
+
+ Copyright (C) Andrew Tridgell 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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 "vfs_posix.h"
+#include "lib/events/events.h"
+
+/*
+ read from a file
+*/
+NTSTATUS pvfs_read(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_read *rd)
+{
+ struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ ssize_t ret;
+ struct pvfs_file *f;
+ NTSTATUS status;
+ uint32_t maxcnt;
+ uint32_t mask;
+
+ if (rd->generic.level != RAW_READ_READX) {
+ return ntvfs_map_read(ntvfs, req, rd);
+ }
+
+ f = pvfs_find_fd(pvfs, req, rd->readx.in.file.ntvfs);
+ if (!f) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ if (f->handle->fd == -1) {
+ return NT_STATUS_INVALID_DEVICE_REQUEST;
+ }
+
+ mask = SEC_FILE_READ_DATA;
+ if (rd->readx.in.read_for_execute) {
+ mask |= SEC_FILE_EXECUTE;
+ }
+ if (!(f->access_mask & mask)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ maxcnt = rd->readx.in.maxcnt;
+ if (maxcnt > 2*UINT16_MAX && req->ctx->protocol < PROTOCOL_SMB2_02) {
+ DEBUG(3,(__location__ ": Invalid SMB maxcnt 0x%x\n", maxcnt));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ status = pvfs_check_lock(pvfs, f, req->smbpid,
+ rd->readx.in.offset,
+ maxcnt,
+ READ_LOCK);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (f->handle->name->stream_name) {
+ ret = pvfs_stream_read(pvfs, f->handle,
+ rd->readx.out.data, maxcnt, rd->readx.in.offset);
+ } else {
+ ret = pread(f->handle->fd,
+ rd->readx.out.data,
+ maxcnt,
+ rd->readx.in.offset);
+ }
+ if (ret == -1) {
+ return pvfs_map_errno(pvfs, errno);
+ }
+
+ /* only SMB2 honors mincnt */
+ if (req->ctx->protocol >= PROTOCOL_SMB2_02) {
+ if (rd->readx.in.mincnt > ret ||
+ (ret == 0 && maxcnt > 0)) {
+ return NT_STATUS_END_OF_FILE;
+ }
+ }
+
+ f->handle->position = f->handle->seek_offset = rd->readx.in.offset + ret;
+
+ rd->readx.out.nread = ret;
+ rd->readx.out.remaining = 0xFFFF;
+ rd->readx.out.compaction_mode = 0;
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/ntvfs/posix/pvfs_rename.c b/source4/ntvfs/posix/pvfs_rename.c
new file mode 100644
index 0000000..4327161
--- /dev/null
+++ b/source4/ntvfs/posix/pvfs_rename.c
@@ -0,0 +1,675 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ POSIX NTVFS backend - rename
+
+ Copyright (C) Andrew Tridgell 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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 "vfs_posix.h"
+#include "librpc/gen_ndr/security.h"
+#include "param/param.h"
+
+
+/*
+ do a file rename, and send any notify triggers
+*/
+NTSTATUS pvfs_do_rename(struct pvfs_state *pvfs,
+ struct odb_lock *lck,
+ const struct pvfs_filename *name1,
+ const char *name2)
+{
+ const char *r1, *r2;
+ uint32_t mask;
+ NTSTATUS status;
+
+ if (pvfs_sys_rename(pvfs, name1->full_name, name2,
+ name1->allow_override) == -1) {
+ return pvfs_map_errno(pvfs, errno);
+ }
+
+ status = odb_rename(lck, name2);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ if (name1->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) {
+ mask = FILE_NOTIFY_CHANGE_DIR_NAME;
+ } else {
+ mask = FILE_NOTIFY_CHANGE_FILE_NAME;
+ }
+ /*
+ renames to the same directory cause a OLD_NAME->NEW_NAME notify.
+ renames to a different directory are considered a remove/add
+ */
+ r1 = strrchr_m(name1->full_name, '/');
+ r2 = strrchr_m(name2, '/');
+
+ if ((r1-name1->full_name) != (r2-name2) ||
+ strncmp(name1->full_name, name2, r1-name1->full_name) != 0) {
+ notify_trigger(pvfs->notify_context,
+ NOTIFY_ACTION_REMOVED,
+ mask,
+ name1->full_name);
+ notify_trigger(pvfs->notify_context,
+ NOTIFY_ACTION_ADDED,
+ mask,
+ name2);
+ } else {
+ notify_trigger(pvfs->notify_context,
+ NOTIFY_ACTION_OLD_NAME,
+ mask,
+ name1->full_name);
+ notify_trigger(pvfs->notify_context,
+ NOTIFY_ACTION_NEW_NAME,
+ mask,
+ name2);
+ }
+
+ /* this is a strange one. w2k3 gives an additional event for CHANGE_ATTRIBUTES
+ and CHANGE_CREATION on the new file when renaming files, but not
+ directories */
+ if ((name1->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) == 0) {
+ notify_trigger(pvfs->notify_context,
+ NOTIFY_ACTION_MODIFIED,
+ FILE_NOTIFY_CHANGE_ATTRIBUTES|FILE_NOTIFY_CHANGE_CREATION,
+ name2);
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ resolve a wildcard rename pattern. This works on one component of the name
+*/
+static const char *pvfs_resolve_wildcard_component(TALLOC_CTX *mem_ctx,
+ const char *fname,
+ const char *pattern)
+{
+ const char *p1, *p2;
+ char *dest, *d;
+
+ /* the length is bounded by the length of the two strings combined */
+ dest = talloc_array(mem_ctx, char, strlen(fname) + strlen(pattern) + 1);
+ if (dest == NULL) {
+ return NULL;
+ }
+
+ p1 = fname;
+ p2 = pattern;
+ d = dest;
+
+ while (*p2) {
+ codepoint_t c1, c2;
+ size_t c_size1, c_size2;
+ c1 = next_codepoint(p1, &c_size1);
+ c2 = next_codepoint(p2, &c_size2);
+ if (c2 == '?') {
+ d += push_codepoint(d, c1);
+ } else if (c2 == '*') {
+ memcpy(d, p1, strlen(p1));
+ d += strlen(p1);
+ break;
+ } else {
+ d += push_codepoint(d, c2);
+ }
+
+ p1 += c_size1;
+ p2 += c_size2;
+ }
+
+ *d = 0;
+
+ talloc_set_name_const(dest, dest);
+
+ return dest;
+}
+
+/*
+ resolve a wildcard rename pattern.
+*/
+static const char *pvfs_resolve_wildcard(TALLOC_CTX *mem_ctx,
+ const char *fname,
+ const char *pattern)
+{
+ const char *base1, *base2;
+ const char *ext1, *ext2;
+ char *p;
+
+ /* break into base part plus extension */
+ p = strrchr_m(fname, '.');
+ if (p == NULL) {
+ ext1 = "";
+ base1 = fname;
+ } else {
+ ext1 = talloc_strdup(mem_ctx, p+1);
+ base1 = talloc_strndup(mem_ctx, fname, p-fname);
+ }
+ if (ext1 == NULL || base1 == NULL) {
+ return NULL;
+ }
+
+ p = strrchr_m(pattern, '.');
+ if (p == NULL) {
+ ext2 = "";
+ base2 = fname;
+ } else {
+ ext2 = talloc_strdup(mem_ctx, p+1);
+ base2 = talloc_strndup(mem_ctx, pattern, p-pattern);
+ }
+ if (ext2 == NULL || base2 == NULL) {
+ return NULL;
+ }
+
+ base1 = pvfs_resolve_wildcard_component(mem_ctx, base1, base2);
+ ext1 = pvfs_resolve_wildcard_component(mem_ctx, ext1, ext2);
+ if (base1 == NULL || ext1 == NULL) {
+ return NULL;
+ }
+
+ if (*ext1 == 0) {
+ return base1;
+ }
+
+ return talloc_asprintf(mem_ctx, "%s.%s", base1, ext1);
+}
+
+/*
+ retry an rename after a sharing violation
+*/
+static void pvfs_retry_rename(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_rename *io = talloc_get_type(_io, union smb_rename);
+ 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_rename(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 rename retry after a sharing violation
+ or a non granted oplock
+*/
+static NTSTATUS pvfs_rename_setup_retry(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_rename *io,
+ struct odb_lock *lck,
+ NTSTATUS status)
+{
+ struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ 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_rename);
+}
+
+/*
+ rename one file from a wildcard set
+*/
+static NTSTATUS pvfs_rename_one(struct pvfs_state *pvfs,
+ struct ntvfs_request *req,
+ const char *dir_path,
+ const char *fname1,
+ const char *fname2,
+ uint16_t attrib)
+{
+ struct pvfs_filename *name1, *name2;
+ TALLOC_CTX *mem_ctx = talloc_new(req);
+ struct odb_lock *lck = NULL;
+ NTSTATUS status;
+
+ /* resolve the wildcard pattern for this name */
+ fname2 = pvfs_resolve_wildcard(mem_ctx, fname1, fname2);
+ if (fname2 == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* get a pvfs_filename source object */
+ status = pvfs_resolve_partial(pvfs, mem_ctx,
+ dir_path, fname1,
+ PVFS_RESOLVE_NO_OPENDB,
+ &name1);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto failed;
+ }
+
+ /* make sure its matches the given attributes */
+ status = pvfs_match_attrib(pvfs, name1, attrib, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto failed;
+ }
+
+ status = pvfs_can_rename(pvfs, req, name1, &lck);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(lck);
+ goto failed;
+ }
+
+ /* get a pvfs_filename dest object */
+ status = pvfs_resolve_partial(pvfs, mem_ctx,
+ dir_path, fname2,
+ PVFS_RESOLVE_NO_OPENDB,
+ &name2);
+ if (NT_STATUS_IS_OK(status)) {
+ status = pvfs_can_delete(pvfs, req, name2, NULL);
+ if (!NT_STATUS_IS_OK(status)) {
+ goto failed;
+ }
+ }
+
+ status = NT_STATUS_OK;
+
+ fname2 = talloc_asprintf(mem_ctx, "%s/%s", dir_path, fname2);
+ if (fname2 == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = pvfs_do_rename(pvfs, lck, name1, fname2);
+
+failed:
+ talloc_free(mem_ctx);
+ return status;
+}
+
+
+/*
+ rename a set of files with wildcards
+*/
+static NTSTATUS pvfs_rename_wildcard(struct pvfs_state *pvfs,
+ struct ntvfs_request *req,
+ union smb_rename *ren,
+ struct pvfs_filename *name1,
+ struct pvfs_filename *name2)
+{
+ struct pvfs_dir *dir;
+ NTSTATUS status;
+ off_t ofs = 0;
+ const char *fname, *fname2, *dir_path;
+ uint16_t attrib = ren->rename.in.attrib;
+ int total_renamed = 0;
+
+ /* get list of matching files */
+ status = pvfs_list_start(pvfs, name1, req, &dir);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = NT_STATUS_NO_SUCH_FILE;
+
+ dir_path = pvfs_list_unix_path(dir);
+
+ /* only allow wildcard renames within a directory */
+ if (strncmp(dir_path, name2->full_name, strlen(dir_path)) != 0 ||
+ name2->full_name[strlen(dir_path)] != '/' ||
+ strchr(name2->full_name + strlen(dir_path) + 1, '/')) {
+ DEBUG(3,(__location__ ": Invalid rename for %s -> %s\n",
+ name1->original_name, name2->original_name));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ fname2 = talloc_strdup(name2, name2->full_name + strlen(dir_path) + 1);
+ if (fname2 == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ while ((fname = pvfs_list_next(dir, &ofs))) {
+ status = pvfs_rename_one(pvfs, req,
+ dir_path,
+ fname, fname2, attrib);
+ if (NT_STATUS_IS_OK(status)) {
+ total_renamed++;
+ }
+ }
+
+ if (total_renamed == 0) {
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ rename a set of files - SMBmv interface
+*/
+static NTSTATUS pvfs_rename_mv(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_rename *ren)
+{
+ struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ NTSTATUS status;
+ struct pvfs_filename *name1, *name2;
+ struct odb_lock *lck = NULL;
+
+ /* resolve the cifs name to a posix name */
+ status = pvfs_resolve_name(pvfs, req, ren->rename.in.pattern1,
+ PVFS_RESOLVE_WILDCARD, &name1);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = pvfs_resolve_name(pvfs, req, ren->rename.in.pattern2,
+ PVFS_RESOLVE_WILDCARD, &name2);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (name1->has_wildcard || name2->has_wildcard) {
+ return pvfs_rename_wildcard(pvfs, req, ren, name1, name2);
+ }
+
+ if (!name1->exists) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ if (strcmp(name1->full_name, name2->full_name) == 0) {
+ return NT_STATUS_OK;
+ }
+
+ if (name2->exists) {
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+
+ status = pvfs_match_attrib(pvfs, name1, ren->rename.in.attrib, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = pvfs_access_check_parent(pvfs, req, name2, SEC_DIR_ADD_FILE);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = pvfs_can_rename(pvfs, req, name1, &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_rename_setup_retry(pvfs->ntvfs, req, ren, lck, status);
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = pvfs_do_rename(pvfs, lck, name1, name2->full_name);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ rename a stream
+*/
+static NTSTATUS pvfs_rename_stream(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_rename *ren,
+ struct pvfs_filename *name1)
+{
+ struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ NTSTATUS status;
+ struct odb_lock *lck = NULL;
+
+ if (name1->has_wildcard) {
+ DEBUG(3,(__location__ ": Invalid wildcard rename for %s\n",
+ name1->original_name));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (ren->ntrename.in.new_name[0] != ':') {
+ DEBUG(3,(__location__ ": Invalid rename for %s\n",
+ ren->ntrename.in.new_name));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!name1->exists) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ if (ren->ntrename.in.flags != RENAME_FLAG_RENAME) {
+ DEBUG(3,(__location__ ": Invalid rename flags 0x%x for %s\n",
+ ren->ntrename.in.flags, ren->ntrename.in.new_name));
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ status = pvfs_can_rename(pvfs, req, name1, &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_rename_setup_retry(pvfs->ntvfs, req, ren, lck, status);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = pvfs_access_check_simple(pvfs, req, name1, SEC_FILE_WRITE_ATTRIBUTE);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ status = pvfs_stream_rename(pvfs, name1, -1,
+ ren->ntrename.in.new_name+1,
+ true);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ rename a set of files - ntrename interface
+*/
+static NTSTATUS pvfs_rename_nt(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_rename *ren)
+{
+ struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ NTSTATUS status;
+ struct pvfs_filename *name1, *name2;
+ struct odb_lock *lck = NULL;
+
+ switch (ren->ntrename.in.flags) {
+ case RENAME_FLAG_RENAME:
+ case RENAME_FLAG_HARD_LINK:
+ case RENAME_FLAG_COPY:
+ case RENAME_FLAG_MOVE_CLUSTER_INFORMATION:
+ break;
+ default:
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ /* resolve the cifs name to a posix name */
+ status = pvfs_resolve_name(pvfs, req, ren->ntrename.in.old_name,
+ PVFS_RESOLVE_WILDCARD | PVFS_RESOLVE_STREAMS, &name1);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (name1->stream_name) {
+ /* stream renames need to be handled separately */
+ return pvfs_rename_stream(ntvfs, req, ren, name1);
+ }
+
+ status = pvfs_resolve_name(pvfs, req, ren->ntrename.in.new_name,
+ PVFS_RESOLVE_WILDCARD, &name2);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (name1->has_wildcard || name2->has_wildcard) {
+ return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
+ }
+
+ if (!name1->exists) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ if (strcmp(name1->full_name, name2->full_name) == 0) {
+ return NT_STATUS_OK;
+ }
+
+ if (name2->exists) {
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+
+ status = pvfs_match_attrib(pvfs, name1, ren->ntrename.in.attrib, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = pvfs_can_rename(pvfs, req, name1, &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_rename_setup_retry(pvfs->ntvfs, req, ren, lck, status);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ switch (ren->ntrename.in.flags) {
+ case RENAME_FLAG_RENAME:
+ status = pvfs_access_check_parent(pvfs, req, name2, SEC_DIR_ADD_FILE);
+ NT_STATUS_NOT_OK_RETURN(status);
+ status = pvfs_do_rename(pvfs, lck, name1, name2->full_name);
+ NT_STATUS_NOT_OK_RETURN(status);
+ break;
+
+ case RENAME_FLAG_HARD_LINK:
+ status = pvfs_access_check_parent(pvfs, req, name2, SEC_DIR_ADD_FILE);
+ NT_STATUS_NOT_OK_RETURN(status);
+ if (link(name1->full_name, name2->full_name) == -1) {
+ return pvfs_map_errno(pvfs, errno);
+ }
+ break;
+
+ case RENAME_FLAG_COPY:
+ status = pvfs_access_check_parent(pvfs, req, name2, SEC_DIR_ADD_FILE);
+ NT_STATUS_NOT_OK_RETURN(status);
+ return pvfs_copy_file(pvfs, name1, name2, name1->allow_override && name2->allow_override);
+
+ case RENAME_FLAG_MOVE_CLUSTER_INFORMATION:
+ DEBUG(3,(__location__ ": Invalid rename cluster for %s\n",
+ name1->original_name));
+ return NT_STATUS_INVALID_PARAMETER;
+
+ default:
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+
+ return NT_STATUS_OK;
+}
+
+/*
+ rename a set of files - ntrename interface
+*/
+NTSTATUS pvfs_rename(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_rename *ren)
+{
+ struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ struct pvfs_file *f;
+
+ switch (ren->generic.level) {
+ case RAW_RENAME_RENAME:
+ return pvfs_rename_mv(ntvfs, req, ren);
+
+ case RAW_RENAME_NTRENAME:
+ return pvfs_rename_nt(ntvfs, req, ren);
+
+ case RAW_RENAME_NTTRANS:
+ f = pvfs_find_fd(pvfs, req, ren->nttrans.in.file.ntvfs);
+ if (!f) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ /* wk23 ignores the request */
+ return NT_STATUS_OK;
+
+ default:
+ break;
+ }
+
+ return NT_STATUS_INVALID_LEVEL;
+}
+
diff --git a/source4/ntvfs/posix/pvfs_resolve.c b/source4/ntvfs/posix/pvfs_resolve.c
new file mode 100644
index 0000000..fbd8c7d
--- /dev/null
+++ b/source4/ntvfs/posix/pvfs_resolve.c
@@ -0,0 +1,826 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ POSIX NTVFS backend - filename resolution
+
+ Copyright (C) Andrew Tridgell 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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/>.
+*/
+
+/*
+ this is the core code for converting a filename from the format as
+ given by a client to a posix filename, including any case-matching
+ required, and checks for legal characters
+*/
+
+
+#include "includes.h"
+#include "vfs_posix.h"
+#include "system/dir.h"
+#include "param/param.h"
+
+/**
+ compare two filename components. This is where the name mangling hook will go
+*/
+static int component_compare(struct pvfs_state *pvfs, const char *comp, const char *name)
+{
+ int ret;
+
+ ret = strcasecmp_m(comp, name);
+
+ if (ret != 0) {
+ char *shortname = pvfs_short_name_component(pvfs, name);
+ if (shortname) {
+ ret = strcasecmp_m(comp, shortname);
+ talloc_free(shortname);
+ }
+ }
+
+ return ret;
+}
+
+/*
+ search for a filename in a case insensitive fashion
+
+ TODO: add a cache for previously resolved case-insensitive names
+ TODO: add mangled name support
+*/
+static NTSTATUS pvfs_case_search(struct pvfs_state *pvfs,
+ struct pvfs_filename *name,
+ unsigned int flags)
+{
+ /* break into a series of components */
+ size_t num_components;
+ char **components;
+ char *p, *partial_name;
+ size_t i;
+
+ /* break up the full name into pathname components */
+ num_components=2;
+ p = name->full_name + strlen(pvfs->base_directory) + 1;
+
+ for (;*p;p++) {
+ if (*p == '/') {
+ num_components++;
+ }
+ }
+
+ components = talloc_array(name, char *, num_components);
+ p = name->full_name + strlen(pvfs->base_directory);
+ *p++ = 0;
+
+ components[0] = name->full_name;
+
+ for (i=1;i<num_components;i++) {
+ components[i] = p;
+ p = strchr(p, '/');
+ if (p) *p++ = 0;
+ if (pvfs_is_reserved_name(pvfs, components[i])) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ }
+
+ partial_name = talloc_strdup(name, components[0]);
+ if (!partial_name) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* for each component, check if it exists as-is, and if not then
+ do a directory scan */
+ for (i=1;i<num_components;i++) {
+ char *test_name;
+ DIR *dir;
+ struct dirent *de;
+ char *long_component;
+
+ /* possibly remap from the short name cache */
+ long_component = pvfs_mangled_lookup(pvfs, name, components[i]);
+ if (long_component) {
+ components[i] = long_component;
+ }
+
+ test_name = talloc_asprintf(name, "%s/%s", partial_name, components[i]);
+ if (!test_name) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* check if this component exists as-is */
+ if (stat(test_name, &name->st) == 0) {
+ if (i<num_components-1 && !S_ISDIR(name->st.st_mode)) {
+ return NT_STATUS_OBJECT_PATH_NOT_FOUND;
+ }
+ talloc_free(partial_name);
+ partial_name = test_name;
+ if (i == num_components - 1) {
+ name->exists = true;
+ }
+ continue;
+ }
+
+ /* the filesystem might be case insensitive, in which
+ case a search is pointless unless the name is
+ mangled */
+ if ((pvfs->flags & PVFS_FLAG_CI_FILESYSTEM) &&
+ !pvfs_is_mangled_component(pvfs, components[i])) {
+ if (i < num_components-1) {
+ return NT_STATUS_OBJECT_PATH_NOT_FOUND;
+ }
+ partial_name = test_name;
+ continue;
+ }
+
+ dir = opendir(partial_name);
+ if (!dir) {
+ return pvfs_map_errno(pvfs, errno);
+ }
+
+ while ((de = readdir(dir))) {
+ if (component_compare(pvfs, components[i], de->d_name) == 0) {
+ break;
+ }
+ }
+
+ if (!de) {
+ if (i < num_components-1) {
+ closedir(dir);
+ return NT_STATUS_OBJECT_PATH_NOT_FOUND;
+ }
+ } else {
+ components[i] = talloc_strdup(name, de->d_name);
+ }
+ test_name = talloc_asprintf(name, "%s/%s", partial_name, components[i]);
+ talloc_free(partial_name);
+ partial_name = test_name;
+
+ closedir(dir);
+ }
+
+ if (!name->exists) {
+ if (stat(partial_name, &name->st) == 0) {
+ name->exists = true;
+ }
+ }
+
+ talloc_free(name->full_name);
+ name->full_name = partial_name;
+
+ if (name->exists) {
+ return pvfs_fill_dos_info(pvfs, name, flags, -1);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ parse a alternate data stream name
+*/
+static NTSTATUS parse_stream_name(struct pvfs_filename *name,
+ const char *s)
+{
+ char *p, *stream_name;
+ if (s[1] == '\0') {
+ return NT_STATUS_OBJECT_NAME_INVALID;
+ }
+ name->stream_name = stream_name = talloc_strdup(name, s+1);
+ if (name->stream_name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ p = stream_name;
+
+ while (*p) {
+ size_t c_size;
+ codepoint_t c = next_codepoint(p, &c_size);
+
+ switch (c) {
+ case '/':
+ case '\\':
+ return NT_STATUS_OBJECT_NAME_INVALID;
+ case ':':
+ *p= 0;
+ p++;
+ if (*p == '\0') {
+ return NT_STATUS_OBJECT_NAME_INVALID;
+ }
+ if (strcasecmp_m(p, "$DATA") != 0) {
+ if (strchr_m(p, ':')) {
+ return NT_STATUS_OBJECT_NAME_INVALID;
+ }
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ c_size = 0;
+ p--;
+ break;
+ }
+
+ p += c_size;
+ }
+
+ if (strcmp(name->stream_name, "") == 0) {
+ /*
+ * we don't set stream_name to NULL, here
+ * as this would be wrong for directories
+ *
+ * pvfs_fill_dos_info() will set it to NULL
+ * if it's not a directory.
+ */
+ name->stream_id = 0;
+ } else {
+ name->stream_id = pvfs_name_hash(name->stream_name,
+ strlen(name->stream_name));
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ convert a CIFS pathname to a unix pathname. Note that this does NOT
+ take into account case insensitivity, and in fact does not access
+ the filesystem at all. It is merely a reformatting and charset
+ checking routine.
+
+ errors are returned if the filename is illegal given the flags
+*/
+static NTSTATUS pvfs_unix_path(struct pvfs_state *pvfs, const char *cifs_name,
+ unsigned int flags, struct pvfs_filename *name)
+{
+ char *ret, *p, *p_start;
+ NTSTATUS status;
+
+ name->original_name = talloc_strdup(name, cifs_name);
+
+ /* remove any :$DATA */
+ p = strrchr(name->original_name, ':');
+ if (p && strcasecmp_m(p, ":$DATA") == 0) {
+ if (p > name->original_name && p[-1] == ':') {
+ p--;
+ }
+ *p = 0;
+ }
+
+ name->stream_name = NULL;
+ name->stream_id = 0;
+ name->has_wildcard = false;
+
+ while (*cifs_name == '\\') {
+ cifs_name++;
+ }
+
+ if (*cifs_name == 0) {
+ name->full_name = talloc_asprintf(name, "%s/.", pvfs->base_directory);
+ if (name->full_name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ return NT_STATUS_OK;
+ }
+
+ ret = talloc_asprintf(name, "%s/%s", pvfs->base_directory, cifs_name);
+ if (ret == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ p = ret + strlen(pvfs->base_directory) + 1;
+
+ /* now do an in-place conversion of '\' to '/', checking
+ for legal characters */
+ p_start = p;
+
+ while (*p) {
+ size_t c_size;
+ codepoint_t c = next_codepoint(p, &c_size);
+
+ if (c <= 0x1F) {
+ return NT_STATUS_OBJECT_NAME_INVALID;
+ }
+
+ switch (c) {
+ case '\\':
+ if (name->has_wildcard) {
+ /* wildcards are only allowed in the last part
+ of a name */
+ return NT_STATUS_OBJECT_NAME_INVALID;
+ }
+ if (p > p_start && (p[1] == '\\' || p[1] == '\0')) {
+ /* see if it is definitely a "\\" or
+ * a trailing "\". If it is then fail here,
+ * and let the next layer up try again after
+ * pvfs_reduce_name() if it wants to. This is
+ * much more efficient on average than always
+ * scanning for these separately
+ */
+ return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
+ } else {
+ *p = '/';
+ }
+ break;
+ case ':':
+ if (!(flags & PVFS_RESOLVE_STREAMS)) {
+ return NT_STATUS_OBJECT_NAME_INVALID;
+ }
+ if (name->has_wildcard) {
+ return NT_STATUS_OBJECT_NAME_INVALID;
+ }
+ status = parse_stream_name(name, p);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ *p-- = 0;
+ break;
+ case '*':
+ case '>':
+ case '<':
+ case '?':
+ case '"':
+ if (!(flags & PVFS_RESOLVE_WILDCARD)) {
+ return NT_STATUS_OBJECT_NAME_INVALID;
+ }
+ name->has_wildcard = true;
+ break;
+ case '/':
+ case '|':
+ return NT_STATUS_OBJECT_NAME_INVALID;
+ case '.':
+ /* see if it is definitely a .. or
+ . component. If it is then fail here, and
+ let the next layer up try again after
+ pvfs_reduce_name() if it wants to. This is
+ much more efficient on average than always
+ scanning for these separately */
+ if (p[1] == '.' &&
+ (p[2] == 0 || p[2] == '\\') &&
+ (p == p_start || p[-1] == '/')) {
+ return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
+ }
+ if ((p[1] == 0 || p[1] == '\\') &&
+ (p == p_start || p[-1] == '/')) {
+ return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
+ }
+ break;
+ }
+
+ p += c_size;
+ }
+
+ name->full_name = ret;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ reduce a name that contains .. components or repeated \ separators
+ return NULL if it can't be reduced
+*/
+static NTSTATUS pvfs_reduce_name(TALLOC_CTX *mem_ctx,
+ const char **fname, unsigned int flags)
+{
+ codepoint_t c;
+ size_t c_size, len;
+ size_t i, num_components, err_count;
+ char **components;
+ char *p, *s, *ret;
+
+ s = talloc_strdup(mem_ctx, *fname);
+ if (s == NULL) return NT_STATUS_NO_MEMORY;
+
+ for (num_components=1, p=s; *p; p += c_size) {
+ c = next_codepoint(p, &c_size);
+ if (c == '\\') num_components++;
+ }
+
+ components = talloc_array(s, char *, num_components+1);
+ if (components == NULL) {
+ talloc_free(s);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ components[0] = s;
+ for (i=0, p=s; *p; p += c_size) {
+ c = next_codepoint(p, &c_size);
+ if (c == '\\') {
+ *p = 0;
+ components[++i] = p+1;
+ }
+ }
+ components[i+1] = NULL;
+
+ /*
+ rather bizarre!
+
+ '.' components are not allowed, but the rules for what error
+ code to give don't seem to make sense. This is a close
+ approximation.
+ */
+ for (err_count=i=0;components[i];i++) {
+ if (strcmp(components[i], "") == 0) {
+ continue;
+ }
+ if (ISDOT(components[i]) || err_count) {
+ err_count++;
+ }
+ }
+ if (err_count > 0) {
+ if (flags & PVFS_RESOLVE_WILDCARD) err_count--;
+
+ if (err_count==1) {
+ return NT_STATUS_OBJECT_NAME_INVALID;
+ } else {
+ return NT_STATUS_OBJECT_PATH_NOT_FOUND;
+ }
+ }
+
+ /* remove any null components */
+ for (i=0;components[i];i++) {
+ if (strcmp(components[i], "") == 0) {
+ memmove(&components[i], &components[i+1],
+ sizeof(char *)*(num_components-i));
+ i--;
+ continue;
+ }
+ if (ISDOTDOT(components[i])) {
+ if (i < 1) return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
+ memmove(&components[i-1], &components[i+1],
+ sizeof(char *)*(num_components-i));
+ i -= 2;
+ continue;
+ }
+ }
+
+ if (components[0] == NULL) {
+ talloc_free(s);
+ *fname = talloc_strdup(mem_ctx, "\\");
+ return NT_STATUS_OK;
+ }
+
+ for (len=i=0;components[i];i++) {
+ len += strlen(components[i]) + 1;
+ }
+
+ /* rebuild the name */
+ ret = talloc_array(mem_ctx, char, len+1);
+ if (ret == NULL) {
+ talloc_free(s);
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ for (len=0,i=0;components[i];i++) {
+ size_t len1 = strlen(components[i]);
+ ret[len] = '\\';
+ memcpy(ret+len+1, components[i], len1);
+ len += len1 + 1;
+ }
+ ret[len] = 0;
+
+ talloc_set_name_const(ret, ret);
+
+ talloc_free(s);
+
+ *fname = ret;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ resolve a name from relative client format to a struct pvfs_filename
+ the memory for the filename is made as a talloc child of 'name'
+
+ flags include:
+ PVFS_RESOLVE_NO_WILDCARD = wildcards are considered illegal characters
+ PVFS_RESOLVE_STREAMS = stream names are allowed
+
+ TODO: ../ collapsing, and outside share checking
+*/
+NTSTATUS pvfs_resolve_name(struct pvfs_state *pvfs,
+ struct ntvfs_request *req,
+ const char *cifs_name,
+ unsigned int flags, struct pvfs_filename **name)
+{
+ NTSTATUS status;
+
+ *name = talloc(req, struct pvfs_filename);
+ if (*name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*name)->exists = false;
+ (*name)->stream_exists = false;
+ (*name)->allow_override = false;
+
+ if (!(pvfs->fs_attribs & FS_ATTR_NAMED_STREAMS)) {
+ flags &= ~PVFS_RESOLVE_STREAMS;
+ }
+
+ /* SMB2 doesn't allow a leading slash */
+ if (req->ctx->protocol >= PROTOCOL_SMB2_02 &&
+ *cifs_name == '\\') {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* do the basic conversion to a unix formatted path,
+ also checking for allowable characters */
+ status = pvfs_unix_path(pvfs, cifs_name, flags, *name);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_PATH_SYNTAX_BAD)) {
+ /* it might contain .. components which need to be reduced */
+ status = pvfs_reduce_name(*name, &cifs_name, flags);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ status = pvfs_unix_path(pvfs, cifs_name, flags, *name);
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* if it has a wildcard then no point doing a stat() of the
+ full name. Instead We need check if the directory exists
+ */
+ if ((*name)->has_wildcard) {
+ const char *p;
+ char *dir_name, *saved_name;
+ p = strrchr((*name)->full_name, '/');
+ if (p == NULL) {
+ /* root directory wildcard is OK */
+ return NT_STATUS_OK;
+ }
+ dir_name = talloc_strndup(*name, (*name)->full_name, (p-(*name)->full_name));
+ if (stat(dir_name, &(*name)->st) == 0) {
+ talloc_free(dir_name);
+ return NT_STATUS_OK;
+ }
+ /* we need to search for a matching name */
+ saved_name = (*name)->full_name;
+ (*name)->full_name = dir_name;
+ status = pvfs_case_search(pvfs, *name, flags);
+ if (!NT_STATUS_IS_OK(status)) {
+ /* the directory doesn't exist */
+ (*name)->full_name = saved_name;
+ return status;
+ }
+ /* it does exist, but might need a case change */
+ if (dir_name != (*name)->full_name) {
+ (*name)->full_name = talloc_asprintf(*name, "%s%s",
+ (*name)->full_name, p);
+ NT_STATUS_HAVE_NO_MEMORY((*name)->full_name);
+ } else {
+ (*name)->full_name = saved_name;
+ talloc_free(dir_name);
+ }
+ return NT_STATUS_OK;
+ }
+
+ /* if we can stat() the full name now then we are done */
+ if (stat((*name)->full_name, &(*name)->st) == 0) {
+ (*name)->exists = true;
+ return pvfs_fill_dos_info(pvfs, *name, flags, -1);
+ }
+
+ /* search for a matching filename */
+ status = pvfs_case_search(pvfs, *name, flags);
+
+ return status;
+}
+
+
+/*
+ do a partial resolve, returning a pvfs_filename structure given a
+ base path and a relative component. It is an error if the file does
+ not exist. No case-insensitive matching is done.
+
+ this is used in places like directory searching where we need a pvfs_filename
+ to pass to a function, but already know the unix base directory and component
+*/
+NTSTATUS pvfs_resolve_partial(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx,
+ const char *unix_dir, const char *fname,
+ unsigned int flags, struct pvfs_filename **name)
+{
+ NTSTATUS status;
+
+ *name = talloc(mem_ctx, struct pvfs_filename);
+ if (*name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*name)->full_name = talloc_asprintf(*name, "%s/%s", unix_dir, fname);
+ if ((*name)->full_name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (stat((*name)->full_name, &(*name)->st) == -1) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ (*name)->exists = true;
+ (*name)->stream_exists = true;
+ (*name)->has_wildcard = false;
+ (*name)->original_name = talloc_strdup(*name, fname);
+ (*name)->stream_name = NULL;
+ (*name)->stream_id = 0;
+ (*name)->allow_override = false;
+
+ status = pvfs_fill_dos_info(pvfs, *name, flags, -1);
+
+ return status;
+}
+
+
+/*
+ fill in the pvfs_filename info for an open file, given the current
+ info for a (possibly) non-open file. This is used by places that need
+ to update the pvfs_filename stat information, and by pvfs_open()
+*/
+NTSTATUS pvfs_resolve_name_fd(struct pvfs_state *pvfs, int fd,
+ struct pvfs_filename *name, unsigned int flags)
+{
+ dev_t device = (dev_t)0;
+ ino_t inode = 0;
+
+ if (name->exists) {
+ device = name->st.st_dev;
+ inode = name->st.st_ino;
+ }
+
+ if (fd == -1) {
+ if (stat(name->full_name, &name->st) == -1) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+ } else {
+ if (fstat(fd, &name->st) == -1) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+ }
+
+ if (name->exists &&
+ (device != name->st.st_dev || inode != name->st.st_ino)) {
+ /* the file we are looking at has changed! this could
+ be someone trying to exploit a race
+ condition. Certainly we don't want to continue
+ operating on this file */
+ DEBUG(0,("pvfs: WARNING: file '%s' changed during resolve - failing\n",
+ name->full_name));
+ return NT_STATUS_UNEXPECTED_IO_ERROR;
+ }
+
+ name->exists = true;
+
+ return pvfs_fill_dos_info(pvfs, name, flags, fd);
+}
+
+/*
+ fill in the pvfs_filename info for an open file, given the current
+ info for a (possibly) non-open file. This is used by places that need
+ to update the pvfs_filename stat information, and the path
+ after a possible rename on a different handle.
+*/
+NTSTATUS pvfs_resolve_name_handle(struct pvfs_state *pvfs,
+ struct pvfs_file_handle *h)
+{
+ NTSTATUS status;
+
+ if (h->have_opendb_entry) {
+ struct odb_lock *lck;
+ const char *name = NULL;
+
+ lck = odb_lock(h, h->pvfs->odb_context, &h->odb_locking_key);
+ if (lck == NULL) {
+ DEBUG(0,("%s: failed to lock file '%s' in opendb\n",
+ __FUNCTION__, h->name->full_name));
+ /* we were supposed to do a blocking lock, so something
+ is badly wrong! */
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ status = odb_get_path(lck, &name);
+ if (NT_STATUS_IS_OK(status)) {
+ /*
+ * This relies on the fact that
+ * renames of open files are only
+ * allowed by setpathinfo() and setfileinfo()
+ * and there're only renames within the same
+ * directory supported
+ */
+ if (strcmp(h->name->full_name, name) != 0) {
+ const char *orig_dir;
+ const char *new_file;
+ char *new_orig;
+ char *delim;
+ char *full_name = discard_const_p(char, name);
+
+ delim = strrchr(name, '/');
+ if (!delim) {
+ talloc_free(lck);
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ new_file = delim + 1;
+ delim = strrchr(h->name->original_name, '\\');
+ if (delim) {
+ delim[0] = '\0';
+ orig_dir = h->name->original_name;
+ new_orig = talloc_asprintf(h->name, "%s\\%s",
+ orig_dir, new_file);
+ if (!new_orig) {
+ talloc_free(lck);
+ return NT_STATUS_NO_MEMORY;
+ }
+ } else {
+ new_orig = talloc_strdup(h->name, new_file);
+ if (!new_orig) {
+ talloc_free(lck);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ talloc_free(h->name->original_name);
+ talloc_free(h->name->full_name);
+ h->name->full_name = talloc_steal(h->name, full_name);
+ h->name->original_name = new_orig;
+ }
+ }
+
+ talloc_free(lck);
+ }
+
+ /*
+ * TODO: pass PVFS_RESOLVE_NO_OPENDB and get
+ * the write time from odb_lock() above.
+ */
+ status = pvfs_resolve_name_fd(pvfs, h->fd, h->name, 0);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ if (!null_nttime(h->write_time.close_time)) {
+ h->name->dos.write_time = h->write_time.close_time;
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ resolve the parent of a given name
+*/
+NTSTATUS pvfs_resolve_parent(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx,
+ const struct pvfs_filename *child,
+ struct pvfs_filename **name)
+{
+ NTSTATUS status;
+ char *p;
+
+ *name = talloc(mem_ctx, struct pvfs_filename);
+ if (*name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*name)->full_name = talloc_strdup(*name, child->full_name);
+ if ((*name)->full_name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ p = strrchr_m((*name)->full_name, '/');
+ if (p == NULL) {
+ return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
+ }
+
+ /* this handles the root directory */
+ if (p == (*name)->full_name) {
+ p[1] = 0;
+ } else {
+ p[0] = 0;
+ }
+
+ if (stat((*name)->full_name, &(*name)->st) == -1) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ (*name)->exists = true;
+ (*name)->stream_exists = true;
+ (*name)->has_wildcard = false;
+ /* we can't get the correct 'original_name', but for the purposes
+ of this call this is close enough */
+ (*name)->original_name = talloc_strdup(*name, child->original_name);
+ if ((*name)->original_name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ (*name)->stream_name = NULL;
+ (*name)->stream_id = 0;
+ (*name)->allow_override = false;
+
+ status = pvfs_fill_dos_info(pvfs, *name, PVFS_RESOLVE_NO_OPENDB, -1);
+
+ return status;
+}
diff --git a/source4/ntvfs/posix/pvfs_search.c b/source4/ntvfs/posix/pvfs_search.c
new file mode 100644
index 0000000..f352a6a
--- /dev/null
+++ b/source4/ntvfs/posix/pvfs_search.c
@@ -0,0 +1,865 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ POSIX NTVFS backend - directory search functions
+
+ Copyright (C) Andrew Tridgell 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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 "vfs_posix.h"
+#include "system/time.h"
+#include "librpc/gen_ndr/security.h"
+#include "samba/service_stream.h"
+#include "lib/events/events.h"
+#include "../lib/util/dlinklist.h"
+#include "lib/util/idtree.h"
+
+/* place a reasonable limit on old-style searches as clients tend to
+ not send search close requests */
+#define MAX_OLD_SEARCHES 2000
+#define MAX_SEARCH_HANDLES (UINT16_MAX - 1)
+#define INVALID_SEARCH_HANDLE UINT16_MAX
+
+/*
+ destroy an open search
+*/
+static int pvfs_search_destructor(struct pvfs_search_state *search)
+{
+ DLIST_REMOVE(search->pvfs->search.list, search);
+ idr_remove(search->pvfs->search.idtree, search->handle);
+ return 0;
+}
+
+/*
+ called when a search timer goes off
+*/
+static void pvfs_search_timer(struct tevent_context *ev, struct tevent_timer *te,
+ struct timeval t, void *ptr)
+{
+ struct pvfs_search_state *search = talloc_get_type(ptr, struct pvfs_search_state);
+ talloc_free(search);
+}
+
+/*
+ setup a timer to destroy a open search after a inactivity period
+*/
+static void pvfs_search_setup_timer(struct pvfs_search_state *search)
+{
+ struct tevent_context *ev = search->pvfs->ntvfs->ctx->event_ctx;
+ if (search->handle == INVALID_SEARCH_HANDLE) return;
+ talloc_free(search->te);
+ search->te = tevent_add_timer(ev, search,
+ timeval_current_ofs(search->pvfs->search.inactivity_time, 0),
+ pvfs_search_timer, search);
+}
+
+/*
+ fill in a single search result for a given info level
+*/
+static NTSTATUS fill_search_info(struct pvfs_state *pvfs,
+ enum smb_search_data_level level,
+ const char *unix_path,
+ const char *fname,
+ struct pvfs_search_state *search,
+ off_t dir_offset,
+ union smb_search_data *file)
+{
+ struct pvfs_filename *name;
+ NTSTATUS status;
+ const char *shortname;
+ uint32_t dir_index = (uint32_t)dir_offset; /* truncated - see the code
+ in pvfs_list_seek_ofs() for
+ how we cope with this */
+
+ status = pvfs_resolve_partial(pvfs, file, unix_path, fname, 0, &name);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = pvfs_match_attrib(pvfs, name, search->search_attrib, search->must_attrib);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ switch (level) {
+ case RAW_SEARCH_DATA_SEARCH:
+ shortname = pvfs_short_name(pvfs, name, name);
+ file->search.attrib = name->dos.attrib;
+ file->search.write_time = nt_time_to_unix(name->dos.write_time);
+ file->search.size = name->st.st_size;
+ file->search.name = shortname;
+ file->search.id.reserved = search->handle >> 8;
+ memset(file->search.id.name, ' ', sizeof(file->search.id.name));
+ memcpy(file->search.id.name, shortname,
+ MIN(strlen(shortname)+1, sizeof(file->search.id.name)));
+ file->search.id.handle = search->handle & 0xFF;
+ file->search.id.server_cookie = dir_index;
+ file->search.id.client_cookie = 0;
+ return NT_STATUS_OK;
+
+ case RAW_SEARCH_DATA_STANDARD:
+ file->standard.resume_key = dir_index;
+ file->standard.create_time = nt_time_to_unix(name->dos.create_time);
+ file->standard.access_time = nt_time_to_unix(name->dos.access_time);
+ file->standard.write_time = nt_time_to_unix(name->dos.write_time);
+ file->standard.size = name->st.st_size;
+ file->standard.alloc_size = name->dos.alloc_size;
+ file->standard.attrib = name->dos.attrib;
+ file->standard.name.s = fname;
+ return NT_STATUS_OK;
+
+ case RAW_SEARCH_DATA_EA_SIZE:
+ file->ea_size.resume_key = dir_index;
+ file->ea_size.create_time = nt_time_to_unix(name->dos.create_time);
+ file->ea_size.access_time = nt_time_to_unix(name->dos.access_time);
+ file->ea_size.write_time = nt_time_to_unix(name->dos.write_time);
+ file->ea_size.size = name->st.st_size;
+ file->ea_size.alloc_size = name->dos.alloc_size;
+ file->ea_size.attrib = name->dos.attrib;
+ file->ea_size.ea_size = name->dos.ea_size;
+ file->ea_size.name.s = fname;
+ return NT_STATUS_OK;
+
+ case RAW_SEARCH_DATA_EA_LIST:
+ file->ea_list.resume_key = dir_index;
+ file->ea_list.create_time = nt_time_to_unix(name->dos.create_time);
+ file->ea_list.access_time = nt_time_to_unix(name->dos.access_time);
+ file->ea_list.write_time = nt_time_to_unix(name->dos.write_time);
+ file->ea_list.size = name->st.st_size;
+ file->ea_list.alloc_size = name->dos.alloc_size;
+ file->ea_list.attrib = name->dos.attrib;
+ file->ea_list.name.s = fname;
+ return pvfs_query_ea_list(pvfs, file, name, -1,
+ search->num_ea_names,
+ search->ea_names,
+ &file->ea_list.eas);
+
+ case RAW_SEARCH_DATA_DIRECTORY_INFO:
+ file->directory_info.file_index = dir_index;
+ file->directory_info.create_time = name->dos.create_time;
+ file->directory_info.access_time = name->dos.access_time;
+ file->directory_info.write_time = name->dos.write_time;
+ file->directory_info.change_time = name->dos.change_time;
+ file->directory_info.size = name->st.st_size;
+ file->directory_info.alloc_size = name->dos.alloc_size;
+ file->directory_info.attrib = name->dos.attrib;
+ file->directory_info.name.s = fname;
+ return NT_STATUS_OK;
+
+ case RAW_SEARCH_DATA_FULL_DIRECTORY_INFO:
+ file->full_directory_info.file_index = dir_index;
+ file->full_directory_info.create_time = name->dos.create_time;
+ file->full_directory_info.access_time = name->dos.access_time;
+ file->full_directory_info.write_time = name->dos.write_time;
+ file->full_directory_info.change_time = name->dos.change_time;
+ file->full_directory_info.size = name->st.st_size;
+ file->full_directory_info.alloc_size = name->dos.alloc_size;
+ file->full_directory_info.attrib = name->dos.attrib;
+ file->full_directory_info.ea_size = name->dos.ea_size;
+ file->full_directory_info.name.s = fname;
+ return NT_STATUS_OK;
+
+ case RAW_SEARCH_DATA_NAME_INFO:
+ file->name_info.file_index = dir_index;
+ file->name_info.name.s = fname;
+ return NT_STATUS_OK;
+
+ case RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO:
+ file->both_directory_info.file_index = dir_index;
+ file->both_directory_info.create_time = name->dos.create_time;
+ file->both_directory_info.access_time = name->dos.access_time;
+ file->both_directory_info.write_time = name->dos.write_time;
+ file->both_directory_info.change_time = name->dos.change_time;
+ file->both_directory_info.size = name->st.st_size;
+ file->both_directory_info.alloc_size = name->dos.alloc_size;
+ file->both_directory_info.attrib = name->dos.attrib;
+ file->both_directory_info.ea_size = name->dos.ea_size;
+ file->both_directory_info.short_name.s = pvfs_short_name(pvfs, file, name);
+ file->both_directory_info.name.s = fname;
+ return NT_STATUS_OK;
+
+ case RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO:
+ file->id_full_directory_info.file_index = dir_index;
+ file->id_full_directory_info.create_time = name->dos.create_time;
+ file->id_full_directory_info.access_time = name->dos.access_time;
+ file->id_full_directory_info.write_time = name->dos.write_time;
+ file->id_full_directory_info.change_time = name->dos.change_time;
+ file->id_full_directory_info.size = name->st.st_size;
+ file->id_full_directory_info.alloc_size = name->dos.alloc_size;
+ file->id_full_directory_info.attrib = name->dos.attrib;
+ file->id_full_directory_info.ea_size = name->dos.ea_size;
+ file->id_full_directory_info.file_id = name->dos.file_id;
+ file->id_full_directory_info.name.s = fname;
+ return NT_STATUS_OK;
+
+ case RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO:
+ file->id_both_directory_info.file_index = dir_index;
+ file->id_both_directory_info.create_time = name->dos.create_time;
+ file->id_both_directory_info.access_time = name->dos.access_time;
+ file->id_both_directory_info.write_time = name->dos.write_time;
+ file->id_both_directory_info.change_time = name->dos.change_time;
+ file->id_both_directory_info.size = name->st.st_size;
+ file->id_both_directory_info.alloc_size = name->dos.alloc_size;
+ file->id_both_directory_info.attrib = name->dos.attrib;
+ file->id_both_directory_info.ea_size = name->dos.ea_size;
+ file->id_both_directory_info.file_id = name->dos.file_id;
+ file->id_both_directory_info.short_name.s = pvfs_short_name(pvfs, file, name);
+ file->id_both_directory_info.name.s = fname;
+ return NT_STATUS_OK;
+
+ case RAW_SEARCH_DATA_GENERIC:
+ case RAW_SEARCH_DATA_UNIX_INFO:
+ case RAW_SEARCH_DATA_UNIX_INFO2:
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ return NT_STATUS_INVALID_LEVEL;
+}
+
+
+/*
+ the search fill loop
+*/
+static NTSTATUS pvfs_search_fill(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx,
+ unsigned int max_count,
+ struct pvfs_search_state *search,
+ enum smb_search_data_level level,
+ unsigned int *reply_count,
+ void *search_private,
+ bool (*callback)(void *, const union smb_search_data *))
+{
+ struct pvfs_dir *dir = search->dir;
+ NTSTATUS status;
+
+ *reply_count = 0;
+
+ if (max_count == 0) {
+ max_count = 1;
+ }
+
+ while ((*reply_count) < max_count) {
+ union smb_search_data *file;
+ const char *name;
+ off_t ofs = search->current_index;
+
+ name = pvfs_list_next(dir, &search->current_index);
+ if (name == NULL) break;
+
+ file = talloc(mem_ctx, union smb_search_data);
+ if (!file) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = fill_search_info(pvfs, level,
+ pvfs_list_unix_path(dir), name,
+ search, search->current_index, file);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(file);
+ continue;
+ }
+
+ if (!callback(search_private, file)) {
+ talloc_free(file);
+ search->current_index = ofs;
+ break;
+ }
+
+ (*reply_count)++;
+ talloc_free(file);
+ }
+
+ pvfs_search_setup_timer(search);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ we've run out of search handles - cleanup those that the client forgot
+ to close
+*/
+static void pvfs_search_cleanup(struct pvfs_state *pvfs)
+{
+ int i;
+ time_t t = time_mono(NULL);
+
+ for (i=0;i<MAX_OLD_SEARCHES;i++) {
+ struct pvfs_search_state *search;
+ void *p = idr_find(pvfs->search.idtree, i);
+
+ if (p == NULL) return;
+
+ search = talloc_get_type(p, struct pvfs_search_state);
+ if (pvfs_list_eos(search->dir, search->current_index) &&
+ search->last_used != 0 &&
+ t > search->last_used + 30) {
+ /* its almost certainly been forgotten
+ about */
+ talloc_free(search);
+ }
+ }
+}
+
+
+/*
+ list files in a directory matching a wildcard pattern - old SMBsearch interface
+*/
+static NTSTATUS pvfs_search_first_old(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_search_first *io,
+ void *search_private,
+ bool (*callback)(void *, const union smb_search_data *))
+{
+ struct pvfs_dir *dir;
+ struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ struct pvfs_search_state *search;
+ unsigned int reply_count;
+ uint16_t search_attrib;
+ const char *pattern;
+ NTSTATUS status;
+ struct pvfs_filename *name;
+ int id;
+
+ search_attrib = io->search_first.in.search_attrib;
+ pattern = io->search_first.in.pattern;
+
+ /* resolve the cifs name to a posix name */
+ status = pvfs_resolve_name(pvfs, req, pattern, PVFS_RESOLVE_WILDCARD, &name);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (!name->has_wildcard && !name->exists) {
+ return STATUS_NO_MORE_FILES;
+ }
+
+ status = pvfs_access_check_parent(pvfs, req, name, SEC_DIR_TRAVERSE | SEC_DIR_LIST);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* we initially make search a child of the request, then if we
+ need to keep it long term we steal it for the private
+ structure */
+ search = talloc(req, struct pvfs_search_state);
+ if (!search) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* do the actual directory listing */
+ status = pvfs_list_start(pvfs, name, search, &dir);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* we need to give a handle back to the client so it
+ can continue a search */
+ id = idr_get_new(pvfs->search.idtree, search, MAX_OLD_SEARCHES);
+ if (id == -1) {
+ pvfs_search_cleanup(pvfs);
+ id = idr_get_new(pvfs->search.idtree, search, MAX_OLD_SEARCHES);
+ }
+ if (id == -1) {
+ return NT_STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ search->pvfs = pvfs;
+ search->handle = id;
+ search->dir = dir;
+ search->current_index = 0;
+ search->search_attrib = search_attrib & 0xFF;
+ search->must_attrib = (search_attrib>>8) & 0xFF;
+ search->last_used = time_mono(NULL);
+ search->te = NULL;
+
+ DLIST_ADD(pvfs->search.list, search);
+
+ talloc_set_destructor(search, pvfs_search_destructor);
+
+ status = pvfs_search_fill(pvfs, req, io->search_first.in.max_count, search, io->generic.data_level,
+ &reply_count, search_private, callback);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ io->search_first.out.count = reply_count;
+
+ /* not matching any entries is an error */
+ if (reply_count == 0) {
+ return STATUS_NO_MORE_FILES;
+ }
+
+ talloc_steal(pvfs, search);
+
+ return NT_STATUS_OK;
+}
+
+/* continue a old style search */
+static NTSTATUS pvfs_search_next_old(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_search_next *io,
+ void *search_private,
+ bool (*callback)(void *, const union smb_search_data *))
+{
+ struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ void *p;
+ struct pvfs_search_state *search;
+ struct pvfs_dir *dir;
+ unsigned int reply_count, max_count;
+ uint16_t handle;
+ NTSTATUS status;
+
+ handle = io->search_next.in.id.handle | (io->search_next.in.id.reserved<<8);
+ max_count = io->search_next.in.max_count;
+
+ p = idr_find(pvfs->search.idtree, handle);
+ if (p == NULL) {
+ /* we didn't find the search handle */
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ search = talloc_get_type(p, struct pvfs_search_state);
+
+ dir = search->dir;
+
+ status = pvfs_list_seek_ofs(dir, io->search_next.in.id.server_cookie,
+ &search->current_index);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ search->last_used = time_mono(NULL);
+
+ status = pvfs_search_fill(pvfs, req, max_count, search, io->generic.data_level,
+ &reply_count, search_private, callback);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ io->search_next.out.count = reply_count;
+
+ /* not matching any entries means end of search */
+ if (reply_count == 0) {
+ talloc_free(search);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ list files in a directory matching a wildcard pattern
+*/
+static NTSTATUS pvfs_search_first_trans2(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_search_first *io,
+ void *search_private,
+ bool (*callback)(void *, const union smb_search_data *))
+{
+ struct pvfs_dir *dir;
+ struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ struct pvfs_search_state *search;
+ unsigned int reply_count;
+ uint16_t search_attrib, max_count;
+ const char *pattern;
+ NTSTATUS status;
+ struct pvfs_filename *name;
+ int id;
+
+ search_attrib = io->t2ffirst.in.search_attrib;
+ pattern = io->t2ffirst.in.pattern;
+ max_count = io->t2ffirst.in.max_count;
+
+ /* resolve the cifs name to a posix name */
+ status = pvfs_resolve_name(pvfs, req, pattern, PVFS_RESOLVE_WILDCARD, &name);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (!name->has_wildcard && !name->exists) {
+ return NT_STATUS_NO_SUCH_FILE;
+ }
+
+ status = pvfs_access_check_parent(pvfs, req, name, SEC_DIR_TRAVERSE | SEC_DIR_LIST);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* we initially make search a child of the request, then if we
+ need to keep it long term we steal it for the private
+ structure */
+ search = talloc(req, struct pvfs_search_state);
+ if (!search) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* do the actual directory listing */
+ status = pvfs_list_start(pvfs, name, search, &dir);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ id = idr_get_new(pvfs->search.idtree, search, MAX_SEARCH_HANDLES);
+ if (id == -1) {
+ return NT_STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ search->pvfs = pvfs;
+ search->handle = id;
+ search->dir = dir;
+ search->current_index = 0;
+ search->search_attrib = search_attrib;
+ search->must_attrib = 0;
+ search->last_used = 0;
+ search->num_ea_names = io->t2ffirst.in.num_names;
+ search->ea_names = io->t2ffirst.in.ea_names;
+ search->te = NULL;
+
+ DLIST_ADD(pvfs->search.list, search);
+ talloc_set_destructor(search, pvfs_search_destructor);
+
+ status = pvfs_search_fill(pvfs, req, max_count, search, io->generic.data_level,
+ &reply_count, search_private, callback);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* not matching any entries is an error */
+ if (reply_count == 0) {
+ return NT_STATUS_NO_SUCH_FILE;
+ }
+
+ io->t2ffirst.out.count = reply_count;
+ io->t2ffirst.out.handle = search->handle;
+ io->t2ffirst.out.end_of_search = pvfs_list_eos(dir, search->current_index) ? 1 : 0;
+
+ /* work out if we are going to keep the search state
+ and allow for a search continue */
+ if ((io->t2ffirst.in.flags & FLAG_TRANS2_FIND_CLOSE) ||
+ ((io->t2ffirst.in.flags & FLAG_TRANS2_FIND_CLOSE_IF_END) &&
+ io->t2ffirst.out.end_of_search)) {
+ talloc_free(search);
+ } else {
+ talloc_steal(pvfs, search);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/* continue a search */
+static NTSTATUS pvfs_search_next_trans2(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_search_next *io,
+ void *search_private,
+ bool (*callback)(void *, const union smb_search_data *))
+{
+ struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ void *p;
+ struct pvfs_search_state *search;
+ struct pvfs_dir *dir;
+ unsigned int reply_count;
+ uint16_t handle;
+ NTSTATUS status;
+
+ handle = io->t2fnext.in.handle;
+
+ p = idr_find(pvfs->search.idtree, handle);
+ if (p == NULL) {
+ /* we didn't find the search handle */
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ search = talloc_get_type(p, struct pvfs_search_state);
+
+ dir = search->dir;
+
+ status = NT_STATUS_OK;
+
+ /* work out what type of continuation is being used */
+ if (io->t2fnext.in.last_name && *io->t2fnext.in.last_name) {
+ status = pvfs_list_seek(dir, io->t2fnext.in.last_name, &search->current_index);
+ if (!NT_STATUS_IS_OK(status) && io->t2fnext.in.resume_key) {
+ status = pvfs_list_seek_ofs(dir, io->t2fnext.in.resume_key,
+ &search->current_index);
+ }
+ } else if (!(io->t2fnext.in.flags & FLAG_TRANS2_FIND_CONTINUE)) {
+ status = pvfs_list_seek_ofs(dir, io->t2fnext.in.resume_key,
+ &search->current_index);
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ search->num_ea_names = io->t2fnext.in.num_names;
+ search->ea_names = io->t2fnext.in.ea_names;
+
+ status = pvfs_search_fill(pvfs, req, io->t2fnext.in.max_count, search, io->generic.data_level,
+ &reply_count, search_private, callback);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ io->t2fnext.out.count = reply_count;
+ io->t2fnext.out.end_of_search = pvfs_list_eos(dir, search->current_index) ? 1 : 0;
+
+ /* work out if we are going to keep the search state */
+ if ((io->t2fnext.in.flags & FLAG_TRANS2_FIND_CLOSE) ||
+ ((io->t2fnext.in.flags & FLAG_TRANS2_FIND_CLOSE_IF_END) &&
+ io->t2fnext.out.end_of_search)) {
+ talloc_free(search);
+ }
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS pvfs_search_first_smb2(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, const struct smb2_find *io,
+ void *search_private,
+ bool (*callback)(void *, const union smb_search_data *))
+{
+ struct pvfs_dir *dir;
+ struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ struct pvfs_search_state *search;
+ unsigned int reply_count;
+ uint16_t max_count;
+ const char *pattern;
+ NTSTATUS status;
+ struct pvfs_filename *name;
+ struct pvfs_file *f;
+
+ f = pvfs_find_fd(pvfs, req, io->in.file.ntvfs);
+ if (!f) {
+ return NT_STATUS_FILE_CLOSED;
+ }
+
+ /* its only valid for directories */
+ if (f->handle->fd != -1) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!(f->access_mask & SEC_DIR_LIST)) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ if (f->search) {
+ talloc_free(f->search);
+ f->search = NULL;
+ }
+
+ if (strequal(io->in.pattern, "")) {
+ return NT_STATUS_OBJECT_NAME_INVALID;
+ }
+ if (strchr_m(io->in.pattern, '\\')) {
+ return NT_STATUS_OBJECT_NAME_INVALID;
+ }
+ if (strchr_m(io->in.pattern, '/')) {
+ return NT_STATUS_OBJECT_NAME_INVALID;
+ }
+
+ if (strequal("", f->handle->name->original_name)) {
+ pattern = talloc_asprintf(req, "%s", io->in.pattern);
+ NT_STATUS_HAVE_NO_MEMORY(pattern);
+ } else {
+ pattern = talloc_asprintf(req, "%s\\%s",
+ f->handle->name->original_name,
+ io->in.pattern);
+ NT_STATUS_HAVE_NO_MEMORY(pattern);
+ }
+
+ /* resolve the cifs name to a posix name */
+ status = pvfs_resolve_name(pvfs, req, pattern, PVFS_RESOLVE_WILDCARD, &name);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ if (!name->has_wildcard && !name->exists) {
+ return NT_STATUS_NO_SUCH_FILE;
+ }
+
+ /* we initially make search a child of the request, then if we
+ need to keep it long term we steal it for the private
+ structure */
+ search = talloc(req, struct pvfs_search_state);
+ NT_STATUS_HAVE_NO_MEMORY(search);
+
+ /* do the actual directory listing */
+ status = pvfs_list_start(pvfs, name, search, &dir);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ search->pvfs = pvfs;
+ search->handle = INVALID_SEARCH_HANDLE;
+ search->dir = dir;
+ search->current_index = 0;
+ search->search_attrib = 0x0000FFFF;
+ search->must_attrib = 0;
+ search->last_used = 0;
+ search->num_ea_names = 0;
+ search->ea_names = NULL;
+ search->te = NULL;
+
+ if (io->in.continue_flags & SMB2_CONTINUE_FLAG_SINGLE) {
+ max_count = 1;
+ } else {
+ max_count = UINT16_MAX;
+ }
+
+ status = pvfs_search_fill(pvfs, req, max_count, search, io->data_level,
+ &reply_count, search_private, callback);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ /* not matching any entries is an error */
+ if (reply_count == 0) {
+ return NT_STATUS_NO_SUCH_FILE;
+ }
+
+ f->search = talloc_steal(f, search);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS pvfs_search_next_smb2(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, const struct smb2_find *io,
+ void *search_private,
+ bool (*callback)(void *, const union smb_search_data *))
+{
+ struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ struct pvfs_search_state *search;
+ unsigned int reply_count;
+ uint16_t max_count;
+ NTSTATUS status;
+ struct pvfs_file *f;
+
+ f = pvfs_find_fd(pvfs, req, io->in.file.ntvfs);
+ if (!f) {
+ return NT_STATUS_FILE_CLOSED;
+ }
+
+ /* its only valid for directories */
+ if (f->handle->fd != -1) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* if there's no search started on the dir handle, it's like a search_first */
+ search = f->search;
+ if (!search) {
+ return pvfs_search_first_smb2(ntvfs, req, io, search_private, callback);
+ }
+
+ if (io->in.continue_flags & SMB2_CONTINUE_FLAG_RESTART) {
+ search->current_index = 0;
+ }
+
+ if (io->in.continue_flags & SMB2_CONTINUE_FLAG_SINGLE) {
+ max_count = 1;
+ } else {
+ max_count = UINT16_MAX;
+ }
+
+ status = pvfs_search_fill(pvfs, req, max_count, search, io->data_level,
+ &reply_count, search_private, callback);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ /* not matching any entries is an error */
+ if (reply_count == 0) {
+ return STATUS_NO_MORE_FILES;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ list files in a directory matching a wildcard pattern
+*/
+NTSTATUS pvfs_search_first(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_search_first *io,
+ void *search_private,
+ bool (*callback)(void *, const union smb_search_data *))
+{
+ switch (io->generic.level) {
+ case RAW_SEARCH_SEARCH:
+ case RAW_SEARCH_FFIRST:
+ case RAW_SEARCH_FUNIQUE:
+ return pvfs_search_first_old(ntvfs, req, io, search_private, callback);
+
+ case RAW_SEARCH_TRANS2:
+ return pvfs_search_first_trans2(ntvfs, req, io, search_private, callback);
+
+ case RAW_SEARCH_SMB2:
+ return pvfs_search_first_smb2(ntvfs, req, &io->smb2, search_private, callback);
+ }
+
+ return NT_STATUS_INVALID_LEVEL;
+}
+
+/* continue a search */
+NTSTATUS pvfs_search_next(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_search_next *io,
+ void *search_private,
+ bool (*callback)(void *, const union smb_search_data *))
+{
+ switch (io->generic.level) {
+ case RAW_SEARCH_SEARCH:
+ case RAW_SEARCH_FFIRST:
+ return pvfs_search_next_old(ntvfs, req, io, search_private, callback);
+
+ case RAW_SEARCH_FUNIQUE:
+ return NT_STATUS_INVALID_LEVEL;
+
+ case RAW_SEARCH_TRANS2:
+ return pvfs_search_next_trans2(ntvfs, req, io, search_private, callback);
+
+ case RAW_SEARCH_SMB2:
+ return pvfs_search_next_smb2(ntvfs, req, &io->smb2, search_private, callback);
+ }
+
+ return NT_STATUS_INVALID_LEVEL;
+}
+
+
+/* close a search */
+NTSTATUS pvfs_search_close(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_search_close *io)
+{
+ struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ void *p;
+ struct pvfs_search_state *search;
+ uint16_t handle = INVALID_SEARCH_HANDLE;
+
+ switch (io->generic.level) {
+ case RAW_FINDCLOSE_GENERIC:
+ return NT_STATUS_INVALID_LEVEL;
+
+ case RAW_FINDCLOSE_FCLOSE:
+ handle = io->fclose.in.id.handle;
+ break;
+
+ case RAW_FINDCLOSE_FINDCLOSE:
+ handle = io->findclose.in.handle;
+ break;
+ }
+
+ p = idr_find(pvfs->search.idtree, handle);
+ if (p == NULL) {
+ /* we didn't find the search handle */
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ search = talloc_get_type(p, struct pvfs_search_state);
+
+ talloc_free(search);
+
+ return NT_STATUS_OK;
+}
+
diff --git a/source4/ntvfs/posix/pvfs_seek.c b/source4/ntvfs/posix/pvfs_seek.c
new file mode 100644
index 0000000..2cd3410
--- /dev/null
+++ b/source4/ntvfs/posix/pvfs_seek.c
@@ -0,0 +1,65 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ POSIX NTVFS backend - seek
+
+ Copyright (C) Andrew Tridgell 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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 "vfs_posix.h"
+
+/*
+ seek in a file
+*/
+NTSTATUS pvfs_seek(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_seek *io)
+{
+ struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ struct pvfs_file *f;
+ struct pvfs_file_handle *h;
+ NTSTATUS status;
+
+ f = pvfs_find_fd(pvfs, req, io->lseek.in.file.ntvfs);
+ if (!f) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+ h = f->handle;
+
+ status = NT_STATUS_OK;
+
+ switch (io->lseek.in.mode) {
+ case SEEK_MODE_START:
+ h->seek_offset = io->lseek.in.offset;
+ break;
+
+ case SEEK_MODE_CURRENT:
+ h->seek_offset += io->lseek.in.offset;
+ break;
+
+ case SEEK_MODE_END:
+ status = pvfs_resolve_name_fd(pvfs, h->fd, h->name, PVFS_RESOLVE_NO_OPENDB);
+ h->seek_offset = h->name->st.st_size + io->lseek.in.offset;
+ break;
+ }
+
+ io->lseek.out.offset = h->seek_offset;
+
+ return status;
+}
+
diff --git a/source4/ntvfs/posix/pvfs_setfileinfo.c b/source4/ntvfs/posix/pvfs_setfileinfo.c
new file mode 100644
index 0000000..7fd4e35
--- /dev/null
+++ b/source4/ntvfs/posix/pvfs_setfileinfo.c
@@ -0,0 +1,884 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ POSIX NTVFS backend - setfileinfo
+
+ Copyright (C) Andrew Tridgell 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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 "vfs_posix.h"
+#include "system/time.h"
+#include "librpc/gen_ndr/xattr.h"
+
+
+/*
+ determine what access bits are needed for a call
+*/
+static uint32_t pvfs_setfileinfo_access(union smb_setfileinfo *info)
+{
+ uint32_t needed;
+
+ switch (info->generic.level) {
+ case RAW_SFILEINFO_EA_SET:
+ needed = SEC_FILE_WRITE_EA;
+ break;
+
+ case RAW_SFILEINFO_DISPOSITION_INFO:
+ case RAW_SFILEINFO_DISPOSITION_INFORMATION:
+ needed = SEC_STD_DELETE;
+ break;
+
+ case RAW_SFILEINFO_END_OF_FILE_INFO:
+ needed = SEC_FILE_WRITE_DATA;
+ break;
+
+ case RAW_SFILEINFO_POSITION_INFORMATION:
+ needed = 0;
+ break;
+
+ case RAW_SFILEINFO_SEC_DESC:
+ needed = 0;
+ if (info->set_secdesc.in.secinfo_flags & (SECINFO_OWNER|SECINFO_GROUP)) {
+ needed |= SEC_STD_WRITE_OWNER;
+ }
+ if (info->set_secdesc.in.secinfo_flags & SECINFO_DACL) {
+ needed |= SEC_STD_WRITE_DAC;
+ }
+ if (info->set_secdesc.in.secinfo_flags & SECINFO_SACL) {
+ needed |= SEC_FLAG_SYSTEM_SECURITY;
+ }
+ break;
+
+ case RAW_SFILEINFO_RENAME_INFORMATION:
+ case RAW_SFILEINFO_RENAME_INFORMATION_SMB2:
+ needed = SEC_STD_DELETE;
+ break;
+
+ default:
+ needed = SEC_FILE_WRITE_ATTRIBUTE;
+ break;
+ }
+
+ return needed;
+}
+
+/*
+ rename_information level for streams
+*/
+static NTSTATUS pvfs_setfileinfo_rename_stream(struct pvfs_state *pvfs,
+ struct ntvfs_request *req,
+ struct pvfs_filename *name,
+ int fd,
+ DATA_BLOB *odb_locking_key,
+ union smb_setfileinfo *info)
+{
+ NTSTATUS status;
+ struct odb_lock *lck = NULL;
+
+ /* strangely, this gives a sharing violation, not invalid
+ parameter */
+ if (info->rename_information.in.new_name[0] != ':') {
+ return NT_STATUS_SHARING_VIOLATION;
+ }
+
+ status = pvfs_access_check_simple(pvfs, req, name, SEC_FILE_WRITE_ATTRIBUTE);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ lck = odb_lock(req, pvfs->odb_context, odb_locking_key);
+ if (lck == NULL) {
+ DEBUG(0,("Unable to lock opendb for can_stat\n"));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+
+ status = pvfs_stream_rename(pvfs, name, fd,
+ info->rename_information.in.new_name+1,
+ info->rename_information.in.overwrite);
+ return status;
+}
+
+/*
+ rename_information level
+*/
+static NTSTATUS pvfs_setfileinfo_rename(struct pvfs_state *pvfs,
+ struct ntvfs_request *req,
+ struct pvfs_filename *name,
+ int fd,
+ DATA_BLOB *odb_locking_key,
+ union smb_setfileinfo *info)
+{
+ NTSTATUS status;
+ struct pvfs_filename *name2;
+ char *new_name, *p;
+ struct odb_lock *lck = NULL;
+
+ /* renames are only allowed within a directory */
+ if (strchr_m(info->rename_information.in.new_name, '\\') &&
+ (req->ctx->protocol < PROTOCOL_SMB2_02)) {
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ /* handle stream renames specially */
+ if (name->stream_name) {
+ return pvfs_setfileinfo_rename_stream(pvfs, req, name, fd,
+ odb_locking_key, info);
+ }
+
+ /* w2k3 does not appear to allow relative rename. On SMB2, vista sends it sometimes,
+ but I suspect it is just uninitialised memory */
+ if (info->rename_information.in.root_fid != 0 &&
+ (req->ctx->protocol < PROTOCOL_SMB2_02)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* construct the fully qualified windows name for the new file name */
+ if (req->ctx->protocol >= PROTOCOL_SMB2_02) {
+ /* SMB2 sends the full path of the new name */
+ new_name = talloc_asprintf(req, "\\%s", info->rename_information.in.new_name);
+ } else {
+ new_name = talloc_strdup(req, name->original_name);
+ if (new_name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ p = strrchr_m(new_name, '\\');
+ if (p == NULL) {
+ return NT_STATUS_OBJECT_NAME_INVALID;
+ } else {
+ *p = 0;
+ }
+
+ new_name = talloc_asprintf(req, "%s\\%s", new_name,
+ info->rename_information.in.new_name);
+ }
+ if (new_name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* resolve the new name */
+ status = pvfs_resolve_name(pvfs, req, new_name, 0, &name2);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* if the destination exists, then check the rename is allowed */
+ if (name2->exists) {
+ if (strcmp(name2->full_name, name->full_name) == 0) {
+ /* rename to same name is null-op */
+ return NT_STATUS_OK;
+ }
+
+ if (!info->rename_information.in.overwrite) {
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+
+ 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;
+ }
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+
+ status = pvfs_access_check_parent(pvfs, req, name2, SEC_DIR_ADD_FILE);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ lck = odb_lock(req, pvfs->odb_context, odb_locking_key);
+ if (lck == NULL) {
+ DEBUG(0,("Unable to lock opendb for can_stat\n"));
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ status = pvfs_do_rename(pvfs, lck, name, name2->full_name);
+ talloc_free(lck);
+ NT_STATUS_NOT_OK_RETURN(status);
+ if (NT_STATUS_IS_OK(status)) {
+ name->full_name = talloc_steal(name, name2->full_name);
+ name->original_name = talloc_steal(name, name2->original_name);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ add a single DOS EA
+*/
+NTSTATUS pvfs_setfileinfo_ea_set(struct pvfs_state *pvfs,
+ struct pvfs_filename *name,
+ int fd, uint16_t num_eas,
+ struct ea_struct *eas)
+{
+ struct xattr_DosEAs *ealist;
+ int i, j;
+ NTSTATUS status;
+
+ if (num_eas == 0) {
+ return NT_STATUS_OK;
+ }
+
+ if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ ealist = talloc(name, struct xattr_DosEAs);
+
+ /* load the current list */
+ status = pvfs_doseas_load(pvfs, name, fd, ealist);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ for (j=0;j<num_eas;j++) {
+ struct ea_struct *ea = &eas[j];
+ /* see if its already there */
+ for (i=0;i<ealist->num_eas;i++) {
+ if (strcasecmp_m(ealist->eas[i].name, ea->name.s) == 0) {
+ ealist->eas[i].value = ea->value;
+ break;
+ }
+ }
+
+ if (i==ealist->num_eas) {
+ /* add it */
+ ealist->eas = talloc_realloc(ealist, ealist->eas,
+ struct xattr_EA,
+ ealist->num_eas+1);
+ if (ealist->eas == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ ealist->eas[i].name = ea->name.s;
+ ealist->eas[i].value = ea->value;
+ ealist->num_eas++;
+ }
+ }
+
+ /* pull out any null EAs */
+ for (i=0;i<ealist->num_eas;i++) {
+ if (ealist->eas[i].value.length == 0) {
+ memmove(&ealist->eas[i],
+ &ealist->eas[i+1],
+ (ealist->num_eas-(i+1)) * sizeof(ealist->eas[i]));
+ ealist->num_eas--;
+ i--;
+ }
+ }
+
+ status = pvfs_doseas_save(pvfs, name, fd, ealist);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ notify_trigger(pvfs->notify_context,
+ NOTIFY_ACTION_MODIFIED,
+ FILE_NOTIFY_CHANGE_EA,
+ name->full_name);
+
+ name->dos.ea_size = 4;
+ for (i=0;i<ealist->num_eas;i++) {
+ name->dos.ea_size += 4 + strlen(ealist->eas[i].name)+1 +
+ ealist->eas[i].value.length;
+ }
+
+ /* update the ea_size attrib */
+ return pvfs_dosattrib_save(pvfs, name, fd);
+}
+
+/*
+ set info on a open file
+*/
+NTSTATUS pvfs_setfileinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_setfileinfo *info)
+{
+ struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ struct pvfs_file *f;
+ struct pvfs_file_handle *h;
+ struct pvfs_filename newstats;
+ NTSTATUS status;
+ uint32_t access_needed;
+ uint32_t change_mask = 0;
+
+ f = pvfs_find_fd(pvfs, req, info->generic.in.file.ntvfs);
+ if (!f) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ h = f->handle;
+
+ access_needed = pvfs_setfileinfo_access(info);
+ if ((f->access_mask & access_needed) != access_needed) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ /* update the file information */
+ status = pvfs_resolve_name_handle(pvfs, h);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* we take a copy of the current file stats, then update
+ newstats in each of the elements below. At the end we
+ compare, and make any changes needed */
+ newstats = *h->name;
+
+ switch (info->generic.level) {
+ case RAW_SFILEINFO_SETATTR:
+ if (!null_time(info->setattr.in.write_time)) {
+ unix_to_nt_time(&newstats.dos.write_time, info->setattr.in.write_time);
+ }
+ if (info->setattr.in.attrib != FILE_ATTRIBUTE_NORMAL) {
+ newstats.dos.attrib = info->setattr.in.attrib;
+ }
+ break;
+
+ case RAW_SFILEINFO_SETATTRE:
+ case RAW_SFILEINFO_STANDARD:
+ if (!null_time(info->setattre.in.create_time)) {
+ unix_to_nt_time(&newstats.dos.create_time, info->setattre.in.create_time);
+ }
+ if (!null_time(info->setattre.in.access_time)) {
+ unix_to_nt_time(&newstats.dos.access_time, info->setattre.in.access_time);
+ }
+ if (!null_time(info->setattre.in.write_time)) {
+ unix_to_nt_time(&newstats.dos.write_time, info->setattre.in.write_time);
+ }
+ break;
+
+ case RAW_SFILEINFO_EA_SET:
+ return pvfs_setfileinfo_ea_set(pvfs, h->name, h->fd,
+ info->ea_set.in.num_eas,
+ info->ea_set.in.eas);
+
+ case RAW_SFILEINFO_BASIC_INFO:
+ case RAW_SFILEINFO_BASIC_INFORMATION:
+ if (!null_nttime(info->basic_info.in.create_time)) {
+ newstats.dos.create_time = info->basic_info.in.create_time;
+ }
+ if (!null_nttime(info->basic_info.in.access_time)) {
+ newstats.dos.access_time = info->basic_info.in.access_time;
+ }
+ if (!null_nttime(info->basic_info.in.write_time)) {
+ newstats.dos.write_time = info->basic_info.in.write_time;
+ }
+ if (!null_nttime(info->basic_info.in.change_time)) {
+ newstats.dos.change_time = info->basic_info.in.change_time;
+ }
+ if (info->basic_info.in.attrib != 0) {
+ newstats.dos.attrib = info->basic_info.in.attrib;
+ }
+ break;
+
+ case RAW_SFILEINFO_DISPOSITION_INFO:
+ case RAW_SFILEINFO_DISPOSITION_INFORMATION:
+ return pvfs_set_delete_on_close(pvfs, req, f,
+ info->disposition_info.in.delete_on_close);
+
+ 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;
+ }
+ newstats.dos.alloc_size = pvfs_round_alloc_size(pvfs,
+ newstats.dos.alloc_size);
+ break;
+
+ 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;
+
+ case RAW_SFILEINFO_POSITION_INFORMATION:
+ h->position = info->position_information.in.position;
+ break;
+
+ case RAW_SFILEINFO_FULL_EA_INFORMATION:
+ return pvfs_setfileinfo_ea_set(pvfs, h->name, h->fd,
+ info->full_ea_information.in.eas.num_eas,
+ info->full_ea_information.in.eas.eas);
+
+ case RAW_SFILEINFO_MODE_INFORMATION:
+ /* this one is a puzzle */
+ if (info->mode_information.in.mode != 0 &&
+ info->mode_information.in.mode != 2 &&
+ info->mode_information.in.mode != 4 &&
+ info->mode_information.in.mode != 6) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ h->mode = info->mode_information.in.mode;
+ break;
+
+ case RAW_SFILEINFO_RENAME_INFORMATION:
+ case RAW_SFILEINFO_RENAME_INFORMATION_SMB2:
+ return pvfs_setfileinfo_rename(pvfs, req, h->name, f->handle->fd,
+ &h->odb_locking_key,
+ info);
+
+ case RAW_SFILEINFO_SEC_DESC:
+ notify_trigger(pvfs->notify_context,
+ NOTIFY_ACTION_MODIFIED,
+ FILE_NOTIFY_CHANGE_SECURITY,
+ h->name->full_name);
+ return pvfs_acl_set(pvfs, req, h->name, h->fd, f->access_mask, info);
+
+ default:
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ /* possibly change the file size */
+ if (newstats.st.st_size != h->name->st.st_size) {
+ if (h->name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) {
+ return NT_STATUS_FILE_IS_A_DIRECTORY;
+ }
+ if (h->name->stream_name) {
+ status = pvfs_stream_truncate(pvfs, h->name, h->fd, newstats.st.st_size);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ change_mask |= FILE_NOTIFY_CHANGE_STREAM_SIZE;
+ } else {
+ int ret;
+ if (f->access_mask &
+ (SEC_FILE_WRITE_DATA|SEC_FILE_APPEND_DATA)) {
+ ret = ftruncate(h->fd, newstats.st.st_size);
+ } else {
+ ret = truncate(h->name->full_name, newstats.st.st_size);
+ }
+ if (ret == -1) {
+ return pvfs_map_errno(pvfs, errno);
+ }
+ change_mask |= FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_ATTRIBUTES;
+ }
+ }
+
+ /* possibly change the file timestamps */
+ if (newstats.dos.create_time != h->name->dos.create_time) {
+ change_mask |= FILE_NOTIFY_CHANGE_CREATION;
+ }
+ if (newstats.dos.access_time != h->name->dos.access_time) {
+ change_mask |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
+ }
+ if (newstats.dos.write_time != h->name->dos.write_time) {
+ change_mask |= FILE_NOTIFY_CHANGE_LAST_WRITE;
+ }
+ if ((change_mask & FILE_NOTIFY_CHANGE_LAST_ACCESS) ||
+ (change_mask & FILE_NOTIFY_CHANGE_LAST_WRITE)) {
+ struct timeval tv[2];
+
+ nttime_to_timeval(&tv[0], newstats.dos.access_time);
+ nttime_to_timeval(&tv[1], newstats.dos.write_time);
+
+ if (!timeval_is_zero(&tv[0]) || !timeval_is_zero(&tv[1])) {
+ if (utimes(h->name->full_name, tv) == -1) {
+ DEBUG(0,("pvfs_setfileinfo: utimes() failed '%s' - %s\n",
+ h->name->full_name, strerror(errno)));
+ return pvfs_map_errno(pvfs, errno);
+ }
+ }
+ }
+ if (change_mask & FILE_NOTIFY_CHANGE_LAST_WRITE) {
+ struct odb_lock *lck;
+
+ lck = odb_lock(req, h->pvfs->odb_context, &h->odb_locking_key);
+ if (lck == NULL) {
+ DEBUG(0,("Unable to lock opendb for write time update\n"));
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+
+ status = odb_set_write_time(lck, newstats.dos.write_time, true);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("Unable to update write time: %s\n",
+ nt_errstr(status)));
+ talloc_free(lck);
+ return status;
+ }
+
+ talloc_free(lck);
+
+ h->write_time.update_forced = true;
+ h->write_time.update_on_close = false;
+ talloc_free(h->write_time.update_event);
+ h->write_time.update_event = NULL;
+ }
+
+ /* possibly change the attribute */
+ if (newstats.dos.attrib != h->name->dos.attrib) {
+ mode_t mode;
+ if ((newstats.dos.attrib & FILE_ATTRIBUTE_DIRECTORY) &&
+ !(h->name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ mode = pvfs_fileperms(pvfs, newstats.dos.attrib);
+ if (!(h->name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) {
+ if (pvfs_sys_fchmod(pvfs, h->fd, mode, h->name->allow_override) == -1) {
+ return pvfs_map_errno(pvfs, errno);
+ }
+ }
+ change_mask |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
+ }
+
+ *h->name = newstats;
+
+ notify_trigger(pvfs->notify_context,
+ NOTIFY_ACTION_MODIFIED,
+ change_mask,
+ h->name->full_name);
+
+ 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 = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ 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
+*/
+NTSTATUS pvfs_setpathinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_setfileinfo *info)
+{
+ struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ struct pvfs_filename *name;
+ struct pvfs_filename newstats;
+ NTSTATUS status;
+ uint32_t access_needed;
+ uint32_t change_mask = 0;
+ struct odb_lock *lck = NULL;
+ DATA_BLOB odb_locking_key;
+
+ /* resolve the cifs name to a posix name */
+ status = pvfs_resolve_name(pvfs, req, info->generic.in.file.path,
+ PVFS_RESOLVE_STREAMS, &name);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (!name->exists) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ access_needed = pvfs_setfileinfo_access(info);
+ status = pvfs_access_check_simple(pvfs, req, name, access_needed);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* we take a copy of the current file stats, then update
+ newstats in each of the elements below. At the end we
+ compare, and make any changes needed */
+ newstats = *name;
+
+ switch (info->generic.level) {
+ case RAW_SFILEINFO_SETATTR:
+ if (!null_time(info->setattr.in.write_time)) {
+ unix_to_nt_time(&newstats.dos.write_time, info->setattr.in.write_time);
+ }
+ if (info->setattr.in.attrib == 0) {
+ newstats.dos.attrib = FILE_ATTRIBUTE_NORMAL;
+ } else if (info->setattr.in.attrib != FILE_ATTRIBUTE_NORMAL) {
+ newstats.dos.attrib = info->setattr.in.attrib;
+ }
+ break;
+
+ case RAW_SFILEINFO_SETATTRE:
+ case RAW_SFILEINFO_STANDARD:
+ if (!null_time(info->setattre.in.create_time)) {
+ unix_to_nt_time(&newstats.dos.create_time, info->setattre.in.create_time);
+ }
+ if (!null_time(info->setattre.in.access_time)) {
+ unix_to_nt_time(&newstats.dos.access_time, info->setattre.in.access_time);
+ }
+ if (!null_time(info->setattre.in.write_time)) {
+ unix_to_nt_time(&newstats.dos.write_time, info->setattre.in.write_time);
+ }
+ break;
+
+ case RAW_SFILEINFO_EA_SET:
+ return pvfs_setfileinfo_ea_set(pvfs, name, -1,
+ info->ea_set.in.num_eas,
+ info->ea_set.in.eas);
+
+ case RAW_SFILEINFO_BASIC_INFO:
+ case RAW_SFILEINFO_BASIC_INFORMATION:
+ if (!null_nttime(info->basic_info.in.create_time)) {
+ newstats.dos.create_time = info->basic_info.in.create_time;
+ }
+ if (!null_nttime(info->basic_info.in.access_time)) {
+ newstats.dos.access_time = info->basic_info.in.access_time;
+ }
+ if (!null_nttime(info->basic_info.in.write_time)) {
+ newstats.dos.write_time = info->basic_info.in.write_time;
+ }
+ if (!null_nttime(info->basic_info.in.change_time)) {
+ newstats.dos.change_time = info->basic_info.in.change_time;
+ }
+ if (info->basic_info.in.attrib != 0) {
+ newstats.dos.attrib = info->basic_info.in.attrib;
+ }
+ break;
+
+ 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 */
+ break;
+ }
+ 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;
+ }
+ newstats.dos.alloc_size = pvfs_round_alloc_size(pvfs,
+ newstats.dos.alloc_size);
+ break;
+
+ 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;
+
+ case RAW_SFILEINFO_MODE_INFORMATION:
+ if (info->mode_information.in.mode != 0 &&
+ info->mode_information.in.mode != 2 &&
+ info->mode_information.in.mode != 4 &&
+ info->mode_information.in.mode != 6) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ return NT_STATUS_OK;
+
+ case RAW_SFILEINFO_RENAME_INFORMATION:
+ case RAW_SFILEINFO_RENAME_INFORMATION_SMB2:
+ status = pvfs_locking_key(name, name, &odb_locking_key);
+ NT_STATUS_NOT_OK_RETURN(status);
+ status = pvfs_setfileinfo_rename(pvfs, req, name, -1,
+ &odb_locking_key, info);
+ NT_STATUS_NOT_OK_RETURN(status);
+ return NT_STATUS_OK;
+
+ case RAW_SFILEINFO_DISPOSITION_INFO:
+ case RAW_SFILEINFO_DISPOSITION_INFORMATION:
+ case RAW_SFILEINFO_POSITION_INFORMATION:
+ return NT_STATUS_OK;
+
+ default:
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ /* possibly change the file size */
+ if (newstats.st.st_size != name->st.st_size) {
+ if (name->stream_name) {
+ status = pvfs_stream_truncate(pvfs, name, -1, newstats.st.st_size);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ } else if (truncate(name->full_name, newstats.st.st_size) == -1) {
+ return pvfs_map_errno(pvfs, errno);
+ }
+ change_mask |= FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_ATTRIBUTES;
+ }
+
+ /* possibly change the file timestamps */
+ if (newstats.dos.create_time != name->dos.create_time) {
+ change_mask |= FILE_NOTIFY_CHANGE_CREATION;
+ }
+ if (newstats.dos.access_time != name->dos.access_time) {
+ change_mask |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
+ }
+ if (newstats.dos.write_time != name->dos.write_time) {
+ change_mask |= FILE_NOTIFY_CHANGE_LAST_WRITE;
+ }
+ if ((change_mask & FILE_NOTIFY_CHANGE_LAST_ACCESS) ||
+ (change_mask & FILE_NOTIFY_CHANGE_LAST_WRITE)) {
+ struct timeval tv[2];
+
+ nttime_to_timeval(&tv[0], newstats.dos.access_time);
+ nttime_to_timeval(&tv[1], newstats.dos.write_time);
+
+ if (!timeval_is_zero(&tv[0]) || !timeval_is_zero(&tv[1])) {
+ if (utimes(name->full_name, tv) == -1) {
+ DEBUG(0,("pvfs_setpathinfo: utimes() failed '%s' - %s\n",
+ name->full_name, strerror(errno)));
+ return pvfs_map_errno(pvfs, errno);
+ }
+ }
+ }
+ if (change_mask & FILE_NOTIFY_CHANGE_LAST_WRITE) {
+ if (lck == NULL) {
+ DATA_BLOB lkey;
+ status = pvfs_locking_key(name, name, &lkey);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ lck = odb_lock(req, pvfs->odb_context, &lkey);
+ data_blob_free(&lkey);
+ if (lck == NULL) {
+ DEBUG(0,("Unable to lock opendb for write time update\n"));
+ return NT_STATUS_INTERNAL_ERROR;
+ }
+ }
+
+ status = odb_set_write_time(lck, newstats.dos.write_time, true);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
+ /* it could be that nobody has opened the file */
+ } else if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("Unable to update write time: %s\n",
+ nt_errstr(status)));
+ return status;
+ }
+ }
+
+ /* possibly change the attribute */
+ newstats.dos.attrib |= (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY);
+ if (newstats.dos.attrib != name->dos.attrib) {
+ mode_t mode = pvfs_fileperms(pvfs, newstats.dos.attrib);
+ if (pvfs_sys_chmod(pvfs, name->full_name, mode, name->allow_override) == -1) {
+ return pvfs_map_errno(pvfs, errno);
+ }
+ change_mask |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
+ }
+
+ *name = newstats;
+
+ if (change_mask != 0) {
+ notify_trigger(pvfs->notify_context,
+ NOTIFY_ACTION_MODIFIED,
+ change_mask,
+ name->full_name);
+ }
+
+ return pvfs_dosattrib_save(pvfs, name, -1);
+}
+
diff --git a/source4/ntvfs/posix/pvfs_shortname.c b/source4/ntvfs/posix/pvfs_shortname.c
new file mode 100644
index 0000000..9e3cf5f
--- /dev/null
+++ b/source4/ntvfs/posix/pvfs_shortname.c
@@ -0,0 +1,699 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ POSIX NTVFS backend - 8.3 name routines
+
+ Copyright (C) Andrew Tridgell 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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 "system/locale.h"
+#include "vfs_posix.h"
+#include "param/param.h"
+
+#undef strcasecmp
+
+/*
+ this mangling scheme uses the following format
+
+ Annnn~n.AAA
+
+ where nnnnn is a base 36 hash, and A represents characters from the original string
+
+ The hash is taken of the leading part of the long filename, in uppercase
+
+ for simplicity, we only allow ascii characters in 8.3 names
+*/
+
+/*
+ ===============================================================================
+ NOTE NOTE NOTE!!!
+
+ This file deliberately uses non-multibyte string functions in many places. This
+ is *not* a mistake. This code is multi-byte safe, but it gets this property
+ through some very subtle knowledge of the way multi-byte strings are encoded
+ and the fact that this mangling algorithm only supports ascii characters in
+ 8.3 names.
+
+ please don't convert this file to use the *_m() functions!!
+ ===============================================================================
+*/
+
+
+#if 1
+#define M_DEBUG(level, x) DEBUG(level, x)
+#else
+#define M_DEBUG(level, x)
+#endif
+
+/* these flags are used to mark characters in as having particular
+ properties */
+#define FLAG_BASECHAR 1
+#define FLAG_ASCII 2
+#define FLAG_ILLEGAL 4
+#define FLAG_WILDCARD 8
+
+/* the "possible" flags are used as a fast way to find possible DOS
+ reserved filenames */
+#define FLAG_POSSIBLE1 16
+#define FLAG_POSSIBLE2 32
+#define FLAG_POSSIBLE3 64
+#define FLAG_POSSIBLE4 128
+
+#define DEFAULT_MANGLE_PREFIX 4
+
+#define MANGLE_BASECHARS "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+
+#define FLAG_CHECK(c, flag) (ctx->char_flags[(uint8_t)(c)] & (flag))
+
+static const char *reserved_names[] =
+{ "AUX", "CON",
+ "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9",
+ "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9",
+ "NUL", "PRN", NULL };
+
+
+struct pvfs_mangle_context {
+ uint8_t char_flags[256];
+ /*
+ this determines how many characters are used from the original
+ filename in the 8.3 mangled name. A larger value leads to a weaker
+ hash and more collisions. The largest possible value is 6.
+ */
+ int mangle_prefix;
+ uint32_t mangle_modulus;
+
+ /* we will use a very simple direct mapped prefix cache. The big
+ advantage of this cache structure is speed and low memory usage
+
+ The cache is indexed by the low-order bits of the hash, and confirmed by
+ hashing the resulting cache entry to match the known hash
+ */
+ uint32_t cache_size;
+ char **prefix_cache;
+ uint32_t *prefix_cache_hashes;
+
+ /* this is used to reverse the base 36 mapping */
+ unsigned char base_reverse[256];
+};
+
+
+/*
+ hash a string of the specified length. The string does not need to be
+ null terminated
+
+ this hash needs to be fast with a low collision rate (what hash doesn't?)
+*/
+static uint32_t mangle_hash(struct pvfs_mangle_context *ctx,
+ const char *key, size_t length)
+{
+ return pvfs_name_hash(key, length) % ctx->mangle_modulus;
+}
+
+/*
+ insert an entry into the prefix cache. The string might not be null
+ terminated */
+static void cache_insert(struct pvfs_mangle_context *ctx,
+ const char *prefix, int length, uint32_t hash)
+{
+ int i = hash % ctx->cache_size;
+
+ if (ctx->prefix_cache[i]) {
+ talloc_free(ctx->prefix_cache[i]);
+ }
+
+ ctx->prefix_cache[i] = talloc_strndup(ctx->prefix_cache, prefix, length);
+ ctx->prefix_cache_hashes[i] = hash;
+}
+
+/*
+ lookup an entry in the prefix cache. Return NULL if not found.
+*/
+static const char *cache_lookup(struct pvfs_mangle_context *ctx, uint32_t hash)
+{
+ int i = hash % ctx->cache_size;
+
+
+ if (!ctx->prefix_cache[i] || hash != ctx->prefix_cache_hashes[i]) {
+ return NULL;
+ }
+
+ /* yep, it matched */
+ return ctx->prefix_cache[i];
+}
+
+
+/*
+ determine if a string is possibly in a mangled format, ignoring
+ case
+
+ In this algorithm, mangled names use only pure ascii characters (no
+ multi-byte) so we can avoid doing a UCS2 conversion
+ */
+static bool is_mangled_component(struct pvfs_mangle_context *ctx,
+ const char *name, size_t len)
+{
+ unsigned int i;
+
+ M_DEBUG(10,("is_mangled_component %s (len %u) ?\n", name, (unsigned int)len));
+
+ /* check the length */
+ if (len > 12 || len < 8)
+ return false;
+
+ /* the best distinguishing characteristic is the ~ */
+ if (name[6] != '~')
+ return false;
+
+ /* check extension */
+ if (len > 8) {
+ if (name[8] != '.')
+ return false;
+ for (i=9; name[i] && i < len; i++) {
+ if (! FLAG_CHECK(name[i], FLAG_ASCII)) {
+ return false;
+ }
+ }
+ }
+
+ /* check lead characters */
+ for (i=0;i<ctx->mangle_prefix;i++) {
+ if (! FLAG_CHECK(name[i], FLAG_ASCII)) {
+ return false;
+ }
+ }
+
+ /* check rest of hash */
+ if (! FLAG_CHECK(name[7], FLAG_BASECHAR)) {
+ return false;
+ }
+ for (i=ctx->mangle_prefix;i<6;i++) {
+ if (! FLAG_CHECK(name[i], FLAG_BASECHAR)) {
+ return false;
+ }
+ }
+
+ M_DEBUG(10,("is_mangled_component %s (len %u) -> yes\n", name, (unsigned int)len));
+
+ return true;
+}
+
+
+
+/*
+ determine if a string is possibly in a mangled format, ignoring
+ case
+
+ In this algorithm, mangled names use only pure ascii characters (no
+ multi-byte) so we can avoid doing a UCS2 conversion
+
+ NOTE! This interface must be able to handle a path with unix
+ directory separators. It should return true if any component is
+ mangled
+ */
+static bool is_mangled(struct pvfs_mangle_context *ctx, const char *name)
+{
+ const char *p;
+ const char *s;
+
+ M_DEBUG(10,("is_mangled %s ?\n", name));
+
+ for (s=name; (p=strchr(s, '/')); s=p+1) {
+ if (is_mangled_component(ctx, s, PTR_DIFF(p, s))) {
+ return true;
+ }
+ }
+
+ /* and the last part ... */
+ return is_mangled_component(ctx, s, strlen(s));
+}
+
+
+/*
+ see if a filename is an allowable 8.3 name.
+
+ we are only going to allow ascii characters in 8.3 names, as this
+ simplifies things greatly (it means that we know the string won't
+ get larger when converted from UNIX to DOS formats)
+*/
+static bool is_8_3(struct pvfs_mangle_context *ctx,
+ const char *name, bool check_case, bool allow_wildcards)
+{
+ int len, i;
+ char *dot_p;
+
+ /* as a special case, the names '.' and '..' are allowable 8.3 names */
+ if (name[0] == '.') {
+ if (!name[1] || (name[1] == '.' && !name[2])) {
+ return true;
+ }
+ }
+
+ /* the simplest test is on the overall length of the
+ filename. Note that we deliberately use the ascii string
+ length (not the multi-byte one) as it is faster, and gives us
+ the result we need in this case. Using strlen_m would not
+ only be slower, it would be incorrect */
+ len = strlen(name);
+ if (len > 12)
+ return false;
+
+ /* find the '.'. Note that once again we use the non-multibyte
+ function */
+ dot_p = strchr(name, '.');
+
+ if (!dot_p) {
+ /* if the name doesn't contain a '.' then its length
+ must be less than 8 */
+ if (len > 8) {
+ return false;
+ }
+ } else {
+ int prefix_len, suffix_len;
+
+ /* if it does contain a dot then the prefix must be <=
+ 8 and the suffix <= 3 in length */
+ prefix_len = PTR_DIFF(dot_p, name);
+ suffix_len = len - (prefix_len+1);
+
+ if (prefix_len > 8 || suffix_len > 3 || suffix_len == 0) {
+ return false;
+ }
+
+ /* a 8.3 name cannot contain more than 1 '.' */
+ if (strchr(dot_p+1, '.')) {
+ return false;
+ }
+ }
+
+ /* the length are all OK. Now check to see if the characters themselves are OK */
+ for (i=0; name[i]; i++) {
+ /* note that we may allow wildcard petterns! */
+ if (!FLAG_CHECK(name[i], FLAG_ASCII|(allow_wildcards ? FLAG_WILDCARD : 0)) &&
+ name[i] != '.') {
+ return false;
+ }
+ }
+
+ /* it is a good 8.3 name */
+ return true;
+}
+
+
+/*
+ try to find a 8.3 name in the cache, and if found then
+ return the original long name.
+*/
+static char *check_cache(struct pvfs_mangle_context *ctx,
+ TALLOC_CTX *mem_ctx, const char *name)
+{
+ uint32_t hash, multiplier;
+ unsigned int i;
+ const char *prefix;
+ char extension[4];
+
+ /* make sure that this is a mangled name from this cache */
+ if (!is_mangled(ctx, name)) {
+ M_DEBUG(10,("check_cache: %s -> not mangled\n", name));
+ return NULL;
+ }
+
+ /* we need to extract the hash from the 8.3 name */
+ hash = ctx->base_reverse[(unsigned char)name[7]];
+ for (multiplier=36, i=5;i>=ctx->mangle_prefix;i--) {
+ uint32_t v = ctx->base_reverse[(unsigned char)name[i]];
+ hash += multiplier * v;
+ multiplier *= 36;
+ }
+
+ /* now look in the prefix cache for that hash */
+ prefix = cache_lookup(ctx, hash);
+ if (!prefix) {
+ M_DEBUG(10,("check_cache: %s -> %08X -> not found\n", name, hash));
+ return NULL;
+ }
+
+ /* we found it - construct the full name */
+ if (name[8] == '.') {
+ strncpy(extension, name+9, 3);
+ extension[3] = 0;
+ } else {
+ extension[0] = 0;
+ }
+
+ if (extension[0]) {
+ return talloc_asprintf(mem_ctx, "%s.%s", prefix, extension);
+ }
+
+ return talloc_strdup(mem_ctx, prefix);
+}
+
+
+/*
+ look for a DOS reserved name
+*/
+static bool is_reserved_name(struct pvfs_mangle_context *ctx, const char *name)
+{
+ if (FLAG_CHECK(name[0], FLAG_POSSIBLE1) &&
+ FLAG_CHECK(name[1], FLAG_POSSIBLE2) &&
+ FLAG_CHECK(name[2], FLAG_POSSIBLE3) &&
+ FLAG_CHECK(name[3], FLAG_POSSIBLE4)) {
+ /* a likely match, scan the lot */
+ int i;
+ for (i=0; reserved_names[i]; i++) {
+ if (strcasecmp(name, reserved_names[i]) == 0) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+
+/*
+ See if a filename is a legal long filename.
+ A filename ending in a '.' is not legal unless it's "." or "..". JRA.
+*/
+static bool is_legal_name(struct pvfs_mangle_context *ctx, const char *name)
+{
+ while (*name) {
+ size_t c_size;
+ codepoint_t c = next_codepoint(name, &c_size);
+ if (c == INVALID_CODEPOINT) {
+ return false;
+ }
+ /* all high chars are OK */
+ if (c >= 128) {
+ name += c_size;
+ continue;
+ }
+ if (FLAG_CHECK(c, FLAG_ILLEGAL)) {
+ return false;
+ }
+ name += c_size;
+ }
+
+ return true;
+}
+
+/*
+ the main forward mapping function, which converts a long filename to
+ a 8.3 name
+
+ if need83 is not set then we only do the mangling if the name is illegal
+ as a long name
+
+ if cache83 is not set then we don't cache the result
+
+ return NULL if we don't need to do any conversion
+*/
+static char *name_map(struct pvfs_mangle_context *ctx,
+ const char *name, bool need83, bool cache83)
+{
+ char *dot_p;
+ char lead_chars[7];
+ char extension[4];
+ unsigned int extension_length, i;
+ unsigned int prefix_len;
+ uint32_t hash, v;
+ char *new_name;
+ const char *basechars = MANGLE_BASECHARS;
+
+ /* reserved names are handled specially */
+ if (!is_reserved_name(ctx, name)) {
+ /* if the name is already a valid 8.3 name then we don't need to
+ do anything */
+ if (is_8_3(ctx, name, false, false)) {
+ return NULL;
+ }
+
+ /* if the caller doesn't strictly need 8.3 then just check for illegal
+ filenames */
+ if (!need83 && is_legal_name(ctx, name)) {
+ return NULL;
+ }
+ }
+
+ /* find the '.' if any */
+ dot_p = strrchr(name, '.');
+
+ if (dot_p) {
+ /* if the extension contains any illegal characters or
+ is too long or zero length then we treat it as part
+ of the prefix */
+ for (i=0; i<4 && dot_p[i+1]; i++) {
+ if (! FLAG_CHECK(dot_p[i+1], FLAG_ASCII)) {
+ dot_p = NULL;
+ break;
+ }
+ }
+ if (i == 0 || i == 4) dot_p = NULL;
+ }
+
+ /* the leading characters in the mangled name is taken from
+ the first characters of the name, if they are ascii otherwise
+ '_' is used
+ */
+ for (i=0;i<ctx->mangle_prefix && name[i];i++) {
+ lead_chars[i] = name[i];
+ if (! FLAG_CHECK(lead_chars[i], FLAG_ASCII)) {
+ lead_chars[i] = '_';
+ }
+ lead_chars[i] = toupper((unsigned char)lead_chars[i]);
+ }
+ for (;i<ctx->mangle_prefix;i++) {
+ lead_chars[i] = '_';
+ }
+
+ /* the prefix is anything up to the first dot */
+ if (dot_p) {
+ prefix_len = PTR_DIFF(dot_p, name);
+ } else {
+ prefix_len = strlen(name);
+ }
+
+ /* the extension of the mangled name is taken from the first 3
+ ascii chars after the dot */
+ extension_length = 0;
+ if (dot_p) {
+ for (i=1; extension_length < 3 && dot_p[i]; i++) {
+ unsigned char c = dot_p[i];
+ if (FLAG_CHECK(c, FLAG_ASCII)) {
+ extension[extension_length++] = toupper(c);
+ }
+ }
+ }
+
+ /* find the hash for this prefix */
+ v = hash = mangle_hash(ctx, name, prefix_len);
+
+ new_name = talloc_array(ctx, char, 13);
+ if (new_name == NULL) {
+ return NULL;
+ }
+
+ /* now form the mangled name. */
+ for (i=0;i<ctx->mangle_prefix;i++) {
+ new_name[i] = lead_chars[i];
+ }
+ new_name[7] = basechars[v % 36];
+ new_name[6] = '~';
+ for (i=5; i>=ctx->mangle_prefix; i--) {
+ v = v / 36;
+ new_name[i] = basechars[v % 36];
+ }
+
+ /* add the extension */
+ if (extension_length) {
+ new_name[8] = '.';
+ memcpy(&new_name[9], extension, extension_length);
+ new_name[9+extension_length] = 0;
+ } else {
+ new_name[8] = 0;
+ }
+
+ if (cache83) {
+ /* put it in the cache */
+ cache_insert(ctx, name, prefix_len, hash);
+ }
+
+ M_DEBUG(10,("name_map: %s -> %08X -> %s (cache=%d)\n",
+ name, hash, new_name, cache83));
+
+ return new_name;
+}
+
+
+/* initialise the flags table
+
+ we allow only a very restricted set of characters as 'ascii' in this
+ mangling backend. This isn't a significant problem as modern clients
+ use the 'long' filenames anyway, and those don't have these
+ restrictions.
+*/
+static void init_tables(struct pvfs_mangle_context *ctx)
+{
+ const char *basechars = MANGLE_BASECHARS;
+ int i;
+ /* the list of reserved dos names - all of these are illegal */
+
+ ZERO_STRUCT(ctx->char_flags);
+
+ for (i=1;i<128;i++) {
+ if ((i >= '0' && i <= '9') ||
+ (i >= 'a' && i <= 'z') ||
+ (i >= 'A' && i <= 'Z')) {
+ ctx->char_flags[i] |= (FLAG_ASCII | FLAG_BASECHAR);
+ }
+ if (strchr("_-$~", i)) {
+ ctx->char_flags[i] |= FLAG_ASCII;
+ }
+
+ if (strchr("*\\/?<>|\":", i)) {
+ ctx->char_flags[i] |= FLAG_ILLEGAL;
+ }
+
+ if (strchr("*?\"<>", i)) {
+ ctx->char_flags[i] |= FLAG_WILDCARD;
+ }
+ }
+
+ ZERO_STRUCT(ctx->base_reverse);
+ for (i=0;i<36;i++) {
+ ctx->base_reverse[(uint8_t)basechars[i]] = i;
+ }
+
+ /* fill in the reserved names flags. These are used as a very
+ fast filter for finding possible DOS reserved filenames */
+ for (i=0; reserved_names[i]; i++) {
+ unsigned char c1, c2, c3, c4;
+
+ c1 = (unsigned char)reserved_names[i][0];
+ c2 = (unsigned char)reserved_names[i][1];
+ c3 = (unsigned char)reserved_names[i][2];
+ c4 = (unsigned char)reserved_names[i][3];
+
+ ctx->char_flags[c1] |= FLAG_POSSIBLE1;
+ ctx->char_flags[c2] |= FLAG_POSSIBLE2;
+ ctx->char_flags[c3] |= FLAG_POSSIBLE3;
+ ctx->char_flags[c4] |= FLAG_POSSIBLE4;
+ ctx->char_flags[tolower(c1)] |= FLAG_POSSIBLE1;
+ ctx->char_flags[tolower(c2)] |= FLAG_POSSIBLE2;
+ ctx->char_flags[tolower(c3)] |= FLAG_POSSIBLE3;
+ ctx->char_flags[tolower(c4)] |= FLAG_POSSIBLE4;
+
+ ctx->char_flags[(unsigned char)'.'] |= FLAG_POSSIBLE4;
+ }
+
+ ctx->mangle_modulus = 1;
+ for (i=0;i<(7-ctx->mangle_prefix);i++) {
+ ctx->mangle_modulus *= 36;
+ }
+}
+
+/*
+ initialise the mangling code
+ */
+NTSTATUS pvfs_mangle_init(struct pvfs_state *pvfs)
+{
+ struct pvfs_mangle_context *ctx;
+
+ ctx = talloc(pvfs, struct pvfs_mangle_context);
+ if (ctx == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ /* by default have a max of 512 entries in the cache. */
+ ctx->cache_size = lpcfg_parm_int(pvfs->ntvfs->ctx->lp_ctx, NULL, "mangle", "cachesize", 512);
+
+ ctx->prefix_cache = talloc_array(ctx, char *, ctx->cache_size);
+ if (ctx->prefix_cache == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ ctx->prefix_cache_hashes = talloc_array(ctx, uint32_t, ctx->cache_size);
+ if (ctx->prefix_cache_hashes == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ memset(ctx->prefix_cache, 0, sizeof(char *) * ctx->cache_size);
+ memset(ctx->prefix_cache_hashes, 0, sizeof(uint32_t) * ctx->cache_size);
+
+ ctx->mangle_prefix = lpcfg_parm_int(pvfs->ntvfs->ctx->lp_ctx, NULL, "mangle", "prefix", -1);
+ if (ctx->mangle_prefix < 0 || ctx->mangle_prefix > 6) {
+ ctx->mangle_prefix = DEFAULT_MANGLE_PREFIX;
+ }
+
+ init_tables(ctx);
+
+ pvfs->mangle_ctx = ctx;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ return the short name for a component of a full name
+*/
+char *pvfs_short_name_component(struct pvfs_state *pvfs, const char *name)
+{
+ return name_map(pvfs->mangle_ctx, name, true, true);
+}
+
+
+/*
+ return the short name for a given entry in a directory
+*/
+const char *pvfs_short_name(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx,
+ struct pvfs_filename *name)
+{
+ char *p = strrchr(name->full_name, '/');
+ char *ret = pvfs_short_name_component(pvfs, p+1);
+ if (ret == NULL) {
+ return p+1;
+ }
+ talloc_steal(mem_ctx, ret);
+ return ret;
+}
+
+/*
+ lookup a mangled name, returning the original long name if present
+ in the cache
+*/
+char *pvfs_mangled_lookup(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx,
+ const char *name)
+{
+ return check_cache(pvfs->mangle_ctx, mem_ctx, name);
+}
+
+
+/*
+ look for a DOS reserved name
+*/
+bool pvfs_is_reserved_name(struct pvfs_state *pvfs, const char *name)
+{
+ return is_reserved_name(pvfs->mangle_ctx, name);
+}
+
+
+/*
+ see if a component of a filename could be a mangled name from our
+ mangling code
+*/
+bool pvfs_is_mangled_component(struct pvfs_state *pvfs, const char *name)
+{
+ return is_mangled_component(pvfs->mangle_ctx, name, strlen(name));
+}
diff --git a/source4/ntvfs/posix/pvfs_streams.c b/source4/ntvfs/posix/pvfs_streams.c
new file mode 100644
index 0000000..9210237
--- /dev/null
+++ b/source4/ntvfs/posix/pvfs_streams.c
@@ -0,0 +1,556 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ POSIX NTVFS backend - alternate data streams
+
+ Copyright (C) Andrew Tridgell 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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 "vfs_posix.h"
+#include "librpc/gen_ndr/xattr.h"
+
+/*
+ normalise a stream name, removing a :$DATA suffix if there is one
+ Note: this returns the existing pointer to the name if the name does
+ not need normalising
+ */
+static const char *stream_name_normalise(TALLOC_CTX *ctx, const char *name)
+{
+ const char *c = strchr_m(name, ':');
+ if (c == NULL || strcasecmp_m(c, ":$DATA") != 0) {
+ return name;
+ }
+ return talloc_strndup(ctx, name, c-name);
+}
+
+/*
+ compare two stream names, taking account of the default $DATA extension
+ */
+static int stream_name_cmp(const char *name1, const char *name2)
+{
+ const char *c1, *c2;
+ int l1, l2, ret;
+ c1 = strchr_m(name1, ':');
+ c2 = strchr_m(name2, ':');
+
+ /* check the first part is the same */
+ l1 = c1?(c1 - name1):strlen(name1);
+ l2 = c2?(c2 - name2):strlen(name2);
+ if (l1 != l2) {
+ return l1 - l2;
+ }
+ ret = strncasecmp_m(name1, name2, l1);
+ if (ret != 0) {
+ return ret;
+ }
+
+ /* the first parts are the same, check the suffix */
+ if (c1 && c2) {
+ return strcasecmp_m(c1, c2);
+ }
+
+ if (c1) {
+ return strcasecmp_m(c1, ":$DATA");
+ }
+ if (c2) {
+ return strcasecmp_m(c2, ":$DATA");
+ }
+
+ /* neither names have a suffix */
+ return 0;
+}
+
+
+/*
+ return the list of file streams for RAW_FILEINFO_STREAM_INFORMATION
+*/
+NTSTATUS pvfs_stream_information(struct pvfs_state *pvfs,
+ TALLOC_CTX *mem_ctx,
+ struct pvfs_filename *name, int fd,
+ struct stream_information *info)
+{
+ struct xattr_DosStreams *streams;
+ int i;
+ NTSTATUS status;
+
+ /* directories don't have streams */
+ if (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) {
+ info->num_streams = 0;
+ info->streams = NULL;
+ return NT_STATUS_OK;
+ }
+
+ streams = talloc(mem_ctx, struct xattr_DosStreams);
+ if (streams == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = pvfs_streams_load(pvfs, name, fd, streams);
+ if (!NT_STATUS_IS_OK(status)) {
+ ZERO_STRUCTP(streams);
+ }
+
+ info->num_streams = streams->num_streams+1;
+ info->streams = talloc_array(mem_ctx, struct stream_struct, info->num_streams);
+ if (!info->streams) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ info->streams[0].size = name->st.st_size;
+ info->streams[0].alloc_size = name->dos.alloc_size;
+ info->streams[0].stream_name.s = talloc_strdup(info->streams, "::$DATA");
+
+ for (i=0;i<streams->num_streams;i++) {
+ info->streams[i+1].size = streams->streams[i].size;
+ info->streams[i+1].alloc_size = streams->streams[i].alloc_size;
+ if (strchr(streams->streams[i].name, ':') == NULL) {
+ info->streams[i+1].stream_name.s = talloc_asprintf(streams->streams,
+ ":%s:$DATA",
+ streams->streams[i].name);
+ } else {
+ info->streams[i+1].stream_name.s = talloc_strdup(streams->streams,
+ streams->streams[i].name);
+ }
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ fill in the stream information for a name
+*/
+NTSTATUS pvfs_stream_info(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd)
+{
+ struct xattr_DosStreams *streams;
+ int i;
+ NTSTATUS status;
+
+ /* the NULL stream always exists */
+ if (name->stream_name == NULL) {
+ name->stream_exists = true;
+ return NT_STATUS_OK;
+ }
+
+ streams = talloc(name, struct xattr_DosStreams);
+ if (streams == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = pvfs_streams_load(pvfs, name, fd, streams);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(streams);
+ return status;
+ }
+
+ for (i=0;i<streams->num_streams;i++) {
+ struct xattr_DosStream *s = &streams->streams[i];
+ if (stream_name_cmp(s->name, name->stream_name) == 0) {
+ name->dos.alloc_size = pvfs_round_alloc_size(pvfs, s->alloc_size);
+ name->st.st_size = s->size;
+ name->stream_exists = true;
+ talloc_free(streams);
+ return NT_STATUS_OK;
+ }
+ }
+
+ talloc_free(streams);
+
+ name->dos.alloc_size = 0;
+ name->st.st_size = 0;
+ name->stream_exists = false;
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ update size information for a stream
+*/
+static NTSTATUS pvfs_stream_update_size(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd,
+ off_t size)
+{
+ struct xattr_DosStreams *streams;
+ int i;
+ NTSTATUS status;
+
+ streams = talloc(name, struct xattr_DosStreams);
+ if (streams == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = pvfs_streams_load(pvfs, name, fd, streams);
+ if (!NT_STATUS_IS_OK(status)) {
+ ZERO_STRUCTP(streams);
+ }
+
+ for (i=0;i<streams->num_streams;i++) {
+ struct xattr_DosStream *s = &streams->streams[i];
+ if (stream_name_cmp(s->name, name->stream_name) == 0) {
+ s->size = size;
+ s->alloc_size = pvfs_round_alloc_size(pvfs, size);
+ break;
+ }
+ }
+
+ if (i == streams->num_streams) {
+ struct xattr_DosStream *s;
+ streams->streams = talloc_realloc(streams, streams->streams,
+ struct xattr_DosStream,
+ streams->num_streams+1);
+ if (streams->streams == NULL) {
+ talloc_free(streams);
+ return NT_STATUS_NO_MEMORY;
+ }
+ streams->num_streams++;
+ s = &streams->streams[i];
+
+ s->flags = XATTR_STREAM_FLAG_INTERNAL;
+ s->size = size;
+ s->alloc_size = pvfs_round_alloc_size(pvfs, size);
+ s->name = stream_name_normalise(streams, name->stream_name);
+ if (s->name == NULL) {
+ talloc_free(streams);
+ return NT_STATUS_NO_MEMORY;
+ }
+ }
+
+ status = pvfs_streams_save(pvfs, name, fd, streams);
+ talloc_free(streams);
+
+ return status;
+}
+
+
+/*
+ rename a stream
+*/
+NTSTATUS pvfs_stream_rename(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd,
+ const char *new_name, bool overwrite)
+{
+ struct xattr_DosStreams *streams;
+ int i, found_old, found_new;
+ NTSTATUS status;
+
+ streams = talloc(name, struct xattr_DosStreams);
+ if (streams == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ new_name = stream_name_normalise(streams, new_name);
+ if (new_name == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = pvfs_streams_load(pvfs, name, fd, streams);
+ if (!NT_STATUS_IS_OK(status)) {
+ ZERO_STRUCTP(streams);
+ }
+
+ /* the default stream always exists */
+ if (strcmp(new_name, "") == 0 ||
+ strcasecmp_m(new_name, ":$DATA") == 0) {
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+
+ /* try to find the old/new names in the list */
+ found_old = found_new = -1;
+ for (i=0;i<streams->num_streams;i++) {
+ struct xattr_DosStream *s = &streams->streams[i];
+ if (stream_name_cmp(s->name, new_name) == 0) {
+ found_new = i;
+ }
+ if (stream_name_cmp(s->name, name->stream_name) == 0) {
+ found_old = i;
+ }
+ }
+
+ if (found_old == -1) {
+ talloc_free(streams);
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ if (found_new == -1) {
+ /* a simple rename */
+ struct xattr_DosStream *s = &streams->streams[found_old];
+ s->name = new_name;
+ } else {
+ if (!overwrite) {
+ return NT_STATUS_OBJECT_NAME_COLLISION;
+ }
+ if (found_old != found_new) {
+ /* remove the old one and replace with the new one */
+ streams->streams[found_old].name = new_name;
+ memmove(&streams->streams[found_new],
+ &streams->streams[found_new+1],
+ sizeof(streams->streams[0]) *
+ (streams->num_streams - (found_new+1)));
+ streams->num_streams--;
+ }
+ }
+
+ status = pvfs_streams_save(pvfs, name, fd, streams);
+
+ if (NT_STATUS_IS_OK(status)) {
+
+ /* update the in-memory copy of the name of the open file */
+ talloc_free(name->stream_name);
+ name->stream_name = talloc_strdup(name, new_name);
+
+ talloc_free(streams);
+ }
+
+ return status;
+}
+
+
+/*
+ create the xattr for a alternate data stream
+*/
+NTSTATUS pvfs_stream_create(struct pvfs_state *pvfs,
+ struct pvfs_filename *name,
+ int fd)
+{
+ NTSTATUS status;
+ status = pvfs_xattr_create(pvfs, name->full_name, fd,
+ XATTR_DOSSTREAM_PREFIX, name->stream_name);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ return pvfs_stream_update_size(pvfs, name, fd, 0);
+}
+
+/*
+ delete the xattr for a alternate data stream
+*/
+NTSTATUS pvfs_stream_delete(struct pvfs_state *pvfs,
+ struct pvfs_filename *name,
+ int fd)
+{
+ NTSTATUS status;
+ struct xattr_DosStreams *streams;
+ int i;
+
+ status = pvfs_xattr_delete(pvfs, name->full_name, fd,
+ XATTR_DOSSTREAM_PREFIX, name->stream_name);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ streams = talloc(name, struct xattr_DosStreams);
+ if (streams == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = pvfs_streams_load(pvfs, name, fd, streams);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(streams);
+ return status;
+ }
+
+ for (i=0;i<streams->num_streams;i++) {
+ struct xattr_DosStream *s = &streams->streams[i];
+ if (stream_name_cmp(s->name, name->stream_name) == 0) {
+ memmove(s, s+1, (streams->num_streams - (i+1)) * sizeof(*s));
+ streams->num_streams--;
+ break;
+ }
+ }
+
+ status = pvfs_streams_save(pvfs, name, fd, streams);
+ talloc_free(streams);
+
+ return status;
+}
+
+/*
+ load a stream into a blob
+*/
+static NTSTATUS pvfs_stream_load(struct pvfs_state *pvfs,
+ TALLOC_CTX *mem_ctx,
+ struct pvfs_filename *name,
+ int fd,
+ size_t estimated_size,
+ DATA_BLOB *blob)
+{
+ NTSTATUS status;
+
+ status = pvfs_xattr_load(pvfs, mem_ctx, name->full_name, fd,
+ XATTR_DOSSTREAM_PREFIX,
+ name->stream_name, estimated_size, blob);
+
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+ /* try with a case insensitive match */
+ struct xattr_DosStreams *streams;
+ int i;
+
+ streams = talloc(mem_ctx, struct xattr_DosStreams);
+ if (streams == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ status = pvfs_streams_load(pvfs, name, fd, streams);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(streams);
+ return NT_STATUS_NOT_FOUND;
+ }
+ for (i=0;i<streams->num_streams;i++) {
+ struct xattr_DosStream *s = &streams->streams[i];
+ if (stream_name_cmp(s->name, name->stream_name) == 0) {
+ status = pvfs_xattr_load(pvfs, mem_ctx, name->full_name, fd,
+ XATTR_DOSSTREAM_PREFIX,
+ s->name, estimated_size, blob);
+ talloc_free(streams);
+ return status;
+ }
+ }
+ talloc_free(streams);
+ return NT_STATUS_NOT_FOUND;
+ }
+
+ return status;
+}
+
+/*
+ the equivalent of pread() on a stream
+*/
+ssize_t pvfs_stream_read(struct pvfs_state *pvfs,
+ struct pvfs_file_handle *h, void *data, size_t count, off_t offset)
+{
+ NTSTATUS status;
+ DATA_BLOB blob;
+ if (count == 0) {
+ return 0;
+ }
+ status = pvfs_stream_load(pvfs, h, h->name, h->fd, offset+count, &blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ errno = EIO;
+ return -1;
+ }
+ if (offset >= blob.length) {
+ data_blob_free(&blob);
+ return 0;
+ }
+ if (count > blob.length - offset) {
+ count = blob.length - offset;
+ }
+ memcpy(data, blob.data + offset, count);
+ data_blob_free(&blob);
+ return count;
+}
+
+
+/*
+ the equivalent of pwrite() on a stream
+*/
+ssize_t pvfs_stream_write(struct pvfs_state *pvfs,
+ struct pvfs_file_handle *h, const void *data, size_t count, off_t offset)
+{
+ NTSTATUS status;
+ DATA_BLOB blob;
+ if (count == 0) {
+ return 0;
+ }
+
+ if (count+offset > XATTR_MAX_STREAM_SIZE) {
+ if (!pvfs->ea_db || count+offset > XATTR_MAX_STREAM_SIZE_TDB) {
+ errno = ENOSPC;
+ return -1;
+ }
+ }
+
+ /* we have to load the existing stream, then modify, then save */
+ status = pvfs_stream_load(pvfs, h, h->name, h->fd, offset+count, &blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ blob = data_blob(NULL, 0);
+ }
+ if (count+offset > blob.length) {
+ blob.data = talloc_realloc(blob.data, blob.data, uint8_t, count+offset);
+ if (blob.data == NULL) {
+ errno = ENOMEM;
+ return -1;
+ }
+ if (offset > blob.length) {
+ memset(blob.data+blob.length, 0, offset - blob.length);
+ }
+ blob.length = count+offset;
+ }
+ memcpy(blob.data + offset, data, count);
+
+ status = pvfs_xattr_save(pvfs, h->name->full_name, h->fd, XATTR_DOSSTREAM_PREFIX,
+ h->name->stream_name, &blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ data_blob_free(&blob);
+ /* getting this error mapping right is probably
+ not worth it */
+ errno = ENOSPC;
+ return -1;
+ }
+
+ status = pvfs_stream_update_size(pvfs, h->name, h->fd, blob.length);
+
+ data_blob_free(&blob);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ errno = EIO;
+ return -1;
+ }
+
+ return count;
+}
+
+/*
+ the equivalent of truncate() on a stream
+*/
+NTSTATUS pvfs_stream_truncate(struct pvfs_state *pvfs,
+ struct pvfs_filename *name, int fd, off_t length)
+{
+ NTSTATUS status;
+ DATA_BLOB blob;
+
+ if (length > XATTR_MAX_STREAM_SIZE) {
+ if (!pvfs->ea_db || length > XATTR_MAX_STREAM_SIZE_TDB) {
+ return NT_STATUS_DISK_FULL;
+ }
+ }
+
+ /* we have to load the existing stream, then modify, then save */
+ status = pvfs_stream_load(pvfs, name, name, fd, length, &blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ if (length <= blob.length) {
+ blob.length = length;
+ } else if (length > blob.length) {
+ blob.data = talloc_realloc(blob.data, blob.data, uint8_t, length);
+ if (blob.data == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ memset(blob.data+blob.length, 0, length - blob.length);
+ blob.length = length;
+ }
+
+ status = pvfs_xattr_save(pvfs, name->full_name, fd, XATTR_DOSSTREAM_PREFIX,
+ name->stream_name, &blob);
+
+ if (NT_STATUS_IS_OK(status)) {
+ status = pvfs_stream_update_size(pvfs, name, fd, blob.length);
+ }
+ data_blob_free(&blob);
+
+ return status;
+}
diff --git a/source4/ntvfs/posix/pvfs_sys.c b/source4/ntvfs/posix/pvfs_sys.c
new file mode 100644
index 0000000..32770c1
--- /dev/null
+++ b/source4/ntvfs/posix/pvfs_sys.c
@@ -0,0 +1,662 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ POSIX NTVFS backend - pvfs_sys wrappers
+
+ Copyright (C) Andrew Tridgell 2010
+ Copyright (C) Andrew Bartlett 2010
+
+ 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 "vfs_posix.h"
+#include "../lib/util/unix_privs.h"
+
+/*
+ these wrapper functions must only be called when the appropriate ACL
+ has already been checked. The wrappers will override a EACCES result
+ by gaining root privileges if the 'pvfs:perm override' is set on the
+ share (it is enabled by default)
+
+ Careful use of O_NOFOLLOW and O_DIRECTORY is used to prevent
+ security attacks via symlinks
+ */
+
+
+struct pvfs_sys_ctx {
+ struct pvfs_state *pvfs;
+ void *privs;
+ const char *old_wd;
+ struct stat st_orig;
+};
+
+
+/*
+ we create PVFS_NOFOLLOW and PVFS_DIRECTORY as aliases for O_NOFOLLOW
+ and O_DIRECTORY on systems that have them. On systems that don't
+ have O_NOFOLLOW we are less safe, but the root override code is off
+ by default.
+ */
+#ifdef O_NOFOLLOW
+#define PVFS_NOFOLLOW O_NOFOLLOW
+#else
+#define PVFS_NOFOLLOW 0
+#endif
+#ifdef O_DIRECTORY
+#define PVFS_DIRECTORY O_DIRECTORY
+#else
+#define PVFS_DIRECTORY 0
+#endif
+
+/*
+ return to original directory when context is destroyed
+ */
+static int pvfs_sys_pushdir_destructor(struct pvfs_sys_ctx *ctx)
+{
+ struct stat st;
+
+ if (ctx->old_wd == NULL) {
+ return 0;
+ }
+
+ if (chdir(ctx->old_wd) != 0) {
+ smb_panic("Failed to restore working directory");
+ }
+ if (stat(".", &st) != 0) {
+ smb_panic("Failed to stat working directory");
+ }
+ if (st.st_ino != ctx->st_orig.st_ino ||
+ st.st_dev != ctx->st_orig.st_dev) {
+ smb_panic("Working directory changed during call");
+ }
+
+ return 0;
+}
+
+
+/*
+ chdir() to the directory part of a pathname, but disallow any
+ component with a symlink
+
+ Note that we can't use O_NOFOLLOW on the whole path as that only
+ prevents links in the final component of the path
+ */
+static int pvfs_sys_chdir_nosymlink(struct pvfs_sys_ctx *ctx, const char *pathname)
+{
+ char *p, *path;
+ size_t base_len = strlen(ctx->pvfs->base_directory);
+
+ /* don't check for symlinks in the base directory of the share */
+ if (strncmp(ctx->pvfs->base_directory, pathname, base_len) == 0 &&
+ pathname[base_len] == '/') {
+ if (chdir(ctx->pvfs->base_directory) != 0) {
+ return -1;
+ }
+ pathname += base_len + 1;
+ }
+
+ path = talloc_strdup(ctx, pathname);
+ if (path == NULL) {
+ return -1;
+ }
+ while ((p = strchr(path, '/'))) {
+ int fd;
+ struct stat st1, st2;
+ *p = 0;
+ fd = open(path, PVFS_NOFOLLOW | PVFS_DIRECTORY | O_RDONLY);
+ if (fd == -1) {
+ return -1;
+ }
+ if (chdir(path) != 0) {
+ close(fd);
+ return -1;
+ }
+ if (stat(".", &st1) != 0 ||
+ fstat(fd, &st2) != 0) {
+ close(fd);
+ return -1;
+ }
+ close(fd);
+ if (st1.st_ino != st2.st_ino ||
+ st1.st_dev != st2.st_dev) {
+ DEBUG(0,(__location__ ": Inode changed during chdir in '%s' - symlink attack?\n",
+ pathname));
+ return -1;
+ }
+ path = p + 1;
+ }
+
+ return 0;
+}
+
+
+/*
+ become root, and change directory to the directory component of a
+ path. Return a talloc context which when freed will move us back
+ to the original directory, and return us to the original uid
+
+ change the pathname argument to contain just the base component of
+ the path
+
+ return NULL on error, which could include an attempt to subvert
+ security using symlink tricks
+ */
+static struct pvfs_sys_ctx *pvfs_sys_pushdir(struct pvfs_state *pvfs,
+ const char **pathname)
+{
+ struct pvfs_sys_ctx *ctx;
+ char *cwd, *p, *dirname;
+ int ret;
+
+ ctx = talloc_zero(pvfs, struct pvfs_sys_ctx);
+ if (ctx == NULL) {
+ return NULL;
+ }
+ ctx->pvfs = pvfs;
+ ctx->privs = root_privileges();
+ if (ctx->privs == NULL) {
+ talloc_free(ctx);
+ return NULL;
+ }
+
+ talloc_steal(ctx, ctx->privs);
+
+ if (!pathname) {
+ /* no pathname needed */
+ return ctx;
+ }
+
+ p = strrchr(*pathname, '/');
+ if (p == NULL) {
+ /* we don't need to change directory */
+ return ctx;
+ }
+
+ /* we keep the old st around, so we can tell that
+ we have come back to the right directory */
+ if (stat(".", &ctx->st_orig) != 0) {
+ talloc_free(ctx);
+ return NULL;
+ }
+
+ cwd = get_current_dir_name();
+ if (cwd == NULL) {
+ talloc_free(ctx);
+ return NULL;
+ }
+
+ ctx->old_wd = talloc_strdup(ctx, cwd);
+ free(cwd);
+
+ if (ctx->old_wd == NULL) {
+ talloc_free(ctx);
+ return NULL;
+ }
+
+ dirname = talloc_strndup(ctx, *pathname, (p - *pathname));
+ if (dirname == NULL) {
+ talloc_free(ctx);
+ return NULL;
+ }
+
+ ret = pvfs_sys_chdir_nosymlink(ctx, *pathname);
+ if (ret == -1) {
+ talloc_free(ctx);
+ return NULL;
+ }
+
+ talloc_set_destructor(ctx, pvfs_sys_pushdir_destructor);
+
+ /* return the basename as the filename that should be operated on */
+ (*pathname) = talloc_strdup(ctx, p+1);
+ if (! *pathname) {
+ talloc_free(ctx);
+ return NULL;
+ }
+
+ return ctx;
+}
+
+
+/*
+ chown a file that we created with a root privileges override
+ */
+static int pvfs_sys_fchown(struct pvfs_state *pvfs, struct pvfs_sys_ctx *ctx, int fd)
+{
+ return fchown(fd, root_privileges_original_uid(ctx->privs), -1);
+}
+
+/*
+ chown a directory that we created with a root privileges override
+ */
+static int pvfs_sys_chown(struct pvfs_state *pvfs, struct pvfs_sys_ctx *ctx, const char *name)
+{
+ /* to avoid symlink hacks, we need to use fchown() on a directory fd */
+ int ret, fd;
+ fd = open(name, PVFS_DIRECTORY | PVFS_NOFOLLOW | O_RDONLY);
+ if (fd == -1) {
+ return -1;
+ }
+ ret = pvfs_sys_fchown(pvfs, ctx, fd);
+ close(fd);
+ return ret;
+}
+
+
+/*
+ wrap open for system override
+*/
+int pvfs_sys_open(struct pvfs_state *pvfs, const char *filename, int flags, mode_t mode, bool allow_override)
+{
+ int fd, ret;
+ struct pvfs_sys_ctx *ctx;
+ int saved_errno, orig_errno;
+ int retries = 5;
+
+ orig_errno = errno;
+
+ fd = open(filename, flags, mode);
+ if (fd != -1 ||
+ !allow_override ||
+ errno != EACCES) {
+ return fd;
+ }
+
+ saved_errno = errno;
+ ctx = pvfs_sys_pushdir(pvfs, &filename);
+ if (ctx == NULL) {
+ errno = saved_errno;
+ return -1;
+ }
+
+ /* don't allow permission overrides to follow links */
+ flags |= PVFS_NOFOLLOW;
+
+ /*
+ if O_CREAT was specified and O_EXCL was not specified
+ then initially do the open without O_CREAT, as in that case
+ we know that we did not create the file, so we don't have
+ to fchown it
+ */
+ if ((flags & O_CREAT) && !(flags & O_EXCL)) {
+ try_again:
+ fd = open(filename, flags & ~O_CREAT, mode);
+ /* if this open succeeded, or if it failed
+ with anything other than ENOENT, then we return the
+ open result, with the original errno */
+ if (fd == -1 && errno != ENOENT) {
+ talloc_free(ctx);
+ errno = saved_errno;
+ return -1;
+ }
+ if (fd != -1) {
+ /* the file already existed and we opened it */
+ talloc_free(ctx);
+ errno = orig_errno;
+ return fd;
+ }
+
+ fd = open(filename, flags | O_EXCL, mode);
+ if (fd == -1 && errno != EEXIST) {
+ talloc_free(ctx);
+ errno = saved_errno;
+ return -1;
+ }
+ if (fd != -1) {
+ /* we created the file, we need to set the
+ right ownership on it */
+ ret = pvfs_sys_fchown(pvfs, ctx, fd);
+ if (ret == -1) {
+ close(fd);
+ unlink(filename);
+ talloc_free(ctx);
+ errno = saved_errno;
+ return -1;
+ }
+ talloc_free(ctx);
+ errno = orig_errno;
+ return fd;
+ }
+
+ /* the file got created between the two times
+ we tried to open it! Try again */
+ if (retries-- > 0) {
+ goto try_again;
+ }
+
+ talloc_free(ctx);
+ errno = saved_errno;
+ return -1;
+ }
+
+ fd = open(filename, flags, mode);
+ if (fd == -1) {
+ talloc_free(ctx);
+ errno = saved_errno;
+ return -1;
+ }
+
+ /* if we have created a file then fchown it */
+ if (flags & O_CREAT) {
+ ret = pvfs_sys_fchown(pvfs, ctx, fd);
+ if (ret == -1) {
+ close(fd);
+ unlink(filename);
+ talloc_free(ctx);
+ errno = saved_errno;
+ return -1;
+ }
+ }
+
+ talloc_free(ctx);
+ return fd;
+}
+
+
+/*
+ wrap unlink for system override
+*/
+int pvfs_sys_unlink(struct pvfs_state *pvfs, const char *filename, bool allow_override)
+{
+ int ret;
+ struct pvfs_sys_ctx *ctx;
+ int saved_errno, orig_errno;
+
+ orig_errno = errno;
+
+ ret = unlink(filename);
+ if (ret != -1 ||
+ !allow_override ||
+ errno != EACCES) {
+ return ret;
+ }
+
+ saved_errno = errno;
+
+ ctx = pvfs_sys_pushdir(pvfs, &filename);
+ if (ctx == NULL) {
+ errno = saved_errno;
+ return -1;
+ }
+
+ ret = unlink(filename);
+ if (ret == -1) {
+ talloc_free(ctx);
+ errno = saved_errno;
+ return -1;
+ }
+
+ talloc_free(ctx);
+ errno = orig_errno;
+ return ret;
+}
+
+
+static bool contains_symlink(const char *path)
+{
+ int fd = open(path, PVFS_NOFOLLOW | O_RDONLY);
+ int posix_errno = errno;
+ if (fd != -1) {
+ close(fd);
+ return false;
+ }
+
+#if defined(ENOTSUP) && defined(OSF1)
+ /* handle special Tru64 errno */
+ if (errno == ENOTSUP) {
+ posix_errno = ELOOP;
+ }
+#endif /* ENOTSUP */
+
+#ifdef EFTYPE
+ /* fix broken NetBSD errno */
+ if (errno == EFTYPE) {
+ posix_errno = ELOOP;
+ }
+#endif /* EFTYPE */
+
+ /* fix broken FreeBSD errno */
+ if (errno == EMLINK) {
+ posix_errno = ELOOP;
+ }
+
+ return (posix_errno == ELOOP);
+}
+
+/*
+ wrap rename for system override
+*/
+int pvfs_sys_rename(struct pvfs_state *pvfs, const char *name1, const char *name2, bool allow_override)
+{
+ int ret;
+ struct pvfs_sys_ctx *ctx;
+ int saved_errno, orig_errno;
+
+ orig_errno = errno;
+
+ ret = rename(name1, name2);
+ if (ret != -1 ||
+ !allow_override ||
+ errno != EACCES) {
+ return ret;
+ }
+
+ saved_errno = errno;
+
+ ctx = pvfs_sys_pushdir(pvfs, &name1);
+ if (ctx == NULL) {
+ errno = saved_errno;
+ return -1;
+ }
+
+ /* we need the destination as an absolute path */
+ if (name2[0] != '/') {
+ name2 = talloc_asprintf(ctx, "%s/%s", ctx->old_wd, name2);
+ if (name2 == NULL) {
+ talloc_free(ctx);
+ errno = saved_errno;
+ return -1;
+ }
+ }
+
+ /* make sure the destination isn't a symlink beforehand */
+ if (contains_symlink(name2)) {
+ talloc_free(ctx);
+ errno = saved_errno;
+ return -1;
+ }
+
+ ret = rename(name1, name2);
+ if (ret == -1) {
+ talloc_free(ctx);
+ errno = saved_errno;
+ return -1;
+ }
+
+ /* make sure the destination isn't a symlink afterwards */
+ if (contains_symlink(name2)) {
+ DEBUG(0,(__location__ ": Possible symlink attack in rename to '%s' - unlinking\n", name2));
+ unlink(name2);
+ talloc_free(ctx);
+ errno = saved_errno;
+ return -1;
+ }
+
+ talloc_free(ctx);
+ errno = orig_errno;
+ return ret;
+}
+
+
+/*
+ wrap mkdir for system override
+*/
+int pvfs_sys_mkdir(struct pvfs_state *pvfs, const char *dirname, mode_t mode, bool allow_override)
+{
+ int ret;
+ struct pvfs_sys_ctx *ctx;
+ int saved_errno, orig_errno;
+
+ orig_errno = errno;
+
+ ret = mkdir(dirname, mode);
+ if (ret != -1 ||
+ !allow_override ||
+ errno != EACCES) {
+ return ret;
+ }
+
+ saved_errno = errno;
+ ctx = pvfs_sys_pushdir(pvfs, &dirname);
+ if (ctx == NULL) {
+ errno = saved_errno;
+ return -1;
+ }
+
+ ret = mkdir(dirname, mode);
+ if (ret == -1) {
+ talloc_free(ctx);
+ errno = saved_errno;
+ return -1;
+ }
+
+ ret = pvfs_sys_chown(pvfs, ctx, dirname);
+ if (ret == -1) {
+ rmdir(dirname);
+ talloc_free(ctx);
+ errno = saved_errno;
+ return -1;
+ }
+
+ talloc_free(ctx);
+ errno = orig_errno;
+ return ret;
+}
+
+
+/*
+ wrap rmdir for system override
+*/
+int pvfs_sys_rmdir(struct pvfs_state *pvfs, const char *dirname, bool allow_override)
+{
+ int ret;
+ struct pvfs_sys_ctx *ctx;
+ int saved_errno, orig_errno;
+
+ orig_errno = errno;
+
+ ret = rmdir(dirname);
+ if (ret != -1 ||
+ !allow_override ||
+ errno != EACCES) {
+ return ret;
+ }
+
+ saved_errno = errno;
+
+ ctx = pvfs_sys_pushdir(pvfs, &dirname);
+ if (ctx == NULL) {
+ errno = saved_errno;
+ return -1;
+ }
+
+ ret = rmdir(dirname);
+ if (ret == -1) {
+ talloc_free(ctx);
+ errno = saved_errno;
+ return -1;
+ }
+
+ talloc_free(ctx);
+ errno = orig_errno;
+ return ret;
+}
+
+/*
+ wrap fchmod for system override
+*/
+int pvfs_sys_fchmod(struct pvfs_state *pvfs, int fd, mode_t mode, bool allow_override)
+{
+ int ret;
+ struct pvfs_sys_ctx *ctx;
+ int saved_errno, orig_errno;
+
+ orig_errno = errno;
+
+ ret = fchmod(fd, mode);
+ if (ret != -1 ||
+ !allow_override ||
+ errno != EACCES) {
+ return ret;
+ }
+
+ saved_errno = errno;
+
+ ctx = pvfs_sys_pushdir(pvfs, NULL);
+ if (ctx == NULL) {
+ errno = saved_errno;
+ return -1;
+ }
+
+ ret = fchmod(fd, mode);
+ if (ret == -1) {
+ talloc_free(ctx);
+ errno = saved_errno;
+ return -1;
+ }
+
+ talloc_free(ctx);
+ errno = orig_errno;
+ return ret;
+}
+
+
+/*
+ wrap chmod for system override
+*/
+int pvfs_sys_chmod(struct pvfs_state *pvfs, const char *filename, mode_t mode, bool allow_override)
+{
+ int ret;
+ struct pvfs_sys_ctx *ctx;
+ int saved_errno, orig_errno;
+
+ orig_errno = errno;
+
+ ret = chmod(filename, mode);
+ if (ret != -1 ||
+ !allow_override ||
+ errno != EACCES) {
+ return ret;
+ }
+
+ saved_errno = errno;
+
+ ctx = pvfs_sys_pushdir(pvfs, &filename);
+ if (ctx == NULL) {
+ errno = saved_errno;
+ return -1;
+ }
+
+ ret = chmod(filename, mode);
+ if (ret == -1) {
+ talloc_free(ctx);
+ errno = saved_errno;
+ return -1;
+ }
+
+ talloc_free(ctx);
+ errno = orig_errno;
+ return ret;
+}
diff --git a/source4/ntvfs/posix/pvfs_unlink.c b/source4/ntvfs/posix/pvfs_unlink.c
new file mode 100644
index 0000000..605ec10
--- /dev/null
+++ b/source4/ntvfs/posix/pvfs_unlink.c
@@ -0,0 +1,276 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ POSIX NTVFS backend - unlink
+
+ Copyright (C) Andrew Tridgell 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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 "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 = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ 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
+*/
+static NTSTATUS pvfs_unlink_file(struct pvfs_state *pvfs,
+ struct pvfs_filename *name)
+{
+ NTSTATUS status = NT_STATUS_OK;
+
+ if (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) {
+ return NT_STATUS_FILE_IS_A_DIRECTORY;
+ }
+
+ if (name->st.st_nlink == 1) {
+ status = pvfs_xattr_unlink_hook(pvfs, name->full_name);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+ }
+
+ /* finally try the actual unlink */
+ if (pvfs_sys_unlink(pvfs, name->full_name, name->allow_override) == -1) {
+ status = pvfs_map_errno(pvfs, errno);
+ }
+
+ if (NT_STATUS_IS_OK(status)) {
+ notify_trigger(pvfs->notify_context,
+ NOTIFY_ACTION_REMOVED,
+ FILE_NOTIFY_CHANGE_FILE_NAME,
+ name->full_name);
+ }
+
+ return status;
+}
+
+/*
+ unlink one file
+*/
+static NTSTATUS pvfs_unlink_one(struct pvfs_state *pvfs,
+ struct ntvfs_request *req,
+ union smb_unlink *unl,
+ struct pvfs_filename *name)
+{
+ NTSTATUS status;
+ struct odb_lock *lck = NULL;
+
+ /* make sure its matches the given attributes */
+ status = pvfs_match_attrib(pvfs, name,
+ unl->unlink.in.attrib, 0);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ 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;
+ }
+
+ if (name->stream_name) {
+ if (!name->stream_exists) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ return pvfs_stream_delete(pvfs, name, -1);
+ }
+
+ return pvfs_unlink_file(pvfs, name);
+}
+
+/*
+ delete a file - the dirtype specifies the file types to include in the search.
+ The name can contain CIFS wildcards, but rarely does (except with OS/2 clients)
+*/
+NTSTATUS pvfs_unlink(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_unlink *unl)
+{
+ struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ struct pvfs_dir *dir;
+ NTSTATUS status;
+ uint32_t total_deleted=0;
+ struct pvfs_filename *name;
+ const char *fname;
+ off_t ofs;
+
+ /* resolve the cifs name to a posix name */
+ status = pvfs_resolve_name(pvfs, req, unl->unlink.in.pattern,
+ PVFS_RESOLVE_WILDCARD |
+ PVFS_RESOLVE_STREAMS |
+ PVFS_RESOLVE_NO_OPENDB,
+ &name);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ if (!name->exists && !name->has_wildcard) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ if (name->exists &&
+ (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) {
+ return NT_STATUS_FILE_IS_A_DIRECTORY;
+ }
+
+ if (!name->has_wildcard) {
+ return pvfs_unlink_one(pvfs, req, unl, name);
+ }
+
+ /*
+ * disable async requests in the wildcard case
+ * until we have proper tests for this
+ */
+ req->async_states->state &= ~NTVFS_ASYNC_STATE_MAY_ASYNC;
+
+ /* get list of matching files */
+ status = pvfs_list_start(pvfs, name, req, &dir);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = NT_STATUS_NO_SUCH_FILE;
+ talloc_free(name);
+
+ ofs = 0;
+
+ while ((fname = pvfs_list_next(dir, &ofs))) {
+ /* this seems to be a special case */
+ if ((unl->unlink.in.attrib & FILE_ATTRIBUTE_DIRECTORY) &&
+ (ISDOT(fname) || ISDOTDOT(fname))) {
+ return NT_STATUS_OBJECT_NAME_INVALID;
+ }
+
+ /* get a pvfs_filename object */
+ status = pvfs_resolve_partial(pvfs, req,
+ pvfs_list_unix_path(dir),
+ fname,
+ PVFS_RESOLVE_NO_OPENDB,
+ &name);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ status = pvfs_unlink_one(pvfs, req, unl, name);
+ if (NT_STATUS_IS_OK(status)) {
+ total_deleted++;
+ }
+
+ talloc_free(name);
+ }
+
+ if (total_deleted > 0) {
+ status = NT_STATUS_OK;
+ }
+
+ return status;
+}
+
+
diff --git a/source4/ntvfs/posix/pvfs_util.c b/source4/ntvfs/posix/pvfs_util.c
new file mode 100644
index 0000000..2975e04
--- /dev/null
+++ b/source4/ntvfs/posix/pvfs_util.c
@@ -0,0 +1,206 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Andrew Tridgell 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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/>.
+*/
+/*
+ utility functions for posix backend
+*/
+
+#include "includes.h"
+#include "vfs_posix.h"
+
+/*
+ return true if a string contains one of the CIFS wildcard characters
+*/
+bool pvfs_has_wildcard(const char *str)
+{
+ if (strpbrk(str, "*?<>\"")) {
+ return true;
+ }
+ return false;
+}
+
+/*
+ map a unix errno to a NTSTATUS
+*/
+NTSTATUS pvfs_map_errno(struct pvfs_state *pvfs, int unix_errno)
+{
+ NTSTATUS status;
+ status = map_nt_error_from_unix_common(unix_errno);
+ DEBUG(10,(__location__ " mapped unix errno %d -> %s\n", unix_errno, nt_errstr(status)));
+ return status;
+}
+
+
+/*
+ check if a filename has an attribute matching the given attribute search value
+ this is used by calls like unlink and search which take an attribute
+ and only include special files if they match the given attribute
+*/
+NTSTATUS pvfs_match_attrib(struct pvfs_state *pvfs, struct pvfs_filename *name,
+ uint32_t attrib, uint32_t must_attrib)
+{
+ if ((name->dos.attrib & ~attrib) & FILE_ATTRIBUTE_DIRECTORY) {
+ return NT_STATUS_FILE_IS_A_DIRECTORY;
+ }
+ if ((name->dos.attrib & ~attrib) & (FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM)) {
+ return NT_STATUS_NO_SUCH_FILE;
+ }
+ if (must_attrib & ~name->dos.attrib) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+ return NT_STATUS_OK;
+}
+
+
+/*
+ normalise a file attribute
+*/
+uint32_t pvfs_attrib_normalise(uint32_t attrib, mode_t mode)
+{
+ if (attrib != FILE_ATTRIBUTE_NORMAL) {
+ attrib &= ~FILE_ATTRIBUTE_NORMAL;
+ }
+ if (S_ISDIR(mode)) {
+ attrib |= FILE_ATTRIBUTE_DIRECTORY;
+ } else {
+ attrib &= ~FILE_ATTRIBUTE_DIRECTORY;
+ }
+ return attrib;
+}
+
+
+/*
+ copy a file. Caller is supposed to have already ensured that the
+ operation is allowed. The destination file must not exist.
+*/
+NTSTATUS pvfs_copy_file(struct pvfs_state *pvfs,
+ struct pvfs_filename *name1,
+ struct pvfs_filename *name2,
+ bool allow_override)
+{
+ int fd1, fd2;
+ mode_t mode;
+ NTSTATUS status;
+ size_t buf_size = 0x10000;
+ uint8_t *buf = talloc_array(name2, uint8_t, buf_size);
+
+ if (buf == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ fd1 = pvfs_sys_open(pvfs, name1->full_name, O_RDONLY, 0, allow_override);
+ if (fd1 == -1) {
+ talloc_free(buf);
+ return pvfs_map_errno(pvfs, errno);
+ }
+
+ fd2 = pvfs_sys_open(pvfs, name2->full_name, O_CREAT|O_EXCL|O_WRONLY, 0, allow_override);
+ if (fd2 == -1) {
+ close(fd1);
+ talloc_free(buf);
+ return pvfs_map_errno(pvfs, errno);
+ }
+
+ while (1) {
+ ssize_t ret2, ret = read(fd1, buf, buf_size);
+ if (ret == -1 &&
+ (errno == EINTR || errno == EAGAIN)) {
+ continue;
+ }
+ if (ret <= 0) break;
+
+ ret2 = write(fd2, buf, ret);
+ if (ret2 == -1 &&
+ (errno == EINTR || errno == EAGAIN)) {
+ continue;
+ }
+
+ if (ret2 != ret) {
+ close(fd1);
+ close(fd2);
+ talloc_free(buf);
+ pvfs_sys_unlink(pvfs, name2->full_name, allow_override);
+ if (ret2 == -1) {
+ return pvfs_map_errno(pvfs, errno);
+ }
+ return NT_STATUS_DISK_FULL;
+ }
+ }
+
+ talloc_free(buf);
+ close(fd1);
+
+ mode = pvfs_fileperms(pvfs, name1->dos.attrib);
+ if (pvfs_sys_fchmod(pvfs, fd2, mode, allow_override) == -1) {
+ status = pvfs_map_errno(pvfs, errno);
+ close(fd2);
+ pvfs_sys_unlink(pvfs, name2->full_name, allow_override);
+ return status;
+ }
+
+ name2->st.st_mode = mode;
+ name2->dos = name1->dos;
+
+ status = pvfs_dosattrib_save(pvfs, name2, fd2);
+ if (!NT_STATUS_IS_OK(status)) {
+ close(fd2);
+ pvfs_sys_unlink(pvfs, name2->full_name, allow_override);
+ return status;
+ }
+
+ close(fd2);
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ hash a string of the specified length. The string does not need to be
+ null terminated
+
+ hash algorithm changed to FNV1 by idra@samba.org (Simo Sorce).
+ see http://www.isthe.com/chongo/tech/comp/fnv/index.html for a
+ discussion on Fowler / Noll / Vo (FNV) Hash by one of it's authors
+*/
+uint32_t pvfs_name_hash(const char *key, size_t length)
+{
+ const uint32_t fnv1_prime = 0x01000193;
+ const uint32_t fnv1_init = 0xa6b93095;
+ uint32_t value = fnv1_init;
+
+ while (*key && length--) {
+ size_t c_size;
+ codepoint_t c = next_codepoint(key, &c_size);
+ c = toupper_m(c);
+ value *= fnv1_prime;
+ value ^= (uint32_t)c;
+ key += c_size;
+ }
+
+ return value;
+}
+
+
+/*
+ file allocation size rounding. This is required to pass ifstest
+*/
+uint64_t pvfs_round_alloc_size(struct pvfs_state *pvfs, uint64_t size)
+{
+ const uint32_t round_value = pvfs->alloc_size_rounding;
+ return round_value * ((size + round_value - 1)/round_value);
+}
diff --git a/source4/ntvfs/posix/pvfs_wait.c b/source4/ntvfs/posix/pvfs_wait.c
new file mode 100644
index 0000000..21ff33e
--- /dev/null
+++ b/source4/ntvfs/posix/pvfs_wait.c
@@ -0,0 +1,212 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ POSIX NTVFS backend - async request wait routines
+
+ Copyright (C) Andrew Tridgell 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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/events/events.h"
+#include "../lib/util/dlinklist.h"
+#include "vfs_posix.h"
+#include "samba/service_stream.h"
+#include "lib/messaging/irpc.h"
+
+/* the context for a single wait instance */
+struct pvfs_wait {
+ struct pvfs_wait *next, *prev;
+ struct pvfs_state *pvfs;
+ void (*handler)(void *, enum pvfs_wait_notice);
+ void *private_data;
+ int msg_type;
+ struct imessaging_context *msg_ctx;
+ struct tevent_context *ev;
+ struct ntvfs_request *req;
+ enum pvfs_wait_notice reason;
+};
+
+/*
+ called from the ntvfs layer when we have requested setup of an async
+ call. this ensures that async calls runs with the right state of
+ previous ntvfs handlers in the chain (such as security context)
+*/
+NTSTATUS pvfs_async_setup(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, void *private_data)
+{
+ struct pvfs_wait *pwait = talloc_get_type(private_data,
+ struct pvfs_wait);
+ pwait->handler(pwait->private_data, pwait->reason);
+ return NT_STATUS_OK;
+}
+
+/*
+ receive a completion message for a wait
+*/
+static void pvfs_wait_dispatch(struct imessaging_context *msg,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id src,
+ size_t num_fds,
+ int *fds,
+ DATA_BLOB *data)
+{
+ struct pvfs_wait *pwait = talloc_get_type(private_data,
+ struct pvfs_wait);
+ struct ntvfs_request *req;
+ void *p = NULL;
+
+ if (num_fds != 0) {
+ DBG_WARNING("Received %zu fds, ignoring message\n", num_fds);
+ return;
+ }
+
+ /* we need to check that this one is for us. See
+ imessaging_send_ptr() for the other side of this.
+ */
+ if (data->length == sizeof(void *)) {
+ void **pp;
+ pp = (void **)data->data;
+ p = *pp;
+ }
+ if (p == NULL || p != pwait->private_data) {
+ return;
+ }
+
+ pwait->reason = PVFS_WAIT_EVENT;
+
+ /* the extra reference here is to ensure that the req
+ structure is not destroyed when the async request reply is
+ sent, which would cause problems with the other ntvfs
+ modules above us */
+ req = talloc_reference(msg, pwait->req);
+ ntvfs_async_setup(pwait->req, pwait);
+ talloc_unlink(msg, req);
+}
+
+
+/*
+ receive a timeout on a message wait
+*/
+static void pvfs_wait_timeout(struct tevent_context *ev,
+ struct tevent_timer *te, struct timeval t,
+ void *private_data)
+{
+ struct pvfs_wait *pwait = talloc_get_type(private_data,
+ struct pvfs_wait);
+ struct ntvfs_request *req = pwait->req;
+
+ pwait->reason = PVFS_WAIT_TIMEOUT;
+
+ req = talloc_reference(ev, req);
+ if (req != NULL) {
+ ntvfs_async_setup(req, pwait);
+ talloc_unlink(ev, req);
+ }
+}
+
+
+/*
+ destroy a pending wait
+ */
+static int pvfs_wait_destructor(struct pvfs_wait *pwait)
+{
+ if (pwait->msg_type != -1) {
+ imessaging_deregister(pwait->msg_ctx, pwait->msg_type, pwait);
+ }
+ DLIST_REMOVE(pwait->pvfs->wait_list, pwait);
+ return 0;
+}
+
+/*
+ setup a request to wait on a message of type msg_type, with a
+ timeout (given as an expiry time)
+
+ the return value is a handle. To stop waiting talloc_free this
+ handle.
+
+ if msg_type == -1 then no message is registered, and it is assumed
+ that the caller handles any messaging setup needed
+*/
+struct pvfs_wait *pvfs_wait_message(struct pvfs_state *pvfs,
+ struct ntvfs_request *req,
+ int msg_type,
+ struct timeval end_time,
+ void (*fn)(void *, enum pvfs_wait_notice),
+ void *private_data)
+{
+ struct pvfs_wait *pwait;
+
+ pwait = talloc(pvfs, struct pvfs_wait);
+ if (pwait == NULL) {
+ return NULL;
+ }
+
+ pwait->private_data = private_data;
+ pwait->handler = fn;
+ pwait->msg_ctx = pvfs->ntvfs->ctx->msg_ctx;
+ pwait->ev = pvfs->ntvfs->ctx->event_ctx;
+ pwait->msg_type = msg_type;
+ pwait->req = talloc_reference(pwait, req);
+ pwait->pvfs = pvfs;
+
+ if (!timeval_is_zero(&end_time)) {
+ /* setup a timer */
+ tevent_add_timer(pwait->ev, pwait, end_time, pvfs_wait_timeout, pwait);
+ }
+
+ /* register with the messaging subsystem for this message
+ type */
+ if (msg_type != -1) {
+ imessaging_register(pwait->msg_ctx,
+ pwait,
+ msg_type,
+ pvfs_wait_dispatch);
+ }
+
+ /* tell the main smb server layer that we will be replying
+ asynchronously */
+ req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
+
+ DLIST_ADD(pvfs->wait_list, pwait);
+
+ /* make sure we cleanup the timer and message handler */
+ talloc_set_destructor(pwait, pvfs_wait_destructor);
+
+ return pwait;
+}
+
+
+/*
+ cancel an outstanding async request
+*/
+NTSTATUS pvfs_cancel(struct ntvfs_module_context *ntvfs, struct ntvfs_request *req)
+{
+ struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ struct pvfs_wait *pwait;
+
+ for (pwait=pvfs->wait_list;pwait;pwait=pwait->next) {
+ if (pwait->req == req) {
+ /* trigger a cancel on the request */
+ pwait->reason = PVFS_WAIT_CANCEL;
+ ntvfs_async_setup(pwait->req, pwait);
+ return NT_STATUS_OK;
+ }
+ }
+
+ return NT_STATUS_DOS(ERRDOS, ERRcancelviolation);
+}
diff --git a/source4/ntvfs/posix/pvfs_write.c b/source4/ntvfs/posix/pvfs_write.c
new file mode 100644
index 0000000..9773325
--- /dev/null
+++ b/source4/ntvfs/posix/pvfs_write.c
@@ -0,0 +1,145 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ POSIX NTVFS backend - write
+
+ Copyright (C) Andrew Tridgell 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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 "vfs_posix.h"
+#include "librpc/gen_ndr/security.h"
+#include "lib/events/events.h"
+
+static void pvfs_write_time_update_handler(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv,
+ void *private_data)
+{
+ struct pvfs_file_handle *h = talloc_get_type(private_data,
+ struct pvfs_file_handle);
+ struct odb_lock *lck;
+ NTSTATUS status;
+ NTTIME write_time;
+
+ lck = odb_lock(h, h->pvfs->odb_context, &h->odb_locking_key);
+ if (lck == NULL) {
+ DEBUG(0,("Unable to lock opendb for write time update\n"));
+ return;
+ }
+
+ write_time = timeval_to_nttime(&tv);
+
+ status = odb_set_write_time(lck, write_time, false);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("Unable to update write time: %s\n",
+ nt_errstr(status)));
+ return;
+ }
+
+ talloc_free(lck);
+
+ h->write_time.update_event = NULL;
+}
+
+static void pvfs_trigger_write_time_update(struct pvfs_file_handle *h)
+{
+ struct pvfs_state *pvfs = h->pvfs;
+ struct timeval tv;
+
+ if (h->write_time.update_triggered) {
+ return;
+ }
+
+ tv = timeval_current_ofs_usec(pvfs->writetime_delay);
+
+ h->write_time.update_triggered = true;
+ h->write_time.update_on_close = true;
+ h->write_time.update_event = tevent_add_timer(pvfs->ntvfs->ctx->event_ctx,
+ h, tv,
+ pvfs_write_time_update_handler,
+ h);
+ if (!h->write_time.update_event) {
+ DEBUG(0,("Failed tevent_add_timer\n"));
+ }
+}
+
+/*
+ write to a file
+*/
+NTSTATUS pvfs_write(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_write *wr)
+{
+ struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ ssize_t ret;
+ struct pvfs_file *f;
+ NTSTATUS status;
+
+ if (wr->generic.level != RAW_WRITE_WRITEX) {
+ return ntvfs_map_write(ntvfs, req, wr);
+ }
+
+ f = pvfs_find_fd(pvfs, req, wr->writex.in.file.ntvfs);
+ if (!f) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ if (f->handle->fd == -1) {
+ return NT_STATUS_INVALID_DEVICE_REQUEST;
+ }
+
+ if (!(f->access_mask & (SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA))) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ status = pvfs_check_lock(pvfs, f, req->smbpid,
+ wr->writex.in.offset,
+ wr->writex.in.count,
+ WRITE_LOCK);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ status = pvfs_break_level2_oplocks(f);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ pvfs_trigger_write_time_update(f->handle);
+
+ if (f->handle->name->stream_name) {
+ ret = pvfs_stream_write(pvfs,
+ f->handle,
+ wr->writex.in.data,
+ wr->writex.in.count,
+ wr->writex.in.offset);
+ } else {
+ ret = pwrite(f->handle->fd,
+ wr->writex.in.data,
+ wr->writex.in.count,
+ wr->writex.in.offset);
+ }
+ if (ret == -1) {
+ if (errno == EFBIG) {
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+ return pvfs_map_errno(pvfs, errno);
+ }
+
+ f->handle->seek_offset = wr->writex.in.offset + ret;
+
+ wr->writex.out.nwritten = ret;
+ wr->writex.out.remaining = 0; /* should fill this in? */
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/ntvfs/posix/pvfs_xattr.c b/source4/ntvfs/posix/pvfs_xattr.c
new file mode 100644
index 0000000..ab88d89
--- /dev/null
+++ b/source4/ntvfs/posix/pvfs_xattr.c
@@ -0,0 +1,488 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ POSIX NTVFS backend - xattr support
+
+ Copyright (C) Andrew Tridgell 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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 "vfs_posix.h"
+#include "../lib/util/unix_privs.h"
+#include "librpc/gen_ndr/ndr_xattr.h"
+#include "param/param.h"
+#include "ntvfs/posix/posix_eadb_proto.h"
+
+/*
+ pull a xattr as a blob
+*/
+static NTSTATUS pull_xattr_blob(struct pvfs_state *pvfs,
+ TALLOC_CTX *mem_ctx,
+ const char *attr_name,
+ const char *fname,
+ int fd,
+ size_t estimated_size,
+ DATA_BLOB *blob)
+{
+ NTSTATUS status;
+
+ if (pvfs->ea_db) {
+ return pull_xattr_blob_tdb(pvfs, mem_ctx, attr_name, fname,
+ fd, estimated_size, blob);
+ }
+
+ status = pull_xattr_blob_system(pvfs, mem_ctx, attr_name, fname,
+ fd, estimated_size, blob);
+
+ /* if the filesystem doesn't support them, then tell pvfs not to try again */
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)||
+ NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)||
+ NT_STATUS_EQUAL(status, NT_STATUS_INVALID_SYSTEM_SERVICE)) {
+ DEBUG(2,("pvfs_xattr: xattr not supported in filesystem: %s\n", nt_errstr(status)));
+ pvfs->flags &= ~PVFS_FLAG_XATTR_ENABLE;
+ status = NT_STATUS_NOT_FOUND;
+ }
+
+ return status;
+}
+
+/*
+ push a xattr as a blob
+*/
+static NTSTATUS push_xattr_blob(struct pvfs_state *pvfs,
+ const char *attr_name,
+ const char *fname,
+ int fd,
+ const DATA_BLOB *blob)
+{
+ if (pvfs->ea_db) {
+ return push_xattr_blob_tdb(pvfs, attr_name, fname, fd, blob);
+ }
+ return push_xattr_blob_system(pvfs, attr_name, fname, fd, blob);
+}
+
+
+/*
+ delete a xattr
+*/
+static NTSTATUS delete_xattr(struct pvfs_state *pvfs, const char *attr_name,
+ const char *fname, int fd)
+{
+ if (pvfs->ea_db) {
+ return delete_posix_eadb(pvfs, attr_name, fname, fd);
+ }
+ return delete_xattr_system(pvfs, attr_name, fname, fd);
+}
+
+/*
+ a hook called on unlink - allows the tdb xattr backend to cleanup
+*/
+NTSTATUS pvfs_xattr_unlink_hook(struct pvfs_state *pvfs, const char *fname)
+{
+ if (pvfs->ea_db) {
+ return unlink_posix_eadb(pvfs, fname);
+ }
+ return unlink_xattr_system(pvfs, fname);
+}
+
+
+/*
+ load a NDR structure from a xattr
+*/
+NTSTATUS pvfs_xattr_ndr_load(struct pvfs_state *pvfs,
+ TALLOC_CTX *mem_ctx,
+ const char *fname, int fd, const char *attr_name,
+ void *p, void *pull_fn)
+{
+ NTSTATUS status;
+ DATA_BLOB blob;
+ enum ndr_err_code ndr_err;
+
+ status = pull_xattr_blob(pvfs, mem_ctx, attr_name, fname,
+ fd, XATTR_DOSATTRIB_ESTIMATED_SIZE, &blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* pull the blob */
+ ndr_err = ndr_pull_struct_blob(&blob, mem_ctx, p,
+ (ndr_pull_flags_fn_t)pull_fn);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ data_blob_free(&blob);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ save a NDR structure into a xattr
+*/
+NTSTATUS pvfs_xattr_ndr_save(struct pvfs_state *pvfs,
+ const char *fname, int fd, const char *attr_name,
+ void *p, void *push_fn)
+{
+ TALLOC_CTX *mem_ctx = talloc_new(NULL);
+ DATA_BLOB blob;
+ NTSTATUS status;
+ enum ndr_err_code ndr_err;
+
+ ndr_err = ndr_push_struct_blob(&blob, mem_ctx, p, (ndr_push_flags_fn_t)push_fn);
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
+ talloc_free(mem_ctx);
+ return ndr_map_error2ntstatus(ndr_err);
+ }
+
+ status = push_xattr_blob(pvfs, attr_name, fname, fd, &blob);
+ talloc_free(mem_ctx);
+
+ return status;
+}
+
+
+/*
+ fill in file attributes from extended attributes
+*/
+NTSTATUS pvfs_dosattrib_load(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd)
+{
+ NTSTATUS status;
+ struct xattr_DosAttrib attrib;
+ TALLOC_CTX *mem_ctx = talloc_new(name);
+ struct xattr_DosInfo1 *info1;
+ struct xattr_DosInfo2Old *info2;
+
+ if (name->stream_name != NULL) {
+ name->stream_exists = false;
+ } else {
+ name->stream_exists = true;
+ }
+
+ if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
+ return NT_STATUS_OK;
+ }
+
+ status = pvfs_xattr_ndr_load(pvfs, mem_ctx, name->full_name,
+ fd, XATTR_DOSATTRIB_NAME,
+ &attrib,
+ (void *) ndr_pull_xattr_DosAttrib);
+
+ /* not having a DosAttrib is not an error */
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+ talloc_free(mem_ctx);
+ return pvfs_stream_info(pvfs, name, fd);
+ }
+
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(mem_ctx);
+ return status;
+ }
+
+ switch (attrib.version) {
+ case 1:
+ info1 = &attrib.info.info1;
+ name->dos.attrib = pvfs_attrib_normalise(info1->attrib,
+ name->st.st_mode);
+ name->dos.ea_size = info1->ea_size;
+ if (name->st.st_size == info1->size) {
+ name->dos.alloc_size =
+ pvfs_round_alloc_size(pvfs, info1->alloc_size);
+ }
+ if (!null_nttime(info1->create_time)) {
+ name->dos.create_time = info1->create_time;
+ }
+ if (!null_nttime(info1->change_time)) {
+ name->dos.change_time = info1->change_time;
+ }
+ name->dos.flags = 0;
+ break;
+
+ case 2:
+ /*
+ * Note: This is only used to parse existing values from disk
+ * We use xattr_DosInfo1 again for storing new values
+ */
+ info2 = &attrib.info.oldinfo2;
+ name->dos.attrib = pvfs_attrib_normalise(info2->attrib,
+ name->st.st_mode);
+ name->dos.ea_size = info2->ea_size;
+ if (name->st.st_size == info2->size) {
+ name->dos.alloc_size =
+ pvfs_round_alloc_size(pvfs, info2->alloc_size);
+ }
+ if (!null_nttime(info2->create_time)) {
+ name->dos.create_time = info2->create_time;
+ }
+ if (!null_nttime(info2->change_time)) {
+ name->dos.change_time = info2->change_time;
+ }
+ name->dos.flags = info2->flags;
+ break;
+
+ default:
+ DEBUG(0,("ERROR: Unsupported xattr DosAttrib version %d on '%s'\n",
+ attrib.version, name->full_name));
+ talloc_free(mem_ctx);
+ return NT_STATUS_INVALID_LEVEL;
+ }
+ talloc_free(mem_ctx);
+
+ status = pvfs_stream_info(pvfs, name, fd);
+
+ return status;
+}
+
+
+/*
+ save the file attribute into the xattr
+*/
+NTSTATUS pvfs_dosattrib_save(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd)
+{
+ struct xattr_DosAttrib attrib;
+ struct xattr_DosInfo1 *info1;
+
+ if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
+ return NT_STATUS_OK;
+ }
+
+ attrib.version = 1;
+ info1 = &attrib.info.info1;
+
+ name->dos.attrib = pvfs_attrib_normalise(name->dos.attrib, name->st.st_mode);
+
+ info1->attrib = name->dos.attrib;
+ info1->ea_size = name->dos.ea_size;
+ info1->size = name->st.st_size;
+ info1->alloc_size = name->dos.alloc_size;
+ info1->create_time = name->dos.create_time;
+ info1->change_time = name->dos.change_time;
+
+ return pvfs_xattr_ndr_save(pvfs, name->full_name, fd,
+ XATTR_DOSATTRIB_NAME, &attrib,
+ (void *) ndr_push_xattr_DosAttrib);
+}
+
+
+/*
+ load the set of DOS EAs
+*/
+NTSTATUS pvfs_doseas_load(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd,
+ struct xattr_DosEAs *eas)
+{
+ NTSTATUS status;
+ ZERO_STRUCTP(eas);
+
+ if (name->stream_name) {
+ /* We don't support EAs on streams */
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
+ return NT_STATUS_OK;
+ }
+ status = pvfs_xattr_ndr_load(pvfs, eas, name->full_name, fd, XATTR_DOSEAS_NAME,
+ eas, (void *) ndr_pull_xattr_DosEAs);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+ return NT_STATUS_OK;
+ }
+ return status;
+}
+
+/*
+ save the set of DOS EAs
+*/
+NTSTATUS pvfs_doseas_save(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd,
+ struct xattr_DosEAs *eas)
+{
+ if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
+ return NT_STATUS_OK;
+ }
+ return pvfs_xattr_ndr_save(pvfs, name->full_name, fd, XATTR_DOSEAS_NAME, eas,
+ (void *) ndr_push_xattr_DosEAs);
+}
+
+
+/*
+ load the set of streams from extended attributes
+*/
+NTSTATUS pvfs_streams_load(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd,
+ struct xattr_DosStreams *streams)
+{
+ NTSTATUS status;
+ ZERO_STRUCTP(streams);
+ if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
+ return NT_STATUS_OK;
+ }
+ status = pvfs_xattr_ndr_load(pvfs, streams, name->full_name, fd,
+ XATTR_DOSSTREAMS_NAME,
+ streams,
+ (void *) ndr_pull_xattr_DosStreams);
+ if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
+ return NT_STATUS_OK;
+ }
+ return status;
+}
+
+/*
+ save the set of streams into filesystem xattr
+*/
+NTSTATUS pvfs_streams_save(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd,
+ struct xattr_DosStreams *streams)
+{
+ if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
+ return NT_STATUS_OK;
+ }
+ return pvfs_xattr_ndr_save(pvfs, name->full_name, fd,
+ XATTR_DOSSTREAMS_NAME,
+ streams,
+ (void *) ndr_push_xattr_DosStreams);
+}
+
+
+/*
+ load the current ACL from extended attributes
+*/
+NTSTATUS pvfs_acl_load(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd,
+ struct xattr_NTACL *acl)
+{
+ NTSTATUS status;
+ ZERO_STRUCTP(acl);
+ if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
+ return NT_STATUS_NOT_FOUND;
+ }
+ status = pvfs_xattr_ndr_load(pvfs, acl, name->full_name, fd,
+ XATTR_NTACL_NAME,
+ acl,
+ (void *) ndr_pull_xattr_NTACL);
+ return status;
+}
+
+/*
+ save the acl for a file into filesystem xattr
+*/
+NTSTATUS pvfs_acl_save(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd,
+ struct xattr_NTACL *acl)
+{
+ NTSTATUS status;
+ void *privs;
+
+ if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
+ return NT_STATUS_OK;
+ }
+
+ /* this xattr is in the "system" namespace, so we need
+ admin privileges to set it */
+ privs = root_privileges();
+ status = pvfs_xattr_ndr_save(pvfs, name->full_name, fd,
+ XATTR_NTACL_NAME,
+ acl,
+ (void *) ndr_push_xattr_NTACL);
+ talloc_free(privs);
+ return status;
+}
+
+/*
+ create a zero length xattr with the given name
+*/
+NTSTATUS pvfs_xattr_create(struct pvfs_state *pvfs,
+ const char *fname, int fd,
+ const char *attr_prefix,
+ const char *attr_name)
+{
+ NTSTATUS status;
+ DATA_BLOB blob = data_blob(NULL, 0);
+ char *aname = talloc_asprintf(NULL, "%s%s", attr_prefix, attr_name);
+ if (aname == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ status = push_xattr_blob(pvfs, aname, fname, fd, &blob);
+ talloc_free(aname);
+ return status;
+}
+
+
+/*
+ delete a xattr with the given name
+*/
+NTSTATUS pvfs_xattr_delete(struct pvfs_state *pvfs,
+ const char *fname, int fd,
+ const char *attr_prefix,
+ const char *attr_name)
+{
+ NTSTATUS status;
+ char *aname = talloc_asprintf(NULL, "%s%s", attr_prefix, attr_name);
+ if (aname == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ status = delete_xattr(pvfs, aname, fname, fd);
+ talloc_free(aname);
+ return status;
+}
+
+/*
+ load a xattr with the given name
+*/
+NTSTATUS pvfs_xattr_load(struct pvfs_state *pvfs,
+ TALLOC_CTX *mem_ctx,
+ const char *fname, int fd,
+ const char *attr_prefix,
+ const char *attr_name,
+ size_t estimated_size,
+ DATA_BLOB *blob)
+{
+ NTSTATUS status;
+ char *aname = talloc_asprintf(mem_ctx, "%s%s", attr_prefix, attr_name);
+ if (aname == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ status = pull_xattr_blob(pvfs, mem_ctx, aname, fname, fd, estimated_size, blob);
+ talloc_free(aname);
+ return status;
+}
+
+/*
+ save a xattr with the given name
+*/
+NTSTATUS pvfs_xattr_save(struct pvfs_state *pvfs,
+ const char *fname, int fd,
+ const char *attr_prefix,
+ const char *attr_name,
+ const DATA_BLOB *blob)
+{
+ NTSTATUS status;
+ char *aname = talloc_asprintf(NULL, "%s%s", attr_prefix, attr_name);
+ if (aname == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ status = push_xattr_blob(pvfs, aname, fname, fd, blob);
+ talloc_free(aname);
+ return status;
+}
+
+
+/*
+ probe for system support for xattrs
+*/
+void pvfs_xattr_probe(struct pvfs_state *pvfs)
+{
+ TALLOC_CTX *tmp_ctx = talloc_new(pvfs);
+ DATA_BLOB blob;
+ pull_xattr_blob(pvfs, tmp_ctx, "user.XattrProbe", pvfs->base_directory,
+ -1, 1, &blob);
+ pull_xattr_blob(pvfs, tmp_ctx, "security.XattrProbe", pvfs->base_directory,
+ -1, 1, &blob);
+ talloc_free(tmp_ctx);
+}
diff --git a/source4/ntvfs/posix/python/pyposix_eadb.c b/source4/ntvfs/posix/python/pyposix_eadb.c
new file mode 100644
index 0000000..aca92e7
--- /dev/null
+++ b/source4/ntvfs/posix/python/pyposix_eadb.c
@@ -0,0 +1,140 @@
+/*
+ Unix SMB/CIFS implementation. Xattr manipulation bindings.
+ Copyright (C) Matthieu Patou <mat@matws.net> 2009-2010
+ Base on work of pyglue.c by Jelmer Vernooij <jelmer@samba.org> 2007 and
+ Matthias Dieter Wallnöfer 2009
+
+ 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 "lib/replace/system/python.h"
+#include "python/py3compat.h"
+#include "includes.h"
+#include "system/filesys.h"
+#include <tdb.h>
+#include "lib/tdb_wrap/tdb_wrap.h"
+#include "librpc/ndr/libndr.h"
+#include "ntvfs/posix/posix_eadb.h"
+#include "libcli/util/pyerrors.h"
+#include "param/pyparam.h"
+
+static PyObject *py_is_xattr_supported(PyObject *self,
+ PyObject *Py_UNUSED(ignored))
+{
+ Py_RETURN_TRUE;
+}
+
+static PyObject *py_wrap_setxattr(PyObject *self, PyObject *args)
+{
+ char *filename, *attribute, *tdbname;
+ DATA_BLOB blob;
+ Py_ssize_t blobsize;
+ NTSTATUS status;
+ TALLOC_CTX *mem_ctx;
+ struct tdb_wrap *eadb;
+
+ if (!PyArg_ParseTuple(args, "sss"PYARG_BYTES_LEN, &tdbname, &filename, &attribute,
+ &blob.data, &blobsize))
+ return NULL;
+
+ blob.length = blobsize;
+ mem_ctx = talloc_new(NULL);
+ eadb = tdb_wrap_open(
+ mem_ctx, tdbname, 50000,
+ lpcfg_tdb_flags(py_default_loadparm_context(mem_ctx),
+ TDB_DEFAULT),
+ O_RDWR|O_CREAT, 0600);
+
+ if (eadb == NULL) {
+ PyErr_SetFromErrno(PyExc_IOError);
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+ status = push_xattr_blob_tdb_raw(eadb, attribute, filename, -1,
+ &blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ PyErr_SetNTSTATUS(status);
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+ talloc_free(mem_ctx);
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_wrap_getxattr(PyObject *self, PyObject *args)
+{
+ char *filename, *attribute, *tdbname;
+ TALLOC_CTX *mem_ctx;
+ DATA_BLOB blob;
+ PyObject *ret;
+ NTSTATUS status;
+ struct tdb_wrap *eadb = NULL;
+
+ if (!PyArg_ParseTuple(args, "sss", &tdbname, &filename, &attribute))
+ return NULL;
+
+ mem_ctx = talloc_new(NULL);
+ eadb = tdb_wrap_open(
+ mem_ctx, tdbname, 50000,
+ lpcfg_tdb_flags(py_default_loadparm_context(mem_ctx),
+ TDB_DEFAULT),
+ O_RDWR|O_CREAT, 0600);
+ if (eadb == NULL) {
+ PyErr_SetFromErrno(PyExc_IOError);
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+ status = pull_xattr_blob_tdb_raw(eadb, mem_ctx, attribute, filename,
+ -1, 100, &blob);
+ if (!NT_STATUS_IS_OK(status)) {
+ PyErr_SetNTSTATUS(status);
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+ ret = Py_BuildValue(PYARG_BYTES_LEN, blob.data, blob.length);
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+static PyMethodDef py_posix_eadb_methods[] = {
+ { "wrap_getxattr", (PyCFunction)py_wrap_getxattr, METH_VARARGS,
+ "wrap_getxattr(filename,attribute) -> blob\n"
+ "Retrieve given attribute on the given file." },
+ { "wrap_setxattr", (PyCFunction)py_wrap_setxattr, METH_VARARGS,
+ "wrap_setxattr(filename,attribute,value)\n"
+ "Set the given attribute to the given value on the given file." },
+ { "is_xattr_supported", (PyCFunction)py_is_xattr_supported, METH_NOARGS,
+ "Return true if xattr are supported on this system\n"},
+ {0}
+};
+
+static struct PyModuleDef moduledef = {
+ PyModuleDef_HEAD_INIT,
+ .m_name = "posix_eadb",
+ .m_doc = "Python bindings for xattr manipulation.",
+ .m_size = -1,
+ .m_methods = py_posix_eadb_methods,
+};
+
+MODULE_INIT_FUNC(posix_eadb)
+{
+ PyObject *m;
+
+ m = PyModule_Create(&moduledef);
+
+ if (m == NULL)
+ return NULL;
+
+ return m;
+}
diff --git a/source4/ntvfs/posix/python/pyxattr_native.c b/source4/ntvfs/posix/python/pyxattr_native.c
new file mode 100644
index 0000000..6b237b0
--- /dev/null
+++ b/source4/ntvfs/posix/python/pyxattr_native.c
@@ -0,0 +1,129 @@
+/*
+ Unix SMB/CIFS implementation. Xattr manipulation bindings.
+ Copyright (C) Matthieu Patou <mat@matws.net> 2009
+ Base on work of pyglue.c by Jelmer Vernooij <jelmer@samba.org> 2007 and
+ Matthias Dieter Wallnöfer 2009
+
+ 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 "lib/replace/system/python.h"
+#include "python/py3compat.h"
+#include "includes.h"
+#include "librpc/ndr/libndr.h"
+#include "system/filesys.h"
+#include "lib/util/base64.h"
+
+static PyObject *py_is_xattr_supported(PyObject *self,
+ PyObject *Py_UNUSED(ignored))
+{
+#if !defined(HAVE_XATTR_SUPPORT)
+ Py_RETURN_FALSE;
+#else
+ Py_RETURN_TRUE;
+#endif
+}
+
+static PyObject *py_wrap_setxattr(PyObject *self, PyObject *args)
+{
+ char *filename, *attribute;
+ int ret = 0;
+ Py_ssize_t blobsize;
+ DATA_BLOB blob;
+
+ if (!PyArg_ParseTuple(args, "ss"PYARG_BYTES_LEN, &filename, &attribute, &blob.data,
+ &blobsize))
+ return NULL;
+
+ blob.length = blobsize;
+ ret = setxattr(filename, attribute, blob.data, blob.length, 0);
+ if( ret < 0 ) {
+ if (errno == ENOTSUP) {
+ PyErr_SetFromErrno(PyExc_IOError);
+ } else {
+ PyErr_SetFromErrnoWithFilename(PyExc_OSError, filename);
+ }
+ return NULL;
+ }
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_wrap_getxattr(PyObject *self, PyObject *args)
+{
+ char *filename, *attribute;
+ int len;
+ TALLOC_CTX *mem_ctx;
+ char *buf;
+ PyObject *ret;
+ if (!PyArg_ParseTuple(args, "ss", &filename, &attribute))
+ return NULL;
+ mem_ctx = talloc_new(NULL);
+ len = getxattr(filename,attribute,NULL,0);
+ if( len < 0 ) {
+ if (errno == ENOTSUP) {
+ PyErr_SetFromErrno(PyExc_IOError);
+ } else {
+ PyErr_SetFromErrnoWithFilename(PyExc_OSError, filename);
+ }
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+ /* check length ... */
+ buf = talloc_zero_array(mem_ctx, char, len);
+ len = getxattr(filename, attribute, buf, len);
+ if( len < 0 ) {
+ if (errno == ENOTSUP) {
+ PyErr_SetFromErrno(PyExc_IOError);
+ } else {
+ PyErr_SetFromErrnoWithFilename(PyExc_OSError, filename);
+ }
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+ ret = Py_BuildValue(PYARG_BYTES_LEN, buf, len);
+ talloc_free(mem_ctx);
+ return ret;
+}
+
+static PyMethodDef py_xattr_methods[] = {
+ { "wrap_getxattr", (PyCFunction)py_wrap_getxattr, METH_VARARGS,
+ "wrap_getxattr(filename,attribute) -> blob\n"
+ "Retrieve given attribute on the given file." },
+ { "wrap_setxattr", (PyCFunction)py_wrap_setxattr, METH_VARARGS,
+ "wrap_setxattr(filename,attribute,value)\n"
+ "Set the given attribute to the given value on the given file." },
+ { "is_xattr_supported", (PyCFunction)py_is_xattr_supported, METH_NOARGS,
+ "Return true if xattr are supported on this system\n"},
+ {0}
+};
+
+static struct PyModuleDef moduledef = {
+ PyModuleDef_HEAD_INIT,
+ .m_name = "xattr_native",
+ .m_doc = "Python bindings for xattr manipulation.",
+ .m_size = -1,
+ .m_methods = py_xattr_methods,
+};
+
+MODULE_INIT_FUNC(xattr_native)
+{
+ PyObject *m;
+
+ m = PyModule_Create(&moduledef);
+
+ if (m == NULL)
+ return NULL;
+
+ return m;
+}
diff --git a/source4/ntvfs/posix/python/pyxattr_tdb.c b/source4/ntvfs/posix/python/pyxattr_tdb.c
new file mode 100644
index 0000000..5f07809
--- /dev/null
+++ b/source4/ntvfs/posix/python/pyxattr_tdb.c
@@ -0,0 +1,177 @@
+/*
+ Unix SMB/CIFS implementation. Xattr manipulation bindings.
+ Copyright (C) Matthieu Patou <mat@matws.net> 2009-2010
+ Base on work of pyglue.c by Jelmer Vernooij <jelmer@samba.org> 2007 and
+ Matthias Dieter Wallnöfer 2009
+
+ 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 "lib/replace/system/python.h"
+#include "python/py3compat.h"
+#include "includes.h"
+#include "system/filesys.h"
+#include <tdb.h>
+#include "lib/tdb_wrap/tdb_wrap.h"
+#include "librpc/ndr/libndr.h"
+#include "ntvfs/posix/posix_eadb.h"
+#include "libcli/util/pyerrors.h"
+#include "param/pyparam.h"
+#include "lib/dbwrap/dbwrap.h"
+#include "lib/dbwrap/dbwrap_open.h"
+#include "lib/dbwrap/dbwrap_tdb.h"
+#include "source3/lib/xattr_tdb.h"
+
+static PyObject *py_is_xattr_supported(PyObject *self,
+ PyObject *Py_UNUSED(ignored))
+{
+ Py_RETURN_TRUE;
+}
+
+static PyObject *py_wrap_setxattr(PyObject *self, PyObject *args)
+{
+ char *filename, *attribute, *tdbname;
+ DATA_BLOB blob;
+ Py_ssize_t blobsize;
+ int ret;
+ TALLOC_CTX *mem_ctx;
+ struct loadparm_context *lp_ctx;
+ struct db_context *eadb = NULL;
+ struct file_id id;
+ struct stat sbuf;
+
+ if (!PyArg_ParseTuple(args, "sss"PYARG_BYTES_LEN, &tdbname, &filename, &attribute,
+ &blob.data, &blobsize))
+ return NULL;
+
+ blob.length = blobsize;
+ mem_ctx = talloc_new(NULL);
+
+ lp_ctx = py_default_loadparm_context(mem_ctx);
+ eadb = db_open_tdb(mem_ctx, tdbname, 50000,
+ lpcfg_tdb_flags(lp_ctx, TDB_DEFAULT),
+ O_RDWR|O_CREAT, 0600, DBWRAP_LOCK_ORDER_2,
+ DBWRAP_FLAG_NONE);
+
+ if (eadb == NULL) {
+ PyErr_SetFromErrno(PyExc_IOError);
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+
+ ret = stat(filename, &sbuf);
+ if (ret < 0) {
+ PyErr_SetFromErrno(PyExc_IOError);
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+
+ ZERO_STRUCT(id);
+ id.devid = sbuf.st_dev;
+ id.inode = sbuf.st_ino;
+
+ ret = xattr_tdb_setattr(eadb, &id, attribute, blob.data, blob.length, 0);
+ if (ret < 0) {
+ PyErr_SetFromErrno(PyExc_TypeError);
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+ talloc_free(mem_ctx);
+ Py_RETURN_NONE;
+}
+
+static PyObject *py_wrap_getxattr(PyObject *self, PyObject *args)
+{
+ char *filename, *attribute, *tdbname;
+ TALLOC_CTX *mem_ctx;
+ struct loadparm_context *lp_ctx;
+ DATA_BLOB blob;
+ PyObject *ret_obj;
+ int ret;
+ ssize_t xattr_size;
+ struct db_context *eadb = NULL;
+ struct file_id id;
+ struct stat sbuf;
+
+ if (!PyArg_ParseTuple(args, "sss", &tdbname, &filename, &attribute))
+ return NULL;
+
+ mem_ctx = talloc_new(NULL);
+
+ lp_ctx = py_default_loadparm_context(mem_ctx);
+ eadb = db_open_tdb(mem_ctx, tdbname, 50000,
+ lpcfg_tdb_flags(lp_ctx, TDB_DEFAULT),
+ O_RDWR|O_CREAT, 0600, DBWRAP_LOCK_ORDER_2,
+ DBWRAP_FLAG_NONE);
+
+ if (eadb == NULL) {
+ PyErr_SetFromErrno(PyExc_IOError);
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+
+ ret = stat(filename, &sbuf);
+ if (ret < 0) {
+ PyErr_SetFromErrno(PyExc_IOError);
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+
+ ZERO_STRUCT(id);
+ id.devid = sbuf.st_dev;
+ id.inode = sbuf.st_ino;
+
+ xattr_size = xattr_tdb_getattr(eadb, mem_ctx, &id, attribute, &blob);
+ if (xattr_size < 0) {
+ PyErr_SetFromErrno(PyExc_TypeError);
+ talloc_free(mem_ctx);
+ return NULL;
+ }
+ ret_obj = Py_BuildValue(PYARG_BYTES_LEN, blob.data, xattr_size);
+ talloc_free(mem_ctx);
+ return ret_obj;
+}
+
+static PyMethodDef py_xattr_methods[] = {
+ { "wrap_getxattr", (PyCFunction)py_wrap_getxattr, METH_VARARGS,
+ "wrap_getxattr(filename,attribute) -> blob\n"
+ "Retrieve given attribute on the given file." },
+ { "wrap_setxattr", (PyCFunction)py_wrap_setxattr, METH_VARARGS,
+ "wrap_setxattr(filename,attribute,value)\n"
+ "Set the given attribute to the given value on the given file." },
+ { "is_xattr_supported", (PyCFunction)py_is_xattr_supported, METH_NOARGS,
+ "Return true if xattr are supported on this system\n"},
+ {0}
+};
+
+static struct PyModuleDef moduledef = {
+ PyModuleDef_HEAD_INIT,
+ .m_name = "xattr_tdb",
+ .m_doc = "Python bindings for xattr manipulation.",
+ .m_size = -1,
+ .m_methods = py_xattr_methods,
+};
+
+MODULE_INIT_FUNC(xattr_tdb)
+{
+ PyObject *m;
+
+ m = PyModule_Create(&moduledef);
+
+ if (m == NULL)
+ return NULL;
+
+ return m;
+}
+
diff --git a/source4/ntvfs/posix/vfs_posix.c b/source4/ntvfs/posix/vfs_posix.c
new file mode 100644
index 0000000..8375def
--- /dev/null
+++ b/source4/ntvfs/posix/vfs_posix.c
@@ -0,0 +1,426 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ POSIX NTVFS backend
+
+ Copyright (C) Andrew Tridgell 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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/>.
+*/
+/*
+ this implements most of the POSIX NTVFS backend
+ This is the default backend
+*/
+
+#include "includes.h"
+#include "vfs_posix.h"
+#include "librpc/gen_ndr/security.h"
+#include <tdb.h>
+#include "lib/tdb_wrap/tdb_wrap.h"
+#include "libcli/security/security.h"
+#include "lib/events/events.h"
+#include "param/param.h"
+#include "lib/util/idtree.h"
+
+/*
+ setup config options for a posix share
+*/
+static void pvfs_setup_options(struct pvfs_state *pvfs)
+{
+ struct share_config *scfg = pvfs->ntvfs->ctx->config;
+ char *eadb;
+ char *xattr_backend;
+ bool def_perm_override = false;
+
+ if (share_bool_option(scfg, SHARE_MAP_HIDDEN, SHARE_MAP_HIDDEN_DEFAULT))
+ pvfs->flags |= PVFS_FLAG_MAP_HIDDEN;
+ if (share_bool_option(scfg, SHARE_MAP_ARCHIVE, SHARE_MAP_ARCHIVE_DEFAULT))
+ pvfs->flags |= PVFS_FLAG_MAP_ARCHIVE;
+ if (share_bool_option(scfg, SHARE_MAP_SYSTEM, SHARE_MAP_SYSTEM_DEFAULT))
+ pvfs->flags |= PVFS_FLAG_MAP_SYSTEM;
+ if (share_bool_option(scfg, SHARE_READONLY, SHARE_READONLY_DEFAULT))
+ pvfs->flags |= PVFS_FLAG_READONLY;
+ if (share_bool_option(scfg, SHARE_STRICT_SYNC, SHARE_STRICT_SYNC_DEFAULT))
+ pvfs->flags |= PVFS_FLAG_STRICT_SYNC;
+ if (share_bool_option(scfg, SHARE_STRICT_LOCKING, SHARE_STRICT_LOCKING_DEFAULT))
+ pvfs->flags |= PVFS_FLAG_STRICT_LOCKING;
+ if (share_bool_option(scfg, SHARE_CI_FILESYSTEM, SHARE_CI_FILESYSTEM_DEFAULT))
+ pvfs->flags |= PVFS_FLAG_CI_FILESYSTEM;
+ if (share_bool_option(scfg, PVFS_FAKE_OPLOCKS, PVFS_FAKE_OPLOCKS_DEFAULT))
+ pvfs->flags |= PVFS_FLAG_FAKE_OPLOCKS;
+
+#if defined(O_DIRECTORY) && defined(O_NOFOLLOW)
+ /* set PVFS_PERM_OVERRIDE by default only if the system
+ * supports the necessary capabilities to make it secure
+ */
+ def_perm_override = true;
+#endif
+ if (share_bool_option(scfg, PVFS_PERM_OVERRIDE, def_perm_override))
+ pvfs->flags |= PVFS_FLAG_PERM_OVERRIDE;
+
+ /* file perm options */
+ pvfs->options.create_mask = share_int_option(scfg,
+ SHARE_CREATE_MASK,
+ SHARE_CREATE_MASK_DEFAULT);
+ pvfs->options.dir_mask = share_int_option(scfg,
+ SHARE_DIR_MASK,
+ SHARE_DIR_MASK_DEFAULT);
+ pvfs->options.force_dir_mode = share_int_option(scfg,
+ SHARE_FORCE_DIR_MODE,
+ SHARE_FORCE_DIR_MODE_DEFAULT);
+ pvfs->options.force_create_mode = share_int_option(scfg,
+ SHARE_FORCE_CREATE_MODE,
+ SHARE_FORCE_CREATE_MODE_DEFAULT);
+ /* this must be a power of 2 */
+ pvfs->alloc_size_rounding = share_int_option(scfg,
+ PVFS_ALLOCATION_ROUNDING,
+ PVFS_ALLOCATION_ROUNDING_DEFAULT);
+
+ pvfs->search.inactivity_time = share_int_option(scfg,
+ PVFS_SEARCH_INACTIVITY,
+ PVFS_SEARCH_INACTIVITY_DEFAULT);
+
+#ifdef HAVE_XATTR_SUPPORT
+ if (share_bool_option(scfg, PVFS_XATTR, PVFS_XATTR_DEFAULT))
+ pvfs->flags |= PVFS_FLAG_XATTR_ENABLE;
+#endif
+
+ pvfs->sharing_violation_delay = share_int_option(scfg,
+ PVFS_SHARE_DELAY,
+ PVFS_SHARE_DELAY_DEFAULT);
+
+ pvfs->oplock_break_timeout = share_int_option(scfg,
+ PVFS_OPLOCK_TIMEOUT,
+ PVFS_OPLOCK_TIMEOUT_DEFAULT);
+
+ pvfs->writetime_delay = share_int_option(scfg,
+ PVFS_WRITETIME_DELAY,
+ PVFS_WRITETIME_DELAY_DEFAULT);
+
+ pvfs->share_name = talloc_strdup(pvfs, scfg->name);
+
+ pvfs->fs_attribs =
+ FS_ATTR_CASE_SENSITIVE_SEARCH |
+ FS_ATTR_CASE_PRESERVED_NAMES |
+ FS_ATTR_UNICODE_ON_DISK;
+
+ /* allow xattrs to be stored in a external tdb */
+ eadb = share_string_option(pvfs, scfg, PVFS_EADB, NULL);
+ if (eadb != NULL) {
+ pvfs->ea_db = tdb_wrap_open(
+ pvfs, eadb, 50000,
+ lpcfg_tdb_flags(pvfs->ntvfs->ctx->lp_ctx, TDB_DEFAULT),
+ O_RDWR|O_CREAT, 0600);
+ if (pvfs->ea_db != NULL) {
+ pvfs->flags |= PVFS_FLAG_XATTR_ENABLE;
+ } else {
+ DEBUG(0,("Failed to open eadb '%s' - %s\n",
+ eadb, strerror(errno)));
+ pvfs->flags &= ~PVFS_FLAG_XATTR_ENABLE;
+ }
+ TALLOC_FREE(eadb);
+ }
+
+ if (pvfs->flags & PVFS_FLAG_XATTR_ENABLE) {
+ pvfs->fs_attribs |= FS_ATTR_NAMED_STREAMS;
+ }
+ if (pvfs->flags & PVFS_FLAG_XATTR_ENABLE) {
+ pvfs->fs_attribs |= FS_ATTR_PERSISTANT_ACLS;
+ }
+
+ pvfs->sid_cache.creator_owner = dom_sid_parse_talloc(pvfs, SID_CREATOR_OWNER);
+ pvfs->sid_cache.creator_group = dom_sid_parse_talloc(pvfs, SID_CREATOR_GROUP);
+
+ /* check if the system really supports xattrs */
+ if (pvfs->flags & PVFS_FLAG_XATTR_ENABLE) {
+ pvfs_xattr_probe(pvfs);
+ }
+
+ /* enable an ACL backend */
+ xattr_backend = share_string_option(pvfs, scfg, PVFS_ACL, "xattr");
+ pvfs->acl_ops = pvfs_acl_backend_byname(xattr_backend);
+ TALLOC_FREE(xattr_backend);
+}
+
+static int pvfs_state_destructor(struct pvfs_state *pvfs)
+{
+ struct pvfs_file *f, *fn;
+ struct pvfs_search_state *s, *sn;
+
+ /*
+ * make sure we cleanup files and searches before anything else
+ * because there destructors need to access the pvfs_state struct
+ */
+ for (f=pvfs->files.list; f; f=fn) {
+ fn = f->next;
+ talloc_free(f);
+ }
+
+ for (s=pvfs->search.list; s; s=sn) {
+ sn = s->next;
+ talloc_free(s);
+ }
+
+ return 0;
+}
+
+/*
+ connect to a share - used when a tree_connect operation comes
+ in. For a disk based backend we needs to ensure that the base
+ directory exists (tho it doesn't need to be accessible by the user,
+ that comes later)
+*/
+static NTSTATUS pvfs_connect(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_tcon* tcon)
+{
+ struct pvfs_state *pvfs;
+ struct stat st;
+ char *base_directory;
+ NTSTATUS status;
+ const char *sharename;
+
+ switch (tcon->generic.level) {
+ case RAW_TCON_TCON:
+ sharename = tcon->tcon.in.service;
+ break;
+ case RAW_TCON_TCONX:
+ sharename = tcon->tconx.in.path;
+ break;
+ case RAW_TCON_SMB2:
+ sharename = tcon->smb2.in.path;
+ break;
+ default:
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ if (strncmp(sharename, "\\\\", 2) == 0) {
+ char *p = strchr(sharename+2, '\\');
+ if (p) {
+ sharename = p + 1;
+ }
+ }
+
+ /*
+ * TODO: call this from ntvfs_posix_init()
+ * but currently we don't have a lp_ctx there
+ */
+ status = pvfs_acl_init();
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ pvfs = talloc_zero(ntvfs, struct pvfs_state);
+ NT_STATUS_HAVE_NO_MEMORY(pvfs);
+
+ /* for simplicity of path construction, remove any trailing slash now */
+ base_directory = share_string_option(pvfs, ntvfs->ctx->config, SHARE_PATH, "");
+ NT_STATUS_HAVE_NO_MEMORY(base_directory);
+ if (strcmp(base_directory, "/") != 0) {
+ trim_string(base_directory, NULL, "/");
+ }
+
+ pvfs->ntvfs = ntvfs;
+ pvfs->base_directory = base_directory;
+
+ /* the directory must exist. Note that we deliberately don't
+ check that it is readable */
+ if (stat(pvfs->base_directory, &st) != 0 || !S_ISDIR(st.st_mode)) {
+ DEBUG(0,("pvfs_connect: '%s' is not a directory, when connecting to [%s]\n",
+ pvfs->base_directory, sharename));
+ return NT_STATUS_BAD_NETWORK_NAME;
+ }
+
+ ntvfs->ctx->fs_type = talloc_strdup(ntvfs->ctx, "NTFS");
+ NT_STATUS_HAVE_NO_MEMORY(ntvfs->ctx->fs_type);
+
+ ntvfs->ctx->dev_type = talloc_strdup(ntvfs->ctx, "A:");
+ NT_STATUS_HAVE_NO_MEMORY(ntvfs->ctx->dev_type);
+
+ if (tcon->generic.level == RAW_TCON_TCONX) {
+ tcon->tconx.out.fs_type = ntvfs->ctx->fs_type;
+ tcon->tconx.out.dev_type = ntvfs->ctx->dev_type;
+ }
+
+ ntvfs->private_data = pvfs;
+
+ pvfs->brl_context = brlock_init(pvfs,
+ pvfs->ntvfs->ctx->server_id,
+ pvfs->ntvfs->ctx->lp_ctx,
+ pvfs->ntvfs->ctx->msg_ctx);
+ if (pvfs->brl_context == NULL) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ pvfs->odb_context = odb_init(pvfs, pvfs->ntvfs->ctx);
+ if (pvfs->odb_context == NULL) {
+ return NT_STATUS_INTERNAL_DB_CORRUPTION;
+ }
+
+ /* allow this to be NULL - we just disable change notify */
+ pvfs->notify_context = notify_init(pvfs,
+ pvfs->ntvfs->ctx->server_id,
+ pvfs->ntvfs->ctx->msg_ctx,
+ pvfs->ntvfs->ctx->lp_ctx,
+ pvfs->ntvfs->ctx->event_ctx,
+ pvfs->ntvfs->ctx->config);
+
+ /* allocate the search handle -> ptr tree */
+ pvfs->search.idtree = idr_init(pvfs);
+ NT_STATUS_HAVE_NO_MEMORY(pvfs->search.idtree);
+
+ status = pvfs_mangle_init(pvfs);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ pvfs_setup_options(pvfs);
+
+ talloc_set_destructor(pvfs, pvfs_state_destructor);
+
+#ifdef SIGXFSZ
+ /* who had the stupid idea to generate a signal on a large
+ file write instead of just failing it!? */
+ BlockSignals(true, SIGXFSZ);
+#endif
+
+ return NT_STATUS_OK;
+}
+
+/*
+ disconnect from a share
+*/
+static NTSTATUS pvfs_disconnect(struct ntvfs_module_context *ntvfs)
+{
+ return NT_STATUS_OK;
+}
+
+/*
+ check if a directory exists
+*/
+static NTSTATUS pvfs_chkpath(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_chkpath *cp)
+{
+ struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
+ struct pvfs_state);
+ struct pvfs_filename *name;
+ NTSTATUS status;
+
+ /* resolve the cifs name to a posix name */
+ status = pvfs_resolve_name(pvfs, req, cp->chkpath.in.path, 0, &name);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ if (!name->exists) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ if (!S_ISDIR(name->st.st_mode)) {
+ return NT_STATUS_NOT_A_DIRECTORY;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ copy a set of files
+*/
+static NTSTATUS pvfs_copy(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, struct smb_copy *cp)
+{
+ DEBUG(0,("pvfs_copy not implemented\n"));
+ return NT_STATUS_NOT_SUPPORTED;
+}
+
+/*
+ return print queue info
+*/
+static NTSTATUS pvfs_lpq(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_lpq *lpq)
+{
+ return NT_STATUS_NOT_SUPPORTED;
+}
+
+/* SMBtrans - not used on file shares */
+static NTSTATUS pvfs_trans(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, struct smb_trans2 *trans2)
+{
+ return NT_STATUS_ACCESS_DENIED;
+}
+
+/*
+ initialise the POSIX disk backend, registering ourselves with the ntvfs subsystem
+ */
+NTSTATUS ntvfs_posix_init(TALLOC_CTX *ctx)
+{
+ NTSTATUS ret;
+ struct ntvfs_ops ops;
+ NTVFS_CURRENT_CRITICAL_SIZES(vers);
+
+ ZERO_STRUCT(ops);
+
+ ops.type = NTVFS_DISK;
+
+ /* fill in all the operations */
+ ops.connect_fn = pvfs_connect;
+ ops.disconnect_fn = pvfs_disconnect;
+ ops.unlink_fn = pvfs_unlink;
+ ops.chkpath_fn = pvfs_chkpath;
+ ops.qpathinfo_fn = pvfs_qpathinfo;
+ ops.setpathinfo_fn = pvfs_setpathinfo;
+ ops.open_fn = pvfs_open;
+ ops.mkdir_fn = pvfs_mkdir;
+ ops.rmdir_fn = pvfs_rmdir;
+ ops.rename_fn = pvfs_rename;
+ ops.copy_fn = pvfs_copy;
+ ops.ioctl_fn = pvfs_ioctl;
+ ops.read_fn = pvfs_read;
+ ops.write_fn = pvfs_write;
+ ops.seek_fn = pvfs_seek;
+ ops.flush_fn = pvfs_flush;
+ ops.close_fn = pvfs_close;
+ ops.exit_fn = pvfs_exit;
+ ops.lock_fn = pvfs_lock;
+ ops.setfileinfo_fn = pvfs_setfileinfo;
+ ops.qfileinfo_fn = pvfs_qfileinfo;
+ ops.fsinfo_fn = pvfs_fsinfo;
+ ops.lpq_fn = pvfs_lpq;
+ ops.search_first_fn = pvfs_search_first;
+ ops.search_next_fn = pvfs_search_next;
+ ops.search_close_fn = pvfs_search_close;
+ ops.trans_fn = pvfs_trans;
+ ops.logoff_fn = pvfs_logoff;
+ ops.async_setup_fn = pvfs_async_setup;
+ ops.cancel_fn = pvfs_cancel;
+ ops.notify_fn = pvfs_notify;
+
+ /* register ourselves with the NTVFS subsystem. We register
+ under the name 'default' as we wish to be the default
+ backend, and also register as 'posix' */
+ ops.name = "default";
+ ret = ntvfs_register(&ops, &vers);
+
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(0,("Failed to register POSIX backend as '%s'!\n", ops.name));
+ }
+
+ ops.name = "posix";
+ ret = ntvfs_register(&ops, &vers);
+
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(0,("Failed to register POSIX backend as '%s'!\n", ops.name));
+ }
+
+ if (NT_STATUS_IS_OK(ret)) {
+ ret = ntvfs_common_init();
+ }
+
+ return ret;
+}
diff --git a/source4/ntvfs/posix/vfs_posix.h b/source4/ntvfs/posix/vfs_posix.h
new file mode 100644
index 0000000..3dbd785
--- /dev/null
+++ b/source4/ntvfs/posix/vfs_posix.h
@@ -0,0 +1,290 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ POSIX NTVFS backend - structure definitions
+
+ Copyright (C) Andrew Tridgell 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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/>.
+*/
+
+#ifndef _VFS_POSIX_H_
+#define _VFS_POSIX_H_
+
+#include "librpc/gen_ndr/xattr.h"
+#include "system/filesys.h"
+#include "ntvfs/ntvfs.h"
+#include "ntvfs/common/ntvfs_common.h"
+#include "libcli/wbclient/wbclient.h"
+#include "lib/events/events.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 */
+struct pvfs_state {
+ struct ntvfs_module_context *ntvfs;
+ const char *base_directory;
+ struct GUID *base_fs_uuid;
+
+ const char *share_name;
+ unsigned int flags;
+
+ struct pvfs_mangle_context *mangle_ctx;
+
+ struct brl_context *brl_context;
+ struct odb_context *odb_context;
+ struct notify_context *notify_context;
+
+ /* a list of pending async requests. Needed to support
+ ntcancel */
+ struct pvfs_wait *wait_list;
+
+ /* the sharing violation timeout (nsecs) */
+ unsigned int sharing_violation_delay;
+
+ /* the oplock break timeout (secs) */
+ unsigned int oplock_break_timeout;
+
+ /* the write time update delay (nsecs) */
+ unsigned int writetime_delay;
+
+ /* filesystem attributes (see FS_ATTR_*) */
+ uint32_t fs_attribs;
+
+ /* if posix:eadb is set, then this gets setup */
+ struct tdb_wrap *ea_db;
+
+ /* the allocation size rounding */
+ uint32_t alloc_size_rounding;
+
+ struct {
+ /* the open files as DLINKLIST */
+ struct pvfs_file *list;
+ } files;
+
+ struct {
+ /* an id tree mapping open search ID to a pvfs_search_state structure */
+ struct idr_context *idtree;
+
+ /* the open searches as DLINKLIST */
+ struct pvfs_search_state *list;
+
+ /* how long to keep inactive searches around for */
+ unsigned int inactivity_time;
+ } search;
+
+ /* used to accelerate acl mapping */
+ struct {
+ const struct dom_sid *creator_owner;
+ const struct dom_sid *creator_group;
+ } sid_cache;
+
+ /* the acl backend */
+ const struct pvfs_acl_ops *acl_ops;
+
+ /* non-flag share options */
+ struct {
+ mode_t dir_mask;
+ mode_t force_dir_mode;
+ mode_t create_mask;
+ mode_t force_create_mode;
+ } options;
+};
+
+/* this is the basic information needed about a file from the filesystem */
+struct pvfs_dos_fileinfo {
+ NTTIME create_time;
+ NTTIME access_time;
+ NTTIME write_time;
+ NTTIME change_time;
+ uint32_t attrib;
+ uint64_t alloc_size;
+ uint32_t nlink;
+ uint32_t ea_size;
+ uint64_t file_id;
+ uint32_t flags;
+};
+
+/*
+ this is the structure returned by pvfs_resolve_name(). It holds the posix details of
+ a filename passed by the client to any function
+*/
+struct pvfs_filename {
+ char *original_name;
+ char *full_name;
+ char *stream_name; /* does not include :$DATA suffix */
+ uint32_t stream_id; /* this uses a hash, so is probabilistic */
+ bool has_wildcard;
+ bool exists; /* true if the base filename exists */
+ bool stream_exists; /* true if the stream exists */
+ bool allow_override;
+ struct stat st;
+ struct pvfs_dos_fileinfo dos;
+};
+
+
+/* open file handle state - encapsulates the posix fd
+
+ Note that this is separated from the pvfs_file structure in order
+ to cope with the openx DENY_DOS semantics where a 2nd DENY_DOS open
+ on the same connection gets the same low level filesystem handle,
+ rather than a new handle
+*/
+struct pvfs_file_handle {
+ int fd;
+
+ struct pvfs_filename *name;
+
+ /* a unique file key to be used for open file locking */
+ DATA_BLOB odb_locking_key;
+
+ uint32_t create_options;
+
+ /* this is set by the mode_information level. What does it do? */
+ uint32_t mode;
+
+ /* yes, we need 2 independent positions ... */
+ uint64_t seek_offset;
+ uint64_t position;
+
+ 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;
+
+ struct {
+ bool update_triggered;
+ struct tevent_timer *update_event;
+ bool update_on_close;
+ NTTIME close_time;
+ bool update_forced;
+ } write_time;
+
+ /* the open went through to completion */
+ bool open_completed;
+
+ uint8_t private_flags;
+};
+
+/* open file state */
+struct pvfs_file {
+ struct pvfs_file *next, *prev;
+ struct pvfs_file_handle *handle;
+ struct ntvfs_handle *ntvfs;
+
+ struct pvfs_state *pvfs;
+
+ uint32_t impersonation;
+ uint32_t share_access;
+ uint32_t access_mask;
+
+ /* a list of pending locks - used for locking cancel operations */
+ struct pvfs_pending_lock *pending_list;
+
+ /* a file handle to be used for byte range locking */
+ struct brl_handle *brl_handle;
+
+ /* a count of active locks - used to avoid calling brlock_close on
+ file close */
+ uint64_t lock_count;
+
+ /* for directories, a buffer of pending notify events */
+ struct pvfs_notify_buffer *notify_buffer;
+
+ /* for directories, the state of an incomplete SMB2 Find */
+ struct pvfs_search_state *search;
+};
+
+/* the state of a search started with pvfs_search_first() */
+struct pvfs_search_state {
+ struct pvfs_search_state *prev, *next;
+ struct pvfs_state *pvfs;
+ uint16_t handle;
+ off_t current_index;
+ uint16_t search_attrib;
+ uint16_t must_attrib;
+ struct pvfs_dir *dir;
+ time_t last_used; /* monotonic clock time */
+ unsigned int num_ea_names;
+ struct ea_name *ea_names;
+ struct tevent_timer *te;
+};
+
+/* flags to pvfs_resolve_name() */
+#define PVFS_RESOLVE_WILDCARD (1<<0)
+#define PVFS_RESOLVE_STREAMS (1<<1)
+#define PVFS_RESOLVE_NO_OPENDB (1<<2)
+
+/* flags in pvfs->flags */
+#define PVFS_FLAG_CI_FILESYSTEM (1<<0) /* the filesystem is case insensitive */
+#define PVFS_FLAG_MAP_ARCHIVE (1<<1)
+#define PVFS_FLAG_MAP_SYSTEM (1<<2)
+#define PVFS_FLAG_MAP_HIDDEN (1<<3)
+#define PVFS_FLAG_READONLY (1<<4)
+#define PVFS_FLAG_STRICT_SYNC (1<<5)
+#define PVFS_FLAG_STRICT_LOCKING (1<<6)
+#define PVFS_FLAG_XATTR_ENABLE (1<<7)
+#define PVFS_FLAG_FAKE_OPLOCKS (1<<8)
+#define PVFS_FLAG_PERM_OVERRIDE (1<<10)
+
+/* forward declare some anonymous structures */
+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_WRITETIME_DELAY "posix:writetimeupdatedelay"
+#define PVFS_ALLOCATION_ROUNDING "posix:allocationrounding"
+#define PVFS_SEARCH_INACTIVITY "posix:searchinactivity"
+#define PVFS_ACL "posix:acl"
+#define PVFS_PERM_OVERRIDE "posix:permission override"
+
+#define PVFS_XATTR_DEFAULT true
+#define PVFS_FAKE_OPLOCKS_DEFAULT false
+#define PVFS_SHARE_DELAY_DEFAULT 1000000 /* nsecs */
+#define PVFS_OPLOCK_TIMEOUT_DEFAULT 30 /* secs */
+#define PVFS_WRITETIME_DELAY_DEFAULT 2000000 /* nsecs */
+#define PVFS_ALLOCATION_ROUNDING_DEFAULT 512
+#define PVFS_SEARCH_INACTIVITY_DEFAULT 300
+
+struct pvfs_acl_ops {
+ const char *name;
+ NTSTATUS (*acl_load)(struct pvfs_state *, struct pvfs_filename *, int , TALLOC_CTX *,
+ struct security_descriptor **);
+ NTSTATUS (*acl_save)(struct pvfs_state *, struct pvfs_filename *, int , struct security_descriptor *);
+};
+
+#include "ntvfs/posix/vfs_posix_proto.h"
+#include "ntvfs/posix/vfs_acl_proto.h"
+
+#endif /* _VFS_POSIX_H_ */
diff --git a/source4/ntvfs/posix/wscript_build b/source4/ntvfs/posix/wscript_build
new file mode 100644
index 0000000..649dea6
--- /dev/null
+++ b/source4/ntvfs/posix/wscript_build
@@ -0,0 +1,61 @@
+#!/usr/bin/env python
+
+if bld.CONFIG_SET('WITH_NTVFS_FILESERVER'):
+ bld.SAMBA_SUBSYSTEM('pvfs_acl',
+ source='pvfs_acl.c',
+ autoproto='vfs_acl_proto.h',
+ deps='events samba-modules',
+ )
+
+
+ bld.SAMBA_MODULE('pvfs_acl_xattr',
+ source='pvfs_acl_xattr.c',
+ subsystem='pvfs_acl',
+ init_function='pvfs_acl_xattr_init',
+ deps='NDR_XATTR events'
+ )
+
+
+ bld.SAMBA_MODULE('pvfs_acl_nfs4',
+ source='pvfs_acl_nfs4.c',
+ subsystem='pvfs_acl',
+ init_function='pvfs_acl_nfs4_init',
+ deps='NDR_NFS4ACL samdb events'
+ )
+
+
+ bld.SAMBA_MODULE('ntvfs_posix',
+ source='vfs_posix.c pvfs_util.c pvfs_search.c pvfs_dirlist.c pvfs_fileinfo.c pvfs_unlink.c pvfs_mkdir.c pvfs_open.c pvfs_read.c pvfs_flush.c pvfs_write.c pvfs_fsinfo.c pvfs_qfileinfo.c pvfs_setfileinfo.c pvfs_rename.c pvfs_resolve.c pvfs_shortname.c pvfs_lock.c pvfs_oplock.c pvfs_wait.c pvfs_seek.c pvfs_ioctl.c pvfs_xattr.c pvfs_streams.c pvfs_notify.c pvfs_sys.c xattr_system.c',
+ autoproto='vfs_posix_proto.h',
+ subsystem='ntvfs',
+ init_function='ntvfs_posix_init',
+ deps='NDR_XATTR attr ntvfs_common MESSAGING LIBWBCLIENT_OLD pvfs_acl posix_eadb',
+ internal_module=True
+ )
+
+
+bld.SAMBA_LIBRARY('posix_eadb',
+ source='posix_eadb.c',
+ deps='tdb tdb-wrap samba-util',
+ autoproto='posix_eadb_proto.h',
+ private_library=True)
+
+pyparam_util = bld.pyembed_libname('pyparam_util')
+
+bld.SAMBA_PYTHON('python_xattr_native',
+ source='python/pyxattr_native.c',
+ deps='ndr ldb samdb samba-credentials %s attr' % pyparam_util,
+ realname='samba/xattr_native.so'
+ )
+
+bld.SAMBA_PYTHON('python_posix_eadb',
+ source='python/pyposix_eadb.c',
+ deps='%s posix_eadb tdb' % pyparam_util,
+ realname='samba/posix_eadb.so'
+ )
+
+bld.SAMBA_PYTHON('python_xattr_tdb',
+ source='python/pyxattr_tdb.c',
+ deps='%s xattr_tdb' % pyparam_util,
+ realname='samba/xattr_tdb.so'
+ )
diff --git a/source4/ntvfs/posix/xattr_system.c b/source4/ntvfs/posix/xattr_system.c
new file mode 100644
index 0000000..ebb2010
--- /dev/null
+++ b/source4/ntvfs/posix/xattr_system.c
@@ -0,0 +1,145 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ POSIX NTVFS backend - xattr support using filesystem xattrs
+
+ Copyright (C) Andrew Tridgell 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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 "vfs_posix.h"
+
+/*
+ pull a xattr as a blob, from either a file or a file descriptor
+*/
+NTSTATUS pull_xattr_blob_system(struct pvfs_state *pvfs,
+ TALLOC_CTX *mem_ctx,
+ const char *attr_name,
+ const char *fname,
+ int fd,
+ size_t estimated_size,
+ DATA_BLOB *blob)
+{
+ int ret;
+
+ *blob = data_blob_talloc(mem_ctx, NULL, estimated_size+16);
+ if (blob->data == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+again:
+ if (fd != -1) {
+ ret = fgetxattr(fd, attr_name, blob->data, estimated_size);
+ } else {
+ ret = getxattr(fname, attr_name, blob->data, estimated_size);
+ }
+ if (ret == -1 && errno == ERANGE) {
+ estimated_size *= 2;
+ blob->data = talloc_realloc(mem_ctx, blob->data,
+ uint8_t, estimated_size);
+ if (blob->data == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ blob->length = estimated_size;
+ goto again;
+ }
+ if (ret == -1 && errno == EPERM) {
+ struct stat statbuf;
+
+ if (fd != -1) {
+ ret = fstat(fd, &statbuf);
+ } else {
+ ret = stat(fname, &statbuf);
+ }
+ if (ret == 0) {
+ /* check if this is a directory and the sticky bit is set */
+ if (S_ISDIR(statbuf.st_mode) && (statbuf.st_mode & S_ISVTX)) {
+ /* pretend we could not find the xattr */
+
+ data_blob_free(blob);
+ return NT_STATUS_NOT_FOUND;
+
+ } else {
+ /* if not this was probably a legitimate error
+ * reset ret and errno to the correct values */
+ errno = EPERM;
+ ret = -1;
+ }
+ }
+ }
+
+ if (ret == -1) {
+ data_blob_free(blob);
+ return pvfs_map_errno(pvfs, errno);
+ }
+
+ blob->length = ret;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ push a xattr as a blob, from either a file or a file descriptor
+*/
+NTSTATUS push_xattr_blob_system(struct pvfs_state *pvfs,
+ const char *attr_name,
+ const char *fname,
+ int fd,
+ const DATA_BLOB *blob)
+{
+ int ret;
+
+ if (fd != -1) {
+ ret = fsetxattr(fd, attr_name, blob->data, blob->length, 0);
+ } else {
+ ret = setxattr(fname, attr_name, blob->data, blob->length, 0);
+ }
+ if (ret == -1) {
+ return pvfs_map_errno(pvfs, errno);
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ delete a xattr
+*/
+NTSTATUS delete_xattr_system(struct pvfs_state *pvfs, const char *attr_name,
+ const char *fname, int fd)
+{
+ int ret;
+
+ if (fd != -1) {
+ ret = fremovexattr(fd, attr_name);
+ } else {
+ ret = removexattr(fname, attr_name);
+ }
+ if (ret == -1) {
+ return pvfs_map_errno(pvfs, errno);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ unlink a file - cleanup any xattrs
+*/
+NTSTATUS unlink_xattr_system(struct pvfs_state *pvfs, const char *fname)
+{
+ /* nothing needs to be done for filesystem based xattrs */
+ return NT_STATUS_OK;
+}
diff --git a/source4/ntvfs/simple/README b/source4/ntvfs/simple/README
new file mode 100644
index 0000000..f1de5d9
--- /dev/null
+++ b/source4/ntvfs/simple/README
@@ -0,0 +1,10 @@
+This module (ntvfs 'simple') provides a very, very simple posix backend.
+
+WARNING: All file access is done as user 'root'!!!
+ Only use this module for testing, with only test data!!!
+
+For activating this module use:
+
+[testshare]
+ path = /tmp/testshare
+ nfvfs handler = simple
diff --git a/source4/ntvfs/simple/svfs.h b/source4/ntvfs/simple/svfs.h
new file mode 100644
index 0000000..e5ad3b9
--- /dev/null
+++ b/source4/ntvfs/simple/svfs.h
@@ -0,0 +1,38 @@
+
+struct svfs_private {
+ struct ntvfs_module_context *ntvfs;
+
+ /* the base directory */
+ char *connectpath;
+
+ /* a linked list of open searches */
+ struct search_state *search;
+
+ /* next available search handle */
+ uint16_t next_search_handle;
+
+ struct svfs_file *open_files;
+};
+
+struct svfs_dir {
+ unsigned int count;
+ char *unix_dir;
+ struct svfs_dirfile {
+ char *name;
+ struct stat st;
+ } *files;
+};
+
+struct svfs_file {
+ struct svfs_file *next, *prev;
+ int fd;
+ struct ntvfs_handle *handle;
+ char *name;
+};
+
+struct search_state {
+ struct search_state *next, *prev;
+ uint16_t handle;
+ unsigned int current_index;
+ struct svfs_dir *dir;
+};
diff --git a/source4/ntvfs/simple/svfs_util.c b/source4/ntvfs/simple/svfs_util.c
new file mode 100644
index 0000000..fd32311
--- /dev/null
+++ b/source4/ntvfs/simple/svfs_util.c
@@ -0,0 +1,189 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ simple NTVFS filesystem backend
+
+ Copyright (C) Andrew Tridgell 2003
+
+ 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/>.
+*/
+/*
+ utility functions for simple backend
+*/
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "svfs.h"
+#include "system/time.h"
+#include "system/dir.h"
+#include "ntvfs/ntvfs.h"
+#include "ntvfs/simple/proto.h"
+
+/*
+ convert a windows path to a unix path - don't do any mangling or case sensitive handling
+*/
+char *svfs_unix_path(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, const char *name)
+{
+ struct svfs_private *p = ntvfs->private_data;
+ char *ret;
+ char *name_lower = strlower_talloc(p, name);
+
+ if (*name != '\\') {
+ ret = talloc_asprintf(req, "%s/%s", p->connectpath, name_lower);
+ } else {
+ ret = talloc_asprintf(req, "%s%s", p->connectpath, name_lower);
+ }
+ all_string_sub(ret, "\\", "/", 0);
+ talloc_free(name_lower);
+ return ret;
+}
+
+
+/*
+ read a directory and find all matching file names and stat info
+ returned names are separate unix and DOS names. The returned names
+ are relative to the directory
+*/
+struct svfs_dir *svfs_list_unix(TALLOC_CTX *mem_ctx, struct ntvfs_request *req, const char *unix_path)
+{
+ char *p, *mask;
+ struct svfs_dir *dir;
+ DIR *odir;
+ struct dirent *dent;
+ unsigned int allocated = 0;
+ char *low_mask;
+
+ dir = talloc(mem_ctx, struct svfs_dir);
+ if (!dir) { return NULL; }
+
+ dir->count = 0;
+ dir->files = 0;
+
+ /* find the base directory */
+ p = strrchr(unix_path, '/');
+ if (!p) { return NULL; }
+
+ dir->unix_dir = talloc_strndup(mem_ctx, unix_path, PTR_DIFF(p, unix_path));
+ if (!dir->unix_dir) { return NULL; }
+
+ /* the wildcard pattern is the last part */
+ mask = p+1;
+
+ low_mask = strlower_talloc(mem_ctx, mask);
+ if (!low_mask) { return NULL; }
+
+ odir = opendir(dir->unix_dir);
+ if (!odir) { return NULL; }
+
+ while ((dent = readdir(odir))) {
+ unsigned int i = dir->count;
+ char *full_name;
+ char *low_name;
+
+ if (strchr(dent->d_name, ':') && !strchr(unix_path, ':')) {
+ /* don't show streams in dir listing */
+ continue;
+ }
+
+ low_name = strlower_talloc(mem_ctx, dent->d_name);
+ if (!low_name) { continue; }
+
+ /* check it matches the wildcard pattern */
+ if (ms_fnmatch_protocol(low_mask, low_name, PROTOCOL_NT1,
+ false) != 0) {
+ continue;
+ }
+
+ if (dir->count >= allocated) {
+ allocated = (allocated + 100) * 1.2;
+ dir->files = talloc_realloc(dir, dir->files, struct svfs_dirfile, allocated);
+ if (!dir->files) {
+ closedir(odir);
+ return NULL;
+ }
+ }
+
+ dir->files[i].name = low_name;
+ if (!dir->files[i].name) { continue; }
+
+ full_name = talloc_asprintf(mem_ctx, "%s/%s", dir->unix_dir,
+ dir->files[i].name);
+ if (!full_name) { continue; }
+
+ if (stat(full_name, &dir->files[i].st) == 0) {
+ dir->count++;
+ }
+
+ talloc_free(full_name);
+ }
+
+ closedir(odir);
+
+ return dir;
+}
+
+/*
+ read a directory and find all matching file names and stat info
+ returned names are separate unix and DOS names. The returned names
+ are relative to the directory
+*/
+struct svfs_dir *svfs_list(struct ntvfs_module_context *ntvfs, struct ntvfs_request *req, const char *pattern)
+{
+ struct svfs_private *p = ntvfs->private_data;
+ char *unix_path;
+
+ unix_path = svfs_unix_path(ntvfs, req, pattern);
+ if (!unix_path) { return NULL; }
+
+ return svfs_list_unix(p, req, unix_path);
+}
+
+
+/*******************************************************************
+set the time on a file via file descriptor
+*******************************************************************/
+int svfs_file_utime(int fd, struct utimbuf *times)
+{
+ char *fd_path = NULL;
+ int ret;
+
+ ret = asprintf(&fd_path, "/proc/self/%d", fd);
+ if (ret == -1) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ if (!fd_path) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ ret = utime(fd_path, times);
+ free(fd_path);
+ return ret;
+}
+
+
+/*
+ map a unix file attrib to a DOS attribute
+*/
+uint16_t svfs_unix_to_dos_attrib(mode_t mode)
+{
+ uint16_t ret = 0;
+ if (S_ISDIR(mode)) ret |= FILE_ATTRIBUTE_DIRECTORY;
+ if (!(mode & S_IWUSR)) ret |= FILE_ATTRIBUTE_READONLY;
+ return ret;
+}
+
diff --git a/source4/ntvfs/simple/vfs_simple.c b/source4/ntvfs/simple/vfs_simple.c
new file mode 100644
index 0000000..000da41
--- /dev/null
+++ b/source4/ntvfs/simple/vfs_simple.c
@@ -0,0 +1,1112 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ simple NTVFS filesystem backend
+
+ Copyright (C) Andrew Tridgell 2003
+
+ 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/>.
+*/
+/*
+ this implements a very simple NTVFS filesystem backend.
+
+ this backend largely ignores the POSIX -> CIFS mappings, just doing absolutely
+ minimal work to give a working backend.
+*/
+
+#include "includes.h"
+#include "system/dir.h"
+#include "system/filesys.h"
+#include "svfs.h"
+#include "system/time.h"
+#include "../lib/util/dlinklist.h"
+#include "ntvfs/ntvfs.h"
+#include "ntvfs/simple/proto.h"
+
+#ifndef O_DIRECTORY
+#define O_DIRECTORY 0
+#endif
+
+#define CHECK_READ_ONLY(req) do { if (share_bool_option(ntvfs->ctx->config, SHARE_READONLY, true)) return NT_STATUS_ACCESS_DENIED; } while (0)
+
+/*
+ connect to a share - used when a tree_connect operation comes
+ in. For a disk based backend we needs to ensure that the base
+ directory exists (tho it doesn't need to be accessible by the user,
+ that comes later)
+*/
+static NTSTATUS svfs_connect(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_tcon* tcon)
+{
+ struct stat st;
+ struct svfs_private *p;
+ struct share_config *scfg = ntvfs->ctx->config;
+ const char *sharename;
+
+ switch (tcon->generic.level) {
+ case RAW_TCON_TCON:
+ sharename = tcon->tcon.in.service;
+ break;
+ case RAW_TCON_TCONX:
+ sharename = tcon->tconx.in.path;
+ break;
+ case RAW_TCON_SMB2:
+ sharename = tcon->smb2.in.path;
+ break;
+ default:
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ if (strncmp(sharename, "\\\\", 2) == 0) {
+ char *p2 = strchr(sharename+2, '\\');
+ if (p2) {
+ sharename = p2 + 1;
+ }
+ }
+
+ p = talloc(ntvfs, struct svfs_private);
+ NT_STATUS_HAVE_NO_MEMORY(p);
+ p->ntvfs = ntvfs;
+ p->next_search_handle = 0;
+ p->connectpath = share_string_option(p, scfg, SHARE_PATH, "");
+ p->open_files = NULL;
+ p->search = NULL;
+
+ /* the directory must exist */
+ if (stat(p->connectpath, &st) != 0 || !S_ISDIR(st.st_mode)) {
+ DEBUG(0,("'%s' is not a directory, when connecting to [%s]\n",
+ p->connectpath, sharename));
+ return NT_STATUS_BAD_NETWORK_NAME;
+ }
+
+ ntvfs->ctx->fs_type = talloc_strdup(ntvfs->ctx, "NTFS");
+ NT_STATUS_HAVE_NO_MEMORY(ntvfs->ctx->fs_type);
+ ntvfs->ctx->dev_type = talloc_strdup(ntvfs->ctx, "A:");
+ NT_STATUS_HAVE_NO_MEMORY(ntvfs->ctx->dev_type);
+
+ if (tcon->generic.level == RAW_TCON_TCONX) {
+ tcon->tconx.out.fs_type = ntvfs->ctx->fs_type;
+ tcon->tconx.out.dev_type = ntvfs->ctx->dev_type;
+ }
+
+ ntvfs->private_data = p;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ disconnect from a share
+*/
+static NTSTATUS svfs_disconnect(struct ntvfs_module_context *ntvfs)
+{
+ return NT_STATUS_OK;
+}
+
+/*
+ find open file handle given fd
+*/
+static struct svfs_file *find_fd(struct svfs_private *sp, struct ntvfs_handle *handle)
+{
+ struct svfs_file *f;
+ void *p;
+
+ p = ntvfs_handle_get_backend_data(handle, sp->ntvfs);
+ if (!p) return NULL;
+
+ f = talloc_get_type(p, struct svfs_file);
+ if (!f) return NULL;
+
+ return f;
+}
+
+/*
+ delete a file - the dirtype specifies the file types to include in the search.
+ The name can contain CIFS wildcards, but rarely does (except with OS/2 clients)
+*/
+static NTSTATUS svfs_unlink(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_unlink *unl)
+{
+ char *unix_path;
+
+ CHECK_READ_ONLY(req);
+
+ unix_path = svfs_unix_path(ntvfs, req, unl->unlink.in.pattern);
+
+ /* ignoring wildcards ... */
+ if (unlink(unix_path) == -1) {
+ return map_nt_error_from_unix_common(errno);
+ }
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ ioctl interface - we don't do any
+*/
+static NTSTATUS svfs_ioctl(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_ioctl *io)
+{
+ return NT_STATUS_INVALID_PARAMETER;
+}
+
+/*
+ check if a directory exists
+*/
+static NTSTATUS svfs_chkpath(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_chkpath *cp)
+{
+ char *unix_path;
+ struct stat st;
+
+ unix_path = svfs_unix_path(ntvfs, req, cp->chkpath.in.path);
+
+ if (stat(unix_path, &st) == -1) {
+ return map_nt_error_from_unix_common(errno);
+ }
+
+ if (!S_ISDIR(st.st_mode)) {
+ return NT_STATUS_NOT_A_DIRECTORY;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ build a file_id from a stat struct
+*/
+static uint64_t svfs_file_id(struct stat *st)
+{
+ uint64_t ret = st->st_ino;
+ ret <<= 32;
+ ret |= st->st_dev;
+ return ret;
+}
+
+/*
+ approximately map a struct stat to a generic fileinfo struct
+*/
+static NTSTATUS svfs_map_fileinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_fileinfo *info,
+ struct stat *st, const char *unix_path)
+{
+ struct svfs_dir *dir = NULL;
+ char *pattern = NULL;
+ int i, ret;
+ const char *s, *short_name;
+
+ s = strrchr(unix_path, '/');
+ if (s) {
+ short_name = s+1;
+ } else {
+ short_name = "";
+ }
+
+ ret = asprintf(&pattern, "%s:*", unix_path);
+ if (ret == -1) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (pattern) {
+ dir = svfs_list_unix(req, req, pattern);
+ }
+
+ unix_to_nt_time(&info->generic.out.create_time, st->st_ctime);
+ unix_to_nt_time(&info->generic.out.access_time, st->st_atime);
+ unix_to_nt_time(&info->generic.out.write_time, st->st_mtime);
+ unix_to_nt_time(&info->generic.out.change_time, st->st_mtime);
+ info->generic.out.alloc_size = st->st_size;
+ info->generic.out.size = st->st_size;
+ info->generic.out.attrib = svfs_unix_to_dos_attrib(st->st_mode);
+ info->generic.out.alloc_size = st->st_blksize * st->st_blocks;
+ info->generic.out.nlink = st->st_nlink;
+ info->generic.out.directory = S_ISDIR(st->st_mode) ? 1 : 0;
+ info->generic.out.file_id = svfs_file_id(st);
+ /* REWRITE: TODO stuff in here */
+ info->generic.out.delete_pending = 0;
+ info->generic.out.ea_size = 0;
+ info->generic.out.num_eas = 0;
+ info->generic.out.fname.s = talloc_strdup(req, short_name);
+ info->generic.out.alt_fname.s = talloc_strdup(req, short_name);
+ info->generic.out.compressed_size = 0;
+ info->generic.out.format = 0;
+ info->generic.out.unit_shift = 0;
+ info->generic.out.chunk_shift = 0;
+ info->generic.out.cluster_shift = 0;
+
+ info->generic.out.access_flags = 0;
+ info->generic.out.position = 0;
+ info->generic.out.mode = 0;
+ info->generic.out.alignment_requirement = 0;
+ info->generic.out.reparse_tag = 0;
+ info->generic.out.num_streams = 0;
+ /* setup a single data stream */
+ info->generic.out.num_streams = 1 + (dir?dir->count:0);
+ info->generic.out.streams = talloc_array(req,
+ struct stream_struct,
+ info->generic.out.num_streams);
+ if (!info->generic.out.streams) {
+ return NT_STATUS_NO_MEMORY;
+ }
+ info->generic.out.streams[0].size = st->st_size;
+ info->generic.out.streams[0].alloc_size = st->st_size;
+ info->generic.out.streams[0].stream_name.s = talloc_strdup(req,"::$DATA");
+
+ for (i=0;dir && i<dir->count;i++) {
+ s = strchr(dir->files[i].name, ':');
+ info->generic.out.streams[1+i].size = dir->files[i].st.st_size;
+ info->generic.out.streams[1+i].alloc_size = dir->files[i].st.st_size;
+ info->generic.out.streams[1+i].stream_name.s = s?s:dir->files[i].name;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ return info on a pathname
+*/
+static NTSTATUS svfs_qpathinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_fileinfo *info)
+{
+ char *unix_path;
+ struct stat st;
+
+ DEBUG(19,("svfs_qpathinfo: file %s level 0x%x\n", info->generic.in.file.path, info->generic.level));
+ if (info->generic.level != RAW_FILEINFO_GENERIC) {
+ return ntvfs_map_qpathinfo(ntvfs, req, info);
+ }
+
+ unix_path = svfs_unix_path(ntvfs, req, info->generic.in.file.path);
+ DEBUG(19,("svfs_qpathinfo: file %s\n", unix_path));
+ if (stat(unix_path, &st) == -1) {
+ DEBUG(19,("svfs_qpathinfo: file %s errno=%d\n", unix_path, errno));
+ return map_nt_error_from_unix_common(errno);
+ }
+ DEBUG(19,("svfs_qpathinfo: file %s, stat done\n", unix_path));
+ return svfs_map_fileinfo(ntvfs, req, info, &st, unix_path);
+}
+
+/*
+ query info on a open file
+*/
+static NTSTATUS svfs_qfileinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_fileinfo *info)
+{
+ struct svfs_private *p = ntvfs->private_data;
+ struct svfs_file *f;
+ struct stat st;
+
+ if (info->generic.level != RAW_FILEINFO_GENERIC) {
+ return ntvfs_map_qfileinfo(ntvfs, req, info);
+ }
+
+ f = find_fd(p, info->generic.in.file.ntvfs);
+ if (!f) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ if (fstat(f->fd, &st) == -1) {
+ return map_nt_error_from_unix_common(errno);
+ }
+
+ return svfs_map_fileinfo(ntvfs, req,info, &st, f->name);
+}
+
+
+/*
+ open a file
+*/
+static NTSTATUS svfs_open(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_open *io)
+{
+ struct svfs_private *p = ntvfs->private_data;
+ char *unix_path;
+ struct stat st;
+ int fd, flags;
+ struct svfs_file *f;
+ int create_flags, rdwr_flags;
+ bool readonly;
+ NTSTATUS status;
+ struct ntvfs_handle *handle;
+
+ if (io->generic.level != RAW_OPEN_GENERIC) {
+ return ntvfs_map_open(ntvfs, req, io);
+ }
+
+ readonly = share_bool_option(ntvfs->ctx->config, SHARE_READONLY, SHARE_READONLY_DEFAULT);
+ if (readonly) {
+ create_flags = 0;
+ rdwr_flags = O_RDONLY;
+ } else {
+ create_flags = O_CREAT;
+ rdwr_flags = O_RDWR;
+ }
+
+ unix_path = svfs_unix_path(ntvfs, req, io->ntcreatex.in.fname);
+
+ switch (io->generic.in.open_disposition) {
+ case NTCREATEX_DISP_SUPERSEDE:
+ case NTCREATEX_DISP_OVERWRITE_IF:
+ flags = create_flags | O_TRUNC;
+ break;
+ case NTCREATEX_DISP_OPEN:
+ case NTCREATEX_DISP_OVERWRITE:
+ flags = 0;
+ break;
+ case NTCREATEX_DISP_CREATE:
+ flags = create_flags | O_EXCL;
+ break;
+ case NTCREATEX_DISP_OPEN_IF:
+ flags = create_flags;
+ break;
+ default:
+ flags = 0;
+ break;
+ }
+
+ flags |= rdwr_flags;
+
+ if (io->generic.in.create_options & NTCREATEX_OPTIONS_DIRECTORY) {
+ flags = O_RDONLY | O_DIRECTORY;
+ if (readonly) {
+ goto do_open;
+ }
+ switch (io->generic.in.open_disposition) {
+ case NTCREATEX_DISP_CREATE:
+ if (mkdir(unix_path, 0755) == -1) {
+ DEBUG(9,("svfs_open: mkdir %s errno=%d\n", unix_path, errno));
+ return map_nt_error_from_unix_common(errno);
+ }
+ break;
+ case NTCREATEX_DISP_OPEN_IF:
+ if (mkdir(unix_path, 0755) == -1 && errno != EEXIST) {
+ DEBUG(9,("svfs_open: mkdir %s errno=%d\n", unix_path, errno));
+ return map_nt_error_from_unix_common(errno);
+ }
+ break;
+ }
+ }
+
+do_open:
+ fd = open(unix_path, flags, 0644);
+ if (fd == -1) {
+ return map_nt_error_from_unix_common(errno);
+ }
+
+ if (fstat(fd, &st) == -1) {
+ DEBUG(9,("svfs_open: fstat errno=%d\n", errno));
+ close(fd);
+ return map_nt_error_from_unix_common(errno);
+ }
+
+ status = ntvfs_handle_new(ntvfs, req, &handle);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ f = talloc(handle, struct svfs_file);
+ if (f == NULL) {
+ close(fd);
+ return NT_STATUS_NO_MEMORY;
+ }
+ f->fd = fd;
+ f->name = talloc_strdup(f, unix_path);
+ NT_STATUS_HAVE_NO_MEMORY(f->name);
+
+ DLIST_ADD(p->open_files, f);
+
+ status = ntvfs_handle_set_backend_data(handle, ntvfs, f);
+ NT_STATUS_NOT_OK_RETURN(status);
+
+ ZERO_STRUCT(io->generic.out);
+
+ unix_to_nt_time(&io->generic.out.create_time, st.st_ctime);
+ unix_to_nt_time(&io->generic.out.access_time, st.st_atime);
+ unix_to_nt_time(&io->generic.out.write_time, st.st_mtime);
+ unix_to_nt_time(&io->generic.out.change_time, st.st_mtime);
+ io->generic.out.file.ntvfs = handle;
+ io->generic.out.alloc_size = st.st_size;
+ io->generic.out.size = st.st_size;
+ io->generic.out.attrib = svfs_unix_to_dos_attrib(st.st_mode);
+ io->generic.out.is_directory = S_ISDIR(st.st_mode) ? 1 : 0;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ create a directory
+*/
+static NTSTATUS svfs_mkdir(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_mkdir *md)
+{
+ char *unix_path;
+
+ CHECK_READ_ONLY(req);
+
+ if (md->generic.level != RAW_MKDIR_MKDIR) {
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ unix_path = svfs_unix_path(ntvfs, req, md->mkdir.in.path);
+
+ if (mkdir(unix_path, 0777) == -1) {
+ return map_nt_error_from_unix_common(errno);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ remove a directory
+*/
+static NTSTATUS svfs_rmdir(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, struct smb_rmdir *rd)
+{
+ char *unix_path;
+
+ CHECK_READ_ONLY(req);
+
+ unix_path = svfs_unix_path(ntvfs, req, rd->in.path);
+
+ if (rmdir(unix_path) == -1) {
+ return map_nt_error_from_unix_common(errno);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ rename a set of files
+*/
+static NTSTATUS svfs_rename(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_rename *ren)
+{
+ char *unix_path1, *unix_path2;
+
+ CHECK_READ_ONLY(req);
+
+ if (ren->generic.level != RAW_RENAME_RENAME) {
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ unix_path1 = svfs_unix_path(ntvfs, req, ren->rename.in.pattern1);
+ unix_path2 = svfs_unix_path(ntvfs, req, ren->rename.in.pattern2);
+
+ if (rename(unix_path1, unix_path2) == -1) {
+ return map_nt_error_from_unix_common(errno);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ copy a set of files
+*/
+static NTSTATUS svfs_copy(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, struct smb_copy *cp)
+{
+ return NT_STATUS_NOT_SUPPORTED;
+}
+
+/*
+ read from a file
+*/
+static NTSTATUS svfs_read(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_read *rd)
+{
+ struct svfs_private *p = ntvfs->private_data;
+ struct svfs_file *f;
+ ssize_t ret;
+
+ if (rd->generic.level != RAW_READ_READX) {
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ f = find_fd(p, rd->readx.in.file.ntvfs);
+ if (!f) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ ret = pread(f->fd,
+ rd->readx.out.data,
+ rd->readx.in.maxcnt,
+ rd->readx.in.offset);
+ if (ret == -1) {
+ return map_nt_error_from_unix_common(errno);
+ }
+
+ rd->readx.out.nread = ret;
+ rd->readx.out.remaining = 0; /* should fill this in? */
+ rd->readx.out.compaction_mode = 0;
+
+ return NT_STATUS_OK;
+}
+
+/*
+ write to a file
+*/
+static NTSTATUS svfs_write(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_write *wr)
+{
+ struct svfs_private *p = ntvfs->private_data;
+ struct svfs_file *f;
+ ssize_t ret;
+
+ if (wr->generic.level != RAW_WRITE_WRITEX) {
+ return ntvfs_map_write(ntvfs, req, wr);
+ }
+
+ CHECK_READ_ONLY(req);
+
+ f = find_fd(p, wr->writex.in.file.ntvfs);
+ if (!f) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ ret = pwrite(f->fd,
+ wr->writex.in.data,
+ wr->writex.in.count,
+ wr->writex.in.offset);
+ if (ret == -1) {
+ return map_nt_error_from_unix_common(errno);
+ }
+
+ wr->writex.out.nwritten = ret;
+ wr->writex.out.remaining = 0; /* should fill this in? */
+
+ return NT_STATUS_OK;
+}
+
+/*
+ seek in a file
+*/
+static NTSTATUS svfs_seek(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_seek *io)
+{
+ return NT_STATUS_NOT_SUPPORTED;
+}
+
+/*
+ flush a file
+*/
+static NTSTATUS svfs_flush(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_flush *io)
+{
+ struct svfs_private *p = ntvfs->private_data;
+ struct svfs_file *f;
+
+ switch (io->generic.level) {
+ case RAW_FLUSH_FLUSH:
+ case RAW_FLUSH_SMB2:
+ /* ignore the additional unknown option in SMB2 */
+ f = find_fd(p, io->generic.in.file.ntvfs);
+ if (!f) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+ fsync(f->fd);
+ return NT_STATUS_OK;
+
+ case RAW_FLUSH_ALL:
+ for (f=p->open_files;f;f=f->next) {
+ fsync(f->fd);
+ }
+ return NT_STATUS_OK;
+ }
+
+ return NT_STATUS_INVALID_LEVEL;
+}
+
+/*
+ close a file
+*/
+static NTSTATUS svfs_close(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_close *io)
+{
+ struct svfs_private *p = ntvfs->private_data;
+ struct svfs_file *f;
+
+ if (io->generic.level != RAW_CLOSE_CLOSE) {
+ /* we need a mapping function */
+ return NT_STATUS_INVALID_LEVEL;
+ }
+
+ f = find_fd(p, io->close.in.file.ntvfs);
+ if (!f) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ if (close(f->fd) == -1) {
+ return map_nt_error_from_unix_common(errno);
+ }
+
+ DLIST_REMOVE(p->open_files, f);
+ talloc_free(f->name);
+ talloc_free(f);
+
+ return NT_STATUS_OK;
+}
+
+/*
+ exit - closing files
+*/
+static NTSTATUS svfs_exit(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req)
+{
+ return NT_STATUS_NOT_SUPPORTED;
+}
+
+/*
+ logoff - closing files
+*/
+static NTSTATUS svfs_logoff(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req)
+{
+ return NT_STATUS_NOT_SUPPORTED;
+}
+
+/*
+ setup for an async call
+*/
+static NTSTATUS svfs_async_setup(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ void *private_data)
+{
+ return NT_STATUS_OK;
+}
+
+/*
+ cancel an async call
+*/
+static NTSTATUS svfs_cancel(struct ntvfs_module_context *ntvfs, struct ntvfs_request *req)
+{
+ return NT_STATUS_UNSUCCESSFUL;
+}
+
+/*
+ lock a byte range
+*/
+static NTSTATUS svfs_lock(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_lock *lck)
+{
+ DEBUG(0,("REWRITE: not doing byte range locking!\n"));
+ return NT_STATUS_OK;
+}
+
+/*
+ set info on a pathname
+*/
+static NTSTATUS svfs_setpathinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_setfileinfo *st)
+{
+ CHECK_READ_ONLY(req);
+
+ return NT_STATUS_NOT_SUPPORTED;
+}
+
+/*
+ set info on a open file
+*/
+static NTSTATUS svfs_setfileinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_setfileinfo *info)
+{
+ struct svfs_private *p = ntvfs->private_data;
+ struct svfs_file *f;
+ struct utimbuf unix_times;
+
+ CHECK_READ_ONLY(req);
+
+ f = find_fd(p, info->generic.in.file.ntvfs);
+ if (!f) {
+ return NT_STATUS_INVALID_HANDLE;
+ }
+
+ switch (info->generic.level) {
+ case RAW_SFILEINFO_END_OF_FILE_INFO:
+ case RAW_SFILEINFO_END_OF_FILE_INFORMATION:
+ if (ftruncate(f->fd,
+ info->end_of_file_info.in.size) == -1) {
+ return map_nt_error_from_unix_common(errno);
+ }
+ break;
+ case RAW_SFILEINFO_SETATTRE:
+ unix_times.actime = info->setattre.in.access_time;
+ unix_times.modtime = info->setattre.in.write_time;
+
+ if (unix_times.actime == 0 && unix_times.modtime == 0) {
+ break;
+ }
+
+ /* set modify time = to access time if modify time was 0 */
+ if (unix_times.actime != 0 && unix_times.modtime == 0) {
+ unix_times.modtime = unix_times.actime;
+ }
+
+ /* Set the date on this file */
+ if (svfs_file_utime(f->fd, &unix_times) != 0) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ break;
+ default:
+ DEBUG(2,("svfs_setfileinfo: level %d not implemented\n",
+ info->generic.level));
+ return NT_STATUS_NOT_IMPLEMENTED;
+ }
+ return NT_STATUS_OK;
+}
+
+
+/*
+ return filesystem space info
+*/
+static NTSTATUS svfs_fsinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_fsinfo *fs)
+{
+ struct svfs_private *p = ntvfs->private_data;
+ struct stat st;
+
+ if (fs->generic.level != RAW_QFS_GENERIC) {
+ return ntvfs_map_fsinfo(ntvfs, req, fs);
+ }
+
+ if (sys_fsusage(p->connectpath,
+ &fs->generic.out.blocks_free,
+ &fs->generic.out.blocks_total) == -1) {
+ return map_nt_error_from_unix_common(errno);
+ }
+
+ fs->generic.out.block_size = 512;
+
+ if (stat(p->connectpath, &st) != 0) {
+ return NT_STATUS_DISK_CORRUPT_ERROR;
+ }
+
+ fs->generic.out.fs_id = st.st_ino;
+ unix_to_nt_time(&fs->generic.out.create_time, st.st_ctime);
+ fs->generic.out.serial_number = st.st_ino;
+ fs->generic.out.fs_attr = 0;
+ fs->generic.out.max_file_component_length = 255;
+ fs->generic.out.device_type = 0;
+ fs->generic.out.device_characteristics = 0;
+ fs->generic.out.quota_soft = 0;
+ fs->generic.out.quota_hard = 0;
+ fs->generic.out.quota_flags = 0;
+ fs->generic.out.volume_name = talloc_strdup(req, ntvfs->ctx->config->name);
+ fs->generic.out.fs_type = ntvfs->ctx->fs_type;
+
+ return NT_STATUS_OK;
+}
+
+#if 0
+/*
+ return filesystem attribute info
+*/
+static NTSTATUS svfs_fsattr(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_fsattr *fs)
+{
+ struct stat st;
+ struct svfs_private *p = ntvfs->private_data;
+
+ if (fs->generic.level != RAW_FSATTR_GENERIC) {
+ return ntvfs_map_fsattr(ntvfs, req, fs);
+ }
+
+ if (stat(p->connectpath, &st) == -1) {
+ return map_nt_error_from_unix_common(errno);
+ }
+
+ unix_to_nt_time(&fs->generic.out.create_time, st.st_ctime);
+ fs->generic.out.fs_attr =
+ FILE_CASE_PRESERVED_NAMES |
+ FILE_CASE_SENSITIVE_SEARCH |
+ FILE_PERSISTENT_ACLS;
+ fs->generic.out.max_file_component_length = 255;
+ fs->generic.out.serial_number = 1;
+ fs->generic.out.fs_type = talloc_strdup(req, "NTFS");
+ fs->generic.out.volume_name = talloc_strdup(req,
+ lpcfg_servicename(req->tcon->service));
+
+ return NT_STATUS_OK;
+}
+#endif
+
+/*
+ return print queue info
+*/
+static NTSTATUS svfs_lpq(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_lpq *lpq)
+{
+ return NT_STATUS_NOT_SUPPORTED;
+}
+
+/*
+ list files in a directory matching a wildcard pattern
+*/
+static NTSTATUS svfs_search_first(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_search_first *io,
+ void *search_private,
+ bool (*callback)(void *, const union smb_search_data *))
+{
+ struct svfs_dir *dir;
+ int i;
+ struct svfs_private *p = ntvfs->private_data;
+ struct search_state *search;
+ union smb_search_data file;
+ unsigned int max_count;
+
+ if (io->generic.level != RAW_SEARCH_TRANS2) {
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ if (io->generic.data_level != RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO) {
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ search = talloc_zero(p, struct search_state);
+ if (!search) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ max_count = io->t2ffirst.in.max_count;
+
+ dir = svfs_list(ntvfs, req, io->t2ffirst.in.pattern);
+ if (!dir) {
+ return NT_STATUS_FOOBAR;
+ }
+
+ search->handle = p->next_search_handle;
+ search->dir = dir;
+
+ if (dir->count < max_count) {
+ max_count = dir->count;
+ }
+
+ for (i=0; i < max_count;i++) {
+ ZERO_STRUCT(file);
+ unix_to_nt_time(&file.both_directory_info.create_time, dir->files[i].st.st_ctime);
+ unix_to_nt_time(&file.both_directory_info.access_time, dir->files[i].st.st_atime);
+ unix_to_nt_time(&file.both_directory_info.write_time, dir->files[i].st.st_mtime);
+ unix_to_nt_time(&file.both_directory_info.change_time, dir->files[i].st.st_mtime);
+ file.both_directory_info.name.s = dir->files[i].name;
+ file.both_directory_info.short_name.s = dir->files[i].name;
+ file.both_directory_info.size = dir->files[i].st.st_size;
+ file.both_directory_info.attrib = svfs_unix_to_dos_attrib(dir->files[i].st.st_mode);
+
+ if (!callback(search_private, &file)) {
+ break;
+ }
+ }
+
+ search->current_index = i;
+
+ io->t2ffirst.out.count = i;
+ io->t2ffirst.out.handle = search->handle;
+ io->t2ffirst.out.end_of_search = (i == dir->count) ? 1 : 0;
+
+ /* work out if we are going to keep the search state */
+ if ((io->t2ffirst.in.flags & FLAG_TRANS2_FIND_CLOSE) ||
+ ((io->t2ffirst.in.flags & FLAG_TRANS2_FIND_CLOSE_IF_END) && (i == dir->count))) {
+ talloc_free(search);
+ } else {
+ p->next_search_handle++;
+ DLIST_ADD(p->search, search);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/* continue a search */
+static NTSTATUS svfs_search_next(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_search_next *io,
+ void *search_private,
+ bool (*callback)(void *, const union smb_search_data *))
+{
+ struct svfs_dir *dir;
+ int i;
+ struct svfs_private *p = ntvfs->private_data;
+ struct search_state *search;
+ union smb_search_data file;
+ unsigned int max_count;
+
+ if (io->generic.level != RAW_SEARCH_TRANS2) {
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ if (io->generic.data_level != RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO) {
+ return NT_STATUS_NOT_SUPPORTED;
+ }
+
+ for (search=p->search; search; search = search->next) {
+ if (search->handle == io->t2fnext.in.handle) break;
+ }
+
+ if (!search) {
+ /* we didn't find the search handle */
+ return NT_STATUS_FOOBAR;
+ }
+
+ dir = search->dir;
+
+ /* the client might be asking for something other than just continuing
+ with the search */
+ if (!(io->t2fnext.in.flags & FLAG_TRANS2_FIND_CONTINUE) &&
+ (io->t2fnext.in.flags & FLAG_TRANS2_FIND_REQUIRE_RESUME) &&
+ io->t2fnext.in.last_name && *io->t2fnext.in.last_name) {
+ /* look backwards first */
+ for (i=search->current_index; i > 0; i--) {
+ if (strcmp(io->t2fnext.in.last_name, dir->files[i-1].name) == 0) {
+ search->current_index = i;
+ goto found;
+ }
+ }
+
+ /* then look forwards */
+ for (i=search->current_index+1; i <= dir->count; i++) {
+ if (strcmp(io->t2fnext.in.last_name, dir->files[i-1].name) == 0) {
+ search->current_index = i;
+ goto found;
+ }
+ }
+ }
+
+found:
+ max_count = search->current_index + io->t2fnext.in.max_count;
+
+ if (max_count > dir->count) {
+ max_count = dir->count;
+ }
+
+ for (i = search->current_index; i < max_count;i++) {
+ ZERO_STRUCT(file);
+ unix_to_nt_time(&file.both_directory_info.create_time, dir->files[i].st.st_ctime);
+ unix_to_nt_time(&file.both_directory_info.access_time, dir->files[i].st.st_atime);
+ unix_to_nt_time(&file.both_directory_info.write_time, dir->files[i].st.st_mtime);
+ unix_to_nt_time(&file.both_directory_info.change_time, dir->files[i].st.st_mtime);
+ file.both_directory_info.name.s = dir->files[i].name;
+ file.both_directory_info.short_name.s = dir->files[i].name;
+ file.both_directory_info.size = dir->files[i].st.st_size;
+ file.both_directory_info.attrib = svfs_unix_to_dos_attrib(dir->files[i].st.st_mode);
+
+ if (!callback(search_private, &file)) {
+ break;
+ }
+ }
+
+ io->t2fnext.out.count = i - search->current_index;
+ io->t2fnext.out.end_of_search = (i == dir->count) ? 1 : 0;
+
+ search->current_index = i;
+
+ /* work out if we are going to keep the search state */
+ if ((io->t2fnext.in.flags & FLAG_TRANS2_FIND_CLOSE) ||
+ ((io->t2fnext.in.flags & FLAG_TRANS2_FIND_CLOSE_IF_END) && (i == dir->count))) {
+ DLIST_REMOVE(p->search, search);
+ talloc_free(search);
+ }
+
+ return NT_STATUS_OK;
+}
+
+/* close a search */
+static NTSTATUS svfs_search_close(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_search_close *io)
+{
+ struct svfs_private *p = ntvfs->private_data;
+ struct search_state *search;
+
+ for (search=p->search; search; search = search->next) {
+ if (search->handle == io->findclose.in.handle) break;
+ }
+
+ if (!search) {
+ /* we didn't find the search handle */
+ return NT_STATUS_FOOBAR;
+ }
+
+ DLIST_REMOVE(p->search, search);
+ talloc_free(search);
+
+ return NT_STATUS_OK;
+}
+
+/* SMBtrans - not used on file shares */
+static NTSTATUS svfs_trans(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, struct smb_trans2 *trans2)
+{
+ return NT_STATUS_ACCESS_DENIED;
+}
+
+
+/*
+ initialise the POSIX disk backend, registering ourselves with the ntvfs subsystem
+ */
+NTSTATUS ntvfs_simple_init(TALLOC_CTX *ctx)
+{
+ NTSTATUS ret;
+ struct ntvfs_ops ops;
+ NTVFS_CURRENT_CRITICAL_SIZES(vers);
+
+ ZERO_STRUCT(ops);
+
+ /* fill in all the operations */
+ ops.connect_fn = svfs_connect;
+ ops.disconnect_fn = svfs_disconnect;
+ ops.unlink_fn = svfs_unlink;
+ ops.chkpath_fn = svfs_chkpath;
+ ops.qpathinfo_fn = svfs_qpathinfo;
+ ops.setpathinfo_fn = svfs_setpathinfo;
+ ops.open_fn = svfs_open;
+ ops.mkdir_fn = svfs_mkdir;
+ ops.rmdir_fn = svfs_rmdir;
+ ops.rename_fn = svfs_rename;
+ ops.copy_fn = svfs_copy;
+ ops.ioctl_fn = svfs_ioctl;
+ ops.read_fn = svfs_read;
+ ops.write_fn = svfs_write;
+ ops.seek_fn = svfs_seek;
+ ops.flush_fn = svfs_flush;
+ ops.close_fn = svfs_close;
+ ops.exit_fn = svfs_exit;
+ ops.lock_fn = svfs_lock;
+ ops.setfileinfo_fn = svfs_setfileinfo;
+ ops.qfileinfo_fn = svfs_qfileinfo;
+ ops.fsinfo_fn = svfs_fsinfo;
+ ops.lpq_fn = svfs_lpq;
+ ops.search_first_fn = svfs_search_first;
+ ops.search_next_fn = svfs_search_next;
+ ops.search_close_fn = svfs_search_close;
+ ops.trans_fn = svfs_trans;
+ ops.logoff_fn = svfs_logoff;
+ ops.async_setup_fn = svfs_async_setup;
+ ops.cancel_fn = svfs_cancel;
+
+ /* register ourselves with the NTVFS subsystem. We register
+ under names 'simple'
+ */
+
+ ops.type = NTVFS_DISK;
+ ops.name = "simple";
+ ret = ntvfs_register(&ops, &vers);
+
+ if (!NT_STATUS_IS_OK(ret)) {
+ DEBUG(0,("Failed to register simple backend with name: %s!\n",
+ ops.name));
+ }
+
+ return ret;
+}
diff --git a/source4/ntvfs/sysdep/README b/source4/ntvfs/sysdep/README
new file mode 100644
index 0000000..86b473e
--- /dev/null
+++ b/source4/ntvfs/sysdep/README
@@ -0,0 +1,5 @@
+This directory contains OS dependent interfaces to facilities that
+are only available on a few of our target systems, and require
+substantial code to abstract.
+
+
diff --git a/source4/ntvfs/sysdep/inotify.c b/source4/ntvfs/sysdep/inotify.c
new file mode 100644
index 0000000..94e0b17
--- /dev/null
+++ b/source4/ntvfs/sysdep/inotify.c
@@ -0,0 +1,398 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Andrew Tridgell 2006
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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/>.
+*/
+
+/*
+ notify implementation using inotify
+*/
+
+#include "includes.h"
+#include "system/filesys.h"
+#include <tevent.h>
+#include "ntvfs/sysdep/sys_notify.h"
+#include "../lib/util/dlinklist.h"
+#include "libcli/raw/smb.h"
+#include "param/param.h"
+
+#include <sys/inotify.h>
+
+/* glibc < 2.5 headers don't have these defines */
+#ifndef IN_ONLYDIR
+#define IN_ONLYDIR 0x01000000
+#endif
+#ifndef IN_MASK_ADD
+#define IN_MASK_ADD 0x20000000
+#endif
+
+struct inotify_private {
+ struct sys_notify_context *ctx;
+ int fd;
+ struct inotify_watch_context *watches;
+};
+
+struct inotify_watch_context {
+ struct inotify_watch_context *next, *prev;
+ struct inotify_private *in;
+ int wd;
+ sys_notify_callback_t callback;
+ void *private_data;
+ uint32_t mask; /* the inotify mask */
+ uint32_t filter; /* the windows completion filter */
+ const char *path;
+};
+
+
+/*
+ see if a particular event from inotify really does match a requested
+ notify event in SMB
+*/
+static bool filter_match(struct inotify_watch_context *w,
+ struct inotify_event *e)
+{
+ if ((e->mask & w->mask) == 0) {
+ /* this happens because inotify_add_watch() coalesces watches on the same
+ path, oring their masks together */
+ return false;
+ }
+
+ /* SMB separates the filters for files and directories */
+ if (e->mask & IN_ISDIR) {
+ if ((w->filter & FILE_NOTIFY_CHANGE_DIR_NAME) == 0) {
+ return false;
+ }
+ } else {
+ if ((e->mask & IN_ATTRIB) &&
+ (w->filter & (FILE_NOTIFY_CHANGE_ATTRIBUTES|
+ FILE_NOTIFY_CHANGE_LAST_WRITE|
+ FILE_NOTIFY_CHANGE_LAST_ACCESS|
+ FILE_NOTIFY_CHANGE_EA|
+ FILE_NOTIFY_CHANGE_SECURITY))) {
+ return true;
+ }
+ if ((e->mask & IN_MODIFY) &&
+ (w->filter & FILE_NOTIFY_CHANGE_ATTRIBUTES)) {
+ return true;
+ }
+ if ((w->filter & FILE_NOTIFY_CHANGE_FILE_NAME) == 0) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+
+/*
+ dispatch one inotify event
+
+ the cookies are used to correctly handle renames
+*/
+static void inotify_dispatch(struct inotify_private *in,
+ struct inotify_event *e,
+ uint32_t prev_cookie,
+ struct inotify_event *e2)
+{
+ struct inotify_watch_context *w, *next;
+ struct notify_event ne;
+
+ /* ignore extraneous events, such as unmount and IN_IGNORED events */
+ if ((e->mask & (IN_ATTRIB|IN_MODIFY|IN_CREATE|IN_DELETE|
+ IN_MOVED_FROM|IN_MOVED_TO)) == 0) {
+ return;
+ }
+
+ /* map the inotify mask to a action. This gets complicated for
+ renames */
+ if (e->mask & IN_CREATE) {
+ ne.action = NOTIFY_ACTION_ADDED;
+ } else if (e->mask & IN_DELETE) {
+ ne.action = NOTIFY_ACTION_REMOVED;
+ } else if (e->mask & IN_MOVED_FROM) {
+ if (e2 != NULL && e2->cookie == e->cookie) {
+ ne.action = NOTIFY_ACTION_OLD_NAME;
+ } else {
+ ne.action = NOTIFY_ACTION_REMOVED;
+ }
+ } else if (e->mask & IN_MOVED_TO) {
+ if (e->cookie == prev_cookie) {
+ ne.action = NOTIFY_ACTION_NEW_NAME;
+ } else {
+ ne.action = NOTIFY_ACTION_ADDED;
+ }
+ } else {
+ ne.action = NOTIFY_ACTION_MODIFIED;
+ }
+ ne.path = e->name;
+
+ /* find any watches that have this watch descriptor */
+ for (w=in->watches;w;w=next) {
+ next = w->next;
+ if (w->wd == e->wd && filter_match(w, e)) {
+ ne.dir = w->path;
+ w->callback(in->ctx, w->private_data, &ne);
+ }
+ }
+
+ /* SMB expects a file rename to generate three events, two for
+ the rename and the other for a modify of the
+ destination. Strange! */
+ if (ne.action != NOTIFY_ACTION_NEW_NAME ||
+ (e->mask & IN_ISDIR) != 0) {
+ return;
+ }
+
+ ne.action = NOTIFY_ACTION_MODIFIED;
+ e->mask = IN_ATTRIB;
+
+ for (w=in->watches;w;w=next) {
+ next = w->next;
+ if (w->wd == e->wd && filter_match(w, e) &&
+ !(w->filter & FILE_NOTIFY_CHANGE_CREATION)) {
+ ne.dir = w->path;
+ w->callback(in->ctx, w->private_data, &ne);
+ }
+ }
+}
+
+/*
+ called when the kernel has some events for us
+*/
+static void inotify_handler(struct tevent_context *ev, struct tevent_fd *fde,
+ uint16_t flags, void *private_data)
+{
+ struct inotify_private *in = talloc_get_type(private_data,
+ struct inotify_private);
+ int bufsize = 0;
+ struct inotify_event *e0, *e;
+ uint32_t prev_cookie=0;
+
+ /*
+ we must use FIONREAD as we cannot predict the length of the
+ filenames, and thus can't know how much to allocate
+ otherwise
+ */
+ if (ioctl(in->fd, FIONREAD, &bufsize) != 0 ||
+ bufsize == 0) {
+ DEBUG(0,("No data on inotify fd?!\n"));
+ return;
+ }
+
+ e0 = e = talloc_size(in, bufsize);
+ if (e == NULL) return;
+
+ if (read(in->fd, e0, bufsize) != bufsize) {
+ DEBUG(0,("Failed to read all inotify data\n"));
+ talloc_free(e0);
+ return;
+ }
+
+ /* we can get more than one event in the buffer */
+ while (bufsize >= sizeof(*e)) {
+ struct inotify_event *e2 = NULL;
+ bufsize -= e->len + sizeof(*e);
+ if (bufsize >= sizeof(*e)) {
+ e2 = (struct inotify_event *)(e->len + sizeof(*e) + (char *)e);
+ }
+ inotify_dispatch(in, e, prev_cookie, e2);
+ prev_cookie = e->cookie;
+ e = e2;
+ }
+
+ talloc_free(e0);
+}
+
+/*
+ setup the inotify handle - called the first time a watch is added on
+ this context
+*/
+static NTSTATUS inotify_setup(struct sys_notify_context *ctx)
+{
+ struct inotify_private *in;
+ struct tevent_fd *fde;
+
+ in = talloc(ctx, struct inotify_private);
+ NT_STATUS_HAVE_NO_MEMORY(in);
+
+ in->fd = inotify_init();
+ if (in->fd == -1) {
+ DEBUG(0,("Failed to init inotify - %s\n", strerror(errno)));
+ talloc_free(in);
+ return map_nt_error_from_unix_common(errno);
+ }
+ in->ctx = ctx;
+ in->watches = NULL;
+
+ ctx->private_data = in;
+
+ /* add a event waiting for the inotify fd to be readable */
+ fde = tevent_add_fd(ctx->ev, in, in->fd,
+ TEVENT_FD_READ, inotify_handler, in);
+ if (!fde) {
+ if (errno == 0) {
+ errno = ENOMEM;
+ }
+ DEBUG(0,("Failed to tevent_add_fd() - %s\n", strerror(errno)));
+ talloc_free(in);
+ return map_nt_error_from_unix_common(errno);
+ }
+
+ tevent_fd_set_auto_close(fde);
+
+ return NT_STATUS_OK;
+}
+
+
+/*
+ map from a change notify mask to a inotify mask. Remove any bits
+ which we can handle
+*/
+static const struct {
+ uint32_t notify_mask;
+ uint32_t inotify_mask;
+} inotify_mapping[] = {
+ {FILE_NOTIFY_CHANGE_FILE_NAME, IN_CREATE|IN_DELETE|IN_MOVED_FROM|IN_MOVED_TO},
+ {FILE_NOTIFY_CHANGE_DIR_NAME, IN_CREATE|IN_DELETE|IN_MOVED_FROM|IN_MOVED_TO},
+ {FILE_NOTIFY_CHANGE_ATTRIBUTES, IN_ATTRIB|IN_MOVED_TO|IN_MOVED_FROM|IN_MODIFY},
+ {FILE_NOTIFY_CHANGE_LAST_WRITE, IN_ATTRIB},
+ {FILE_NOTIFY_CHANGE_LAST_ACCESS, IN_ATTRIB},
+ {FILE_NOTIFY_CHANGE_EA, IN_ATTRIB},
+ {FILE_NOTIFY_CHANGE_SECURITY, IN_ATTRIB}
+};
+
+static uint32_t inotify_map(struct notify_entry *e)
+{
+ int i;
+ uint32_t out=0;
+ for (i=0;i<ARRAY_SIZE(inotify_mapping);i++) {
+ if (inotify_mapping[i].notify_mask & e->filter) {
+ out |= inotify_mapping[i].inotify_mask;
+ e->filter &= ~inotify_mapping[i].notify_mask;
+ }
+ }
+ return out;
+}
+
+/*
+ destroy a watch
+*/
+static int watch_destructor(struct inotify_watch_context *w)
+{
+ struct inotify_private *in = w->in;
+ int wd = w->wd;
+ DLIST_REMOVE(w->in->watches, w);
+
+ /* only rm the watch if its the last one with this wd */
+ for (w=in->watches;w;w=w->next) {
+ if (w->wd == wd) break;
+ }
+ if (w == NULL) {
+ inotify_rm_watch(in->fd, wd);
+ }
+ return 0;
+}
+
+
+/*
+ add a watch. The watch is removed when the caller calls
+ talloc_free() on *handle
+*/
+static NTSTATUS inotify_watch(struct sys_notify_context *ctx,
+ struct notify_entry *e,
+ sys_notify_callback_t callback,
+ void *private_data,
+ void *handle_p)
+{
+ struct inotify_private *in;
+ int wd;
+ uint32_t mask;
+ struct inotify_watch_context *w;
+ uint32_t filter = e->filter;
+ void **handle = (void **)handle_p;
+
+ /* maybe setup the inotify fd */
+ if (ctx->private_data == NULL) {
+ NTSTATUS status;
+ status = inotify_setup(ctx);
+ NT_STATUS_NOT_OK_RETURN(status);
+ }
+
+ in = talloc_get_type(ctx->private_data, struct inotify_private);
+
+ mask = inotify_map(e);
+ if (mask == 0) {
+ /* this filter can't be handled by inotify */
+ return NT_STATUS_INVALID_PARAMETER;
+ }
+
+ /* using IN_MASK_ADD allows us to cope with inotify() returning the same
+ watch descriptor for multiple watches on the same path */
+ mask |= (IN_MASK_ADD | IN_ONLYDIR);
+
+ /* get a new watch descriptor for this path */
+ wd = inotify_add_watch(in->fd, e->path, mask);
+ if (wd == -1) {
+ e->filter = filter;
+ return map_nt_error_from_unix_common(errno);
+ }
+
+ w = talloc(in, struct inotify_watch_context);
+ if (w == NULL) {
+ inotify_rm_watch(in->fd, wd);
+ e->filter = filter;
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ w->in = in;
+ w->wd = wd;
+ w->callback = callback;
+ w->private_data = private_data;
+ w->mask = mask;
+ w->filter = filter;
+ w->path = talloc_strdup(w, e->path);
+ if (w->path == NULL) {
+ inotify_rm_watch(in->fd, wd);
+ e->filter = filter;
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ (*handle) = w;
+
+ DLIST_ADD(in->watches, w);
+
+ /* the caller frees the handle to stop watching */
+ talloc_set_destructor(w, watch_destructor);
+
+ return NT_STATUS_OK;
+}
+
+
+static struct sys_notify_backend inotify = {
+ .name = "inotify",
+ .notify_watch = inotify_watch
+};
+
+/*
+ initialise the inotify module
+ */
+NTSTATUS sys_notify_inotify_init(TALLOC_CTX *);
+NTSTATUS sys_notify_inotify_init(TALLOC_CTX *ctx)
+{
+ /* register ourselves as a system inotify module */
+ return sys_notify_register(ctx, &inotify);
+}
diff --git a/source4/ntvfs/sysdep/sys_lease.c b/source4/ntvfs/sysdep/sys_lease.c
new file mode 100644
index 0000000..1ef72f1
--- /dev/null
+++ b/source4/ntvfs/sysdep/sys_lease.c
@@ -0,0 +1,152 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Stefan Metzmacher 2008
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ abstract the various kernel interfaces to leases (oplocks) into a
+ single Samba friendly interface
+*/
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "ntvfs/sysdep/sys_lease.h"
+#include "../lib/util/dlinklist.h"
+#include "param/param.h"
+#include "lib/util/samba_modules.h"
+
+#undef strcasecmp
+
+/* list of registered backends */
+static struct sys_lease_ops *backends;
+static uint32_t num_backends;
+
+#define LEASE_BACKEND "lease:backend"
+
+/*
+ initialise a system change notify backend
+*/
+_PUBLIC_ struct sys_lease_context *sys_lease_context_create(struct share_config *scfg,
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct imessaging_context *msg,
+ sys_lease_send_break_fn break_send)
+{
+ struct sys_lease_context *ctx;
+ const char *bname;
+ int i;
+ NTSTATUS status;
+ TALLOC_CTX * tmp_ctx;
+
+ if (num_backends == 0) {
+ return NULL;
+ }
+
+ if (ev == NULL) {
+ return NULL;
+ }
+
+ ctx = talloc_zero(mem_ctx, struct sys_lease_context);
+ if (ctx == NULL) {
+ return NULL;
+ }
+
+ tmp_ctx = talloc_new(ctx);
+ if (tmp_ctx == NULL) {
+ return NULL;
+ }
+
+ ctx->event_ctx = ev;
+ ctx->msg_ctx = msg;
+ ctx->break_send = break_send;
+
+ bname = share_string_option(tmp_ctx, scfg, LEASE_BACKEND, NULL);
+ if (!bname) {
+ talloc_free(ctx);
+ return NULL;
+ }
+
+ for (i=0;i<num_backends;i++) {
+ if (strcasecmp(backends[i].name, bname) == 0) {
+ ctx->ops = &backends[i];
+ break;
+ }
+ }
+
+ if (!ctx->ops) {
+ talloc_free(ctx);
+ return NULL;
+ }
+
+ status = ctx->ops->init(ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(ctx);
+ return NULL;
+ }
+
+ TALLOC_FREE(tmp_ctx);
+ return ctx;
+}
+
+/*
+ register a lease backend
+*/
+_PUBLIC_ NTSTATUS sys_lease_register(TALLOC_CTX *ctx,
+ const struct sys_lease_ops *backend)
+{
+ struct sys_lease_ops *b;
+ b = talloc_realloc(ctx, backends,
+ struct sys_lease_ops, num_backends+1);
+ NT_STATUS_HAVE_NO_MEMORY(b);
+ backends = b;
+ backends[num_backends] = *backend;
+ num_backends++;
+ return NT_STATUS_OK;
+}
+
+_PUBLIC_ NTSTATUS sys_lease_init(void)
+{
+ static bool initialized = false;
+#define _MODULE_PROTO(init) extern NTSTATUS init(TALLOC_CTX *);
+ STATIC_sys_lease_MODULES_PROTO;
+ init_module_fn static_init[] = { STATIC_sys_lease_MODULES };
+
+ if (initialized) return NT_STATUS_OK;
+ initialized = true;
+
+ run_init_functions(NULL, static_init);
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS sys_lease_setup(struct sys_lease_context *ctx,
+ struct opendb_entry *e)
+{
+ return ctx->ops->setup(ctx, e);
+}
+
+NTSTATUS sys_lease_update(struct sys_lease_context *ctx,
+ struct opendb_entry *e)
+{
+ return ctx->ops->update(ctx, e);
+}
+
+NTSTATUS sys_lease_remove(struct sys_lease_context *ctx,
+ struct opendb_entry *e)
+{
+ return ctx->ops->remove(ctx, e);
+}
diff --git a/source4/ntvfs/sysdep/sys_lease.h b/source4/ntvfs/sysdep/sys_lease.h
new file mode 100644
index 0000000..8b8d4bd
--- /dev/null
+++ b/source4/ntvfs/sysdep/sys_lease.h
@@ -0,0 +1,66 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Stefan Metzmacher 2008
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "param/share.h"
+
+struct sys_lease_context;
+struct opendb_entry;
+struct imessaging_context;
+struct tevent_context;
+
+typedef NTSTATUS (*sys_lease_send_break_fn)(struct imessaging_context *,
+ struct opendb_entry *,
+ uint8_t level);
+
+struct sys_lease_ops {
+ const char *name;
+ NTSTATUS (*init)(struct sys_lease_context *ctx);
+ NTSTATUS (*setup)(struct sys_lease_context *ctx,
+ struct opendb_entry *e);
+ NTSTATUS (*update)(struct sys_lease_context *ctx,
+ struct opendb_entry *e);
+ NTSTATUS (*remove)(struct sys_lease_context *ctx,
+ struct opendb_entry *e);
+};
+
+struct sys_lease_context {
+ struct tevent_context *event_ctx;
+ struct imessaging_context *msg_ctx;
+ sys_lease_send_break_fn break_send;
+ void *private_data; /* for use of backend */
+ const struct sys_lease_ops *ops;
+};
+
+NTSTATUS sys_lease_register(TALLOC_CTX *ctx, const struct sys_lease_ops *ops);
+NTSTATUS sys_lease_init(void);
+
+struct sys_lease_context *sys_lease_context_create(struct share_config *scfg,
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct imessaging_context *msg_ctx,
+ sys_lease_send_break_fn break_send);
+
+NTSTATUS sys_lease_setup(struct sys_lease_context *ctx,
+ struct opendb_entry *e);
+
+NTSTATUS sys_lease_update(struct sys_lease_context *ctx,
+ struct opendb_entry *e);
+
+NTSTATUS sys_lease_remove(struct sys_lease_context *ctx,
+ struct opendb_entry *e);
diff --git a/source4/ntvfs/sysdep/sys_lease_linux.c b/source4/ntvfs/sysdep/sys_lease_linux.c
new file mode 100644
index 0000000..54c0007
--- /dev/null
+++ b/source4/ntvfs/sysdep/sys_lease_linux.c
@@ -0,0 +1,215 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Stefan Metzmacher 2008
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ lease (oplock) implementation using fcntl F_SETLEASE on linux
+*/
+
+#include "includes.h"
+#include <tevent.h>
+#include "system/filesys.h"
+#include "ntvfs/sysdep/sys_lease.h"
+#include "ntvfs/ntvfs.h"
+#include "librpc/gen_ndr/ndr_opendb.h"
+#include "../lib/util/dlinklist.h"
+#include "cluster/cluster.h"
+
+NTSTATUS sys_lease_linux_init(TALLOC_CTX *);
+
+#define LINUX_LEASE_RT_SIGNAL (SIGRTMIN+1)
+
+struct linux_lease_pending {
+ struct linux_lease_pending *prev, *next;
+ struct sys_lease_context *ctx;
+ struct opendb_entry e;
+};
+
+/* the global linked list of pending leases */
+static struct linux_lease_pending *leases;
+
+static void linux_lease_signal_handler(struct tevent_context *ev_ctx,
+ struct tevent_signal *se,
+ int signum, int count,
+ void *_info, void *private_data)
+{
+ struct sys_lease_context *ctx = talloc_get_type(private_data,
+ struct sys_lease_context);
+ siginfo_t *info = (siginfo_t *)_info;
+ struct linux_lease_pending *c;
+ int got_fd = info->si_fd;
+
+ for (c = leases; c; c = c->next) {
+ int *fd = (int *)c->e.fd;
+
+ if (got_fd == *fd) {
+ break;
+ }
+ }
+
+ if (!c) {
+ return;
+ }
+
+ ctx->break_send(ctx->msg_ctx, &c->e, OPLOCK_BREAK_TO_NONE);
+}
+
+static int linux_lease_pending_destructor(struct linux_lease_pending *p)
+{
+ int ret;
+ int *fd = (int *)p->e.fd;
+
+ DLIST_REMOVE(leases, p);
+
+ if (*fd == -1) {
+ return 0;
+ }
+
+ ret = fcntl(*fd, F_SETLEASE, F_UNLCK);
+ if (ret == -1) {
+ DEBUG(0,("%s: failed to remove oplock: %s\n",
+ __FUNCTION__, strerror(errno)));
+ }
+
+ return 0;
+}
+
+static NTSTATUS linux_lease_init(struct sys_lease_context *ctx)
+{
+ struct tevent_signal *se;
+
+ se = tevent_add_signal(ctx->event_ctx, ctx,
+ LINUX_LEASE_RT_SIGNAL, SA_SIGINFO,
+ linux_lease_signal_handler, ctx);
+ NT_STATUS_HAVE_NO_MEMORY(se);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS linux_lease_setup(struct sys_lease_context *ctx,
+ struct opendb_entry *e)
+{
+ int ret;
+ int *fd = (int *)e->fd;
+ struct linux_lease_pending *p;
+
+ if (e->oplock_level == OPLOCK_NONE) {
+ e->fd = NULL;
+ return NT_STATUS_OK;
+ } else if (e->oplock_level == OPLOCK_LEVEL_II) {
+ /*
+ * the linux kernel doesn't support level2 oplocks
+ * so fix up the granted oplock level
+ */
+ e->oplock_level = OPLOCK_NONE;
+ e->allow_level_II_oplock = false;
+ e->fd = NULL;
+ return NT_STATUS_OK;
+ }
+
+ p = talloc(ctx, struct linux_lease_pending);
+ NT_STATUS_HAVE_NO_MEMORY(p);
+
+ p->ctx = ctx;
+ p->e = *e;
+
+ ret = fcntl(*fd, F_SETSIG, LINUX_LEASE_RT_SIGNAL);
+ if (ret == -1) {
+ talloc_free(p);
+ return map_nt_error_from_unix_common(errno);
+ }
+
+ ret = fcntl(*fd, F_SETLEASE, F_WRLCK);
+ if (ret == -1) {
+ talloc_free(p);
+ return map_nt_error_from_unix_common(errno);
+ }
+
+ DLIST_ADD(leases, p);
+
+ talloc_set_destructor(p, linux_lease_pending_destructor);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS linux_lease_remove(struct sys_lease_context *ctx,
+ struct opendb_entry *e);
+
+static NTSTATUS linux_lease_update(struct sys_lease_context *ctx,
+ struct opendb_entry *e)
+{
+ struct linux_lease_pending *c;
+
+ for (c = leases; c; c = c->next) {
+ if (c->e.fd == e->fd) {
+ break;
+ }
+ }
+
+ if (!c) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ /*
+ * set the fd pointer to NULL so that the caller
+ * will not call the remove function as the oplock
+ * is already removed
+ */
+ e->fd = NULL;
+
+ talloc_free(c);
+
+ return NT_STATUS_OK;
+}
+
+static NTSTATUS linux_lease_remove(struct sys_lease_context *ctx,
+ struct opendb_entry *e)
+{
+ struct linux_lease_pending *c;
+
+ for (c = leases; c; c = c->next) {
+ if (c->e.fd == e->fd) {
+ break;
+ }
+ }
+
+ if (!c) {
+ return NT_STATUS_OBJECT_NAME_NOT_FOUND;
+ }
+
+ talloc_free(c);
+
+ return NT_STATUS_OK;
+}
+
+static struct sys_lease_ops linux_lease_ops = {
+ .name = "linux",
+ .init = linux_lease_init,
+ .setup = linux_lease_setup,
+ .update = linux_lease_update,
+ .remove = linux_lease_remove
+};
+
+/*
+ initialise the linux lease module
+ */
+NTSTATUS sys_lease_linux_init(TALLOC_CTX *ctx)
+{
+ /* register ourselves as a system lease module */
+ return sys_lease_register(ctx, &linux_lease_ops);
+}
diff --git a/source4/ntvfs/sysdep/sys_notify.c b/source4/ntvfs/sysdep/sys_notify.c
new file mode 100644
index 0000000..6991461
--- /dev/null
+++ b/source4/ntvfs/sysdep/sys_notify.c
@@ -0,0 +1,151 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Andrew Tridgell 2006
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ abstract the various kernel interfaces to change notify into a
+ single Samba friendly interface
+*/
+
+#include "includes.h"
+#include "system/filesys.h"
+#include "ntvfs/sysdep/sys_notify.h"
+#include <tevent.h>
+#include "../lib/util/dlinklist.h"
+#include "param/param.h"
+#include "lib/util/samba_modules.h"
+
+#undef strcasecmp
+
+/* list of registered backends */
+static struct sys_notify_backend *backends;
+static uint32_t num_backends;
+
+#define NOTIFY_BACKEND "notify:backend"
+
+/*
+ initialise a system change notify backend
+*/
+_PUBLIC_ struct sys_notify_context *sys_notify_context_create(struct share_config *scfg,
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev)
+{
+ struct sys_notify_context *ctx;
+ const char *bname;
+ int i;
+
+ if (num_backends == 0) {
+ return NULL;
+ }
+
+ if (ev == NULL) {
+ return NULL;
+ }
+
+ ctx = talloc_zero(mem_ctx, struct sys_notify_context);
+ if (ctx == NULL) {
+ return NULL;
+ }
+
+ ctx->ev = ev;
+
+ bname = share_string_option(ctx, scfg, NOTIFY_BACKEND, NULL);
+ if (!bname) {
+ if (num_backends) {
+ bname = backends[0].name;
+ } else {
+ bname = "__unknown__";
+ }
+ }
+
+ for (i=0;i<num_backends;i++) {
+ char *enable_opt_name;
+ bool enabled;
+
+ enable_opt_name = talloc_asprintf(mem_ctx, "notify:%s",
+ backends[i].name);
+ enabled = share_bool_option(scfg, enable_opt_name, true);
+ talloc_free(enable_opt_name);
+
+ if (!enabled)
+ continue;
+
+ if (strcasecmp(backends[i].name, bname) == 0) {
+ bname = backends[i].name;
+ break;
+ }
+ }
+
+ ctx->name = bname;
+ ctx->notify_watch = NULL;
+
+ if (i < num_backends) {
+ ctx->notify_watch = backends[i].notify_watch;
+ }
+
+ return ctx;
+}
+
+/*
+ add a watch
+
+ note that this call must modify the e->filter and e->subdir_filter
+ bits to remove ones handled by this backend. Any remaining bits will
+ be handled by the generic notify layer
+*/
+_PUBLIC_ NTSTATUS sys_notify_watch(struct sys_notify_context *ctx,
+ struct notify_entry *e,
+ sys_notify_callback_t callback,
+ void *private_data, void *handle)
+{
+ if (!ctx->notify_watch) {
+ return NT_STATUS_INVALID_SYSTEM_SERVICE;
+ }
+ return ctx->notify_watch(ctx, e, callback, private_data, handle);
+}
+
+/*
+ register a notify backend
+*/
+_PUBLIC_ NTSTATUS sys_notify_register(TALLOC_CTX *ctx,
+ struct sys_notify_backend *backend)
+{
+ struct sys_notify_backend *b;
+ b = talloc_realloc(ctx, backends,
+ struct sys_notify_backend, num_backends+1);
+ NT_STATUS_HAVE_NO_MEMORY(b);
+ backends = b;
+ backends[num_backends] = *backend;
+ num_backends++;
+ return NT_STATUS_OK;
+}
+
+_PUBLIC_ NTSTATUS sys_notify_init(void)
+{
+ static bool initialized = false;
+#define _MODULE_PROTO(init) extern NTSTATUS init(TALLOC_CTX *);
+ STATIC_sys_notify_MODULES_PROTO;
+ init_module_fn static_init[] = { STATIC_sys_notify_MODULES };
+
+ if (initialized) return NT_STATUS_OK;
+ initialized = true;
+
+ run_init_functions(NULL, static_init);
+
+ return NT_STATUS_OK;
+}
diff --git a/source4/ntvfs/sysdep/sys_notify.h b/source4/ntvfs/sysdep/sys_notify.h
new file mode 100644
index 0000000..9e10f14
--- /dev/null
+++ b/source4/ntvfs/sysdep/sys_notify.h
@@ -0,0 +1,54 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ Copyright (C) Andrew Tridgell 2006
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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 "librpc/gen_ndr/notify.h"
+#include "param/share.h"
+
+struct sys_notify_context;
+
+typedef void (*sys_notify_callback_t)(struct sys_notify_context *,
+ void *, struct notify_event *ev);
+
+typedef NTSTATUS (*notify_watch_t)(struct sys_notify_context *ctx,
+ struct notify_entry *e,
+ sys_notify_callback_t callback,
+ void *private_data,
+ void *handle_p);
+
+struct sys_notify_context {
+ struct tevent_context *ev;
+ void *private_data; /* for use of backend */
+ const char *name;
+ notify_watch_t notify_watch;
+};
+
+struct sys_notify_backend {
+ const char *name;
+ notify_watch_t notify_watch;
+};
+
+NTSTATUS sys_notify_register(TALLOC_CTX *ctx,
+ struct sys_notify_backend *backend);
+struct sys_notify_context *sys_notify_context_create(struct share_config *scfg,
+ TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev);
+NTSTATUS sys_notify_watch(struct sys_notify_context *ctx, struct notify_entry *e,
+ sys_notify_callback_t callback, void *private_data,
+ void *handle);
+NTSTATUS sys_notify_init(void);
diff --git a/source4/ntvfs/sysdep/wscript_build b/source4/ntvfs/sysdep/wscript_build
new file mode 100644
index 0000000..bfb4e55
--- /dev/null
+++ b/source4/ntvfs/sysdep/wscript_build
@@ -0,0 +1,31 @@
+#!/usr/bin/env python
+
+bld.SAMBA_MODULE('sys_notify_inotify',
+ source='inotify.c',
+ subsystem='sys_notify',
+ init_function='sys_notify_inotify_init',
+ deps='events inotify',
+ enabled = bld.CONFIG_SET('HAVE_LINUX_INOTIFY')
+ )
+
+
+bld.SAMBA_SUBSYSTEM('sys_notify',
+ source='sys_notify.c',
+ deps='talloc tevent'
+ )
+
+
+bld.SAMBA_MODULE('sys_lease_linux',
+ source='sys_lease_linux.c',
+ deps='tevent',
+ subsystem='sys_lease',
+ init_function='sys_lease_linux_init',
+ enabled = bld.CONFIG_SET('HAVE_F_SETLEASE_DECL')
+ )
+
+
+bld.SAMBA_SUBSYSTEM('sys_lease',
+ source='sys_lease.c',
+ deps='talloc'
+ )
+
diff --git a/source4/ntvfs/sysdep/wscript_configure b/source4/ntvfs/sysdep/wscript_configure
new file mode 100644
index 0000000..2035884
--- /dev/null
+++ b/source4/ntvfs/sysdep/wscript_configure
@@ -0,0 +1,13 @@
+#!/usr/bin/env python
+
+import sys
+
+# Check for inotify support (Skip if we are SunOS)
+#NOTE: illumos provides sys/inotify.h but is not an exact match for linux
+host_os = sys.platform
+if host_os.rfind('sunos') == -1:
+ conf.CHECK_HEADERS('sys/inotify.h', add_headers=False)
+ if (conf.CONFIG_SET('HAVE_SYS_INOTIFY_H')):
+ conf.DEFINE('HAVE_LINUX_INOTIFY', 1)
+
+conf.CHECK_DECLS('SA_SIGINFO', headers='signal.h', reverse=True)
diff --git a/source4/ntvfs/unixuid/vfs_unixuid.c b/source4/ntvfs/unixuid/vfs_unixuid.c
new file mode 100644
index 0000000..a7729d2
--- /dev/null
+++ b/source4/ntvfs/unixuid/vfs_unixuid.c
@@ -0,0 +1,724 @@
+/*
+ Unix SMB/CIFS implementation.
+
+ A pass-through NTVFS module to setup a security context using unix
+ uid/gid
+
+ Copyright (C) Andrew Tridgell 2004
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 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 "system/filesys.h"
+#include "system/passwd.h"
+#include "auth/auth.h"
+#include "ntvfs/ntvfs.h"
+#include "libcli/wbclient/wbclient.h"
+#define TEVENT_DEPRECATED
+#include <tevent.h>
+#include "../lib/util/setid.h"
+
+NTSTATUS ntvfs_unixuid_init(TALLOC_CTX *);
+
+struct unixuid_private {
+ struct security_unix_token *last_sec_ctx;
+ struct security_token *last_token;
+};
+
+
+/*
+ pull the current security context into a security_unix_token
+*/
+static struct security_unix_token *save_unix_security(TALLOC_CTX *mem_ctx)
+{
+ struct security_unix_token *sec = talloc(mem_ctx, struct security_unix_token);
+ if (sec == NULL) {
+ return NULL;
+ }
+ sec->uid = geteuid();
+ sec->gid = getegid();
+ sec->ngroups = getgroups(0, NULL);
+ if (sec->ngroups == -1) {
+ talloc_free(sec);
+ return NULL;
+ }
+ sec->groups = talloc_array(sec, gid_t, sec->ngroups);
+ if (sec->groups == NULL) {
+ talloc_free(sec);
+ return NULL;
+ }
+
+ if (getgroups(sec->ngroups, sec->groups) != sec->ngroups) {
+ talloc_free(sec);
+ return NULL;
+ }
+
+ return sec;
+}
+
+/*
+ set the current security context from a security_unix_token
+*/
+static NTSTATUS set_unix_security(struct security_unix_token *sec)
+{
+ samba_seteuid(0);
+
+ if (samba_setgroups(sec->ngroups, sec->groups) != 0) {
+ DBG_ERR("*** samba_setgroups failed\n");
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ if (samba_setegid(sec->gid) != 0) {
+ DBG_ERR("*** samba_setegid(%u) failed\n", sec->gid);
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ if (samba_seteuid(sec->uid) != 0) {
+ DBG_ERR("*** samba_seteuid(%u) failed\n", sec->uid);
+ return NT_STATUS_ACCESS_DENIED;
+ }
+ return NT_STATUS_OK;
+}
+
+static int unixuid_nesting_level;
+
+/*
+ called at the start and end of a tevent nesting loop. Needs to save/restore
+ unix security context
+ */
+static int unixuid_event_nesting_hook(struct tevent_context *ev,
+ void *private_data,
+ uint32_t level,
+ bool begin,
+ void *stack_ptr,
+ const char *location)
+{
+ struct security_unix_token *sec_ctx;
+
+ if (unixuid_nesting_level == 0) {
+ /* we don't need to do anything unless we are nested
+ inside of a call in this module */
+ return 0;
+ }
+
+ if (begin) {
+ sec_ctx = save_unix_security(ev);
+ if (sec_ctx == NULL) {
+ DEBUG(0,("%s: Failed to save security context\n", location));
+ return -1;
+ }
+ *(struct security_unix_token **)stack_ptr = sec_ctx;
+ if (samba_seteuid(0) != 0 || samba_setegid(0) != 0) {
+ DEBUG(0,("%s: Failed to change to root\n", location));
+ return -1;
+ }
+ } else {
+ /* called when we come out of a nesting level */
+ NTSTATUS status;
+
+ sec_ctx = *(struct security_unix_token **)stack_ptr;
+ if (sec_ctx == NULL) {
+ /* this happens the first time this function
+ is called, as we install the hook while
+ inside an event in unixuid_connect() */
+ return 0;
+ }
+
+ sec_ctx = talloc_get_type_abort(sec_ctx, struct security_unix_token);
+ status = set_unix_security(sec_ctx);
+ talloc_free(sec_ctx);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("%s: Failed to revert security context (%s)\n",
+ location, nt_errstr(status)));
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+/*
+ form a security_unix_token from the current security_token
+*/
+static NTSTATUS nt_token_to_unix_security(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ struct security_token *token,
+ struct security_unix_token **sec)
+{
+ return security_token_to_unix_token(req, token, sec);
+}
+
+/*
+ setup our unix security context according to the session authentication info
+*/
+static NTSTATUS unixuid_setup_security(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, struct security_unix_token **sec)
+{
+ struct unixuid_private *priv = ntvfs->private_data;
+ struct security_token *token;
+ struct security_unix_token *newsec;
+ NTSTATUS status;
+
+ /* If we are asked to set up, but have not had a successful
+ * session setup or tree connect, then these may not be filled
+ * in. ACCESS_DENIED is the right error code here */
+ if (req->session_info == NULL || priv == NULL) {
+ return NT_STATUS_ACCESS_DENIED;
+ }
+
+ token = req->session_info->security_token;
+
+ *sec = save_unix_security(ntvfs);
+ if (*sec == NULL) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ if (token == priv->last_token) {
+ newsec = priv->last_sec_ctx;
+ } else {
+ status = nt_token_to_unix_security(ntvfs, req, token, &newsec);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(*sec);
+ return status;
+ }
+ if (priv->last_sec_ctx) {
+ talloc_free(priv->last_sec_ctx);
+ }
+ priv->last_sec_ctx = newsec;
+ priv->last_token = token;
+ talloc_steal(priv, newsec);
+ }
+
+ status = set_unix_security(newsec);
+ if (!NT_STATUS_IS_OK(status)) {
+ talloc_free(*sec);
+ return status;
+ }
+
+ return NT_STATUS_OK;
+}
+
+/*
+ this pass through macro operates on request contexts
+*/
+#define PASS_THRU_REQ(ntvfs, req, op, args) do { \
+ NTSTATUS status2; \
+ struct security_unix_token *sec; \
+ status = unixuid_setup_security(ntvfs, req, &sec); \
+ NT_STATUS_NOT_OK_RETURN(status); \
+ unixuid_nesting_level++; \
+ status = ntvfs_next_##op args; \
+ unixuid_nesting_level--; \
+ status2 = set_unix_security(sec); \
+ talloc_free(sec); \
+ if (!NT_STATUS_IS_OK(status2)) smb_panic("Unable to reset security context"); \
+} while (0)
+
+
+
+/*
+ connect to a share - used when a tree_connect operation comes in.
+*/
+static NTSTATUS unixuid_connect(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_tcon *tcon)
+{
+ struct unixuid_private *priv;
+ NTSTATUS status;
+
+ priv = talloc(ntvfs, struct unixuid_private);
+ if (!priv) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ priv->last_sec_ctx = NULL;
+ priv->last_token = NULL;
+ ntvfs->private_data = priv;
+
+ tevent_loop_set_nesting_hook(ntvfs->ctx->event_ctx,
+ unixuid_event_nesting_hook,
+ &unixuid_nesting_level);
+
+ /* we don't use PASS_THRU_REQ here, as the connect operation runs with
+ root privileges. This allows the backends to setup any database
+ links they might need during the connect. */
+ status = ntvfs_next_connect(ntvfs, req, tcon);
+
+ return status;
+}
+
+/*
+ disconnect from a share
+*/
+static NTSTATUS unixuid_disconnect(struct ntvfs_module_context *ntvfs)
+{
+ struct unixuid_private *priv = ntvfs->private_data;
+ NTSTATUS status;
+
+ talloc_free(priv);
+ ntvfs->private_data = NULL;
+
+ status = ntvfs_next_disconnect(ntvfs);
+
+ return status;
+}
+
+
+/*
+ delete a file
+*/
+static NTSTATUS unixuid_unlink(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_unlink *unl)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, unlink, (ntvfs, req, unl));
+
+ return status;
+}
+
+/*
+ ioctl interface
+*/
+static NTSTATUS unixuid_ioctl(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_ioctl *io)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, ioctl, (ntvfs, req, io));
+
+ return status;
+}
+
+/*
+ check if a directory exists
+*/
+static NTSTATUS unixuid_chkpath(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_chkpath *cp)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, chkpath, (ntvfs, req, cp));
+
+ return status;
+}
+
+/*
+ return info on a pathname
+*/
+static NTSTATUS unixuid_qpathinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_fileinfo *info)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, qpathinfo, (ntvfs, req, info));
+
+ return status;
+}
+
+/*
+ query info on a open file
+*/
+static NTSTATUS unixuid_qfileinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_fileinfo *info)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, qfileinfo, (ntvfs, req, info));
+
+ return status;
+}
+
+
+/*
+ set info on a pathname
+*/
+static NTSTATUS unixuid_setpathinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_setfileinfo *st)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, setpathinfo, (ntvfs, req, st));
+
+ return status;
+}
+
+/*
+ open a file
+*/
+static NTSTATUS unixuid_open(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_open *io)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, open, (ntvfs, req, io));
+
+ return status;
+}
+
+/*
+ create a directory
+*/
+static NTSTATUS unixuid_mkdir(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_mkdir *md)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, mkdir, (ntvfs, req, md));
+
+ return status;
+}
+
+/*
+ remove a directory
+*/
+static NTSTATUS unixuid_rmdir(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, struct smb_rmdir *rd)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, rmdir, (ntvfs, req, rd));
+
+ return status;
+}
+
+/*
+ rename a set of files
+*/
+static NTSTATUS unixuid_rename(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_rename *ren)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, rename, (ntvfs, req, ren));
+
+ return status;
+}
+
+/*
+ copy a set of files
+*/
+static NTSTATUS unixuid_copy(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, struct smb_copy *cp)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, copy, (ntvfs, req, cp));
+
+ return status;
+}
+
+/*
+ read from a file
+*/
+static NTSTATUS unixuid_read(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_read *rd)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, read, (ntvfs, req, rd));
+
+ return status;
+}
+
+/*
+ write to a file
+*/
+static NTSTATUS unixuid_write(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_write *wr)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, write, (ntvfs, req, wr));
+
+ return status;
+}
+
+/*
+ seek in a file
+*/
+static NTSTATUS unixuid_seek(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_seek *io)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, seek, (ntvfs, req, io));
+
+ return status;
+}
+
+/*
+ flush a file
+*/
+static NTSTATUS unixuid_flush(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_flush *io)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, flush, (ntvfs, req, io));
+
+ return status;
+}
+
+/*
+ close a file
+*/
+static NTSTATUS unixuid_close(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_close *io)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, close, (ntvfs, req, io));
+
+ return status;
+}
+
+/*
+ exit - closing files
+*/
+static NTSTATUS unixuid_exit(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, exit, (ntvfs, req));
+
+ return status;
+}
+
+/*
+ logoff - closing files
+*/
+static NTSTATUS unixuid_logoff(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req)
+{
+ struct unixuid_private *priv = ntvfs->private_data;
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, logoff, (ntvfs, req));
+
+ priv->last_token = NULL;
+
+ return status;
+}
+
+/*
+ async setup
+*/
+static NTSTATUS unixuid_async_setup(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ void *private_data)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, async_setup, (ntvfs, req, private_data));
+
+ return status;
+}
+
+/*
+ cancel an async request
+*/
+static NTSTATUS unixuid_cancel(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, cancel, (ntvfs, req));
+
+ return status;
+}
+
+/*
+ change notify
+*/
+static NTSTATUS unixuid_notify(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_notify *info)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, notify, (ntvfs, req, info));
+
+ return status;
+}
+
+/*
+ lock a byte range
+*/
+static NTSTATUS unixuid_lock(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_lock *lck)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, lock, (ntvfs, req, lck));
+
+ return status;
+}
+
+/*
+ set info on a open file
+*/
+static NTSTATUS unixuid_setfileinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req,
+ union smb_setfileinfo *info)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, setfileinfo, (ntvfs, req, info));
+
+ return status;
+}
+
+
+/*
+ return filesystem space info
+*/
+static NTSTATUS unixuid_fsinfo(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_fsinfo *fs)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, fsinfo, (ntvfs, req, fs));
+
+ return status;
+}
+
+/*
+ return print queue info
+*/
+static NTSTATUS unixuid_lpq(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_lpq *lpq)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, lpq, (ntvfs, req, lpq));
+
+ return status;
+}
+
+/*
+ list files in a directory matching a wildcard pattern
+*/
+static NTSTATUS unixuid_search_first(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_search_first *io,
+ void *search_private,
+ bool (*callback)(void *, const union smb_search_data *))
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, search_first, (ntvfs, req, io, search_private, callback));
+
+ return status;
+}
+
+/* continue a search */
+static NTSTATUS unixuid_search_next(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_search_next *io,
+ void *search_private,
+ bool (*callback)(void *, const union smb_search_data *))
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, search_next, (ntvfs, req, io, search_private, callback));
+
+ return status;
+}
+
+/* close a search */
+static NTSTATUS unixuid_search_close(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, union smb_search_close *io)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, search_close, (ntvfs, req, io));
+
+ return status;
+}
+
+/* SMBtrans - not used on file shares */
+static NTSTATUS unixuid_trans(struct ntvfs_module_context *ntvfs,
+ struct ntvfs_request *req, struct smb_trans2 *trans2)
+{
+ NTSTATUS status;
+
+ PASS_THRU_REQ(ntvfs, req, trans, (ntvfs, req, trans2));
+
+ return status;
+}
+
+/*
+ initialise the unixuid backend, registering ourselves with the ntvfs subsystem
+ */
+NTSTATUS ntvfs_unixuid_init(TALLOC_CTX *ctx)
+{
+ NTSTATUS ret;
+ struct ntvfs_ops ops;
+ NTVFS_CURRENT_CRITICAL_SIZES(vers);
+
+ ZERO_STRUCT(ops);
+
+ /* fill in all the operations */
+ ops.connect_fn = unixuid_connect;
+ ops.disconnect_fn = unixuid_disconnect;
+ ops.unlink_fn = unixuid_unlink;
+ ops.chkpath_fn = unixuid_chkpath;
+ ops.qpathinfo_fn = unixuid_qpathinfo;
+ ops.setpathinfo_fn = unixuid_setpathinfo;
+ ops.open_fn = unixuid_open;
+ ops.mkdir_fn = unixuid_mkdir;
+ ops.rmdir_fn = unixuid_rmdir;
+ ops.rename_fn = unixuid_rename;
+ ops.copy_fn = unixuid_copy;
+ ops.ioctl_fn = unixuid_ioctl;
+ ops.read_fn = unixuid_read;
+ ops.write_fn = unixuid_write;
+ ops.seek_fn = unixuid_seek;
+ ops.flush_fn = unixuid_flush;
+ ops.close_fn = unixuid_close;
+ ops.exit_fn = unixuid_exit;
+ ops.lock_fn = unixuid_lock;
+ ops.setfileinfo_fn = unixuid_setfileinfo;
+ ops.qfileinfo_fn = unixuid_qfileinfo;
+ ops.fsinfo_fn = unixuid_fsinfo;
+ ops.lpq_fn = unixuid_lpq;
+ ops.search_first_fn = unixuid_search_first;
+ ops.search_next_fn = unixuid_search_next;
+ ops.search_close_fn = unixuid_search_close;
+ ops.trans_fn = unixuid_trans;
+ ops.logoff_fn = unixuid_logoff;
+ ops.async_setup_fn = unixuid_async_setup;
+ ops.cancel_fn = unixuid_cancel;
+ ops.notify_fn = unixuid_notify;
+
+ ops.name = "unixuid";
+
+ /* we register under all 3 backend types, as we are not type specific */
+ ops.type = NTVFS_DISK;
+ ret = ntvfs_register(&ops, &vers);
+ if (!NT_STATUS_IS_OK(ret)) goto failed;
+
+ ops.type = NTVFS_PRINT;
+ ret = ntvfs_register(&ops, &vers);
+ if (!NT_STATUS_IS_OK(ret)) goto failed;
+
+ ops.type = NTVFS_IPC;
+ ret = ntvfs_register(&ops, &vers);
+ if (!NT_STATUS_IS_OK(ret)) goto failed;
+
+failed:
+ return ret;
+}
diff --git a/source4/ntvfs/unixuid/wscript_build b/source4/ntvfs/unixuid/wscript_build
new file mode 100644
index 0000000..56fd42d
--- /dev/null
+++ b/source4/ntvfs/unixuid/wscript_build
@@ -0,0 +1,9 @@
+#!/usr/bin/env python
+
+bld.SAMBA_MODULE('ntvfs_unixuid',
+ source='vfs_unixuid.c',
+ subsystem='ntvfs',
+ init_function='ntvfs_unixuid_init',
+ deps='auth_unix_token talloc'
+ )
+
diff --git a/source4/ntvfs/wscript_build b/source4/ntvfs/wscript_build
new file mode 100644
index 0000000..3b81216
--- /dev/null
+++ b/source4/ntvfs/wscript_build
@@ -0,0 +1,40 @@
+#!/usr/bin/env python
+
+bld.SAMBA_LIBRARY('ntvfs',
+ source='ntvfs_base.c ntvfs_generic.c ntvfs_interface.c ntvfs_util.c',
+ autoproto='ntvfs_proto.h',
+ deps='tevent samba-modules',
+ private_library=True,
+ enabled=bld.CONFIG_SET('WITH_NTVFS_FILESERVER')
+ )
+
+bld.RECURSE('posix')
+if bld.CONFIG_SET('WITH_NTVFS_FILESERVER'):
+ bld.RECURSE('common')
+ bld.RECURSE('unixuid')
+ bld.RECURSE('sysdep')
+
+ bld.SAMBA_MODULE('ntvfs_cifs',
+ source='cifs/vfs_cifs.c',
+ subsystem='ntvfs',
+ init_function='ntvfs_cifs_init',
+ deps='LIBCLI_SMB smbclient-raw param_options'
+ )
+
+
+ bld.SAMBA_MODULE('ntvfs_simple',
+ source='simple/vfs_simple.c simple/svfs_util.c',
+ autoproto='simple/proto.h',
+ subsystem='ntvfs',
+ init_function='ntvfs_simple_init',
+ deps='talloc'
+ )
+
+
+ bld.SAMBA_MODULE('ntvfs_ipc',
+ source='ipc/vfs_ipc.c ipc/ipc_rap.c ipc/rap_server.c',
+ autoproto='ipc/proto.h',
+ subsystem='ntvfs',
+ init_function='ntvfs_ipc_init',
+ deps='NDR_NAMED_PIPE_AUTH npa_tstream gssapi samba-credentials DCERPC_SHARE'
+ )