From ef2e26c91b80556af033d3335e55f5dfa6fff31d Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Wed, 13 Aug 2003 01:53:07 +0000 Subject: first public release of samba4 code (This used to be commit b0510b5428b3461aeb9bbe3cc95f62fc73e2b97f) --- source4/ntvfs/README | 26 ++ source4/ntvfs/cifs/README | 5 + source4/ntvfs/cifs/vfs_cifs.c | 723 +++++++++++++++++++++++++++++++ source4/ntvfs/ipc/README | 5 + source4/ntvfs/ipc/vfs_ipc.c | 295 +++++++++++++ source4/ntvfs/ntvfs_base.c | 145 +++++++ source4/ntvfs/ntvfs_dfs.c | 117 +++++ source4/ntvfs/ntvfs_generic.c | 544 ++++++++++++++++++++++++ source4/ntvfs/ntvfs_util.c | 26 ++ source4/ntvfs/posix/vfs_posix.c | 151 +++++++ source4/ntvfs/print/README | 3 + source4/ntvfs/print/vfs_print.c | 104 +++++ source4/ntvfs/reference/ref.h | 36 ++ source4/ntvfs/reference/ref_util.c | 142 +++++++ source4/ntvfs/reference/vfs_ref.c | 843 ++++++++++++++++++++++++++++++++++++ source4/ntvfs/simple/svfs.h | 28 ++ source4/ntvfs/simple/svfs_util.c | 162 +++++++ source4/ntvfs/simple/vfs_simple.c | 848 +++++++++++++++++++++++++++++++++++++ 18 files changed, 4203 insertions(+) create mode 100644 source4/ntvfs/README create mode 100644 source4/ntvfs/cifs/README create mode 100644 source4/ntvfs/cifs/vfs_cifs.c create mode 100644 source4/ntvfs/ipc/README create mode 100644 source4/ntvfs/ipc/vfs_ipc.c create mode 100644 source4/ntvfs/ntvfs_base.c create mode 100644 source4/ntvfs/ntvfs_dfs.c create mode 100644 source4/ntvfs/ntvfs_generic.c create mode 100644 source4/ntvfs/ntvfs_util.c create mode 100644 source4/ntvfs/posix/vfs_posix.c create mode 100644 source4/ntvfs/print/README create mode 100644 source4/ntvfs/print/vfs_print.c create mode 100644 source4/ntvfs/reference/ref.h create mode 100644 source4/ntvfs/reference/ref_util.c create mode 100644 source4/ntvfs/reference/vfs_ref.c create mode 100644 source4/ntvfs/simple/svfs.h create mode 100644 source4/ntvfs/simple/svfs_util.c create mode 100644 source4/ntvfs/simple/vfs_simple.c (limited to 'source4/ntvfs') diff --git a/source4/ntvfs/README b/source4/ntvfs/README new file mode 100644 index 0000000000..c86c9a0050 --- /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 0000000000..a43ad09bdf --- /dev/null +++ b/source4/ntvfs/cifs/README @@ -0,0 +1,5 @@ +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 poeple may +find it useful as a CIFS gateway. + diff --git a/source4/ntvfs/cifs/vfs_cifs.c b/source4/ntvfs/cifs/vfs_cifs.c new file mode 100644 index 0000000000..9a17336519 --- /dev/null +++ b/source4/ntvfs/cifs/vfs_cifs.c @@ -0,0 +1,723 @@ +/* + Unix SMB/CIFS implementation. + + CIFS-on-CIFS NTVFS filesystem backend + + Copyright (C) Andrew Tridgell 2003 + Copyright (C) James J Myers 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 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +/* + this implements a CIFS->CIFS NTVFS filesystem backend. + +*/ + +#include "includes.h" + +/* this is stored in ntvfs_private */ +struct cvfs_private { + struct cli_tree *tree; + struct cli_transport *transport; + struct tcon_context *conn; + const char *map_calls; +}; + + +/* a structure used to pass information to an async handler */ +struct async_info { + struct request_context *req; + void *parms; +}; + +/* + an idle function to cope with messages from the smbd client while + waiting for a reply from the server + this function won't be needed once all of the cifs backend + and the core of smbd is converted to use async calls +*/ +static void idle_func(struct cli_transport *transport, void *p_private) +{ + struct cvfs_private *private = p_private; + if (socket_pending(private->conn->smb->socket.fd)) { + smbd_process_async(private->conn->smb); + } +} + +/* + a handler for oplock break events from the server - these need to be passed + along to the client + */ +static BOOL oplock_handler(struct cli_transport *transport, uint16 tid, uint16 fnum, uint8 level, void *p_private) +{ + struct cvfs_private *private = p_private; + + DEBUG(5,("vfs_cifs: sending oplock break level %d for fnum %d\n", level, fnum)); + return req_send_oplock_break(private->conn, fnum, level); +} + +/* + a handler for read events on a connection to a backend server +*/ +static void cifs_socket_handler(struct event_context *ev, struct fd_event *fde, time_t t, uint16 flags) +{ + struct tcon_context *conn = fde->private; + struct cvfs_private *private = conn->ntvfs_private; + + DEBUG(5,("cifs_socket_handler event on fd %d\n", fde->fd)); + + if (!cli_request_receive_next(private->transport)) { + /* the connection to our server is dead */ + close_cnum(conn); + } +} + +/* + connect to a share - used when a tree_connect operation comes in. +*/ +static NTSTATUS cvfs_connect(struct request_context *req, const char *sharename) +{ + struct tcon_context *conn = req->conn; + NTSTATUS status; + struct cvfs_private *private; + char *map_calls; + struct fd_event fde; + const char *host, *user, *pass, *domain, *remote_share; + + /* Here we need to determine which server to connect to. + * For now we use parametric options, type cifs. + * Later we will use security=server and auth_server.c. + */ + host = lp_parm_string(req->conn->service, "cifs", "server"); + user = lp_parm_string(req->conn->service, "cifs", "user"); + pass = lp_parm_string(req->conn->service, "cifs", "password"); + domain = lp_parm_string(req->conn->service, "cifs", "domain"); + remote_share = lp_parm_string(req->conn->service, "cifs", "share"); + if (!remote_share) { + remote_share = sharename; + } + + if (!host || !user || !pass || !domain) { + DEBUG(1,("CIFS backend: You must supply server, user, password and domain\n")); + return NT_STATUS_INVALID_PARAMETER; + } + + private = talloc(req->conn->mem_ctx, sizeof(struct cvfs_private)); + if (!private) { + return NT_STATUS_NO_MEMORY; + } + ZERO_STRUCTP(private); + + req->conn->ntvfs_private = (void *)private; + + status = cli_tree_full_connection(&private->tree, + "vfs_cifs", + host, + 0, + remote_share, "?????", + user, domain, + pass); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + private->transport = private->tree->session->transport; + private->tree->session->pid = SVAL(req->in.hdr, HDR_PID); + private->conn = req->conn; + + conn->fs_type = talloc_strdup(conn->mem_ctx, "NTFS"); + conn->dev_type = talloc_strdup(conn->mem_ctx, "A:"); + + map_calls = lp_parm_string(req->conn->service, "cifs", "map calls"); + if (map_calls) { + private->map_calls = talloc_strdup(conn->mem_ctx, map_calls); + } + + /* if we are mapping trans2, then we need to not give a trans2 + pointer in the operations structure */ + if (private->map_calls && in_list("trans2", private->map_calls, True)) { + conn->ntvfs_ops->trans2 = NULL; + } + + /* we need to tell the event loop that we wish to receive read events + on our SMB connection to the server */ + fde.fd = private->transport->socket->fd; + fde.flags = EVENT_FD_READ; + fde.private = req->conn; + fde.handler = cifs_socket_handler; + + event_add_fd(conn->smb->events, &fde); + + /* we need to receive oplock break requests from the server */ + cli_oplock_handler(private->transport, oplock_handler, private); + cli_transport_idle_handler(private->transport, idle_func, 100, private); + + return NT_STATUS_OK; +} + +/* + disconnect from a share +*/ +static NTSTATUS cvfs_disconnect(struct tcon_context *conn) +{ + struct cvfs_private *private = conn->ntvfs_private; + + event_remove_fd_all(conn->smb->events, private->transport->socket->fd); + smb_tree_disconnect(private->tree); + cli_tree_close(private->tree); + + return NT_STATUS_OK; +} + +/* + 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 cli_request *c_req) +{ + struct async_info *async = c_req->async.private; + struct request_context *req = async->req; + req->async.status = cli_request_simple_recv(c_req); + req->async.send_fn(req); +} + + +/* save some typing for the simple functions */ +#define ASYNC_RECV_TAIL(io, async_fn) do { \ + if (!c_req) return NT_STATUS_UNSUCCESSFUL; \ + { \ + struct async_info *async = c_req->async.private; \ + async = talloc(req->mem_ctx, sizeof(*async)); \ + if (!async) return NT_STATUS_NO_MEMORY; \ + async->parms = io; \ + async->req = req; \ + c_req->async.private = async; \ + } \ + c_req->async.fn = async_fn; \ + req->control_flags |= REQ_CONTROL_ASYNC; \ + return NT_STATUS_OK; \ +} while (0) + +#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 request_context *req, struct smb_unlink *unl) +{ + struct cvfs_private *private = req->conn->ntvfs_private; + struct cli_request *c_req; + + /* see if the front end will allow us to perform this + function asynchronously. */ + if (!req->async.send_fn) { + return smb_raw_unlink(private->tree, unl); + } + + c_req = smb_raw_unlink_send(private->tree, unl); + + SIMPLE_ASYNC_TAIL; +} + +/* + a handler for async ioctl replies + */ +static void async_ioctl(struct cli_request *c_req) +{ + struct async_info *async = c_req->async.private; + struct request_context *req = async->req; + req->async.status = smb_raw_ioctl_recv(c_req, req->mem_ctx, async->parms); + req->async.send_fn(req); +} + +/* + ioctl interface +*/ +static NTSTATUS cvfs_ioctl(struct request_context *req, struct smb_ioctl *io) +{ + struct cvfs_private *private = req->conn->ntvfs_private; + struct cli_request *c_req; + + /* see if the front end will allow us to perform this + function asynchronously. */ + if (!req->async.send_fn) { + return smb_raw_ioctl(private->tree, req->mem_ctx, io); + } + + c_req = smb_raw_ioctl_send(private->tree, io); + + ASYNC_RECV_TAIL(io, async_ioctl); +} + +/* + check if a directory exists +*/ +static NTSTATUS cvfs_chkpath(struct request_context *req, struct smb_chkpath *cp) +{ + struct cvfs_private *private = req->conn->ntvfs_private; + struct cli_request *c_req; + + if (!req->async.send_fn) { + return smb_raw_chkpath(private->tree, cp); + } + + c_req = smb_raw_chkpath_send(private->tree, cp); + + SIMPLE_ASYNC_TAIL; +} + +/* + a handler for async qpathinfo replies + */ +static void async_qpathinfo(struct cli_request *c_req) +{ + struct async_info *async = c_req->async.private; + struct request_context *req = async->req; + req->async.status = smb_raw_pathinfo_recv(c_req, req->mem_ctx, async->parms); + req->async.send_fn(req); +} + +/* + return info on a pathname +*/ +static NTSTATUS cvfs_qpathinfo(struct request_context *req, union smb_fileinfo *info) +{ + struct cvfs_private *private = req->conn->ntvfs_private; + struct cli_request *c_req; + + if (!req->async.send_fn) { + return smb_raw_pathinfo(private->tree, req->mem_ctx, info); + } + + c_req = smb_raw_pathinfo_send(private->tree, info); + + ASYNC_RECV_TAIL(info, async_qpathinfo); +} + +/* + a handler for async qfileinfo replies + */ +static void async_qfileinfo(struct cli_request *c_req) +{ + struct async_info *async = c_req->async.private; + struct request_context *req = async->req; + req->async.status = smb_raw_fileinfo_recv(c_req, req->mem_ctx, async->parms); + req->async.send_fn(req); +} + +/* + query info on a open file +*/ +static NTSTATUS cvfs_qfileinfo(struct request_context *req, union smb_fileinfo *info) +{ + struct cvfs_private *private = req->conn->ntvfs_private; + struct cli_request *c_req; + + if (!req->async.send_fn) { + return smb_raw_fileinfo(private->tree, req->mem_ctx, info); + } + + c_req = smb_raw_fileinfo_send(private->tree, info); + + ASYNC_RECV_TAIL(info, async_qfileinfo); +} + + +/* + set info on a pathname +*/ +static NTSTATUS cvfs_setpathinfo(struct request_context *req, union smb_setfileinfo *st) +{ + struct cvfs_private *private = req->conn->ntvfs_private; + struct cli_request *c_req; + + if (!req->async.send_fn) { + return smb_raw_setpathinfo(private->tree, st); + } + + c_req = smb_raw_setpathinfo_send(private->tree, st); + + SIMPLE_ASYNC_TAIL; +} + + +/* + a handler for async open replies + */ +static void async_open(struct cli_request *c_req) +{ + struct async_info *async = c_req->async.private; + struct request_context *req = async->req; + req->async.status = smb_raw_open_recv(c_req, req->mem_ctx, async->parms); + req->async.send_fn(req); +} + +/* + open a file +*/ +static NTSTATUS cvfs_open(struct request_context *req, union smb_open *io) +{ + struct cvfs_private *private = req->conn->ntvfs_private; + struct cli_request *c_req; + + if (private->map_calls && in_list("open", private->map_calls, True) && + io->generic.level != RAW_OPEN_GENERIC) { + return ntvfs_map_open(req, io); + } + + if (!req->async.send_fn) { + return smb_raw_open(private->tree, req->mem_ctx, io); + } + + c_req = smb_raw_open_send(private->tree, io); + + ASYNC_RECV_TAIL(io, async_open); +} + +/* + create a directory +*/ +static NTSTATUS cvfs_mkdir(struct request_context *req, union smb_mkdir *md) +{ + struct cvfs_private *private = req->conn->ntvfs_private; + struct cli_request *c_req; + + if (!req->async.send_fn) { + return smb_raw_mkdir(private->tree, md); + } + + c_req = smb_raw_mkdir_send(private->tree, md); + + SIMPLE_ASYNC_TAIL; +} + +/* + remove a directory +*/ +static NTSTATUS cvfs_rmdir(struct request_context *req, struct smb_rmdir *rd) +{ + struct cvfs_private *private = req->conn->ntvfs_private; + struct cli_request *c_req; + + if (!req->async.send_fn) { + return smb_raw_rmdir(private->tree, rd); + } + c_req = smb_raw_rmdir_send(private->tree, rd); + + SIMPLE_ASYNC_TAIL; +} + +/* + rename a set of files +*/ +static NTSTATUS cvfs_rename(struct request_context *req, struct smb_rename *ren) +{ + struct cvfs_private *private = req->conn->ntvfs_private; + struct cli_request *c_req; + + if (!req->async.send_fn) { + return smb_raw_rename(private->tree, ren); + } + + c_req = smb_raw_rename_send(private->tree, ren); + + SIMPLE_ASYNC_TAIL; +} + +/* + copy a set of files +*/ +static NTSTATUS cvfs_copy(struct request_context *req, struct smb_copy *cp) +{ + return NT_STATUS_NOT_SUPPORTED; +} + +/* + a handler for async read replies + */ +static void async_read(struct cli_request *c_req) +{ + struct async_info *async = c_req->async.private; + struct request_context *req = async->req; + req->async.status = smb_raw_read_recv(c_req, async->parms); + req->async.send_fn(req); +} + +/* + read from a file +*/ +static NTSTATUS cvfs_read(struct request_context *req, union smb_read *rd) +{ + struct cvfs_private *private = req->conn->ntvfs_private; + struct cli_request *c_req; + + if (!req->async.send_fn) { + return smb_raw_read(private->tree, rd); + } + + c_req = smb_raw_read_send(private->tree, rd); + + ASYNC_RECV_TAIL(rd, async_read); +} + +/* + a handler for async write replies + */ +static void async_write(struct cli_request *c_req) +{ + struct async_info *async = c_req->async.private; + struct request_context *req = async->req; + req->async.status = smb_raw_write_recv(c_req, async->parms); + req->async.send_fn(req); +} + +/* + write to a file +*/ +static NTSTATUS cvfs_write(struct request_context *req, union smb_write *wr) +{ + struct cvfs_private *private = req->conn->ntvfs_private; + struct cli_request *c_req; + + if (!req->async.send_fn) { + return smb_raw_write(private->tree, wr); + } + + c_req = smb_raw_write_send(private->tree, wr); + + ASYNC_RECV_TAIL(wr, async_write); +} + +/* + seek in a file +*/ +static NTSTATUS cvfs_seek(struct request_context *req, struct smb_seek *io) +{ + return NT_STATUS_NOT_SUPPORTED; +} + +/* + flush a file +*/ +static NTSTATUS cvfs_flush(struct request_context *req, struct smb_flush *io) +{ + return NT_STATUS_OK; +} + +/* + close a file +*/ +static NTSTATUS cvfs_close(struct request_context *req, union smb_close *io) +{ + struct cvfs_private *private = req->conn->ntvfs_private; + struct cli_request *c_req; + + if (!req->async.send_fn) { + return smb_raw_close(private->tree, io); + } + + c_req = smb_raw_close_send(private->tree, io); + + SIMPLE_ASYNC_TAIL; +} + +/* + exit - closing files? +*/ +static NTSTATUS cvfs_exit(struct request_context *req) +{ + return NT_STATUS_NOT_SUPPORTED; +} + +/* + lock a byte range +*/ +static NTSTATUS cvfs_lock(struct request_context *req, union smb_lock *lck) +{ + struct cvfs_private *private = req->conn->ntvfs_private; + struct cli_request *c_req; + + if (!req->async.send_fn) { + return smb_raw_lock(private->tree, lck); + } + + c_req = smb_raw_lock_send(private->tree, lck); + SIMPLE_ASYNC_TAIL; +} + +/* + set info on a open file +*/ +static NTSTATUS cvfs_setfileinfo(struct request_context *req, + union smb_setfileinfo *info) +{ + struct cvfs_private *private = req->conn->ntvfs_private; + struct cli_request *c_req; + + if (!req->async.send_fn) { + return smb_raw_setfileinfo(private->tree, info); + } + c_req = smb_raw_setfileinfo_send(private->tree, info); + + SIMPLE_ASYNC_TAIL; +} + + +/* + a handler for async fsinfo replies + */ +static void async_fsinfo(struct cli_request *c_req) +{ + struct async_info *async = c_req->async.private; + struct request_context *req = async->req; + req->async.status = smb_raw_fsinfo_recv(c_req, req->mem_ctx, async->parms); + req->async.send_fn(req); +} + +/* + return filesystem space info +*/ +static NTSTATUS cvfs_fsinfo(struct request_context *req, union smb_fsinfo *fs) +{ + struct cvfs_private *private = req->conn->ntvfs_private; + struct cli_request *c_req; + + if (!req->async.send_fn) { + return smb_raw_fsinfo(private->tree, req->mem_ctx, fs); + } + + c_req = smb_raw_fsinfo_send(private->tree, req->mem_ctx, fs); + + ASYNC_RECV_TAIL(fs, async_fsinfo); +} + +/* + return print queue info +*/ +static NTSTATUS cvfs_lpq(struct request_context *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 request_context *req, union smb_search_first *io, + void *search_private, + BOOL (*callback)(void *, union smb_search_data *)) +{ + struct cvfs_private *private = req->conn->ntvfs_private; + + return smb_raw_search_first(private->tree, req->mem_ctx, io, search_private, callback); +} + +/* continue a search */ +static NTSTATUS cvfs_search_next(struct request_context *req, union smb_search_next *io, + void *search_private, + BOOL (*callback)(void *, union smb_search_data *)) +{ + struct cvfs_private *private = req->conn->ntvfs_private; + + return smb_raw_search_next(private->tree, req->mem_ctx, io, search_private, callback); +} + +/* close a search */ +static NTSTATUS cvfs_search_close(struct request_context *req, union smb_search_close *io) +{ + struct cvfs_private *private = req->conn->ntvfs_private; + + return smb_raw_search_close(private->tree, io); +} + +/* + a handler for async trans2 replies + */ +static void async_trans2(struct cli_request *c_req) +{ + struct async_info *async = c_req->async.private; + struct request_context *req = async->req; + req->async.status = smb_raw_trans2_recv(c_req, req->mem_ctx, async->parms); + req->async.send_fn(req); +} + +/* raw trans2 */ +static NTSTATUS cvfs_trans2(struct request_context *req, struct smb_trans2 *trans2) +{ + struct cvfs_private *private = req->conn->ntvfs_private; + struct cli_request *c_req; + + if (!req->async.send_fn) { + return smb_raw_trans2(private->tree, req->mem_ctx, trans2); + } + + c_req = smb_raw_trans2_send(private->tree, trans2); + + ASYNC_RECV_TAIL(trans2, async_trans2); +} + +/* + initialise the CIFS->CIFS backend, registering ourselves with the ntvfs subsystem + */ +BOOL cifs_vfs_init(void) +{ + BOOL ret; + struct ntvfs_ops ops; + + ZERO_STRUCT(ops); + + /* fill in all the operations */ + ops.connect = cvfs_connect; + ops.disconnect = cvfs_disconnect; + ops.unlink = cvfs_unlink; + ops.chkpath = cvfs_chkpath; + ops.qpathinfo = cvfs_qpathinfo; + ops.setpathinfo = cvfs_setpathinfo; + ops.open = cvfs_open; + ops.mkdir = cvfs_mkdir; + ops.rmdir = cvfs_rmdir; + ops.rename = cvfs_rename; + ops.copy = cvfs_copy; + ops.ioctl = cvfs_ioctl; + ops.read = cvfs_read; + ops.write = cvfs_write; + ops.seek = cvfs_seek; + ops.flush = cvfs_flush; + ops.close = cvfs_close; + ops.exit = cvfs_exit; + ops.lock = cvfs_lock; + ops.setfileinfo = cvfs_setfileinfo; + ops.qfileinfo = cvfs_qfileinfo; + ops.fsinfo = cvfs_fsinfo; + ops.lpq = cvfs_lpq; + ops.search_first = cvfs_search_first; + ops.search_next = cvfs_search_next; + ops.search_close = cvfs_search_close; + + /* only define this one for trans2 testing */ + ops.trans2 = cvfs_trans2; + + /* register ourselves with the NTVFS subsystem. We register under the name 'cifs'. */ + + ret = ntvfs_register("cifs", NTVFS_DISK, &ops); + + if (!ret) { + DEBUG(0,("Failed to register CIFS backend!\n")); + return False; + } + + return True; +} diff --git a/source4/ntvfs/ipc/README b/source4/ntvfs/ipc/README new file mode 100644 index 0000000000..059a7146e5 --- /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/vfs_ipc.c b/source4/ntvfs/ipc/vfs_ipc.c new file mode 100644 index 0000000000..fe310d104e --- /dev/null +++ b/source4/ntvfs/ipc/vfs_ipc.c @@ -0,0 +1,295 @@ +/* + Unix SMB/CIFS implementation. + default IPC$ NTVFS 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 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +/* + this implements the IPC$ backend, called by the NTVFS subsystem to + handle requests on IPC$ shares +*/ + + +#include "includes.h" + +/* + connect to a share - always works +*/ +static NTSTATUS ipc_connect(struct request_context *req, const char *sharename) +{ + struct tcon_context *conn = req->conn; + + conn->fs_type = talloc_strdup(conn->mem_ctx, "IPC"); + conn->dev_type = talloc_strdup(conn->mem_ctx, "IPC"); + + return NT_STATUS_OK; +} + +/* + disconnect from a share +*/ +static NTSTATUS ipc_disconnect(struct tcon_context *tcon) +{ + return NT_STATUS_OK; +} + +/* + delete a file +*/ +static NTSTATUS ipc_unlink(struct request_context *req, struct smb_unlink *unl) +{ + return NT_STATUS_ACCESS_DENIED; +} + + +/* + ioctl interface - we don't do any +*/ +static NTSTATUS ipc_ioctl(struct request_context *req, struct smb_ioctl *io) +{ + return NT_STATUS_ACCESS_DENIED; +} + +/* + check if a directory exists +*/ +static NTSTATUS ipc_chkpath(struct request_context *req, struct smb_chkpath *cp) +{ + return NT_STATUS_ACCESS_DENIED; +} + +/* + return info on a pathname +*/ +static NTSTATUS ipc_qpathinfo(struct request_context *req, union smb_fileinfo *info) +{ + return NT_STATUS_ACCESS_DENIED; +} + +/* + set info on a pathname +*/ +static NTSTATUS ipc_setpathinfo(struct request_context *req, union smb_setfileinfo *st) +{ + return NT_STATUS_ACCESS_DENIED; +} + +/* + open a file +*/ +static NTSTATUS ipc_open(struct request_context *req, union smb_open *oi) +{ + return NT_STATUS_ACCESS_DENIED; +} + +/* + create a directory +*/ +static NTSTATUS ipc_mkdir(struct request_context *req, union smb_mkdir *md) +{ + return NT_STATUS_ACCESS_DENIED; +} + +/* + remove a directory +*/ +static NTSTATUS ipc_rmdir(struct request_context *req, struct smb_rmdir *rd) +{ + return NT_STATUS_ACCESS_DENIED; +} + +/* + rename a set of files +*/ +static NTSTATUS ipc_rename(struct request_context *req, struct smb_rename *ren) +{ + return NT_STATUS_ACCESS_DENIED; +} + +/* + copy a set of files +*/ +static NTSTATUS ipc_copy(struct request_context *req, struct smb_copy *cp) +{ + return NT_STATUS_ACCESS_DENIED; +} + +/* + read from a file +*/ +static NTSTATUS ipc_read(struct request_context *req, union smb_read *rd) +{ + return NT_STATUS_ACCESS_DENIED; +} + +/* + write to a file +*/ +static NTSTATUS ipc_write(struct request_context *req, union smb_write *wr) +{ + return NT_STATUS_ACCESS_DENIED; +} + +/* + seek in a file +*/ +static NTSTATUS ipc_seek(struct request_context *req, struct smb_seek *io) +{ + return NT_STATUS_ACCESS_DENIED; +} + +/* + flush a file +*/ +static NTSTATUS ipc_flush(struct request_context *req, struct smb_flush *io) +{ + return NT_STATUS_ACCESS_DENIED; +} + +/* + close a file +*/ +static NTSTATUS ipc_close(struct request_context *req, union smb_close *io) +{ + return NT_STATUS_ACCESS_DENIED; +} + +/* + exit - closing files? +*/ +static NTSTATUS ipc_exit(struct request_context *req) +{ + return NT_STATUS_ACCESS_DENIED; +} + +/* + lock a byte range +*/ +static NTSTATUS ipc_lock(struct request_context *req, union smb_lock *lck) +{ + return NT_STATUS_ACCESS_DENIED; +} + +/* + set info on a open file +*/ +static NTSTATUS ipc_setfileinfo(struct request_context *req, union smb_setfileinfo *info) +{ + return NT_STATUS_ACCESS_DENIED; +} + +/* + query info on a open file +*/ +static NTSTATUS ipc_qfileinfo(struct request_context *req, union smb_fileinfo *info) +{ + return NT_STATUS_ACCESS_DENIED; +} + + +/* + return filesystem info +*/ +static NTSTATUS ipc_fsinfo(struct request_context *req, union smb_fsinfo *fs) +{ + return NT_STATUS_ACCESS_DENIED; +} + +/* + return print queue info +*/ +static NTSTATUS ipc_lpq(struct request_context *req, union smb_lpq *lpq) +{ + return NT_STATUS_ACCESS_DENIED; +} + +/* + list files in a directory matching a wildcard pattern +*/ +NTSTATUS ipc_search_first(struct request_context *req, union smb_search_first *io, + void *search_private, + BOOL (*callback)(void *, union smb_search_data *)) +{ + return NT_STATUS_ACCESS_DENIED; +} + +/* + continue listing files in a directory +*/ +NTSTATUS ipc_search_next(struct request_context *req, union smb_search_next *io, + void *search_private, + BOOL (*callback)(void *, union smb_search_data *)) +{ + return NT_STATUS_ACCESS_DENIED; +} + +/* + end listing files in a directory +*/ +NTSTATUS ipc_search_close(struct request_context *req, union smb_search_close *io) +{ + return NT_STATUS_ACCESS_DENIED; +} + + +/* + initialialise the IPC backend, registering ourselves with the ntvfs subsystem + */ +BOOL ipc_vfs_init(void) +{ + BOOL ret; + struct ntvfs_ops ops; + + ZERO_STRUCT(ops); + + /* fill in all the operations */ + ops.connect = ipc_connect; + ops.disconnect = ipc_disconnect; + ops.unlink = ipc_unlink; + ops.chkpath = ipc_chkpath; + ops.qpathinfo = ipc_qpathinfo; + ops.setpathinfo = ipc_setpathinfo; + ops.open = ipc_open; + ops.mkdir = ipc_mkdir; + ops.rmdir = ipc_rmdir; + ops.rename = ipc_rename; + ops.copy = ipc_copy; + ops.ioctl = ipc_ioctl; + ops.read = ipc_read; + ops.write = ipc_write; + ops.seek = ipc_seek; + ops.flush = ipc_flush; + ops.close = ipc_close; + ops.exit = ipc_exit; + ops.lock = ipc_lock; + ops.setfileinfo = ipc_setfileinfo; + ops.qfileinfo = ipc_qfileinfo; + ops.fsinfo = ipc_fsinfo; + ops.lpq = ipc_lpq; + ops.search_first = ipc_search_first; + ops.search_next = ipc_search_next; + ops.search_close = ipc_search_close; + + /* register ourselves with the NTVFS subsystem. */ + ret = ntvfs_register("ipc", NTVFS_IPC, &ops); + + if (!ret) { + DEBUG(0,("Failed to register IPC backend!\n")); + return False; + } + + return True; +} diff --git a/source4/ntvfs/ntvfs_base.c b/source4/ntvfs/ntvfs_base.c new file mode 100644 index 0000000000..57cdb97e9f --- /dev/null +++ b/source4/ntvfs/ntvfs_base.c @@ -0,0 +1,145 @@ +/* + Unix SMB/CIFS implementation. + NTVFS base code + 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 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +/* + this implements the core code for all NTVFS modules. Backends register themselves here. +*/ + +#include "includes.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 { + const char *name; + enum ntvfs_type type; + 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 +*/ +BOOL ntvfs_register(const char *name, enum ntvfs_type type, struct ntvfs_ops *ops) +{ + if (ntvfs_backend_byname(name, type) != NULL) { + /* its already registered! */ + DEBUG(2,("NTVFS backend '%s' for type %d already registered\n", + name, (int)type)); + return False; + } + + backends = Realloc(backends, sizeof(backends[0]) * (num_backends+1)); + if (!backends) { + smb_panic("out of memory in ntvfs_register"); + } + + backends[num_backends].name = smb_xstrdup(name); + backends[num_backends].type = type; + backends[num_backends].ops = smb_xmemdup(ops, sizeof(*ops)); + + num_backends++; + + return True; +} + + +/* + return the operations structure for a named backend of the specified type +*/ +struct ntvfs_ops *ntvfs_backend_byname(const char *name, enum ntvfs_type type) +{ + int i; + + for (i=0;isizeof_ntvfs_ops = sizeof(struct ntvfs_ops); + sizes->sizeof_SMB_OFF_T = sizeof(SMB_OFF_T); + sizes->sizeof_tcon_context = sizeof(struct tcon_context); + sizes->sizeof_request_context = sizeof(struct request_context); + + return NTVFS_INTERFACE_VERSION; +} + + +/* + initialise the NTVFS subsystem +*/ +BOOL ntvfs_init(void) +{ + /* initialise our 3 basic backends. These are assumed to be + * present and are always built in */ + if (!posix_vfs_init() || + !ipc_vfs_init() || + !print_vfs_init()) { + return False; + } + /* initialize optional backends, e.g. CIFS. We allow failures here. */ + cifs_vfs_init(); + +#if WITH_NTVFS_STFS + tank_vfs_init(); +#endif + + DEBUG(3,("NTVFS version %d initialised\n", NTVFS_INTERFACE_VERSION)); + return True; +} + + +/* + initialise a connection structure to point at a NTVFS backend +*/ +NTSTATUS ntvfs_init_connection(struct request_context *req) +{ + const char *handler = lp_ntvfs_handler(req->conn->service); + + if (strequal(handler, "default")) + handler = "ipc"; + + req->conn->ntvfs_ops = ntvfs_backend_byname(handler, req->conn->type); + + if (!req->conn->ntvfs_ops) { + DEBUG(1,("ntvfs_init_connection: failed to find backend=%s, type=%d\n", handler, req->conn->type)); + return NT_STATUS_UNSUCCESSFUL; + } + + return NT_STATUS_OK; +} diff --git a/source4/ntvfs/ntvfs_dfs.c b/source4/ntvfs/ntvfs_dfs.c new file mode 100644 index 0000000000..7acd1f7cbb --- /dev/null +++ b/source4/ntvfs/ntvfs_dfs.c @@ -0,0 +1,117 @@ +/* + Unix SMB/CIFS implementation. + NTVFS base code + 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 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +/* + this implements the core code for all NTVFS modules. Backends register themselves here. +*/ + +#include "includes.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 { + const char *name; + enum ntvfs_type type; + 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 +*/ +BOOL ntvfs_register(const char *name, enum ntvfs_type type, struct ntvfs_ops *ops) +{ + if (ntvfs_backend_byname(name, type) != NULL) { + /* its already registered! */ + DEBUG(2,("NTVFS backend '%s' for type %d already registered\n", + name, (int)type)); + return False; + } + + backends = Realloc(backends, sizeof(backends[0]) * (num_backends+1)); + if (!backends) { + smb_panic("out of memory in ntvfs_register"); + } + + backends[num_backends].name = smb_xstrdup(name); + backends[num_backends].type = type; + backends[num_backends].ops = smb_xmemdup(ops, sizeof(*ops)); + + num_backends++; + + return True; +} + + +/* + return the operations structure for a named backend of the specified type +*/ +struct ntvfs_ops *ntvfs_backend_byname(const char *name, enum ntvfs_type type) +{ + int i; + + for (i=0;isizeof_ntvfs_ops = sizeof(struct ntvfs_ops); + sizes->sizeof_SMB_OFF_T = sizeof(SMB_OFF_T); + sizes->sizeof_tcon_context = sizeof(struct tcon_context); + + return NTVFS_INTERFACE_VERSION; +} + + +/* + initialise the NTVFS subsystem +*/ +BOOL ntvfs_init(void) +{ + /* initialise our 3 basic backends. These are assumed to be + * present and are always built in */ + if (!posix_vfs_init() || + !ipc_vfs_init() || + !print_vfs_init()) { + return False; + } + + DEBUG(3,("NTVFS version %d initialised\n", NTVFS_INTERFACE_VERSION)); + return True; +} diff --git a/source4/ntvfs/ntvfs_generic.c b/source4/ntvfs/ntvfs_generic.c new file mode 100644 index 0000000000..def448a671 --- /dev/null +++ b/source4/ntvfs/ntvfs_generic.c @@ -0,0 +1,544 @@ +/* + Unix SMB/CIFS implementation. + + NTVFS generic level mapping code + + 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 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +/* + this 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 varient of each + call unless they need fine grained control of the calls. +*/ + +#include "includes.h" + +/* + see if a filename ends in EXE COM DLL or SYM. This is needed for the DENY_DOS mapping for OpenX +*/ +static BOOL is_exe_file(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 open generic to any mapper +*/ +NTSTATUS ntvfs_map_open(struct request_context *req, union smb_open *io) +{ + NTSTATUS status; + union smb_open io2; + + if (io->generic.level == RAW_OPEN_GENERIC) { + return NT_STATUS_INVALID_LEVEL; + } + + switch (io->generic.level) { + case RAW_OPEN_OPENX: + ZERO_STRUCT(io2.generic.in); + io2.generic.level = RAW_OPEN_GENERIC; + if (io->openx.in.flags & OPENX_FLAGS_REQUEST_OPLOCK) { + io2.generic.in.flags |= NTCREATEX_FLAGS_REQUEST_OPLOCK; + } + if (io->openx.in.flags & OPENX_FLAGS_REQUEST_BATCH_OPLOCK) { + io2.generic.in.flags |= NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK; + } + + switch (io->openx.in.open_mode & OPENX_MODE_ACCESS_MASK) { + case OPENX_MODE_ACCESS_READ: + io2.generic.in.access_mask = GENERIC_RIGHTS_FILE_READ; + break; + case OPENX_MODE_ACCESS_WRITE: + io2.generic.in.access_mask = GENERIC_RIGHTS_FILE_WRITE; + break; + case OPENX_MODE_ACCESS_RDWR: + case OPENX_MODE_ACCESS_FCB: + io2.generic.in.access_mask = GENERIC_RIGHTS_FILE_ALL_ACCESS; + break; + } + + switch (io->openx.in.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! */ + if (is_exe_file(io->openx.in.fname)) { + io2.generic.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE; + } else { + if ((io->openx.in.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.share_access = NTCREATEX_SHARE_ACCESS_NONE; + break; + } + + switch (io->openx.in.open_func) { + case (OPENX_OPEN_FUNC_FAIL): + io2.generic.in.open_disposition = NTCREATEX_DISP_CREATE; + break; + 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; + } + io2.generic.in.alloc_size = io->openx.in.size; + io2.generic.in.file_attr = io->openx.in.file_attrs; + io2.generic.in.fname = io->openx.in.fname; + + status = req->conn->ntvfs_ops->open(req, &io2); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + ZERO_STRUCT(io->openx.out); + io->openx.out.fnum = io2.generic.out.fnum; + 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; + + return NT_STATUS_OK; + + + case RAW_OPEN_OPEN: + ZERO_STRUCT(io2.generic.in); + io2.generic.level = RAW_OPEN_GENERIC; + io2.generic.in.file_attr = io->open.in.search_attrs; + io2.generic.in.fname = io->open.in.fname; + io2.generic.in.open_disposition = NTCREATEX_DISP_OPEN; + DEBUG(9,("ntvfs_map_open(OPEN): mapping flags=0x%x\n", + io->open.in.flags)); + switch (io->open.in.flags & OPEN_FLAGS_MODE_MASK) { + case OPEN_FLAGS_OPEN_READ: + io2.generic.in.access_mask = GENERIC_RIGHTS_FILE_READ; + io->open.out.rmode = DOS_OPEN_RDONLY; + break; + case OPEN_FLAGS_OPEN_WRITE: + io2.generic.in.access_mask = GENERIC_RIGHTS_FILE_WRITE; + io->open.out.rmode = DOS_OPEN_WRONLY; + break; + case OPEN_FLAGS_OPEN_RDWR: + case 0xf: /* FCB mode */ + io2.generic.in.access_mask = GENERIC_RIGHTS_FILE_ALL_ACCESS; + io->open.out.rmode = DOS_OPEN_RDWR; /* assume we got r/w */ + break; + default: + DEBUG(2,("ntvfs_map_open(OPEN): invalid mode 0x%x\n", + io->open.in.flags & OPEN_FLAGS_MODE_MASK)); + return NT_STATUS_INVALID_PARAMETER; + } + + switch(io->open.in.flags & OPEN_FLAGS_DENY_MASK) { + case OPEN_FLAGS_DENY_DOS: + /* DENY_DOS is quite strange - it depends on the filename! */ + /* REWRITE: is this necessary for OPEN? */ + if (is_exe_file(io->open.in.fname)) { + io2.generic.in.share_access = NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_WRITE; + } else { + if ((io->open.in.flags & OPEN_FLAGS_MODE_MASK) == + OPEN_FLAGS_OPEN_READ) { + io2.generic.in.share_access = NTCREATEX_SHARE_ACCESS_READ; + } else { + io2.generic.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + } + } + break; + case OPEN_FLAGS_DENY_ALL: + io2.generic.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + break; + case OPEN_FLAGS_DENY_WRITE: + io2.generic.in.share_access = NTCREATEX_SHARE_ACCESS_READ; + break; + case OPEN_FLAGS_DENY_READ: + io2.generic.in.share_access = NTCREATEX_SHARE_ACCESS_WRITE; + break; + case OPEN_FLAGS_DENY_NONE: + io2.generic.in.share_access = NTCREATEX_SHARE_ACCESS_WRITE | + NTCREATEX_SHARE_ACCESS_READ | NTCREATEX_SHARE_ACCESS_DELETE; + break; + case 0x70: /* FCB mode */ + io2.generic.in.share_access = NTCREATEX_SHARE_ACCESS_NONE; + break; + default: + DEBUG(2,("ntvfs_map_open(OPEN): invalid DENY 0x%x\n", + io->open.in.flags & OPEN_FLAGS_DENY_MASK)); + return NT_STATUS_INVALID_PARAMETER; + } + DEBUG(9,("ntvfs_map_open(OPEN): mapped flags=0x%x to access_mask=0x%x and share_access=0x%x\n", + io->open.in.flags, io2.generic.in.access_mask, io2.generic.in.share_access)); + + status = req->conn->ntvfs_ops->open(req, &io2); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + ZERO_STRUCT(io->openx.out); + io->open.out.fnum = io2.generic.out.fnum; + io->open.out.attrib = io2.generic.out.attrib; + io->open.out.write_time = nt_time_to_unix(&io2.generic.out.write_time); + io->open.out.size = io2.generic.out.size; + io->open.out.rmode = DOS_OPEN_RDWR; + + return NT_STATUS_OK; + } + + return NT_STATUS_INVALID_LEVEL; +} + + +/* + NTVFS fsinfo generic to any mapper +*/ +NTSTATUS ntvfs_map_fsinfo(struct request_context *req, union smb_fsinfo *fs) +{ + NTSTATUS status; + union smb_fsinfo fs2; + + if (fs->generic.level == RAW_QFS_GENERIC) { + return NT_STATUS_INVALID_LEVEL; + } + + /* ask the backend for the generic info */ + fs2.generic.level = RAW_QFS_GENERIC; + + status = req->conn->ntvfs_ops->fsinfo(req, &fs2); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + /* and convert it to the required level */ + switch (fs->generic.level) { + case RAW_QFS_GENERIC: + return NT_STATUS_INVALID_LEVEL; + + case RAW_QFS_DSKATTR: { + /* map from generic to DSKATTR */ + unsigned 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->smb->negotiate.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; + } + + + return NT_STATUS_INVALID_LEVEL; +} + + +/* + NTVFS fileinfo generic to any mapper +*/ +NTSTATUS ntvfs_map_fileinfo(struct request_context *req, union smb_fileinfo *info, union smb_fileinfo *info2) +{ + /* and convert it to the required level using results in info2 */ + switch (info->generic.level) { + case RAW_FILEINFO_GENERIC: + return NT_STATUS_INVALID_LEVEL; + case RAW_FILEINFO_GETATTR: + info->getattr.out.attrib = info2->generic.out.attrib & 0x3f; + 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.device = info2->generic.out.device; + info->internal_information.out.inode = info2->generic.out.inode; + 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: + /* setup a single data stream */ + info->stream_info.out.num_streams = info2->generic.out.num_streams; + info->stream_info.out.streams = talloc(req->mem_ctx, sizeof(info2->stream_info.out.streams[0])); + if (!info->stream_info.out.streams) { + return NT_STATUS_NO_MEMORY; + } + info->stream_info.out.streams[0].size = info2->generic.out.streams[0].size; + info->stream_info.out.streams[0].alloc_size = info2->generic.out.streams[0].alloc_size; + info->stream_info.out.streams[0].stream_name.s = info2->generic.out.streams[0].stream_name.s; + info->stream_info.out.streams[0].stream_name.private_length = info->generic.out.streams[0].stream_name.private_length; + return NT_STATUS_OK; + + case RAW_FILEINFO_NAME_INFO: + case RAW_FILEINFO_NAME_INFORMATION: + info->name_info.out.fname.s = info2->generic.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: + info->alt_name_info.out.fname.s = info2->generic.out.alt_fname.s; + info->alt_name_info.out.fname.private_length = info2->generic.out.alt_fname.private_length; + return NT_STATUS_OK; + } + + return NT_STATUS_INVALID_LEVEL; +} + +/* + NTVFS fileinfo generic to any mapper +*/ +NTSTATUS ntvfs_map_qfileinfo(struct request_context *req, union smb_fileinfo *info) +{ + NTSTATUS status; + union smb_fileinfo info2; + + if (info->generic.level == RAW_FILEINFO_GENERIC) { + return NT_STATUS_INVALID_LEVEL; + } + + /* ask the backend for the generic info */ + info2.generic.level = RAW_FILEINFO_GENERIC; + info2.generic.in.fnum = info->generic.in.fnum; + + status = req->conn->ntvfs_ops->qfileinfo(req, &info2); + 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 request_context *req, union smb_fileinfo *info) +{ + NTSTATUS status; + union smb_fileinfo info2; + + if (info->generic.level == RAW_FILEINFO_GENERIC) { + return NT_STATUS_INVALID_LEVEL; + } + + /* ask the backend for the generic info */ + info2.generic.level = RAW_FILEINFO_GENERIC; + info2.generic.in.fname = info->generic.in.fname; + + status = req->conn->ntvfs_ops->qpathinfo(req, &info2); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + return ntvfs_map_fileinfo(req, info, &info2); +} diff --git a/source4/ntvfs/ntvfs_util.c b/source4/ntvfs/ntvfs_util.c new file mode 100644 index 0000000000..4036fb0935 --- /dev/null +++ b/source4/ntvfs/ntvfs_util.c @@ -0,0 +1,26 @@ +/* + Unix SMB/CIFS implementation. + NTVFS utility code + 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 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +/* + this implements common utility functions that many NTVFS backends may wish to use +*/ + +#include "includes.h" + + diff --git a/source4/ntvfs/posix/vfs_posix.c b/source4/ntvfs/posix/vfs_posix.c new file mode 100644 index 0000000000..b27e7493a0 --- /dev/null +++ b/source4/ntvfs/posix/vfs_posix.c @@ -0,0 +1,151 @@ +/* + Unix SMB/CIFS implementation. + POSIX NTVFS backend + Copyright (C) Andrew Tridgell 1992-2003 + Copyright (C) Andrew Bartlett 2001 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +/* + this implements most of the POSIX NTVFS backend + This is the default backend +*/ + +#include "includes.h" + +/* + 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_context *ctx, const char *sharename) +{ + struct stat st; + struct connection_struct *conn = ctx->conn; + NTSTATUS status; + + /* the directory must exist */ + if (stat(conn->connectpath, &st) != 0 || !S_ISDIR(st.st_mode)) { + DEBUG(0,("'%s' is not a directory, when connecting to [%s]\n", + conn->connectpath, lp_servicename(SNUM(conn)))); + return NT_STATUS_BAD_NETWORK_NAME; + } + + /* Initialise old VFS function pointers */ + if (!smbd_vfs_init(conn)) { + DEBUG(0, ("vfs_init failed for service %s\n", lp_servicename(SNUM(conn)))); + return NT_STATUS_BAD_NETWORK_NAME; + } + + /* become the user for the rest */ + status = ntvfs_change_to_user(ctx); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + /* the posix backend can do preexec */ + status = ntvfs_connect_preexec(ctx); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + /* Invoke the old POSIX VFS make connection hook */ + if (conn->vfs_ops.connect && + conn->vfs_ops.connect(conn, lp_servicename(snum), user) < 0) { + DEBUG(0,("make_connection: POSIX VFS make connection failed!\n")); + return NT_STATUS_UNSUCCESSFUL; + } + } + + + /* + * Print out the 'connected as' stuff here as we need + * to know the effective uid and gid we will be using + * (at least initially). + */ + if( DEBUGLVL( IS_IPC(conn) ? 3 : 1 ) ) { + dbgtext( "%s (%s) ", get_remote_machine_name(), conn->client_address ); + dbgtext( "connect to service %s ", lp_servicename(SNUM(conn)) ); + dbgtext( "initially as user %s ", user ); + dbgtext( "(uid=%d, gid=%d) ", (int)geteuid(), (int)getegid() ); + dbgtext( "(pid %d)\n", (int)sys_getpid() ); + } + + return NT_STATUS_OK; +} + +/* + disconnect from a share +*/ +static NTSTATUS pvfs_disconnect(struct ntvfs_context *ctx) +{ + return NT_STATUS_OK; +} + +/* + 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 pvfs_unlink(struct ntvfs_context *ctx, const char *name, uint16 dirtype) +{ + NTSTATUS status; + + if (ntvfs_dfs_redirect(ctx, name)) { + return NT_STATUS_PATH_NOT_COVERED; + } + + status = unlink_internals(ctx->conn, dirtype, name); + if (!NT_STATUS_IS_OK(status)) { + return status; + } + + ntvfs_run_change_notify_queue(); + + return NT_STATUS_OK; +} + + + + + + + +/* + initialialise the POSIX disk backend, registering ourselves with the ntvfs subsystem + */ +BOOL posix_vfs_init(void) +{ + BOOL ret; + struct ntvfs_ops ops; + + ZERO_STRUCT(ops); + + /* fill in all the operations */ + ops.connect = pvfs_connect; + ops.disconnect = pvfs_disconnect; + ops.unlink = pvfs_unlink; + + /* register ourselves with the NTVFS subsystem. We register under the name 'default' + as we wish to be the default backend */ + ret = ntvfs_register("default", NTVFS_DISK, &ops); + + if (!ret) { + DEBUG(0,("Failed to register POSIX backend!\n")); + return False; + } + + return True; +} diff --git a/source4/ntvfs/print/README b/source4/ntvfs/print/README new file mode 100644 index 0000000000..441c82dddd --- /dev/null +++ b/source4/ntvfs/print/README @@ -0,0 +1,3 @@ +This is the print NTVFS backend for Samba. NTVFS operations that are +made on print shares are directed here by default. Most directory +operations and many file operations are not supported on print shares. diff --git a/source4/ntvfs/print/vfs_print.c b/source4/ntvfs/print/vfs_print.c new file mode 100644 index 0000000000..2563f0ad9c --- /dev/null +++ b/source4/ntvfs/print/vfs_print.c @@ -0,0 +1,104 @@ +/* + Unix SMB/CIFS implementation. + default print NTVFS 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 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +/* + this implements the print backend, called by the NTVFS subsystem to + handle requests on printing shares +*/ + +#include "includes.h" + +/* + connect to a share - used when a tree_connect operation comes + in. For printing shares this should check that the spool directory + is available +*/ +static NTSTATUS print_connect(struct request_context *req, const char *sharename) +{ + return NT_STATUS_OK; +} + +/* + disconnect from a share +*/ +static NTSTATUS print_disconnect(struct tcon_context *conn) +{ + return NT_STATUS_OK; +} + +/* + lots of operations are not allowed on printing shares - mostly return NT_STATUS_ACCESS_DENIED +*/ +static NTSTATUS print_unlink(struct request_context *req, struct smb_unlink *unl) +{ + return NT_STATUS_ACCESS_DENIED; +} + + +/* + ioctl - used for job query +*/ +static NTSTATUS print_ioctl(struct request_context *req, struct smb_ioctl *io) +{ + char *p; + + if (io->in.request == IOCTL_QUERY_JOB_INFO) { + /* a request for the print job id of an open print job */ + io->out.blob = data_blob_talloc(req->mem_ctx, NULL, 32); + + memset(io->out.blob.data, 0, io->out.blob.length); + + p = io->out.blob.data; + SSVAL(p,0, 1 /* REWRITE: fsp->rap_print_jobid */); + push_string(NULL, p+2, lp_netbios_name(), 15, STR_TERMINATE|STR_ASCII); + push_string(NULL, p+18, lp_servicename(req->conn->service), 13, STR_TERMINATE|STR_ASCII); + return NT_STATUS_OK; + } + + return NT_STATUS_INVALID_PARAMETER; +} + + +/* + initialialise the print backend, registering ourselves with the ntvfs subsystem + */ +BOOL print_vfs_init(void) +{ + BOOL ret; + struct ntvfs_ops ops; + + ZERO_STRUCT(ops); + + /* fill in all the operations */ + ops.connect = print_connect; + ops.disconnect = print_disconnect; + ops.unlink = print_unlink; + ops.ioctl = print_ioctl; + + /* register ourselves with the NTVFS subsystem. We register under the name 'default' + as we wish to be the default backend */ + ret = ntvfs_register("default", NTVFS_PRINT, &ops); + + if (!ret) { + DEBUG(0,("Failed to register PRINT backend!\n")); + return False; + } + + return True; +} diff --git a/source4/ntvfs/reference/ref.h b/source4/ntvfs/reference/ref.h new file mode 100644 index 0000000000..00eab76094 --- /dev/null +++ b/source4/ntvfs/reference/ref.h @@ -0,0 +1,36 @@ + +struct rvfs_private { + /* the meta-data database */ + TDB_CONTEXT *tdb; + + /* the base directory */ + char *connectpath; + + /* a linked list of open searches */ + struct search_state *search; + + /* next available search handle */ + uint16 next_search_handle; +}; + +struct rvfs_dir { + uint_t count; + char *unix_dir; + struct { + char *name; + } *files; +}; + +struct search_state { + struct search_state *next, *prev; + TALLOC_CTX *mem_ctx; + uint16 handle; + uint_t current_index; + struct rvfs_dir *dir; +}; + + +struct ref_struct { + NTTIME mtime, ctime, atime, wtime; + char *; +}; diff --git a/source4/ntvfs/reference/ref_util.c b/source4/ntvfs/reference/ref_util.c new file mode 100644 index 0000000000..8234944ebd --- /dev/null +++ b/source4/ntvfs/reference/ref_util.c @@ -0,0 +1,142 @@ +/* + 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 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +/* + utility functions for simple backend +*/ + +#include "includes.h" +#include "svfs.h" + +/* + convert a windows path to a unix path - don't do any manging or case sensitive handling +*/ +char *svfs_unix_path(struct request_context *req, const char *name) +{ + struct svfs_private *private = req->conn->ntvfs_private; + char *ret; + + if (*name != '\\') { + ret = talloc_asprintf(req->mem_ctx, "%s/%s", private->connectpath, name); + } else { + ret = talloc_asprintf(req->mem_ctx, "%s%s", private->connectpath, name); + } + all_string_sub(ret, "\\", "/", 0); + + strlower(ret); + + 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(TALLOC_CTX *mem_ctx, struct request_context *req, const char *pattern) +{ + char *unix_path; + char *p, *mask; + struct svfs_dir *dir; + DIR *odir; + struct dirent *dent; + uint_t allocated = 0; + char *low_mask; + + unix_path = svfs_unix_path(req, pattern); + if (!unix_path) { return NULL; } + + dir = talloc(mem_ctx, sizeof(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 = talloc_strdup(mem_ctx, mask); + if (!low_mask) { return NULL; } + strlower(low_mask); + + odir = opendir(dir->unix_dir); + if (!odir) { return NULL; } + + while ((dent = readdir(odir))) { + uint_t i = dir->count; + char *full_name; + char *low_name; + + low_name = talloc_strdup(mem_ctx, dent->d_name); + if (!low_name) { continue; } + strlower(low_name); + + /* check it matches the wildcard pattern */ + if (ms_fnmatch(low_mask, low_name, PROTOCOL_NT1) != 0) { + continue; + } + + if (dir->count >= allocated) { + allocated = (allocated + 100) * 1.2; + dir->files = talloc_realloc(mem_ctx, dir->files, allocated * sizeof(dir->files[0])); + if (!dir->files) { + closedir(odir); + return NULL; + } + } + + dir->files[i].name = low_name; + if (!dir->files[i].name) { continue; } + + asprintf(&full_name, "%s/%s", dir->unix_dir, dir->files[i].name); + if (!full_name) { continue; } + + if (stat(full_name, &dir->files[i].st) == 0) { + dir->count++; + } + + free(full_name); + } + + closedir(odir); + + return dir; +} + + +/* + convert a unix stat struct to a dos attrib +*/ +uint32 svfs_file_attrib(struct stat *st) +{ + if (S_ISDIR(st->st_mode)) { + return FILE_ATTRIBUTE_DIRECTORY; + } + return 0; +} diff --git a/source4/ntvfs/reference/vfs_ref.c b/source4/ntvfs/reference/vfs_ref.c new file mode 100644 index 0000000000..b4fa2e61ff --- /dev/null +++ b/source4/ntvfs/reference/vfs_ref.c @@ -0,0 +1,843 @@ +/* + 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 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +/* + this 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 "svfs.h" + +/* + 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 request_context *req, const char *sharename) +{ + struct stat st; + struct tcon_context *conn = req->conn; + struct svfs_private *private; + + conn->ntvfs_private = talloc(conn->mem_ctx, sizeof(struct svfs_private)); + + private = conn->ntvfs_private; + + private->connectpath = talloc_strdup(conn->mem_ctx, lp_pathname(conn->service)); + + /* the directory must exist */ + if (stat(private->connectpath, &st) != 0 || !S_ISDIR(st.st_mode)) { + DEBUG(0,("'%s' is not a directory, when connecting to [%s]\n", + private->connectpath, sharename)); + return NT_STATUS_BAD_NETWORK_NAME; + } + + conn->fs_type = talloc_strdup(conn->mem_ctx, "NTFS"); + conn->dev_type = talloc_strdup(conn->mem_ctx, "A:"); + + return NT_STATUS_OK; +} + +/* + disconnect from a share +*/ +static NTSTATUS svfs_disconnect(struct request_context *req) +{ + return NT_STATUS_OK; +} + +/* + 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 request_context *req, struct smb_unlink *unl) +{ + char *unix_path; + + unix_path = svfs_unix_path(req, unl->in.pattern); + + /* ignoring wildcards ... */ + if (unlink(unix_path) == -1) { + return map_nt_error_from_unix(errno); + } + + return NT_STATUS_OK; +} + + +/* + ioctl interface - we don't do any +*/ +static NTSTATUS svfs_ioctl(struct request_context *req, struct smb_ioctl *io) +{ + return NT_STATUS_INVALID_PARAMETER; +} + +/* + check if a directory exists +*/ +static NTSTATUS svfs_chkpath(struct request_context *req, struct smb_chkpath *cp) +{ + char *unix_path; + struct stat st; + + unix_path = svfs_unix_path(req, cp->in.path); + + if (stat(unix_path, &st) == -1) { + return map_nt_error_from_unix(errno); + } + + if (!S_ISDIR(st.st_mode)) { + return NT_STATUS_NOT_A_DIRECTORY; + } + + return NT_STATUS_OK; +} + +/* + approximately map a struct stat to a fileinfo struct +*/ +static NTSTATUS map_fileinfo(struct request_context *req, union smb_fileinfo *info, struct stat *st) +{ + switch (info->generic.level) { + case SMB_FILEINFO_NETWORK_OPEN_INFORMATION: + unix_to_nt_time(&info->netopen.out.create_time, st->st_ctime); + unix_to_nt_time(&info->netopen.out.access_time, st->st_atime); + unix_to_nt_time(&info->netopen.out.write_time, st->st_mtime); + unix_to_nt_time(&info->netopen.out.change_time, st->st_mtime); + info->netopen.out.alloc_size = st->st_size; + info->netopen.out.size = st->st_size; + info->netopen.out.attrib = svfs_file_attrib(st); + info->netopen.out.unknown = 0; + return NT_STATUS_OK; + + case SMB_FILEINFO_ALL_INFO: + unix_to_nt_time(&info->all_info.out.create_time, st->st_ctime); + unix_to_nt_time(&info->all_info.out.access_time, st->st_atime); + unix_to_nt_time(&info->all_info.out.write_time, st->st_mtime); + unix_to_nt_time(&info->all_info.out.change_time, st->st_mtime); + info->all_info.out.attrib = svfs_file_attrib(st); + info->all_info.out.alloc_size = st->st_size; + info->all_info.out.size = st->st_size; + info->all_info.out.nlink = st->st_nlink; + info->all_info.out.delete_pending = 0; + info->all_info.out.directory = S_ISDIR(st->st_mode) ? 1 : 0; + info->all_info.out.index_number = st->st_ino; + info->all_info.out.ea_size = 0; + info->all_info.out.access_flags = 0xd01BF; /* what is this!? */ + info->all_info.out.current_offset = 0; + info->all_info.out.open_mode = 0; /* what do do put here!? */ + info->all_info.out.alignment_requirement = 0; /* what do do put here!? */ + info->all_info.out.fname = talloc_strdup(req->mem_ctx, "TODO - STORE FILENAME"); + return NT_STATUS_OK; + + case SMB_FILEINFO_BASIC: + unix_to_nt_time(&info->basic.out.create_time, st->st_ctime); + unix_to_nt_time(&info->basic.out.access_time, st->st_atime); + unix_to_nt_time(&info->basic.out.write_time, st->st_mtime); + unix_to_nt_time(&info->basic.out.change_time, st->st_mtime); + info->basic.out.attrib = svfs_file_attrib(st); + return NT_STATUS_OK; + + case SMB_FILEINFO_INFO_STANDARD: + info->info_standard.out.create_time = st->st_ctime; + info->info_standard.out.access_time = st->st_atime; + info->info_standard.out.write_time = st->st_mtime; + info->info_standard.out.size = st->st_size; + info->info_standard.out.alloc_size = st->st_size; + info->info_standard.out.attrib = svfs_file_attrib(st); + return NT_STATUS_OK; + + case SMB_FILEINFO_INFO_STANDARD_EA: + info->info_standard_ea.out.create_time = st->st_ctime; + info->info_standard_ea.out.access_time = st->st_atime; + info->info_standard_ea.out.write_time = st->st_mtime; + info->info_standard_ea.out.size = st->st_size; + info->info_standard_ea.out.alloc_size = st->st_size; + info->info_standard_ea.out.attrib = svfs_file_attrib(st); + info->info_standard_ea.out.ea_size = 0; + return NT_STATUS_OK; + + case SMB_FILEINFO_STANDARD_INFO: + info->standard_info.out.alloc_size = st->st_size; + info->standard_info.out.size = st->st_size; + info->standard_info.out.nlink = st->st_nlink; + info->standard_info.out.delete_pending = 0; + info->standard_info.out.directory = S_ISDIR(st->st_mode) ? 1 : 0; + info->standard_info.out.unknown = 0; + return NT_STATUS_OK; + + case SMB_FILEINFO_INTERNAL: + info->internal.out.device = st->st_dev; + info->internal.out.device = st->st_ino; + return NT_STATUS_OK; + + case SMB_FILEINFO_EA: + info->ea.out.unknown = 0; + return NT_STATUS_OK; + + case SMB_FILEINFO_ATTRIB_TAGINFO: + info->tag.out.attrib = svfs_file_attrib(st); + info->tag.out.reparse_tag = 0; + return NT_STATUS_OK; + + case SMB_FILEINFO_STREAM: + /* setup a single data stream */ + info->stream.out.num_streams = 1; + info->stream.out.streams = talloc(req->mem_ctx, sizeof(info->stream.out.streams[0])); + if (!info->stream.out.streams) { + return NT_STATUS_NO_MEMORY; + } + info->stream.out.streams[0].size = st->st_size; + info->stream.out.streams[0].alloc_size = st->st_size; + info->stream.out.streams[0].stream_name = talloc_strdup(req->mem_ctx,"::$DATA"); + return NT_STATUS_OK; + } + + return NT_STATUS_INVALID_LEVEL; +} + +/* + return info on a pathname +*/ +static NTSTATUS svfs_qpathinfo(struct request_context *req, union smb_fileinfo *info) +{ + char *unix_path; + struct stat st; + + unix_path = svfs_unix_path(req, info->basic.in.fname); + + if (stat(unix_path, &st) == -1) { + return map_nt_error_from_unix(errno); + } + + return map_fileinfo(req, info, &st); +} + +/* + query info on a open file +*/ +static NTSTATUS svfs_qfileinfo(struct request_context *req, union smb_fileinfo *info) +{ + struct stat st; + + if (fstat(info->generic.in.fnum, &st) == -1) { + return map_nt_error_from_unix(errno); + } + + return map_fileinfo(req, info, &st); +} + + +/* + set info on a pathname +*/ +static NTSTATUS svfs_setpathinfo(struct request_context *req, union smb_setfileinfo *st) +{ + return NT_STATUS_NOT_SUPPORTED; +} + +/* + open a file +*/ +static NTSTATUS svfs_open(struct request_context *req, union smb_open *io) +{ + char *unix_path; + struct stat st; + int fd, flags; + + if (io->generic.level != SMB_OPEN_GENERIC) { + return ntvfs_map_open(req, io); + } + + unix_path = svfs_unix_path(req, io->ntcreatex.in.fname); + + switch (io->generic.in.open_disposition) { + case FILE_SUPERSEDE: + flags = O_RDWR | O_CREAT | O_TRUNC; + break; + case FILE_OPEN: + flags = O_RDWR; + break; + case FILE_CREATE: + flags = O_RDWR | O_CREAT | O_EXCL; + break; + case FILE_OPEN_IF: + flags = O_RDWR | O_CREAT; + break; + case FILE_OVERWRITE: + flags = O_RDWR; + break; + case FILE_OVERWRITE_IF: + flags = O_RDWR | O_CREAT | O_TRUNC; + break; + default: + flags = O_RDWR; + break; + } + + if (io->generic.in.create_options & FILE_DIRECTORY_FILE) { + flags = O_RDONLY | O_DIRECTORY; + switch (io->generic.in.open_disposition) { + case FILE_CREATE: + if (mkdir(unix_path, 0755) == -1) { + return map_nt_error_from_unix(errno); + } + break; + case FILE_OPEN_IF: + if (mkdir(unix_path, 0755) == -1 && errno != EEXIST) { + return map_nt_error_from_unix(errno); + } + break; + } + } + + fd = open(unix_path, flags, 0644); + if (fd == -1) { + return map_nt_error_from_unix(errno); + } + + if (fstat(fd, &st) == -1) { + close(fd); + return map_nt_error_from_unix(errno); + } + + 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.fnum = fd; + io->generic.out.alloc_size = st.st_size; + io->generic.out.size = st.st_size; + io->generic.out.file_attr = svfs_file_attrib(&st); + io->generic.out.is_directory = S_ISDIR(st.st_mode) ? 1 : 0; + + return NT_STATUS_OK; +} + +/* + create a directory +*/ +static NTSTATUS svfs_mkdir(struct request_context *req, union smb_mkdir *md) +{ + char *unix_path; + + if (md->generic.level != RAW_MKDIR_MKDIR) { + return NT_STATUS_INVALID_LEVEL; + } + + unix_path = svfs_unix_path(req, md->mkdir.in.path); + + if (mkdir(unix_path, 0777) == -1) { + return map_nt_error_from_unix(errno); + } + + return NT_STATUS_OK; +} + +/* + remove a directory +*/ +static NTSTATUS svfs_rmdir(struct request_context *req, struct smb_rmdir *rd) +{ + char *unix_path; + + unix_path = svfs_unix_path(req, rd->in.path); + + if (rmdir(unix_path) == -1) { + return map_nt_error_from_unix(errno); + } + + return NT_STATUS_OK; +} + +/* + rename a set of files +*/ +static NTSTATUS svfs_rename(struct request_context *req, struct smb_rename *ren) +{ + char *unix_path1, *unix_path2; + + unix_path1 = svfs_unix_path(req, ren->in.pattern1); + unix_path2 = svfs_unix_path(req, ren->in.pattern2); + + if (rename(unix_path1, unix_path2) != 0) { + return map_nt_error_from_unix(errno); + } + + return NT_STATUS_OK; +} + +/* + copy a set of files +*/ +static NTSTATUS svfs_copy(struct request_context *req, struct smb_copy *cp) +{ + return NT_STATUS_NOT_SUPPORTED; +} + +/* + read from a file +*/ +static NTSTATUS svfs_read(struct request_context *req, union smb_read *rd) +{ + ssize_t ret; + + if (rd->generic.level != SMB_READ_READX) { + return NT_STATUS_NOT_SUPPORTED; + } + + ret = pread(rd->readx.in.fnum, + rd->readx.out.data, + rd->readx.in.maxcnt, + rd->readx.in.offset); + if (ret == -1) { + return map_nt_error_from_unix(errno); + } + + rd->readx.out.nread = ret; + rd->readx.out.remaining = 0; /* should fill this in? */ + + return NT_STATUS_OK; +} + +/* + write to a file +*/ +static NTSTATUS svfs_write(struct request_context *req, union smb_write *wr) +{ + ssize_t ret; + + switch (wr->generic.level) { + case SMB_WRITE_WRITEX: + ret = pwrite(wr->writex.in.fnum, + wr->writex.in.data, + wr->writex.in.count, + wr->writex.in.offset); + if (ret == -1) { + return map_nt_error_from_unix(errno); + } + + wr->writex.out.nwritten = ret; + wr->writex.out.remaining = 0; /* should fill this in? */ + + return NT_STATUS_OK; + + case SMB_WRITE_WRITE: + if (wr->write.in.count == 0) { + /* a truncate! */ + ret = ftruncate(wr->write.in.fnum, wr->write.in.offset); + } else { + ret = pwrite(wr->write.in.fnum, + wr->write.in.data, + wr->write.in.count, + wr->write.in.offset); + } + if (ret == -1) { + return map_nt_error_from_unix(errno); + } + + wr->write.out.nwritten = ret; + + return NT_STATUS_OK; + } + + return NT_STATUS_NOT_SUPPORTED; +} + +/* + seek in a file +*/ +static NTSTATUS svfs_seek(struct request_context *req, struct smb_seek *io) +{ + return NT_STATUS_NOT_SUPPORTED; +} + +/* + flush a file +*/ +static NTSTATUS svfs_flush(struct request_context *req, struct smb_flush *io) +{ + fsync(io->in.fnum); + return NT_STATUS_OK; +} + +/* + close a file +*/ +static NTSTATUS svfs_close(struct request_context *req, union smb_close *io) +{ + if (io->generic.level != SMB_CLOSE_CLOSE) { + /* we need a mapping function */ + return NT_STATUS_INVALID_LEVEL; + } + + if (close(io->close.in.fnum) != 0) { + return map_nt_error_from_unix(errno); + } + + return NT_STATUS_OK; +} + +/* + exit - closing files? +*/ +static NTSTATUS svfs_exit(struct request_context *req) +{ + return NT_STATUS_NOT_SUPPORTED; +} + +/* + lock a byte range +*/ +static NTSTATUS svfs_lock(struct request_context *req, union smb_lock *lck) +{ + DEBUG(0,("REWRITE: not doing byte range locking!\n")); + return NT_STATUS_OK; +} + +/* + set info on a open file +*/ +static NTSTATUS svfs_setfileinfo(struct request_context *req, + union smb_setfileinfo *info) +{ + DEBUG(0,("REWRITE: svfs_setfileinfo: not doing setfileinfo level %d\n", + info->generic.level)); + switch (info->generic.level) { + case SMB_SETFILEINFO_BASIC: + info->basic.out.ea_error_offset = 0; + break; + case SMB_SETFILEINFO_END_OF_FILE: + if (ftruncate(info->eof.in.fnum, info->eof.in.size) != 0) { + return map_nt_error_from_unix(errno); + } + info->eof.out.ea_error_offset = 0; + break; + case SMB_SETFILEINFO_ALLOCATION: + info->eof.out.ea_error_offset = 0; + break; + } + return NT_STATUS_OK; +} + + +/* + return filesystem space info +*/ +static NTSTATUS svfs_fsinfo(struct request_context *req, union smb_fsinfo *fs) +{ + struct svfs_private *private = req->conn->ntvfs_private; + + if (fs->generic.level != SMB_FSINFO_GENERIC) { + return ntvfs_map_fsinfo(req, fs); + } + + if (sys_fsusage(private->connectpath, + &fs->generic.out.blocks_free, + &fs->generic.out.blocks_total) == -1) { + return map_nt_error_from_unix(errno); + } + + fs->generic.out.block_size = 512; + + return NT_STATUS_OK; +} + +/* + return filesystem attribute info +*/ +static NTSTATUS svfs_fsattr(struct request_context *req, union smb_fsattr *fs) +{ + struct stat st; + struct svfs_private *private = req->conn->ntvfs_private; + + if (fs->generic.level != SMB_FSATTR_GENERIC) { + return ntvfs_map_fsattr(req, fs); + } + + if (stat(private->connectpath, &st) != 0) { + return map_nt_error_from_unix(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->mem_ctx, "NTFS"); + fs->generic.out.volume_name = talloc_strdup(req->mem_ctx, + lp_servicename(req->conn->service)); + + return NT_STATUS_OK; +} + +/* + return print queue info +*/ +static NTSTATUS svfs_lpq(struct request_context *req, union smb_lpq *lpq) +{ + return NT_STATUS_NOT_SUPPORTED; +} + +/* + list files in a directory matching a wildcard pattern +*/ +NTSTATUS svfs_search_first(struct request_context *req, union smb_search_first *io, + void *search_private, + BOOL (*callback)(void *, union smb_search_data *)) +{ + struct svfs_dir *dir; + int i; + struct svfs_private *private = req->conn->ntvfs_private; + struct search_state *search; + union smb_search_data file; + TALLOC_CTX *mem_ctx; + uint_t max_count; + + if (io->generic.level != SMB_SEARCH_T2FFIRST_BOTH) { + return NT_STATUS_NOT_SUPPORTED; + } + + mem_ctx = talloc_init("svfs_search"); + + search = talloc_zero(mem_ctx, sizeof(struct search_state)); + if (!search) { + return NT_STATUS_NO_MEMORY; + } + + max_count = io->t2ffirst.in.max_count; + + dir = svfs_list(mem_ctx, req, io->t2ffirst.in.pattern); + if (!dir) { + talloc_destroy_pool(mem_ctx); + return NT_STATUS_FOOBAR; + } + + search->mem_ctx = mem_ctx; + search->handle = private->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.create_time, dir->files[i].st.st_ctime); + unix_to_nt_time(&file.both.access_time, dir->files[i].st.st_atime); + unix_to_nt_time(&file.both.write_time, dir->files[i].st.st_mtime); + unix_to_nt_time(&file.both.change_time, dir->files[i].st.st_mtime); + file.both.name = dir->files[i].name; + file.both.short_name = dir->files[i].name; + file.both.size = dir->files[i].st.st_size; + file.both.ex_attrib = svfs_file_attrib(&dir->files[i].st); + + 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_destroy(search->mem_ctx); + } else { + private->next_search_handle++; + DLIST_ADD(private->search, search); + } + + return NT_STATUS_OK; +} + +/* continue a search */ +NTSTATUS svfs_search_next(struct request_context *req, union smb_search_next *io, + void *search_private, + BOOL (*callback)(void *, union smb_search_data *)) +{ + struct svfs_dir *dir; + int i; + struct svfs_private *private = req->conn->ntvfs_private; + struct search_state *search; + union smb_search_data file; + uint_t max_count; + + if (io->generic.level != SMB_SEARCH_T2FFIRST_BOTH) { + return NT_STATUS_NOT_SUPPORTED; + } + + for (search=private->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.create_time, dir->files[i].st.st_ctime); + unix_to_nt_time(&file.both.access_time, dir->files[i].st.st_atime); + unix_to_nt_time(&file.both.write_time, dir->files[i].st.st_mtime); + unix_to_nt_time(&file.both.change_time, dir->files[i].st.st_mtime); + file.both.name = dir->files[i].name; + file.both.short_name = dir->files[i].name; + file.both.size = dir->files[i].st.st_size; + file.both.ex_attrib = svfs_file_attrib(&dir->files[i].st); + + 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(private->search, search); + talloc_destroy(search->mem_ctx); + } + + return NT_STATUS_OK; +} + +/* close a search */ +NTSTATUS svfs_search_close(struct request_context *req, union smb_search_close *io) +{ + struct svfs_private *private = req->conn->ntvfs_private; + struct search_state *search; + + for (search=private->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(private->search, search); + talloc_destroy(search->mem_ctx); + + return NT_STATUS_OK; +} + + +/* + initialialise the POSIX disk backend, registering ourselves with the ntvfs subsystem + */ +BOOL posix_vfs_init(void) +{ + BOOL ret; + struct ntvfs_ops ops; + + ZERO_STRUCT(ops); + + /* fill in all the operations */ + ops.connect = svfs_connect; + ops.disconnect = svfs_disconnect; + ops.unlink = svfs_unlink; + ops.chkpath = svfs_chkpath; + ops.qpathinfo = svfs_qpathinfo; + ops.setpathinfo = svfs_setpathinfo; + ops.open = svfs_open; + ops.mkdir = svfs_mkdir; + ops.rmdir = svfs_rmdir; + ops.rename = svfs_rename; + ops.copy = svfs_copy; + ops.ioctl = svfs_ioctl; + ops.read = svfs_read; + ops.write = svfs_write; + ops.seek = svfs_seek; + ops.flush = svfs_flush; + ops.close = svfs_close; + ops.exit = svfs_exit; + ops.lock = svfs_lock; + ops.setfileinfo = svfs_setfileinfo; + ops.qfileinfo = svfs_qfileinfo; + ops.fsinfo = svfs_fsinfo; + ops.fsattr = svfs_fsattr; + ops.lpq = svfs_lpq; + ops.search_first = svfs_search_first; + ops.search_next = svfs_search_next; + ops.search_close = svfs_search_close; + + /* register ourselves with the NTVFS subsystem. We register under the name 'default' + as we wish to be the default backend */ + ret = ntvfs_register("simple", NTVFS_DISK, &ops); + + if (!ret) { + DEBUG(0,("Failed to register POSIX backend!\n")); + return False; + } + + return True; +} diff --git a/source4/ntvfs/simple/svfs.h b/source4/ntvfs/simple/svfs.h new file mode 100644 index 0000000000..e74fd07fce --- /dev/null +++ b/source4/ntvfs/simple/svfs.h @@ -0,0 +1,28 @@ + +struct svfs_private { + /* the base directory */ + char *connectpath; + + /* a linked list of open searches */ + struct search_state *search; + + /* next available search handle */ + uint16 next_search_handle; +}; + +struct svfs_dir { + uint_t count; + char *unix_dir; + struct { + char *name; + struct stat st; + } *files; +}; + +struct search_state { + struct search_state *next, *prev; + TALLOC_CTX *mem_ctx; + uint16 handle; + uint_t 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 0000000000..a8a88cfad0 --- /dev/null +++ b/source4/ntvfs/simple/svfs_util.c @@ -0,0 +1,162 @@ +/* + 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 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +/* + utility functions for simple backend +*/ + +#include "includes.h" +#include "svfs.h" + +/* + convert a windows path to a unix path - don't do any manging or case sensitive handling +*/ +char *svfs_unix_path(struct request_context *req, const char *name) +{ + struct svfs_private *private = req->conn->ntvfs_private; + char *ret; + + if (*name != '\\') { + ret = talloc_asprintf(req->mem_ctx, "%s/%s", private->connectpath, name); + } else { + ret = talloc_asprintf(req->mem_ctx, "%s%s", private->connectpath, name); + } + all_string_sub(ret, "\\", "/", 0); + + strlower(ret); + + 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(TALLOC_CTX *mem_ctx, struct request_context *req, const char *pattern) +{ + char *unix_path; + char *p, *mask; + struct svfs_dir *dir; + DIR *odir; + struct dirent *dent; + uint_t allocated = 0; + char *low_mask; + + unix_path = svfs_unix_path(req, pattern); + if (!unix_path) { return NULL; } + + dir = talloc(mem_ctx, sizeof(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 = talloc_strdup(mem_ctx, mask); + if (!low_mask) { return NULL; } + strlower(low_mask); + + odir = opendir(dir->unix_dir); + if (!odir) { return NULL; } + + while ((dent = readdir(odir))) { + uint_t i = dir->count; + char *full_name; + char *low_name; + + low_name = talloc_strdup(mem_ctx, dent->d_name); + if (!low_name) { continue; } + strlower(low_name); + + /* check it matches the wildcard pattern */ + if (ms_fnmatch(low_mask, low_name, PROTOCOL_NT1) != 0) { + continue; + } + + if (dir->count >= allocated) { + allocated = (allocated + 100) * 1.2; + dir->files = talloc_realloc(mem_ctx, dir->files, allocated * sizeof(dir->files[0])); + if (!dir->files) { + closedir(odir); + return NULL; + } + } + + dir->files[i].name = low_name; + if (!dir->files[i].name) { continue; } + + asprintf(&full_name, "%s/%s", dir->unix_dir, dir->files[i].name); + if (!full_name) { continue; } + + if (stat(full_name, &dir->files[i].st) == 0) { + dir->count++; + } + + free(full_name); + } + + closedir(odir); + + return dir; +} + + +/******************************************************************* +set the time on a file via file descriptor +*******************************************************************/ +int svfs_file_utime(int fd, struct utimbuf *times) +{ + char *fd_path = NULL; + int ret; + + asprintf(&fd_path, "/proc/self/%d", fd); + 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 svfs_unix_to_dos_attrib(mode_t mode) +{ + uint16 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 0000000000..caf7732ac7 --- /dev/null +++ b/source4/ntvfs/simple/vfs_simple.c @@ -0,0 +1,848 @@ +/* + 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 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +/* + this 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 "svfs.h" + +/* + 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 request_context *req, const char *sharename) +{ + struct stat st; + struct tcon_context *conn = req->conn; + struct svfs_private *private; + + conn->ntvfs_private = talloc(conn->mem_ctx, sizeof(struct svfs_private)); + + private = conn->ntvfs_private; + + private->next_search_handle = 0; + private->connectpath = talloc_strdup(conn->mem_ctx, lp_pathname(conn->service)); + + /* the directory must exist */ + if (stat(private->connectpath, &st) != 0 || !S_ISDIR(st.st_mode)) { + DEBUG(0,("'%s' is not a directory, when connecting to [%s]\n", + private->connectpath, sharename)); + return NT_STATUS_BAD_NETWORK_NAME; + } + + conn->fs_type = talloc_strdup(conn->mem_ctx, "NTFS"); + conn->dev_type = talloc_strdup(conn->mem_ctx, "A:"); + + return NT_STATUS_OK; +} + +/* + disconnect from a share +*/ +static NTSTATUS svfs_disconnect(struct tcon_context *req) +{ + return NT_STATUS_OK; +} + +/* + 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 request_context *req, struct smb_unlink *unl) +{ + char *unix_path; + + unix_path = svfs_unix_path(req, unl->in.pattern); + + /* ignoring wildcards ... */ + if (unlink(unix_path) == -1) { + return map_nt_error_from_unix(errno); + } + + return NT_STATUS_OK; +} + + +/* + ioctl interface - we don't do any +*/ +static NTSTATUS svfs_ioctl(struct request_context *req, struct smb_ioctl *io) +{ + return NT_STATUS_INVALID_PARAMETER; +} + +/* + check if a directory exists +*/ +static NTSTATUS svfs_chkpath(struct request_context *req, struct smb_chkpath *cp) +{ + char *unix_path; + struct stat st; + + unix_path = svfs_unix_path(req, cp->in.path); + + if (stat(unix_path, &st) == -1) { + return map_nt_error_from_unix(errno); + } + + if (!S_ISDIR(st.st_mode)) { + return NT_STATUS_NOT_A_DIRECTORY; + } + + return NT_STATUS_OK; +} + +/* + approximately map a struct stat to a generic fileinfo struct +*/ +static NTSTATUS svfs_map_fileinfo(struct request_context *req, union smb_fileinfo *info, struct stat *st) +{ + 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.device = st->st_dev; + info->generic.out.inode = st->st_ino; + /* 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->mem_ctx, "TODO - STORE FILENAME"); + info->generic.out.alt_fname.s = talloc_strdup(req->mem_ctx, "TODO - STORE ALT_FN"); + info->generic.out.ex_attrib = 0; + 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; + info->generic.out.streams = talloc(req->mem_ctx, sizeof(info->stream_info.out.streams[0])); + 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->mem_ctx,"::$DATA"); + /* REWRITE: end */ + + return NT_STATUS_OK; +} + +/* + return info on a pathname +*/ +static NTSTATUS svfs_qpathinfo(struct request_context *req, union smb_fileinfo *info) +{ + char *unix_path; + struct stat st; + + DEBUG(19,("svfs_qpathinfo: file %s level 0x%x\n", info->generic.in.fname, info->generic.level)); + if (info->generic.level != RAW_FILEINFO_GENERIC) { + return ntvfs_map_qpathinfo(req, info); + } + + unix_path = svfs_unix_path(req, info->generic.in.fname); + 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)); + if (errno == 0) + errno = ENOENT; + return map_nt_error_from_unix(errno); + } + DEBUG(19,("svfs_qpathinfo: file %s, stat done\n", unix_path)); + return svfs_map_fileinfo(req, info, &st); +} + +/* + query info on a open file +*/ +static NTSTATUS svfs_qfileinfo(struct request_context *req, union smb_fileinfo *info) +{ + struct stat st; + + if (info->generic.level != RAW_FILEINFO_GENERIC) { + return ntvfs_map_qfileinfo(req, info); + } + + if (fstat(info->generic.in.fnum, &st) == -1) { + if (errno == 0) + errno = ENOENT; + return map_nt_error_from_unix(errno); + } + + return svfs_map_fileinfo(req, info, &st); +} + + +/* + open a file +*/ +static NTSTATUS svfs_open(struct request_context *req, union smb_open *io) +{ + char *unix_path; + struct stat st; + int fd, flags; + + if (io->generic.level != RAW_OPEN_GENERIC) { + return ntvfs_map_open(req, io); + } + + unix_path = svfs_unix_path(req, io->ntcreatex.in.fname); + + switch (io->generic.in.open_disposition) { + case NTCREATEX_DISP_SUPERSEDE: + flags = O_RDWR | O_CREAT | O_TRUNC; + break; + case NTCREATEX_DISP_OPEN: + flags = O_RDWR; + break; + case NTCREATEX_DISP_CREATE: + flags = O_RDWR | O_CREAT | O_EXCL; + break; + case NTCREATEX_DISP_OPEN_IF: + flags = O_RDWR | O_CREAT; + break; + case NTCREATEX_DISP_OVERWRITE: + flags = O_RDWR; + break; + case NTCREATEX_DISP_OVERWRITE_IF: + flags = O_RDWR | O_CREAT | O_TRUNC; + break; + default: + flags = O_RDWR; + break; + } + + if (io->generic.in.create_options & NTCREATEX_OPTIONS_DIRECTORY) { + flags = O_RDONLY | O_DIRECTORY; + 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(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(errno); + } + break; + } + } + DEBUG(9,("svfs_open: file %s flags=0x%x\n", unix_path, flags)); + fd = open(unix_path, flags, 0644); + DEBUG(9,("svfs_open: fd=%d errno=%d\n", fd, errno)); + if (fd == -1) { + if (errno == 0) + errno = ENOENT; + return map_nt_error_from_unix(errno); + } + + if (fstat(fd, &st) == -1) { + DEBUG(9,("svfs_open: fstat errno=%d\n", errno)); + if (errno == 0) + errno = ENOENT; + close(fd); + return map_nt_error_from_unix(errno); + } + + 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.fnum = fd; + 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 request_context *req, union smb_mkdir *md) +{ + char *unix_path; + + if (md->generic.level != RAW_MKDIR_MKDIR) { + return NT_STATUS_INVALID_LEVEL; + } + + unix_path = svfs_unix_path(req, md->mkdir.in.path); + + if (mkdir(unix_path, 0777) == -1) { + return map_nt_error_from_unix(errno); + } + + return NT_STATUS_OK; +} + +/* + remove a directory +*/ +static NTSTATUS svfs_rmdir(struct request_context *req, struct smb_rmdir *rd) +{ + char *unix_path; + + unix_path = svfs_unix_path(req, rd->in.path); + + if (rmdir(unix_path) == -1) { + return map_nt_error_from_unix(errno); + } + + return NT_STATUS_OK; +} + +/* + rename a set of files +*/ +static NTSTATUS svfs_rename(struct request_context *req, struct smb_rename *ren) +{ + char *unix_path1, *unix_path2; + + unix_path1 = svfs_unix_path(req, ren->in.pattern1); + unix_path2 = svfs_unix_path(req, ren->in.pattern2); + + if (rename(unix_path1, unix_path2) != 0) { + return map_nt_error_from_unix(errno); + } + + return NT_STATUS_OK; +} + +/* + copy a set of files +*/ +static NTSTATUS svfs_copy(struct request_context *req, struct smb_copy *cp) +{ + return NT_STATUS_NOT_SUPPORTED; +} + +/* + read from a file +*/ +static NTSTATUS svfs_read(struct request_context *req, union smb_read *rd) +{ + ssize_t ret; + + if (rd->generic.level != RAW_READ_READX) { + return NT_STATUS_NOT_SUPPORTED; + } + + ret = pread(rd->readx.in.fnum, + rd->readx.out.data, + rd->readx.in.maxcnt, + rd->readx.in.offset); + if (ret == -1) { + return map_nt_error_from_unix(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 request_context *req, union smb_write *wr) +{ + ssize_t ret; + + switch (wr->generic.level) { + case RAW_WRITE_WRITEX: + ret = pwrite(wr->writex.in.fnum, + wr->writex.in.data, + wr->writex.in.count, + wr->writex.in.offset); + if (ret == -1) { + return map_nt_error_from_unix(errno); + } + + wr->writex.out.nwritten = ret; + wr->writex.out.remaining = 0; /* should fill this in? */ + + return NT_STATUS_OK; + + case RAW_WRITE_WRITE: + if (wr->write.in.count == 0) { + /* a truncate! */ + ret = ftruncate(wr->write.in.fnum, wr->write.in.offset); + } else { + ret = pwrite(wr->write.in.fnum, + wr->write.in.data, + wr->write.in.count, + wr->write.in.offset); + } + if (ret == -1) { + return map_nt_error_from_unix(errno); + } + + wr->write.out.nwritten = ret; + + return NT_STATUS_OK; + } + + return NT_STATUS_NOT_SUPPORTED; +} + +/* + seek in a file +*/ +static NTSTATUS svfs_seek(struct request_context *req, struct smb_seek *io) +{ + return NT_STATUS_NOT_SUPPORTED; +} + +/* + flush a file +*/ +static NTSTATUS svfs_flush(struct request_context *req, struct smb_flush *io) +{ + fsync(io->in.fnum); + return NT_STATUS_OK; +} + +/* + close a file +*/ +static NTSTATUS svfs_close(struct request_context *req, union smb_close *io) +{ + if (io->generic.level != RAW_CLOSE_CLOSE) { + /* we need a mapping function */ + return NT_STATUS_INVALID_LEVEL; + } + + if (close(io->close.in.fnum) != 0) { + return map_nt_error_from_unix(errno); + } + + return NT_STATUS_OK; +} + +/* + exit - closing files? +*/ +static NTSTATUS svfs_exit(struct request_context *req) +{ + return NT_STATUS_NOT_SUPPORTED; +} + +/* + lock a byte range +*/ +static NTSTATUS svfs_lock(struct request_context *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 request_context *req, union smb_setfileinfo *st) +{ + return NT_STATUS_NOT_SUPPORTED; +} + +/* + set info on a open file +*/ +static NTSTATUS svfs_setfileinfo(struct request_context *req, + union smb_setfileinfo *info) +{ + struct utimbuf unix_times; + int fd; + + switch (info->generic.level) { + case RAW_SFILEINFO_END_OF_FILE_INFO: + case RAW_SFILEINFO_END_OF_FILE_INFORMATION: + if (ftruncate(info->end_of_file_info.file.fnum, + info->end_of_file_info.in.size) != 0) { + return map_nt_error_from_unix(errno); + } + break; + case RAW_SFILEINFO_SETATTRE: + unix_times.actime = info->setattre.in.access_time; + unix_times.modtime = info->setattre.in.write_time; + fd = info->setattre.file.fnum; + + 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(fd, &unix_times) != 0) { + return NT_STATUS_ACCESS_DENIED; + } + break; + } + return NT_STATUS_OK; +} + + +/* + return filesystem space info +*/ +static NTSTATUS svfs_fsinfo(struct request_context *req, union smb_fsinfo *fs) +{ + struct svfs_private *private = req->conn->ntvfs_private; + struct stat st; + + if (fs->generic.level != RAW_QFS_GENERIC) { + return ntvfs_map_fsinfo(req, fs); + } + + if (sys_fsusage(private->connectpath, + &fs->generic.out.blocks_free, + &fs->generic.out.blocks_total) == -1) { + return map_nt_error_from_unix(errno); + } + + fs->generic.out.block_size = 512; + + if (stat(private->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->mem_ctx, lp_servicename(req->conn->service)); + fs->generic.out.fs_type = req->conn->fs_type; + + return NT_STATUS_OK; +} + +#if 0 +/* + return filesystem attribute info +*/ +static NTSTATUS svfs_fsattr(struct request_context *req, union smb_fsattr *fs) +{ + struct stat st; + struct svfs_private *private = req->conn->ntvfs_private; + + if (fs->generic.level != RAW_FSATTR_GENERIC) { + return ntvfs_map_fsattr(req, fs); + } + + if (stat(private->connectpath, &st) != 0) { + return map_nt_error_from_unix(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->mem_ctx, "NTFS"); + fs->generic.out.volume_name = talloc_strdup(req->mem_ctx, + lp_servicename(req->conn->service)); + + return NT_STATUS_OK; +} +#endif + +/* + return print queue info +*/ +static NTSTATUS svfs_lpq(struct request_context *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 request_context *req, union smb_search_first *io, + void *search_private, + BOOL (*callback)(void *, union smb_search_data *)) +{ + struct svfs_dir *dir; + int i; + struct svfs_private *private = req->conn->ntvfs_private; + struct search_state *search; + union smb_search_data file; + TALLOC_CTX *mem_ctx; + uint_t max_count; + + if (io->generic.level != RAW_SEARCH_BOTH_DIRECTORY_INFO) { + return NT_STATUS_NOT_SUPPORTED; + } + + mem_ctx = talloc_init("svfs_search"); + + search = talloc_zero(mem_ctx, sizeof(struct search_state)); + if (!search) { + return NT_STATUS_NO_MEMORY; + } + + max_count = io->t2ffirst.in.max_count; + + dir = svfs_list(mem_ctx, req, io->t2ffirst.in.pattern); + if (!dir) { + talloc_destroy_pool(mem_ctx); + return NT_STATUS_FOOBAR; + } + + search->mem_ctx = mem_ctx; + search->handle = private->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_destroy(search->mem_ctx); + } else { + private->next_search_handle++; + DLIST_ADD(private->search, search); + } + + return NT_STATUS_OK; +} + +/* continue a search */ +static NTSTATUS svfs_search_next(struct request_context *req, union smb_search_next *io, + void *search_private, + BOOL (*callback)(void *, union smb_search_data *)) +{ + struct svfs_dir *dir; + int i; + struct svfs_private *private = req->conn->ntvfs_private; + struct search_state *search; + union smb_search_data file; + uint_t max_count; + + if (io->generic.level != RAW_SEARCH_BOTH_DIRECTORY_INFO) { + return NT_STATUS_NOT_SUPPORTED; + } + + for (search=private->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(private->search, search); + talloc_destroy(search->mem_ctx); + } + + return NT_STATUS_OK; +} + +/* close a search */ +static NTSTATUS svfs_search_close(struct request_context *req, union smb_search_close *io) +{ + struct svfs_private *private = req->conn->ntvfs_private; + struct search_state *search; + + for (search=private->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(private->search, search); + talloc_destroy(search->mem_ctx); + + return NT_STATUS_OK; +} + + +/* + initialialise the POSIX disk backend, registering ourselves with the ntvfs subsystem + */ +BOOL posix_vfs_init(void) +{ + BOOL ret; + struct ntvfs_ops ops; + + ZERO_STRUCT(ops); + + /* fill in all the operations */ + ops.connect = svfs_connect; + ops.disconnect = svfs_disconnect; + ops.unlink = svfs_unlink; + ops.chkpath = svfs_chkpath; + ops.qpathinfo = svfs_qpathinfo; + ops.setpathinfo = svfs_setpathinfo; + ops.open = svfs_open; + ops.mkdir = svfs_mkdir; + ops.rmdir = svfs_rmdir; + ops.rename = svfs_rename; + ops.copy = svfs_copy; + ops.ioctl = svfs_ioctl; + ops.read = svfs_read; + ops.write = svfs_write; + ops.seek = svfs_seek; + ops.flush = svfs_flush; + ops.close = svfs_close; + ops.exit = svfs_exit; + ops.lock = svfs_lock; + ops.setfileinfo = svfs_setfileinfo; + ops.qfileinfo = svfs_qfileinfo; + ops.fsinfo = svfs_fsinfo; + ops.lpq = svfs_lpq; + ops.search_first = svfs_search_first; + ops.search_next = svfs_search_next; + ops.search_close = svfs_search_close; + + /* register ourselves with the NTVFS subsystem. We register under the name 'default' + as we wish to be the default backend */ + ret = ntvfs_register("simple", NTVFS_DISK, &ops); + + if (!ret) { + DEBUG(0,("Failed to register POSIX backend!\n")); + return False; + } + + return True; +} -- cgit