wmi-1.3.16 from opsview.com
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
This is the 'CIFS on CIFS' backend for Samba. It provides a NTVFS
|
||||
backend that talks to a remote CIFS server. The primary aim of this
|
||||
backend is for debugging and development, although some poeple may
|
||||
find it useful as a CIFS gateway.
|
||||
|
||||
There are two modes of operation: Password specified and delegated
|
||||
credentials.
|
||||
|
||||
Password specified:
|
||||
-------------------
|
||||
|
||||
This uses a static username/password in the config file, example:
|
||||
|
||||
[myshare]
|
||||
ntvfs handler = cifs
|
||||
cifs:server = myserver
|
||||
cifs:user = tridge
|
||||
cifs:password = mypass
|
||||
cifs:domain = TESTDOM
|
||||
cifs:share = test
|
||||
|
||||
|
||||
Delegated credentials:
|
||||
----------------------
|
||||
|
||||
If your incoming user is authenticated with Kerberos, and the machine
|
||||
account for this Samba4 proxy server is 'trusted for delegation', then
|
||||
the Samba4 proxy can forward the client's credentials to the target.
|
||||
|
||||
You must be joined to the domain (net join <domain> member).
|
||||
|
||||
To set 'trusted for delegation' with MMC, see the checkbox in the
|
||||
Computer account property page under Users and Computers.
|
||||
|
||||
[myshare]
|
||||
ntvfs handler = cifs
|
||||
cifs:server = myserver
|
||||
cifs:share = test
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,12 @@
|
||||
This module (ntvfs 'cifsposix') provides a simple posix backend which
|
||||
provides better semantics for clients which implement the cifs unix
|
||||
extensions or the newer cifs posix extensions
|
||||
|
||||
WARNING: All file access is done as user 'root'!!!
|
||||
Only use this module for testing, with only test data!!!
|
||||
|
||||
For activating this module use:
|
||||
|
||||
[testshare]
|
||||
path = /tmp/testshare
|
||||
nfvfs handler = cifsposix
|
||||
@@ -0,0 +1,38 @@
|
||||
|
||||
struct cifspsx_private {
|
||||
struct ntvfs_module_context *ntvfs;
|
||||
|
||||
/* the base directory */
|
||||
char *connectpath;
|
||||
|
||||
/* a linked list of open searches */
|
||||
struct search_state *search;
|
||||
|
||||
/* next available search handle */
|
||||
uint16_t next_search_handle;
|
||||
|
||||
struct cifspsx_file *open_files;
|
||||
};
|
||||
|
||||
struct cifspsx_dir {
|
||||
unsigned int count;
|
||||
char *unix_dir;
|
||||
struct cifspsx_dirfile {
|
||||
char *name;
|
||||
struct stat st;
|
||||
} *files;
|
||||
};
|
||||
|
||||
struct cifspsx_file {
|
||||
struct cifspsx_file *next, *prev;
|
||||
int fd;
|
||||
struct ntvfs_handle *handle;
|
||||
char *name;
|
||||
};
|
||||
|
||||
struct search_state {
|
||||
struct search_state *next, *prev;
|
||||
uint16_t handle;
|
||||
unsigned int current_index;
|
||||
struct cifspsx_dir *dir;
|
||||
};
|
||||
@@ -0,0 +1,188 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
simpler Samba VFS filesystem backend for clients which support the
|
||||
CIFS Unix Extensions or newer CIFS POSIX protocol extensions
|
||||
|
||||
|
||||
Copyright (C) Andrew Tridgell 2003
|
||||
Copyright (C) Steve French 2006
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
/*
|
||||
utility functions for cifs posix backend
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "system/filesys.h"
|
||||
#include "cifsposix.h"
|
||||
#include "system/time.h"
|
||||
#include "system/dir.h"
|
||||
#include "ntvfs/ntvfs.h"
|
||||
|
||||
/*
|
||||
convert a windows path to a unix path - don't do any manging or case sensitive handling
|
||||
*/
|
||||
char *cifspsx_unix_path(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, const char *name)
|
||||
{
|
||||
struct cifspsx_private *private = ntvfs->private_data;
|
||||
char *ret;
|
||||
|
||||
if (*name != '\\') {
|
||||
ret = talloc_asprintf(req, "%s/%s", private->connectpath, name);
|
||||
} else {
|
||||
ret = talloc_asprintf(req, "%s%s", private->connectpath, name);
|
||||
}
|
||||
all_string_sub(ret, "\\", "/", 0);
|
||||
|
||||
strlower(ret + strlen(private->connectpath));
|
||||
|
||||
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 cifspsx_dir *cifspsx_list_unix(TALLOC_CTX *mem_ctx, struct ntvfs_request *req, const char *unix_path)
|
||||
{
|
||||
char *p, *mask;
|
||||
struct cifspsx_dir *dir;
|
||||
DIR *odir;
|
||||
struct dirent *dent;
|
||||
uint_t allocated = 0;
|
||||
char *low_mask;
|
||||
|
||||
dir = talloc(mem_ctx, struct cifspsx_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;
|
||||
|
||||
if (strchr(dent->d_name, ':') && !strchr(unix_path, ':')) {
|
||||
/* don't show streams in dir listing */
|
||||
continue;
|
||||
}
|
||||
|
||||
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(dir, dir->files, struct cifspsx_dirfile, allocated);
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
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 cifspsx_dir *cifspsx_list(struct ntvfs_module_context *ntvfs, struct ntvfs_request *req, const char *pattern)
|
||||
{
|
||||
struct cifspsx_private *private = ntvfs->private_data;
|
||||
char *unix_path;
|
||||
|
||||
unix_path = cifspsx_unix_path(ntvfs, req, pattern);
|
||||
if (!unix_path) { return NULL; }
|
||||
|
||||
return cifspsx_list_unix(private, req, unix_path);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************
|
||||
set the time on a file via file descriptor
|
||||
*******************************************************************/
|
||||
int cifspsx_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_t cifspsx_unix_to_dos_attrib(mode_t mode)
|
||||
{
|
||||
uint16_t ret = 0;
|
||||
if (S_ISDIR(mode)) ret |= FILE_ATTRIBUTE_DIRECTORY;
|
||||
if (!(mode & S_IWUSR)) ret |= FILE_ATTRIBUTE_READONLY;
|
||||
return ret;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,688 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
generic byte range locking code
|
||||
|
||||
Copyright (C) Andrew Tridgell 1992-2004
|
||||
Copyright (C) Jeremy Allison 1992-2000
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
/* This module implements a tdb based byte range locking service,
|
||||
replacing the fcntl() based byte range locking previously
|
||||
used. This allows us to provide the same semantics as NT */
|
||||
|
||||
#include "includes.h"
|
||||
#include "system/filesys.h"
|
||||
#include "lib/tdb/include/tdb.h"
|
||||
#include "messaging/messaging.h"
|
||||
#include "db_wrap.h"
|
||||
#include "lib/messaging/irpc.h"
|
||||
#include "libcli/libcli.h"
|
||||
|
||||
/*
|
||||
in this module a "DATA_BLOB *file_key" is a blob that uniquely identifies
|
||||
a file. For a local posix filesystem this will usually be a combination
|
||||
of the device and inode numbers of the file, but it can be anything
|
||||
that uniquely idetifies a file for locking purposes, as long
|
||||
as it is applied consistently.
|
||||
*/
|
||||
|
||||
struct brl_context;
|
||||
/*
|
||||
the lock context contains the elements that define whether one
|
||||
lock is the same as another lock
|
||||
*/
|
||||
struct lock_context {
|
||||
uint32_t server;
|
||||
uint16_t smbpid;
|
||||
struct brl_context *ctx;
|
||||
};
|
||||
|
||||
/* The data in brlock records is an unsorted linear array of these
|
||||
records. It is unnecessary to store the count as tdb provides the
|
||||
size of the record */
|
||||
struct lock_struct {
|
||||
struct lock_context context;
|
||||
struct ntvfs_handle *ntvfs;
|
||||
uint64_t start;
|
||||
uint64_t size;
|
||||
enum brl_type lock_type;
|
||||
void *notify_ptr;
|
||||
};
|
||||
|
||||
/* this struct is attached to on oprn file handle */
|
||||
struct brl_handle {
|
||||
DATA_BLOB key;
|
||||
struct ntvfs_handle *ntvfs;
|
||||
struct lock_struct last_lock;
|
||||
};
|
||||
|
||||
/* this struct is typicaly attached to tcon */
|
||||
struct brl_context {
|
||||
struct tdb_wrap *w;
|
||||
uint32_t server;
|
||||
struct messaging_context *messaging_ctx;
|
||||
};
|
||||
|
||||
/*
|
||||
Open up the brlock.tdb database. Close it down using
|
||||
talloc_free(). We need the messaging_ctx to allow for
|
||||
pending lock notifications.
|
||||
*/
|
||||
struct brl_context *brl_init(TALLOC_CTX *mem_ctx, uint32_t server,
|
||||
struct messaging_context *messaging_ctx)
|
||||
{
|
||||
char *path;
|
||||
struct brl_context *brl;
|
||||
|
||||
brl = talloc(mem_ctx, struct brl_context);
|
||||
if (brl == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
path = smbd_tmp_path(brl, "brlock.tdb");
|
||||
brl->w = tdb_wrap_open(brl, path, 0,
|
||||
TDB_DEFAULT, O_RDWR|O_CREAT, 0600);
|
||||
talloc_free(path);
|
||||
if (brl->w == NULL) {
|
||||
talloc_free(brl);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
brl->server = server;
|
||||
brl->messaging_ctx = messaging_ctx;
|
||||
|
||||
return brl;
|
||||
}
|
||||
|
||||
struct brl_handle *brl_create_handle(TALLOC_CTX *mem_ctx, struct ntvfs_handle *ntvfs, DATA_BLOB *file_key)
|
||||
{
|
||||
struct brl_handle *brlh;
|
||||
|
||||
brlh = talloc(mem_ctx, struct brl_handle);
|
||||
if (brlh == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
brlh->key = *file_key;
|
||||
brlh->ntvfs = ntvfs;
|
||||
ZERO_STRUCT(brlh->last_lock);
|
||||
|
||||
return brlh;
|
||||
}
|
||||
|
||||
/*
|
||||
see if two locking contexts are equal
|
||||
*/
|
||||
static BOOL brl_same_context(struct lock_context *ctx1, struct lock_context *ctx2)
|
||||
{
|
||||
return (ctx1->server == ctx2->server &&
|
||||
ctx1->smbpid == ctx2->smbpid &&
|
||||
ctx1->ctx == ctx2->ctx);
|
||||
}
|
||||
|
||||
/*
|
||||
see if lck1 and lck2 overlap
|
||||
*/
|
||||
static BOOL brl_overlap(struct lock_struct *lck1,
|
||||
struct lock_struct *lck2)
|
||||
{
|
||||
/* this extra check is not redundent - it copes with locks
|
||||
that go beyond the end of 64 bit file space */
|
||||
if (lck1->size != 0 &&
|
||||
lck1->start == lck2->start &&
|
||||
lck1->size == lck2->size) {
|
||||
return True;
|
||||
}
|
||||
|
||||
if (lck1->start >= (lck2->start+lck2->size) ||
|
||||
lck2->start >= (lck1->start+lck1->size)) {
|
||||
return False;
|
||||
}
|
||||
return True;
|
||||
}
|
||||
|
||||
/*
|
||||
See if lock2 can be added when lock1 is in place.
|
||||
*/
|
||||
static BOOL brl_conflict(struct lock_struct *lck1,
|
||||
struct lock_struct *lck2)
|
||||
{
|
||||
/* pending locks don't conflict with anything */
|
||||
if (lck1->lock_type >= PENDING_READ_LOCK ||
|
||||
lck2->lock_type >= PENDING_READ_LOCK) {
|
||||
return False;
|
||||
}
|
||||
|
||||
if (lck1->lock_type == READ_LOCK && lck2->lock_type == READ_LOCK) {
|
||||
return False;
|
||||
}
|
||||
|
||||
if (brl_same_context(&lck1->context, &lck2->context) &&
|
||||
lck2->lock_type == READ_LOCK && lck1->ntvfs == lck2->ntvfs) {
|
||||
return False;
|
||||
}
|
||||
|
||||
return brl_overlap(lck1, lck2);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Check to see if this lock conflicts, but ignore our own locks on the
|
||||
same fnum only.
|
||||
*/
|
||||
static BOOL brl_conflict_other(struct lock_struct *lck1, struct lock_struct *lck2)
|
||||
{
|
||||
/* pending locks don't conflict with anything */
|
||||
if (lck1->lock_type >= PENDING_READ_LOCK ||
|
||||
lck2->lock_type >= PENDING_READ_LOCK) {
|
||||
return False;
|
||||
}
|
||||
|
||||
if (lck1->lock_type == READ_LOCK && lck2->lock_type == READ_LOCK)
|
||||
return False;
|
||||
|
||||
/*
|
||||
* note that incoming write calls conflict with existing READ
|
||||
* locks even if the context is the same. JRA. See LOCKTEST7
|
||||
* in smbtorture.
|
||||
*/
|
||||
if (brl_same_context(&lck1->context, &lck2->context) &&
|
||||
lck1->ntvfs == lck2->ntvfs &&
|
||||
(lck2->lock_type == READ_LOCK || lck1->lock_type == WRITE_LOCK)) {
|
||||
return False;
|
||||
}
|
||||
|
||||
return brl_overlap(lck1, lck2);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
amazingly enough, w2k3 "remembers" whether the last lock failure
|
||||
is the same as this one and changes its error code. I wonder if any
|
||||
app depends on this?
|
||||
*/
|
||||
static NTSTATUS brl_lock_failed(struct brl_handle *brlh, struct lock_struct *lock)
|
||||
{
|
||||
/*
|
||||
* this function is only called for non pending lock!
|
||||
*/
|
||||
|
||||
/*
|
||||
* if the notify_ptr is non NULL,
|
||||
* it means that we're at the end of a pending lock
|
||||
* and the real lock is requested after the timout went by
|
||||
* In this case we need to remember the last_lock and always
|
||||
* give FILE_LOCK_CONFLICT
|
||||
*/
|
||||
if (lock->notify_ptr) {
|
||||
brlh->last_lock = *lock;
|
||||
return NT_STATUS_FILE_LOCK_CONFLICT;
|
||||
}
|
||||
|
||||
/*
|
||||
* amazing the little things you learn with a test
|
||||
* suite. Locks beyond this offset (as a 64 bit
|
||||
* number!) always generate the conflict error code,
|
||||
* unless the top bit is set
|
||||
*/
|
||||
if (lock->start >= 0xEF000000 && (lock->start >> 63) == 0) {
|
||||
brlh->last_lock = *lock;
|
||||
return NT_STATUS_FILE_LOCK_CONFLICT;
|
||||
}
|
||||
|
||||
/*
|
||||
* if the current lock matches the last failed lock on the file handle
|
||||
* and starts at the same offset, then FILE_LOCK_CONFLICT should be returned
|
||||
*/
|
||||
if (lock->context.server == brlh->last_lock.context.server &&
|
||||
lock->context.ctx == brlh->last_lock.context.ctx &&
|
||||
lock->ntvfs == brlh->last_lock.ntvfs &&
|
||||
lock->start == brlh->last_lock.start) {
|
||||
return NT_STATUS_FILE_LOCK_CONFLICT;
|
||||
}
|
||||
|
||||
brlh->last_lock = *lock;
|
||||
return NT_STATUS_LOCK_NOT_GRANTED;
|
||||
}
|
||||
|
||||
/*
|
||||
Lock a range of bytes. The lock_type can be a PENDING_*_LOCK, in
|
||||
which case a real lock is first tried, and if that fails then a
|
||||
pending lock is created. When the pending lock is triggered (by
|
||||
someone else closing an overlapping lock range) a messaging
|
||||
notification is sent, identified by the notify_ptr
|
||||
*/
|
||||
NTSTATUS brl_lock(struct brl_context *brl,
|
||||
struct brl_handle *brlh,
|
||||
uint16_t smbpid,
|
||||
uint64_t start, uint64_t size,
|
||||
enum brl_type lock_type,
|
||||
void *notify_ptr)
|
||||
{
|
||||
TDB_DATA kbuf, dbuf;
|
||||
int count=0, i;
|
||||
struct lock_struct lock, *locks=NULL;
|
||||
NTSTATUS status;
|
||||
|
||||
kbuf.dptr = brlh->key.data;
|
||||
kbuf.dsize = brlh->key.length;
|
||||
|
||||
if (tdb_chainlock(brl->w->tdb, kbuf) != 0) {
|
||||
return NT_STATUS_INTERNAL_DB_CORRUPTION;
|
||||
}
|
||||
|
||||
/* if this is a pending lock, then with the chainlock held we
|
||||
try to get the real lock. If we succeed then we don't need
|
||||
to make it pending. This prevents a possible race condition
|
||||
where the pending lock gets created after the lock that is
|
||||
preventing the real lock gets removed */
|
||||
if (lock_type >= PENDING_READ_LOCK) {
|
||||
enum brl_type rw = (lock_type==PENDING_READ_LOCK? READ_LOCK : WRITE_LOCK);
|
||||
|
||||
/* here we need to force that the last_lock isn't overwritten */
|
||||
lock = brlh->last_lock;
|
||||
status = brl_lock(brl, brlh, smbpid, start, size, rw, NULL);
|
||||
brlh->last_lock = lock;
|
||||
|
||||
if (NT_STATUS_IS_OK(status)) {
|
||||
tdb_chainunlock(brl->w->tdb, kbuf);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
dbuf = tdb_fetch(brl->w->tdb, kbuf);
|
||||
|
||||
lock.context.smbpid = smbpid;
|
||||
lock.context.server = brl->server;
|
||||
lock.context.ctx = brl;
|
||||
lock.ntvfs = brlh->ntvfs;
|
||||
lock.context.ctx = brl;
|
||||
lock.start = start;
|
||||
lock.size = size;
|
||||
lock.lock_type = lock_type;
|
||||
lock.notify_ptr = notify_ptr;
|
||||
|
||||
if (dbuf.dptr) {
|
||||
/* there are existing locks - make sure they don't conflict */
|
||||
locks = (struct lock_struct *)dbuf.dptr;
|
||||
count = dbuf.dsize / sizeof(*locks);
|
||||
for (i=0; i<count; i++) {
|
||||
if (brl_conflict(&locks[i], &lock)) {
|
||||
status = brl_lock_failed(brlh, &lock);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* no conflicts - add it to the list of locks */
|
||||
locks = realloc_p(locks, struct lock_struct, count+1);
|
||||
if (!locks) {
|
||||
status = NT_STATUS_NO_MEMORY;
|
||||
goto fail;
|
||||
} else {
|
||||
dbuf.dptr = (uint8_t *)locks;
|
||||
}
|
||||
locks[count] = lock;
|
||||
dbuf.dsize += sizeof(lock);
|
||||
|
||||
if (tdb_store(brl->w->tdb, kbuf, dbuf, TDB_REPLACE) != 0) {
|
||||
status = NT_STATUS_INTERNAL_DB_CORRUPTION;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
free(dbuf.dptr);
|
||||
tdb_chainunlock(brl->w->tdb, kbuf);
|
||||
|
||||
/* the caller needs to know if the real lock was granted. If
|
||||
we have reached here then it must be a pending lock that
|
||||
was granted, so tell them the lock failed */
|
||||
if (lock_type >= PENDING_READ_LOCK) {
|
||||
return NT_STATUS_LOCK_NOT_GRANTED;
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
|
||||
fail:
|
||||
|
||||
free(dbuf.dptr);
|
||||
tdb_chainunlock(brl->w->tdb, kbuf);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
we are removing a lock that might be holding up a pending lock. Scan for pending
|
||||
locks that cover this range and if we find any then notify the server that it should
|
||||
retry the lock
|
||||
*/
|
||||
static void brl_notify_unlock(struct brl_context *brl,
|
||||
struct lock_struct *locks, int count,
|
||||
struct lock_struct *removed_lock)
|
||||
{
|
||||
int i, last_notice;
|
||||
|
||||
/* the last_notice logic is to prevent stampeding on a lock
|
||||
range. It prevents us sending hundreds of notifies on the
|
||||
same range of bytes. It doesn't prevent all possible
|
||||
stampedes, but it does prevent the most common problem */
|
||||
last_notice = -1;
|
||||
|
||||
for (i=0;i<count;i++) {
|
||||
if (locks[i].lock_type >= PENDING_READ_LOCK &&
|
||||
brl_overlap(&locks[i], removed_lock)) {
|
||||
if (last_notice != -1 && brl_overlap(&locks[i], &locks[last_notice])) {
|
||||
continue;
|
||||
}
|
||||
if (locks[i].lock_type == PENDING_WRITE_LOCK) {
|
||||
last_notice = i;
|
||||
}
|
||||
messaging_send_ptr(brl->messaging_ctx, locks[i].context.server,
|
||||
MSG_BRL_RETRY, locks[i].notify_ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
send notifications for all pending locks - the file is being closed by this
|
||||
user
|
||||
*/
|
||||
static void brl_notify_all(struct brl_context *brl,
|
||||
struct lock_struct *locks, int count)
|
||||
{
|
||||
int i;
|
||||
for (i=0;i<count;i++) {
|
||||
if (locks->lock_type >= PENDING_READ_LOCK) {
|
||||
brl_notify_unlock(brl, locks, count, &locks[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Unlock a range of bytes.
|
||||
*/
|
||||
NTSTATUS brl_unlock(struct brl_context *brl,
|
||||
struct brl_handle *brlh,
|
||||
uint16_t smbpid,
|
||||
uint64_t start, uint64_t size)
|
||||
{
|
||||
TDB_DATA kbuf, dbuf;
|
||||
int count, i;
|
||||
struct lock_struct *locks;
|
||||
struct lock_context context;
|
||||
NTSTATUS status;
|
||||
|
||||
kbuf.dptr = brlh->key.data;
|
||||
kbuf.dsize = brlh->key.length;
|
||||
|
||||
if (tdb_chainlock(brl->w->tdb, kbuf) != 0) {
|
||||
return NT_STATUS_INTERNAL_DB_CORRUPTION;
|
||||
}
|
||||
|
||||
dbuf = tdb_fetch(brl->w->tdb, kbuf);
|
||||
if (!dbuf.dptr) {
|
||||
tdb_chainunlock(brl->w->tdb, kbuf);
|
||||
return NT_STATUS_RANGE_NOT_LOCKED;
|
||||
}
|
||||
|
||||
context.smbpid = smbpid;
|
||||
context.server = brl->server;
|
||||
context.ctx = brl;
|
||||
|
||||
/* there are existing locks - find a match */
|
||||
locks = (struct lock_struct *)dbuf.dptr;
|
||||
count = dbuf.dsize / sizeof(*locks);
|
||||
|
||||
for (i=0; i<count; i++) {
|
||||
struct lock_struct *lock = &locks[i];
|
||||
|
||||
if (brl_same_context(&lock->context, &context) &&
|
||||
lock->ntvfs == brlh->ntvfs &&
|
||||
lock->start == start &&
|
||||
lock->size == size &&
|
||||
lock->lock_type < PENDING_READ_LOCK) {
|
||||
/* found it - delete it */
|
||||
if (count == 1) {
|
||||
if (tdb_delete(brl->w->tdb, kbuf) != 0) {
|
||||
status = NT_STATUS_INTERNAL_DB_CORRUPTION;
|
||||
goto fail;
|
||||
}
|
||||
} else {
|
||||
struct lock_struct removed_lock = *lock;
|
||||
if (i < count-1) {
|
||||
memmove(&locks[i], &locks[i+1],
|
||||
sizeof(*locks)*((count-1) - i));
|
||||
}
|
||||
count--;
|
||||
|
||||
/* send notifications for any relevant pending locks */
|
||||
brl_notify_unlock(brl, locks, count, &removed_lock);
|
||||
|
||||
dbuf.dsize = count * sizeof(*locks);
|
||||
|
||||
if (tdb_store(brl->w->tdb, kbuf, dbuf, TDB_REPLACE) != 0) {
|
||||
status = NT_STATUS_INTERNAL_DB_CORRUPTION;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
free(dbuf.dptr);
|
||||
tdb_chainunlock(brl->w->tdb, kbuf);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
/* we didn't find it */
|
||||
status = NT_STATUS_RANGE_NOT_LOCKED;
|
||||
|
||||
fail:
|
||||
free(dbuf.dptr);
|
||||
tdb_chainunlock(brl->w->tdb, kbuf);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
remove a pending lock. This is called when the caller has either
|
||||
given up trying to establish a lock or when they have succeeded in
|
||||
getting it. In either case they no longer need to be notified.
|
||||
*/
|
||||
NTSTATUS brl_remove_pending(struct brl_context *brl,
|
||||
struct brl_handle *brlh,
|
||||
void *notify_ptr)
|
||||
{
|
||||
TDB_DATA kbuf, dbuf;
|
||||
int count, i;
|
||||
struct lock_struct *locks;
|
||||
NTSTATUS status;
|
||||
|
||||
kbuf.dptr = brlh->key.data;
|
||||
kbuf.dsize = brlh->key.length;
|
||||
|
||||
if (tdb_chainlock(brl->w->tdb, kbuf) != 0) {
|
||||
return NT_STATUS_INTERNAL_DB_CORRUPTION;
|
||||
}
|
||||
|
||||
dbuf = tdb_fetch(brl->w->tdb, kbuf);
|
||||
if (!dbuf.dptr) {
|
||||
tdb_chainunlock(brl->w->tdb, kbuf);
|
||||
return NT_STATUS_RANGE_NOT_LOCKED;
|
||||
}
|
||||
|
||||
/* there are existing locks - find a match */
|
||||
locks = (struct lock_struct *)dbuf.dptr;
|
||||
count = dbuf.dsize / sizeof(*locks);
|
||||
|
||||
for (i=0; i<count; i++) {
|
||||
struct lock_struct *lock = &locks[i];
|
||||
|
||||
if (lock->lock_type >= PENDING_READ_LOCK &&
|
||||
lock->notify_ptr == notify_ptr &&
|
||||
lock->context.server == brl->server) {
|
||||
/* found it - delete it */
|
||||
if (count == 1) {
|
||||
if (tdb_delete(brl->w->tdb, kbuf) != 0) {
|
||||
status = NT_STATUS_INTERNAL_DB_CORRUPTION;
|
||||
goto fail;
|
||||
}
|
||||
} else {
|
||||
if (i < count-1) {
|
||||
memmove(&locks[i], &locks[i+1],
|
||||
sizeof(*locks)*((count-1) - i));
|
||||
}
|
||||
count--;
|
||||
dbuf.dsize = count * sizeof(*locks);
|
||||
if (tdb_store(brl->w->tdb, kbuf, dbuf, TDB_REPLACE) != 0) {
|
||||
status = NT_STATUS_INTERNAL_DB_CORRUPTION;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
free(dbuf.dptr);
|
||||
tdb_chainunlock(brl->w->tdb, kbuf);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
/* we didn't find it */
|
||||
status = NT_STATUS_RANGE_NOT_LOCKED;
|
||||
|
||||
fail:
|
||||
free(dbuf.dptr);
|
||||
tdb_chainunlock(brl->w->tdb, kbuf);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Test if we are allowed to perform IO on a region of an open file
|
||||
*/
|
||||
NTSTATUS brl_locktest(struct brl_context *brl,
|
||||
struct brl_handle *brlh,
|
||||
uint16_t smbpid,
|
||||
uint64_t start, uint64_t size,
|
||||
enum brl_type lock_type)
|
||||
{
|
||||
TDB_DATA kbuf, dbuf;
|
||||
int count, i;
|
||||
struct lock_struct lock, *locks;
|
||||
|
||||
kbuf.dptr = brlh->key.data;
|
||||
kbuf.dsize = brlh->key.length;
|
||||
|
||||
dbuf = tdb_fetch(brl->w->tdb, kbuf);
|
||||
if (dbuf.dptr == NULL) {
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
lock.context.smbpid = smbpid;
|
||||
lock.context.server = brl->server;
|
||||
lock.context.ctx = brl;
|
||||
lock.ntvfs = brlh->ntvfs;
|
||||
lock.start = start;
|
||||
lock.size = size;
|
||||
lock.lock_type = lock_type;
|
||||
|
||||
/* there are existing locks - make sure they don't conflict */
|
||||
locks = (struct lock_struct *)dbuf.dptr;
|
||||
count = dbuf.dsize / sizeof(*locks);
|
||||
|
||||
for (i=0; i<count; i++) {
|
||||
if (brl_conflict_other(&locks[i], &lock)) {
|
||||
free(dbuf.dptr);
|
||||
return NT_STATUS_FILE_LOCK_CONFLICT;
|
||||
}
|
||||
}
|
||||
|
||||
free(dbuf.dptr);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Remove any locks associated with a open file.
|
||||
*/
|
||||
NTSTATUS brl_close(struct brl_context *brl,
|
||||
struct brl_handle *brlh)
|
||||
{
|
||||
TDB_DATA kbuf, dbuf;
|
||||
int count, i, dcount=0;
|
||||
struct lock_struct *locks;
|
||||
NTSTATUS status;
|
||||
|
||||
kbuf.dptr = brlh->key.data;
|
||||
kbuf.dsize = brlh->key.length;
|
||||
|
||||
if (tdb_chainlock(brl->w->tdb, kbuf) != 0) {
|
||||
return NT_STATUS_INTERNAL_DB_CORRUPTION;
|
||||
}
|
||||
|
||||
dbuf = tdb_fetch(brl->w->tdb, kbuf);
|
||||
if (!dbuf.dptr) {
|
||||
tdb_chainunlock(brl->w->tdb, kbuf);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/* there are existing locks - remove any for this fnum */
|
||||
locks = (struct lock_struct *)dbuf.dptr;
|
||||
count = dbuf.dsize / sizeof(*locks);
|
||||
|
||||
for (i=0; i<count; i++) {
|
||||
struct lock_struct *lock = &locks[i];
|
||||
|
||||
if (lock->context.ctx == brl &&
|
||||
lock->context.server == brl->server &&
|
||||
lock->ntvfs == brlh->ntvfs) {
|
||||
/* found it - delete it */
|
||||
if (count > 1 && i < count-1) {
|
||||
memmove(&locks[i], &locks[i+1],
|
||||
sizeof(*locks)*((count-1) - i));
|
||||
}
|
||||
count--;
|
||||
i--;
|
||||
dcount++;
|
||||
}
|
||||
}
|
||||
|
||||
status = NT_STATUS_OK;
|
||||
|
||||
if (count == 0) {
|
||||
if (tdb_delete(brl->w->tdb, kbuf) != 0) {
|
||||
status = NT_STATUS_INTERNAL_DB_CORRUPTION;
|
||||
}
|
||||
} else if (dcount != 0) {
|
||||
/* tell all pending lock holders for this file that
|
||||
they have a chance now. This is a bit indiscriminant,
|
||||
but works OK */
|
||||
brl_notify_all(brl, locks, count);
|
||||
|
||||
dbuf.dsize = count * sizeof(*locks);
|
||||
|
||||
if (tdb_store(brl->w->tdb, kbuf, dbuf, TDB_REPLACE) != 0) {
|
||||
status = NT_STATUS_INTERNAL_DB_CORRUPTION;
|
||||
}
|
||||
}
|
||||
|
||||
free(dbuf.dptr);
|
||||
tdb_chainunlock(brl->w->tdb, kbuf);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
################################################
|
||||
# Start LIBRARY ntvfs_common
|
||||
[SUBSYSTEM::ntvfs_common]
|
||||
PRIVATE_PROTO_HEADER = proto.h
|
||||
OBJ_FILES = \
|
||||
init.o \
|
||||
brlock.o \
|
||||
opendb.o \
|
||||
notify.o
|
||||
PUBLIC_DEPENDENCIES = NDR_OPENDB NDR_NOTIFY sys_notify share
|
||||
# End LIBRARY ntvfs_common
|
||||
################################################
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
Copyright (C) Stefan Metzmacher 2006
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
/*
|
||||
this is the change notify database. It implements mechanisms for
|
||||
storing current change notify waiters in a tdb, and checking if a
|
||||
given event matches any of the stored notify waiiters.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "ntvfs/sysdep/sys_notify.h"
|
||||
|
||||
_PUBLIC_ NTSTATUS ntvfs_common_init(void)
|
||||
{
|
||||
return sys_notify_init();
|
||||
}
|
||||
@@ -0,0 +1,659 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
Copyright (C) Andrew Tridgell 2006
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
/*
|
||||
this is the change notify database. It implements mechanisms for
|
||||
storing current change notify waiters in a tdb, and checking if a
|
||||
given event matches any of the stored notify waiiters.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "system/filesys.h"
|
||||
#include "lib/tdb/include/tdb.h"
|
||||
#include "lib/util/util_tdb.h"
|
||||
#include "messaging/messaging.h"
|
||||
#include "db_wrap.h"
|
||||
#include "lib/messaging/irpc.h"
|
||||
#include "librpc/gen_ndr/ndr_notify.h"
|
||||
#include "lib/util/dlinklist.h"
|
||||
#include "ntvfs/sysdep/sys_notify.h"
|
||||
|
||||
struct notify_context {
|
||||
struct tdb_wrap *w;
|
||||
uint32_t server;
|
||||
struct messaging_context *messaging_ctx;
|
||||
struct notify_list *list;
|
||||
struct notify_array *array;
|
||||
int seqnum;
|
||||
struct sys_notify_context *sys_notify_ctx;
|
||||
};
|
||||
|
||||
|
||||
struct notify_list {
|
||||
struct notify_list *next, *prev;
|
||||
void *private;
|
||||
void (*callback)(void *, const struct notify_event *);
|
||||
void *sys_notify_handle;
|
||||
int depth;
|
||||
};
|
||||
|
||||
#define NOTIFY_KEY "notify array"
|
||||
|
||||
#define NOTIFY_ENABLE "notify:enable"
|
||||
#define NOTIFY_ENABLE_DEFAULT True
|
||||
|
||||
static NTSTATUS notify_remove_all(struct notify_context *notify);
|
||||
static void notify_handler(struct messaging_context *msg_ctx, void *private,
|
||||
uint32_t msg_type, uint32_t server_id, DATA_BLOB *data);
|
||||
|
||||
/*
|
||||
destroy the notify context
|
||||
*/
|
||||
static int notify_destructor(struct notify_context *notify)
|
||||
{
|
||||
messaging_deregister(notify->messaging_ctx, MSG_PVFS_NOTIFY, notify);
|
||||
notify_remove_all(notify);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
Open up the notify.tdb database. You should close it down using
|
||||
talloc_free(). We need the messaging_ctx to allow for notifications
|
||||
via internal messages
|
||||
*/
|
||||
struct notify_context *notify_init(TALLOC_CTX *mem_ctx, uint32_t server,
|
||||
struct messaging_context *messaging_ctx,
|
||||
struct event_context *ev,
|
||||
struct share_config *scfg)
|
||||
{
|
||||
char *path;
|
||||
struct notify_context *notify;
|
||||
|
||||
if (share_bool_option(scfg, NOTIFY_ENABLE, NOTIFY_ENABLE_DEFAULT) != True) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
notify = talloc(mem_ctx, struct notify_context);
|
||||
if (notify == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
path = smbd_tmp_path(notify, "notify.tdb");
|
||||
notify->w = tdb_wrap_open(notify, path, 0,
|
||||
TDB_SEQNUM,
|
||||
O_RDWR|O_CREAT, 0600);
|
||||
talloc_free(path);
|
||||
if (notify->w == NULL) {
|
||||
talloc_free(notify);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
notify->server = server;
|
||||
notify->messaging_ctx = messaging_ctx;
|
||||
notify->list = NULL;
|
||||
notify->array = NULL;
|
||||
notify->seqnum = tdb_get_seqnum(notify->w->tdb);
|
||||
|
||||
talloc_set_destructor(notify, notify_destructor);
|
||||
|
||||
/* register with the messaging subsystem for the notify
|
||||
message type */
|
||||
messaging_register(notify->messaging_ctx, notify,
|
||||
MSG_PVFS_NOTIFY, notify_handler);
|
||||
|
||||
notify->sys_notify_ctx = sys_notify_context_create(scfg, notify, ev);
|
||||
|
||||
return notify;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
lock the notify db
|
||||
*/
|
||||
static NTSTATUS notify_lock(struct notify_context *notify)
|
||||
{
|
||||
if (tdb_lock_bystring(notify->w->tdb, NOTIFY_KEY) != 0) {
|
||||
return NT_STATUS_INTERNAL_DB_CORRUPTION;
|
||||
}
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
unlock the notify db
|
||||
*/
|
||||
static void notify_unlock(struct notify_context *notify)
|
||||
{
|
||||
tdb_unlock_bystring(notify->w->tdb, NOTIFY_KEY);
|
||||
}
|
||||
|
||||
/*
|
||||
load the notify array
|
||||
*/
|
||||
static NTSTATUS notify_load(struct notify_context *notify)
|
||||
{
|
||||
TDB_DATA dbuf;
|
||||
DATA_BLOB blob;
|
||||
NTSTATUS status;
|
||||
int seqnum;
|
||||
|
||||
seqnum = tdb_get_seqnum(notify->w->tdb);
|
||||
|
||||
if (seqnum == notify->seqnum && notify->array != NULL) {
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
notify->seqnum = seqnum;
|
||||
|
||||
talloc_free(notify->array);
|
||||
notify->array = talloc_zero(notify, struct notify_array);
|
||||
NT_STATUS_HAVE_NO_MEMORY(notify->array);
|
||||
|
||||
dbuf = tdb_fetch_bystring(notify->w->tdb, NOTIFY_KEY);
|
||||
if (dbuf.dptr == NULL) {
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
blob.data = dbuf.dptr;
|
||||
blob.length = dbuf.dsize;
|
||||
|
||||
status = ndr_pull_struct_blob(&blob, notify->array, notify->array,
|
||||
(ndr_pull_flags_fn_t)ndr_pull_notify_array);
|
||||
free(dbuf.dptr);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
compare notify entries for sorting
|
||||
*/
|
||||
static int notify_compare(const void *p1, const void *p2)
|
||||
{
|
||||
const struct notify_entry *e1 = p1, *e2 = p2;
|
||||
return strcmp(e1->path, e2->path);
|
||||
}
|
||||
|
||||
/*
|
||||
save the notify array
|
||||
*/
|
||||
static NTSTATUS notify_save(struct notify_context *notify)
|
||||
{
|
||||
TDB_DATA dbuf;
|
||||
DATA_BLOB blob;
|
||||
NTSTATUS status;
|
||||
int ret;
|
||||
TALLOC_CTX *tmp_ctx;
|
||||
|
||||
/* if possible, remove some depth arrays */
|
||||
while (notify->array->num_depths > 0 &&
|
||||
notify->array->depth[notify->array->num_depths-1].num_entries == 0) {
|
||||
notify->array->num_depths--;
|
||||
}
|
||||
|
||||
/* we might just be able to delete the record */
|
||||
if (notify->array->num_depths == 0) {
|
||||
ret = tdb_delete_bystring(notify->w->tdb, NOTIFY_KEY);
|
||||
if (ret != 0) {
|
||||
return NT_STATUS_INTERNAL_DB_CORRUPTION;
|
||||
}
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
tmp_ctx = talloc_new(notify);
|
||||
|
||||
status = ndr_push_struct_blob(&blob, tmp_ctx, notify->array,
|
||||
(ndr_push_flags_fn_t)ndr_push_notify_array);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
talloc_free(tmp_ctx);
|
||||
return status;
|
||||
}
|
||||
|
||||
dbuf.dptr = blob.data;
|
||||
dbuf.dsize = blob.length;
|
||||
|
||||
ret = tdb_store_bystring(notify->w->tdb, NOTIFY_KEY, dbuf, TDB_REPLACE);
|
||||
talloc_free(tmp_ctx);
|
||||
if (ret != 0) {
|
||||
return NT_STATUS_INTERNAL_DB_CORRUPTION;
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
handle incoming notify messages
|
||||
*/
|
||||
static void notify_handler(struct messaging_context *msg_ctx, void *private,
|
||||
uint32_t msg_type, uint32_t server_id, DATA_BLOB *data)
|
||||
{
|
||||
struct notify_context *notify = talloc_get_type(private, struct notify_context);
|
||||
NTSTATUS status;
|
||||
struct notify_event ev;
|
||||
TALLOC_CTX *tmp_ctx = talloc_new(notify);
|
||||
struct notify_list *listel;
|
||||
|
||||
status = ndr_pull_struct_blob(data, tmp_ctx, &ev,
|
||||
(ndr_pull_flags_fn_t)ndr_pull_notify_event);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
talloc_free(tmp_ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
for (listel=notify->list;listel;listel=listel->next) {
|
||||
if (listel->private == ev.private) {
|
||||
listel->callback(listel->private, &ev);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
talloc_free(tmp_ctx);
|
||||
}
|
||||
|
||||
/*
|
||||
callback from sys_notify telling us about changes from the OS
|
||||
*/
|
||||
static void sys_notify_callback(struct sys_notify_context *ctx,
|
||||
void *ptr, struct notify_event *ev)
|
||||
{
|
||||
struct notify_list *listel = talloc_get_type(ptr, struct notify_list);
|
||||
ev->private = listel;
|
||||
listel->callback(listel->private, ev);
|
||||
}
|
||||
|
||||
/*
|
||||
add an entry to the notify array
|
||||
*/
|
||||
static NTSTATUS notify_add_array(struct notify_context *notify, struct notify_entry *e,
|
||||
void *private, int depth)
|
||||
{
|
||||
int i;
|
||||
struct notify_depth *d;
|
||||
struct notify_entry *ee;
|
||||
|
||||
/* possibly expand the depths array */
|
||||
if (depth >= notify->array->num_depths) {
|
||||
d = talloc_realloc(notify->array, notify->array->depth,
|
||||
struct notify_depth, depth+1);
|
||||
NT_STATUS_HAVE_NO_MEMORY(d);
|
||||
for (i=notify->array->num_depths;i<=depth;i++) {
|
||||
ZERO_STRUCT(d[i]);
|
||||
}
|
||||
notify->array->depth = d;
|
||||
notify->array->num_depths = depth+1;
|
||||
}
|
||||
d = ¬ify->array->depth[depth];
|
||||
|
||||
/* expand the entries array */
|
||||
ee = talloc_realloc(notify->array->depth, d->entries, struct notify_entry,
|
||||
d->num_entries+1);
|
||||
NT_STATUS_HAVE_NO_MEMORY(ee);
|
||||
d->entries = ee;
|
||||
|
||||
d->entries[d->num_entries] = *e;
|
||||
d->entries[d->num_entries].private = private;
|
||||
d->entries[d->num_entries].server = notify->server;
|
||||
d->entries[d->num_entries].path_len = strlen(e->path);
|
||||
d->num_entries++;
|
||||
|
||||
d->max_mask |= e->filter;
|
||||
d->max_mask_subdir |= e->subdir_filter;
|
||||
|
||||
if (d->num_entries > 1) {
|
||||
qsort(d->entries, d->num_entries, sizeof(d->entries[0]), notify_compare);
|
||||
}
|
||||
|
||||
/* recalculate the maximum masks */
|
||||
d->max_mask = 0;
|
||||
d->max_mask_subdir = 0;
|
||||
|
||||
for (i=0;i<d->num_entries;i++) {
|
||||
d->max_mask |= d->entries[i].filter;
|
||||
d->max_mask_subdir |= d->entries[i].subdir_filter;
|
||||
}
|
||||
|
||||
return notify_save(notify);
|
||||
}
|
||||
|
||||
/*
|
||||
add a notify watch. This is called when a notify is first setup on a open
|
||||
directory handle.
|
||||
*/
|
||||
NTSTATUS notify_add(struct notify_context *notify, struct notify_entry *e0,
|
||||
void (*callback)(void *, const struct notify_event *),
|
||||
void *private)
|
||||
{
|
||||
struct notify_entry e = *e0;
|
||||
NTSTATUS status;
|
||||
char *tmp_path = NULL;
|
||||
struct notify_list *listel;
|
||||
size_t len;
|
||||
int depth;
|
||||
|
||||
/* see if change notify is enabled at all */
|
||||
if (notify == NULL) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
status = notify_lock(notify);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
status = notify_load(notify);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* cope with /. on the end of the path */
|
||||
len = strlen(e.path);
|
||||
if (len > 1 && e.path[len-1] == '.' && e.path[len-2] == '/') {
|
||||
tmp_path = talloc_strndup(notify, e.path, len-2);
|
||||
if (tmp_path == NULL) {
|
||||
status = NT_STATUS_NO_MEMORY;
|
||||
goto done;
|
||||
}
|
||||
e.path = tmp_path;
|
||||
}
|
||||
|
||||
depth = count_chars(e.path, '/');
|
||||
|
||||
listel = talloc_zero(notify, struct notify_list);
|
||||
if (listel == NULL) {
|
||||
status = NT_STATUS_NO_MEMORY;
|
||||
goto done;
|
||||
}
|
||||
|
||||
listel->private = private;
|
||||
listel->callback = callback;
|
||||
listel->depth = depth;
|
||||
DLIST_ADD(notify->list, listel);
|
||||
|
||||
/* ignore failures from sys_notify */
|
||||
if (notify->sys_notify_ctx != NULL) {
|
||||
/*
|
||||
this call will modify e.filter and e.subdir_filter
|
||||
to remove bits handled by the backend
|
||||
*/
|
||||
status = sys_notify_watch(notify->sys_notify_ctx, &e,
|
||||
sys_notify_callback, listel,
|
||||
&listel->sys_notify_handle);
|
||||
if (NT_STATUS_IS_OK(status)) {
|
||||
talloc_steal(listel, listel->sys_notify_handle);
|
||||
}
|
||||
}
|
||||
|
||||
/* if the system notify handler couldn't handle some of the
|
||||
filter bits, or couldn't handle a request for recursion
|
||||
then we need to install it in the array used for the
|
||||
intra-samba notify handling */
|
||||
if (e.filter != 0 || e.subdir_filter != 0) {
|
||||
status = notify_add_array(notify, &e, private, depth);
|
||||
}
|
||||
|
||||
done:
|
||||
notify_unlock(notify);
|
||||
talloc_free(tmp_path);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
remove a notify watch. Called when the directory handle is closed
|
||||
*/
|
||||
NTSTATUS notify_remove(struct notify_context *notify, void *private)
|
||||
{
|
||||
NTSTATUS status;
|
||||
struct notify_list *listel;
|
||||
int i, depth;
|
||||
struct notify_depth *d;
|
||||
|
||||
/* see if change notify is enabled at all */
|
||||
if (notify == NULL) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
for (listel=notify->list;listel;listel=listel->next) {
|
||||
if (listel->private == private) {
|
||||
DLIST_REMOVE(notify->list, listel);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (listel == NULL) {
|
||||
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
||||
}
|
||||
|
||||
depth = listel->depth;
|
||||
|
||||
talloc_free(listel);
|
||||
|
||||
status = notify_lock(notify);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
status = notify_load(notify);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
notify_unlock(notify);
|
||||
return status;
|
||||
}
|
||||
|
||||
if (depth >= notify->array->num_depths) {
|
||||
notify_unlock(notify);
|
||||
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
||||
}
|
||||
|
||||
/* we only have to search at the depth of this element */
|
||||
d = ¬ify->array->depth[depth];
|
||||
|
||||
for (i=0;i<d->num_entries;i++) {
|
||||
if (private == d->entries[i].private &&
|
||||
notify->server == d->entries[i].server) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == d->num_entries) {
|
||||
notify_unlock(notify);
|
||||
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
||||
}
|
||||
|
||||
if (i < d->num_entries-1) {
|
||||
memmove(&d->entries[i], &d->entries[i+1],
|
||||
sizeof(d->entries[i])*(d->num_entries-(i+1)));
|
||||
}
|
||||
d->num_entries--;
|
||||
|
||||
status = notify_save(notify);
|
||||
|
||||
notify_unlock(notify);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
remove all notify watches for this messaging server
|
||||
*/
|
||||
static NTSTATUS notify_remove_all(struct notify_context *notify)
|
||||
{
|
||||
NTSTATUS status;
|
||||
int i, depth, del_count=0;
|
||||
|
||||
if (notify->list == NULL) {
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
status = notify_lock(notify);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
status = notify_load(notify);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
notify_unlock(notify);
|
||||
return status;
|
||||
}
|
||||
|
||||
/* we have to search for all entries across all depths, looking for matches
|
||||
for our server id */
|
||||
for (depth=0;depth<notify->array->num_depths;depth++) {
|
||||
struct notify_depth *d = ¬ify->array->depth[depth];
|
||||
for (i=0;i<d->num_entries;i++) {
|
||||
if (notify->server == d->entries[i].server) {
|
||||
if (i < d->num_entries-1) {
|
||||
memmove(&d->entries[i], &d->entries[i+1],
|
||||
sizeof(d->entries[i])*(d->num_entries-(i+1)));
|
||||
}
|
||||
i--;
|
||||
d->num_entries--;
|
||||
del_count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (del_count > 0) {
|
||||
status = notify_save(notify);
|
||||
}
|
||||
|
||||
notify_unlock(notify);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
send a notify message to another messaging server
|
||||
*/
|
||||
static void notify_send(struct notify_context *notify, struct notify_entry *e,
|
||||
const char *path, uint32_t action)
|
||||
{
|
||||
struct notify_event ev;
|
||||
DATA_BLOB data;
|
||||
NTSTATUS status;
|
||||
TALLOC_CTX *tmp_ctx;
|
||||
|
||||
ev.action = action;
|
||||
ev.path = path;
|
||||
ev.private = e->private;
|
||||
|
||||
tmp_ctx = talloc_new(notify);
|
||||
|
||||
status = ndr_push_struct_blob(&data, tmp_ctx, &ev,
|
||||
(ndr_push_flags_fn_t)ndr_push_notify_event);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
talloc_free(tmp_ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
status = messaging_send(notify->messaging_ctx, e->server,
|
||||
MSG_PVFS_NOTIFY, &data);
|
||||
talloc_free(tmp_ctx);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
trigger a notify message for anyone waiting on a matching event
|
||||
|
||||
This function is called a lot, and needs to be very fast. The unusual data structure
|
||||
and traversal is designed to be fast in the average case, even for large numbers of
|
||||
notifies
|
||||
*/
|
||||
void notify_trigger(struct notify_context *notify,
|
||||
uint32_t action, uint32_t filter, const char *path)
|
||||
{
|
||||
NTSTATUS status;
|
||||
int depth;
|
||||
const char *p, *next_p;
|
||||
|
||||
/* see if change notify is enabled at all */
|
||||
if (notify == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
status = notify_load(notify);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* loop along the given path, working with each directory depth separately */
|
||||
for (depth=0,p=path;
|
||||
p && depth < notify->array->num_depths;
|
||||
p=next_p,depth++) {
|
||||
int p_len = p - path;
|
||||
int min_i, max_i, i;
|
||||
struct notify_depth *d = ¬ify->array->depth[depth];
|
||||
next_p = strchr(p+1, '/');
|
||||
|
||||
/* see if there are any entries at this depth */
|
||||
if (d->num_entries == 0) continue;
|
||||
|
||||
/* try to skip based on the maximum mask. If next_p is
|
||||
NULL then we know it will be a 'this directory'
|
||||
match, otherwise it must be a subdir match */
|
||||
if (next_p != NULL) {
|
||||
if (0 == (filter & d->max_mask_subdir)) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
if (0 == (filter & d->max_mask)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* we know there is an entry here worth looking
|
||||
for. Use a bisection search to find the first entry
|
||||
with a matching path */
|
||||
min_i = 0;
|
||||
max_i = d->num_entries-1;
|
||||
|
||||
while (min_i < max_i) {
|
||||
struct notify_entry *e;
|
||||
int cmp;
|
||||
i = (min_i+max_i)/2;
|
||||
e = &d->entries[i];
|
||||
cmp = strncmp(path, e->path, p_len);
|
||||
if (cmp == 0) {
|
||||
if (p_len == e->path_len) {
|
||||
max_i = i;
|
||||
} else {
|
||||
max_i = i-1;
|
||||
}
|
||||
} else if (cmp < 0) {
|
||||
max_i = i-1;
|
||||
} else {
|
||||
min_i = i+1;
|
||||
}
|
||||
}
|
||||
|
||||
if (min_i != max_i) {
|
||||
/* none match */
|
||||
continue;
|
||||
}
|
||||
|
||||
/* we now know that the entries start at min_i */
|
||||
for (i=min_i;i<d->num_entries;i++) {
|
||||
struct notify_entry *e = &d->entries[i];
|
||||
if (p_len != e->path_len ||
|
||||
strncmp(path, e->path, p_len) != 0) break;
|
||||
if (next_p != NULL) {
|
||||
if (0 == (filter & e->subdir_filter)) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
if (0 == (filter & e->filter)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
notify_send(notify, e, path + e->path_len + 1, action);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,605 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
Copyright (C) Andrew Tridgell 2004
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
/*
|
||||
this is the open files database. It implements shared storage of
|
||||
what files are open between server instances, and implements the rules
|
||||
of shared access to files.
|
||||
|
||||
The caller needs to provide a file_key, which specifies what file
|
||||
they are talking about. This needs to be a unique key across all
|
||||
filesystems, and is usually implemented in terms of a device/inode
|
||||
pair.
|
||||
|
||||
Before any operations can be performed the caller needs to establish
|
||||
a lock on the record associated with file_key. That is done by
|
||||
calling odb_lock(). The caller releases this lock by calling
|
||||
talloc_free() on the returned handle.
|
||||
|
||||
All other operations on a record are done by passing the odb_lock()
|
||||
handle back to this module. The handle contains internal
|
||||
information about what file_key is being operated on.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "system/filesys.h"
|
||||
#include "lib/tdb/include/tdb.h"
|
||||
#include "messaging/messaging.h"
|
||||
#include "db_wrap.h"
|
||||
#include "lib/messaging/irpc.h"
|
||||
#include "librpc/gen_ndr/ndr_opendb.h"
|
||||
#include "ntvfs/ntvfs.h"
|
||||
|
||||
struct odb_context {
|
||||
struct tdb_wrap *w;
|
||||
struct ntvfs_context *ntvfs_ctx;
|
||||
BOOL oplocks;
|
||||
};
|
||||
|
||||
/*
|
||||
an odb lock handle. You must obtain one of these using odb_lock() before doing
|
||||
any other operations.
|
||||
*/
|
||||
struct odb_lock {
|
||||
struct odb_context *odb;
|
||||
TDB_DATA key;
|
||||
};
|
||||
|
||||
/*
|
||||
Open up the openfiles.tdb database. Close it down using
|
||||
talloc_free(). We need the messaging_ctx to allow for pending open
|
||||
notifications.
|
||||
*/
|
||||
_PUBLIC_ struct odb_context *odb_init(TALLOC_CTX *mem_ctx,
|
||||
struct ntvfs_context *ntvfs_ctx)
|
||||
{
|
||||
char *path;
|
||||
struct odb_context *odb;
|
||||
|
||||
odb = talloc(mem_ctx, struct odb_context);
|
||||
if (odb == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
path = smbd_tmp_path(odb, "openfiles.tdb");
|
||||
odb->w = tdb_wrap_open(odb, path, 0,
|
||||
TDB_DEFAULT,
|
||||
O_RDWR|O_CREAT, 0600);
|
||||
talloc_free(path);
|
||||
if (odb->w == NULL) {
|
||||
talloc_free(odb);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
odb->ntvfs_ctx = ntvfs_ctx;
|
||||
|
||||
/* leave oplocks disabled by default until the code is working */
|
||||
odb->oplocks = lp_parm_bool(-1, "opendb", "oplocks", False);
|
||||
|
||||
return odb;
|
||||
}
|
||||
|
||||
/*
|
||||
destroy a lock on the database
|
||||
*/
|
||||
static int odb_lock_destructor(struct odb_lock *lck)
|
||||
{
|
||||
tdb_chainunlock(lck->odb->w->tdb, lck->key);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
get a lock on a entry in the odb. This call returns a lock handle,
|
||||
which the caller should unlock using talloc_free().
|
||||
*/
|
||||
_PUBLIC_ struct odb_lock *odb_lock(TALLOC_CTX *mem_ctx,
|
||||
struct odb_context *odb, DATA_BLOB *file_key)
|
||||
{
|
||||
struct odb_lock *lck;
|
||||
|
||||
lck = talloc(mem_ctx, struct odb_lock);
|
||||
if (lck == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
lck->odb = talloc_reference(lck, odb);
|
||||
lck->key.dptr = talloc_memdup(lck, file_key->data, file_key->length);
|
||||
lck->key.dsize = file_key->length;
|
||||
if (lck->key.dptr == NULL) {
|
||||
talloc_free(lck);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (tdb_chainlock(odb->w->tdb, lck->key) != 0) {
|
||||
talloc_free(lck);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
talloc_set_destructor(lck, odb_lock_destructor);
|
||||
|
||||
return lck;
|
||||
}
|
||||
|
||||
/*
|
||||
determine if two odb_entry structures conflict
|
||||
|
||||
return NT_STATUS_OK on no conflict
|
||||
*/
|
||||
static NTSTATUS share_conflict(struct opendb_entry *e1, struct opendb_entry *e2)
|
||||
{
|
||||
/* if either open involves no read.write or delete access then
|
||||
it can't conflict */
|
||||
if (!(e1->access_mask & (SEC_FILE_WRITE_DATA |
|
||||
SEC_FILE_APPEND_DATA |
|
||||
SEC_FILE_READ_DATA |
|
||||
SEC_FILE_EXECUTE |
|
||||
SEC_STD_DELETE))) {
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
if (!(e2->access_mask & (SEC_FILE_WRITE_DATA |
|
||||
SEC_FILE_APPEND_DATA |
|
||||
SEC_FILE_READ_DATA |
|
||||
SEC_FILE_EXECUTE |
|
||||
SEC_STD_DELETE))) {
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/* data IO access masks. This is skipped if the two open handles
|
||||
are on different streams (as in that case the masks don't
|
||||
interact) */
|
||||
if (e1->stream_id != e2->stream_id) {
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
#define CHECK_MASK(am, right, sa, share) \
|
||||
if (((am) & (right)) && !((sa) & (share))) return NT_STATUS_SHARING_VIOLATION
|
||||
|
||||
CHECK_MASK(e1->access_mask, SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA,
|
||||
e2->share_access, NTCREATEX_SHARE_ACCESS_WRITE);
|
||||
CHECK_MASK(e2->access_mask, SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA,
|
||||
e1->share_access, NTCREATEX_SHARE_ACCESS_WRITE);
|
||||
|
||||
CHECK_MASK(e1->access_mask, SEC_FILE_READ_DATA | SEC_FILE_EXECUTE,
|
||||
e2->share_access, NTCREATEX_SHARE_ACCESS_READ);
|
||||
CHECK_MASK(e2->access_mask, SEC_FILE_READ_DATA | SEC_FILE_EXECUTE,
|
||||
e1->share_access, NTCREATEX_SHARE_ACCESS_READ);
|
||||
|
||||
CHECK_MASK(e1->access_mask, SEC_STD_DELETE,
|
||||
e2->share_access, NTCREATEX_SHARE_ACCESS_DELETE);
|
||||
CHECK_MASK(e2->access_mask, SEC_STD_DELETE,
|
||||
e1->share_access, NTCREATEX_SHARE_ACCESS_DELETE);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
pull a record, translating from the db format to the opendb_file structure defined
|
||||
in opendb.idl
|
||||
*/
|
||||
static NTSTATUS odb_pull_record(struct odb_lock *lck, struct opendb_file *file)
|
||||
{
|
||||
struct odb_context *odb = lck->odb;
|
||||
TDB_DATA dbuf;
|
||||
DATA_BLOB blob;
|
||||
NTSTATUS status;
|
||||
|
||||
dbuf = tdb_fetch(odb->w->tdb, lck->key);
|
||||
if (dbuf.dptr == NULL) {
|
||||
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
||||
}
|
||||
|
||||
blob.data = dbuf.dptr;
|
||||
blob.length = dbuf.dsize;
|
||||
|
||||
status = ndr_pull_struct_blob(&blob, lck, file, (ndr_pull_flags_fn_t)ndr_pull_opendb_file);
|
||||
|
||||
free(dbuf.dptr);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
push a record, translating from the opendb_file structure defined in opendb.idl
|
||||
*/
|
||||
static NTSTATUS odb_push_record(struct odb_lock *lck, struct opendb_file *file)
|
||||
{
|
||||
struct odb_context *odb = lck->odb;
|
||||
TDB_DATA dbuf;
|
||||
DATA_BLOB blob;
|
||||
NTSTATUS status;
|
||||
int ret;
|
||||
|
||||
if (file->num_entries == 0) {
|
||||
ret = tdb_delete(odb->w->tdb, lck->key);
|
||||
if (ret != 0) {
|
||||
return NT_STATUS_INTERNAL_DB_CORRUPTION;
|
||||
}
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
status = ndr_push_struct_blob(&blob, lck, file, (ndr_push_flags_fn_t)ndr_push_opendb_file);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
dbuf.dptr = blob.data;
|
||||
dbuf.dsize = blob.length;
|
||||
|
||||
ret = tdb_store(odb->w->tdb, lck->key, dbuf, TDB_REPLACE);
|
||||
data_blob_free(&blob);
|
||||
if (ret != 0) {
|
||||
return NT_STATUS_INTERNAL_DB_CORRUPTION;
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
send an oplock break to a client
|
||||
*/
|
||||
static NTSTATUS odb_oplock_break_send(struct odb_context *odb, struct opendb_entry *e)
|
||||
{
|
||||
/* tell the server handling this open file about the need to send the client
|
||||
a break */
|
||||
return messaging_send_ptr(odb->ntvfs_ctx->msg_ctx, e->server,
|
||||
MSG_NTVFS_OPLOCK_BREAK, e->file_handle);
|
||||
}
|
||||
|
||||
/*
|
||||
register an open file in the open files database. This implements the share_access
|
||||
rules
|
||||
|
||||
Note that the path is only used by the delete on close logic, not
|
||||
for comparing with other filenames
|
||||
*/
|
||||
_PUBLIC_ NTSTATUS odb_open_file(struct odb_lock *lck, void *file_handle,
|
||||
uint32_t stream_id, uint32_t share_access,
|
||||
uint32_t access_mask, BOOL delete_on_close,
|
||||
const char *path,
|
||||
uint32_t oplock_level, uint32_t *oplock_granted)
|
||||
{
|
||||
struct odb_context *odb = lck->odb;
|
||||
struct opendb_entry e;
|
||||
int i;
|
||||
struct opendb_file file;
|
||||
NTSTATUS status;
|
||||
|
||||
if (odb->oplocks == False) {
|
||||
oplock_level = OPLOCK_NONE;
|
||||
}
|
||||
|
||||
status = odb_pull_record(lck, &file);
|
||||
if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
|
||||
/* initialise a blank structure */
|
||||
ZERO_STRUCT(file);
|
||||
file.path = path;
|
||||
} else {
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
}
|
||||
|
||||
/* see if it conflicts */
|
||||
e.server = odb->ntvfs_ctx->server_id;
|
||||
e.file_handle = file_handle;
|
||||
e.stream_id = stream_id;
|
||||
e.share_access = share_access;
|
||||
e.access_mask = access_mask;
|
||||
e.delete_on_close = delete_on_close;
|
||||
e.oplock_level = OPLOCK_NONE;
|
||||
|
||||
/* see if anyone has an oplock, which we need to break */
|
||||
for (i=0;i<file.num_entries;i++) {
|
||||
if (file.entries[i].oplock_level == OPLOCK_BATCH) {
|
||||
/* a batch oplock caches close calls, which
|
||||
means the client application might have
|
||||
already closed the file. We have to allow
|
||||
this close to propogate by sending a oplock
|
||||
break request and suspending this call
|
||||
until the break is acknowledged or the file
|
||||
is closed */
|
||||
odb_oplock_break_send(odb, &file.entries[i]);
|
||||
return NT_STATUS_OPLOCK_NOT_GRANTED;
|
||||
}
|
||||
}
|
||||
|
||||
if (file.delete_on_close ||
|
||||
(file.num_entries != 0 && delete_on_close)) {
|
||||
/* while delete on close is set, no new opens are allowed */
|
||||
return NT_STATUS_DELETE_PENDING;
|
||||
}
|
||||
|
||||
/* check for sharing violations */
|
||||
for (i=0;i<file.num_entries;i++) {
|
||||
status = share_conflict(&file.entries[i], &e);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
}
|
||||
|
||||
/* we now know the open could succeed, but we need to check
|
||||
for any exclusive oplocks. We can't grant a second open
|
||||
till these are broken. Note that we check for batch oplocks
|
||||
before checking for sharing violations, and check for
|
||||
exclusive oplocks afterwards. */
|
||||
for (i=0;i<file.num_entries;i++) {
|
||||
if (file.entries[i].oplock_level == OPLOCK_EXCLUSIVE) {
|
||||
odb_oplock_break_send(odb, &file.entries[i]);
|
||||
return NT_STATUS_OPLOCK_NOT_GRANTED;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
possibly grant an exclusive or batch oplock if this is the only client
|
||||
with the file open. We don't yet grant levelII oplocks.
|
||||
*/
|
||||
if (oplock_granted != NULL) {
|
||||
if ((oplock_level == OPLOCK_BATCH ||
|
||||
oplock_level == OPLOCK_EXCLUSIVE) &&
|
||||
file.num_entries == 0) {
|
||||
(*oplock_granted) = oplock_level;
|
||||
} else {
|
||||
(*oplock_granted) = OPLOCK_NONE;
|
||||
}
|
||||
e.oplock_level = (*oplock_granted);
|
||||
}
|
||||
|
||||
/* it doesn't conflict, so add it to the end */
|
||||
file.entries = talloc_realloc(lck, file.entries, struct opendb_entry,
|
||||
file.num_entries+1);
|
||||
NT_STATUS_HAVE_NO_MEMORY(file.entries);
|
||||
|
||||
file.entries[file.num_entries] = e;
|
||||
file.num_entries++;
|
||||
|
||||
return odb_push_record(lck, &file);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
register a pending open file in the open files database
|
||||
*/
|
||||
_PUBLIC_ NTSTATUS odb_open_file_pending(struct odb_lock *lck, void *private)
|
||||
{
|
||||
struct odb_context *odb = lck->odb;
|
||||
struct opendb_file file;
|
||||
NTSTATUS status;
|
||||
|
||||
status = odb_pull_record(lck, &file);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
file.pending = talloc_realloc(lck, file.pending, struct opendb_pending,
|
||||
file.num_pending+1);
|
||||
NT_STATUS_HAVE_NO_MEMORY(file.pending);
|
||||
|
||||
file.pending[file.num_pending].server = odb->ntvfs_ctx->server_id;
|
||||
file.pending[file.num_pending].notify_ptr = private;
|
||||
|
||||
file.num_pending++;
|
||||
|
||||
return odb_push_record(lck, &file);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
remove a opendb entry
|
||||
*/
|
||||
_PUBLIC_ NTSTATUS odb_close_file(struct odb_lock *lck, void *file_handle)
|
||||
{
|
||||
struct odb_context *odb = lck->odb;
|
||||
struct opendb_file file;
|
||||
int i;
|
||||
NTSTATUS status;
|
||||
|
||||
status = odb_pull_record(lck, &file);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
/* find the entry, and delete it */
|
||||
for (i=0;i<file.num_entries;i++) {
|
||||
if (file_handle == file.entries[i].file_handle &&
|
||||
odb->ntvfs_ctx->server_id == file.entries[i].server) {
|
||||
if (file.entries[i].delete_on_close) {
|
||||
file.delete_on_close = True;
|
||||
}
|
||||
if (i < file.num_entries-1) {
|
||||
memmove(file.entries+i, file.entries+i+1,
|
||||
(file.num_entries - (i+1)) *
|
||||
sizeof(struct opendb_entry));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == file.num_entries) {
|
||||
return NT_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
/* send any pending notifications, removing them once sent */
|
||||
for (i=0;i<file.num_pending;i++) {
|
||||
messaging_send_ptr(odb->ntvfs_ctx->msg_ctx, file.pending[i].server,
|
||||
MSG_PVFS_RETRY_OPEN,
|
||||
file.pending[i].notify_ptr);
|
||||
}
|
||||
file.num_pending = 0;
|
||||
|
||||
file.num_entries--;
|
||||
|
||||
return odb_push_record(lck, &file);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
remove a pending opendb entry
|
||||
*/
|
||||
_PUBLIC_ NTSTATUS odb_remove_pending(struct odb_lock *lck, void *private)
|
||||
{
|
||||
struct odb_context *odb = lck->odb;
|
||||
int i;
|
||||
NTSTATUS status;
|
||||
struct opendb_file file;
|
||||
|
||||
status = odb_pull_record(lck, &file);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
/* find the entry, and delete it */
|
||||
for (i=0;i<file.num_pending;i++) {
|
||||
if (private == file.pending[i].notify_ptr &&
|
||||
odb->ntvfs_ctx->server_id == file.pending[i].server) {
|
||||
if (i < file.num_pending-1) {
|
||||
memmove(file.pending+i, file.pending+i+1,
|
||||
(file.num_pending - (i+1)) *
|
||||
sizeof(struct opendb_pending));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == file.num_pending) {
|
||||
return NT_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
file.num_pending--;
|
||||
|
||||
return odb_push_record(lck, &file);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
rename the path in a open file
|
||||
*/
|
||||
_PUBLIC_ NTSTATUS odb_rename(struct odb_lock *lck, const char *path)
|
||||
{
|
||||
struct opendb_file file;
|
||||
NTSTATUS status;
|
||||
|
||||
status = odb_pull_record(lck, &file);
|
||||
if (NT_STATUS_EQUAL(NT_STATUS_OBJECT_NAME_NOT_FOUND, status)) {
|
||||
/* not having the record at all is OK */
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
file.path = path;
|
||||
return odb_push_record(lck, &file);
|
||||
}
|
||||
|
||||
/*
|
||||
update delete on close flag on an open file
|
||||
*/
|
||||
_PUBLIC_ NTSTATUS odb_set_delete_on_close(struct odb_lock *lck, BOOL del_on_close)
|
||||
{
|
||||
NTSTATUS status;
|
||||
struct opendb_file file;
|
||||
|
||||
status = odb_pull_record(lck, &file);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
file.delete_on_close = del_on_close;
|
||||
|
||||
return odb_push_record(lck, &file);
|
||||
}
|
||||
|
||||
/*
|
||||
return the current value of the delete_on_close bit, and how many
|
||||
people still have the file open
|
||||
*/
|
||||
_PUBLIC_ NTSTATUS odb_get_delete_on_close(struct odb_context *odb,
|
||||
DATA_BLOB *key, BOOL *del_on_close,
|
||||
int *open_count, char **path)
|
||||
{
|
||||
NTSTATUS status;
|
||||
struct opendb_file file;
|
||||
struct odb_lock *lck;
|
||||
|
||||
lck = odb_lock(odb, odb, key);
|
||||
NT_STATUS_HAVE_NO_MEMORY(lck);
|
||||
|
||||
status = odb_pull_record(lck, &file);
|
||||
if (NT_STATUS_EQUAL(NT_STATUS_OBJECT_NAME_NOT_FOUND, status)) {
|
||||
talloc_free(lck);
|
||||
(*del_on_close) = False;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
talloc_free(lck);
|
||||
return status;
|
||||
}
|
||||
|
||||
(*del_on_close) = file.delete_on_close;
|
||||
if (open_count != NULL) {
|
||||
(*open_count) = file.num_entries;
|
||||
}
|
||||
if (path != NULL) {
|
||||
*path = talloc_strdup(odb, file.path);
|
||||
NT_STATUS_HAVE_NO_MEMORY(*path);
|
||||
if (file.num_entries == 1 && file.entries[0].delete_on_close) {
|
||||
(*del_on_close) = True;
|
||||
}
|
||||
}
|
||||
|
||||
talloc_free(lck);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
determine if a file can be opened with the given share_access,
|
||||
create_options and access_mask
|
||||
*/
|
||||
_PUBLIC_ NTSTATUS odb_can_open(struct odb_lock *lck,
|
||||
uint32_t share_access, uint32_t create_options,
|
||||
uint32_t access_mask)
|
||||
{
|
||||
struct odb_context *odb = lck->odb;
|
||||
NTSTATUS status;
|
||||
struct opendb_file file;
|
||||
struct opendb_entry e;
|
||||
int i;
|
||||
|
||||
status = odb_pull_record(lck, &file);
|
||||
if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
if ((create_options & NTCREATEX_OPTIONS_DELETE_ON_CLOSE) &&
|
||||
file.num_entries != 0) {
|
||||
return NT_STATUS_SHARING_VIOLATION;
|
||||
}
|
||||
|
||||
if (file.delete_on_close) {
|
||||
return NT_STATUS_DELETE_PENDING;
|
||||
}
|
||||
|
||||
e.server = odb->ntvfs_ctx->server_id;
|
||||
e.file_handle = NULL;
|
||||
e.stream_id = 0;
|
||||
e.share_access = share_access;
|
||||
e.access_mask = access_mask;
|
||||
|
||||
for (i=0;i<file.num_entries;i++) {
|
||||
status = share_conflict(&file.entries[i], &e);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
/* note that we discard the error code
|
||||
here. We do this as unless we are actually
|
||||
doing an open (which comes via a different
|
||||
function), we need to return a sharing
|
||||
violation */
|
||||
return NT_STATUS_SHARING_VIOLATION;
|
||||
}
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
# NTVFS Server subsystem
|
||||
include posix/config.mk
|
||||
include common/config.mk
|
||||
include unixuid/config.mk
|
||||
include sysdep/config.mk
|
||||
|
||||
################################################
|
||||
# Start MODULE ntvfs_cifs
|
||||
[MODULE::ntvfs_cifs]
|
||||
INIT_FUNCTION = ntvfs_cifs_init
|
||||
SUBSYSTEM = ntvfs
|
||||
OBJ_FILES = \
|
||||
cifs/vfs_cifs.o
|
||||
PUBLIC_DEPENDENCIES = \
|
||||
LIBCLI_SMB LIBCLI_RAW
|
||||
# End MODULE ntvfs_cifs
|
||||
################################################
|
||||
|
||||
################################################
|
||||
# Start MODULE ntvfs_simple
|
||||
[MODULE::ntvfs_simple]
|
||||
INIT_FUNCTION = ntvfs_simple_init
|
||||
SUBSYSTEM = ntvfs
|
||||
PRIVATE_PROTO_HEADER = simple/proto.h
|
||||
OBJ_FILES = \
|
||||
simple/vfs_simple.o \
|
||||
simple/svfs_util.o
|
||||
# End MODULE ntvfs_simple
|
||||
################################################
|
||||
|
||||
################################################
|
||||
# Start MODULE ntvfs_cifsposix
|
||||
[MODULE::ntvfs_cifsposix]
|
||||
#ENABLE = NO
|
||||
INIT_FUNCTION = ntvfs_cifs_posix_init
|
||||
SUBSYSTEM = ntvfs
|
||||
PRIVATE_PROTO_HEADER = cifs_posix_cli/proto.h
|
||||
OBJ_FILES = \
|
||||
cifs_posix_cli/vfs_cifs_posix.o \
|
||||
cifs_posix_cli/svfs_util.o
|
||||
# End MODULE ntvfs_cifsposix
|
||||
################################################
|
||||
|
||||
################################################
|
||||
# Start MODULE ntvfs_print
|
||||
[MODULE::ntvfs_print]
|
||||
INIT_FUNCTION = ntvfs_print_init
|
||||
SUBSYSTEM = ntvfs
|
||||
OBJ_FILES = \
|
||||
print/vfs_print.o
|
||||
# End MODULE ntvfs_print
|
||||
################################################
|
||||
|
||||
################################################
|
||||
# Start MODULE ntvfs_ipc
|
||||
[MODULE::ntvfs_ipc]
|
||||
SUBSYSTEM = ntvfs
|
||||
INIT_FUNCTION = ntvfs_ipc_init
|
||||
PRIVATE_PROTO_HEADER = ipc/proto.h
|
||||
OBJ_FILES = \
|
||||
ipc/vfs_ipc.o \
|
||||
ipc/ipc_rap.o \
|
||||
ipc/rap_server.o
|
||||
PUBLIC_DEPENDENCIES = dcerpc_server DCERPC_COMMON
|
||||
# End MODULE ntvfs_ipc
|
||||
################################################
|
||||
|
||||
|
||||
################################################
|
||||
# Start MODULE ntvfs_nbench
|
||||
[MODULE::ntvfs_nbench]
|
||||
SUBSYSTEM = ntvfs
|
||||
INIT_FUNCTION = ntvfs_nbench_init
|
||||
OBJ_FILES = \
|
||||
nbench/vfs_nbench.o
|
||||
# End MODULE ntvfs_nbench
|
||||
################################################
|
||||
|
||||
|
||||
################################################
|
||||
# Start SUBSYSTEM NTVFS
|
||||
[LIBRARY::ntvfs]
|
||||
PUBLIC_HEADERS = ntvfs.h
|
||||
VERSION = 0.0.1
|
||||
SO_VERSION = 0
|
||||
DESCRIPTION = Virtual File System with NTFS semantics
|
||||
PRIVATE_PROTO_HEADER = ntvfs_proto.h
|
||||
OBJ_FILES = \
|
||||
ntvfs_base.o \
|
||||
ntvfs_generic.o \
|
||||
ntvfs_interface.o \
|
||||
ntvfs_util.o
|
||||
PRIVATE_DEPENDENCIES = auth
|
||||
#
|
||||
# End SUBSYSTEM NTVFS
|
||||
################################################
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -0,0 +1,462 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
RAP handlers
|
||||
|
||||
Copyright (C) Volker Lendecke 2004
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 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.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "libcli/raw/interfaces.h"
|
||||
#include "libcli/rap/rap.h"
|
||||
#include "ntvfs/ipc/proto.h"
|
||||
#include "librpc/ndr/libndr.h"
|
||||
|
||||
#define NERR_Success 0
|
||||
#define NERR_badpass 86
|
||||
#define NERR_notsupported 50
|
||||
|
||||
struct rap_string_heap {
|
||||
TALLOC_CTX *mem_ctx;
|
||||
int offset;
|
||||
int num_strings;
|
||||
const char **strings;
|
||||
};
|
||||
|
||||
struct rap_heap_save {
|
||||
int offset, num_strings;
|
||||
};
|
||||
|
||||
static void rap_heap_save(struct rap_string_heap *heap,
|
||||
struct rap_heap_save *save)
|
||||
{
|
||||
save->offset = heap->offset;
|
||||
save->num_strings = heap->num_strings;
|
||||
}
|
||||
|
||||
static void rap_heap_restore(struct rap_string_heap *heap,
|
||||
struct rap_heap_save *save)
|
||||
{
|
||||
heap->offset = save->offset;
|
||||
heap->num_strings = save->num_strings;
|
||||
}
|
||||
|
||||
struct rap_call {
|
||||
TALLOC_CTX *mem_ctx;
|
||||
uint16_t callno;
|
||||
const char *paramdesc;
|
||||
const char *datadesc;
|
||||
|
||||
uint16_t status;
|
||||
uint16_t convert;
|
||||
|
||||
uint16_t rcv_paramlen, rcv_datalen;
|
||||
|
||||
struct ndr_push *ndr_push_param;
|
||||
struct ndr_push *ndr_push_data;
|
||||
struct rap_string_heap *heap;
|
||||
|
||||
struct ndr_pull *ndr_pull_param;
|
||||
struct ndr_pull *ndr_pull_data;
|
||||
};
|
||||
|
||||
#define RAPNDR_FLAGS (LIBNDR_FLAG_NOALIGN|LIBNDR_FLAG_STR_ASCII|LIBNDR_FLAG_STR_NULLTERM);
|
||||
|
||||
static struct rap_call *new_rap_srv_call(TALLOC_CTX *mem_ctx,
|
||||
struct smb_trans2 *trans)
|
||||
{
|
||||
struct rap_call *call;
|
||||
|
||||
call = talloc(mem_ctx, struct rap_call);
|
||||
|
||||
if (call == NULL)
|
||||
return NULL;
|
||||
|
||||
ZERO_STRUCTP(call);
|
||||
|
||||
call->mem_ctx = mem_ctx;
|
||||
|
||||
call->ndr_pull_param = ndr_pull_init_blob(&trans->in.params, mem_ctx);
|
||||
call->ndr_pull_param->flags = RAPNDR_FLAGS;
|
||||
|
||||
call->ndr_pull_data = ndr_pull_init_blob(&trans->in.data, mem_ctx);
|
||||
call->ndr_pull_data->flags = RAPNDR_FLAGS;
|
||||
|
||||
call->heap = talloc(mem_ctx, struct rap_string_heap);
|
||||
|
||||
if (call->heap == NULL)
|
||||
return NULL;
|
||||
|
||||
ZERO_STRUCTP(call->heap);
|
||||
|
||||
call->heap->mem_ctx = mem_ctx;
|
||||
|
||||
return call;
|
||||
}
|
||||
|
||||
static NTSTATUS rap_srv_pull_word(struct rap_call *call, uint16_t *result)
|
||||
{
|
||||
if (*call->paramdesc++ != 'W')
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
|
||||
return ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, result);
|
||||
}
|
||||
|
||||
static NTSTATUS rap_srv_pull_dword(struct rap_call *call, uint32_t *result)
|
||||
{
|
||||
if (*call->paramdesc++ != 'D')
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
|
||||
return ndr_pull_uint32(call->ndr_pull_param, NDR_SCALARS, result);
|
||||
}
|
||||
|
||||
static NTSTATUS rap_srv_pull_string(struct rap_call *call, const char **result)
|
||||
{
|
||||
char paramdesc = *call->paramdesc++;
|
||||
|
||||
if (paramdesc == 'O') {
|
||||
*result = NULL;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
if (paramdesc != 'z')
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
|
||||
return ndr_pull_string(call->ndr_pull_param, NDR_SCALARS, result);
|
||||
}
|
||||
|
||||
static NTSTATUS rap_srv_pull_bufsize(struct rap_call *call, uint16_t *bufsize)
|
||||
{
|
||||
NTSTATUS result;
|
||||
|
||||
if ( (*call->paramdesc++ != 'r') || (*call->paramdesc++ != 'L') )
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
|
||||
result = ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, bufsize);
|
||||
|
||||
if (!NT_STATUS_IS_OK(result))
|
||||
return result;
|
||||
|
||||
call->heap->offset = *bufsize;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static NTSTATUS rap_srv_pull_expect_multiple(struct rap_call *call)
|
||||
{
|
||||
if ( (*call->paramdesc++ != 'e') || (*call->paramdesc++ != 'h') )
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static NTSTATUS rap_push_string(struct ndr_push *data_push,
|
||||
struct rap_string_heap *heap,
|
||||
const char *str)
|
||||
{
|
||||
size_t space;
|
||||
|
||||
if (str == NULL)
|
||||
str = "";
|
||||
|
||||
space = strlen(str)+1;
|
||||
|
||||
if (heap->offset < space)
|
||||
return NT_STATUS_BUFFER_TOO_SMALL;
|
||||
|
||||
heap->offset -= space;
|
||||
|
||||
NDR_CHECK(ndr_push_uint16(data_push, NDR_SCALARS, heap->offset));
|
||||
NDR_CHECK(ndr_push_uint16(data_push, NDR_SCALARS, 0));
|
||||
|
||||
heap->strings = talloc_realloc(heap->mem_ctx,
|
||||
heap->strings,
|
||||
const char *,
|
||||
heap->num_strings + 1);
|
||||
|
||||
if (heap->strings == NULL)
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
|
||||
heap->strings[heap->num_strings] = str;
|
||||
heap->num_strings += 1;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
#define NDR_OK(call) do { result = call; \
|
||||
if (NT_STATUS_EQUAL(result, NT_STATUS_BUFFER_TOO_SMALL)) \
|
||||
goto buffer_overflow; \
|
||||
if (!NT_STATUS_IS_OK(result)) \
|
||||
goto done; \
|
||||
} while (0)
|
||||
|
||||
static NTSTATUS _rap_netshareenum(struct rap_call *call)
|
||||
{
|
||||
struct rap_NetShareEnum r;
|
||||
NTSTATUS result;
|
||||
|
||||
NDR_OK(rap_srv_pull_word(call, &r.in.level));
|
||||
NDR_OK(rap_srv_pull_bufsize(call, &r.in.bufsize));
|
||||
NDR_OK(rap_srv_pull_expect_multiple(call));
|
||||
|
||||
switch(r.in.level) {
|
||||
case 0:
|
||||
if (strcmp(call->datadesc, "B13") != 0)
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
break;
|
||||
case 1:
|
||||
if (strcmp(call->datadesc, "B13BWz") != 0)
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
break;
|
||||
default:
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
break;
|
||||
}
|
||||
|
||||
result = rap_netshareenum(call, &r);
|
||||
|
||||
if (!NT_STATUS_IS_OK(result))
|
||||
return result;
|
||||
|
||||
for (r.out.count = 0; r.out.count < r.out.available; r.out.count++) {
|
||||
|
||||
int i = r.out.count;
|
||||
struct ndr_push_save data_save;
|
||||
struct rap_heap_save heap_save;
|
||||
|
||||
ndr_push_save(call->ndr_push_data, &data_save);
|
||||
rap_heap_save(call->heap, &heap_save);
|
||||
|
||||
switch(r.in.level) {
|
||||
case 0:
|
||||
NDR_OK(ndr_push_bytes(call->ndr_push_data,
|
||||
(const uint8_t *)r.out.info[i].info0.name,
|
||||
sizeof(r.out.info[i].info0.name)));
|
||||
break;
|
||||
case 1:
|
||||
NDR_OK(ndr_push_bytes(call->ndr_push_data,
|
||||
(const uint8_t *)r.out.info[i].info1.name,
|
||||
sizeof(r.out.info[i].info1.name)));
|
||||
NDR_OK(ndr_push_uint8(call->ndr_push_data,
|
||||
NDR_SCALARS, r.out.info[i].info1.pad));
|
||||
NDR_OK(ndr_push_uint16(call->ndr_push_data,
|
||||
NDR_SCALARS, r.out.info[i].info1.type));
|
||||
|
||||
NDR_OK(rap_push_string(call->ndr_push_data,
|
||||
call->heap,
|
||||
r.out.info[i].info1.comment));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (call->ndr_push_data->offset > call->heap->offset) {
|
||||
|
||||
buffer_overflow:
|
||||
|
||||
ndr_push_restore(call->ndr_push_data, &data_save);
|
||||
rap_heap_restore(call->heap, &heap_save);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
call->status = r.out.status;
|
||||
|
||||
NDR_CHECK(ndr_push_uint16(call->ndr_push_param, NDR_SCALARS, r.out.count));
|
||||
NDR_CHECK(ndr_push_uint16(call->ndr_push_param, NDR_SCALARS, r.out.available));
|
||||
|
||||
result = NT_STATUS_OK;
|
||||
|
||||
done:
|
||||
return result;
|
||||
}
|
||||
|
||||
static NTSTATUS _rap_netserverenum2(struct rap_call *call)
|
||||
{
|
||||
struct rap_NetServerEnum2 r;
|
||||
NTSTATUS result;
|
||||
|
||||
NDR_OK(rap_srv_pull_word(call, &r.in.level));
|
||||
NDR_OK(rap_srv_pull_bufsize(call, &r.in.bufsize));
|
||||
NDR_OK(rap_srv_pull_expect_multiple(call));
|
||||
NDR_OK(rap_srv_pull_dword(call, &r.in.servertype));
|
||||
NDR_OK(rap_srv_pull_string(call, &r.in.domain));
|
||||
|
||||
switch(r.in.level) {
|
||||
case 0:
|
||||
if (strcmp(call->datadesc, "B16") != 0)
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
break;
|
||||
case 1:
|
||||
if (strcmp(call->datadesc, "B16BBDz") != 0)
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
break;
|
||||
default:
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
break;
|
||||
}
|
||||
|
||||
result = rap_netserverenum2(call, &r);
|
||||
|
||||
if (!NT_STATUS_IS_OK(result))
|
||||
return result;
|
||||
|
||||
for (r.out.count = 0; r.out.count < r.out.available; r.out.count++) {
|
||||
|
||||
int i = r.out.count;
|
||||
struct ndr_push_save data_save;
|
||||
struct rap_heap_save heap_save;
|
||||
|
||||
ndr_push_save(call->ndr_push_data, &data_save);
|
||||
rap_heap_save(call->heap, &heap_save);
|
||||
|
||||
switch(r.in.level) {
|
||||
case 0:
|
||||
NDR_OK(ndr_push_bytes(call->ndr_push_data,
|
||||
(const uint8_t *)r.out.info[i].info0.name,
|
||||
sizeof(r.out.info[i].info0.name)));
|
||||
break;
|
||||
case 1:
|
||||
NDR_OK(ndr_push_bytes(call->ndr_push_data,
|
||||
(const uint8_t *)r.out.info[i].info1.name,
|
||||
sizeof(r.out.info[i].info1.name)));
|
||||
NDR_OK(ndr_push_uint8(call->ndr_push_data,
|
||||
NDR_SCALARS, r.out.info[i].info1.version_major));
|
||||
NDR_OK(ndr_push_uint8(call->ndr_push_data,
|
||||
NDR_SCALARS, r.out.info[i].info1.version_minor));
|
||||
NDR_OK(ndr_push_uint32(call->ndr_push_data,
|
||||
NDR_SCALARS, r.out.info[i].info1.servertype));
|
||||
|
||||
NDR_OK(rap_push_string(call->ndr_push_data,
|
||||
call->heap,
|
||||
r.out.info[i].info1.comment));
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (call->ndr_push_data->offset > call->heap->offset) {
|
||||
|
||||
buffer_overflow:
|
||||
|
||||
ndr_push_restore(call->ndr_push_data, &data_save);
|
||||
rap_heap_restore(call->heap, &heap_save);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
call->status = r.out.status;
|
||||
|
||||
NDR_CHECK(ndr_push_uint16(call->ndr_push_param, NDR_SCALARS, r.out.count));
|
||||
NDR_CHECK(ndr_push_uint16(call->ndr_push_param, NDR_SCALARS, r.out.available));
|
||||
|
||||
result = NT_STATUS_OK;
|
||||
|
||||
done:
|
||||
return result;
|
||||
}
|
||||
|
||||
static NTSTATUS api_Unsupported(struct rap_call *call)
|
||||
{
|
||||
call->status = NERR_notsupported;
|
||||
call->convert = 0;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static const struct
|
||||
{
|
||||
const char *name;
|
||||
int id;
|
||||
NTSTATUS (*fn)(struct rap_call *call);
|
||||
} api_commands[] = {
|
||||
{"NetShareEnum", RAP_WshareEnum, _rap_netshareenum },
|
||||
{"NetServerEnum2", RAP_NetServerEnum2, _rap_netserverenum2 },
|
||||
{NULL, -1, api_Unsupported}
|
||||
};
|
||||
|
||||
NTSTATUS ipc_rap_call(TALLOC_CTX *mem_ctx, struct smb_trans2 *trans)
|
||||
{
|
||||
int i;
|
||||
NTSTATUS result;
|
||||
struct rap_call *call;
|
||||
DATA_BLOB result_param, result_data;
|
||||
struct ndr_push *final_param;
|
||||
struct ndr_push *final_data;
|
||||
|
||||
call = new_rap_srv_call(mem_ctx, trans);
|
||||
|
||||
if (call == NULL)
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
|
||||
NDR_CHECK(ndr_pull_uint16(call->ndr_pull_param, NDR_SCALARS, &call->callno));
|
||||
NDR_CHECK(ndr_pull_string(call->ndr_pull_param, NDR_SCALARS,
|
||||
&call->paramdesc));
|
||||
NDR_CHECK(ndr_pull_string(call->ndr_pull_param, NDR_SCALARS,
|
||||
&call->datadesc));
|
||||
|
||||
call->ndr_push_param = ndr_push_init_ctx(call);
|
||||
call->ndr_push_data = ndr_push_init_ctx(call);
|
||||
|
||||
if ((call->ndr_push_param == NULL) || (call->ndr_push_data == NULL))
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
|
||||
call->ndr_push_param->flags = RAPNDR_FLAGS;
|
||||
call->ndr_push_data->flags = RAPNDR_FLAGS;
|
||||
|
||||
result = NT_STATUS_INVALID_SYSTEM_SERVICE;
|
||||
|
||||
for (i=0; api_commands[i].name != NULL; i++) {
|
||||
if (api_commands[i].id == call->callno) {
|
||||
DEBUG(5, ("Running RAP call %s\n",
|
||||
api_commands[i].name));
|
||||
result = api_commands[i].fn(call);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!NT_STATUS_IS_OK(result))
|
||||
return result;
|
||||
|
||||
result_param = ndr_push_blob(call->ndr_push_param);
|
||||
result_data = ndr_push_blob(call->ndr_push_data);
|
||||
|
||||
final_param = ndr_push_init_ctx(call);
|
||||
final_data = ndr_push_init_ctx(call);
|
||||
|
||||
if ((final_param == NULL) || (final_data == NULL))
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
|
||||
final_param->flags = RAPNDR_FLAGS;
|
||||
final_data->flags = RAPNDR_FLAGS;
|
||||
|
||||
NDR_CHECK(ndr_push_uint16(final_param, NDR_SCALARS, call->status));
|
||||
NDR_CHECK(ndr_push_uint16(final_param,
|
||||
NDR_SCALARS, call->heap->offset - result_data.length));
|
||||
NDR_CHECK(ndr_push_bytes(final_param, result_param.data,
|
||||
result_param.length));
|
||||
|
||||
NDR_CHECK(ndr_push_bytes(final_data, result_data.data,
|
||||
result_data.length));
|
||||
|
||||
for (i=call->heap->num_strings-1; i>=0; i--)
|
||||
NDR_CHECK(ndr_push_string(final_data, NDR_SCALARS,
|
||||
call->heap->strings[i]));
|
||||
|
||||
trans->out.setup_count = 0;
|
||||
trans->out.setup = NULL;
|
||||
trans->out.params = ndr_push_blob(final_param);
|
||||
trans->out.data = ndr_push_blob(final_data);
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
RAP handlers
|
||||
|
||||
Copyright (C) Volker Lendecke 2004
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 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.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "param/share.h"
|
||||
#include "libcli/rap/rap.h"
|
||||
#include "librpc/gen_ndr/srvsvc.h"
|
||||
#include "rpc_server/common/common.h"
|
||||
|
||||
/* At this moment these are just dummy functions, but you might get the
|
||||
* idea. */
|
||||
|
||||
NTSTATUS rap_netshareenum(TALLOC_CTX *mem_ctx,
|
||||
struct rap_NetShareEnum *r)
|
||||
{
|
||||
NTSTATUS nterr;
|
||||
const char **snames;
|
||||
struct share_context *sctx;
|
||||
struct share_config *scfg;
|
||||
int i, j, count;
|
||||
|
||||
r->out.status = 0;
|
||||
r->out.available = 0;
|
||||
r->out.info = NULL;
|
||||
|
||||
nterr = share_get_context(mem_ctx, &sctx);
|
||||
if (!NT_STATUS_IS_OK(nterr)) {
|
||||
return nterr;
|
||||
}
|
||||
|
||||
nterr = share_list_all(mem_ctx, sctx, &count, &snames);
|
||||
if (!NT_STATUS_IS_OK(nterr)) {
|
||||
return nterr;
|
||||
}
|
||||
|
||||
r->out.available = count;
|
||||
r->out.info = talloc_array(mem_ctx,
|
||||
union rap_shareenum_info, r->out.available);
|
||||
|
||||
for (i = 0, j = 0; i < r->out.available; i++) {
|
||||
if (!NT_STATUS_IS_OK(share_get_config(mem_ctx, sctx, snames[i], &scfg))) {
|
||||
DEBUG(3, ("WARNING: Service [%s] disappeared after enumeration!\n", snames[i]));
|
||||
continue;
|
||||
}
|
||||
strncpy(r->out.info[j].info1.name,
|
||||
snames[i],
|
||||
sizeof(r->out.info[0].info1.name));
|
||||
r->out.info[i].info1.pad = 0;
|
||||
r->out.info[i].info1.type = dcesrv_common_get_share_type(mem_ctx, NULL, scfg);
|
||||
r->out.info[i].info1.comment = talloc_strdup(mem_ctx, share_string_option(scfg, SHARE_COMMENT, ""));
|
||||
talloc_free(scfg);
|
||||
j++;
|
||||
}
|
||||
r->out.available = j;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
NTSTATUS rap_netserverenum2(TALLOC_CTX *mem_ctx,
|
||||
struct rap_NetServerEnum2 *r)
|
||||
{
|
||||
r->out.status = 0;
|
||||
r->out.available = 0;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
@@ -0,0 +1,913 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
default IPC$ NTVFS backend
|
||||
|
||||
Copyright (C) Andrew Tridgell 2003
|
||||
Copyright (C) Stefan (metze) Metzmacher 2004-2005
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 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"
|
||||
#include "lib/util/dlinklist.h"
|
||||
#include "ntvfs/ntvfs.h"
|
||||
#include "libcli/rap/rap.h"
|
||||
#include "ntvfs/ipc/proto.h"
|
||||
#include "rpc_server/dcerpc_server.h"
|
||||
#include "libcli/raw/ioctl.h"
|
||||
|
||||
/* this is the private structure used to keep the state of an open
|
||||
ipc$ connection. It needs to keep information about all open
|
||||
pipes */
|
||||
struct ipc_private {
|
||||
struct ntvfs_module_context *ntvfs;
|
||||
|
||||
struct dcesrv_context *dcesrv;
|
||||
|
||||
/* a list of open pipes */
|
||||
struct pipe_state {
|
||||
struct pipe_state *next, *prev;
|
||||
struct ipc_private *private;
|
||||
const char *pipe_name;
|
||||
struct ntvfs_handle *handle;
|
||||
struct dcesrv_connection *dce_conn;
|
||||
uint16_t ipc_state;
|
||||
} *pipe_list;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
find a open pipe give a file handle
|
||||
*/
|
||||
static struct pipe_state *pipe_state_find(struct ipc_private *private, struct ntvfs_handle *handle)
|
||||
{
|
||||
struct pipe_state *s;
|
||||
void *p;
|
||||
|
||||
p = ntvfs_handle_get_backend_data(handle, private->ntvfs);
|
||||
if (!p) return NULL;
|
||||
|
||||
s = talloc_get_type(p, struct pipe_state);
|
||||
if (!s) return NULL;
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
/*
|
||||
find a open pipe give a wire fnum
|
||||
*/
|
||||
static struct pipe_state *pipe_state_find_key(struct ipc_private *private, struct ntvfs_request *req, const DATA_BLOB *key)
|
||||
{
|
||||
struct ntvfs_handle *h;
|
||||
|
||||
h = ntvfs_handle_search_by_wire_key(private->ntvfs, req, key);
|
||||
if (!h) return NULL;
|
||||
|
||||
return pipe_state_find(private, h);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
connect to a share - always works
|
||||
*/
|
||||
static NTSTATUS ipc_connect(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, const char *sharename)
|
||||
{
|
||||
NTSTATUS status;
|
||||
struct ipc_private *private;
|
||||
|
||||
ntvfs->ctx->fs_type = talloc_strdup(ntvfs->ctx, "IPC");
|
||||
NT_STATUS_HAVE_NO_MEMORY(ntvfs->ctx->fs_type);
|
||||
|
||||
ntvfs->ctx->dev_type = talloc_strdup(ntvfs->ctx, "IPC");
|
||||
NT_STATUS_HAVE_NO_MEMORY(ntvfs->ctx->dev_type);
|
||||
|
||||
/* prepare the private state for this connection */
|
||||
private = talloc(ntvfs, struct ipc_private);
|
||||
NT_STATUS_HAVE_NO_MEMORY(private);
|
||||
|
||||
ntvfs->private_data = private;
|
||||
|
||||
private->ntvfs = ntvfs;
|
||||
private->pipe_list = NULL;
|
||||
|
||||
/* setup the DCERPC server subsystem */
|
||||
status = dcesrv_init_ipc_context(private, &private->dcesrv);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
disconnect from a share
|
||||
*/
|
||||
static NTSTATUS ipc_disconnect(struct ntvfs_module_context *ntvfs)
|
||||
{
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
delete a file
|
||||
*/
|
||||
static NTSTATUS ipc_unlink(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
union smb_unlink *unl)
|
||||
{
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
/*
|
||||
check if a directory exists
|
||||
*/
|
||||
static NTSTATUS ipc_chkpath(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
union smb_chkpath *cp)
|
||||
{
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
/*
|
||||
return info on a pathname
|
||||
*/
|
||||
static NTSTATUS ipc_qpathinfo(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_fileinfo *info)
|
||||
{
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
/*
|
||||
set info on a pathname
|
||||
*/
|
||||
static NTSTATUS ipc_setpathinfo(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_setfileinfo *st)
|
||||
{
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
destroy a open pipe structure
|
||||
*/
|
||||
static int ipc_fd_destructor(struct pipe_state *p)
|
||||
{
|
||||
DLIST_REMOVE(p->private->pipe_list, p);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct socket_address *ipc_get_my_addr(struct dcesrv_connection *dce_conn, TALLOC_CTX *mem_ctx)
|
||||
{
|
||||
struct ipc_private *private = dce_conn->transport.private_data;
|
||||
|
||||
return ntvfs_get_my_addr(private->ntvfs, mem_ctx);
|
||||
}
|
||||
|
||||
static struct socket_address *ipc_get_peer_addr(struct dcesrv_connection *dce_conn, TALLOC_CTX *mem_ctx)
|
||||
{
|
||||
struct ipc_private *private = dce_conn->transport.private_data;
|
||||
|
||||
return ntvfs_get_peer_addr(private->ntvfs, mem_ctx);
|
||||
}
|
||||
|
||||
/*
|
||||
open a file backend - used for MSRPC pipes
|
||||
*/
|
||||
static NTSTATUS ipc_open_generic(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, const char *fname,
|
||||
struct pipe_state **ps)
|
||||
{
|
||||
struct pipe_state *p;
|
||||
NTSTATUS status;
|
||||
struct dcerpc_binding *ep_description;
|
||||
struct ipc_private *private = ntvfs->private_data;
|
||||
struct ntvfs_handle *h;
|
||||
|
||||
status = ntvfs_handle_new(ntvfs, req, &h);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
p = talloc(h, struct pipe_state);
|
||||
NT_STATUS_HAVE_NO_MEMORY(p);
|
||||
|
||||
ep_description = talloc(req, struct dcerpc_binding);
|
||||
NT_STATUS_HAVE_NO_MEMORY(ep_description);
|
||||
|
||||
while (fname[0] == '\\') fname++;
|
||||
|
||||
p->pipe_name = talloc_asprintf(p, "\\pipe\\%s", fname);
|
||||
NT_STATUS_HAVE_NO_MEMORY(p->pipe_name);
|
||||
|
||||
p->handle = h;
|
||||
p->ipc_state = 0x5ff;
|
||||
|
||||
/*
|
||||
we're all set, now ask the dcerpc server subsystem to open the
|
||||
endpoint. At this stage the pipe isn't bound, so we don't
|
||||
know what interface the user actually wants, just that they want
|
||||
one of the interfaces attached to this pipe endpoint.
|
||||
*/
|
||||
ep_description->transport = NCACN_NP;
|
||||
ep_description->endpoint = talloc_reference(ep_description, p->pipe_name);
|
||||
|
||||
/* The session info is refcount-increased in the
|
||||
* dcesrv_endpoint_search_connect() function
|
||||
*/
|
||||
status = dcesrv_endpoint_search_connect(private->dcesrv,
|
||||
p,
|
||||
ep_description,
|
||||
h->session_info,
|
||||
ntvfs->ctx->event_ctx,
|
||||
ntvfs->ctx->msg_ctx,
|
||||
ntvfs->ctx->server_id,
|
||||
0,
|
||||
&p->dce_conn);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
p->dce_conn->transport.private_data = private;
|
||||
p->dce_conn->transport.report_output_data = NULL;
|
||||
p->dce_conn->transport.get_my_addr = ipc_get_my_addr;
|
||||
p->dce_conn->transport.get_peer_addr = ipc_get_peer_addr;
|
||||
|
||||
DLIST_ADD(private->pipe_list, p);
|
||||
|
||||
p->private = private;
|
||||
|
||||
talloc_set_destructor(p, ipc_fd_destructor);
|
||||
|
||||
status = ntvfs_handle_set_backend_data(h, private->ntvfs, p);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
*ps = p;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
open a file with ntcreatex - used for MSRPC pipes
|
||||
*/
|
||||
static NTSTATUS ipc_open_ntcreatex(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_open *oi)
|
||||
{
|
||||
struct pipe_state *p;
|
||||
NTSTATUS status;
|
||||
|
||||
status = ipc_open_generic(ntvfs, req, oi->ntcreatex.in.fname, &p);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
ZERO_STRUCT(oi->ntcreatex.out);
|
||||
oi->ntcreatex.out.file.ntvfs= p->handle;
|
||||
oi->ntcreatex.out.ipc_state = p->ipc_state;
|
||||
oi->ntcreatex.out.file_type = FILE_TYPE_MESSAGE_MODE_PIPE;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
open a file with openx - used for MSRPC pipes
|
||||
*/
|
||||
static NTSTATUS ipc_open_openx(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_open *oi)
|
||||
{
|
||||
struct pipe_state *p;
|
||||
NTSTATUS status;
|
||||
const char *fname = oi->openx.in.fname;
|
||||
|
||||
status = ipc_open_generic(ntvfs, req, fname, &p);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
ZERO_STRUCT(oi->openx.out);
|
||||
oi->openx.out.file.ntvfs= p->handle;
|
||||
oi->openx.out.ftype = 2;
|
||||
oi->openx.out.devstate = p->ipc_state;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
open a file with SMB2 Create - used for MSRPC pipes
|
||||
*/
|
||||
static NTSTATUS ipc_open_smb2(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_open *oi)
|
||||
{
|
||||
struct pipe_state *p;
|
||||
NTSTATUS status;
|
||||
|
||||
status = ipc_open_generic(ntvfs, req, oi->smb2.in.fname, &p);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
oi->smb2.out.file.ntvfs = p->handle;
|
||||
oi->smb2.out.oplock_flags = oi->smb2.in.oplock_flags;
|
||||
oi->smb2.out.create_action = NTCREATEX_ACTION_EXISTED;
|
||||
oi->smb2.out.create_time = 0;
|
||||
oi->smb2.out.access_time = 0;
|
||||
oi->smb2.out.write_time = 0;
|
||||
oi->smb2.out.change_time = 0;
|
||||
oi->smb2.out.alloc_size = 4096;
|
||||
oi->smb2.out.size = 0;
|
||||
oi->smb2.out.file_attr = FILE_ATTRIBUTE_NORMAL;
|
||||
oi->smb2.out._pad = 0;
|
||||
oi->smb2.out.blob = data_blob(NULL, 0);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
open a file - used for MSRPC pipes
|
||||
*/
|
||||
static NTSTATUS ipc_open(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_open *oi)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
switch (oi->generic.level) {
|
||||
case RAW_OPEN_NTCREATEX:
|
||||
status = ipc_open_ntcreatex(ntvfs, req, oi);
|
||||
break;
|
||||
case RAW_OPEN_OPENX:
|
||||
status = ipc_open_openx(ntvfs, req, oi);
|
||||
break;
|
||||
case RAW_OPEN_SMB2:
|
||||
status = ipc_open_smb2(ntvfs, req, oi);
|
||||
break;
|
||||
default:
|
||||
status = NT_STATUS_NOT_SUPPORTED;
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
create a directory
|
||||
*/
|
||||
static NTSTATUS ipc_mkdir(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_mkdir *md)
|
||||
{
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
/*
|
||||
remove a directory
|
||||
*/
|
||||
static NTSTATUS ipc_rmdir(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, struct smb_rmdir *rd)
|
||||
{
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
/*
|
||||
rename a set of files
|
||||
*/
|
||||
static NTSTATUS ipc_rename(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_rename *ren)
|
||||
{
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
/*
|
||||
copy a set of files
|
||||
*/
|
||||
static NTSTATUS ipc_copy(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, struct smb_copy *cp)
|
||||
{
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
static NTSTATUS ipc_readx_dcesrv_output(void *private_data, DATA_BLOB *out, size_t *nwritten)
|
||||
{
|
||||
DATA_BLOB *blob = private_data;
|
||||
|
||||
if (out->length < blob->length) {
|
||||
blob->length = out->length;
|
||||
}
|
||||
memcpy(blob->data, out->data, blob->length);
|
||||
*nwritten = blob->length;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
read from a file
|
||||
*/
|
||||
static NTSTATUS ipc_read(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_read *rd)
|
||||
{
|
||||
struct ipc_private *private = ntvfs->private_data;
|
||||
DATA_BLOB data;
|
||||
struct pipe_state *p;
|
||||
NTSTATUS status = NT_STATUS_OK;
|
||||
|
||||
if (rd->generic.level != RAW_READ_GENERIC) {
|
||||
return ntvfs_map_read(ntvfs, req, rd);
|
||||
}
|
||||
|
||||
p = pipe_state_find(private, rd->readx.in.file.ntvfs);
|
||||
if (!p) {
|
||||
return NT_STATUS_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
data.length = rd->readx.in.maxcnt;
|
||||
data.data = rd->readx.out.data;
|
||||
if (data.length > UINT16_MAX) {
|
||||
data.length = UINT16_MAX;
|
||||
}
|
||||
|
||||
if (data.length != 0) {
|
||||
status = dcesrv_output(p->dce_conn, &data, ipc_readx_dcesrv_output);
|
||||
if (NT_STATUS_IS_ERR(status)) {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
rd->readx.out.remaining = 0;
|
||||
rd->readx.out.compaction_mode = 0;
|
||||
rd->readx.out.nread = data.length;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
write to a file
|
||||
*/
|
||||
static NTSTATUS ipc_write(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_write *wr)
|
||||
{
|
||||
struct ipc_private *private = ntvfs->private_data;
|
||||
DATA_BLOB data;
|
||||
struct pipe_state *p;
|
||||
NTSTATUS status;
|
||||
|
||||
if (wr->generic.level != RAW_WRITE_GENERIC) {
|
||||
return ntvfs_map_write(ntvfs, req, wr);
|
||||
}
|
||||
|
||||
data.data = discard_const_p(void, wr->writex.in.data);
|
||||
data.length = wr->writex.in.count;
|
||||
|
||||
p = pipe_state_find(private, wr->writex.in.file.ntvfs);
|
||||
if (!p) {
|
||||
return NT_STATUS_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
status = dcesrv_input(p->dce_conn, &data);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
wr->writex.out.nwritten = data.length;
|
||||
wr->writex.out.remaining = 0;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
seek in a file
|
||||
*/
|
||||
static NTSTATUS ipc_seek(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
union smb_seek *io)
|
||||
{
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
/*
|
||||
flush a file
|
||||
*/
|
||||
static NTSTATUS ipc_flush(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
union smb_flush *io)
|
||||
{
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
/*
|
||||
close a file
|
||||
*/
|
||||
static NTSTATUS ipc_close(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_close *io)
|
||||
{
|
||||
struct ipc_private *private = ntvfs->private_data;
|
||||
struct pipe_state *p;
|
||||
|
||||
if (io->generic.level != RAW_CLOSE_CLOSE) {
|
||||
return ntvfs_map_close(ntvfs, req, io);
|
||||
}
|
||||
|
||||
p = pipe_state_find(private, io->close.in.file.ntvfs);
|
||||
if (!p) {
|
||||
return NT_STATUS_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
talloc_free(p);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
exit - closing files
|
||||
*/
|
||||
static NTSTATUS ipc_exit(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req)
|
||||
{
|
||||
struct ipc_private *private = ntvfs->private_data;
|
||||
struct pipe_state *p, *next;
|
||||
|
||||
for (p=private->pipe_list; p; p=next) {
|
||||
next = p->next;
|
||||
if (p->handle->session_info == req->session_info &&
|
||||
p->handle->smbpid == req->smbpid) {
|
||||
talloc_free(p);
|
||||
}
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
logoff - closing files open by the user
|
||||
*/
|
||||
static NTSTATUS ipc_logoff(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req)
|
||||
{
|
||||
struct ipc_private *private = ntvfs->private_data;
|
||||
struct pipe_state *p, *next;
|
||||
|
||||
for (p=private->pipe_list; p; p=next) {
|
||||
next = p->next;
|
||||
if (p->handle->session_info == req->session_info) {
|
||||
talloc_free(p);
|
||||
}
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
setup for an async call
|
||||
*/
|
||||
static NTSTATUS ipc_async_setup(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
void *private)
|
||||
{
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
cancel an async call
|
||||
*/
|
||||
static NTSTATUS ipc_cancel(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req)
|
||||
{
|
||||
return NT_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
/*
|
||||
lock a byte range
|
||||
*/
|
||||
static NTSTATUS ipc_lock(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_lock *lck)
|
||||
{
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
/*
|
||||
set info on a open file
|
||||
*/
|
||||
static NTSTATUS ipc_setfileinfo(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_setfileinfo *info)
|
||||
{
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
/*
|
||||
query info on a open file
|
||||
*/
|
||||
static NTSTATUS ipc_qfileinfo(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_fileinfo *info)
|
||||
{
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
return filesystem info
|
||||
*/
|
||||
static NTSTATUS ipc_fsinfo(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_fsinfo *fs)
|
||||
{
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
/*
|
||||
return print queue info
|
||||
*/
|
||||
static NTSTATUS ipc_lpq(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_lpq *lpq)
|
||||
{
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
/*
|
||||
list files in a directory matching a wildcard pattern
|
||||
*/
|
||||
static NTSTATUS ipc_search_first(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_search_first *io,
|
||||
void *search_private,
|
||||
BOOL (*callback)(void *, union smb_search_data *))
|
||||
{
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
/*
|
||||
continue listing files in a directory
|
||||
*/
|
||||
static NTSTATUS ipc_search_next(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_search_next *io,
|
||||
void *search_private,
|
||||
BOOL (*callback)(void *, union smb_search_data *))
|
||||
{
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
/*
|
||||
end listing files in a directory
|
||||
*/
|
||||
static NTSTATUS ipc_search_close(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_search_close *io)
|
||||
{
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
static NTSTATUS ipc_trans_dcesrv_output(void *private_data, DATA_BLOB *out, size_t *nwritten)
|
||||
{
|
||||
NTSTATUS status = NT_STATUS_OK;
|
||||
DATA_BLOB *blob = private_data;
|
||||
|
||||
if (out->length > blob->length) {
|
||||
status = STATUS_BUFFER_OVERFLOW;
|
||||
}
|
||||
|
||||
if (out->length < blob->length) {
|
||||
blob->length = out->length;
|
||||
}
|
||||
memcpy(blob->data, out->data, blob->length);
|
||||
*nwritten = blob->length;
|
||||
return status;
|
||||
}
|
||||
|
||||
/* SMBtrans - handle a DCERPC command */
|
||||
static NTSTATUS ipc_dcerpc_cmd(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, struct smb_trans2 *trans)
|
||||
{
|
||||
struct pipe_state *p;
|
||||
struct ipc_private *private = ntvfs->private_data;
|
||||
NTSTATUS status;
|
||||
DATA_BLOB fnum_key;
|
||||
uint16_t fnum;
|
||||
|
||||
/*
|
||||
* the fnum is in setup[1], a 16 bit value
|
||||
* the setup[*] values are already in host byteorder
|
||||
* but ntvfs_handle_search_by_wire_key() expects
|
||||
* network byteorder
|
||||
*/
|
||||
SSVAL(&fnum, 0, trans->in.setup[1]);
|
||||
fnum_key = data_blob_const(&fnum, 2);
|
||||
|
||||
p = pipe_state_find_key(private, req, &fnum_key);
|
||||
if (!p) {
|
||||
return NT_STATUS_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
trans->out.data = data_blob_talloc(req, NULL, trans->in.max_data);
|
||||
if (!trans->out.data.data) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
/* pass the data to the dcerpc server. Note that we don't
|
||||
expect this to fail, and things like NDR faults are not
|
||||
reported at this stage. Those sorts of errors happen in the
|
||||
dcesrv_output stage */
|
||||
status = dcesrv_input(p->dce_conn, &trans->in.data);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
now ask the dcerpc system for some output. This doesn't yet handle
|
||||
async calls. Again, we only expect NT_STATUS_OK. If the call fails then
|
||||
the error is encoded at the dcerpc level
|
||||
*/
|
||||
status = dcesrv_output(p->dce_conn, &trans->out.data, ipc_trans_dcesrv_output);
|
||||
if (NT_STATUS_IS_ERR(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
trans->out.setup_count = 0;
|
||||
trans->out.setup = NULL;
|
||||
trans->out.params = data_blob(NULL, 0);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/* SMBtrans - set named pipe state */
|
||||
static NTSTATUS ipc_set_nm_pipe_state(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, struct smb_trans2 *trans)
|
||||
{
|
||||
struct ipc_private *private = ntvfs->private_data;
|
||||
struct pipe_state *p;
|
||||
DATA_BLOB fnum_key;
|
||||
|
||||
/* the fnum is in setup[1] */
|
||||
fnum_key = data_blob_const(&trans->in.setup[1], sizeof(trans->in.setup[1]));
|
||||
|
||||
p = pipe_state_find_key(private, req, &fnum_key);
|
||||
if (!p) {
|
||||
return NT_STATUS_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
if (trans->in.params.length != 2) {
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
p->ipc_state = SVAL(trans->in.params.data, 0);
|
||||
|
||||
trans->out.setup_count = 0;
|
||||
trans->out.setup = NULL;
|
||||
trans->out.params = data_blob(NULL, 0);
|
||||
trans->out.data = data_blob(NULL, 0);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
/* SMBtrans - used to provide access to SMB pipes */
|
||||
static NTSTATUS ipc_trans(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, struct smb_trans2 *trans)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
if (strequal(trans->in.trans_name, "\\PIPE\\LANMAN"))
|
||||
return ipc_rap_call(req, trans);
|
||||
|
||||
if (trans->in.setup_count != 2) {
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
switch (trans->in.setup[0]) {
|
||||
case TRANSACT_SETNAMEDPIPEHANDLESTATE:
|
||||
status = ipc_set_nm_pipe_state(ntvfs, req, trans);
|
||||
break;
|
||||
case TRANSACT_DCERPCCMD:
|
||||
status = ipc_dcerpc_cmd(ntvfs, req, trans);
|
||||
break;
|
||||
default:
|
||||
status = NT_STATUS_INVALID_PARAMETER;
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static NTSTATUS ipc_ioctl_smb2(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_ioctl *io)
|
||||
{
|
||||
struct pipe_state *p;
|
||||
struct ipc_private *private = ntvfs->private_data;
|
||||
NTSTATUS status;
|
||||
|
||||
switch (io->smb2.in.function) {
|
||||
case FSCTL_NAMED_PIPE_READ_WRITE:
|
||||
break;
|
||||
|
||||
default:
|
||||
return NT_STATUS_FS_DRIVER_REQUIRED;
|
||||
}
|
||||
|
||||
p = pipe_state_find(private, io->smb2.in.file.ntvfs);
|
||||
if (!p) {
|
||||
return NT_STATUS_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
io->smb2.out.out = data_blob_talloc(req, NULL, io->smb2.in.max_response_size);
|
||||
NT_STATUS_HAVE_NO_MEMORY(io->smb2.out.out.data);
|
||||
|
||||
/* pass the data to the dcerpc server. Note that we don't
|
||||
expect this to fail, and things like NDR faults are not
|
||||
reported at this stage. Those sorts of errors happen in the
|
||||
dcesrv_output stage */
|
||||
status = dcesrv_input(p->dce_conn, &io->smb2.in.out);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
/*
|
||||
now ask the dcerpc system for some output. This doesn't yet handle
|
||||
async calls. Again, we only expect NT_STATUS_OK. If the call fails then
|
||||
the error is encoded at the dcerpc level
|
||||
*/
|
||||
status = dcesrv_output(p->dce_conn, &io->smb2.out.out, ipc_trans_dcesrv_output);
|
||||
NT_STATUS_IS_ERR_RETURN(status);
|
||||
|
||||
io->smb2.out._pad = 0;
|
||||
io->smb2.out.function = io->smb2.in.function;
|
||||
io->smb2.out.unknown2 = 0;
|
||||
io->smb2.out.unknown3 = 0;
|
||||
io->smb2.out.in = io->smb2.in.out;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
ioctl interface
|
||||
*/
|
||||
static NTSTATUS ipc_ioctl(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_ioctl *io)
|
||||
{
|
||||
switch (io->generic.level) {
|
||||
case RAW_IOCTL_SMB2:
|
||||
return ipc_ioctl_smb2(ntvfs, req, io);
|
||||
|
||||
case RAW_IOCTL_SMB2_NO_HANDLE:
|
||||
return NT_STATUS_FS_DRIVER_REQUIRED;
|
||||
|
||||
default:
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
initialialise the IPC backend, registering ourselves with the ntvfs subsystem
|
||||
*/
|
||||
NTSTATUS ntvfs_ipc_init(void)
|
||||
{
|
||||
NTSTATUS ret;
|
||||
struct ntvfs_ops ops;
|
||||
NTVFS_CURRENT_CRITICAL_SIZES(vers);
|
||||
|
||||
ZERO_STRUCT(ops);
|
||||
|
||||
/* fill in the name and type */
|
||||
ops.name = "default";
|
||||
ops.type = NTVFS_IPC;
|
||||
|
||||
/* fill in all the operations */
|
||||
ops.connect = 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;
|
||||
ops.trans = ipc_trans;
|
||||
ops.logoff = ipc_logoff;
|
||||
ops.async_setup = ipc_async_setup;
|
||||
ops.cancel = ipc_cancel;
|
||||
|
||||
/* register ourselves with the NTVFS subsystem. */
|
||||
ret = ntvfs_register(&ops, &vers);
|
||||
|
||||
if (!NT_STATUS_IS_OK(ret)) {
|
||||
DEBUG(0,("Failed to register IPC backend!\n"));
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
This module provides a way to capture file sharing loads for the
|
||||
NBENCH benchmark client. It also servers as an example of a NTVFS
|
||||
filter module.
|
||||
|
||||
Here is an example config that passes through to the CIFS NTVFS backend.
|
||||
|
||||
[bench]
|
||||
ntvfs handler = nbench cifs
|
||||
cifs:server = myserver
|
||||
cifs:user = myuser
|
||||
cifs:password = mypass
|
||||
cifs:domain = MYDOMAIN
|
||||
cifs:share = bench
|
||||
@@ -0,0 +1,973 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
a pass-thru NTVFS module to record a NBENCH load file
|
||||
|
||||
Copyright (C) Andrew Tridgell 2004
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
/*
|
||||
"passthru" in this module refers to the next level of NTVFS being used
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "ntvfs/ntvfs.h"
|
||||
#include "system/filesys.h"
|
||||
|
||||
/* this is stored in ntvfs_private */
|
||||
struct nbench_private {
|
||||
int log_fd;
|
||||
};
|
||||
|
||||
/*
|
||||
log one request to the nbench log
|
||||
*/
|
||||
static void nbench_log(struct ntvfs_request *req,
|
||||
const char *format, ...) PRINTF_ATTRIBUTE(2, 3);
|
||||
|
||||
static void nbench_log(struct ntvfs_request *req,
|
||||
const char *format, ...)
|
||||
{
|
||||
struct nbench_private *private = req->async_states->ntvfs->private_data;
|
||||
va_list ap;
|
||||
char *s = NULL;
|
||||
|
||||
va_start(ap, format);
|
||||
vasprintf(&s, format, ap);
|
||||
va_end(ap);
|
||||
|
||||
write(private->log_fd, s, strlen(s));
|
||||
free(s);
|
||||
}
|
||||
|
||||
static char *nbench_ntvfs_handle_string(struct ntvfs_request *req, struct ntvfs_handle *h)
|
||||
{
|
||||
DATA_BLOB key;
|
||||
uint16_t fnum = 0;
|
||||
|
||||
key = ntvfs_handle_get_wire_key(h, req);
|
||||
|
||||
switch (key.length) {
|
||||
case 2: /* SMB fnum */
|
||||
fnum = SVAL(key.data, 0);
|
||||
break;
|
||||
default:
|
||||
DEBUG(0,("%s: invalid wire handle size: %u\n",
|
||||
__FUNCTION__, (unsigned)key.length));
|
||||
break;
|
||||
}
|
||||
|
||||
return talloc_asprintf(req, "%u", fnum);
|
||||
}
|
||||
|
||||
/*
|
||||
this pass through macro operates on request contexts, and disables
|
||||
async calls.
|
||||
|
||||
async calls are a pain for the nbench module as it makes pulling the
|
||||
status code and any result parameters much harder.
|
||||
*/
|
||||
#define PASS_THRU_REQ_PRE_ASYNC(ntvfs, req, op, par1) do { \
|
||||
status = ntvfs_async_state_push(ntvfs, req, par1, nbench_##op##_send); \
|
||||
if (!NT_STATUS_IS_OK(status)) { \
|
||||
return status; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define PASS_THRU_REQ_POST_ASYNC(req) do { \
|
||||
req->async_states->status = status; \
|
||||
if (!(req->async_states->state & NTVFS_ASYNC_STATE_ASYNC)) { \
|
||||
req->async_states->send_fn(req); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define PASS_THRU_REQ(ntvfs, req, op, par1, args) do { \
|
||||
PASS_THRU_REQ_PRE_ASYNC(ntvfs, req, op, par1); \
|
||||
status = ntvfs_next_##op args; \
|
||||
PASS_THRU_REQ_POST_ASYNC(req); \
|
||||
} while (0)
|
||||
|
||||
#define PASS_THRU_REP_POST(req) do { \
|
||||
ntvfs_async_state_pop(req); \
|
||||
if (req->async_states->state & NTVFS_ASYNC_STATE_ASYNC) { \
|
||||
req->async_states->send_fn(req); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
connect to a share - used when a tree_connect operation comes in.
|
||||
*/
|
||||
static NTSTATUS nbench_connect(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, const char *sharename)
|
||||
{
|
||||
struct nbench_private *nprivates;
|
||||
NTSTATUS status;
|
||||
char *logname = NULL;
|
||||
|
||||
nprivates = talloc(ntvfs, struct nbench_private);
|
||||
if (!nprivates) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
asprintf(&logname, "/tmp/nbenchlog%d.%u", ntvfs->depth, getpid());
|
||||
nprivates->log_fd = open(logname, O_WRONLY|O_CREAT|O_APPEND, 0644);
|
||||
free(logname);
|
||||
|
||||
if (nprivates->log_fd == -1) {
|
||||
DEBUG(0,("Failed to open nbench log\n"));
|
||||
return NT_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
ntvfs->private_data = nprivates;
|
||||
|
||||
status = ntvfs_next_connect(ntvfs, req, sharename);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
disconnect from a share
|
||||
*/
|
||||
static NTSTATUS nbench_disconnect(struct ntvfs_module_context *ntvfs)
|
||||
{
|
||||
struct nbench_private *nprivates = ntvfs->private_data;
|
||||
NTSTATUS status;
|
||||
|
||||
close(nprivates->log_fd);
|
||||
|
||||
status = ntvfs_next_disconnect(ntvfs);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
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 void nbench_unlink_send(struct ntvfs_request *req)
|
||||
{
|
||||
union smb_unlink *unl = req->async_states->private_data;
|
||||
|
||||
nbench_log(req, "Unlink \"%s\" 0x%x %s\n",
|
||||
unl->unlink.in.pattern, unl->unlink.in.attrib,
|
||||
get_nt_error_c_code(req->async_states->status));
|
||||
|
||||
PASS_THRU_REP_POST(req);
|
||||
}
|
||||
|
||||
static NTSTATUS nbench_unlink(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
union smb_unlink *unl)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
PASS_THRU_REQ(ntvfs, req, unlink, unl, (ntvfs, req, unl));
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
ioctl interface
|
||||
*/
|
||||
static void nbench_ioctl_send(struct ntvfs_request *req)
|
||||
{
|
||||
nbench_log(req, "Ioctl - NOT HANDLED\n");
|
||||
|
||||
PASS_THRU_REP_POST(req);
|
||||
}
|
||||
|
||||
static NTSTATUS nbench_ioctl(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_ioctl *io)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
PASS_THRU_REQ(ntvfs, req, ioctl, io, (ntvfs, req, io));
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
check if a directory exists
|
||||
*/
|
||||
static void nbench_chkpath_send(struct ntvfs_request *req)
|
||||
{
|
||||
union smb_chkpath *cp = req->async_states->private_data;
|
||||
|
||||
nbench_log(req, "Chkpath \"%s\" %s\n",
|
||||
cp->chkpath.in.path,
|
||||
get_nt_error_c_code(req->async_states->status));
|
||||
|
||||
PASS_THRU_REP_POST(req);
|
||||
}
|
||||
|
||||
static NTSTATUS nbench_chkpath(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
union smb_chkpath *cp)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
PASS_THRU_REQ(ntvfs, req, chkpath, cp, (ntvfs, req, cp));
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
return info on a pathname
|
||||
*/
|
||||
static void nbench_qpathinfo_send(struct ntvfs_request *req)
|
||||
{
|
||||
union smb_fileinfo *info = req->async_states->private_data;
|
||||
|
||||
nbench_log(req, "QUERY_PATH_INFORMATION \"%s\" %d %s\n",
|
||||
info->generic.in.file.path,
|
||||
info->generic.level,
|
||||
get_nt_error_c_code(req->async_states->status));
|
||||
|
||||
PASS_THRU_REP_POST(req);
|
||||
}
|
||||
|
||||
static NTSTATUS nbench_qpathinfo(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_fileinfo *info)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
PASS_THRU_REQ(ntvfs, req, qpathinfo, info, (ntvfs, req, info));
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
query info on a open file
|
||||
*/
|
||||
static void nbench_qfileinfo_send(struct ntvfs_request *req)
|
||||
{
|
||||
union smb_fileinfo *info = req->async_states->private_data;
|
||||
|
||||
nbench_log(req, "QUERY_FILE_INFORMATION %s %d %s\n",
|
||||
nbench_ntvfs_handle_string(req, info->generic.in.file.ntvfs),
|
||||
info->generic.level,
|
||||
get_nt_error_c_code(req->async_states->status));
|
||||
|
||||
PASS_THRU_REP_POST(req);
|
||||
}
|
||||
|
||||
static NTSTATUS nbench_qfileinfo(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_fileinfo *info)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
PASS_THRU_REQ(ntvfs, req, qfileinfo, info, (ntvfs, req, info));
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
set info on a pathname
|
||||
*/
|
||||
static void nbench_setpathinfo_send(struct ntvfs_request *req)
|
||||
{
|
||||
union smb_setfileinfo *st = req->async_states->private_data;
|
||||
|
||||
nbench_log(req, "SET_PATH_INFORMATION \"%s\" %d %s\n",
|
||||
st->generic.in.file.path,
|
||||
st->generic.level,
|
||||
get_nt_error_c_code(req->async_states->status));
|
||||
|
||||
PASS_THRU_REP_POST(req);
|
||||
}
|
||||
|
||||
static NTSTATUS nbench_setpathinfo(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_setfileinfo *st)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
PASS_THRU_REQ(ntvfs, req, setpathinfo, st, (ntvfs, req, st));
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
open a file
|
||||
*/
|
||||
static void nbench_open_send(struct ntvfs_request *req)
|
||||
{
|
||||
union smb_open *io = req->async_states->private_data;
|
||||
|
||||
switch (io->generic.level) {
|
||||
case RAW_OPEN_NTCREATEX:
|
||||
if (!NT_STATUS_IS_OK(req->async_states->status)) {
|
||||
ZERO_STRUCT(io->ntcreatex.out);
|
||||
}
|
||||
nbench_log(req, "NTCreateX \"%s\" 0x%x 0x%x %s %s\n",
|
||||
io->ntcreatex.in.fname,
|
||||
io->ntcreatex.in.create_options,
|
||||
io->ntcreatex.in.open_disposition,
|
||||
nbench_ntvfs_handle_string(req, io->ntcreatex.out.file.ntvfs),
|
||||
get_nt_error_c_code(req->async_states->status));
|
||||
break;
|
||||
|
||||
default:
|
||||
nbench_log(req, "Open-%d - NOT HANDLED\n",
|
||||
io->generic.level);
|
||||
break;
|
||||
}
|
||||
|
||||
PASS_THRU_REP_POST(req);
|
||||
}
|
||||
|
||||
static NTSTATUS nbench_open(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_open *io)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
#undef open /* AIX defines open to be open64 */
|
||||
PASS_THRU_REQ(ntvfs, req, open, io, (ntvfs, req, io));
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
create a directory
|
||||
*/
|
||||
static void nbench_mkdir_send(struct ntvfs_request *req)
|
||||
{
|
||||
nbench_log(req, "Mkdir - NOT HANDLED\n");
|
||||
|
||||
PASS_THRU_REP_POST(req);
|
||||
}
|
||||
|
||||
static NTSTATUS nbench_mkdir(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_mkdir *md)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
PASS_THRU_REQ(ntvfs, req, mkdir, md, (ntvfs, req, md));
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
remove a directory
|
||||
*/
|
||||
static void nbench_rmdir_send(struct ntvfs_request *req)
|
||||
{
|
||||
struct smb_rmdir *rd = req->async_states->private_data;
|
||||
|
||||
nbench_log(req, "Rmdir \"%s\" %s\n",
|
||||
rd->in.path,
|
||||
get_nt_error_c_code(req->async_states->status));
|
||||
|
||||
PASS_THRU_REP_POST(req);
|
||||
}
|
||||
|
||||
static NTSTATUS nbench_rmdir(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, struct smb_rmdir *rd)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
PASS_THRU_REQ(ntvfs, req, rmdir, rd, (ntvfs, req, rd));
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
rename a set of files
|
||||
*/
|
||||
static void nbench_rename_send(struct ntvfs_request *req)
|
||||
{
|
||||
union smb_rename *ren = req->async_states->private_data;
|
||||
|
||||
switch (ren->generic.level) {
|
||||
case RAW_RENAME_RENAME:
|
||||
nbench_log(req, "Rename \"%s\" \"%s\" %s\n",
|
||||
ren->rename.in.pattern1,
|
||||
ren->rename.in.pattern2,
|
||||
get_nt_error_c_code(req->async_states->status));
|
||||
break;
|
||||
|
||||
default:
|
||||
nbench_log(req, "Rename-%d - NOT HANDLED\n",
|
||||
ren->generic.level);
|
||||
break;
|
||||
}
|
||||
|
||||
PASS_THRU_REP_POST(req);
|
||||
}
|
||||
|
||||
static NTSTATUS nbench_rename(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_rename *ren)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
PASS_THRU_REQ(ntvfs, req, rename, ren, (ntvfs, req, ren));
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
copy a set of files
|
||||
*/
|
||||
static void nbench_copy_send(struct ntvfs_request *req)
|
||||
{
|
||||
nbench_log(req, "Copy - NOT HANDLED\n");
|
||||
|
||||
PASS_THRU_REP_POST(req);
|
||||
}
|
||||
|
||||
static NTSTATUS nbench_copy(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, struct smb_copy *cp)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
PASS_THRU_REQ(ntvfs, req, copy, cp, (ntvfs, req, cp));
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
read from a file
|
||||
*/
|
||||
static void nbench_read_send(struct ntvfs_request *req)
|
||||
{
|
||||
union smb_read *rd = req->async_states->private_data;
|
||||
|
||||
switch (rd->generic.level) {
|
||||
case RAW_READ_READX:
|
||||
if (!NT_STATUS_IS_OK(req->async_states->status)) {
|
||||
ZERO_STRUCT(rd->readx.out);
|
||||
}
|
||||
nbench_log(req, "ReadX %s %d %d %d %s\n",
|
||||
nbench_ntvfs_handle_string(req, rd->readx.in.file.ntvfs),
|
||||
(int)rd->readx.in.offset,
|
||||
rd->readx.in.maxcnt,
|
||||
rd->readx.out.nread,
|
||||
get_nt_error_c_code(req->async_states->status));
|
||||
break;
|
||||
default:
|
||||
nbench_log(req, "Read-%d - NOT HANDLED\n",
|
||||
rd->generic.level);
|
||||
break;
|
||||
}
|
||||
|
||||
PASS_THRU_REP_POST(req);
|
||||
}
|
||||
|
||||
static NTSTATUS nbench_read(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_read *rd)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
PASS_THRU_REQ(ntvfs, req, read, rd, (ntvfs, req, rd));
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
write to a file
|
||||
*/
|
||||
static void nbench_write_send(struct ntvfs_request *req)
|
||||
{
|
||||
union smb_write *wr = req->async_states->private_data;
|
||||
|
||||
switch (wr->generic.level) {
|
||||
case RAW_WRITE_WRITEX:
|
||||
if (!NT_STATUS_IS_OK(req->async_states->status)) {
|
||||
ZERO_STRUCT(wr->writex.out);
|
||||
}
|
||||
nbench_log(req, "WriteX %s %d %d %d %s\n",
|
||||
nbench_ntvfs_handle_string(req, wr->writex.in.file.ntvfs),
|
||||
(int)wr->writex.in.offset,
|
||||
wr->writex.in.count,
|
||||
wr->writex.out.nwritten,
|
||||
get_nt_error_c_code(req->async_states->status));
|
||||
break;
|
||||
|
||||
case RAW_WRITE_WRITE:
|
||||
if (!NT_STATUS_IS_OK(req->async_states->status)) {
|
||||
ZERO_STRUCT(wr->write.out);
|
||||
}
|
||||
nbench_log(req, "Write %s %d %d %d %s\n",
|
||||
nbench_ntvfs_handle_string(req, wr->write.in.file.ntvfs),
|
||||
wr->write.in.offset,
|
||||
wr->write.in.count,
|
||||
wr->write.out.nwritten,
|
||||
get_nt_error_c_code(req->async_states->status));
|
||||
break;
|
||||
|
||||
default:
|
||||
nbench_log(req, "Write-%d - NOT HANDLED\n",
|
||||
wr->generic.level);
|
||||
break;
|
||||
}
|
||||
|
||||
PASS_THRU_REP_POST(req);
|
||||
}
|
||||
|
||||
static NTSTATUS nbench_write(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_write *wr)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
PASS_THRU_REQ(ntvfs, req, write, wr, (ntvfs, req, wr));
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
seek in a file
|
||||
*/
|
||||
static void nbench_seek_send(struct ntvfs_request *req)
|
||||
{
|
||||
nbench_log(req, "Seek - NOT HANDLED\n");
|
||||
|
||||
PASS_THRU_REP_POST(req);
|
||||
}
|
||||
|
||||
static NTSTATUS nbench_seek(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
union smb_seek *io)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
PASS_THRU_REQ(ntvfs, req, seek, io, (ntvfs, req, io));
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
flush a file
|
||||
*/
|
||||
static void nbench_flush_send(struct ntvfs_request *req)
|
||||
{
|
||||
union smb_flush *io = req->async_states->private_data;
|
||||
|
||||
switch (io->generic.level) {
|
||||
case RAW_FLUSH_FLUSH:
|
||||
nbench_log(req, "Flush %s %s\n",
|
||||
nbench_ntvfs_handle_string(req, io->flush.in.file.ntvfs),
|
||||
get_nt_error_c_code(req->async_states->status));
|
||||
break;
|
||||
case RAW_FLUSH_ALL:
|
||||
nbench_log(req, "Flush %d %s\n",
|
||||
0xFFFF,
|
||||
get_nt_error_c_code(req->async_states->status));
|
||||
break;
|
||||
default:
|
||||
nbench_log(req, "Flush-%d - NOT HANDLED\n",
|
||||
io->generic.level);
|
||||
break;
|
||||
}
|
||||
|
||||
PASS_THRU_REP_POST(req);
|
||||
}
|
||||
|
||||
static NTSTATUS nbench_flush(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
union smb_flush *io)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
PASS_THRU_REQ(ntvfs, req, flush, io, (ntvfs, req, io));
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
close a file
|
||||
*/
|
||||
static void nbench_close_send(struct ntvfs_request *req)
|
||||
{
|
||||
union smb_close *io = req->async_states->private_data;
|
||||
|
||||
switch (io->generic.level) {
|
||||
case RAW_CLOSE_CLOSE:
|
||||
nbench_log(req, "Close %s %s\n",
|
||||
nbench_ntvfs_handle_string(req, io->close.in.file.ntvfs),
|
||||
get_nt_error_c_code(req->async_states->status));
|
||||
break;
|
||||
|
||||
default:
|
||||
nbench_log(req, "Close-%d - NOT HANDLED\n",
|
||||
io->generic.level);
|
||||
break;
|
||||
}
|
||||
|
||||
PASS_THRU_REP_POST(req);
|
||||
}
|
||||
|
||||
static NTSTATUS nbench_close(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_close *io)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
PASS_THRU_REQ(ntvfs, req, close, io, (ntvfs, req, io));
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
exit - closing files
|
||||
*/
|
||||
static void nbench_exit_send(struct ntvfs_request *req)
|
||||
{
|
||||
nbench_log(req, "Exit - NOT HANDLED\n");
|
||||
|
||||
PASS_THRU_REP_POST(req);
|
||||
}
|
||||
|
||||
static NTSTATUS nbench_exit(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
PASS_THRU_REQ(ntvfs, req, exit, NULL, (ntvfs, req));
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
logoff - closing files
|
||||
*/
|
||||
static void nbench_logoff_send(struct ntvfs_request *req)
|
||||
{
|
||||
nbench_log(req, "Logoff - NOT HANDLED\n");
|
||||
|
||||
PASS_THRU_REP_POST(req);
|
||||
}
|
||||
|
||||
static NTSTATUS nbench_logoff(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
PASS_THRU_REQ(ntvfs, req, logoff, NULL, (ntvfs, req));
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
async_setup - send fn
|
||||
*/
|
||||
static void nbench_async_setup_send(struct ntvfs_request *req)
|
||||
{
|
||||
PASS_THRU_REP_POST(req);
|
||||
}
|
||||
|
||||
/*
|
||||
async setup
|
||||
*/
|
||||
static NTSTATUS nbench_async_setup(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
void *private)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
PASS_THRU_REQ(ntvfs, req, async_setup, NULL, (ntvfs, req, private));
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
static void nbench_cancel_send(struct ntvfs_request *req)
|
||||
{
|
||||
PASS_THRU_REP_POST(req);
|
||||
}
|
||||
|
||||
/*
|
||||
cancel an existing async request
|
||||
*/
|
||||
static NTSTATUS nbench_cancel(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
PASS_THRU_REQ(ntvfs, req, cancel, NULL, (ntvfs, req));
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
lock a byte range
|
||||
*/
|
||||
static void nbench_lock_send(struct ntvfs_request *req)
|
||||
{
|
||||
union smb_lock *lck = req->async_states->private_data;
|
||||
|
||||
if (lck->generic.level == RAW_LOCK_LOCKX &&
|
||||
lck->lockx.in.lock_cnt == 1 &&
|
||||
lck->lockx.in.ulock_cnt == 0) {
|
||||
nbench_log(req, "LockX %s %d %d %s\n",
|
||||
nbench_ntvfs_handle_string(req, lck->lockx.in.file.ntvfs),
|
||||
(int)lck->lockx.in.locks[0].offset,
|
||||
(int)lck->lockx.in.locks[0].count,
|
||||
get_nt_error_c_code(req->async_states->status));
|
||||
} else if (lck->generic.level == RAW_LOCK_LOCKX &&
|
||||
lck->lockx.in.ulock_cnt == 1) {
|
||||
nbench_log(req, "UnlockX %s %d %d %s\n",
|
||||
nbench_ntvfs_handle_string(req, lck->lockx.in.file.ntvfs),
|
||||
(int)lck->lockx.in.locks[0].offset,
|
||||
(int)lck->lockx.in.locks[0].count,
|
||||
get_nt_error_c_code(req->async_states->status));
|
||||
} else {
|
||||
nbench_log(req, "Lock-%d - NOT HANDLED\n", lck->generic.level);
|
||||
}
|
||||
|
||||
PASS_THRU_REP_POST(req);
|
||||
}
|
||||
|
||||
static NTSTATUS nbench_lock(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_lock *lck)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
PASS_THRU_REQ(ntvfs, req, lock, lck, (ntvfs, req, lck));
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
set info on a open file
|
||||
*/
|
||||
static void nbench_setfileinfo_send(struct ntvfs_request *req)
|
||||
{
|
||||
union smb_setfileinfo *info = req->async_states->private_data;
|
||||
|
||||
nbench_log(req, "SET_FILE_INFORMATION %s %d %s\n",
|
||||
nbench_ntvfs_handle_string(req, info->generic.in.file.ntvfs),
|
||||
info->generic.level,
|
||||
get_nt_error_c_code(req->async_states->status));
|
||||
|
||||
PASS_THRU_REP_POST(req);
|
||||
}
|
||||
|
||||
static NTSTATUS nbench_setfileinfo(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
union smb_setfileinfo *info)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
PASS_THRU_REQ(ntvfs, req, setfileinfo, info, (ntvfs, req, info));
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
return filesystem space info
|
||||
*/
|
||||
static void nbench_fsinfo_send(struct ntvfs_request *req)
|
||||
{
|
||||
union smb_fsinfo *fs = req->async_states->private_data;
|
||||
|
||||
nbench_log(req, "QUERY_FS_INFORMATION %d %s\n",
|
||||
fs->generic.level,
|
||||
get_nt_error_c_code(req->async_states->status));
|
||||
|
||||
PASS_THRU_REP_POST(req);
|
||||
}
|
||||
|
||||
static NTSTATUS nbench_fsinfo(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_fsinfo *fs)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
PASS_THRU_REQ(ntvfs, req, fsinfo, fs, (ntvfs, req, fs));
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
return print queue info
|
||||
*/
|
||||
static void nbench_lpq_send(struct ntvfs_request *req)
|
||||
{
|
||||
union smb_lpq *lpq = req->async_states->private_data;
|
||||
|
||||
nbench_log(req, "Lpq-%d - NOT HANDLED\n", lpq->generic.level);
|
||||
|
||||
PASS_THRU_REP_POST(req);
|
||||
}
|
||||
|
||||
static NTSTATUS nbench_lpq(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_lpq *lpq)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
PASS_THRU_REQ(ntvfs, req, lpq, lpq, (ntvfs, req, lpq));
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
list files in a directory matching a wildcard pattern
|
||||
*/
|
||||
static void nbench_search_first_send(struct ntvfs_request *req)
|
||||
{
|
||||
union smb_search_first *io = req->async_states->private_data;
|
||||
|
||||
switch (io->generic.level) {
|
||||
case RAW_SEARCH_TRANS2:
|
||||
if (NT_STATUS_IS_ERR(req->async_states->status)) {
|
||||
ZERO_STRUCT(io->t2ffirst.out);
|
||||
}
|
||||
nbench_log(req, "FIND_FIRST \"%s\" %d %d %d %s\n",
|
||||
io->t2ffirst.in.pattern,
|
||||
io->t2ffirst.data_level,
|
||||
io->t2ffirst.in.max_count,
|
||||
io->t2ffirst.out.count,
|
||||
get_nt_error_c_code(req->async_states->status));
|
||||
break;
|
||||
|
||||
default:
|
||||
nbench_log(req, "Search-%d - NOT HANDLED\n", io->generic.level);
|
||||
break;
|
||||
}
|
||||
|
||||
PASS_THRU_REP_POST(req);
|
||||
}
|
||||
|
||||
static NTSTATUS nbench_search_first(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_search_first *io,
|
||||
void *search_private,
|
||||
BOOL (*callback)(void *, union smb_search_data *))
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
PASS_THRU_REQ(ntvfs, req, search_first, io, (ntvfs, req, io, search_private, callback));
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/* continue a search */
|
||||
static void nbench_search_next_send(struct ntvfs_request *req)
|
||||
{
|
||||
union smb_search_next *io = req->async_states->private_data;
|
||||
|
||||
nbench_log(req, "Searchnext-%d - NOT HANDLED\n", io->generic.level);
|
||||
|
||||
PASS_THRU_REP_POST(req);
|
||||
}
|
||||
|
||||
static NTSTATUS nbench_search_next(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_search_next *io,
|
||||
void *search_private,
|
||||
BOOL (*callback)(void *, union smb_search_data *))
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
PASS_THRU_REQ(ntvfs, req, search_next, io, (ntvfs, req, io, search_private, callback));
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/* close a search */
|
||||
static void nbench_search_close_send(struct ntvfs_request *req)
|
||||
{
|
||||
union smb_search_close *io = req->async_states->private_data;
|
||||
|
||||
nbench_log(req, "Searchclose-%d - NOT HANDLED\n", io->generic.level);
|
||||
|
||||
PASS_THRU_REP_POST(req);
|
||||
}
|
||||
|
||||
static NTSTATUS nbench_search_close(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_search_close *io)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
PASS_THRU_REQ(ntvfs, req, search_close, io, (ntvfs, req, io));
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/* SMBtrans - not used on file shares */
|
||||
static void nbench_trans_send(struct ntvfs_request *req)
|
||||
{
|
||||
nbench_log(req, "Trans - NOT HANDLED\n");
|
||||
|
||||
PASS_THRU_REP_POST(req);
|
||||
}
|
||||
|
||||
static NTSTATUS nbench_trans(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, struct smb_trans2 *trans2)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
PASS_THRU_REQ(ntvfs, req, trans, trans2, (ntvfs, req, trans2));
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
initialise the nbench backend, registering ourselves with the ntvfs subsystem
|
||||
*/
|
||||
NTSTATUS ntvfs_nbench_init(void)
|
||||
{
|
||||
NTSTATUS ret;
|
||||
struct ntvfs_ops ops;
|
||||
NTVFS_CURRENT_CRITICAL_SIZES(vers);
|
||||
|
||||
ZERO_STRUCT(ops);
|
||||
|
||||
/* fill in the name and type */
|
||||
ops.name = "nbench";
|
||||
ops.type = NTVFS_DISK;
|
||||
|
||||
/* fill in all the operations */
|
||||
ops.connect = nbench_connect;
|
||||
ops.disconnect = nbench_disconnect;
|
||||
ops.unlink = nbench_unlink;
|
||||
ops.chkpath = nbench_chkpath;
|
||||
ops.qpathinfo = nbench_qpathinfo;
|
||||
ops.setpathinfo = nbench_setpathinfo;
|
||||
ops.open = nbench_open;
|
||||
ops.mkdir = nbench_mkdir;
|
||||
ops.rmdir = nbench_rmdir;
|
||||
ops.rename = nbench_rename;
|
||||
ops.copy = nbench_copy;
|
||||
ops.ioctl = nbench_ioctl;
|
||||
ops.read = nbench_read;
|
||||
ops.write = nbench_write;
|
||||
ops.seek = nbench_seek;
|
||||
ops.flush = nbench_flush;
|
||||
ops.close = nbench_close;
|
||||
ops.exit = nbench_exit;
|
||||
ops.lock = nbench_lock;
|
||||
ops.setfileinfo = nbench_setfileinfo;
|
||||
ops.qfileinfo = nbench_qfileinfo;
|
||||
ops.fsinfo = nbench_fsinfo;
|
||||
ops.lpq = nbench_lpq;
|
||||
ops.search_first = nbench_search_first;
|
||||
ops.search_next = nbench_search_next;
|
||||
ops.search_close = nbench_search_close;
|
||||
ops.trans = nbench_trans;
|
||||
ops.logoff = nbench_logoff;
|
||||
ops.async_setup = nbench_async_setup;
|
||||
ops.cancel = nbench_cancel;
|
||||
|
||||
/* we don't register a trans2 handler as we want to be able to
|
||||
log individual trans2 requests */
|
||||
ops.trans2 = NULL;
|
||||
|
||||
/* register ourselves with the NTVFS subsystem. */
|
||||
ret = ntvfs_register(&ops, &vers);
|
||||
|
||||
if (!NT_STATUS_IS_OK(ret)) {
|
||||
DEBUG(0,("Failed to register nbench backend!\n"));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,313 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
NTVFS structures and defines
|
||||
Copyright (C) Andrew Tridgell 2003
|
||||
Copyright (C) Stefan Metzmacher 2004
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 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.
|
||||
*/
|
||||
|
||||
#include "libcli/raw/interfaces.h"
|
||||
#include "param/share.h"
|
||||
|
||||
/* modules can use the following to determine if the interface has changed */
|
||||
/* version 1 -> 0 - make module stacking easier -- metze */
|
||||
#define NTVFS_INTERFACE_VERSION 0
|
||||
|
||||
struct ntvfs_module_context;
|
||||
struct ntvfs_request;
|
||||
|
||||
/* each backend has to be one one of the following 3 basic types. In
|
||||
* earlier versions of Samba backends needed to handle all types, now
|
||||
* we implement them separately. */
|
||||
enum ntvfs_type {NTVFS_DISK, NTVFS_PRINT, NTVFS_IPC};
|
||||
|
||||
/* the ntvfs operations structure - contains function pointers to
|
||||
the backend implementations of each operation */
|
||||
struct ntvfs_ops {
|
||||
const char *name;
|
||||
enum ntvfs_type type;
|
||||
|
||||
/* initial setup */
|
||||
NTSTATUS (*connect)(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
const char *sharename);
|
||||
NTSTATUS (*disconnect)(struct ntvfs_module_context *ntvfs);
|
||||
|
||||
/* async_setup - called when a backend is processing a async request */
|
||||
NTSTATUS (*async_setup)(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
void *private);
|
||||
|
||||
/* filesystem operations */
|
||||
NTSTATUS (*fsinfo)(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
union smb_fsinfo *fs);
|
||||
|
||||
/* path operations */
|
||||
NTSTATUS (*unlink)(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
union smb_unlink *unl);
|
||||
NTSTATUS (*chkpath)(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
union smb_chkpath *cp);
|
||||
NTSTATUS (*qpathinfo)(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
union smb_fileinfo *st);
|
||||
NTSTATUS (*setpathinfo)(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
union smb_setfileinfo *st);
|
||||
NTSTATUS (*mkdir)(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
union smb_mkdir *md);
|
||||
NTSTATUS (*rmdir)(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
struct smb_rmdir *rd);
|
||||
NTSTATUS (*rename)(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
union smb_rename *ren);
|
||||
NTSTATUS (*copy)(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
struct smb_copy *cp);
|
||||
NTSTATUS (*open)(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
union smb_open *oi);
|
||||
|
||||
/* directory search */
|
||||
NTSTATUS (*search_first)(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
union smb_search_first *io, void *private,
|
||||
BOOL (*callback)(void *private, union smb_search_data *file));
|
||||
NTSTATUS (*search_next)(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
union smb_search_next *io, void *private,
|
||||
BOOL (*callback)(void *private, union smb_search_data *file));
|
||||
NTSTATUS (*search_close)(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
union smb_search_close *io);
|
||||
|
||||
/* operations on open files */
|
||||
NTSTATUS (*ioctl)(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
union smb_ioctl *io);
|
||||
NTSTATUS (*read)(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
union smb_read *io);
|
||||
NTSTATUS (*write)(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
union smb_write *io);
|
||||
NTSTATUS (*seek)(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
union smb_seek *io);
|
||||
NTSTATUS (*flush)(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
union smb_flush *flush);
|
||||
NTSTATUS (*lock)(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
union smb_lock *lck);
|
||||
NTSTATUS (*qfileinfo)(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
union smb_fileinfo *info);
|
||||
NTSTATUS (*setfileinfo)(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
union smb_setfileinfo *info);
|
||||
NTSTATUS (*close)(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
union smb_close *io);
|
||||
|
||||
/* trans interface - used by IPC backend for pipes and RAP calls */
|
||||
NTSTATUS (*trans)(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
struct smb_trans2 *trans);
|
||||
|
||||
/* trans2 interface - only used by CIFS backend to prover complete passthru for testing */
|
||||
NTSTATUS (*trans2)(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
struct smb_trans2 *trans2);
|
||||
|
||||
/* change notify request */
|
||||
NTSTATUS (*notify)(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
union smb_notify *info);
|
||||
|
||||
/* cancel - cancels any pending async request */
|
||||
NTSTATUS (*cancel)(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req);
|
||||
|
||||
/* printing specific operations */
|
||||
NTSTATUS (*lpq)(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
union smb_lpq *lpq);
|
||||
|
||||
/* logoff - called when a vuid is closed */
|
||||
NTSTATUS (*logoff)(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req);
|
||||
NTSTATUS (*exit)(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req);
|
||||
};
|
||||
|
||||
struct ntvfs_module_context {
|
||||
struct ntvfs_module_context *prev, *next;
|
||||
struct ntvfs_context *ctx;
|
||||
int depth;
|
||||
const struct ntvfs_ops *ops;
|
||||
void *private_data;
|
||||
};
|
||||
|
||||
struct ntvfs_context {
|
||||
enum ntvfs_type type;
|
||||
|
||||
/* the reported filesystem type */
|
||||
char *fs_type;
|
||||
|
||||
/* the reported device type */
|
||||
char *dev_type;
|
||||
|
||||
enum protocol_types protocol;
|
||||
|
||||
/*
|
||||
* linked list of module contexts
|
||||
*/
|
||||
struct ntvfs_module_context *modules;
|
||||
|
||||
struct share_config *config;
|
||||
|
||||
uint32_t server_id;
|
||||
struct event_context *event_ctx;
|
||||
struct messaging_context *msg_ctx;
|
||||
|
||||
struct {
|
||||
void *private_data;
|
||||
NTSTATUS (*handler)(void *private_data, struct ntvfs_handle *handle, uint8_t level);
|
||||
} oplock;
|
||||
|
||||
struct {
|
||||
void *private_data;
|
||||
struct socket_address *(*get_my_addr)(void *private_data, TALLOC_CTX *mem_ctx);
|
||||
struct socket_address *(*get_peer_addr)(void *private_data, TALLOC_CTX *mem_ctx);
|
||||
} client;
|
||||
|
||||
struct {
|
||||
void *private_data;
|
||||
NTSTATUS (*create_new)(void *private_data, struct ntvfs_request *req, struct ntvfs_handle **h);
|
||||
NTSTATUS (*make_valid)(void *private_data, struct ntvfs_handle *h);
|
||||
void (*destroy)(void *private_data, struct ntvfs_handle *h);
|
||||
struct ntvfs_handle *(*search_by_wire_key)(void *private_data, struct ntvfs_request *req, const DATA_BLOB *key);
|
||||
DATA_BLOB (*get_wire_key)(void *private_data, struct ntvfs_handle *handle, TALLOC_CTX *mem_ctx);
|
||||
} handles;
|
||||
};
|
||||
|
||||
/* a set of flags to control handling of request structures */
|
||||
#define NTVFS_ASYNC_STATE_ASYNC (1<<1) /* the backend will answer this one later */
|
||||
#define NTVFS_ASYNC_STATE_MAY_ASYNC (1<<2) /* the backend is allowed to answer async */
|
||||
|
||||
/* the ntvfs_async_state structure allows backend functions to
|
||||
delay replying to requests. To use this, the front end must
|
||||
set send_fn to a function to be called by the backend
|
||||
when the reply is finally ready to be sent. The backend
|
||||
must set status to the status it wants in the
|
||||
reply. The backend must set the NTVFS_ASYNC_STATE_ASYNC
|
||||
control_flag on the request to indicate that it wishes to
|
||||
delay the reply
|
||||
|
||||
If NTVFS_ASYNC_STATE_MAY_ASYNC is not set then the backend cannot
|
||||
ask for a delayed reply for this request
|
||||
|
||||
note that the private_data pointer is private to the layer which alloced this struct
|
||||
*/
|
||||
struct ntvfs_async_state {
|
||||
struct ntvfs_async_state *prev, *next;
|
||||
/* the async handling infos */
|
||||
unsigned int state;
|
||||
void *private_data;
|
||||
void (*send_fn)(struct ntvfs_request *);
|
||||
NTSTATUS status;
|
||||
|
||||
/* the passthru module's per session private data */
|
||||
struct ntvfs_module_context *ntvfs;
|
||||
};
|
||||
|
||||
struct ntvfs_request {
|
||||
/* the ntvfs_context this requests belongs to */
|
||||
struct ntvfs_context *ctx;
|
||||
|
||||
/* ntvfs per request async states */
|
||||
struct ntvfs_async_state *async_states;
|
||||
|
||||
/* the session_info, with security_token and maybe delegated credentials */
|
||||
struct auth_session_info *session_info;
|
||||
|
||||
/* the smb pid is needed for locking contexts */
|
||||
uint16_t smbpid;
|
||||
|
||||
/* some statictics for the management tools */
|
||||
struct {
|
||||
/* the system time when the request arrived */
|
||||
struct timeval request_time;
|
||||
} statistics;
|
||||
|
||||
struct {
|
||||
void *private_data;
|
||||
} frontend_data;
|
||||
};
|
||||
|
||||
struct ntvfs_handle {
|
||||
struct ntvfs_context *ctx;
|
||||
|
||||
struct auth_session_info *session_info;
|
||||
|
||||
uint16_t smbpid;
|
||||
|
||||
struct ntvfs_handle_data {
|
||||
struct ntvfs_handle_data *prev, *next;
|
||||
struct ntvfs_module_context *owner;
|
||||
void *private_data;/* this must be a valid talloc pointer */
|
||||
} *backend_data;
|
||||
|
||||
struct {
|
||||
void *private_data;
|
||||
} frontend_data;
|
||||
};
|
||||
|
||||
/* this structure is used by backends to determine the size of some critical types */
|
||||
struct ntvfs_critical_sizes {
|
||||
int interface_version;
|
||||
int sizeof_ntvfs_critical_sizes;
|
||||
int sizeof_ntvfs_context;
|
||||
int sizeof_ntvfs_module_context;
|
||||
int sizeof_ntvfs_ops;
|
||||
int sizeof_ntvfs_async_state;
|
||||
int sizeof_ntvfs_request;
|
||||
int sizeof_ntvfs_handle;
|
||||
int sizeof_ntvfs_handle_data;
|
||||
};
|
||||
|
||||
#define NTVFS_CURRENT_CRITICAL_SIZES(c) \
|
||||
struct ntvfs_critical_sizes c = { \
|
||||
.interface_version = NTVFS_INTERFACE_VERSION, \
|
||||
.sizeof_ntvfs_critical_sizes = sizeof(struct ntvfs_critical_sizes), \
|
||||
.sizeof_ntvfs_context = sizeof(struct ntvfs_context), \
|
||||
.sizeof_ntvfs_module_context = sizeof(struct ntvfs_module_context), \
|
||||
.sizeof_ntvfs_ops = sizeof(struct ntvfs_ops), \
|
||||
.sizeof_ntvfs_async_state = sizeof(struct ntvfs_async_state), \
|
||||
.sizeof_ntvfs_request = sizeof(struct ntvfs_request), \
|
||||
.sizeof_ntvfs_handle = sizeof(struct ntvfs_handle), \
|
||||
.sizeof_ntvfs_handle_data = sizeof(struct ntvfs_handle_data), \
|
||||
}
|
||||
|
||||
struct messaging_context;
|
||||
#include "librpc/gen_ndr/security.h"
|
||||
#include "librpc/gen_ndr/notify.h"
|
||||
#include "ntvfs/ntvfs_proto.h"
|
||||
@@ -0,0 +1,211 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
NTVFS base code
|
||||
|
||||
Copyright (C) Andrew Tridgell 2003
|
||||
Copyright (C) Stefan (metze) Metzmacher 2004
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 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"
|
||||
#include "lib/util/dlinklist.h"
|
||||
#include "build.h"
|
||||
#include "ntvfs/ntvfs.h"
|
||||
|
||||
/* the list of currently registered NTVFS backends, note that there
|
||||
* can be more than one backend with the same name, as long as they
|
||||
* have different typesx */
|
||||
static struct ntvfs_backend {
|
||||
const struct ntvfs_ops *ops;
|
||||
} *backends = NULL;
|
||||
static int num_backends;
|
||||
|
||||
/*
|
||||
register a NTVFS backend.
|
||||
|
||||
The 'name' can be later used by other backends to find the operations
|
||||
structure for this backend.
|
||||
|
||||
The 'type' is used to specify whether this is for a disk, printer or IPC$ share
|
||||
*/
|
||||
_PUBLIC_ NTSTATUS ntvfs_register(const struct ntvfs_ops *ops,
|
||||
const struct ntvfs_critical_sizes *const sizes)
|
||||
{
|
||||
struct ntvfs_ops *new_ops;
|
||||
|
||||
if (ntvfs_interface_differs(sizes)) {
|
||||
DEBUG(0, ("NTVFS backend '%s' for type %d "
|
||||
"failed version check\n",
|
||||
ops->name, (int)ops->type));
|
||||
return NT_STATUS_BAD_FUNCTION_TABLE;
|
||||
}
|
||||
|
||||
if (ntvfs_backend_byname(ops->name, ops->type) != NULL) {
|
||||
/* its already registered! */
|
||||
DEBUG(0,("NTVFS backend '%s' for type %d already registered\n",
|
||||
ops->name, (int)ops->type));
|
||||
return NT_STATUS_OBJECT_NAME_COLLISION;
|
||||
}
|
||||
|
||||
backends = realloc_p(backends, struct ntvfs_backend, num_backends+1);
|
||||
if (!backends) {
|
||||
smb_panic("out of memory in ntvfs_register");
|
||||
}
|
||||
|
||||
new_ops = smb_xmemdup(ops, sizeof(*ops));
|
||||
new_ops->name = smb_xstrdup(ops->name);
|
||||
|
||||
backends[num_backends].ops = new_ops;
|
||||
|
||||
num_backends++;
|
||||
|
||||
DEBUG(3,("NTVFS backend '%s' for type %d registered\n",
|
||||
ops->name,ops->type));
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
return the operations structure for a named backend of the specified type
|
||||
*/
|
||||
_PUBLIC_ const struct ntvfs_ops *ntvfs_backend_byname(const char *name, enum ntvfs_type type)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0;i<num_backends;i++) {
|
||||
if (backends[i].ops->type == type &&
|
||||
strcmp(backends[i].ops->name, name) == 0) {
|
||||
return backends[i].ops;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
return the NTVFS interface version, and the size of some critical types
|
||||
This can be used by backends to either detect compilation errors, or provide
|
||||
multiple implementations for different smbd compilation options in one module
|
||||
*/
|
||||
|
||||
static const NTVFS_CURRENT_CRITICAL_SIZES(critical_sizes);
|
||||
|
||||
_PUBLIC_ const struct ntvfs_critical_sizes *ntvfs_interface_version(void)
|
||||
{
|
||||
return &critical_sizes;
|
||||
}
|
||||
|
||||
_PUBLIC_ BOOL ntvfs_interface_differs(const struct ntvfs_critical_sizes *const iface)
|
||||
{
|
||||
/* The comparison would be easier with memcmp, but compiler-interset
|
||||
* alignment padding is not guaranteed to be zeroed.
|
||||
*/
|
||||
|
||||
#define FIELD_DIFFERS(field) (iface->field != critical_sizes.field)
|
||||
|
||||
if (FIELD_DIFFERS(interface_version))
|
||||
return True;
|
||||
|
||||
if (FIELD_DIFFERS(sizeof_ntvfs_critical_sizes))
|
||||
return True;
|
||||
|
||||
if (FIELD_DIFFERS(sizeof_ntvfs_context))
|
||||
return True;
|
||||
|
||||
if (FIELD_DIFFERS(sizeof_ntvfs_module_context))
|
||||
return True;
|
||||
|
||||
if (FIELD_DIFFERS(sizeof_ntvfs_ops))
|
||||
return True;
|
||||
|
||||
if (FIELD_DIFFERS(sizeof_ntvfs_async_state))
|
||||
return True;
|
||||
|
||||
if (FIELD_DIFFERS(sizeof_ntvfs_request))
|
||||
return True;
|
||||
|
||||
/* Versions match. */
|
||||
return False;
|
||||
|
||||
#undef FIELD_DIFFERS
|
||||
}
|
||||
|
||||
/*
|
||||
initialise a connection structure to point at a NTVFS backend
|
||||
*/
|
||||
NTSTATUS ntvfs_init_connection(TALLOC_CTX *mem_ctx, struct share_config *scfg, enum ntvfs_type type,
|
||||
enum protocol_types protocol,
|
||||
struct event_context *ev, struct messaging_context *msg,
|
||||
uint32_t server_id, struct ntvfs_context **_ctx)
|
||||
{
|
||||
const char **handlers = share_string_list_option(mem_ctx, scfg, SHARE_NTVFS_HANDLER);
|
||||
int i;
|
||||
struct ntvfs_context *ctx;
|
||||
|
||||
if (!handlers) {
|
||||
return NT_STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
ctx = talloc_zero(mem_ctx, struct ntvfs_context);
|
||||
NT_STATUS_HAVE_NO_MEMORY(ctx);
|
||||
ctx->protocol = protocol;
|
||||
ctx->type = type;
|
||||
ctx->config = talloc_steal(ctx, scfg);
|
||||
ctx->event_ctx = ev;
|
||||
ctx->msg_ctx = msg;
|
||||
ctx->server_id = server_id;
|
||||
|
||||
for (i=0; handlers[i]; i++) {
|
||||
struct ntvfs_module_context *ntvfs;
|
||||
|
||||
ntvfs = talloc_zero(ctx, struct ntvfs_module_context);
|
||||
NT_STATUS_HAVE_NO_MEMORY(ntvfs);
|
||||
ntvfs->ctx = ctx;
|
||||
ntvfs->ops = ntvfs_backend_byname(handlers[i], ctx->type);
|
||||
if (!ntvfs->ops) {
|
||||
DEBUG(1,("ntvfs_init_connection: failed to find backend=%s, type=%d\n",
|
||||
handlers[i], ctx->type));
|
||||
return NT_STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
ntvfs->depth = i;
|
||||
DLIST_ADD_END(ctx->modules, ntvfs, struct ntvfs_module_context *);
|
||||
}
|
||||
|
||||
if (!ctx->modules) {
|
||||
return NT_STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
*_ctx = ctx;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
NTSTATUS ntvfs_init(void)
|
||||
{
|
||||
init_module_fn static_init[] = STATIC_ntvfs_MODULES;
|
||||
init_module_fn *shared_init = load_samba_modules(NULL, "ntvfs");
|
||||
|
||||
run_init_functions(static_init);
|
||||
run_init_functions(shared_init);
|
||||
|
||||
talloc_free(shared_init);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,717 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
NTVFS interface functions
|
||||
|
||||
Copyright (C) Stefan (metze) Metzmacher 2004
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 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.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "ntvfs/ntvfs.h"
|
||||
|
||||
/* connect/disconnect */
|
||||
_PUBLIC_ NTSTATUS ntvfs_connect(struct ntvfs_request *req, const char *sharename)
|
||||
{
|
||||
struct ntvfs_module_context *ntvfs = req->ctx->modules;
|
||||
if (!ntvfs->ops->connect) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
return ntvfs->ops->connect(ntvfs, req, sharename);
|
||||
}
|
||||
|
||||
_PUBLIC_ NTSTATUS ntvfs_disconnect(struct ntvfs_context *ntvfs_ctx)
|
||||
{
|
||||
struct ntvfs_module_context *ntvfs;
|
||||
if (ntvfs_ctx == NULL) {
|
||||
return NT_STATUS_INVALID_CONNECTION;
|
||||
}
|
||||
ntvfs = ntvfs_ctx->modules;
|
||||
if (!ntvfs->ops->disconnect) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
return ntvfs->ops->disconnect(ntvfs);
|
||||
}
|
||||
|
||||
/* async setup - called by a backend that wants to setup any state for
|
||||
a async request */
|
||||
_PUBLIC_ NTSTATUS ntvfs_async_setup(struct ntvfs_request *req, void *private)
|
||||
{
|
||||
struct ntvfs_module_context *ntvfs = req->ctx->modules;
|
||||
if (!ntvfs->ops->async_setup) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
return ntvfs->ops->async_setup(ntvfs, req, private);
|
||||
}
|
||||
|
||||
/* filesystem operations */
|
||||
_PUBLIC_ NTSTATUS ntvfs_fsinfo(struct ntvfs_request *req, union smb_fsinfo *fs)
|
||||
{
|
||||
struct ntvfs_module_context *ntvfs = req->ctx->modules;
|
||||
if (!ntvfs->ops->fsinfo) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
return ntvfs->ops->fsinfo(ntvfs, req, fs);
|
||||
}
|
||||
|
||||
/* path operations */
|
||||
_PUBLIC_ NTSTATUS ntvfs_unlink(struct ntvfs_request *req, union smb_unlink *unl)
|
||||
{
|
||||
struct ntvfs_module_context *ntvfs = req->ctx->modules;
|
||||
if (!ntvfs->ops->unlink) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
return ntvfs->ops->unlink(ntvfs, req, unl);
|
||||
}
|
||||
|
||||
_PUBLIC_ NTSTATUS ntvfs_chkpath(struct ntvfs_request *req, union smb_chkpath *cp)
|
||||
{
|
||||
struct ntvfs_module_context *ntvfs = req->ctx->modules;
|
||||
if (!ntvfs->ops->chkpath) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
return ntvfs->ops->chkpath(ntvfs, req, cp);
|
||||
}
|
||||
|
||||
_PUBLIC_ NTSTATUS ntvfs_qpathinfo(struct ntvfs_request *req, union smb_fileinfo *st)
|
||||
{
|
||||
struct ntvfs_module_context *ntvfs = req->ctx->modules;
|
||||
if (!ntvfs->ops->qpathinfo) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
return ntvfs->ops->qpathinfo(ntvfs, req, st);
|
||||
}
|
||||
|
||||
_PUBLIC_ NTSTATUS ntvfs_setpathinfo(struct ntvfs_request *req, union smb_setfileinfo *st)
|
||||
{
|
||||
struct ntvfs_module_context *ntvfs = req->ctx->modules;
|
||||
if (!ntvfs->ops->setpathinfo) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
return ntvfs->ops->setpathinfo(ntvfs, req, st);
|
||||
}
|
||||
|
||||
_PUBLIC_ NTSTATUS ntvfs_open(struct ntvfs_request *req, union smb_open *oi)
|
||||
{
|
||||
struct ntvfs_module_context *ntvfs = req->ctx->modules;
|
||||
if (!ntvfs->ops->open) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
return ntvfs->ops->open(ntvfs, req, oi);
|
||||
}
|
||||
|
||||
_PUBLIC_ NTSTATUS ntvfs_mkdir(struct ntvfs_request *req, union smb_mkdir *md)
|
||||
{
|
||||
struct ntvfs_module_context *ntvfs = req->ctx->modules;
|
||||
if (!ntvfs->ops->mkdir) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
return ntvfs->ops->mkdir(ntvfs, req, md);
|
||||
}
|
||||
|
||||
_PUBLIC_ NTSTATUS ntvfs_rmdir(struct ntvfs_request *req, struct smb_rmdir *rd)
|
||||
{
|
||||
struct ntvfs_module_context *ntvfs = req->ctx->modules;
|
||||
if (!ntvfs->ops->rmdir) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
return ntvfs->ops->rmdir(ntvfs, req, rd);
|
||||
}
|
||||
|
||||
_PUBLIC_ NTSTATUS ntvfs_rename(struct ntvfs_request *req, union smb_rename *ren)
|
||||
{
|
||||
struct ntvfs_module_context *ntvfs = req->ctx->modules;
|
||||
if (!ntvfs->ops->rename) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
return ntvfs->ops->rename(ntvfs, req, ren);
|
||||
}
|
||||
|
||||
_PUBLIC_ NTSTATUS ntvfs_copy(struct ntvfs_request *req, struct smb_copy *cp)
|
||||
{
|
||||
struct ntvfs_module_context *ntvfs = req->ctx->modules;
|
||||
if (!ntvfs->ops->copy) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
return ntvfs->ops->copy(ntvfs, req, cp);
|
||||
}
|
||||
|
||||
/* directory search */
|
||||
_PUBLIC_ NTSTATUS ntvfs_search_first(struct ntvfs_request *req, union smb_search_first *io, void *private,
|
||||
BOOL ntvfs_callback(void *private, union smb_search_data *file))
|
||||
{
|
||||
struct ntvfs_module_context *ntvfs = req->ctx->modules;
|
||||
if (!ntvfs->ops->search_first) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
return ntvfs->ops->search_first(ntvfs, req, io, private, ntvfs_callback);
|
||||
}
|
||||
|
||||
_PUBLIC_ NTSTATUS ntvfs_search_next(struct ntvfs_request *req, union smb_search_next *io, void *private,
|
||||
BOOL ntvfs_callback(void *private, union smb_search_data *file))
|
||||
{
|
||||
struct ntvfs_module_context *ntvfs = req->ctx->modules;
|
||||
if (!ntvfs->ops->search_next) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
return ntvfs->ops->search_next(ntvfs, req, io, private, ntvfs_callback);
|
||||
}
|
||||
|
||||
_PUBLIC_ NTSTATUS ntvfs_search_close(struct ntvfs_request *req, union smb_search_close *io)
|
||||
{
|
||||
struct ntvfs_module_context *ntvfs = req->ctx->modules;
|
||||
if (!ntvfs->ops->search_close) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
return ntvfs->ops->search_close(ntvfs, req, io);
|
||||
}
|
||||
|
||||
/* operations on open files */
|
||||
_PUBLIC_ NTSTATUS ntvfs_ioctl(struct ntvfs_request *req, union smb_ioctl *io)
|
||||
{
|
||||
struct ntvfs_module_context *ntvfs = req->ctx->modules;
|
||||
if (!ntvfs->ops->ioctl) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
return ntvfs->ops->ioctl(ntvfs, req, io);
|
||||
}
|
||||
|
||||
_PUBLIC_ NTSTATUS ntvfs_read(struct ntvfs_request *req, union smb_read *io)
|
||||
{
|
||||
struct ntvfs_module_context *ntvfs = req->ctx->modules;
|
||||
if (!ntvfs->ops->read) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
return ntvfs->ops->read(ntvfs, req, io);
|
||||
}
|
||||
|
||||
_PUBLIC_ NTSTATUS ntvfs_write(struct ntvfs_request *req, union smb_write *io)
|
||||
{
|
||||
struct ntvfs_module_context *ntvfs = req->ctx->modules;
|
||||
if (!ntvfs->ops->write) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
return ntvfs->ops->write(ntvfs, req, io);
|
||||
}
|
||||
|
||||
_PUBLIC_ NTSTATUS ntvfs_seek(struct ntvfs_request *req, union smb_seek *io)
|
||||
{
|
||||
struct ntvfs_module_context *ntvfs = req->ctx->modules;
|
||||
if (!ntvfs->ops->seek) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
return ntvfs->ops->seek(ntvfs, req, io);
|
||||
}
|
||||
|
||||
_PUBLIC_ NTSTATUS ntvfs_flush(struct ntvfs_request *req,
|
||||
union smb_flush *flush)
|
||||
{
|
||||
struct ntvfs_module_context *ntvfs = req->ctx->modules;
|
||||
if (!ntvfs->ops->flush) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
return ntvfs->ops->flush(ntvfs, req, flush);
|
||||
}
|
||||
|
||||
_PUBLIC_ NTSTATUS ntvfs_lock(struct ntvfs_request *req, union smb_lock *lck)
|
||||
{
|
||||
struct ntvfs_module_context *ntvfs = req->ctx->modules;
|
||||
if (!ntvfs->ops->lock) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
return ntvfs->ops->lock(ntvfs, req, lck);
|
||||
}
|
||||
|
||||
_PUBLIC_ NTSTATUS ntvfs_qfileinfo(struct ntvfs_request *req, union smb_fileinfo *info)
|
||||
{
|
||||
struct ntvfs_module_context *ntvfs = req->ctx->modules;
|
||||
if (!ntvfs->ops->qfileinfo) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
return ntvfs->ops->qfileinfo(ntvfs, req, info);
|
||||
}
|
||||
|
||||
_PUBLIC_ NTSTATUS ntvfs_setfileinfo(struct ntvfs_request *req, union smb_setfileinfo *info)
|
||||
{
|
||||
struct ntvfs_module_context *ntvfs = req->ctx->modules;
|
||||
if (!ntvfs->ops->setfileinfo) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
return ntvfs->ops->setfileinfo(ntvfs, req, info);
|
||||
}
|
||||
|
||||
_PUBLIC_ NTSTATUS ntvfs_close(struct ntvfs_request *req, union smb_close *io)
|
||||
{
|
||||
struct ntvfs_module_context *ntvfs = req->ctx->modules;
|
||||
if (!ntvfs->ops->close) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
return ntvfs->ops->close(ntvfs, req, io);
|
||||
}
|
||||
|
||||
/* trans interface - used by IPC backend for pipes and RAP calls */
|
||||
_PUBLIC_ NTSTATUS ntvfs_trans(struct ntvfs_request *req, struct smb_trans2 *trans)
|
||||
{
|
||||
struct ntvfs_module_context *ntvfs = req->ctx->modules;
|
||||
if (!ntvfs->ops->trans) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
return ntvfs->ops->trans(ntvfs, req, trans);
|
||||
}
|
||||
|
||||
/* trans2 interface - only used by CIFS backend to prover complete passthru for testing */
|
||||
_PUBLIC_ NTSTATUS ntvfs_trans2(struct ntvfs_request *req, struct smb_trans2 *trans2)
|
||||
{
|
||||
struct ntvfs_module_context *ntvfs = req->ctx->modules;
|
||||
if (!ntvfs->ops->trans2) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
return ntvfs->ops->trans2(ntvfs, req, trans2);
|
||||
}
|
||||
|
||||
/* printing specific operations */
|
||||
_PUBLIC_ NTSTATUS ntvfs_lpq(struct ntvfs_request *req, union smb_lpq *lpq)
|
||||
{
|
||||
struct ntvfs_module_context *ntvfs = req->ctx->modules;
|
||||
if (!ntvfs->ops->lpq) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
return ntvfs->ops->lpq(ntvfs, req, lpq);
|
||||
}
|
||||
|
||||
/* logoff - called when a vuid is closed */
|
||||
_PUBLIC_ NTSTATUS ntvfs_logoff(struct ntvfs_request *req)
|
||||
{
|
||||
struct ntvfs_module_context *ntvfs = req->ctx->modules;
|
||||
if (!ntvfs->ops->logoff) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
return ntvfs->ops->logoff(ntvfs, req);
|
||||
}
|
||||
|
||||
_PUBLIC_ NTSTATUS ntvfs_exit(struct ntvfs_request *req)
|
||||
{
|
||||
struct ntvfs_module_context *ntvfs = req->ctx->modules;
|
||||
if (!ntvfs->ops->exit) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
return ntvfs->ops->exit(ntvfs, req);
|
||||
}
|
||||
|
||||
/*
|
||||
change notify request
|
||||
*/
|
||||
_PUBLIC_ NTSTATUS ntvfs_notify(struct ntvfs_request *req, union smb_notify *info)
|
||||
{
|
||||
struct ntvfs_module_context *ntvfs = req->ctx->modules;
|
||||
if (!ntvfs->ops->notify) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
return ntvfs->ops->notify(ntvfs, req, info);
|
||||
}
|
||||
|
||||
/*
|
||||
cancel an outstanding async request
|
||||
*/
|
||||
_PUBLIC_ NTSTATUS ntvfs_cancel(struct ntvfs_request *req)
|
||||
{
|
||||
struct ntvfs_module_context *ntvfs = req->ctx->modules;
|
||||
if (!ntvfs->ops->cancel) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
return ntvfs->ops->cancel(ntvfs, req);
|
||||
}
|
||||
|
||||
/* initial setup */
|
||||
_PUBLIC_ NTSTATUS ntvfs_next_connect(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, const char *sharename)
|
||||
{
|
||||
if (!ntvfs->next || !ntvfs->next->ops->connect) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
return ntvfs->next->ops->connect(ntvfs->next, req, sharename);
|
||||
}
|
||||
|
||||
_PUBLIC_ NTSTATUS ntvfs_next_disconnect(struct ntvfs_module_context *ntvfs)
|
||||
{
|
||||
if (!ntvfs->next || !ntvfs->next->ops->disconnect) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
return ntvfs->next->ops->disconnect(ntvfs->next);
|
||||
}
|
||||
|
||||
/* async_setup - called when setting up for a async request */
|
||||
_PUBLIC_ NTSTATUS ntvfs_next_async_setup(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
void *private)
|
||||
{
|
||||
if (!ntvfs->next || !ntvfs->next->ops->async_setup) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
return ntvfs->next->ops->async_setup(ntvfs->next, req, private);
|
||||
}
|
||||
|
||||
/* filesystem operations */
|
||||
_PUBLIC_ NTSTATUS ntvfs_next_fsinfo(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
union smb_fsinfo *fs)
|
||||
{
|
||||
if (!ntvfs->next || !ntvfs->next->ops->fsinfo) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
return ntvfs->next->ops->fsinfo(ntvfs->next, req, fs);
|
||||
}
|
||||
|
||||
/* path operations */
|
||||
_PUBLIC_ NTSTATUS ntvfs_next_unlink(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
union smb_unlink *unl)
|
||||
{
|
||||
if (!ntvfs->next || !ntvfs->next->ops->unlink) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
return ntvfs->next->ops->unlink(ntvfs->next, req, unl);
|
||||
}
|
||||
|
||||
_PUBLIC_ NTSTATUS ntvfs_next_chkpath(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
union smb_chkpath *cp)
|
||||
{
|
||||
if (!ntvfs->next || !ntvfs->next->ops->chkpath) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
return ntvfs->next->ops->chkpath(ntvfs->next, req, cp);
|
||||
}
|
||||
|
||||
_PUBLIC_ NTSTATUS ntvfs_next_qpathinfo(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
union smb_fileinfo *st)
|
||||
{
|
||||
if (!ntvfs->next || !ntvfs->next->ops->qpathinfo) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
return ntvfs->next->ops->qpathinfo(ntvfs->next, req, st);
|
||||
}
|
||||
|
||||
_PUBLIC_ NTSTATUS ntvfs_next_setpathinfo(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
union smb_setfileinfo *st)
|
||||
{
|
||||
if (!ntvfs->next || !ntvfs->next->ops->setpathinfo) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
return ntvfs->next->ops->setpathinfo(ntvfs->next, req, st);
|
||||
}
|
||||
|
||||
_PUBLIC_ NTSTATUS ntvfs_next_mkdir(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
union smb_mkdir *md)
|
||||
{
|
||||
if (!ntvfs->next || !ntvfs->next->ops->mkdir) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
return ntvfs->next->ops->mkdir(ntvfs->next, req, md);
|
||||
}
|
||||
|
||||
_PUBLIC_ NTSTATUS ntvfs_next_rmdir(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
struct smb_rmdir *rd)
|
||||
{
|
||||
if (!ntvfs->next || !ntvfs->next->ops->rmdir) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
return ntvfs->next->ops->rmdir(ntvfs->next, req, rd);
|
||||
}
|
||||
|
||||
_PUBLIC_ NTSTATUS ntvfs_next_rename(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
union smb_rename *ren)
|
||||
{
|
||||
if (!ntvfs->next || !ntvfs->next->ops->rename) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
return ntvfs->next->ops->rename(ntvfs->next, req, ren);
|
||||
}
|
||||
|
||||
_PUBLIC_ NTSTATUS ntvfs_next_copy(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
struct smb_copy *cp)
|
||||
{
|
||||
if (!ntvfs->next || !ntvfs->next->ops->copy) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
return ntvfs->next->ops->copy(ntvfs->next, req, cp);
|
||||
}
|
||||
|
||||
_PUBLIC_ NTSTATUS ntvfs_next_open(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
union smb_open *oi)
|
||||
{
|
||||
if (!ntvfs->next || !ntvfs->next->ops->open) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
return ntvfs->next->ops->open(ntvfs->next, req, oi);
|
||||
}
|
||||
|
||||
|
||||
/* directory search */
|
||||
_PUBLIC_ NTSTATUS ntvfs_next_search_first(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
union smb_search_first *io, void *private,
|
||||
BOOL (*callback)(void *private, union smb_search_data *file))
|
||||
{
|
||||
if (!ntvfs->next || !ntvfs->next->ops->search_first) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
return ntvfs->next->ops->search_first(ntvfs->next, req, io, private, callback);
|
||||
}
|
||||
|
||||
_PUBLIC_ NTSTATUS ntvfs_next_search_next(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
union smb_search_next *io, void *private,
|
||||
BOOL (*callback)(void *private, union smb_search_data *file))
|
||||
{
|
||||
if (!ntvfs->next || !ntvfs->next->ops->search_next) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
return ntvfs->next->ops->search_next(ntvfs->next, req, io, private, callback);
|
||||
}
|
||||
|
||||
_PUBLIC_ NTSTATUS ntvfs_next_search_close(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
union smb_search_close *io)
|
||||
{
|
||||
if (!ntvfs->next || !ntvfs->next->ops->search_close) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
return ntvfs->next->ops->search_close(ntvfs->next, req, io);
|
||||
}
|
||||
|
||||
/* operations on open files */
|
||||
_PUBLIC_ NTSTATUS ntvfs_next_ioctl(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
union smb_ioctl *io)
|
||||
{
|
||||
if (!ntvfs->next || !ntvfs->next->ops->ioctl) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
return ntvfs->next->ops->ioctl(ntvfs->next, req, io);
|
||||
}
|
||||
|
||||
_PUBLIC_ NTSTATUS ntvfs_next_read(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
union smb_read *io)
|
||||
{
|
||||
if (!ntvfs->next || !ntvfs->next->ops->read) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
return ntvfs->next->ops->read(ntvfs->next, req, io);
|
||||
}
|
||||
|
||||
_PUBLIC_ NTSTATUS ntvfs_next_write(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
union smb_write *io)
|
||||
{
|
||||
if (!ntvfs->next || !ntvfs->next->ops->write) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
return ntvfs->next->ops->write(ntvfs->next, req, io);
|
||||
}
|
||||
|
||||
_PUBLIC_ NTSTATUS ntvfs_next_seek(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
union smb_seek *io)
|
||||
{
|
||||
if (!ntvfs->next || !ntvfs->next->ops->seek) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
return ntvfs->next->ops->seek(ntvfs->next, req, io);
|
||||
}
|
||||
|
||||
_PUBLIC_ NTSTATUS ntvfs_next_flush(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
union smb_flush *flush)
|
||||
{
|
||||
if (!ntvfs->next || !ntvfs->next->ops->flush) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
return ntvfs->next->ops->flush(ntvfs->next, req, flush);
|
||||
}
|
||||
|
||||
_PUBLIC_ NTSTATUS ntvfs_next_lock(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
union smb_lock *lck)
|
||||
{
|
||||
if (!ntvfs->next || !ntvfs->next->ops->lock) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
return ntvfs->next->ops->lock(ntvfs->next, req, lck);
|
||||
}
|
||||
|
||||
_PUBLIC_ NTSTATUS ntvfs_next_qfileinfo(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
union smb_fileinfo *info)
|
||||
{
|
||||
if (!ntvfs->next || !ntvfs->next->ops->qfileinfo) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
return ntvfs->next->ops->qfileinfo(ntvfs->next, req, info);
|
||||
}
|
||||
|
||||
_PUBLIC_ NTSTATUS ntvfs_next_setfileinfo(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
union smb_setfileinfo *info)
|
||||
{
|
||||
if (!ntvfs->next || !ntvfs->next->ops->setfileinfo) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
return ntvfs->next->ops->setfileinfo(ntvfs->next, req, info);
|
||||
}
|
||||
|
||||
_PUBLIC_ NTSTATUS ntvfs_next_close(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
union smb_close *io)
|
||||
{
|
||||
if (!ntvfs->next || !ntvfs->next->ops->close) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
return ntvfs->next->ops->close(ntvfs->next, req, io);
|
||||
}
|
||||
|
||||
/* trans interface - used by IPC backend for pipes and RAP calls */
|
||||
_PUBLIC_ NTSTATUS ntvfs_next_trans(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
struct smb_trans2 *trans)
|
||||
{
|
||||
if (!ntvfs->next || !ntvfs->next->ops->trans) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
return ntvfs->next->ops->trans(ntvfs->next, req, trans);
|
||||
}
|
||||
|
||||
/* trans2 interface - only used by CIFS backend to prover complete passthru for testing */
|
||||
_PUBLIC_ NTSTATUS ntvfs_next_trans2(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
struct smb_trans2 *trans2)
|
||||
{
|
||||
if (!ntvfs->next || !ntvfs->next->ops->trans2) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
return ntvfs->next->ops->trans2(ntvfs->next, req, trans2);
|
||||
}
|
||||
|
||||
/*
|
||||
change notify request
|
||||
*/
|
||||
_PUBLIC_ NTSTATUS ntvfs_next_notify(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
union smb_notify *info)
|
||||
{
|
||||
if (!ntvfs->next || !ntvfs->next->ops->notify) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
return ntvfs->next->ops->notify(ntvfs->next, req, info);
|
||||
}
|
||||
|
||||
/* cancel - called to cancel an outstanding async request */
|
||||
_PUBLIC_ NTSTATUS ntvfs_next_cancel(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req)
|
||||
{
|
||||
if (!ntvfs->next || !ntvfs->next->ops->cancel) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
return ntvfs->next->ops->cancel(ntvfs->next, req);
|
||||
}
|
||||
|
||||
/* printing specific operations */
|
||||
_PUBLIC_ NTSTATUS ntvfs_next_lpq(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
union smb_lpq *lpq)
|
||||
{
|
||||
if (!ntvfs->next || !ntvfs->next->ops->lpq) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
return ntvfs->next->ops->lpq(ntvfs->next, req, lpq);
|
||||
}
|
||||
|
||||
|
||||
/* logoff - called when a vuid is closed */
|
||||
_PUBLIC_ NTSTATUS ntvfs_next_logoff(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req)
|
||||
{
|
||||
if (!ntvfs->next || !ntvfs->next->ops->logoff) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
return ntvfs->next->ops->logoff(ntvfs->next, req);
|
||||
}
|
||||
|
||||
_PUBLIC_ NTSTATUS ntvfs_next_exit(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req)
|
||||
{
|
||||
if (!ntvfs->next || !ntvfs->next->ops->exit) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
return ntvfs->next->ops->exit(ntvfs->next, req);
|
||||
}
|
||||
|
||||
/* oplock helpers */
|
||||
_PUBLIC_ NTSTATUS ntvfs_set_oplock_handler(struct ntvfs_context *ntvfs,
|
||||
NTSTATUS (*handler)(void *private_data, struct ntvfs_handle *handle, uint8_t level),
|
||||
void *private_data)
|
||||
{
|
||||
ntvfs->oplock.handler = handler;
|
||||
ntvfs->oplock.private_data = private_data;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
_PUBLIC_ NTSTATUS ntvfs_send_oplock_break(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_handle *handle, uint8_t level)
|
||||
{
|
||||
if (!ntvfs->ctx->oplock.handler) {
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
return ntvfs->ctx->oplock.handler(ntvfs->ctx->oplock.private_data, handle, level);
|
||||
}
|
||||
|
||||
/* client connection callback */
|
||||
_PUBLIC_ NTSTATUS ntvfs_set_addr_callbacks(struct ntvfs_context *ntvfs,
|
||||
struct socket_address *(*my_addr)(void *private_data, TALLOC_CTX *mem_ctx),
|
||||
struct socket_address *(*peer_addr)(void *private_data, TALLOC_CTX *mem_ctx),
|
||||
void *private_data)
|
||||
{
|
||||
ntvfs->client.get_peer_addr = my_addr;
|
||||
ntvfs->client.get_my_addr = peer_addr;
|
||||
ntvfs->client.private_data = private_data;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
_PUBLIC_ struct socket_address *ntvfs_get_my_addr(struct ntvfs_module_context *ntvfs, TALLOC_CTX *mem_ctx)
|
||||
{
|
||||
if (!ntvfs->ctx->client.get_my_addr) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return ntvfs->ctx->client.get_my_addr(ntvfs->ctx->client.private_data, mem_ctx);
|
||||
}
|
||||
|
||||
_PUBLIC_ struct socket_address *ntvfs_get_peer_addr(struct ntvfs_module_context *ntvfs, TALLOC_CTX *mem_ctx)
|
||||
{
|
||||
if (!ntvfs->ctx->client.get_peer_addr) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return ntvfs->ctx->client.get_peer_addr(ntvfs->ctx->client.private_data, mem_ctx);
|
||||
}
|
||||
@@ -0,0 +1,202 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
NTVFS utility code
|
||||
Copyright (C) Stefan Metzmacher 2004
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 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"
|
||||
#include "lib/util/dlinklist.h"
|
||||
#include "ntvfs/ntvfs.h"
|
||||
|
||||
|
||||
_PUBLIC_ struct ntvfs_request *ntvfs_request_create(struct ntvfs_context *ctx, TALLOC_CTX *mem_ctx,
|
||||
struct auth_session_info *session_info,
|
||||
uint16_t smbpid,
|
||||
struct timeval request_time,
|
||||
void *private_data,
|
||||
void (*send_fn)(struct ntvfs_request *),
|
||||
uint32_t state)
|
||||
{
|
||||
struct ntvfs_request *req;
|
||||
struct ntvfs_async_state *async;
|
||||
|
||||
req = talloc(mem_ctx, struct ntvfs_request);
|
||||
if (!req) return NULL;
|
||||
req->ctx = ctx;
|
||||
req->async_states = NULL;
|
||||
req->session_info = session_info;
|
||||
req->smbpid = smbpid;
|
||||
req->statistics.request_time = request_time;
|
||||
|
||||
async = talloc(req, struct ntvfs_async_state);
|
||||
if (!async) goto failed;
|
||||
|
||||
async->state = state;
|
||||
async->private_data = private_data;
|
||||
async->send_fn = send_fn;
|
||||
async->status = NT_STATUS_INTERNAL_ERROR;
|
||||
async->ntvfs = NULL;
|
||||
|
||||
DLIST_ADD(req->async_states, async);
|
||||
|
||||
return req;
|
||||
failed:
|
||||
talloc_free(req);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
_PUBLIC_ NTSTATUS ntvfs_async_state_push(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
void *private_data,
|
||||
void (*send_fn)(struct ntvfs_request *))
|
||||
{
|
||||
struct ntvfs_async_state *async;
|
||||
|
||||
async = talloc(req, struct ntvfs_async_state);
|
||||
NT_STATUS_HAVE_NO_MEMORY(async);
|
||||
|
||||
async->state = req->async_states->state;
|
||||
async->private_data = private_data;
|
||||
async->send_fn = send_fn;
|
||||
async->status = NT_STATUS_INTERNAL_ERROR;
|
||||
|
||||
async->ntvfs = ntvfs;
|
||||
|
||||
DLIST_ADD(req->async_states, async);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
_PUBLIC_ void ntvfs_async_state_pop(struct ntvfs_request *req)
|
||||
{
|
||||
struct ntvfs_async_state *async;
|
||||
|
||||
async = req->async_states;
|
||||
|
||||
DLIST_REMOVE(req->async_states, async);
|
||||
|
||||
req->async_states->state = async->state;
|
||||
req->async_states->status = async->status;
|
||||
|
||||
talloc_free(async);
|
||||
}
|
||||
|
||||
_PUBLIC_ NTSTATUS ntvfs_handle_new(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
struct ntvfs_handle **h)
|
||||
{
|
||||
if (!ntvfs->ctx->handles.create_new) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
return ntvfs->ctx->handles.create_new(ntvfs->ctx->handles.private_data, req, h);
|
||||
}
|
||||
|
||||
_PUBLIC_ NTSTATUS ntvfs_handle_set_backend_data(struct ntvfs_handle *h,
|
||||
struct ntvfs_module_context *ntvfs,
|
||||
TALLOC_CTX *private_data)
|
||||
{
|
||||
struct ntvfs_handle_data *d;
|
||||
BOOL first_time = h->backend_data?False:True;
|
||||
|
||||
for (d=h->backend_data; d; d = d->next) {
|
||||
if (d->owner != ntvfs) continue;
|
||||
d->private_data = talloc_steal(d, private_data);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
d = talloc(h, struct ntvfs_handle_data);
|
||||
NT_STATUS_HAVE_NO_MEMORY(d);
|
||||
d->owner = ntvfs;
|
||||
d->private_data = talloc_steal(d, private_data);
|
||||
|
||||
DLIST_ADD(h->backend_data, d);
|
||||
|
||||
if (first_time) {
|
||||
NTSTATUS status;
|
||||
status = h->ctx->handles.make_valid(h->ctx->handles.private_data, h);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
_PUBLIC_ void *ntvfs_handle_get_backend_data(struct ntvfs_handle *h,
|
||||
struct ntvfs_module_context *ntvfs)
|
||||
{
|
||||
struct ntvfs_handle_data *d;
|
||||
|
||||
for (d=h->backend_data; d; d = d->next) {
|
||||
if (d->owner != ntvfs) continue;
|
||||
return d->private_data;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
_PUBLIC_ void ntvfs_handle_remove_backend_data(struct ntvfs_handle *h,
|
||||
struct ntvfs_module_context *ntvfs)
|
||||
{
|
||||
struct ntvfs_handle_data *d,*n;
|
||||
|
||||
for (d=h->backend_data; d; d = n) {
|
||||
n = d->next;
|
||||
if (d->owner != ntvfs) continue;
|
||||
DLIST_REMOVE(h->backend_data, d);
|
||||
talloc_free(d);
|
||||
d = NULL;
|
||||
}
|
||||
|
||||
if (h->backend_data) return;
|
||||
|
||||
/* if there's no backend_data anymore, destroy the handle */
|
||||
h->ctx->handles.destroy(h->ctx->handles.private_data, h);
|
||||
}
|
||||
|
||||
_PUBLIC_ struct ntvfs_handle *ntvfs_handle_search_by_wire_key(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
const DATA_BLOB *key)
|
||||
{
|
||||
if (!ntvfs->ctx->handles.search_by_wire_key) {
|
||||
return NULL;
|
||||
}
|
||||
return ntvfs->ctx->handles.search_by_wire_key(ntvfs->ctx->handles.private_data, req, key);
|
||||
}
|
||||
|
||||
_PUBLIC_ DATA_BLOB ntvfs_handle_get_wire_key(struct ntvfs_handle *h, TALLOC_CTX *mem_ctx)
|
||||
{
|
||||
return h->ctx->handles.get_wire_key(h->ctx->handles.private_data, h, mem_ctx);
|
||||
}
|
||||
|
||||
_PUBLIC_ NTSTATUS ntvfs_set_handle_callbacks(struct ntvfs_context *ntvfs,
|
||||
NTSTATUS (*create_new)(void *private_data, struct ntvfs_request *req, struct ntvfs_handle **h),
|
||||
NTSTATUS (*make_valid)(void *private_data, struct ntvfs_handle *h),
|
||||
void (*destroy)(void *private_data, struct ntvfs_handle *h),
|
||||
struct ntvfs_handle *(*search_by_wire_key)(void *private_data, struct ntvfs_request *req, const DATA_BLOB *key),
|
||||
DATA_BLOB (*get_wire_key)(void *private_data, struct ntvfs_handle *handle, TALLOC_CTX *mem_ctx),
|
||||
void *private_data)
|
||||
{
|
||||
ntvfs->handles.create_new = create_new;
|
||||
ntvfs->handles.make_valid = make_valid;
|
||||
ntvfs->handles.destroy = destroy;
|
||||
ntvfs->handles.search_by_wire_key = search_by_wire_key;
|
||||
ntvfs->handles.get_wire_key = get_wire_key;
|
||||
ntvfs->handles.private_data = private_data;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
|
||||
|
||||
dnl #############################################
|
||||
dnl see if we have nanosecond resolution for stat
|
||||
AC_CACHE_CHECK([for tv_nsec nanosecond fields in struct stat],ac_cv_have_stat_tv_nsec,[
|
||||
AC_TRY_COMPILE(
|
||||
[
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
],
|
||||
[struct stat st;
|
||||
st.st_mtim.tv_nsec;
|
||||
st.st_atim.tv_nsec;
|
||||
st.st_ctim.tv_nsec;
|
||||
],
|
||||
ac_cv_decl_have_stat_tv_nsec=yes,
|
||||
ac_cv_decl_have_stat_tv_nsec=no)
|
||||
])
|
||||
if test x"$ac_cv_decl_have_stat_tv_nsec" = x"yes"; then
|
||||
AC_DEFINE(HAVE_STAT_TV_NSEC,1,[Whether stat has tv_nsec nanosecond fields])
|
||||
fi
|
||||
|
||||
AC_CHECK_HEADERS(blkid/blkid.h)
|
||||
AC_SEARCH_LIBS_EXT(blkid_get_cache, [blkid], BLKID_LIBS)
|
||||
AC_CHECK_FUNC_EXT(blkid_get_cache, $BLKID_LIBS)
|
||||
SMB_EXT_LIB(BLKID,[${BLKID_LIBS}],[${BLKID_CFLAGS}],[${BLKID_CPPFLAGS}],[${BLKID_LDFLAGS}])
|
||||
if test x"$ac_cv_func_ext_blkid_get_cache" = x"yes"; then
|
||||
AC_DEFINE(HAVE_LIBBLKID,1,[Whether we have blkid support (e2fsprogs)])
|
||||
SMB_ENABLE(BLKID,YES)
|
||||
fi
|
||||
@@ -0,0 +1,61 @@
|
||||
################################################
|
||||
# Start MODULE pvfs_acl_xattr
|
||||
[MODULE::pvfs_acl_xattr]
|
||||
INIT_FUNCTION = pvfs_acl_xattr_init
|
||||
SUBSYSTEM = ntvfs
|
||||
OBJ_FILES = \
|
||||
pvfs_acl_xattr.o
|
||||
PRIVATE_DEPENDENCIES = NDR_XATTR ntvfs_posix
|
||||
# End MODULE pvfs_acl_xattr
|
||||
################################################
|
||||
|
||||
################################################
|
||||
# Start MODULE pvfs_acl_nfs4
|
||||
[MODULE::pvfs_acl_nfs4]
|
||||
INIT_FUNCTION = pvfs_acl_nfs4_init
|
||||
SUBSYSTEM = ntvfs
|
||||
OBJ_FILES = \
|
||||
pvfs_acl_nfs4.o
|
||||
PRIVATE_DEPENDENCIES = NDR_NFS4ACL SAMDB ntvfs_posix
|
||||
# End MODULE pvfs_acl_nfs4
|
||||
################################################
|
||||
|
||||
################################################
|
||||
# Start MODULE ntvfs_posix
|
||||
[MODULE::ntvfs_posix]
|
||||
SUBSYSTEM = ntvfs
|
||||
OUTPUT_TYPE = INTEGRATED
|
||||
INIT_FUNCTION = ntvfs_posix_init
|
||||
PRIVATE_PROTO_HEADER = vfs_posix_proto.h
|
||||
OBJ_FILES = \
|
||||
vfs_posix.o \
|
||||
pvfs_util.o \
|
||||
pvfs_search.o \
|
||||
pvfs_dirlist.o \
|
||||
pvfs_fileinfo.o \
|
||||
pvfs_unlink.o \
|
||||
pvfs_mkdir.o \
|
||||
pvfs_open.o \
|
||||
pvfs_read.o \
|
||||
pvfs_flush.o \
|
||||
pvfs_write.o \
|
||||
pvfs_fsinfo.o \
|
||||
pvfs_qfileinfo.o \
|
||||
pvfs_setfileinfo.o \
|
||||
pvfs_rename.o \
|
||||
pvfs_resolve.o \
|
||||
pvfs_shortname.o \
|
||||
pvfs_lock.o \
|
||||
pvfs_wait.o \
|
||||
pvfs_seek.o \
|
||||
pvfs_ioctl.o \
|
||||
pvfs_xattr.o \
|
||||
pvfs_streams.o \
|
||||
pvfs_acl.o \
|
||||
pvfs_notify.o \
|
||||
xattr_system.o \
|
||||
xattr_tdb.o
|
||||
#PRIVATE_DEPENDENCIES = pvfs_acl_xattr pvfs_acl_nfs4
|
||||
PUBLIC_DEPENDENCIES = NDR_XATTR WRAP_XATTR BLKID ntvfs_common MESSAGING
|
||||
# End MODULE ntvfs_posix
|
||||
################################################
|
||||
@@ -0,0 +1,739 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
POSIX NTVFS backend - ACL support
|
||||
|
||||
Copyright (C) Andrew Tridgell 2004
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 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.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "auth/auth.h"
|
||||
#include "vfs_posix.h"
|
||||
#include "librpc/gen_ndr/xattr.h"
|
||||
#include "libcli/security/security.h"
|
||||
|
||||
|
||||
/* the list of currently registered ACL backends */
|
||||
static struct pvfs_acl_backend {
|
||||
const struct pvfs_acl_ops *ops;
|
||||
} *backends = NULL;
|
||||
static int num_backends;
|
||||
|
||||
/*
|
||||
register a pvfs acl backend.
|
||||
|
||||
The 'name' can be later used by other backends to find the operations
|
||||
structure for this backend.
|
||||
*/
|
||||
_PUBLIC_ NTSTATUS pvfs_acl_register(const struct pvfs_acl_ops *ops)
|
||||
{
|
||||
struct pvfs_acl_ops *new_ops;
|
||||
|
||||
if (pvfs_acl_backend_byname(ops->name) != NULL) {
|
||||
DEBUG(0,("pvfs acl backend '%s' already registered\n", ops->name));
|
||||
return NT_STATUS_OBJECT_NAME_COLLISION;
|
||||
}
|
||||
|
||||
backends = talloc_realloc(talloc_autofree_context(), backends, struct pvfs_acl_backend, num_backends+1);
|
||||
NT_STATUS_HAVE_NO_MEMORY(backends);
|
||||
|
||||
new_ops = talloc_memdup(backends, ops, sizeof(*ops));
|
||||
new_ops->name = talloc_strdup(new_ops, ops->name);
|
||||
|
||||
backends[num_backends].ops = new_ops;
|
||||
|
||||
num_backends++;
|
||||
|
||||
DEBUG(3,("NTVFS backend '%s' registered\n", ops->name));
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
return the operations structure for a named backend
|
||||
*/
|
||||
_PUBLIC_ const struct pvfs_acl_ops *pvfs_acl_backend_byname(const char *name)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0;i<num_backends;i++) {
|
||||
if (strcmp(backends[i].ops->name, name) == 0) {
|
||||
return backends[i].ops;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
map a single access_mask from generic to specific bits for files/dirs
|
||||
*/
|
||||
static uint32_t pvfs_translate_mask(uint32_t access_mask)
|
||||
{
|
||||
if (access_mask & SEC_MASK_GENERIC) {
|
||||
if (access_mask & SEC_GENERIC_READ) access_mask |= SEC_RIGHTS_FILE_READ;
|
||||
if (access_mask & SEC_GENERIC_WRITE) access_mask |= SEC_RIGHTS_FILE_WRITE;
|
||||
if (access_mask & SEC_GENERIC_EXECUTE) access_mask |= SEC_RIGHTS_FILE_EXECUTE;
|
||||
if (access_mask & SEC_GENERIC_ALL) access_mask |= SEC_RIGHTS_FILE_ALL;
|
||||
access_mask &= ~SEC_MASK_GENERIC;
|
||||
}
|
||||
return access_mask;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
map any generic access bits in the given acl
|
||||
this relies on the fact that the mappings for files and directories
|
||||
are the same
|
||||
*/
|
||||
static void pvfs_translate_generic_bits(struct security_acl *acl)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
if (!acl) return;
|
||||
|
||||
for (i=0;i<acl->num_aces;i++) {
|
||||
struct security_ace *ace = &acl->aces[i];
|
||||
ace->access_mask = pvfs_translate_mask(ace->access_mask);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
setup a default ACL for a file
|
||||
*/
|
||||
static NTSTATUS pvfs_default_acl(struct pvfs_state *pvfs,
|
||||
struct ntvfs_request *req,
|
||||
struct pvfs_filename *name, int fd,
|
||||
struct security_descriptor **psd)
|
||||
{
|
||||
struct security_descriptor *sd;
|
||||
NTSTATUS status;
|
||||
struct security_ace ace;
|
||||
mode_t mode;
|
||||
|
||||
*psd = security_descriptor_initialise(req);
|
||||
if (*psd == NULL) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
sd = *psd;
|
||||
|
||||
status = sidmap_uid_to_sid(pvfs->sidmap, sd, name->st.st_uid, &sd->owner_sid);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
status = sidmap_gid_to_sid(pvfs->sidmap, sd, name->st.st_gid, &sd->group_sid);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
sd->type |= SEC_DESC_DACL_PRESENT;
|
||||
|
||||
mode = name->st.st_mode;
|
||||
|
||||
/*
|
||||
we provide up to 4 ACEs
|
||||
- Owner
|
||||
- Group
|
||||
- Everyone
|
||||
- Administrator
|
||||
*/
|
||||
|
||||
|
||||
/* setup owner ACE */
|
||||
ace.type = SEC_ACE_TYPE_ACCESS_ALLOWED;
|
||||
ace.flags = 0;
|
||||
ace.trustee = *sd->owner_sid;
|
||||
ace.access_mask = 0;
|
||||
|
||||
if (mode & S_IRUSR) {
|
||||
if (mode & S_IWUSR) {
|
||||
ace.access_mask |= SEC_RIGHTS_FILE_ALL;
|
||||
} else {
|
||||
ace.access_mask |= SEC_RIGHTS_FILE_READ | SEC_FILE_EXECUTE;
|
||||
}
|
||||
}
|
||||
if (mode & S_IWUSR) {
|
||||
ace.access_mask |= SEC_RIGHTS_FILE_WRITE | SEC_STD_DELETE;
|
||||
}
|
||||
if (ace.access_mask) {
|
||||
security_descriptor_dacl_add(sd, &ace);
|
||||
}
|
||||
|
||||
|
||||
/* setup group ACE */
|
||||
ace.trustee = *sd->group_sid;
|
||||
ace.access_mask = 0;
|
||||
if (mode & S_IRGRP) {
|
||||
ace.access_mask |= SEC_RIGHTS_FILE_READ | SEC_FILE_EXECUTE;
|
||||
}
|
||||
if (mode & S_IWGRP) {
|
||||
/* note that delete is not granted - this matches posix behaviour */
|
||||
ace.access_mask |= SEC_RIGHTS_FILE_WRITE;
|
||||
}
|
||||
if (ace.access_mask) {
|
||||
security_descriptor_dacl_add(sd, &ace);
|
||||
}
|
||||
|
||||
/* setup other ACE */
|
||||
ace.trustee = *dom_sid_parse_talloc(req, SID_WORLD);
|
||||
ace.access_mask = 0;
|
||||
if (mode & S_IROTH) {
|
||||
ace.access_mask |= SEC_RIGHTS_FILE_READ | SEC_FILE_EXECUTE;
|
||||
}
|
||||
if (mode & S_IWOTH) {
|
||||
ace.access_mask |= SEC_RIGHTS_FILE_WRITE;
|
||||
}
|
||||
if (ace.access_mask) {
|
||||
security_descriptor_dacl_add(sd, &ace);
|
||||
}
|
||||
|
||||
/* setup system ACE */
|
||||
ace.trustee = *dom_sid_parse_talloc(req, SID_NT_SYSTEM);
|
||||
ace.access_mask = SEC_RIGHTS_FILE_ALL;
|
||||
security_descriptor_dacl_add(sd, &ace);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
omit any security_descriptor elements not specified in the given
|
||||
secinfo flags
|
||||
*/
|
||||
static void normalise_sd_flags(struct security_descriptor *sd, uint32_t secinfo_flags)
|
||||
{
|
||||
if (!(secinfo_flags & SECINFO_OWNER)) {
|
||||
sd->owner_sid = NULL;
|
||||
}
|
||||
if (!(secinfo_flags & SECINFO_GROUP)) {
|
||||
sd->group_sid = NULL;
|
||||
}
|
||||
if (!(secinfo_flags & SECINFO_DACL)) {
|
||||
sd->dacl = NULL;
|
||||
}
|
||||
if (!(secinfo_flags & SECINFO_SACL)) {
|
||||
sd->sacl = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
answer a setfileinfo for an ACL
|
||||
*/
|
||||
NTSTATUS pvfs_acl_set(struct pvfs_state *pvfs,
|
||||
struct ntvfs_request *req,
|
||||
struct pvfs_filename *name, int fd,
|
||||
uint32_t access_mask,
|
||||
union smb_setfileinfo *info)
|
||||
{
|
||||
uint32_t secinfo_flags = info->set_secdesc.in.secinfo_flags;
|
||||
struct security_descriptor *new_sd, *sd, orig_sd;
|
||||
NTSTATUS status = NT_STATUS_NOT_FOUND;
|
||||
uid_t old_uid = -1;
|
||||
gid_t old_gid = -1;
|
||||
uid_t new_uid = -1;
|
||||
gid_t new_gid = -1;
|
||||
|
||||
if (pvfs->acl_ops != NULL) {
|
||||
status = pvfs->acl_ops->acl_load(pvfs, name, fd, req, &sd);
|
||||
}
|
||||
if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
|
||||
status = pvfs_default_acl(pvfs, req, name, fd, &sd);
|
||||
}
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
new_sd = info->set_secdesc.in.sd;
|
||||
orig_sd = *sd;
|
||||
|
||||
old_uid = name->st.st_uid;
|
||||
old_gid = name->st.st_gid;
|
||||
|
||||
/* only set the elements that have been specified */
|
||||
if (secinfo_flags & SECINFO_OWNER) {
|
||||
if (!(access_mask & SEC_STD_WRITE_OWNER)) {
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
if (!dom_sid_equal(sd->owner_sid, new_sd->owner_sid)) {
|
||||
status = sidmap_sid_to_unixuid(pvfs->sidmap, new_sd->owner_sid, &new_uid);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
}
|
||||
sd->owner_sid = new_sd->owner_sid;
|
||||
}
|
||||
if (secinfo_flags & SECINFO_GROUP) {
|
||||
if (!(access_mask & SEC_STD_WRITE_OWNER)) {
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
if (!dom_sid_equal(sd->group_sid, new_sd->group_sid)) {
|
||||
status = sidmap_sid_to_unixgid(pvfs->sidmap, new_sd->group_sid, &new_gid);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
}
|
||||
sd->group_sid = new_sd->group_sid;
|
||||
}
|
||||
if (secinfo_flags & SECINFO_DACL) {
|
||||
if (!(access_mask & SEC_STD_WRITE_DAC)) {
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
sd->dacl = new_sd->dacl;
|
||||
pvfs_translate_generic_bits(sd->dacl);
|
||||
}
|
||||
if (secinfo_flags & SECINFO_SACL) {
|
||||
if (!(access_mask & SEC_FLAG_SYSTEM_SECURITY)) {
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
sd->sacl = new_sd->sacl;
|
||||
pvfs_translate_generic_bits(sd->sacl);
|
||||
}
|
||||
|
||||
if (new_uid == old_uid) {
|
||||
new_uid = -1;
|
||||
}
|
||||
|
||||
if (new_gid == old_gid) {
|
||||
new_gid = -1;
|
||||
}
|
||||
|
||||
/* if there's something to change try it */
|
||||
if (new_uid != -1 || new_gid != -1) {
|
||||
int ret;
|
||||
if (fd == -1) {
|
||||
ret = chown(name->full_name, new_uid, new_gid);
|
||||
} else {
|
||||
ret = fchown(fd, new_uid, new_gid);
|
||||
}
|
||||
if (ret == -1) {
|
||||
return pvfs_map_errno(pvfs, errno);
|
||||
}
|
||||
}
|
||||
|
||||
/* we avoid saving if the sd is the same. This means when clients
|
||||
copy files and end up copying the default sd that we don't
|
||||
needlessly use xattrs */
|
||||
if (!security_descriptor_equal(sd, &orig_sd) && pvfs->acl_ops) {
|
||||
status = pvfs->acl_ops->acl_save(pvfs, name, fd, sd);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
answer a fileinfo query for the ACL
|
||||
*/
|
||||
NTSTATUS pvfs_acl_query(struct pvfs_state *pvfs,
|
||||
struct ntvfs_request *req,
|
||||
struct pvfs_filename *name, int fd,
|
||||
union smb_fileinfo *info)
|
||||
{
|
||||
NTSTATUS status = NT_STATUS_NOT_FOUND;
|
||||
struct security_descriptor *sd;
|
||||
|
||||
if (pvfs->acl_ops) {
|
||||
status = pvfs->acl_ops->acl_load(pvfs, name, fd, req, &sd);
|
||||
}
|
||||
if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
|
||||
status = pvfs_default_acl(pvfs, req, name, fd, &sd);
|
||||
}
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
normalise_sd_flags(sd, info->query_secdesc.in.secinfo_flags);
|
||||
|
||||
info->query_secdesc.out.sd = sd;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
check the read only bit against any of the write access bits
|
||||
*/
|
||||
static BOOL pvfs_read_only(struct pvfs_state *pvfs, uint32_t access_mask)
|
||||
{
|
||||
if ((pvfs->flags & PVFS_FLAG_READONLY) &&
|
||||
(access_mask & (SEC_FILE_WRITE_DATA |
|
||||
SEC_FILE_APPEND_DATA |
|
||||
SEC_FILE_WRITE_EA |
|
||||
SEC_FILE_WRITE_ATTRIBUTE |
|
||||
SEC_STD_DELETE |
|
||||
SEC_STD_WRITE_DAC |
|
||||
SEC_STD_WRITE_OWNER |
|
||||
SEC_DIR_DELETE_CHILD))) {
|
||||
return True;
|
||||
}
|
||||
return False;
|
||||
}
|
||||
|
||||
/*
|
||||
default access check function based on unix permissions
|
||||
doing this saves on building a full security descriptor
|
||||
for the common case of access check on files with no
|
||||
specific NT ACL
|
||||
*/
|
||||
NTSTATUS pvfs_access_check_unix(struct pvfs_state *pvfs,
|
||||
struct ntvfs_request *req,
|
||||
struct pvfs_filename *name,
|
||||
uint32_t *access_mask)
|
||||
{
|
||||
uid_t uid = geteuid();
|
||||
uint32_t max_bits = SEC_RIGHTS_FILE_READ | SEC_FILE_ALL;
|
||||
|
||||
if (pvfs_read_only(pvfs, *access_mask)) {
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
/* owner and root get extra permissions */
|
||||
if (uid == 0) {
|
||||
max_bits |= SEC_STD_ALL | SEC_FLAG_SYSTEM_SECURITY;
|
||||
} else if (uid == name->st.st_uid) {
|
||||
max_bits |= SEC_STD_ALL;
|
||||
}
|
||||
|
||||
if (*access_mask == SEC_FLAG_MAXIMUM_ALLOWED) {
|
||||
*access_mask = max_bits;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
if (uid != 0 && (*access_mask & SEC_FLAG_SYSTEM_SECURITY)) {
|
||||
return NT_STATUS_PRIVILEGE_NOT_HELD;
|
||||
}
|
||||
|
||||
if (*access_mask & ~max_bits) {
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
*access_mask |= SEC_FILE_READ_ATTRIBUTE;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
check the security descriptor on a file, if any
|
||||
|
||||
*access_mask is modified with the access actually granted
|
||||
*/
|
||||
NTSTATUS pvfs_access_check(struct pvfs_state *pvfs,
|
||||
struct ntvfs_request *req,
|
||||
struct pvfs_filename *name,
|
||||
uint32_t *access_mask)
|
||||
{
|
||||
struct security_token *token = req->session_info->security_token;
|
||||
struct xattr_NTACL *acl;
|
||||
NTSTATUS status;
|
||||
struct security_descriptor *sd;
|
||||
|
||||
if (pvfs_read_only(pvfs, *access_mask)) {
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
acl = talloc(req, struct xattr_NTACL);
|
||||
if (acl == NULL) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
/* expand the generic access bits to file specific bits */
|
||||
*access_mask = pvfs_translate_mask(*access_mask);
|
||||
*access_mask &= ~SEC_FILE_READ_ATTRIBUTE;
|
||||
|
||||
status = pvfs_acl_load(pvfs, name, -1, acl);
|
||||
if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
|
||||
talloc_free(acl);
|
||||
return pvfs_access_check_unix(pvfs, req, name, access_mask);
|
||||
}
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
switch (acl->version) {
|
||||
case 1:
|
||||
sd = acl->info.sd;
|
||||
break;
|
||||
default:
|
||||
return NT_STATUS_INVALID_ACL;
|
||||
}
|
||||
|
||||
/* check the acl against the required access mask */
|
||||
status = sec_access_check(sd, token, *access_mask, access_mask);
|
||||
|
||||
/* this bit is always granted, even if not asked for */
|
||||
*access_mask |= SEC_FILE_READ_ATTRIBUTE;
|
||||
|
||||
talloc_free(acl);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
a simplified interface to access check, designed for calls that
|
||||
do not take or return an access check mask
|
||||
*/
|
||||
NTSTATUS pvfs_access_check_simple(struct pvfs_state *pvfs,
|
||||
struct ntvfs_request *req,
|
||||
struct pvfs_filename *name,
|
||||
uint32_t access_needed)
|
||||
{
|
||||
if (access_needed == 0) {
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
return pvfs_access_check(pvfs, req, name, &access_needed);
|
||||
}
|
||||
|
||||
/*
|
||||
access check for creating a new file/directory
|
||||
*/
|
||||
NTSTATUS pvfs_access_check_create(struct pvfs_state *pvfs,
|
||||
struct ntvfs_request *req,
|
||||
struct pvfs_filename *name,
|
||||
uint32_t *access_mask)
|
||||
{
|
||||
struct pvfs_filename *parent;
|
||||
NTSTATUS status;
|
||||
|
||||
status = pvfs_resolve_parent(pvfs, req, name, &parent);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
status = pvfs_access_check(pvfs, req, parent, access_mask);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
if (! ((*access_mask) & SEC_DIR_ADD_FILE)) {
|
||||
return pvfs_access_check_simple(pvfs, req, parent, SEC_DIR_ADD_FILE);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
access check for creating a new file/directory - no access mask supplied
|
||||
*/
|
||||
NTSTATUS pvfs_access_check_parent(struct pvfs_state *pvfs,
|
||||
struct ntvfs_request *req,
|
||||
struct pvfs_filename *name,
|
||||
uint32_t access_mask)
|
||||
{
|
||||
struct pvfs_filename *parent;
|
||||
NTSTATUS status;
|
||||
|
||||
status = pvfs_resolve_parent(pvfs, req, name, &parent);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
return pvfs_access_check_simple(pvfs, req, parent, access_mask);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
determine if an ACE is inheritable
|
||||
*/
|
||||
static BOOL pvfs_inheritable_ace(struct pvfs_state *pvfs,
|
||||
const struct security_ace *ace,
|
||||
BOOL container)
|
||||
{
|
||||
if (!container) {
|
||||
return (ace->flags & SEC_ACE_FLAG_OBJECT_INHERIT) != 0;
|
||||
}
|
||||
|
||||
if (ace->flags & SEC_ACE_FLAG_CONTAINER_INHERIT) {
|
||||
return True;
|
||||
}
|
||||
|
||||
if ((ace->flags & SEC_ACE_FLAG_OBJECT_INHERIT) &&
|
||||
!(ace->flags & SEC_ACE_FLAG_NO_PROPAGATE_INHERIT)) {
|
||||
return True;
|
||||
}
|
||||
|
||||
return False;
|
||||
}
|
||||
|
||||
/*
|
||||
this is the core of ACL inheritance. It copies any inheritable
|
||||
aces from the parent SD to the child SD. Note that the algorithm
|
||||
depends on whether the child is a container or not
|
||||
*/
|
||||
static NTSTATUS pvfs_acl_inherit_aces(struct pvfs_state *pvfs,
|
||||
struct security_descriptor *parent_sd,
|
||||
struct security_descriptor *sd,
|
||||
BOOL container)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0;i<parent_sd->dacl->num_aces;i++) {
|
||||
struct security_ace ace = parent_sd->dacl->aces[i];
|
||||
NTSTATUS status;
|
||||
const struct dom_sid *creator = NULL, *new_id = NULL;
|
||||
uint32_t orig_flags;
|
||||
|
||||
if (!pvfs_inheritable_ace(pvfs, &ace, container)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
orig_flags = ace.flags;
|
||||
|
||||
/* see the RAW-ACLS inheritance test for details on these rules */
|
||||
if (!container) {
|
||||
ace.flags = 0;
|
||||
} else {
|
||||
ace.flags &= ~SEC_ACE_FLAG_INHERIT_ONLY;
|
||||
|
||||
if (!(ace.flags & SEC_ACE_FLAG_CONTAINER_INHERIT)) {
|
||||
ace.flags |= SEC_ACE_FLAG_INHERIT_ONLY;
|
||||
}
|
||||
if (ace.flags & SEC_ACE_FLAG_NO_PROPAGATE_INHERIT) {
|
||||
ace.flags = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* the CREATOR sids are special when inherited */
|
||||
if (dom_sid_equal(&ace.trustee, pvfs->sid_cache.creator_owner)) {
|
||||
creator = pvfs->sid_cache.creator_owner;
|
||||
new_id = sd->owner_sid;
|
||||
} else if (dom_sid_equal(&ace.trustee, pvfs->sid_cache.creator_group)) {
|
||||
creator = pvfs->sid_cache.creator_group;
|
||||
new_id = sd->group_sid;
|
||||
} else {
|
||||
new_id = &ace.trustee;
|
||||
}
|
||||
|
||||
if (creator && container &&
|
||||
(ace.flags & SEC_ACE_FLAG_CONTAINER_INHERIT)) {
|
||||
uint32_t flags = ace.flags;
|
||||
|
||||
ace.trustee = *new_id;
|
||||
ace.flags = 0;
|
||||
status = security_descriptor_dacl_add(sd, &ace);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
ace.trustee = *creator;
|
||||
ace.flags = flags | SEC_ACE_FLAG_INHERIT_ONLY;
|
||||
status = security_descriptor_dacl_add(sd, &ace);
|
||||
} else if (container &&
|
||||
!(orig_flags & SEC_ACE_FLAG_NO_PROPAGATE_INHERIT)) {
|
||||
status = security_descriptor_dacl_add(sd, &ace);
|
||||
} else {
|
||||
ace.trustee = *new_id;
|
||||
status = security_descriptor_dacl_add(sd, &ace);
|
||||
}
|
||||
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
setup an ACL on a new file/directory based on the inherited ACL from
|
||||
the parent. If there is no inherited ACL then we don't set anything,
|
||||
as the default ACL applies anyway
|
||||
*/
|
||||
NTSTATUS pvfs_acl_inherit(struct pvfs_state *pvfs,
|
||||
struct ntvfs_request *req,
|
||||
struct pvfs_filename *name,
|
||||
int fd)
|
||||
{
|
||||
struct xattr_NTACL *acl;
|
||||
NTSTATUS status;
|
||||
struct pvfs_filename *parent;
|
||||
struct security_descriptor *parent_sd, *sd;
|
||||
BOOL container;
|
||||
|
||||
/* form the parents path */
|
||||
status = pvfs_resolve_parent(pvfs, req, name, &parent);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
acl = talloc(req, struct xattr_NTACL);
|
||||
if (acl == NULL) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
status = pvfs_acl_load(pvfs, parent, -1, acl);
|
||||
if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
switch (acl->version) {
|
||||
case 1:
|
||||
parent_sd = acl->info.sd;
|
||||
break;
|
||||
default:
|
||||
return NT_STATUS_INVALID_ACL;
|
||||
}
|
||||
|
||||
if (parent_sd == NULL ||
|
||||
parent_sd->dacl == NULL ||
|
||||
parent_sd->dacl->num_aces == 0) {
|
||||
/* go with the default ACL */
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/* create the new sd */
|
||||
sd = security_descriptor_initialise(req);
|
||||
if (sd == NULL) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
status = sidmap_uid_to_sid(pvfs->sidmap, sd, name->st.st_uid, &sd->owner_sid);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
status = sidmap_gid_to_sid(pvfs->sidmap, sd, name->st.st_gid, &sd->group_sid);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
sd->type |= SEC_DESC_DACL_PRESENT;
|
||||
|
||||
container = (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) ? True:False;
|
||||
|
||||
/* fill in the aces from the parent */
|
||||
status = pvfs_acl_inherit_aces(pvfs, parent_sd, sd, container);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
/* if there is nothing to inherit then we fallback to the
|
||||
default acl */
|
||||
if (sd->dacl == NULL || sd->dacl->num_aces == 0) {
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
acl->info.sd = sd;
|
||||
|
||||
status = pvfs_acl_save(pvfs, name, fd, acl);
|
||||
|
||||
return status;
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
POSIX NTVFS backend - NT ACLs mapped to NFS4 ACLs, as per
|
||||
http://www.suse.de/~agruen/nfs4acl/
|
||||
|
||||
Copyright (C) Andrew Tridgell 2006
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 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.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "vfs_posix.h"
|
||||
#include "lib/util/unix_privs.h"
|
||||
#include "librpc/gen_ndr/ndr_nfs4acl.h"
|
||||
#include "libcli/security/security.h"
|
||||
|
||||
#define ACE4_IDENTIFIER_GROUP 0x40
|
||||
|
||||
/*
|
||||
load the current ACL from system.nfs4acl
|
||||
*/
|
||||
static NTSTATUS pvfs_acl_load_nfs4(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
struct security_descriptor **psd)
|
||||
{
|
||||
NTSTATUS status;
|
||||
struct nfs4acl *acl;
|
||||
struct security_descriptor *sd;
|
||||
int i;
|
||||
|
||||
acl = talloc_zero(mem_ctx, struct nfs4acl);
|
||||
NT_STATUS_HAVE_NO_MEMORY(acl);
|
||||
|
||||
status = pvfs_xattr_ndr_load(pvfs, mem_ctx, name->full_name, fd,
|
||||
NFS4ACL_XATTR_NAME,
|
||||
acl, ndr_pull_nfs4acl);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
talloc_free(acl);
|
||||
return status;
|
||||
}
|
||||
|
||||
*psd = security_descriptor_initialise(mem_ctx);
|
||||
NT_STATUS_HAVE_NO_MEMORY(*psd);
|
||||
|
||||
sd = *psd;
|
||||
|
||||
sd->type |= acl->a_flags;
|
||||
status = sidmap_uid_to_sid(pvfs->sidmap, sd, name->st.st_uid, &sd->owner_sid);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
status = sidmap_gid_to_sid(pvfs->sidmap, sd, name->st.st_gid, &sd->group_sid);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
for (i=0;i<acl->a_count;i++) {
|
||||
struct nfs4ace *a = &acl->ace[i];
|
||||
struct security_ace ace;
|
||||
struct dom_sid *sid;
|
||||
ace.type = a->e_type;
|
||||
ace.flags = a->e_flags;
|
||||
ace.access_mask = a->e_mask;
|
||||
if (a->e_flags & ACE4_IDENTIFIER_GROUP) {
|
||||
status = sidmap_gid_to_sid(pvfs->sidmap, sd, a->e_id, &sid);
|
||||
} else {
|
||||
status = sidmap_uid_to_sid(pvfs->sidmap, sd, a->e_id, &sid);
|
||||
}
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
ace.trustee = *sid;
|
||||
security_descriptor_dacl_add(sd, &ace);
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
save the acl for a file into system.nfs4acl
|
||||
*/
|
||||
static NTSTATUS pvfs_acl_save_nfs4(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd,
|
||||
struct security_descriptor *sd)
|
||||
{
|
||||
NTSTATUS status;
|
||||
void *privs;
|
||||
struct nfs4acl acl;
|
||||
int i;
|
||||
TALLOC_CTX *tmp_ctx;
|
||||
|
||||
tmp_ctx = talloc_new(pvfs);
|
||||
NT_STATUS_HAVE_NO_MEMORY(tmp_ctx);
|
||||
|
||||
acl.a_version = 0;
|
||||
acl.a_flags = sd->type;
|
||||
acl.a_count = sd->dacl?sd->dacl->num_aces:0;
|
||||
acl.a_owner_mask = 0;
|
||||
acl.a_group_mask = 0;
|
||||
acl.a_other_mask = 0;
|
||||
|
||||
acl.ace = talloc_array(tmp_ctx, struct nfs4ace, acl.a_count);
|
||||
if (!acl.ace) {
|
||||
talloc_free(tmp_ctx);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
for (i=0;i<acl.a_count;i++) {
|
||||
struct nfs4ace *a = &acl.ace[i];
|
||||
struct security_ace *ace = &sd->dacl->aces[i];
|
||||
a->e_type = ace->type;
|
||||
a->e_flags = ace->flags;
|
||||
a->e_mask = ace->access_mask;
|
||||
if (sidmap_sid_is_group(pvfs->sidmap, &ace->trustee)) {
|
||||
gid_t gid;
|
||||
a->e_flags |= ACE4_IDENTIFIER_GROUP;
|
||||
status = sidmap_sid_to_unixgid(pvfs->sidmap, &ace->trustee, &gid);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
talloc_free(tmp_ctx);
|
||||
return status;
|
||||
}
|
||||
a->e_id = gid;
|
||||
} else {
|
||||
uid_t uid;
|
||||
status = sidmap_sid_to_unixuid(pvfs->sidmap, &ace->trustee, &uid);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
talloc_free(tmp_ctx);
|
||||
return status;
|
||||
}
|
||||
a->e_id = uid;
|
||||
}
|
||||
a->e_who = "";
|
||||
}
|
||||
|
||||
privs = root_privileges();
|
||||
status = pvfs_xattr_ndr_save(pvfs, name->full_name, fd,
|
||||
NFS4ACL_XATTR_NAME,
|
||||
&acl, ndr_push_nfs4acl);
|
||||
talloc_free(privs);
|
||||
|
||||
talloc_free(tmp_ctx);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
initialise pvfs acl NFS4 backend
|
||||
*/
|
||||
NTSTATUS pvfs_acl_nfs4_init(void)
|
||||
{
|
||||
struct pvfs_acl_ops ops = {
|
||||
.name = "nfs4acl",
|
||||
.acl_load = pvfs_acl_load_nfs4,
|
||||
.acl_save = pvfs_acl_save_nfs4
|
||||
};
|
||||
return pvfs_acl_register(&ops);
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
POSIX NTVFS backend - NT ACLs in xattrs
|
||||
|
||||
Copyright (C) Andrew Tridgell 2006
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 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.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "vfs_posix.h"
|
||||
#include "lib/util/unix_privs.h"
|
||||
#include "librpc/gen_ndr/ndr_xattr.h"
|
||||
|
||||
/*
|
||||
load the current ACL from extended attributes
|
||||
*/
|
||||
static NTSTATUS pvfs_acl_load_xattr(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
struct security_descriptor **sd)
|
||||
{
|
||||
NTSTATUS status;
|
||||
struct xattr_NTACL *acl;
|
||||
|
||||
if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
|
||||
return NT_STATUS_NOT_FOUND;
|
||||
}
|
||||
|
||||
acl = talloc_zero(mem_ctx, struct xattr_NTACL);
|
||||
NT_STATUS_HAVE_NO_MEMORY(acl);
|
||||
|
||||
status = pvfs_xattr_ndr_load(pvfs, mem_ctx, name->full_name, fd,
|
||||
XATTR_NTACL_NAME,
|
||||
acl,
|
||||
(ndr_pull_flags_fn_t)ndr_pull_xattr_NTACL);
|
||||
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
talloc_free(acl);
|
||||
return status;
|
||||
}
|
||||
|
||||
if (acl->version != 1) {
|
||||
talloc_free(acl);
|
||||
return NT_STATUS_INVALID_ACL;
|
||||
}
|
||||
|
||||
*sd = talloc_steal(mem_ctx, acl->info.sd);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
save the acl for a file into filesystem xattr
|
||||
*/
|
||||
static NTSTATUS pvfs_acl_save_xattr(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd,
|
||||
struct security_descriptor *sd)
|
||||
{
|
||||
NTSTATUS status;
|
||||
void *privs;
|
||||
struct xattr_NTACL acl;
|
||||
|
||||
if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
acl.version = 1;
|
||||
acl.info.sd = sd;
|
||||
|
||||
/* this xattr is in the "system" namespace, so we need
|
||||
admin privileges to set it */
|
||||
privs = root_privileges();
|
||||
status = pvfs_xattr_ndr_save(pvfs, name->full_name, fd,
|
||||
XATTR_NTACL_NAME,
|
||||
&acl,
|
||||
(ndr_push_flags_fn_t)ndr_push_xattr_NTACL);
|
||||
talloc_free(privs);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
initialise pvfs acl xattr backend
|
||||
*/
|
||||
NTSTATUS pvfs_acl_xattr_init(void)
|
||||
{
|
||||
struct pvfs_acl_ops ops = {
|
||||
.name = "xattr",
|
||||
.acl_load = pvfs_acl_load_xattr,
|
||||
.acl_save = pvfs_acl_save_xattr
|
||||
};
|
||||
return pvfs_acl_register(&ops);
|
||||
}
|
||||
@@ -0,0 +1,404 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
Copyright (C) Andrew Tridgell 2004
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
/*
|
||||
directory listing functions for posix backend
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "vfs_posix.h"
|
||||
#include "system/dir.h"
|
||||
|
||||
#define NAME_CACHE_SIZE 100
|
||||
|
||||
struct name_cache_entry {
|
||||
char *name;
|
||||
off_t offset;
|
||||
};
|
||||
|
||||
struct pvfs_dir {
|
||||
struct pvfs_state *pvfs;
|
||||
BOOL no_wildcard;
|
||||
char *single_name;
|
||||
const char *pattern;
|
||||
off_t offset;
|
||||
DIR *dir;
|
||||
const char *unix_path;
|
||||
BOOL end_of_search;
|
||||
struct name_cache_entry *name_cache;
|
||||
uint32_t name_cache_index;
|
||||
};
|
||||
|
||||
/* these three numbers are chosen to minimise the chances of a bad
|
||||
interaction with the OS value for 'end of directory'. On IRIX
|
||||
telldir() returns 0xFFFFFFFF at the end of a directory, and that
|
||||
caused an infinite loop with the original values of 0,1,2
|
||||
|
||||
On XFS on linux telldir returns 0x7FFFFFFF at the end of a
|
||||
directory. Thus the change from 0x80000002, as otherwise
|
||||
0x7FFFFFFF+0x80000002==1==DIR_OFFSET_DOTDOT
|
||||
*/
|
||||
#define DIR_OFFSET_DOT 0
|
||||
#define DIR_OFFSET_DOTDOT 1
|
||||
#define DIR_OFFSET_BASE 0x80000022
|
||||
|
||||
/*
|
||||
a special directory listing case where the pattern has no wildcard. We can just do a single stat()
|
||||
thus avoiding the more expensive directory scan
|
||||
*/
|
||||
static NTSTATUS pvfs_list_no_wildcard(struct pvfs_state *pvfs, struct pvfs_filename *name,
|
||||
const char *pattern, struct pvfs_dir *dir)
|
||||
{
|
||||
if (!name->exists) {
|
||||
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
||||
}
|
||||
|
||||
dir->pvfs = pvfs;
|
||||
dir->no_wildcard = True;
|
||||
dir->end_of_search = False;
|
||||
dir->unix_path = talloc_strdup(dir, name->full_name);
|
||||
if (!dir->unix_path) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
dir->single_name = talloc_strdup(dir, pattern);
|
||||
if (!dir->single_name) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
dir->dir = NULL;
|
||||
dir->offset = 0;
|
||||
dir->pattern = NULL;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
destroy an open search
|
||||
*/
|
||||
static int pvfs_dirlist_destructor(struct pvfs_dir *dir)
|
||||
{
|
||||
if (dir->dir) closedir(dir->dir);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
start to read a directory
|
||||
|
||||
if the pattern matches no files then we return NT_STATUS_OK, with dir->count = 0
|
||||
*/
|
||||
NTSTATUS pvfs_list_start(struct pvfs_state *pvfs, struct pvfs_filename *name,
|
||||
TALLOC_CTX *mem_ctx, struct pvfs_dir **dirp)
|
||||
{
|
||||
char *pattern;
|
||||
struct pvfs_dir *dir;
|
||||
|
||||
(*dirp) = talloc_zero(mem_ctx, struct pvfs_dir);
|
||||
if (*dirp == NULL) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
dir = *dirp;
|
||||
|
||||
/* split the unix path into a directory + pattern */
|
||||
pattern = strrchr(name->full_name, '/');
|
||||
if (!pattern) {
|
||||
/* this should not happen, as pvfs_unix_path is supposed to
|
||||
return an absolute path */
|
||||
return NT_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
*pattern++ = 0;
|
||||
|
||||
if (!name->has_wildcard) {
|
||||
return pvfs_list_no_wildcard(pvfs, name, pattern, dir);
|
||||
}
|
||||
|
||||
dir->unix_path = talloc_strdup(dir, name->full_name);
|
||||
if (!dir->unix_path) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
dir->pattern = talloc_strdup(dir, pattern);
|
||||
if (dir->pattern == NULL) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
dir->dir = opendir(name->full_name);
|
||||
if (!dir->dir) {
|
||||
return pvfs_map_errno(pvfs, errno);
|
||||
}
|
||||
|
||||
dir->pvfs = pvfs;
|
||||
dir->no_wildcard = False;
|
||||
dir->end_of_search = False;
|
||||
dir->offset = DIR_OFFSET_DOT;
|
||||
dir->name_cache = talloc_zero_array(dir,
|
||||
struct name_cache_entry,
|
||||
NAME_CACHE_SIZE);
|
||||
if (dir->name_cache == NULL) {
|
||||
talloc_free(dir);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
talloc_set_destructor(dir, pvfs_dirlist_destructor);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
add an entry to the local cache
|
||||
*/
|
||||
static void dcache_add(struct pvfs_dir *dir, const char *name)
|
||||
{
|
||||
struct name_cache_entry *e;
|
||||
|
||||
dir->name_cache_index = (dir->name_cache_index+1) % NAME_CACHE_SIZE;
|
||||
e = &dir->name_cache[dir->name_cache_index];
|
||||
|
||||
if (e->name) talloc_free(e->name);
|
||||
|
||||
e->name = talloc_strdup(dir->name_cache, name);
|
||||
e->offset = dir->offset;
|
||||
}
|
||||
|
||||
/*
|
||||
return the next entry
|
||||
*/
|
||||
const char *pvfs_list_next(struct pvfs_dir *dir, off_t *ofs)
|
||||
{
|
||||
struct dirent *de;
|
||||
enum protocol_types protocol = dir->pvfs->ntvfs->ctx->protocol;
|
||||
|
||||
/* non-wildcard searches are easy */
|
||||
if (dir->no_wildcard) {
|
||||
dir->end_of_search = True;
|
||||
if (*ofs != 0) return NULL;
|
||||
(*ofs)++;
|
||||
return dir->single_name;
|
||||
}
|
||||
|
||||
/* . and .. are handled separately as some unix systems will
|
||||
not return them first in a directory, but windows client
|
||||
may assume that these entries always appear first */
|
||||
if (*ofs == DIR_OFFSET_DOT) {
|
||||
(*ofs) = DIR_OFFSET_DOTDOT;
|
||||
dir->offset = *ofs;
|
||||
if (ms_fnmatch(dir->pattern, ".", protocol) == 0) {
|
||||
dcache_add(dir, ".");
|
||||
return ".";
|
||||
}
|
||||
}
|
||||
|
||||
if (*ofs == DIR_OFFSET_DOTDOT) {
|
||||
(*ofs) = DIR_OFFSET_BASE;
|
||||
dir->offset = *ofs;
|
||||
if (ms_fnmatch(dir->pattern, "..", protocol) == 0) {
|
||||
dcache_add(dir, "..");
|
||||
return "..";
|
||||
}
|
||||
}
|
||||
|
||||
if (*ofs == DIR_OFFSET_BASE) {
|
||||
rewinddir(dir->dir);
|
||||
} else if (*ofs != dir->offset) {
|
||||
seekdir(dir->dir, (*ofs) - DIR_OFFSET_BASE);
|
||||
}
|
||||
dir->offset = *ofs;
|
||||
|
||||
while ((de = readdir(dir->dir))) {
|
||||
const char *dname = de->d_name;
|
||||
|
||||
if (ISDOT(dname) || ISDOTDOT(dname)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ms_fnmatch(dir->pattern, dname, protocol) != 0) {
|
||||
char *short_name = pvfs_short_name_component(dir->pvfs, dname);
|
||||
if (short_name == NULL ||
|
||||
ms_fnmatch(dir->pattern, short_name, protocol) != 0) {
|
||||
talloc_free(short_name);
|
||||
continue;
|
||||
}
|
||||
talloc_free(short_name);
|
||||
}
|
||||
|
||||
dir->offset = telldir(dir->dir) + DIR_OFFSET_BASE;
|
||||
(*ofs) = dir->offset;
|
||||
|
||||
dcache_add(dir, dname);
|
||||
|
||||
return dname;
|
||||
}
|
||||
|
||||
dir->end_of_search = True;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
return unix directory of an open search
|
||||
*/
|
||||
const char *pvfs_list_unix_path(struct pvfs_dir *dir)
|
||||
{
|
||||
return dir->unix_path;
|
||||
}
|
||||
|
||||
/*
|
||||
return True if end of search has been reached
|
||||
*/
|
||||
BOOL pvfs_list_eos(struct pvfs_dir *dir, off_t ofs)
|
||||
{
|
||||
return dir->end_of_search;
|
||||
}
|
||||
|
||||
/*
|
||||
seek to the given name
|
||||
*/
|
||||
NTSTATUS pvfs_list_seek(struct pvfs_dir *dir, const char *name, off_t *ofs)
|
||||
{
|
||||
struct dirent *de;
|
||||
int i;
|
||||
|
||||
dir->end_of_search = False;
|
||||
|
||||
if (ISDOT(name)) {
|
||||
dir->offset = DIR_OFFSET_DOTDOT;
|
||||
*ofs = dir->offset;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
if (ISDOTDOT(name)) {
|
||||
dir->offset = DIR_OFFSET_BASE;
|
||||
*ofs = dir->offset;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
for (i=dir->name_cache_index;i>=0;i--) {
|
||||
struct name_cache_entry *e = &dir->name_cache[i];
|
||||
if (e->name && strcasecmp_m(name, e->name) == 0) {
|
||||
*ofs = e->offset;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
}
|
||||
for (i=NAME_CACHE_SIZE-1;i>dir->name_cache_index;i--) {
|
||||
struct name_cache_entry *e = &dir->name_cache[i];
|
||||
if (e->name && strcasecmp_m(name, e->name) == 0) {
|
||||
*ofs = e->offset;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
rewinddir(dir->dir);
|
||||
|
||||
while ((de = readdir(dir->dir))) {
|
||||
if (strcasecmp_m(name, de->d_name) == 0) {
|
||||
dir->offset = telldir(dir->dir) + DIR_OFFSET_BASE;
|
||||
*ofs = dir->offset;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
dir->end_of_search = True;
|
||||
|
||||
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
||||
}
|
||||
|
||||
/*
|
||||
seek to the given offset
|
||||
*/
|
||||
NTSTATUS pvfs_list_seek_ofs(struct pvfs_dir *dir, uint32_t resume_key, off_t *ofs)
|
||||
{
|
||||
struct dirent *de;
|
||||
int i;
|
||||
|
||||
dir->end_of_search = False;
|
||||
|
||||
if (resume_key == DIR_OFFSET_DOT) {
|
||||
*ofs = DIR_OFFSET_DOTDOT;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
if (resume_key == DIR_OFFSET_DOTDOT) {
|
||||
*ofs = DIR_OFFSET_BASE;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
if (resume_key == DIR_OFFSET_BASE) {
|
||||
rewinddir(dir->dir);
|
||||
if ((de=readdir(dir->dir)) == NULL) {
|
||||
dir->end_of_search = True;
|
||||
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
||||
}
|
||||
*ofs = telldir(dir->dir) + DIR_OFFSET_BASE;
|
||||
dir->offset = *ofs;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
for (i=dir->name_cache_index;i>=0;i--) {
|
||||
struct name_cache_entry *e = &dir->name_cache[i];
|
||||
if (resume_key == (uint32_t)e->offset) {
|
||||
*ofs = e->offset;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
}
|
||||
for (i=NAME_CACHE_SIZE-1;i>dir->name_cache_index;i--) {
|
||||
struct name_cache_entry *e = &dir->name_cache[i];
|
||||
if (resume_key == (uint32_t)e->offset) {
|
||||
*ofs = e->offset;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
rewinddir(dir->dir);
|
||||
|
||||
while ((de = readdir(dir->dir))) {
|
||||
dir->offset = telldir(dir->dir) + DIR_OFFSET_BASE;
|
||||
if (resume_key == (uint32_t)dir->offset) {
|
||||
*ofs = dir->offset;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
dir->end_of_search = True;
|
||||
|
||||
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
see if a directory is empty
|
||||
*/
|
||||
BOOL pvfs_directory_empty(struct pvfs_state *pvfs, struct pvfs_filename *name)
|
||||
{
|
||||
struct dirent *de;
|
||||
DIR *dir = opendir(name->full_name);
|
||||
if (dir == NULL) {
|
||||
return True;
|
||||
}
|
||||
|
||||
while ((de = readdir(dir))) {
|
||||
if (!ISDOT(de->d_name) && !ISDOTDOT(de->d_name)) {
|
||||
closedir(dir);
|
||||
return False;
|
||||
}
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
return True;
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
POSIX NTVFS backend -
|
||||
|
||||
Copyright (C) Andrew Tridgell 2004
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 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.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "vfs_posix.h"
|
||||
|
||||
/****************************************************************************
|
||||
Change a unix mode to a dos mode.
|
||||
****************************************************************************/
|
||||
static uint32_t dos_mode_from_stat(struct pvfs_state *pvfs, struct stat *st)
|
||||
{
|
||||
int result = 0;
|
||||
|
||||
if ((st->st_mode & S_IWUSR) == 0)
|
||||
result |= FILE_ATTRIBUTE_READONLY;
|
||||
|
||||
if ((pvfs->flags & PVFS_FLAG_MAP_ARCHIVE) && ((st->st_mode & S_IXUSR) != 0))
|
||||
result |= FILE_ATTRIBUTE_ARCHIVE;
|
||||
|
||||
if ((pvfs->flags & PVFS_FLAG_MAP_SYSTEM) && ((st->st_mode & S_IXGRP) != 0))
|
||||
result |= FILE_ATTRIBUTE_SYSTEM;
|
||||
|
||||
if ((pvfs->flags & PVFS_FLAG_MAP_HIDDEN) && ((st->st_mode & S_IXOTH) != 0))
|
||||
result |= FILE_ATTRIBUTE_HIDDEN;
|
||||
|
||||
if (S_ISDIR(st->st_mode))
|
||||
result = FILE_ATTRIBUTE_DIRECTORY | (result & FILE_ATTRIBUTE_READONLY);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
fill in the dos file attributes for a file
|
||||
*/
|
||||
NTSTATUS pvfs_fill_dos_info(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd)
|
||||
{
|
||||
/* make directories appear as size 0 with 1 link */
|
||||
if (S_ISDIR(name->st.st_mode)) {
|
||||
name->st.st_size = 0;
|
||||
name->st.st_nlink = 1;
|
||||
}
|
||||
|
||||
/* for now just use the simple samba mapping */
|
||||
unix_to_nt_time(&name->dos.create_time, name->st.st_ctime);
|
||||
unix_to_nt_time(&name->dos.access_time, name->st.st_atime);
|
||||
unix_to_nt_time(&name->dos.write_time, name->st.st_mtime);
|
||||
unix_to_nt_time(&name->dos.change_time, name->st.st_ctime);
|
||||
#ifdef HAVE_STAT_TV_NSEC
|
||||
name->dos.create_time += name->st.st_ctim.tv_nsec / 100;
|
||||
name->dos.access_time += name->st.st_atim.tv_nsec / 100;
|
||||
name->dos.write_time += name->st.st_mtim.tv_nsec / 100;
|
||||
name->dos.change_time += name->st.st_ctim.tv_nsec / 100;
|
||||
#endif
|
||||
name->dos.attrib = dos_mode_from_stat(pvfs, &name->st);
|
||||
name->dos.alloc_size = pvfs_round_alloc_size(pvfs, name->st.st_size);
|
||||
name->dos.nlink = name->st.st_nlink;
|
||||
name->dos.ea_size = 4;
|
||||
name->dos.file_id = (((uint64_t)name->st.st_dev)<<32) | name->st.st_ino;
|
||||
name->dos.flags = 0;
|
||||
|
||||
return pvfs_dosattrib_load(pvfs, name, fd);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
return a set of unix file permissions for a new file or directory
|
||||
*/
|
||||
mode_t pvfs_fileperms(struct pvfs_state *pvfs, uint32_t attrib)
|
||||
{
|
||||
mode_t mode = S_IRUSR;
|
||||
|
||||
if (attrib & FILE_ATTRIBUTE_DIRECTORY) {
|
||||
mode |= S_IXUSR;
|
||||
}
|
||||
|
||||
if (!(attrib & FILE_ATTRIBUTE_READONLY) ||
|
||||
(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
|
||||
mode |= S_IWUSR;
|
||||
}
|
||||
|
||||
if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
|
||||
if ((attrib & FILE_ATTRIBUTE_ARCHIVE) &&
|
||||
(pvfs->flags & PVFS_FLAG_MAP_ARCHIVE)) {
|
||||
mode |= S_IXUSR;
|
||||
}
|
||||
|
||||
if ((attrib & FILE_ATTRIBUTE_SYSTEM) &&
|
||||
(pvfs->flags & PVFS_FLAG_MAP_SYSTEM)) {
|
||||
mode |= S_IXGRP;
|
||||
}
|
||||
|
||||
if ((attrib & FILE_ATTRIBUTE_HIDDEN) &&
|
||||
(pvfs->flags & PVFS_FLAG_MAP_HIDDEN)) {
|
||||
mode |= S_IXOTH;
|
||||
}
|
||||
}
|
||||
|
||||
return mode;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
POSIX NTVFS backend - flush
|
||||
|
||||
Copyright (C) Andrew Tridgell 2004
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 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.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "vfs_posix.h"
|
||||
|
||||
/*
|
||||
flush a single open file
|
||||
*/
|
||||
static void pvfs_flush_file(struct pvfs_state *pvfs, struct pvfs_file *f)
|
||||
{
|
||||
if (f->handle->fd == -1) {
|
||||
return;
|
||||
}
|
||||
if (pvfs->flags & PVFS_FLAG_STRICT_SYNC) {
|
||||
fsync(f->handle->fd);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
flush a fnum
|
||||
*/
|
||||
NTSTATUS pvfs_flush(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
union smb_flush *io)
|
||||
{
|
||||
struct pvfs_state *pvfs = ntvfs->private_data;
|
||||
struct pvfs_file *f;
|
||||
|
||||
switch (io->generic.level) {
|
||||
case RAW_FLUSH_FLUSH:
|
||||
case RAW_FLUSH_SMB2:
|
||||
/* TODO: take care of io->smb2.in.unknown */
|
||||
f = pvfs_find_fd(pvfs, req, io->generic.in.file.ntvfs);
|
||||
if (!f) {
|
||||
return NT_STATUS_INVALID_HANDLE;
|
||||
}
|
||||
pvfs_flush_file(pvfs, f);
|
||||
return NT_STATUS_OK;
|
||||
|
||||
case RAW_FLUSH_ALL:
|
||||
if (!(pvfs->flags & PVFS_FLAG_STRICT_SYNC)) {
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* they are asking to flush all open files
|
||||
* for the given SMBPID
|
||||
*/
|
||||
for (f=pvfs->files.list;f;f=f->next) {
|
||||
if (f->ntvfs->smbpid != req->smbpid) continue;
|
||||
|
||||
pvfs_flush_file(pvfs, f);
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
return NT_STATUS_INVALID_LEVEL;
|
||||
}
|
||||
@@ -0,0 +1,208 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
POSIX NTVFS backend - fsinfo
|
||||
|
||||
Copyright (C) Andrew Tridgell 2004
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 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.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "vfs_posix.h"
|
||||
#include "librpc/gen_ndr/xattr.h"
|
||||
#include "librpc/ndr/libndr.h"
|
||||
|
||||
/* We use libblkid out of e2fsprogs to identify UUID of a volume */
|
||||
#ifdef HAVE_LIBBLKID
|
||||
#include <blkid/blkid.h>
|
||||
#endif
|
||||
|
||||
static NTSTATUS pvfs_blkid_fs_uuid(struct pvfs_state *pvfs, struct stat *st, struct GUID *uuid)
|
||||
{
|
||||
#ifdef HAVE_LIBBLKID
|
||||
NTSTATUS status;
|
||||
char *uuid_value = NULL;
|
||||
char *devname = NULL;
|
||||
|
||||
devname = blkid_devno_to_devname(st->st_dev);
|
||||
if (!devname) {
|
||||
ZERO_STRUCTP(uuid);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
uuid_value = blkid_get_tag_value(NULL, "UUID", devname);
|
||||
free(devname);
|
||||
if (!uuid_value) {
|
||||
ZERO_STRUCTP(uuid);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
status = GUID_from_string(uuid_value, uuid);
|
||||
free(uuid_value);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
ZERO_STRUCTP(uuid);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
return NT_STATUS_OK;
|
||||
#else
|
||||
ZERO_STRUCTP(uuid);
|
||||
return NT_STATUS_OK;
|
||||
#endif
|
||||
}
|
||||
|
||||
static NTSTATUS pvfs_cache_base_fs_uuid(struct pvfs_state *pvfs, struct stat *st)
|
||||
{
|
||||
NTSTATUS status;
|
||||
struct GUID uuid;
|
||||
|
||||
if (pvfs->base_fs_uuid) return NT_STATUS_OK;
|
||||
|
||||
status = pvfs_blkid_fs_uuid(pvfs, st, &uuid);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
pvfs->base_fs_uuid = talloc(pvfs, struct GUID);
|
||||
NT_STATUS_HAVE_NO_MEMORY(pvfs->base_fs_uuid);
|
||||
*pvfs->base_fs_uuid = uuid;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
/*
|
||||
return filesystem space info
|
||||
*/
|
||||
NTSTATUS pvfs_fsinfo(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_fsinfo *fs)
|
||||
{
|
||||
NTSTATUS status;
|
||||
struct pvfs_state *pvfs = ntvfs->private_data;
|
||||
uint64_t blocks_free, blocks_total;
|
||||
uint_t bpunit;
|
||||
struct stat st;
|
||||
const uint16_t block_size = 512;
|
||||
|
||||
/* only some levels need the expensive sys_fsusage() call */
|
||||
switch (fs->generic.level) {
|
||||
case RAW_QFS_DSKATTR:
|
||||
case RAW_QFS_ALLOCATION:
|
||||
case RAW_QFS_SIZE_INFO:
|
||||
case RAW_QFS_SIZE_INFORMATION:
|
||||
case RAW_QFS_FULL_SIZE_INFORMATION:
|
||||
if (sys_fsusage(pvfs->base_directory, &blocks_free, &blocks_total) == -1) {
|
||||
return pvfs_map_errno(pvfs, errno);
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (stat(pvfs->base_directory, &st) != 0) {
|
||||
return NT_STATUS_DISK_CORRUPT_ERROR;
|
||||
}
|
||||
|
||||
/* now fill in the out fields */
|
||||
switch (fs->generic.level) {
|
||||
case RAW_QFS_GENERIC:
|
||||
return NT_STATUS_INVALID_LEVEL;
|
||||
|
||||
case RAW_QFS_DSKATTR:
|
||||
/* we need to scale the sizes to fit */
|
||||
for (bpunit=64; bpunit<0x10000; bpunit *= 2) {
|
||||
if (blocks_total * (double)block_size < bpunit * 512 * 65535.0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
fs->dskattr.out.blocks_per_unit = bpunit;
|
||||
fs->dskattr.out.block_size = block_size;
|
||||
fs->dskattr.out.units_total = (blocks_total * (double)block_size) / (bpunit * 512);
|
||||
fs->dskattr.out.units_free = (blocks_free * (double)block_size) / (bpunit * 512);
|
||||
|
||||
/* we must return a maximum of 2G to old DOS systems, or they get very confused */
|
||||
if (bpunit > 64 && req->ctx->protocol <= PROTOCOL_LANMAN2) {
|
||||
fs->dskattr.out.blocks_per_unit = 64;
|
||||
fs->dskattr.out.units_total = 0xFFFF;
|
||||
fs->dskattr.out.units_free = 0xFFFF;
|
||||
}
|
||||
return NT_STATUS_OK;
|
||||
|
||||
case RAW_QFS_ALLOCATION:
|
||||
fs->allocation.out.fs_id = st.st_dev;
|
||||
fs->allocation.out.total_alloc_units = blocks_total;
|
||||
fs->allocation.out.avail_alloc_units = blocks_free;
|
||||
fs->allocation.out.sectors_per_unit = 1;
|
||||
fs->allocation.out.bytes_per_sector = block_size;
|
||||
return NT_STATUS_OK;
|
||||
|
||||
case RAW_QFS_VOLUME:
|
||||
fs->volume.out.serial_number = st.st_ino;
|
||||
fs->volume.out.volume_name.s = pvfs->share_name;
|
||||
return NT_STATUS_OK;
|
||||
|
||||
case RAW_QFS_VOLUME_INFO:
|
||||
case RAW_QFS_VOLUME_INFORMATION:
|
||||
unix_to_nt_time(&fs->volume_info.out.create_time, st.st_ctime);
|
||||
fs->volume_info.out.serial_number = st.st_ino;
|
||||
fs->volume_info.out.volume_name.s = pvfs->share_name;
|
||||
return NT_STATUS_OK;
|
||||
|
||||
case RAW_QFS_SIZE_INFO:
|
||||
case RAW_QFS_SIZE_INFORMATION:
|
||||
fs->size_info.out.total_alloc_units = blocks_total;
|
||||
fs->size_info.out.avail_alloc_units = blocks_free;
|
||||
fs->size_info.out.sectors_per_unit = 1;
|
||||
fs->size_info.out.bytes_per_sector = block_size;
|
||||
return NT_STATUS_OK;
|
||||
|
||||
case RAW_QFS_DEVICE_INFO:
|
||||
case RAW_QFS_DEVICE_INFORMATION:
|
||||
fs->device_info.out.device_type = 0;
|
||||
fs->device_info.out.characteristics = 0;
|
||||
return NT_STATUS_OK;
|
||||
|
||||
case RAW_QFS_ATTRIBUTE_INFO:
|
||||
case RAW_QFS_ATTRIBUTE_INFORMATION:
|
||||
fs->attribute_info.out.fs_attr = pvfs->fs_attribs;
|
||||
fs->attribute_info.out.max_file_component_length = 255;
|
||||
fs->attribute_info.out.fs_type.s = ntvfs->ctx->fs_type;
|
||||
return NT_STATUS_OK;
|
||||
|
||||
case RAW_QFS_QUOTA_INFORMATION:
|
||||
ZERO_STRUCT(fs->quota_information.out.unknown);
|
||||
fs->quota_information.out.quota_soft = 0;
|
||||
fs->quota_information.out.quota_hard = 0;
|
||||
fs->quota_information.out.quota_flags = 0;
|
||||
return NT_STATUS_OK;
|
||||
|
||||
case RAW_QFS_FULL_SIZE_INFORMATION:
|
||||
fs->full_size_information.out.total_alloc_units = blocks_total;
|
||||
fs->full_size_information.out.call_avail_alloc_units = blocks_free;
|
||||
fs->full_size_information.out.actual_avail_alloc_units = blocks_free;
|
||||
fs->full_size_information.out.sectors_per_unit = 1;
|
||||
fs->full_size_information.out.bytes_per_sector = block_size;
|
||||
return NT_STATUS_OK;
|
||||
|
||||
case RAW_QFS_OBJECTID_INFORMATION:
|
||||
ZERO_STRUCT(fs->objectid_information.out.guid);
|
||||
ZERO_STRUCT(fs->objectid_information.out.unknown);
|
||||
|
||||
status = pvfs_cache_base_fs_uuid(pvfs, &st);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
fs->objectid_information.out.guid = *pvfs->base_fs_uuid;
|
||||
return NT_STATUS_OK;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return NT_STATUS_INVALID_LEVEL;
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
POSIX NTVFS backend - open and close
|
||||
|
||||
Copyright (C) Andrew Tridgell 2004
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 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.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "vfs_posix.h"
|
||||
#include "libcli/raw/ioctl.h"
|
||||
|
||||
/*
|
||||
old ioctl interface
|
||||
*/
|
||||
static NTSTATUS pvfs_ioctl_old(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_ioctl *io)
|
||||
{
|
||||
return NT_STATUS_DOS(ERRSRV, ERRerror);
|
||||
}
|
||||
|
||||
/*
|
||||
nt ioctl interface
|
||||
*/
|
||||
static NTSTATUS pvfs_ntioctl(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_ioctl *io)
|
||||
{
|
||||
struct pvfs_state *pvfs = ntvfs->private_data;
|
||||
struct pvfs_file *f;
|
||||
|
||||
f = pvfs_find_fd(pvfs, req, io->ntioctl.in.file.ntvfs);
|
||||
if (!f) {
|
||||
return NT_STATUS_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
switch (io->ntioctl.in.function) {
|
||||
case FSCTL_SET_SPARSE:
|
||||
/* maybe some posix systems have a way of marking
|
||||
a file non-sparse? */
|
||||
io->ntioctl.out.blob = data_blob(NULL, 0);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
return NT_STATUS_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
/*
|
||||
ioctl interface
|
||||
*/
|
||||
NTSTATUS pvfs_ioctl(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
union smb_ioctl *io)
|
||||
{
|
||||
switch (io->generic.level) {
|
||||
case RAW_IOCTL_IOCTL:
|
||||
return pvfs_ioctl_old(ntvfs, req, io);
|
||||
|
||||
case RAW_IOCTL_NTIOCTL:
|
||||
return pvfs_ntioctl(ntvfs, req, io);
|
||||
|
||||
case RAW_IOCTL_SMB2:
|
||||
case RAW_IOCTL_SMB2_NO_HANDLE:
|
||||
return NT_STATUS_FS_DRIVER_REQUIRED;
|
||||
}
|
||||
|
||||
return NT_STATUS_INVALID_LEVEL;
|
||||
}
|
||||
@@ -0,0 +1,395 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
POSIX NTVFS backend - locking
|
||||
|
||||
Copyright (C) Andrew Tridgell 2004
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 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.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "vfs_posix.h"
|
||||
#include "system/time.h"
|
||||
#include "lib/util/dlinklist.h"
|
||||
#include "messaging/messaging.h"
|
||||
|
||||
|
||||
/*
|
||||
check if we can perform IO on a range that might be locked
|
||||
*/
|
||||
NTSTATUS pvfs_check_lock(struct pvfs_state *pvfs,
|
||||
struct pvfs_file *f,
|
||||
uint16_t smbpid,
|
||||
uint64_t offset, uint64_t count,
|
||||
enum brl_type rw)
|
||||
{
|
||||
if (!(pvfs->flags & PVFS_FLAG_STRICT_LOCKING)) {
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
return brl_locktest(pvfs->brl_context,
|
||||
f->brl_handle,
|
||||
smbpid,
|
||||
offset, count, rw);
|
||||
}
|
||||
|
||||
/* this state structure holds information about a lock we are waiting on */
|
||||
struct pvfs_pending_lock {
|
||||
struct pvfs_pending_lock *next, *prev;
|
||||
struct pvfs_state *pvfs;
|
||||
union smb_lock *lck;
|
||||
struct pvfs_file *f;
|
||||
struct ntvfs_request *req;
|
||||
int pending_lock;
|
||||
void *wait_handle;
|
||||
struct timeval end_time;
|
||||
};
|
||||
|
||||
/*
|
||||
a secondary attempt to setup a lock has failed - back out
|
||||
the locks we did get and send an error
|
||||
*/
|
||||
static void pvfs_lock_async_failed(struct pvfs_state *pvfs,
|
||||
struct ntvfs_request *req,
|
||||
struct pvfs_file *f,
|
||||
struct smb_lock_entry *locks,
|
||||
int i,
|
||||
NTSTATUS status)
|
||||
{
|
||||
/* undo the locks we just did */
|
||||
for (i=i-1;i>=0;i--) {
|
||||
brl_unlock(pvfs->brl_context,
|
||||
f->brl_handle,
|
||||
locks[i].pid,
|
||||
locks[i].offset,
|
||||
locks[i].count);
|
||||
f->lock_count--;
|
||||
}
|
||||
req->async_states->status = status;
|
||||
req->async_states->send_fn(req);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
called when we receive a pending lock notification. It means that
|
||||
either our lock timed out or somoene else has unlocked a overlapping
|
||||
range, so we should try the lock again. Note that on timeout we
|
||||
do retry the lock, giving it a last chance.
|
||||
*/
|
||||
static void pvfs_pending_lock_continue(void *private, enum pvfs_wait_notice reason)
|
||||
{
|
||||
struct pvfs_pending_lock *pending = private;
|
||||
struct pvfs_state *pvfs = pending->pvfs;
|
||||
struct pvfs_file *f = pending->f;
|
||||
struct ntvfs_request *req = pending->req;
|
||||
union smb_lock *lck = pending->lck;
|
||||
struct smb_lock_entry *locks;
|
||||
enum brl_type rw;
|
||||
NTSTATUS status;
|
||||
int i;
|
||||
BOOL timed_out;
|
||||
|
||||
timed_out = (reason != PVFS_WAIT_EVENT);
|
||||
|
||||
locks = lck->lockx.in.locks + lck->lockx.in.ulock_cnt;
|
||||
|
||||
if (lck->lockx.in.mode & LOCKING_ANDX_SHARED_LOCK) {
|
||||
rw = READ_LOCK;
|
||||
} else {
|
||||
rw = WRITE_LOCK;
|
||||
}
|
||||
|
||||
DLIST_REMOVE(f->pending_list, pending);
|
||||
|
||||
/* we don't retry on a cancel */
|
||||
if (reason == PVFS_WAIT_CANCEL) {
|
||||
status = NT_STATUS_FILE_LOCK_CONFLICT;
|
||||
} else {
|
||||
/*
|
||||
* here it's important to pass the pending pointer
|
||||
* because with this we'll get the correct error code
|
||||
* FILE_LOCK_CONFLICT in the error case
|
||||
*/
|
||||
status = brl_lock(pvfs->brl_context,
|
||||
f->brl_handle,
|
||||
req->smbpid,
|
||||
locks[pending->pending_lock].offset,
|
||||
locks[pending->pending_lock].count,
|
||||
rw, pending);
|
||||
}
|
||||
if (NT_STATUS_IS_OK(status)) {
|
||||
f->lock_count++;
|
||||
timed_out = False;
|
||||
}
|
||||
|
||||
/* if we have failed and timed out, or succeeded, then we
|
||||
don't need the pending lock any more */
|
||||
if (NT_STATUS_IS_OK(status) || timed_out) {
|
||||
NTSTATUS status2;
|
||||
status2 = brl_remove_pending(pvfs->brl_context,
|
||||
f->brl_handle, pending);
|
||||
if (!NT_STATUS_IS_OK(status2)) {
|
||||
DEBUG(0,("pvfs_lock: failed to remove pending lock - %s\n", nt_errstr(status2)));
|
||||
}
|
||||
talloc_free(pending->wait_handle);
|
||||
}
|
||||
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
if (timed_out) {
|
||||
/* no more chances */
|
||||
pvfs_lock_async_failed(pvfs, req, f, locks, pending->pending_lock, status);
|
||||
} else {
|
||||
/* we can try again */
|
||||
DLIST_ADD(f->pending_list, pending);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* if we haven't timed out yet, then we can do more pending locks */
|
||||
if (rw == READ_LOCK) {
|
||||
rw = PENDING_READ_LOCK;
|
||||
} else {
|
||||
rw = PENDING_WRITE_LOCK;
|
||||
}
|
||||
|
||||
/* we've now got the pending lock. try and get the rest, which might
|
||||
lead to more pending locks */
|
||||
for (i=pending->pending_lock+1;i<lck->lockx.in.lock_cnt;i++) {
|
||||
if (pending) {
|
||||
pending->pending_lock = i;
|
||||
}
|
||||
|
||||
status = brl_lock(pvfs->brl_context,
|
||||
f->brl_handle,
|
||||
req->smbpid,
|
||||
locks[i].offset,
|
||||
locks[i].count,
|
||||
rw, pending);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
if (pending) {
|
||||
/* a timed lock failed - setup a wait message to handle
|
||||
the pending lock notification or a timeout */
|
||||
pending->wait_handle = pvfs_wait_message(pvfs, req, MSG_BRL_RETRY,
|
||||
pending->end_time,
|
||||
pvfs_pending_lock_continue,
|
||||
pending);
|
||||
if (pending->wait_handle == NULL) {
|
||||
pvfs_lock_async_failed(pvfs, req, f, locks, i, NT_STATUS_NO_MEMORY);
|
||||
} else {
|
||||
talloc_steal(pending, pending->wait_handle);
|
||||
DLIST_ADD(f->pending_list, pending);
|
||||
}
|
||||
return;
|
||||
}
|
||||
pvfs_lock_async_failed(pvfs, req, f, locks, i, status);
|
||||
return;
|
||||
}
|
||||
|
||||
f->lock_count++;
|
||||
}
|
||||
|
||||
/* we've managed to get all the locks. Tell the client */
|
||||
req->async_states->status = NT_STATUS_OK;
|
||||
req->async_states->send_fn(req);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
called when we close a file that might have locks
|
||||
*/
|
||||
void pvfs_lock_close(struct pvfs_state *pvfs, struct pvfs_file *f)
|
||||
{
|
||||
struct pvfs_pending_lock *p, *next;
|
||||
|
||||
if (f->lock_count || f->pending_list) {
|
||||
DEBUG(5,("pvfs_lock: removing %.0f locks on close\n",
|
||||
(double)f->lock_count));
|
||||
brl_close(f->pvfs->brl_context, f->brl_handle);
|
||||
f->lock_count = 0;
|
||||
}
|
||||
|
||||
/* reply to all the pending lock requests, telling them the
|
||||
lock failed */
|
||||
for (p=f->pending_list;p;p=next) {
|
||||
next = p->next;
|
||||
DLIST_REMOVE(f->pending_list, p);
|
||||
p->req->async_states->status = NT_STATUS_RANGE_NOT_LOCKED;
|
||||
p->req->async_states->send_fn(p->req);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
cancel a set of locks
|
||||
*/
|
||||
static NTSTATUS pvfs_lock_cancel(struct pvfs_state *pvfs, struct ntvfs_request *req, union smb_lock *lck,
|
||||
struct pvfs_file *f)
|
||||
{
|
||||
struct pvfs_pending_lock *p;
|
||||
|
||||
for (p=f->pending_list;p;p=p->next) {
|
||||
/* check if the lock request matches exactly - you can only cancel with exact matches */
|
||||
if (p->lck->lockx.in.ulock_cnt == lck->lockx.in.ulock_cnt &&
|
||||
p->lck->lockx.in.lock_cnt == lck->lockx.in.lock_cnt &&
|
||||
p->lck->lockx.in.file.ntvfs== lck->lockx.in.file.ntvfs &&
|
||||
p->lck->lockx.in.mode == (lck->lockx.in.mode & ~LOCKING_ANDX_CANCEL_LOCK)) {
|
||||
int i;
|
||||
|
||||
for (i=0;i<lck->lockx.in.ulock_cnt + lck->lockx.in.lock_cnt;i++) {
|
||||
if (p->lck->lockx.in.locks[i].pid != lck->lockx.in.locks[i].pid ||
|
||||
p->lck->lockx.in.locks[i].offset != lck->lockx.in.locks[i].offset ||
|
||||
p->lck->lockx.in.locks[i].count != lck->lockx.in.locks[i].count) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i < lck->lockx.in.ulock_cnt) continue;
|
||||
|
||||
/* an exact match! we can cancel it, which is equivalent
|
||||
to triggering the timeout early */
|
||||
pvfs_pending_lock_continue(p, PVFS_WAIT_TIMEOUT);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return NT_STATUS_DOS(ERRDOS, ERRcancelviolation);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
lock or unlock a byte range
|
||||
*/
|
||||
NTSTATUS pvfs_lock(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_lock *lck)
|
||||
{
|
||||
struct pvfs_state *pvfs = ntvfs->private_data;
|
||||
struct pvfs_file *f;
|
||||
struct smb_lock_entry *locks;
|
||||
int i;
|
||||
enum brl_type rw;
|
||||
struct pvfs_pending_lock *pending = NULL;
|
||||
NTSTATUS status;
|
||||
|
||||
if (lck->generic.level != RAW_LOCK_GENERIC) {
|
||||
return ntvfs_map_lock(ntvfs, req, lck);
|
||||
}
|
||||
|
||||
f = pvfs_find_fd(pvfs, req, lck->lockx.in.file.ntvfs);
|
||||
if (!f) {
|
||||
return NT_STATUS_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
if (f->handle->fd == -1) {
|
||||
return NT_STATUS_FILE_IS_A_DIRECTORY;
|
||||
}
|
||||
|
||||
if (lck->lockx.in.timeout != 0 &&
|
||||
(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
|
||||
pending = talloc(f, struct pvfs_pending_lock);
|
||||
if (pending == NULL) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
pending->pvfs = pvfs;
|
||||
pending->lck = lck;
|
||||
pending->f = f;
|
||||
pending->req = req;
|
||||
|
||||
pending->end_time =
|
||||
timeval_current_ofs(lck->lockx.in.timeout/1000,
|
||||
1000*(lck->lockx.in.timeout%1000));
|
||||
}
|
||||
|
||||
if (lck->lockx.in.mode & LOCKING_ANDX_SHARED_LOCK) {
|
||||
rw = pending? PENDING_READ_LOCK : READ_LOCK;
|
||||
} else {
|
||||
rw = pending? PENDING_WRITE_LOCK : WRITE_LOCK;
|
||||
}
|
||||
|
||||
if (lck->lockx.in.mode & LOCKING_ANDX_CANCEL_LOCK) {
|
||||
return pvfs_lock_cancel(pvfs, req, lck, f);
|
||||
}
|
||||
|
||||
if (lck->lockx.in.mode & LOCKING_ANDX_CHANGE_LOCKTYPE) {
|
||||
/* this seems to not be supported by any windows server,
|
||||
or used by any clients */
|
||||
return NT_STATUS_DOS(ERRDOS, ERRnoatomiclocks);
|
||||
}
|
||||
|
||||
if (lck->lockx.in.mode & LOCKING_ANDX_OPLOCK_RELEASE) {
|
||||
DEBUG(0,("received unexpected oplock break\n"));
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
|
||||
/* the unlocks happen first */
|
||||
locks = lck->lockx.in.locks;
|
||||
|
||||
for (i=0;i<lck->lockx.in.ulock_cnt;i++) {
|
||||
status = brl_unlock(pvfs->brl_context,
|
||||
f->brl_handle,
|
||||
locks[i].pid,
|
||||
locks[i].offset,
|
||||
locks[i].count);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
f->lock_count--;
|
||||
}
|
||||
|
||||
locks += i;
|
||||
|
||||
for (i=0;i<lck->lockx.in.lock_cnt;i++) {
|
||||
if (pending) {
|
||||
pending->pending_lock = i;
|
||||
}
|
||||
|
||||
status = brl_lock(pvfs->brl_context,
|
||||
f->brl_handle,
|
||||
locks[i].pid,
|
||||
locks[i].offset,
|
||||
locks[i].count,
|
||||
rw, pending);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
if (pending) {
|
||||
/* a timed lock failed - setup a wait message to handle
|
||||
the pending lock notification or a timeout */
|
||||
pending->wait_handle = pvfs_wait_message(pvfs, req, MSG_BRL_RETRY,
|
||||
pending->end_time,
|
||||
pvfs_pending_lock_continue,
|
||||
pending);
|
||||
if (pending->wait_handle == NULL) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
talloc_steal(pending, pending->wait_handle);
|
||||
DLIST_ADD(f->pending_list, pending);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
/* undo the locks we just did */
|
||||
for (i=i-1;i>=0;i--) {
|
||||
brl_unlock(pvfs->brl_context,
|
||||
f->brl_handle,
|
||||
locks[i].pid,
|
||||
locks[i].offset,
|
||||
locks[i].count);
|
||||
f->lock_count--;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
f->lock_count++;
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
POSIX NTVFS backend - mkdir and rmdir
|
||||
|
||||
Copyright (C) Andrew Tridgell 2004
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 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.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "system/dir.h"
|
||||
#include "vfs_posix.h"
|
||||
#include "librpc/gen_ndr/security.h"
|
||||
|
||||
/*
|
||||
create a directory with EAs
|
||||
*/
|
||||
static NTSTATUS pvfs_t2mkdir(struct pvfs_state *pvfs,
|
||||
struct ntvfs_request *req, union smb_mkdir *md)
|
||||
{
|
||||
NTSTATUS status;
|
||||
struct pvfs_filename *name;
|
||||
mode_t mode;
|
||||
|
||||
/* resolve the cifs name to a posix name */
|
||||
status = pvfs_resolve_name(pvfs, req, md->t2mkdir.in.path, 0, &name);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
if (name->exists) {
|
||||
return NT_STATUS_OBJECT_NAME_COLLISION;
|
||||
}
|
||||
|
||||
status = pvfs_access_check_parent(pvfs, req, name, SEC_DIR_ADD_FILE);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
mode = pvfs_fileperms(pvfs, FILE_ATTRIBUTE_DIRECTORY);
|
||||
|
||||
if (mkdir(name->full_name, mode) == -1) {
|
||||
return pvfs_map_errno(pvfs, errno);
|
||||
}
|
||||
|
||||
pvfs_xattr_unlink_hook(pvfs, name->full_name);
|
||||
|
||||
status = pvfs_resolve_name(pvfs, req, md->t2mkdir.in.path, 0, &name);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
if (!name->exists ||
|
||||
!(name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) {
|
||||
return NT_STATUS_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
/* setup an inherited acl from the parent */
|
||||
status = pvfs_acl_inherit(pvfs, req, name, -1);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
rmdir(name->full_name);
|
||||
return status;
|
||||
}
|
||||
|
||||
/* setup any EAs that were asked for */
|
||||
status = pvfs_setfileinfo_ea_set(pvfs, name, -1,
|
||||
md->t2mkdir.in.num_eas,
|
||||
md->t2mkdir.in.eas);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
rmdir(name->full_name);
|
||||
return status;
|
||||
}
|
||||
|
||||
notify_trigger(pvfs->notify_context,
|
||||
NOTIFY_ACTION_ADDED,
|
||||
FILE_NOTIFY_CHANGE_DIR_NAME,
|
||||
name->full_name);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
create a directory
|
||||
*/
|
||||
NTSTATUS pvfs_mkdir(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_mkdir *md)
|
||||
{
|
||||
struct pvfs_state *pvfs = ntvfs->private_data;
|
||||
NTSTATUS status;
|
||||
struct pvfs_filename *name;
|
||||
mode_t mode;
|
||||
|
||||
if (md->generic.level == RAW_MKDIR_T2MKDIR) {
|
||||
return pvfs_t2mkdir(pvfs, req, md);
|
||||
}
|
||||
|
||||
if (md->generic.level != RAW_MKDIR_MKDIR) {
|
||||
return NT_STATUS_INVALID_LEVEL;
|
||||
}
|
||||
|
||||
/* resolve the cifs name to a posix name */
|
||||
status = pvfs_resolve_name(pvfs, req, md->mkdir.in.path, 0, &name);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
if (name->exists) {
|
||||
return NT_STATUS_OBJECT_NAME_COLLISION;
|
||||
}
|
||||
|
||||
status = pvfs_access_check_parent(pvfs, req, name, SEC_DIR_ADD_FILE);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
mode = pvfs_fileperms(pvfs, FILE_ATTRIBUTE_DIRECTORY);
|
||||
|
||||
if (mkdir(name->full_name, mode) == -1) {
|
||||
return pvfs_map_errno(pvfs, errno);
|
||||
}
|
||||
|
||||
pvfs_xattr_unlink_hook(pvfs, name->full_name);
|
||||
|
||||
/* setup an inherited acl from the parent */
|
||||
status = pvfs_acl_inherit(pvfs, req, name, -1);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
rmdir(name->full_name);
|
||||
return status;
|
||||
}
|
||||
|
||||
notify_trigger(pvfs->notify_context,
|
||||
NOTIFY_ACTION_ADDED,
|
||||
FILE_NOTIFY_CHANGE_DIR_NAME,
|
||||
name->full_name);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
remove a directory
|
||||
*/
|
||||
NTSTATUS pvfs_rmdir(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, struct smb_rmdir *rd)
|
||||
{
|
||||
struct pvfs_state *pvfs = ntvfs->private_data;
|
||||
NTSTATUS status;
|
||||
struct pvfs_filename *name;
|
||||
|
||||
/* resolve the cifs name to a posix name */
|
||||
status = pvfs_resolve_name(pvfs, req, rd->in.path, 0, &name);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
if (!name->exists) {
|
||||
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
||||
}
|
||||
|
||||
status = pvfs_access_check_simple(pvfs, req, name, SEC_STD_DELETE);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
status = pvfs_xattr_unlink_hook(pvfs, name->full_name);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
if (rmdir(name->full_name) == -1) {
|
||||
/* some olders systems don't return ENOTEMPTY to rmdir() */
|
||||
if (errno == EEXIST) {
|
||||
return NT_STATUS_DIRECTORY_NOT_EMPTY;
|
||||
}
|
||||
return pvfs_map_errno(pvfs, errno);
|
||||
}
|
||||
|
||||
notify_trigger(pvfs->notify_context,
|
||||
NOTIFY_ACTION_REMOVED,
|
||||
FILE_NOTIFY_CHANGE_DIR_NAME,
|
||||
name->full_name);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
@@ -0,0 +1,284 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
POSIX NTVFS backend - notify
|
||||
|
||||
Copyright (C) Andrew Tridgell 2006
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 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.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "vfs_posix.h"
|
||||
#include "lib/messaging/irpc.h"
|
||||
#include "messaging/messaging.h"
|
||||
#include "lib/util/dlinklist.h"
|
||||
#include "lib/events/events.h"
|
||||
|
||||
/* pending notifies buffer, hung off struct pvfs_file for open directories
|
||||
that have used change notify */
|
||||
struct pvfs_notify_buffer {
|
||||
struct pvfs_file *f;
|
||||
uint32_t num_changes;
|
||||
struct notify_changes *changes;
|
||||
uint32_t max_buffer_size;
|
||||
uint32_t current_buffer_size;
|
||||
|
||||
/* a list of requests waiting for events on this handle */
|
||||
struct notify_pending {
|
||||
struct notify_pending *next, *prev;
|
||||
struct ntvfs_request *req;
|
||||
union smb_notify *info;
|
||||
} *pending;
|
||||
};
|
||||
|
||||
/*
|
||||
send a notify on the next event run.
|
||||
*/
|
||||
static void pvfs_notify_send_next(struct event_context *ev, struct timed_event *te,
|
||||
struct timeval t, void *ptr)
|
||||
{
|
||||
struct ntvfs_request *req = talloc_get_type(ptr, struct ntvfs_request);
|
||||
req->async_states->send_fn(req);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
send a reply to a pending notify request
|
||||
*/
|
||||
static void pvfs_notify_send(struct pvfs_notify_buffer *notify_buffer,
|
||||
NTSTATUS status, BOOL immediate)
|
||||
{
|
||||
struct notify_pending *pending = notify_buffer->pending;
|
||||
struct ntvfs_request *req;
|
||||
union smb_notify *info;
|
||||
|
||||
if (notify_buffer->current_buffer_size > notify_buffer->max_buffer_size &&
|
||||
notify_buffer->num_changes != 0) {
|
||||
/* on buffer overflow return no changes and destroys the notify buffer */
|
||||
notify_buffer->num_changes = 0;
|
||||
while (notify_buffer->pending) {
|
||||
pvfs_notify_send(notify_buffer, NT_STATUS_OK, immediate);
|
||||
}
|
||||
talloc_free(notify_buffer);
|
||||
return;
|
||||
}
|
||||
|
||||
/* see if there is anyone waiting */
|
||||
if (notify_buffer->pending == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
DLIST_REMOVE(notify_buffer->pending, pending);
|
||||
|
||||
req = pending->req;
|
||||
info = pending->info;
|
||||
|
||||
info->nttrans.out.num_changes = notify_buffer->num_changes;
|
||||
info->nttrans.out.changes = talloc_steal(req, notify_buffer->changes);
|
||||
notify_buffer->num_changes = 0;
|
||||
notify_buffer->changes = NULL;
|
||||
notify_buffer->current_buffer_size = 0;
|
||||
|
||||
talloc_free(pending);
|
||||
|
||||
if (info->nttrans.out.num_changes != 0) {
|
||||
status = NT_STATUS_OK;
|
||||
}
|
||||
|
||||
req->async_states->status = status;
|
||||
|
||||
if (immediate) {
|
||||
req->async_states->send_fn(req);
|
||||
return;
|
||||
}
|
||||
|
||||
/* we can't call pvfs_notify_send() directly here, as that
|
||||
would free the request, and the ntvfs modules above us
|
||||
could use it, so call it on the next event */
|
||||
event_add_timed(req->ctx->event_ctx,
|
||||
req, timeval_zero(), pvfs_notify_send_next, req);
|
||||
}
|
||||
|
||||
/*
|
||||
destroy a notify buffer. Called when the handle is closed
|
||||
*/
|
||||
static int pvfs_notify_destructor(struct pvfs_notify_buffer *n)
|
||||
{
|
||||
notify_remove(n->f->pvfs->notify_context, n);
|
||||
n->f->notify_buffer = NULL;
|
||||
pvfs_notify_send(n, NT_STATUS_OK, True);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
called when a async notify event comes in
|
||||
*/
|
||||
static void pvfs_notify_callback(void *private, const struct notify_event *ev)
|
||||
{
|
||||
struct pvfs_notify_buffer *n = talloc_get_type(private, struct pvfs_notify_buffer);
|
||||
size_t len;
|
||||
struct notify_changes *n2;
|
||||
char *new_path;
|
||||
|
||||
n2 = talloc_realloc(n, n->changes, struct notify_changes, n->num_changes+1);
|
||||
if (n2 == NULL) {
|
||||
/* nothing much we can do for this */
|
||||
return;
|
||||
}
|
||||
n->changes = n2;
|
||||
|
||||
new_path = talloc_strdup(n->changes, ev->path);
|
||||
if (new_path == NULL) {
|
||||
return;
|
||||
}
|
||||
string_replace(new_path, '/', '\\');
|
||||
|
||||
n->changes[n->num_changes].action = ev->action;
|
||||
n->changes[n->num_changes].name.s = new_path;
|
||||
n->num_changes++;
|
||||
|
||||
/*
|
||||
work out how much room this will take in the buffer
|
||||
*/
|
||||
len = 12 + strlen_m(ev->path)*2;
|
||||
if (len & 3) {
|
||||
len += 4 - (len & 3);
|
||||
}
|
||||
n->current_buffer_size += len;
|
||||
|
||||
/* send what we have, unless its the first part of a rename */
|
||||
if (ev->action != NOTIFY_ACTION_OLD_NAME) {
|
||||
pvfs_notify_send(n, NT_STATUS_OK, True);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
setup a notify buffer on a directory handle
|
||||
*/
|
||||
static NTSTATUS pvfs_notify_setup(struct pvfs_state *pvfs, struct pvfs_file *f,
|
||||
uint32_t buffer_size, uint32_t filter, BOOL recursive)
|
||||
{
|
||||
NTSTATUS status;
|
||||
struct notify_entry e;
|
||||
|
||||
f->notify_buffer = talloc_zero(f, struct pvfs_notify_buffer);
|
||||
NT_STATUS_HAVE_NO_MEMORY(f->notify_buffer);
|
||||
|
||||
f->notify_buffer->max_buffer_size = buffer_size;
|
||||
f->notify_buffer->f = f;
|
||||
|
||||
e.filter = filter;
|
||||
e.path = f->handle->name->full_name;
|
||||
if (recursive) {
|
||||
e.subdir_filter = filter;
|
||||
} else {
|
||||
e.subdir_filter = 0;
|
||||
}
|
||||
|
||||
status = notify_add(pvfs->notify_context, &e,
|
||||
pvfs_notify_callback, f->notify_buffer);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
talloc_set_destructor(f->notify_buffer, pvfs_notify_destructor);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
called from the pvfs_wait code when either an event has come in, or
|
||||
the notify request has been cancelled
|
||||
*/
|
||||
static void pvfs_notify_end(void *private, enum pvfs_wait_notice reason)
|
||||
{
|
||||
struct pvfs_notify_buffer *notify_buffer = talloc_get_type(private,
|
||||
struct pvfs_notify_buffer);
|
||||
if (reason == PVFS_WAIT_CANCEL) {
|
||||
pvfs_notify_send(notify_buffer, NT_STATUS_CANCELLED, False);
|
||||
} else {
|
||||
pvfs_notify_send(notify_buffer, NT_STATUS_OK, True);
|
||||
}
|
||||
}
|
||||
|
||||
/* change notify request - always async. This request blocks until the
|
||||
event buffer is non-empty */
|
||||
NTSTATUS pvfs_notify(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
union smb_notify *info)
|
||||
{
|
||||
struct pvfs_state *pvfs = talloc_get_type(ntvfs->private_data,
|
||||
struct pvfs_state);
|
||||
struct pvfs_file *f;
|
||||
NTSTATUS status;
|
||||
struct notify_pending *pending;
|
||||
|
||||
if (info->nttrans.level != RAW_NOTIFY_NTTRANS) {
|
||||
return ntvfs_map_notify(ntvfs, req, info);
|
||||
}
|
||||
|
||||
f = pvfs_find_fd(pvfs, req, info->nttrans.in.file.ntvfs);
|
||||
if (!f) {
|
||||
return NT_STATUS_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
/* this request doesn't make sense unless its async */
|
||||
if (!(req->async_states->state & NTVFS_ASYNC_STATE_MAY_ASYNC)) {
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
/* its only valid for directories */
|
||||
if (f->handle->fd != -1) {
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
/* if the handle doesn't currently have a notify buffer then
|
||||
create one */
|
||||
if (f->notify_buffer == NULL) {
|
||||
status = pvfs_notify_setup(pvfs, f,
|
||||
info->nttrans.in.buffer_size,
|
||||
info->nttrans.in.completion_filter,
|
||||
info->nttrans.in.recursive);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
}
|
||||
|
||||
/* we update the max_buffer_size on each call, but we do not
|
||||
update the recursive flag or filter */
|
||||
f->notify_buffer->max_buffer_size = info->nttrans.in.buffer_size;
|
||||
|
||||
pending = talloc(f->notify_buffer, struct notify_pending);
|
||||
NT_STATUS_HAVE_NO_MEMORY(pending);
|
||||
|
||||
pending->req = talloc_reference(pending, req);
|
||||
NT_STATUS_HAVE_NO_MEMORY(pending->req);
|
||||
pending->info = info;
|
||||
|
||||
DLIST_ADD_END(f->notify_buffer->pending, pending, struct notify_pending *);
|
||||
|
||||
/* if the buffer is empty then start waiting */
|
||||
if (f->notify_buffer->num_changes == 0) {
|
||||
void *wait_handle =
|
||||
pvfs_wait_message(pvfs, req, -1, timeval_zero(),
|
||||
pvfs_notify_end, f->notify_buffer);
|
||||
NT_STATUS_HAVE_NO_MEMORY(wait_handle);
|
||||
talloc_steal(req, wait_handle);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
|
||||
pvfs_notify_send(f->notify_buffer, NT_STATUS_OK, False);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,425 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
POSIX NTVFS backend - read
|
||||
|
||||
Copyright (C) Andrew Tridgell 2004
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 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.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "vfs_posix.h"
|
||||
#include "librpc/gen_ndr/xattr.h"
|
||||
|
||||
|
||||
/*
|
||||
determine what access bits are needed for a call
|
||||
*/
|
||||
static uint32_t pvfs_fileinfo_access(union smb_fileinfo *info)
|
||||
{
|
||||
uint32_t needed;
|
||||
|
||||
switch (info->generic.level) {
|
||||
case RAW_FILEINFO_EA_LIST:
|
||||
case RAW_FILEINFO_ALL_EAS:
|
||||
needed = SEC_FILE_READ_EA;
|
||||
break;
|
||||
|
||||
case RAW_FILEINFO_IS_NAME_VALID:
|
||||
needed = 0;
|
||||
break;
|
||||
|
||||
case RAW_FILEINFO_SEC_DESC:
|
||||
needed = 0;
|
||||
if (info->query_secdesc.in.secinfo_flags & (SECINFO_OWNER|SECINFO_GROUP)) {
|
||||
needed |= SEC_STD_READ_CONTROL;
|
||||
}
|
||||
if (info->query_secdesc.in.secinfo_flags & SECINFO_DACL) {
|
||||
needed |= SEC_STD_READ_CONTROL;
|
||||
}
|
||||
if (info->query_secdesc.in.secinfo_flags & SECINFO_SACL) {
|
||||
needed |= SEC_FLAG_SYSTEM_SECURITY;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
needed = SEC_FILE_READ_ATTRIBUTE;
|
||||
break;
|
||||
}
|
||||
|
||||
return needed;
|
||||
}
|
||||
|
||||
/*
|
||||
reply to a RAW_FILEINFO_EA_LIST call
|
||||
*/
|
||||
NTSTATUS pvfs_query_ea_list(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx,
|
||||
struct pvfs_filename *name, int fd,
|
||||
uint_t num_names,
|
||||
struct ea_name *names,
|
||||
struct smb_ea_list *eas)
|
||||
{
|
||||
NTSTATUS status;
|
||||
int i;
|
||||
struct xattr_DosEAs *ealist = talloc(mem_ctx, struct xattr_DosEAs);
|
||||
|
||||
ZERO_STRUCTP(eas);
|
||||
status = pvfs_doseas_load(pvfs, name, fd, ealist);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
eas->eas = talloc_array(mem_ctx, struct ea_struct, num_names);
|
||||
if (eas->eas == NULL) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
eas->num_eas = num_names;
|
||||
for (i=0;i<num_names;i++) {
|
||||
int j;
|
||||
eas->eas[i].flags = 0;
|
||||
eas->eas[i].name.s = names[i].name.s;
|
||||
eas->eas[i].value = data_blob(NULL, 0);
|
||||
for (j=0;j<ealist->num_eas;j++) {
|
||||
if (strcasecmp_m(eas->eas[i].name.s,
|
||||
ealist->eas[j].name) == 0) {
|
||||
eas->eas[i].value = ealist->eas[j].value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
reply to a RAW_FILEINFO_ALL_EAS call
|
||||
*/
|
||||
static NTSTATUS pvfs_query_all_eas(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx,
|
||||
struct pvfs_filename *name, int fd,
|
||||
struct smb_ea_list *eas)
|
||||
{
|
||||
NTSTATUS status;
|
||||
int i;
|
||||
struct xattr_DosEAs *ealist = talloc(mem_ctx, struct xattr_DosEAs);
|
||||
|
||||
ZERO_STRUCTP(eas);
|
||||
status = pvfs_doseas_load(pvfs, name, fd, ealist);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
eas->eas = talloc_array(mem_ctx, struct ea_struct, ealist->num_eas);
|
||||
if (eas->eas == NULL) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
eas->num_eas = 0;
|
||||
for (i=0;i<ealist->num_eas;i++) {
|
||||
eas->eas[eas->num_eas].flags = 0;
|
||||
eas->eas[eas->num_eas].name.s = ealist->eas[i].name;
|
||||
eas->eas[eas->num_eas].value = ealist->eas[i].value;
|
||||
eas->num_eas++;
|
||||
}
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
approximately map a struct pvfs_filename to a generic fileinfo struct
|
||||
*/
|
||||
static NTSTATUS pvfs_map_fileinfo(struct pvfs_state *pvfs,
|
||||
struct ntvfs_request *req,
|
||||
struct pvfs_filename *name, union smb_fileinfo *info,
|
||||
int fd)
|
||||
{
|
||||
switch (info->generic.level) {
|
||||
case RAW_FILEINFO_GENERIC:
|
||||
return NT_STATUS_INVALID_LEVEL;
|
||||
|
||||
case RAW_FILEINFO_GETATTR:
|
||||
info->getattr.out.attrib = name->dos.attrib;
|
||||
info->getattr.out.size = name->st.st_size;
|
||||
info->getattr.out.write_time = nt_time_to_unix(name->dos.write_time);
|
||||
return NT_STATUS_OK;
|
||||
|
||||
case RAW_FILEINFO_GETATTRE:
|
||||
case RAW_FILEINFO_STANDARD:
|
||||
info->standard.out.create_time = nt_time_to_unix(name->dos.create_time);
|
||||
info->standard.out.access_time = nt_time_to_unix(name->dos.access_time);
|
||||
info->standard.out.write_time = nt_time_to_unix(name->dos.write_time);
|
||||
info->standard.out.size = name->st.st_size;
|
||||
info->standard.out.alloc_size = name->dos.alloc_size;
|
||||
info->standard.out.attrib = name->dos.attrib;
|
||||
return NT_STATUS_OK;
|
||||
|
||||
case RAW_FILEINFO_EA_SIZE:
|
||||
info->ea_size.out.create_time = nt_time_to_unix(name->dos.create_time);
|
||||
info->ea_size.out.access_time = nt_time_to_unix(name->dos.access_time);
|
||||
info->ea_size.out.write_time = nt_time_to_unix(name->dos.write_time);
|
||||
info->ea_size.out.size = name->st.st_size;
|
||||
info->ea_size.out.alloc_size = name->dos.alloc_size;
|
||||
info->ea_size.out.attrib = name->dos.attrib;
|
||||
info->ea_size.out.ea_size = name->dos.ea_size;
|
||||
return NT_STATUS_OK;
|
||||
|
||||
case RAW_FILEINFO_EA_LIST:
|
||||
return pvfs_query_ea_list(pvfs, req, name, fd,
|
||||
info->ea_list.in.num_names,
|
||||
info->ea_list.in.ea_names,
|
||||
&info->ea_list.out);
|
||||
|
||||
case RAW_FILEINFO_ALL_EAS:
|
||||
return pvfs_query_all_eas(pvfs, req, name, fd, &info->all_eas.out);
|
||||
|
||||
case RAW_FILEINFO_IS_NAME_VALID:
|
||||
return NT_STATUS_OK;
|
||||
|
||||
case RAW_FILEINFO_BASIC_INFO:
|
||||
case RAW_FILEINFO_BASIC_INFORMATION:
|
||||
info->basic_info.out.create_time = name->dos.create_time;
|
||||
info->basic_info.out.access_time = name->dos.access_time;
|
||||
info->basic_info.out.write_time = name->dos.write_time;
|
||||
info->basic_info.out.change_time = name->dos.change_time;
|
||||
info->basic_info.out.attrib = name->dos.attrib;
|
||||
return NT_STATUS_OK;
|
||||
|
||||
case RAW_FILEINFO_STANDARD_INFO:
|
||||
case RAW_FILEINFO_STANDARD_INFORMATION:
|
||||
info->standard_info.out.alloc_size = name->dos.alloc_size;
|
||||
info->standard_info.out.size = name->st.st_size;
|
||||
info->standard_info.out.nlink = name->dos.nlink;
|
||||
info->standard_info.out.delete_pending = 0; /* only for qfileinfo */
|
||||
info->standard_info.out.directory =
|
||||
(name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)? 1 : 0;
|
||||
return NT_STATUS_OK;
|
||||
|
||||
case RAW_FILEINFO_EA_INFO:
|
||||
case RAW_FILEINFO_EA_INFORMATION:
|
||||
info->ea_info.out.ea_size = name->dos.ea_size;
|
||||
return NT_STATUS_OK;
|
||||
|
||||
case RAW_FILEINFO_NAME_INFO:
|
||||
case RAW_FILEINFO_NAME_INFORMATION:
|
||||
info->name_info.out.fname.s = name->original_name;
|
||||
return NT_STATUS_OK;
|
||||
|
||||
case RAW_FILEINFO_ALL_INFO:
|
||||
case RAW_FILEINFO_ALL_INFORMATION:
|
||||
info->all_info.out.create_time = name->dos.create_time;
|
||||
info->all_info.out.access_time = name->dos.access_time;
|
||||
info->all_info.out.write_time = name->dos.write_time;
|
||||
info->all_info.out.change_time = name->dos.change_time;
|
||||
info->all_info.out.attrib = name->dos.attrib;
|
||||
info->all_info.out.alloc_size = name->dos.alloc_size;
|
||||
info->all_info.out.size = name->st.st_size;
|
||||
info->all_info.out.nlink = name->dos.nlink;
|
||||
info->all_info.out.delete_pending = 0; /* only set by qfileinfo */
|
||||
info->all_info.out.directory =
|
||||
(name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)? 1 : 0;
|
||||
info->all_info.out.ea_size = name->dos.ea_size;
|
||||
info->all_info.out.fname.s = name->original_name;
|
||||
return NT_STATUS_OK;
|
||||
|
||||
case RAW_FILEINFO_ALT_NAME_INFO:
|
||||
case RAW_FILEINFO_ALT_NAME_INFORMATION:
|
||||
info->name_info.out.fname.s = pvfs_short_name(pvfs, name, name);
|
||||
return NT_STATUS_OK;
|
||||
|
||||
case RAW_FILEINFO_STREAM_INFO:
|
||||
case RAW_FILEINFO_STREAM_INFORMATION:
|
||||
return pvfs_stream_information(pvfs, req, name, fd, &info->stream_info.out);
|
||||
|
||||
case RAW_FILEINFO_COMPRESSION_INFO:
|
||||
case RAW_FILEINFO_COMPRESSION_INFORMATION:
|
||||
info->compression_info.out.compressed_size = name->st.st_size;
|
||||
info->compression_info.out.format = 0;
|
||||
info->compression_info.out.unit_shift = 0;
|
||||
info->compression_info.out.chunk_shift = 0;
|
||||
info->compression_info.out.cluster_shift = 0;
|
||||
return NT_STATUS_OK;
|
||||
|
||||
case RAW_FILEINFO_INTERNAL_INFORMATION:
|
||||
info->internal_information.out.file_id = name->dos.file_id;
|
||||
return NT_STATUS_OK;
|
||||
|
||||
case RAW_FILEINFO_ACCESS_INFORMATION:
|
||||
info->access_information.out.access_flags = 0; /* only set by qfileinfo */
|
||||
return NT_STATUS_OK;
|
||||
|
||||
case RAW_FILEINFO_POSITION_INFORMATION:
|
||||
info->position_information.out.position = 0; /* only set by qfileinfo */
|
||||
return NT_STATUS_OK;
|
||||
|
||||
case RAW_FILEINFO_MODE_INFORMATION:
|
||||
info->mode_information.out.mode = 0; /* only set by qfileinfo */
|
||||
return NT_STATUS_OK;
|
||||
|
||||
case RAW_FILEINFO_ALIGNMENT_INFORMATION:
|
||||
info->alignment_information.out.alignment_requirement = 0;
|
||||
return NT_STATUS_OK;
|
||||
|
||||
case RAW_FILEINFO_NETWORK_OPEN_INFORMATION:
|
||||
info->network_open_information.out.create_time = name->dos.create_time;
|
||||
info->network_open_information.out.access_time = name->dos.access_time;
|
||||
info->network_open_information.out.write_time = name->dos.write_time;
|
||||
info->network_open_information.out.change_time = name->dos.change_time;
|
||||
info->network_open_information.out.alloc_size = name->dos.alloc_size;
|
||||
info->network_open_information.out.size = name->st.st_size;
|
||||
info->network_open_information.out.attrib = name->dos.attrib;
|
||||
return NT_STATUS_OK;
|
||||
|
||||
case RAW_FILEINFO_ATTRIBUTE_TAG_INFORMATION:
|
||||
info->attribute_tag_information.out.attrib = name->dos.attrib;
|
||||
info->attribute_tag_information.out.reparse_tag = 0;
|
||||
return NT_STATUS_OK;
|
||||
|
||||
case RAW_FILEINFO_SEC_DESC:
|
||||
return pvfs_acl_query(pvfs, req, name, fd, info);
|
||||
|
||||
case RAW_FILEINFO_SMB2_ALL_INFORMATION:
|
||||
info->all_info2.out.create_time = name->dos.create_time;
|
||||
info->all_info2.out.access_time = name->dos.access_time;
|
||||
info->all_info2.out.write_time = name->dos.write_time;
|
||||
info->all_info2.out.change_time = name->dos.change_time;
|
||||
info->all_info2.out.attrib = name->dos.attrib;
|
||||
info->all_info2.out.unknown1 = 0;
|
||||
info->all_info2.out.alloc_size = name->dos.alloc_size;
|
||||
info->all_info2.out.size = name->st.st_size;
|
||||
info->all_info2.out.nlink = name->dos.nlink;
|
||||
info->all_info2.out.delete_pending = 0; /* only set by qfileinfo */
|
||||
info->all_info2.out.directory =
|
||||
(name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)? 1 : 0;
|
||||
info->all_info2.out.file_id = name->dos.file_id;
|
||||
info->all_info2.out.ea_size = name->dos.ea_size;
|
||||
info->all_info2.out.access_mask = 0; /* only set by qfileinfo */
|
||||
info->all_info2.out.position = 0; /* only set by qfileinfo */
|
||||
info->all_info2.out.mode = 0; /* only set by qfileinfo */
|
||||
info->all_info2.out.fname.s = name->original_name;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
return NT_STATUS_INVALID_LEVEL;
|
||||
}
|
||||
|
||||
/*
|
||||
return info on a pathname
|
||||
*/
|
||||
NTSTATUS pvfs_qpathinfo(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_fileinfo *info)
|
||||
{
|
||||
struct pvfs_state *pvfs = ntvfs->private_data;
|
||||
struct pvfs_filename *name;
|
||||
NTSTATUS status;
|
||||
|
||||
/* resolve the cifs name to a posix name */
|
||||
status = pvfs_resolve_name(pvfs, req, info->generic.in.file.path, PVFS_RESOLVE_STREAMS, &name);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
if (!name->stream_exists) {
|
||||
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
||||
}
|
||||
|
||||
status = pvfs_can_stat(pvfs, req, name);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return NT_STATUS_DELETE_PENDING;
|
||||
}
|
||||
|
||||
status = pvfs_access_check_simple(pvfs, req, name,
|
||||
pvfs_fileinfo_access(info));
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
status = pvfs_map_fileinfo(pvfs, req, name, info, -1);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
query info on a open file
|
||||
*/
|
||||
NTSTATUS pvfs_qfileinfo(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_fileinfo *info)
|
||||
{
|
||||
struct pvfs_state *pvfs = ntvfs->private_data;
|
||||
struct pvfs_file *f;
|
||||
struct pvfs_file_handle *h;
|
||||
NTSTATUS status;
|
||||
uint32_t access_needed;
|
||||
|
||||
f = pvfs_find_fd(pvfs, req, info->generic.in.file.ntvfs);
|
||||
if (!f) {
|
||||
return NT_STATUS_INVALID_HANDLE;
|
||||
}
|
||||
h = f->handle;
|
||||
|
||||
access_needed = pvfs_fileinfo_access(info);
|
||||
if ((f->access_mask & access_needed) != access_needed) {
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
/* update the file information */
|
||||
status = pvfs_resolve_name_fd(pvfs, h->fd, h->name);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
status = pvfs_map_fileinfo(pvfs, req, h->name, info, h->fd);
|
||||
|
||||
/* a qfileinfo can fill in a bit more info than a qpathinfo -
|
||||
now modify the levels that need to be fixed up */
|
||||
switch (info->generic.level) {
|
||||
case RAW_FILEINFO_STANDARD_INFO:
|
||||
case RAW_FILEINFO_STANDARD_INFORMATION:
|
||||
if (pvfs_delete_on_close_set(pvfs, h, NULL, NULL)) {
|
||||
info->standard_info.out.delete_pending = 1;
|
||||
info->standard_info.out.nlink--;
|
||||
}
|
||||
break;
|
||||
|
||||
case RAW_FILEINFO_ALL_INFO:
|
||||
case RAW_FILEINFO_ALL_INFORMATION:
|
||||
if (pvfs_delete_on_close_set(pvfs, h, NULL, NULL)) {
|
||||
info->all_info.out.delete_pending = 1;
|
||||
info->all_info.out.nlink--;
|
||||
}
|
||||
break;
|
||||
|
||||
case RAW_FILEINFO_POSITION_INFORMATION:
|
||||
info->position_information.out.position = h->position;
|
||||
break;
|
||||
|
||||
case RAW_FILEINFO_ACCESS_INFORMATION:
|
||||
info->access_information.out.access_flags = f->access_mask;
|
||||
break;
|
||||
|
||||
case RAW_FILEINFO_MODE_INFORMATION:
|
||||
info->mode_information.out.mode = h->mode;
|
||||
break;
|
||||
|
||||
case RAW_FILEINFO_SMB2_ALL_INFORMATION:
|
||||
if (pvfs_delete_on_close_set(pvfs, h, NULL, NULL)) {
|
||||
info->all_info2.out.delete_pending = 1;
|
||||
info->all_info2.out.nlink--;
|
||||
}
|
||||
info->all_info2.out.position = h->position;
|
||||
info->all_info2.out.access_mask = f->access_mask;
|
||||
info->all_info2.out.mode = h->mode;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
POSIX NTVFS backend - read
|
||||
|
||||
Copyright (C) Andrew Tridgell 2004
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 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.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "vfs_posix.h"
|
||||
#include "librpc/gen_ndr/security.h"
|
||||
|
||||
/*
|
||||
read from a file
|
||||
*/
|
||||
NTSTATUS pvfs_read(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_read *rd)
|
||||
{
|
||||
struct pvfs_state *pvfs = ntvfs->private_data;
|
||||
ssize_t ret;
|
||||
struct pvfs_file *f;
|
||||
NTSTATUS status;
|
||||
uint32_t maxcnt;
|
||||
uint32_t mask;
|
||||
|
||||
if (rd->generic.level != RAW_READ_READX) {
|
||||
return ntvfs_map_read(ntvfs, req, rd);
|
||||
}
|
||||
|
||||
f = pvfs_find_fd(pvfs, req, rd->readx.in.file.ntvfs);
|
||||
if (!f) {
|
||||
return NT_STATUS_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
if (f->handle->fd == -1) {
|
||||
return NT_STATUS_FILE_IS_A_DIRECTORY;
|
||||
}
|
||||
|
||||
mask = SEC_FILE_READ_DATA;
|
||||
if (rd->readx.in.read_for_execute) {
|
||||
mask |= SEC_FILE_EXECUTE;
|
||||
}
|
||||
if (!(f->access_mask & mask)) {
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
maxcnt = rd->readx.in.maxcnt;
|
||||
if (maxcnt > UINT16_MAX && req->ctx->protocol < PROTOCOL_SMB2) {
|
||||
maxcnt = 0;
|
||||
}
|
||||
|
||||
status = pvfs_check_lock(pvfs, f, req->smbpid,
|
||||
rd->readx.in.offset,
|
||||
maxcnt,
|
||||
READ_LOCK);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
if (f->handle->name->stream_name) {
|
||||
ret = pvfs_stream_read(pvfs, f->handle,
|
||||
rd->readx.out.data, maxcnt, rd->readx.in.offset);
|
||||
} else {
|
||||
ret = pread(f->handle->fd,
|
||||
rd->readx.out.data,
|
||||
maxcnt,
|
||||
rd->readx.in.offset);
|
||||
}
|
||||
if (ret == -1) {
|
||||
return pvfs_map_errno(pvfs, errno);
|
||||
}
|
||||
|
||||
f->handle->position = f->handle->seek_offset = rd->readx.in.offset + ret;
|
||||
|
||||
rd->readx.out.nread = ret;
|
||||
rd->readx.out.remaining = 0xFFFF;
|
||||
rd->readx.out.compaction_mode = 0;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
@@ -0,0 +1,478 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
POSIX NTVFS backend - rename
|
||||
|
||||
Copyright (C) Andrew Tridgell 2004
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 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.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "vfs_posix.h"
|
||||
#include "librpc/gen_ndr/security.h"
|
||||
|
||||
|
||||
/*
|
||||
do a file rename, and send any notify triggers
|
||||
*/
|
||||
NTSTATUS pvfs_do_rename(struct pvfs_state *pvfs, const struct pvfs_filename *name1,
|
||||
const char *name2)
|
||||
{
|
||||
const char *r1, *r2;
|
||||
uint32_t mask;
|
||||
|
||||
if (rename(name1->full_name, name2) == -1) {
|
||||
return pvfs_map_errno(pvfs, errno);
|
||||
}
|
||||
|
||||
if (name1->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) {
|
||||
mask = FILE_NOTIFY_CHANGE_DIR_NAME;
|
||||
} else {
|
||||
mask = FILE_NOTIFY_CHANGE_FILE_NAME;
|
||||
}
|
||||
/*
|
||||
renames to the same directory cause a OLD_NAME->NEW_NAME notify.
|
||||
renames to a different directory are considered a remove/add
|
||||
*/
|
||||
r1 = strrchr_m(name1->full_name, '/');
|
||||
r2 = strrchr_m(name2, '/');
|
||||
|
||||
if ((r1-name1->full_name) != (r2-name2) ||
|
||||
strncmp(name1->full_name, name2, r1-name1->full_name) != 0) {
|
||||
notify_trigger(pvfs->notify_context,
|
||||
NOTIFY_ACTION_REMOVED,
|
||||
mask,
|
||||
name1->full_name);
|
||||
notify_trigger(pvfs->notify_context,
|
||||
NOTIFY_ACTION_ADDED,
|
||||
mask,
|
||||
name2);
|
||||
} else {
|
||||
notify_trigger(pvfs->notify_context,
|
||||
NOTIFY_ACTION_OLD_NAME,
|
||||
mask,
|
||||
name1->full_name);
|
||||
notify_trigger(pvfs->notify_context,
|
||||
NOTIFY_ACTION_NEW_NAME,
|
||||
mask,
|
||||
name2);
|
||||
}
|
||||
|
||||
/* this is a strange one. w2k3 gives an additional event for CHANGE_ATTRIBUTES
|
||||
and CHANGE_CREATION on the new file when renming files, but not
|
||||
directories */
|
||||
if ((name1->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) == 0) {
|
||||
notify_trigger(pvfs->notify_context,
|
||||
NOTIFY_ACTION_MODIFIED,
|
||||
FILE_NOTIFY_CHANGE_ATTRIBUTES|FILE_NOTIFY_CHANGE_CREATION,
|
||||
name2);
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
resolve a wildcard rename pattern. This works on one component of the name
|
||||
*/
|
||||
static const char *pvfs_resolve_wildcard_component(TALLOC_CTX *mem_ctx,
|
||||
const char *fname,
|
||||
const char *pattern)
|
||||
{
|
||||
const char *p1, *p2;
|
||||
char *dest, *d;
|
||||
|
||||
/* the length is bounded by the length of the two strings combined */
|
||||
dest = talloc_size(mem_ctx, strlen(fname) + strlen(pattern) + 1);
|
||||
if (dest == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
p1 = fname;
|
||||
p2 = pattern;
|
||||
d = dest;
|
||||
|
||||
while (*p2) {
|
||||
codepoint_t c1, c2;
|
||||
size_t c_size1, c_size2;
|
||||
c1 = next_codepoint(p1, &c_size1);
|
||||
c2 = next_codepoint(p2, &c_size2);
|
||||
if (c2 == '?') {
|
||||
d += push_codepoint(d, c1);
|
||||
} else if (c2 == '*') {
|
||||
memcpy(d, p1, strlen(p1));
|
||||
d += strlen(p1);
|
||||
break;
|
||||
} else {
|
||||
d += push_codepoint(d, c2);
|
||||
}
|
||||
|
||||
p1 += c_size1;
|
||||
p2 += c_size2;
|
||||
}
|
||||
|
||||
*d = 0;
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
/*
|
||||
resolve a wildcard rename pattern.
|
||||
*/
|
||||
static const char *pvfs_resolve_wildcard(TALLOC_CTX *mem_ctx,
|
||||
const char *fname,
|
||||
const char *pattern)
|
||||
{
|
||||
const char *base1, *base2;
|
||||
const char *ext1, *ext2;
|
||||
char *p;
|
||||
|
||||
/* break into base part plus extension */
|
||||
p = strrchr_m(fname, '.');
|
||||
if (p == NULL) {
|
||||
ext1 = "";
|
||||
base1 = fname;
|
||||
} else {
|
||||
ext1 = talloc_strdup(mem_ctx, p+1);
|
||||
base1 = talloc_strndup(mem_ctx, fname, p-fname);
|
||||
}
|
||||
if (ext1 == NULL || base1 == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
p = strrchr_m(pattern, '.');
|
||||
if (p == NULL) {
|
||||
ext2 = "";
|
||||
base2 = fname;
|
||||
} else {
|
||||
ext2 = talloc_strdup(mem_ctx, p+1);
|
||||
base2 = talloc_strndup(mem_ctx, pattern, p-pattern);
|
||||
}
|
||||
if (ext2 == NULL || base2 == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
base1 = pvfs_resolve_wildcard_component(mem_ctx, base1, base2);
|
||||
ext1 = pvfs_resolve_wildcard_component(mem_ctx, ext1, ext2);
|
||||
if (base1 == NULL || ext1 == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (*ext1 == 0) {
|
||||
return base1;
|
||||
}
|
||||
|
||||
return talloc_asprintf(mem_ctx, "%s.%s", base1, ext1);
|
||||
}
|
||||
|
||||
/*
|
||||
rename one file from a wildcard set
|
||||
*/
|
||||
static NTSTATUS pvfs_rename_one(struct pvfs_state *pvfs,
|
||||
struct ntvfs_request *req,
|
||||
const char *dir_path,
|
||||
const char *fname1,
|
||||
const char *fname2,
|
||||
uint16_t attrib)
|
||||
{
|
||||
struct pvfs_filename *name1, *name2;
|
||||
TALLOC_CTX *mem_ctx = talloc_new(req);
|
||||
NTSTATUS status;
|
||||
struct odb_lock *lck, *lck2;
|
||||
|
||||
/* resolve the wildcard pattern for this name */
|
||||
fname2 = pvfs_resolve_wildcard(mem_ctx, fname1, fname2);
|
||||
if (fname2 == NULL) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
/* get a pvfs_filename source object */
|
||||
status = pvfs_resolve_partial(pvfs, mem_ctx,
|
||||
dir_path, fname1, &name1);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
/* make sure its matches the given attributes */
|
||||
status = pvfs_match_attrib(pvfs, name1, attrib, 0);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
status = pvfs_can_rename(pvfs, req, name1, &lck);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
goto failed;
|
||||
}
|
||||
|
||||
/* get a pvfs_filename dest object */
|
||||
status = pvfs_resolve_partial(pvfs, mem_ctx,
|
||||
dir_path, fname2, &name2);
|
||||
if (NT_STATUS_IS_OK(status)) {
|
||||
status = pvfs_can_delete(pvfs, req, name2, &lck2);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
goto failed;
|
||||
}
|
||||
}
|
||||
|
||||
status = NT_STATUS_OK;
|
||||
|
||||
fname2 = talloc_asprintf(mem_ctx, "%s/%s", dir_path, fname2);
|
||||
if (fname2 == NULL) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
status = pvfs_do_rename(pvfs, name1, fname2);
|
||||
|
||||
if (NT_STATUS_IS_OK(status)) {
|
||||
status = odb_rename(lck, fname2);
|
||||
}
|
||||
|
||||
failed:
|
||||
talloc_free(mem_ctx);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
rename a set of files with wildcards
|
||||
*/
|
||||
static NTSTATUS pvfs_rename_wildcard(struct pvfs_state *pvfs,
|
||||
struct ntvfs_request *req,
|
||||
union smb_rename *ren,
|
||||
struct pvfs_filename *name1,
|
||||
struct pvfs_filename *name2)
|
||||
{
|
||||
struct pvfs_dir *dir;
|
||||
NTSTATUS status;
|
||||
off_t ofs = 0;
|
||||
const char *fname, *fname2, *dir_path;
|
||||
uint16_t attrib = ren->rename.in.attrib;
|
||||
int total_renamed = 0;
|
||||
|
||||
/* get list of matching files */
|
||||
status = pvfs_list_start(pvfs, name1, req, &dir);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
status = NT_STATUS_NO_SUCH_FILE;
|
||||
|
||||
dir_path = pvfs_list_unix_path(dir);
|
||||
|
||||
/* only allow wildcard renames within a directory */
|
||||
if (strncmp(dir_path, name2->full_name, strlen(dir_path)) != 0 ||
|
||||
name2->full_name[strlen(dir_path)] != '/' ||
|
||||
strchr(name2->full_name + strlen(dir_path) + 1, '/')) {
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
fname2 = talloc_strdup(name2, name2->full_name + strlen(dir_path) + 1);
|
||||
if (fname2 == NULL) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
while ((fname = pvfs_list_next(dir, &ofs))) {
|
||||
status = pvfs_rename_one(pvfs, req,
|
||||
dir_path,
|
||||
fname, fname2, attrib);
|
||||
if (NT_STATUS_IS_OK(status)) {
|
||||
total_renamed++;
|
||||
}
|
||||
}
|
||||
|
||||
if (total_renamed == 0) {
|
||||
return status;
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
rename a set of files - SMBmv interface
|
||||
*/
|
||||
static NTSTATUS pvfs_rename_mv(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_rename *ren)
|
||||
{
|
||||
struct pvfs_state *pvfs = ntvfs->private_data;
|
||||
NTSTATUS status;
|
||||
struct pvfs_filename *name1, *name2;
|
||||
struct odb_lock *lck;
|
||||
|
||||
/* resolve the cifs name to a posix name */
|
||||
status = pvfs_resolve_name(pvfs, req, ren->rename.in.pattern1,
|
||||
PVFS_RESOLVE_WILDCARD, &name1);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
status = pvfs_resolve_name(pvfs, req, ren->rename.in.pattern2,
|
||||
PVFS_RESOLVE_WILDCARD, &name2);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
if (name1->has_wildcard || name2->has_wildcard) {
|
||||
return pvfs_rename_wildcard(pvfs, req, ren, name1, name2);
|
||||
}
|
||||
|
||||
if (!name1->exists) {
|
||||
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
||||
}
|
||||
|
||||
if (strcmp(name1->full_name, name2->full_name) == 0) {
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
if (name2->exists) {
|
||||
return NT_STATUS_OBJECT_NAME_COLLISION;
|
||||
}
|
||||
|
||||
status = pvfs_match_attrib(pvfs, name1, ren->rename.in.attrib, 0);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
status = pvfs_access_check_parent(pvfs, req, name2, SEC_DIR_ADD_FILE);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
status = pvfs_can_rename(pvfs, req, name1, &lck);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
status = pvfs_do_rename(pvfs, name1, name2->full_name);
|
||||
if (NT_STATUS_IS_OK(status)) {
|
||||
status = odb_rename(lck, name2->full_name);
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
rename a set of files - ntrename interface
|
||||
*/
|
||||
static NTSTATUS pvfs_rename_nt(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_rename *ren)
|
||||
{
|
||||
struct pvfs_state *pvfs = ntvfs->private_data;
|
||||
NTSTATUS status;
|
||||
struct pvfs_filename *name1, *name2;
|
||||
struct odb_lock *lck;
|
||||
|
||||
switch (ren->ntrename.in.flags) {
|
||||
case RENAME_FLAG_RENAME:
|
||||
case RENAME_FLAG_HARD_LINK:
|
||||
case RENAME_FLAG_COPY:
|
||||
case RENAME_FLAG_MOVE_CLUSTER_INFORMATION:
|
||||
break;
|
||||
default:
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
/* resolve the cifs name to a posix name */
|
||||
status = pvfs_resolve_name(pvfs, req, ren->ntrename.in.old_name,
|
||||
PVFS_RESOLVE_WILDCARD, &name1);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
status = pvfs_resolve_name(pvfs, req, ren->ntrename.in.new_name,
|
||||
PVFS_RESOLVE_WILDCARD, &name2);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
if (name1->has_wildcard || name2->has_wildcard) {
|
||||
return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
|
||||
}
|
||||
|
||||
if (!name1->exists) {
|
||||
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
||||
}
|
||||
|
||||
if (strcmp(name1->full_name, name2->full_name) == 0) {
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
if (name2->exists) {
|
||||
return NT_STATUS_OBJECT_NAME_COLLISION;
|
||||
}
|
||||
|
||||
status = pvfs_match_attrib(pvfs, name1, ren->ntrename.in.attrib, 0);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
status = pvfs_can_rename(pvfs, req, name1, &lck);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
switch (ren->ntrename.in.flags) {
|
||||
case RENAME_FLAG_RENAME:
|
||||
status = pvfs_access_check_parent(pvfs, req, name2, SEC_DIR_ADD_FILE);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
status = pvfs_do_rename(pvfs, name1, name2->full_name);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
break;
|
||||
|
||||
case RENAME_FLAG_HARD_LINK:
|
||||
status = pvfs_access_check_parent(pvfs, req, name2, SEC_DIR_ADD_FILE);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
if (link(name1->full_name, name2->full_name) == -1) {
|
||||
return pvfs_map_errno(pvfs, errno);
|
||||
}
|
||||
break;
|
||||
|
||||
case RENAME_FLAG_COPY:
|
||||
status = pvfs_access_check_parent(pvfs, req, name2, SEC_DIR_ADD_FILE);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
return pvfs_copy_file(pvfs, name1, name2);
|
||||
|
||||
case RENAME_FLAG_MOVE_CLUSTER_INFORMATION:
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
|
||||
default:
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
rename a set of files - ntrename interface
|
||||
*/
|
||||
NTSTATUS pvfs_rename(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_rename *ren)
|
||||
{
|
||||
switch (ren->generic.level) {
|
||||
case RAW_RENAME_RENAME:
|
||||
return pvfs_rename_mv(ntvfs, req, ren);
|
||||
|
||||
case RAW_RENAME_NTRENAME:
|
||||
return pvfs_rename_nt(ntvfs, req, ren);
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return NT_STATUS_INVALID_LEVEL;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,634 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
POSIX NTVFS backend - filename resolution
|
||||
|
||||
Copyright (C) Andrew Tridgell 2004
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
/*
|
||||
this is the core code for converting a filename from the format as
|
||||
given by a client to a posix filename, including any case-matching
|
||||
required, and checks for legal characters
|
||||
*/
|
||||
|
||||
|
||||
#include "includes.h"
|
||||
#include "vfs_posix.h"
|
||||
#include "system/dir.h"
|
||||
|
||||
/*
|
||||
compare two filename components. This is where the name mangling hook will go
|
||||
*/
|
||||
static int component_compare(struct pvfs_state *pvfs, const char *comp, const char *name)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = strcasecmp_m(comp, name);
|
||||
|
||||
if (ret != 0) {
|
||||
char *shortname = pvfs_short_name_component(pvfs, name);
|
||||
if (shortname) {
|
||||
ret = strcasecmp_m(comp, shortname);
|
||||
talloc_free(shortname);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
search for a filename in a case insensitive fashion
|
||||
|
||||
TODO: add a cache for previously resolved case-insensitive names
|
||||
TODO: add mangled name support
|
||||
*/
|
||||
static NTSTATUS pvfs_case_search(struct pvfs_state *pvfs, struct pvfs_filename *name)
|
||||
{
|
||||
/* break into a series of components */
|
||||
int num_components;
|
||||
char **components;
|
||||
char *p, *partial_name;
|
||||
int i;
|
||||
|
||||
/* break up the full name info pathname components */
|
||||
num_components=2;
|
||||
p = name->full_name + strlen(pvfs->base_directory) + 1;
|
||||
|
||||
for (;*p;p++) {
|
||||
if (*p == '/') {
|
||||
num_components++;
|
||||
}
|
||||
}
|
||||
|
||||
components = talloc_array(name, char *, num_components);
|
||||
p = name->full_name + strlen(pvfs->base_directory);
|
||||
*p++ = 0;
|
||||
|
||||
components[0] = name->full_name;
|
||||
|
||||
for (i=1;i<num_components;i++) {
|
||||
components[i] = p;
|
||||
p = strchr(p, '/');
|
||||
if (p) *p++ = 0;
|
||||
if (pvfs_is_reserved_name(pvfs, components[i])) {
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
}
|
||||
|
||||
partial_name = talloc_strdup(name, components[0]);
|
||||
if (!partial_name) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
/* for each component, check if it exists as-is, and if not then
|
||||
do a directory scan */
|
||||
for (i=1;i<num_components;i++) {
|
||||
char *test_name;
|
||||
DIR *dir;
|
||||
struct dirent *de;
|
||||
char *long_component;
|
||||
|
||||
/* possibly remap from the short name cache */
|
||||
long_component = pvfs_mangled_lookup(pvfs, name, components[i]);
|
||||
if (long_component) {
|
||||
components[i] = long_component;
|
||||
}
|
||||
|
||||
test_name = talloc_asprintf(name, "%s/%s", partial_name, components[i]);
|
||||
if (!test_name) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
/* check if this component exists as-is */
|
||||
if (stat(test_name, &name->st) == 0) {
|
||||
if (i<num_components-1 && !S_ISDIR(name->st.st_mode)) {
|
||||
return NT_STATUS_OBJECT_PATH_NOT_FOUND;
|
||||
}
|
||||
talloc_free(partial_name);
|
||||
partial_name = test_name;
|
||||
if (i == num_components - 1) {
|
||||
name->exists = True;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
/* the filesystem might be case insensitive, in which
|
||||
case a search is pointless unless the name is
|
||||
mangled */
|
||||
if ((pvfs->flags & PVFS_FLAG_CI_FILESYSTEM) &&
|
||||
!pvfs_is_mangled_component(pvfs, components[i])) {
|
||||
if (i < num_components-1) {
|
||||
return NT_STATUS_OBJECT_PATH_NOT_FOUND;
|
||||
}
|
||||
partial_name = test_name;
|
||||
continue;
|
||||
}
|
||||
|
||||
dir = opendir(partial_name);
|
||||
if (!dir) {
|
||||
return pvfs_map_errno(pvfs, errno);
|
||||
}
|
||||
|
||||
while ((de = readdir(dir))) {
|
||||
if (component_compare(pvfs, components[i], de->d_name) == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!de) {
|
||||
if (i < num_components-1) {
|
||||
closedir(dir);
|
||||
return NT_STATUS_OBJECT_PATH_NOT_FOUND;
|
||||
}
|
||||
} else {
|
||||
components[i] = talloc_strdup(name, de->d_name);
|
||||
}
|
||||
test_name = talloc_asprintf(name, "%s/%s", partial_name, components[i]);
|
||||
talloc_free(partial_name);
|
||||
partial_name = test_name;
|
||||
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
if (!name->exists) {
|
||||
if (stat(partial_name, &name->st) == 0) {
|
||||
name->exists = True;
|
||||
}
|
||||
}
|
||||
|
||||
talloc_free(name->full_name);
|
||||
name->full_name = partial_name;
|
||||
|
||||
if (name->exists) {
|
||||
return pvfs_fill_dos_info(pvfs, name, -1);
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
parse a alternate data stream name
|
||||
*/
|
||||
static NTSTATUS parse_stream_name(struct pvfs_filename *name, const char *s)
|
||||
{
|
||||
char *p;
|
||||
name->stream_name = talloc_strdup(name, s+1);
|
||||
if (name->stream_name == NULL) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
p = strchr_m(name->stream_name, ':');
|
||||
if (p == NULL) {
|
||||
name->stream_id = pvfs_name_hash(name->stream_name,
|
||||
strlen(name->stream_name));
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
if (strcasecmp_m(p, ":$DATA") != 0) {
|
||||
return NT_STATUS_OBJECT_NAME_INVALID;
|
||||
}
|
||||
*p = 0;
|
||||
if (strcmp(name->stream_name, "") == 0) {
|
||||
name->stream_name = NULL;
|
||||
name->stream_id = 0;
|
||||
} else {
|
||||
name->stream_id = pvfs_name_hash(name->stream_name,
|
||||
strlen(name->stream_name));
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
convert a CIFS pathname to a unix pathname. Note that this does NOT
|
||||
take into account case insensitivity, and in fact does not access
|
||||
the filesystem at all. It is merely a reformatting and charset
|
||||
checking routine.
|
||||
|
||||
errors are returned if the filename is illegal given the flags
|
||||
*/
|
||||
static NTSTATUS pvfs_unix_path(struct pvfs_state *pvfs, const char *cifs_name,
|
||||
uint_t flags, struct pvfs_filename *name)
|
||||
{
|
||||
char *ret, *p, *p_start;
|
||||
NTSTATUS status;
|
||||
|
||||
name->original_name = talloc_strdup(name, cifs_name);
|
||||
name->stream_name = NULL;
|
||||
name->stream_id = 0;
|
||||
name->has_wildcard = False;
|
||||
|
||||
while (*cifs_name == '\\') {
|
||||
cifs_name++;
|
||||
}
|
||||
|
||||
if (*cifs_name == 0) {
|
||||
name->full_name = talloc_asprintf(name, "%s/.", pvfs->base_directory);
|
||||
if (name->full_name == NULL) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
ret = talloc_asprintf(name, "%s/%s", pvfs->base_directory, cifs_name);
|
||||
if (ret == NULL) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
p = ret + strlen(pvfs->base_directory) + 1;
|
||||
|
||||
/* now do an in-place conversion of '\' to '/', checking
|
||||
for legal characters */
|
||||
p_start = p;
|
||||
|
||||
while (*p) {
|
||||
size_t c_size;
|
||||
codepoint_t c = next_codepoint(p, &c_size);
|
||||
switch (c) {
|
||||
case '\\':
|
||||
if (name->has_wildcard) {
|
||||
/* wildcards are only allowed in the last part
|
||||
of a name */
|
||||
return NT_STATUS_ILLEGAL_CHARACTER;
|
||||
}
|
||||
if (p > p_start && p[1] == 0) {
|
||||
*p = 0;
|
||||
} else {
|
||||
*p = '/';
|
||||
}
|
||||
break;
|
||||
case ':':
|
||||
if (!(flags & PVFS_RESOLVE_STREAMS)) {
|
||||
return NT_STATUS_ILLEGAL_CHARACTER;
|
||||
}
|
||||
if (name->has_wildcard) {
|
||||
return NT_STATUS_ILLEGAL_CHARACTER;
|
||||
}
|
||||
status = parse_stream_name(name, p);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
*p-- = 0;
|
||||
break;
|
||||
case '*':
|
||||
case '>':
|
||||
case '<':
|
||||
case '?':
|
||||
case '"':
|
||||
if (!(flags & PVFS_RESOLVE_WILDCARD)) {
|
||||
return NT_STATUS_OBJECT_NAME_INVALID;
|
||||
}
|
||||
name->has_wildcard = True;
|
||||
break;
|
||||
case '/':
|
||||
case '|':
|
||||
return NT_STATUS_ILLEGAL_CHARACTER;
|
||||
case '.':
|
||||
/* see if it is definately a .. or
|
||||
. component. If it is then fail here, and
|
||||
let the next layer up try again after
|
||||
pvfs_reduce_name() if it wants to. This is
|
||||
much more efficient on average than always
|
||||
scanning for these separately */
|
||||
if (p[1] == '.' &&
|
||||
(p[2] == 0 || p[2] == '\\') &&
|
||||
(p == p_start || p[-1] == '/')) {
|
||||
return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
|
||||
}
|
||||
if ((p[1] == 0 || p[1] == '\\') &&
|
||||
(p == p_start || p[-1] == '/')) {
|
||||
return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
p += c_size;
|
||||
}
|
||||
|
||||
name->full_name = ret;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
reduce a name that contains .. components or repeated \ separators
|
||||
return NULL if it can't be reduced
|
||||
*/
|
||||
static NTSTATUS pvfs_reduce_name(TALLOC_CTX *mem_ctx, const char **fname, uint_t flags)
|
||||
{
|
||||
codepoint_t c;
|
||||
size_t c_size, len;
|
||||
int i, num_components, err_count;
|
||||
char **components;
|
||||
char *p, *s, *ret;
|
||||
|
||||
s = talloc_strdup(mem_ctx, *fname);
|
||||
if (s == NULL) return NT_STATUS_NO_MEMORY;
|
||||
|
||||
for (num_components=1, p=s; *p; p += c_size) {
|
||||
c = next_codepoint(p, &c_size);
|
||||
if (c == '\\') num_components++;
|
||||
}
|
||||
|
||||
components = talloc_array(s, char *, num_components+1);
|
||||
if (components == NULL) {
|
||||
talloc_free(s);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
components[0] = s;
|
||||
for (i=0, p=s; *p; p += c_size) {
|
||||
c = next_codepoint(p, &c_size);
|
||||
if (c == '\\') {
|
||||
*p = 0;
|
||||
components[++i] = p+1;
|
||||
}
|
||||
}
|
||||
components[i+1] = NULL;
|
||||
|
||||
/*
|
||||
rather bizarre!
|
||||
|
||||
'.' components are not allowed, but the rules for what error
|
||||
code to give don't seem to make sense. This is a close
|
||||
approximation.
|
||||
*/
|
||||
for (err_count=i=0;components[i];i++) {
|
||||
if (strcmp(components[i], "") == 0) {
|
||||
continue;
|
||||
}
|
||||
if (ISDOT(components[i]) || err_count) {
|
||||
err_count++;
|
||||
}
|
||||
}
|
||||
if (err_count) {
|
||||
if (flags & PVFS_RESOLVE_WILDCARD) err_count--;
|
||||
|
||||
if (err_count==1) {
|
||||
return NT_STATUS_OBJECT_NAME_INVALID;
|
||||
} else {
|
||||
return NT_STATUS_OBJECT_PATH_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
|
||||
/* remove any null components */
|
||||
for (i=0;components[i];i++) {
|
||||
if (strcmp(components[i], "") == 0) {
|
||||
memmove(&components[i], &components[i+1],
|
||||
sizeof(char *)*(num_components-i));
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
if (ISDOTDOT(components[i])) {
|
||||
if (i < 1) return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
|
||||
memmove(&components[i-1], &components[i+1],
|
||||
sizeof(char *)*(num_components-(i+1)));
|
||||
i -= 2;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (components[0] == NULL) {
|
||||
talloc_free(s);
|
||||
*fname = talloc_strdup(mem_ctx, "\\");
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
for (len=i=0;components[i];i++) {
|
||||
len += strlen(components[i]) + 1;
|
||||
}
|
||||
|
||||
/* rebuild the name */
|
||||
ret = talloc_size(mem_ctx, len+1);
|
||||
if (ret == NULL) {
|
||||
talloc_free(s);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
for (len=0,i=0;components[i];i++) {
|
||||
size_t len1 = strlen(components[i]);
|
||||
ret[len] = '\\';
|
||||
memcpy(ret+len+1, components[i], len1);
|
||||
len += len1 + 1;
|
||||
}
|
||||
ret[len] = 0;
|
||||
|
||||
talloc_free(s);
|
||||
|
||||
*fname = ret;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
resolve a name from relative client format to a struct pvfs_filename
|
||||
the memory for the filename is made as a talloc child of 'name'
|
||||
|
||||
flags include:
|
||||
PVFS_RESOLVE_NO_WILDCARD = wildcards are considered illegal characters
|
||||
PVFS_RESOLVE_STREAMS = stream names are allowed
|
||||
|
||||
TODO: ../ collapsing, and outside share checking
|
||||
*/
|
||||
NTSTATUS pvfs_resolve_name(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx,
|
||||
const char *cifs_name,
|
||||
uint_t flags, struct pvfs_filename **name)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
*name = talloc(mem_ctx, struct pvfs_filename);
|
||||
if (*name == NULL) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
(*name)->exists = False;
|
||||
(*name)->stream_exists = False;
|
||||
|
||||
if (!(pvfs->fs_attribs & FS_ATTR_NAMED_STREAMS)) {
|
||||
flags &= ~PVFS_RESOLVE_STREAMS;
|
||||
}
|
||||
|
||||
/* do the basic conversion to a unix formatted path,
|
||||
also checking for allowable characters */
|
||||
status = pvfs_unix_path(pvfs, cifs_name, flags, *name);
|
||||
|
||||
if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_PATH_SYNTAX_BAD)) {
|
||||
/* it might contain .. components which need to be reduced */
|
||||
status = pvfs_reduce_name(*name, &cifs_name, flags);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
status = pvfs_unix_path(pvfs, cifs_name, flags, *name);
|
||||
}
|
||||
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
/* if it has a wildcard then no point doing a stat() */
|
||||
if ((*name)->has_wildcard) {
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/* if we can stat() the full name now then we are done */
|
||||
if (stat((*name)->full_name, &(*name)->st) == 0) {
|
||||
(*name)->exists = True;
|
||||
return pvfs_fill_dos_info(pvfs, *name, -1);
|
||||
}
|
||||
|
||||
/* search for a matching filename */
|
||||
status = pvfs_case_search(pvfs, *name);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
do a partial resolve, returning a pvfs_filename structure given a
|
||||
base path and a relative component. It is an error if the file does
|
||||
not exist. No case-insensitive matching is done.
|
||||
|
||||
this is used in places like directory searching where we need a pvfs_filename
|
||||
to pass to a function, but already know the unix base directory and component
|
||||
*/
|
||||
NTSTATUS pvfs_resolve_partial(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx,
|
||||
const char *unix_dir, const char *fname,
|
||||
struct pvfs_filename **name)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
*name = talloc(mem_ctx, struct pvfs_filename);
|
||||
if (*name == NULL) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
(*name)->full_name = talloc_asprintf(*name, "%s/%s", unix_dir, fname);
|
||||
if ((*name)->full_name == NULL) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
if (stat((*name)->full_name, &(*name)->st) == -1) {
|
||||
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
||||
}
|
||||
|
||||
(*name)->exists = True;
|
||||
(*name)->stream_exists = True;
|
||||
(*name)->has_wildcard = False;
|
||||
(*name)->original_name = talloc_strdup(*name, fname);
|
||||
(*name)->stream_name = NULL;
|
||||
(*name)->stream_id = 0;
|
||||
|
||||
status = pvfs_fill_dos_info(pvfs, *name, -1);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
fill in the pvfs_filename info for an open file, given the current
|
||||
info for a (possibly) non-open file. This is used by places that need
|
||||
to update the pvfs_filename stat information, and by pvfs_open()
|
||||
*/
|
||||
NTSTATUS pvfs_resolve_name_fd(struct pvfs_state *pvfs, int fd,
|
||||
struct pvfs_filename *name)
|
||||
{
|
||||
dev_t device = (dev_t)0;
|
||||
ino_t inode = 0;
|
||||
|
||||
if (name->exists) {
|
||||
device = name->st.st_dev;
|
||||
inode = name->st.st_ino;
|
||||
}
|
||||
|
||||
if (fd == -1) {
|
||||
if (stat(name->full_name, &name->st) == -1) {
|
||||
return NT_STATUS_INVALID_HANDLE;
|
||||
}
|
||||
} else {
|
||||
if (fstat(fd, &name->st) == -1) {
|
||||
return NT_STATUS_INVALID_HANDLE;
|
||||
}
|
||||
}
|
||||
|
||||
if (name->exists &&
|
||||
(device != name->st.st_dev || inode != name->st.st_ino)) {
|
||||
/* the file we are looking at has changed! this could
|
||||
be someone trying to exploit a race
|
||||
condition. Certainly we don't want to continue
|
||||
operating on this file */
|
||||
DEBUG(0,("pvfs: WARNING: file '%s' changed during resolve - failing\n",
|
||||
name->full_name));
|
||||
return NT_STATUS_UNEXPECTED_IO_ERROR;
|
||||
}
|
||||
|
||||
name->exists = True;
|
||||
|
||||
return pvfs_fill_dos_info(pvfs, name, fd);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
resolve the parent of a given name
|
||||
*/
|
||||
NTSTATUS pvfs_resolve_parent(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx,
|
||||
const struct pvfs_filename *child,
|
||||
struct pvfs_filename **name)
|
||||
{
|
||||
NTSTATUS status;
|
||||
char *p;
|
||||
|
||||
*name = talloc(mem_ctx, struct pvfs_filename);
|
||||
if (*name == NULL) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
(*name)->full_name = talloc_strdup(*name, child->full_name);
|
||||
if ((*name)->full_name == NULL) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
p = strrchr_m((*name)->full_name, '/');
|
||||
if (p == NULL) {
|
||||
return NT_STATUS_OBJECT_PATH_SYNTAX_BAD;
|
||||
}
|
||||
|
||||
/* this handles the root directory */
|
||||
if (p == (*name)->full_name) {
|
||||
p[1] = 0;
|
||||
} else {
|
||||
p[0] = 0;
|
||||
}
|
||||
|
||||
if (stat((*name)->full_name, &(*name)->st) == -1) {
|
||||
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
||||
}
|
||||
|
||||
(*name)->exists = True;
|
||||
(*name)->stream_exists = True;
|
||||
(*name)->has_wildcard = False;
|
||||
/* we can't get the correct 'original_name', but for the purposes
|
||||
of this call this is close enough */
|
||||
(*name)->original_name = talloc_reference(*name, child->original_name);
|
||||
(*name)->stream_name = NULL;
|
||||
(*name)->stream_id = 0;
|
||||
|
||||
status = pvfs_fill_dos_info(pvfs, *name, -1);
|
||||
|
||||
return status;
|
||||
}
|
||||
@@ -0,0 +1,843 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
POSIX NTVFS backend - directory search functions
|
||||
|
||||
Copyright (C) Andrew Tridgell 2004
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 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.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "vfs_posix.h"
|
||||
#include "system/time.h"
|
||||
#include "librpc/gen_ndr/security.h"
|
||||
#include "smbd/service_stream.h"
|
||||
#include "lib/events/events.h"
|
||||
#include "lib/util/dlinklist.h"
|
||||
|
||||
/* place a reasonable limit on old-style searches as clients tend to
|
||||
not send search close requests */
|
||||
#define MAX_OLD_SEARCHES 2000
|
||||
#define MAX_SEARCH_HANDLES (UINT16_MAX - 1)
|
||||
#define INVALID_SEARCH_HANDLE UINT16_MAX
|
||||
|
||||
/*
|
||||
destroy an open search
|
||||
*/
|
||||
static int pvfs_search_destructor(struct pvfs_search_state *search)
|
||||
{
|
||||
DLIST_REMOVE(search->pvfs->search.list, search);
|
||||
idr_remove(search->pvfs->search.idtree, search->handle);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
called when a search timer goes off
|
||||
*/
|
||||
static void pvfs_search_timer(struct event_context *ev, struct timed_event *te,
|
||||
struct timeval t, void *ptr)
|
||||
{
|
||||
struct pvfs_search_state *search = talloc_get_type(ptr, struct pvfs_search_state);
|
||||
talloc_free(search);
|
||||
}
|
||||
|
||||
/*
|
||||
setup a timer to destroy a open search after a inactivity period
|
||||
*/
|
||||
static void pvfs_search_setup_timer(struct pvfs_search_state *search)
|
||||
{
|
||||
struct event_context *ev = search->pvfs->ntvfs->ctx->event_ctx;
|
||||
if (search->handle == INVALID_SEARCH_HANDLE) return;
|
||||
talloc_free(search->te);
|
||||
search->te = event_add_timed(ev, search,
|
||||
timeval_current_ofs(search->pvfs->search.inactivity_time, 0),
|
||||
pvfs_search_timer, search);
|
||||
}
|
||||
|
||||
/*
|
||||
fill in a single search result for a given info level
|
||||
*/
|
||||
static NTSTATUS fill_search_info(struct pvfs_state *pvfs,
|
||||
enum smb_search_data_level level,
|
||||
const char *unix_path,
|
||||
const char *fname,
|
||||
struct pvfs_search_state *search,
|
||||
off_t dir_offset,
|
||||
union smb_search_data *file)
|
||||
{
|
||||
struct pvfs_filename *name;
|
||||
NTSTATUS status;
|
||||
const char *shortname;
|
||||
uint32_t dir_index = (uint32_t)dir_offset; /* truncated - see the code
|
||||
in pvfs_list_seek_ofs() for
|
||||
how we cope with this */
|
||||
|
||||
status = pvfs_resolve_partial(pvfs, file, unix_path, fname, &name);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
status = pvfs_match_attrib(pvfs, name, search->search_attrib, search->must_attrib);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
switch (level) {
|
||||
case RAW_SEARCH_DATA_SEARCH:
|
||||
shortname = pvfs_short_name(pvfs, name, name);
|
||||
file->search.attrib = name->dos.attrib;
|
||||
file->search.write_time = nt_time_to_unix(name->dos.write_time);
|
||||
file->search.size = name->st.st_size;
|
||||
file->search.name = shortname;
|
||||
file->search.id.reserved = search->handle >> 8;
|
||||
memset(file->search.id.name, ' ', sizeof(file->search.id.name));
|
||||
memcpy(file->search.id.name, shortname,
|
||||
MIN(strlen(shortname)+1, sizeof(file->search.id.name)));
|
||||
file->search.id.handle = search->handle & 0xFF;
|
||||
file->search.id.server_cookie = dir_index;
|
||||
file->search.id.client_cookie = 0;
|
||||
return NT_STATUS_OK;
|
||||
|
||||
case RAW_SEARCH_DATA_STANDARD:
|
||||
file->standard.resume_key = dir_index;
|
||||
file->standard.create_time = nt_time_to_unix(name->dos.create_time);
|
||||
file->standard.access_time = nt_time_to_unix(name->dos.access_time);
|
||||
file->standard.write_time = nt_time_to_unix(name->dos.write_time);
|
||||
file->standard.size = name->st.st_size;
|
||||
file->standard.alloc_size = name->dos.alloc_size;
|
||||
file->standard.attrib = name->dos.attrib;
|
||||
file->standard.name.s = fname;
|
||||
return NT_STATUS_OK;
|
||||
|
||||
case RAW_SEARCH_DATA_EA_SIZE:
|
||||
file->ea_size.resume_key = dir_index;
|
||||
file->ea_size.create_time = nt_time_to_unix(name->dos.create_time);
|
||||
file->ea_size.access_time = nt_time_to_unix(name->dos.access_time);
|
||||
file->ea_size.write_time = nt_time_to_unix(name->dos.write_time);
|
||||
file->ea_size.size = name->st.st_size;
|
||||
file->ea_size.alloc_size = name->dos.alloc_size;
|
||||
file->ea_size.attrib = name->dos.attrib;
|
||||
file->ea_size.ea_size = name->dos.ea_size;
|
||||
file->ea_size.name.s = fname;
|
||||
return NT_STATUS_OK;
|
||||
|
||||
case RAW_SEARCH_DATA_EA_LIST:
|
||||
file->ea_list.resume_key = dir_index;
|
||||
file->ea_list.create_time = nt_time_to_unix(name->dos.create_time);
|
||||
file->ea_list.access_time = nt_time_to_unix(name->dos.access_time);
|
||||
file->ea_list.write_time = nt_time_to_unix(name->dos.write_time);
|
||||
file->ea_list.size = name->st.st_size;
|
||||
file->ea_list.alloc_size = name->dos.alloc_size;
|
||||
file->ea_list.attrib = name->dos.attrib;
|
||||
file->ea_list.name.s = fname;
|
||||
return pvfs_query_ea_list(pvfs, file, name, -1,
|
||||
search->num_ea_names,
|
||||
search->ea_names,
|
||||
&file->ea_list.eas);
|
||||
|
||||
case RAW_SEARCH_DATA_DIRECTORY_INFO:
|
||||
file->directory_info.file_index = dir_index;
|
||||
file->directory_info.create_time = name->dos.create_time;
|
||||
file->directory_info.access_time = name->dos.access_time;
|
||||
file->directory_info.write_time = name->dos.write_time;
|
||||
file->directory_info.change_time = name->dos.change_time;
|
||||
file->directory_info.size = name->st.st_size;
|
||||
file->directory_info.alloc_size = name->dos.alloc_size;
|
||||
file->directory_info.attrib = name->dos.attrib;
|
||||
file->directory_info.name.s = fname;
|
||||
return NT_STATUS_OK;
|
||||
|
||||
case RAW_SEARCH_DATA_FULL_DIRECTORY_INFO:
|
||||
file->full_directory_info.file_index = dir_index;
|
||||
file->full_directory_info.create_time = name->dos.create_time;
|
||||
file->full_directory_info.access_time = name->dos.access_time;
|
||||
file->full_directory_info.write_time = name->dos.write_time;
|
||||
file->full_directory_info.change_time = name->dos.change_time;
|
||||
file->full_directory_info.size = name->st.st_size;
|
||||
file->full_directory_info.alloc_size = name->dos.alloc_size;
|
||||
file->full_directory_info.attrib = name->dos.attrib;
|
||||
file->full_directory_info.ea_size = name->dos.ea_size;
|
||||
file->full_directory_info.name.s = fname;
|
||||
return NT_STATUS_OK;
|
||||
|
||||
case RAW_SEARCH_DATA_NAME_INFO:
|
||||
file->name_info.file_index = dir_index;
|
||||
file->name_info.name.s = fname;
|
||||
return NT_STATUS_OK;
|
||||
|
||||
case RAW_SEARCH_DATA_BOTH_DIRECTORY_INFO:
|
||||
file->both_directory_info.file_index = dir_index;
|
||||
file->both_directory_info.create_time = name->dos.create_time;
|
||||
file->both_directory_info.access_time = name->dos.access_time;
|
||||
file->both_directory_info.write_time = name->dos.write_time;
|
||||
file->both_directory_info.change_time = name->dos.change_time;
|
||||
file->both_directory_info.size = name->st.st_size;
|
||||
file->both_directory_info.alloc_size = name->dos.alloc_size;
|
||||
file->both_directory_info.attrib = name->dos.attrib;
|
||||
file->both_directory_info.ea_size = name->dos.ea_size;
|
||||
file->both_directory_info.short_name.s = pvfs_short_name(pvfs, file, name);
|
||||
file->both_directory_info.name.s = fname;
|
||||
return NT_STATUS_OK;
|
||||
|
||||
case RAW_SEARCH_DATA_ID_FULL_DIRECTORY_INFO:
|
||||
file->id_full_directory_info.file_index = dir_index;
|
||||
file->id_full_directory_info.create_time = name->dos.create_time;
|
||||
file->id_full_directory_info.access_time = name->dos.access_time;
|
||||
file->id_full_directory_info.write_time = name->dos.write_time;
|
||||
file->id_full_directory_info.change_time = name->dos.change_time;
|
||||
file->id_full_directory_info.size = name->st.st_size;
|
||||
file->id_full_directory_info.alloc_size = name->dos.alloc_size;
|
||||
file->id_full_directory_info.attrib = name->dos.attrib;
|
||||
file->id_full_directory_info.ea_size = name->dos.ea_size;
|
||||
file->id_full_directory_info.file_id = name->dos.file_id;
|
||||
file->id_full_directory_info.name.s = fname;
|
||||
return NT_STATUS_OK;
|
||||
|
||||
case RAW_SEARCH_DATA_ID_BOTH_DIRECTORY_INFO:
|
||||
file->id_both_directory_info.file_index = dir_index;
|
||||
file->id_both_directory_info.create_time = name->dos.create_time;
|
||||
file->id_both_directory_info.access_time = name->dos.access_time;
|
||||
file->id_both_directory_info.write_time = name->dos.write_time;
|
||||
file->id_both_directory_info.change_time = name->dos.change_time;
|
||||
file->id_both_directory_info.size = name->st.st_size;
|
||||
file->id_both_directory_info.alloc_size = name->dos.alloc_size;
|
||||
file->id_both_directory_info.attrib = name->dos.attrib;
|
||||
file->id_both_directory_info.ea_size = name->dos.ea_size;
|
||||
file->id_both_directory_info.file_id = name->dos.file_id;
|
||||
file->id_both_directory_info.short_name.s = pvfs_short_name(pvfs, file, name);
|
||||
file->id_both_directory_info.name.s = fname;
|
||||
return NT_STATUS_OK;
|
||||
|
||||
case RAW_SEARCH_DATA_GENERIC:
|
||||
break;
|
||||
}
|
||||
|
||||
return NT_STATUS_INVALID_LEVEL;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
the search fill loop
|
||||
*/
|
||||
static NTSTATUS pvfs_search_fill(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx,
|
||||
uint_t max_count,
|
||||
struct pvfs_search_state *search,
|
||||
enum smb_search_data_level level,
|
||||
uint_t *reply_count,
|
||||
void *search_private,
|
||||
BOOL (*callback)(void *, union smb_search_data *))
|
||||
{
|
||||
struct pvfs_dir *dir = search->dir;
|
||||
NTSTATUS status;
|
||||
|
||||
*reply_count = 0;
|
||||
|
||||
if (max_count == 0) {
|
||||
max_count = 1;
|
||||
}
|
||||
|
||||
while ((*reply_count) < max_count) {
|
||||
union smb_search_data *file;
|
||||
const char *name;
|
||||
off_t ofs = search->current_index;
|
||||
|
||||
name = pvfs_list_next(dir, &search->current_index);
|
||||
if (name == NULL) break;
|
||||
|
||||
file = talloc(mem_ctx, union smb_search_data);
|
||||
if (!file) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
status = fill_search_info(pvfs, level,
|
||||
pvfs_list_unix_path(dir), name,
|
||||
search, search->current_index, file);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
talloc_free(file);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!callback(search_private, file)) {
|
||||
talloc_free(file);
|
||||
search->current_index = ofs;
|
||||
break;
|
||||
}
|
||||
|
||||
(*reply_count)++;
|
||||
talloc_free(file);
|
||||
}
|
||||
|
||||
pvfs_search_setup_timer(search);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
we've run out of search handles - cleanup those that the client forgot
|
||||
to close
|
||||
*/
|
||||
static void pvfs_search_cleanup(struct pvfs_state *pvfs)
|
||||
{
|
||||
int i;
|
||||
time_t t = time(NULL);
|
||||
|
||||
for (i=0;i<MAX_OLD_SEARCHES;i++) {
|
||||
struct pvfs_search_state *search = idr_find(pvfs->search.idtree, i);
|
||||
if (search == NULL) return;
|
||||
if (pvfs_list_eos(search->dir, search->current_index) &&
|
||||
search->last_used != 0 &&
|
||||
t > search->last_used + 30) {
|
||||
/* its almost certainly been forgotten
|
||||
about */
|
||||
talloc_free(search);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
list files in a directory matching a wildcard pattern - old SMBsearch interface
|
||||
*/
|
||||
static NTSTATUS pvfs_search_first_old(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_search_first *io,
|
||||
void *search_private,
|
||||
BOOL (*callback)(void *, union smb_search_data *))
|
||||
{
|
||||
struct pvfs_dir *dir;
|
||||
struct pvfs_state *pvfs = ntvfs->private_data;
|
||||
struct pvfs_search_state *search;
|
||||
uint_t reply_count;
|
||||
uint16_t search_attrib;
|
||||
const char *pattern;
|
||||
NTSTATUS status;
|
||||
struct pvfs_filename *name;
|
||||
int id;
|
||||
|
||||
search_attrib = io->search_first.in.search_attrib;
|
||||
pattern = io->search_first.in.pattern;
|
||||
|
||||
/* resolve the cifs name to a posix name */
|
||||
status = pvfs_resolve_name(pvfs, req, pattern, PVFS_RESOLVE_WILDCARD, &name);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
if (!name->has_wildcard && !name->exists) {
|
||||
return STATUS_NO_MORE_FILES;
|
||||
}
|
||||
|
||||
status = pvfs_access_check_parent(pvfs, req, name, SEC_DIR_TRAVERSE | SEC_DIR_LIST);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
/* we initially make search a child of the request, then if we
|
||||
need to keep it long term we steal it for the private
|
||||
structure */
|
||||
search = talloc(req, struct pvfs_search_state);
|
||||
if (!search) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
/* do the actual directory listing */
|
||||
status = pvfs_list_start(pvfs, name, search, &dir);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
/* we need to give a handle back to the client so it
|
||||
can continue a search */
|
||||
id = idr_get_new(pvfs->search.idtree, search, MAX_OLD_SEARCHES);
|
||||
if (id == -1) {
|
||||
pvfs_search_cleanup(pvfs);
|
||||
id = idr_get_new(pvfs->search.idtree, search, MAX_OLD_SEARCHES);
|
||||
}
|
||||
if (id == -1) {
|
||||
return NT_STATUS_INSUFFICIENT_RESOURCES;
|
||||
}
|
||||
|
||||
search->pvfs = pvfs;
|
||||
search->handle = id;
|
||||
search->dir = dir;
|
||||
search->current_index = 0;
|
||||
search->search_attrib = search_attrib & 0xFF;
|
||||
search->must_attrib = (search_attrib>>8) & 0xFF;
|
||||
search->last_used = time(NULL);
|
||||
search->te = NULL;
|
||||
|
||||
DLIST_ADD(pvfs->search.list, search);
|
||||
|
||||
talloc_set_destructor(search, pvfs_search_destructor);
|
||||
|
||||
status = pvfs_search_fill(pvfs, req, io->search_first.in.max_count, search, io->generic.data_level,
|
||||
&reply_count, search_private, callback);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
io->search_first.out.count = reply_count;
|
||||
|
||||
/* not matching any entries is an error */
|
||||
if (reply_count == 0) {
|
||||
return STATUS_NO_MORE_FILES;
|
||||
}
|
||||
|
||||
talloc_steal(pvfs, search);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/* continue a old style search */
|
||||
static NTSTATUS pvfs_search_next_old(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_search_next *io,
|
||||
void *search_private,
|
||||
BOOL (*callback)(void *, union smb_search_data *))
|
||||
{
|
||||
struct pvfs_state *pvfs = ntvfs->private_data;
|
||||
struct pvfs_search_state *search;
|
||||
struct pvfs_dir *dir;
|
||||
uint_t reply_count, max_count;
|
||||
uint16_t handle;
|
||||
NTSTATUS status;
|
||||
|
||||
handle = io->search_next.in.id.handle | (io->search_next.in.id.reserved<<8);
|
||||
max_count = io->search_next.in.max_count;
|
||||
|
||||
search = idr_find(pvfs->search.idtree, handle);
|
||||
if (search == NULL) {
|
||||
/* we didn't find the search handle */
|
||||
return NT_STATUS_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
dir = search->dir;
|
||||
|
||||
status = pvfs_list_seek_ofs(dir, io->search_next.in.id.server_cookie,
|
||||
&search->current_index);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
search->last_used = time(NULL);
|
||||
|
||||
status = pvfs_search_fill(pvfs, req, max_count, search, io->generic.data_level,
|
||||
&reply_count, search_private, callback);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
io->search_next.out.count = reply_count;
|
||||
|
||||
/* not matching any entries means end of search */
|
||||
if (reply_count == 0) {
|
||||
talloc_free(search);
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
list files in a directory matching a wildcard pattern
|
||||
*/
|
||||
static NTSTATUS pvfs_search_first_trans2(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_search_first *io,
|
||||
void *search_private,
|
||||
BOOL (*callback)(void *, union smb_search_data *))
|
||||
{
|
||||
struct pvfs_dir *dir;
|
||||
struct pvfs_state *pvfs = ntvfs->private_data;
|
||||
struct pvfs_search_state *search;
|
||||
uint_t reply_count;
|
||||
uint16_t search_attrib, max_count;
|
||||
const char *pattern;
|
||||
NTSTATUS status;
|
||||
struct pvfs_filename *name;
|
||||
int id;
|
||||
|
||||
search_attrib = io->t2ffirst.in.search_attrib;
|
||||
pattern = io->t2ffirst.in.pattern;
|
||||
max_count = io->t2ffirst.in.max_count;
|
||||
|
||||
/* resolve the cifs name to a posix name */
|
||||
status = pvfs_resolve_name(pvfs, req, pattern, PVFS_RESOLVE_WILDCARD, &name);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
if (!name->has_wildcard && !name->exists) {
|
||||
return NT_STATUS_NO_SUCH_FILE;
|
||||
}
|
||||
|
||||
status = pvfs_access_check_parent(pvfs, req, name, SEC_DIR_TRAVERSE | SEC_DIR_LIST);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
/* we initially make search a child of the request, then if we
|
||||
need to keep it long term we steal it for the private
|
||||
structure */
|
||||
search = talloc(req, struct pvfs_search_state);
|
||||
if (!search) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
/* do the actual directory listing */
|
||||
status = pvfs_list_start(pvfs, name, search, &dir);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
id = idr_get_new(pvfs->search.idtree, search, MAX_SEARCH_HANDLES);
|
||||
if (id == -1) {
|
||||
return NT_STATUS_INSUFFICIENT_RESOURCES;
|
||||
}
|
||||
|
||||
search->pvfs = pvfs;
|
||||
search->handle = id;
|
||||
search->dir = dir;
|
||||
search->current_index = 0;
|
||||
search->search_attrib = search_attrib;
|
||||
search->must_attrib = 0;
|
||||
search->last_used = 0;
|
||||
search->num_ea_names = io->t2ffirst.in.num_names;
|
||||
search->ea_names = io->t2ffirst.in.ea_names;
|
||||
search->te = NULL;
|
||||
|
||||
DLIST_ADD(pvfs->search.list, search);
|
||||
talloc_set_destructor(search, pvfs_search_destructor);
|
||||
|
||||
status = pvfs_search_fill(pvfs, req, max_count, search, io->generic.data_level,
|
||||
&reply_count, search_private, callback);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
/* not matching any entries is an error */
|
||||
if (reply_count == 0) {
|
||||
return NT_STATUS_NO_SUCH_FILE;
|
||||
}
|
||||
|
||||
io->t2ffirst.out.count = reply_count;
|
||||
io->t2ffirst.out.handle = search->handle;
|
||||
io->t2ffirst.out.end_of_search = pvfs_list_eos(dir, search->current_index) ? 1 : 0;
|
||||
|
||||
/* work out if we are going to keep the search state
|
||||
and allow for a search continue */
|
||||
if ((io->t2ffirst.in.flags & FLAG_TRANS2_FIND_CLOSE) ||
|
||||
((io->t2ffirst.in.flags & FLAG_TRANS2_FIND_CLOSE_IF_END) &&
|
||||
io->t2ffirst.out.end_of_search)) {
|
||||
talloc_free(search);
|
||||
} else {
|
||||
talloc_steal(pvfs, search);
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/* continue a search */
|
||||
static NTSTATUS pvfs_search_next_trans2(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_search_next *io,
|
||||
void *search_private,
|
||||
BOOL (*callback)(void *, union smb_search_data *))
|
||||
{
|
||||
struct pvfs_state *pvfs = ntvfs->private_data;
|
||||
struct pvfs_search_state *search;
|
||||
struct pvfs_dir *dir;
|
||||
uint_t reply_count;
|
||||
uint16_t handle;
|
||||
NTSTATUS status;
|
||||
|
||||
handle = io->t2fnext.in.handle;
|
||||
|
||||
search = idr_find(pvfs->search.idtree, handle);
|
||||
if (search == NULL) {
|
||||
/* we didn't find the search handle */
|
||||
return NT_STATUS_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
dir = search->dir;
|
||||
|
||||
status = NT_STATUS_OK;
|
||||
|
||||
/* work out what type of continuation is being used */
|
||||
if (io->t2fnext.in.last_name && *io->t2fnext.in.last_name) {
|
||||
status = pvfs_list_seek(dir, io->t2fnext.in.last_name, &search->current_index);
|
||||
if (!NT_STATUS_IS_OK(status) && io->t2fnext.in.resume_key) {
|
||||
status = pvfs_list_seek_ofs(dir, io->t2fnext.in.resume_key,
|
||||
&search->current_index);
|
||||
}
|
||||
} else if (!(io->t2fnext.in.flags & FLAG_TRANS2_FIND_CONTINUE)) {
|
||||
status = pvfs_list_seek_ofs(dir, io->t2fnext.in.resume_key,
|
||||
&search->current_index);
|
||||
}
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
search->num_ea_names = io->t2fnext.in.num_names;
|
||||
search->ea_names = io->t2fnext.in.ea_names;
|
||||
|
||||
status = pvfs_search_fill(pvfs, req, io->t2fnext.in.max_count, search, io->generic.data_level,
|
||||
&reply_count, search_private, callback);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
io->t2fnext.out.count = reply_count;
|
||||
io->t2fnext.out.end_of_search = pvfs_list_eos(dir, search->current_index) ? 1 : 0;
|
||||
|
||||
/* work out if we are going to keep the search state */
|
||||
if ((io->t2fnext.in.flags & FLAG_TRANS2_FIND_CLOSE) ||
|
||||
((io->t2fnext.in.flags & FLAG_TRANS2_FIND_CLOSE_IF_END) &&
|
||||
io->t2fnext.out.end_of_search)) {
|
||||
talloc_free(search);
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static NTSTATUS pvfs_search_first_smb2(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, const struct smb2_find *io,
|
||||
void *search_private,
|
||||
BOOL (*callback)(void *, union smb_search_data *))
|
||||
{
|
||||
struct pvfs_dir *dir;
|
||||
struct pvfs_state *pvfs = ntvfs->private_data;
|
||||
struct pvfs_search_state *search;
|
||||
uint_t reply_count;
|
||||
uint16_t max_count;
|
||||
const char *pattern;
|
||||
NTSTATUS status;
|
||||
struct pvfs_filename *name;
|
||||
struct pvfs_file *f;
|
||||
|
||||
f = pvfs_find_fd(pvfs, req, io->in.file.ntvfs);
|
||||
if (!f) {
|
||||
return NT_STATUS_FILE_CLOSED;
|
||||
}
|
||||
|
||||
/* its only valid for directories */
|
||||
if (f->handle->fd != -1) {
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
if (!(f->access_mask & SEC_DIR_LIST)) {
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
if (f->search) {
|
||||
talloc_free(f->search);
|
||||
f->search = NULL;
|
||||
}
|
||||
|
||||
if (strequal(io->in.pattern, "")) {
|
||||
return NT_STATUS_OBJECT_NAME_INVALID;
|
||||
}
|
||||
if (strchr_m(io->in.pattern, '\\')) {
|
||||
return NT_STATUS_OBJECT_NAME_INVALID;
|
||||
}
|
||||
if (strchr_m(io->in.pattern, '/')) {
|
||||
return NT_STATUS_OBJECT_NAME_INVALID;
|
||||
}
|
||||
|
||||
if (strequal("", f->handle->name->original_name)) {
|
||||
pattern = talloc_asprintf(req, "\\%s", io->in.pattern);
|
||||
NT_STATUS_HAVE_NO_MEMORY(pattern);
|
||||
} else {
|
||||
pattern = talloc_asprintf(req, "\\%s\\%s",
|
||||
f->handle->name->original_name,
|
||||
io->in.pattern);
|
||||
NT_STATUS_HAVE_NO_MEMORY(pattern);
|
||||
}
|
||||
|
||||
/* resolve the cifs name to a posix name */
|
||||
status = pvfs_resolve_name(pvfs, req, pattern, PVFS_RESOLVE_WILDCARD, &name);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
if (!name->has_wildcard && !name->exists) {
|
||||
return NT_STATUS_NO_SUCH_FILE;
|
||||
}
|
||||
|
||||
/* we initially make search a child of the request, then if we
|
||||
need to keep it long term we steal it for the private
|
||||
structure */
|
||||
search = talloc(req, struct pvfs_search_state);
|
||||
NT_STATUS_HAVE_NO_MEMORY(search);
|
||||
|
||||
/* do the actual directory listing */
|
||||
status = pvfs_list_start(pvfs, name, search, &dir);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
search->pvfs = pvfs;
|
||||
search->handle = INVALID_SEARCH_HANDLE;
|
||||
search->dir = dir;
|
||||
search->current_index = 0;
|
||||
search->search_attrib = 0;
|
||||
search->must_attrib = 0;
|
||||
search->last_used = 0;
|
||||
search->num_ea_names = 0;
|
||||
search->ea_names = NULL;
|
||||
search->te = NULL;
|
||||
|
||||
if (io->in.continue_flags & SMB2_CONTINUE_FLAG_SINGLE) {
|
||||
max_count = 1;
|
||||
} else {
|
||||
max_count = UINT16_MAX;
|
||||
}
|
||||
|
||||
status = pvfs_search_fill(pvfs, req, max_count, search, io->data_level,
|
||||
&reply_count, search_private, callback);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
/* not matching any entries is an error */
|
||||
if (reply_count == 0) {
|
||||
return NT_STATUS_NO_SUCH_FILE;
|
||||
}
|
||||
|
||||
f->search = talloc_steal(f, search);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
static NTSTATUS pvfs_search_next_smb2(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, const struct smb2_find *io,
|
||||
void *search_private,
|
||||
BOOL (*callback)(void *, union smb_search_data *))
|
||||
{
|
||||
struct pvfs_state *pvfs = ntvfs->private_data;
|
||||
struct pvfs_search_state *search;
|
||||
uint_t reply_count;
|
||||
uint16_t max_count;
|
||||
NTSTATUS status;
|
||||
struct pvfs_file *f;
|
||||
|
||||
f = pvfs_find_fd(pvfs, req, io->in.file.ntvfs);
|
||||
if (!f) {
|
||||
return NT_STATUS_FILE_CLOSED;
|
||||
}
|
||||
|
||||
/* its only valid for directories */
|
||||
if (f->handle->fd != -1) {
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
/* if there's no search started on the dir handle, it's like a search_first */
|
||||
search = f->search;
|
||||
if (!search) {
|
||||
return pvfs_search_first_smb2(ntvfs, req, io, search_private, callback);
|
||||
}
|
||||
|
||||
if (io->in.continue_flags & SMB2_CONTINUE_FLAG_RESTART) {
|
||||
search->current_index = 0;
|
||||
}
|
||||
|
||||
if (io->in.continue_flags & SMB2_CONTINUE_FLAG_SINGLE) {
|
||||
max_count = 1;
|
||||
} else {
|
||||
max_count = UINT16_MAX;
|
||||
}
|
||||
|
||||
status = pvfs_search_fill(pvfs, req, max_count, search, io->data_level,
|
||||
&reply_count, search_private, callback);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
/* not matching any entries is an error */
|
||||
if (reply_count == 0) {
|
||||
return STATUS_NO_MORE_FILES;
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
list files in a directory matching a wildcard pattern
|
||||
*/
|
||||
NTSTATUS pvfs_search_first(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_search_first *io,
|
||||
void *search_private,
|
||||
BOOL (*callback)(void *, union smb_search_data *))
|
||||
{
|
||||
switch (io->generic.level) {
|
||||
case RAW_SEARCH_SEARCH:
|
||||
case RAW_SEARCH_FFIRST:
|
||||
case RAW_SEARCH_FUNIQUE:
|
||||
return pvfs_search_first_old(ntvfs, req, io, search_private, callback);
|
||||
|
||||
case RAW_SEARCH_TRANS2:
|
||||
return pvfs_search_first_trans2(ntvfs, req, io, search_private, callback);
|
||||
|
||||
case RAW_SEARCH_SMB2:
|
||||
return pvfs_search_first_smb2(ntvfs, req, &io->smb2, search_private, callback);
|
||||
}
|
||||
|
||||
return NT_STATUS_INVALID_LEVEL;
|
||||
}
|
||||
|
||||
/* continue a search */
|
||||
NTSTATUS pvfs_search_next(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_search_next *io,
|
||||
void *search_private,
|
||||
BOOL (*callback)(void *, union smb_search_data *))
|
||||
{
|
||||
switch (io->generic.level) {
|
||||
case RAW_SEARCH_SEARCH:
|
||||
case RAW_SEARCH_FFIRST:
|
||||
return pvfs_search_next_old(ntvfs, req, io, search_private, callback);
|
||||
|
||||
case RAW_SEARCH_FUNIQUE:
|
||||
return NT_STATUS_INVALID_LEVEL;
|
||||
|
||||
case RAW_SEARCH_TRANS2:
|
||||
return pvfs_search_next_trans2(ntvfs, req, io, search_private, callback);
|
||||
|
||||
case RAW_SEARCH_SMB2:
|
||||
return pvfs_search_next_smb2(ntvfs, req, &io->smb2, search_private, callback);
|
||||
}
|
||||
|
||||
return NT_STATUS_INVALID_LEVEL;
|
||||
}
|
||||
|
||||
|
||||
/* close a search */
|
||||
NTSTATUS pvfs_search_close(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_search_close *io)
|
||||
{
|
||||
struct pvfs_state *pvfs = ntvfs->private_data;
|
||||
struct pvfs_search_state *search;
|
||||
uint16_t handle = INVALID_SEARCH_HANDLE;
|
||||
|
||||
switch (io->generic.level) {
|
||||
case RAW_FINDCLOSE_GENERIC:
|
||||
return NT_STATUS_INVALID_LEVEL;
|
||||
|
||||
case RAW_FINDCLOSE_FCLOSE:
|
||||
handle = io->fclose.in.id.handle;
|
||||
break;
|
||||
|
||||
case RAW_FINDCLOSE_FINDCLOSE:
|
||||
handle = io->findclose.in.handle;
|
||||
break;
|
||||
}
|
||||
|
||||
search = idr_find(pvfs->search.idtree, handle);
|
||||
if (search == NULL) {
|
||||
/* we didn't find the search handle */
|
||||
return NT_STATUS_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
talloc_free(search);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
POSIX NTVFS backend - seek
|
||||
|
||||
Copyright (C) Andrew Tridgell 2004
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 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.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "vfs_posix.h"
|
||||
|
||||
/*
|
||||
seek in a file
|
||||
*/
|
||||
NTSTATUS pvfs_seek(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
union smb_seek *io)
|
||||
{
|
||||
struct pvfs_state *pvfs = ntvfs->private_data;
|
||||
struct pvfs_file *f;
|
||||
struct pvfs_file_handle *h;
|
||||
NTSTATUS status;
|
||||
|
||||
f = pvfs_find_fd(pvfs, req, io->lseek.in.file.ntvfs);
|
||||
if (!f) {
|
||||
return NT_STATUS_INVALID_HANDLE;
|
||||
}
|
||||
h = f->handle;
|
||||
|
||||
status = NT_STATUS_OK;
|
||||
|
||||
switch (io->lseek.in.mode) {
|
||||
case SEEK_MODE_START:
|
||||
h->seek_offset = io->lseek.in.offset;
|
||||
break;
|
||||
|
||||
case SEEK_MODE_CURRENT:
|
||||
h->seek_offset += io->lseek.in.offset;
|
||||
break;
|
||||
|
||||
case SEEK_MODE_END:
|
||||
status = pvfs_resolve_name_fd(pvfs, h->fd, h->name);
|
||||
h->seek_offset = h->name->st.st_size + io->lseek.in.offset;
|
||||
break;
|
||||
}
|
||||
|
||||
io->lseek.out.offset = h->seek_offset;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,635 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
POSIX NTVFS backend - setfileinfo
|
||||
|
||||
Copyright (C) Andrew Tridgell 2004
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 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.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "vfs_posix.h"
|
||||
#include "system/time.h"
|
||||
#include "librpc/gen_ndr/xattr.h"
|
||||
|
||||
|
||||
/*
|
||||
determine what access bits are needed for a call
|
||||
*/
|
||||
static uint32_t pvfs_setfileinfo_access(union smb_setfileinfo *info)
|
||||
{
|
||||
uint32_t needed;
|
||||
|
||||
switch (info->generic.level) {
|
||||
case RAW_SFILEINFO_EA_SET:
|
||||
needed = SEC_FILE_WRITE_EA;
|
||||
break;
|
||||
|
||||
case RAW_SFILEINFO_DISPOSITION_INFO:
|
||||
case RAW_SFILEINFO_DISPOSITION_INFORMATION:
|
||||
needed = SEC_STD_DELETE;
|
||||
break;
|
||||
|
||||
case RAW_SFILEINFO_END_OF_FILE_INFO:
|
||||
needed = SEC_FILE_WRITE_DATA;
|
||||
break;
|
||||
|
||||
case RAW_SFILEINFO_POSITION_INFORMATION:
|
||||
needed = 0;
|
||||
break;
|
||||
|
||||
case RAW_SFILEINFO_SEC_DESC:
|
||||
needed = 0;
|
||||
if (info->set_secdesc.in.secinfo_flags & (SECINFO_OWNER|SECINFO_GROUP)) {
|
||||
needed |= SEC_STD_WRITE_OWNER;
|
||||
}
|
||||
if (info->set_secdesc.in.secinfo_flags & SECINFO_DACL) {
|
||||
needed |= SEC_STD_WRITE_DAC;
|
||||
}
|
||||
if (info->set_secdesc.in.secinfo_flags & SECINFO_SACL) {
|
||||
needed |= SEC_FLAG_SYSTEM_SECURITY;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
needed = SEC_FILE_WRITE_ATTRIBUTE;
|
||||
break;
|
||||
}
|
||||
|
||||
return needed;
|
||||
}
|
||||
|
||||
/*
|
||||
rename_information level
|
||||
*/
|
||||
static NTSTATUS pvfs_setfileinfo_rename(struct pvfs_state *pvfs,
|
||||
struct ntvfs_request *req,
|
||||
struct pvfs_filename *name,
|
||||
union smb_setfileinfo *info)
|
||||
{
|
||||
NTSTATUS status;
|
||||
struct pvfs_filename *name2;
|
||||
char *new_name, *p;
|
||||
|
||||
/* renames are only allowed within a directory */
|
||||
if (strchr_m(info->rename_information.in.new_name, '\\')) {
|
||||
return NT_STATUS_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
if (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) {
|
||||
/* don't allow this for now */
|
||||
return NT_STATUS_FILE_IS_A_DIRECTORY;
|
||||
}
|
||||
|
||||
/* don't allow stream renames for now */
|
||||
if (name->stream_name) {
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
/* w2k3 does not appear to allow relative rename */
|
||||
if (info->rename_information.in.root_fid != 0) {
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
/* construct the fully qualified windows name for the new file name */
|
||||
new_name = talloc_strdup(req, name->original_name);
|
||||
if (new_name == NULL) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
p = strrchr_m(new_name, '\\');
|
||||
if (p == NULL) {
|
||||
return NT_STATUS_OBJECT_NAME_INVALID;
|
||||
}
|
||||
*p = 0;
|
||||
|
||||
new_name = talloc_asprintf(req, "%s\\%s", new_name,
|
||||
info->rename_information.in.new_name);
|
||||
if (new_name == NULL) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
/* resolve the new name */
|
||||
status = pvfs_resolve_name(pvfs, name, new_name, 0, &name2);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
/* if the destination exists, then check the rename is allowed */
|
||||
if (name2->exists) {
|
||||
struct odb_lock *lck;
|
||||
|
||||
if (strcmp(name2->full_name, name->full_name) == 0) {
|
||||
/* rename to same name is null-op */
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
if (!info->rename_information.in.overwrite) {
|
||||
return NT_STATUS_OBJECT_NAME_COLLISION;
|
||||
}
|
||||
|
||||
status = pvfs_can_delete(pvfs, req, name2, &lck);
|
||||
if (NT_STATUS_EQUAL(status, NT_STATUS_SHARING_VIOLATION)) {
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
status = pvfs_access_check_parent(pvfs, req, name2, SEC_DIR_ADD_FILE);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
status = pvfs_do_rename(pvfs, name, name2->full_name);
|
||||
if (NT_STATUS_IS_OK(status)) {
|
||||
name->full_name = talloc_steal(name, name2->full_name);
|
||||
name->original_name = talloc_steal(name, name2->original_name);
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
add a single DOS EA
|
||||
*/
|
||||
NTSTATUS pvfs_setfileinfo_ea_set(struct pvfs_state *pvfs,
|
||||
struct pvfs_filename *name,
|
||||
int fd, uint16_t num_eas,
|
||||
struct ea_struct *eas)
|
||||
{
|
||||
struct xattr_DosEAs *ealist;
|
||||
int i, j;
|
||||
NTSTATUS status;
|
||||
|
||||
if (num_eas == 0) {
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
|
||||
return NT_STATUS_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
ealist = talloc(name, struct xattr_DosEAs);
|
||||
|
||||
/* load the current list */
|
||||
status = pvfs_doseas_load(pvfs, name, fd, ealist);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
for (j=0;j<num_eas;j++) {
|
||||
struct ea_struct *ea = &eas[j];
|
||||
/* see if its already there */
|
||||
for (i=0;i<ealist->num_eas;i++) {
|
||||
if (strcasecmp_m(ealist->eas[i].name, ea->name.s) == 0) {
|
||||
ealist->eas[i].value = ea->value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i==ealist->num_eas) {
|
||||
/* add it */
|
||||
ealist->eas = talloc_realloc(ealist, ealist->eas,
|
||||
struct xattr_EA,
|
||||
ealist->num_eas+1);
|
||||
if (ealist->eas == NULL) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
ealist->eas[i].name = ea->name.s;
|
||||
ealist->eas[i].value = ea->value;
|
||||
ealist->num_eas++;
|
||||
}
|
||||
}
|
||||
|
||||
/* pull out any null EAs */
|
||||
for (i=0;i<ealist->num_eas;i++) {
|
||||
if (ealist->eas[i].value.length == 0) {
|
||||
memmove(&ealist->eas[i],
|
||||
&ealist->eas[i+1],
|
||||
(ealist->num_eas-(i+1)) * sizeof(ealist->eas[i]));
|
||||
ealist->num_eas--;
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
status = pvfs_doseas_save(pvfs, name, fd, ealist);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
notify_trigger(pvfs->notify_context,
|
||||
NOTIFY_ACTION_MODIFIED,
|
||||
FILE_NOTIFY_CHANGE_EA,
|
||||
name->full_name);
|
||||
|
||||
name->dos.ea_size = 4;
|
||||
for (i=0;i<ealist->num_eas;i++) {
|
||||
name->dos.ea_size += 4 + strlen(ealist->eas[i].name)+1 +
|
||||
ealist->eas[i].value.length;
|
||||
}
|
||||
|
||||
/* update the ea_size attrib */
|
||||
return pvfs_dosattrib_save(pvfs, name, fd);
|
||||
}
|
||||
|
||||
/*
|
||||
set info on a open file
|
||||
*/
|
||||
NTSTATUS pvfs_setfileinfo(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
union smb_setfileinfo *info)
|
||||
{
|
||||
struct pvfs_state *pvfs = ntvfs->private_data;
|
||||
struct utimbuf unix_times;
|
||||
struct pvfs_file *f;
|
||||
struct pvfs_file_handle *h;
|
||||
struct pvfs_filename newstats;
|
||||
NTSTATUS status;
|
||||
uint32_t access_needed;
|
||||
uint32_t change_mask = 0;
|
||||
|
||||
f = pvfs_find_fd(pvfs, req, info->generic.in.file.ntvfs);
|
||||
if (!f) {
|
||||
return NT_STATUS_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
h = f->handle;
|
||||
|
||||
access_needed = pvfs_setfileinfo_access(info);
|
||||
if ((f->access_mask & access_needed) != access_needed) {
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
/* update the file information */
|
||||
status = pvfs_resolve_name_fd(pvfs, h->fd, h->name);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
/* we take a copy of the current file stats, then update
|
||||
newstats in each of the elements below. At the end we
|
||||
compare, and make any changes needed */
|
||||
newstats = *h->name;
|
||||
|
||||
switch (info->generic.level) {
|
||||
case RAW_SFILEINFO_SETATTR:
|
||||
if (!null_time(info->setattr.in.write_time)) {
|
||||
unix_to_nt_time(&newstats.dos.write_time, info->setattr.in.write_time);
|
||||
}
|
||||
if (info->setattr.in.attrib != FILE_ATTRIBUTE_NORMAL) {
|
||||
newstats.dos.attrib = info->setattr.in.attrib;
|
||||
}
|
||||
break;
|
||||
|
||||
case RAW_SFILEINFO_SETATTRE:
|
||||
case RAW_SFILEINFO_STANDARD:
|
||||
if (!null_time(info->setattre.in.create_time)) {
|
||||
unix_to_nt_time(&newstats.dos.create_time, info->setattre.in.create_time);
|
||||
}
|
||||
if (!null_time(info->setattre.in.access_time)) {
|
||||
unix_to_nt_time(&newstats.dos.access_time, info->setattre.in.access_time);
|
||||
}
|
||||
if (!null_time(info->setattre.in.write_time)) {
|
||||
unix_to_nt_time(&newstats.dos.write_time, info->setattre.in.write_time);
|
||||
}
|
||||
break;
|
||||
|
||||
case RAW_SFILEINFO_EA_SET:
|
||||
return pvfs_setfileinfo_ea_set(pvfs, h->name, h->fd,
|
||||
info->ea_set.in.num_eas,
|
||||
info->ea_set.in.eas);
|
||||
|
||||
case RAW_SFILEINFO_BASIC_INFO:
|
||||
case RAW_SFILEINFO_BASIC_INFORMATION:
|
||||
if (!null_nttime(info->basic_info.in.create_time)) {
|
||||
newstats.dos.create_time = info->basic_info.in.create_time;
|
||||
}
|
||||
if (!null_nttime(info->basic_info.in.access_time)) {
|
||||
newstats.dos.access_time = info->basic_info.in.access_time;
|
||||
}
|
||||
if (!null_nttime(info->basic_info.in.write_time)) {
|
||||
newstats.dos.write_time = info->basic_info.in.write_time;
|
||||
newstats.dos.flags |= XATTR_ATTRIB_FLAG_STICKY_WRITE_TIME;
|
||||
h->sticky_write_time = True;
|
||||
}
|
||||
if (!null_nttime(info->basic_info.in.change_time)) {
|
||||
newstats.dos.change_time = info->basic_info.in.change_time;
|
||||
}
|
||||
if (info->basic_info.in.attrib != 0) {
|
||||
newstats.dos.attrib = info->basic_info.in.attrib;
|
||||
}
|
||||
break;
|
||||
|
||||
case RAW_SFILEINFO_DISPOSITION_INFO:
|
||||
case RAW_SFILEINFO_DISPOSITION_INFORMATION:
|
||||
return pvfs_set_delete_on_close(pvfs, req, f,
|
||||
info->disposition_info.in.delete_on_close);
|
||||
|
||||
case RAW_SFILEINFO_ALLOCATION_INFO:
|
||||
case RAW_SFILEINFO_ALLOCATION_INFORMATION:
|
||||
newstats.dos.alloc_size = info->allocation_info.in.alloc_size;
|
||||
if (newstats.dos.alloc_size < newstats.st.st_size) {
|
||||
newstats.st.st_size = newstats.dos.alloc_size;
|
||||
}
|
||||
newstats.dos.alloc_size = pvfs_round_alloc_size(pvfs,
|
||||
newstats.dos.alloc_size);
|
||||
break;
|
||||
|
||||
case RAW_SFILEINFO_END_OF_FILE_INFO:
|
||||
case RAW_SFILEINFO_END_OF_FILE_INFORMATION:
|
||||
newstats.st.st_size = info->end_of_file_info.in.size;
|
||||
break;
|
||||
|
||||
case RAW_SFILEINFO_POSITION_INFORMATION:
|
||||
h->position = info->position_information.in.position;
|
||||
break;
|
||||
|
||||
case RAW_SFILEINFO_MODE_INFORMATION:
|
||||
/* this one is a puzzle */
|
||||
if (info->mode_information.in.mode != 0 &&
|
||||
info->mode_information.in.mode != 2 &&
|
||||
info->mode_information.in.mode != 4 &&
|
||||
info->mode_information.in.mode != 6) {
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
h->mode = info->mode_information.in.mode;
|
||||
break;
|
||||
|
||||
case RAW_SFILEINFO_RENAME_INFORMATION:
|
||||
return pvfs_setfileinfo_rename(pvfs, req, h->name,
|
||||
info);
|
||||
|
||||
case RAW_SFILEINFO_SEC_DESC:
|
||||
notify_trigger(pvfs->notify_context,
|
||||
NOTIFY_ACTION_MODIFIED,
|
||||
FILE_NOTIFY_CHANGE_SECURITY,
|
||||
h->name->full_name);
|
||||
return pvfs_acl_set(pvfs, req, h->name, h->fd, f->access_mask, info);
|
||||
|
||||
default:
|
||||
return NT_STATUS_INVALID_LEVEL;
|
||||
}
|
||||
|
||||
/* possibly change the file size */
|
||||
if (newstats.st.st_size != h->name->st.st_size) {
|
||||
if (h->name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) {
|
||||
return NT_STATUS_FILE_IS_A_DIRECTORY;
|
||||
}
|
||||
if (h->name->stream_name) {
|
||||
status = pvfs_stream_truncate(pvfs, h->name, h->fd, newstats.st.st_size);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
change_mask |= FILE_NOTIFY_CHANGE_STREAM_SIZE;
|
||||
} else {
|
||||
int ret;
|
||||
if (f->access_mask &
|
||||
(SEC_FILE_WRITE_DATA|SEC_FILE_APPEND_DATA)) {
|
||||
ret = ftruncate(h->fd, newstats.st.st_size);
|
||||
} else {
|
||||
ret = truncate(h->name->full_name, newstats.st.st_size);
|
||||
}
|
||||
if (ret == -1) {
|
||||
return pvfs_map_errno(pvfs, errno);
|
||||
}
|
||||
change_mask |= FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_ATTRIBUTES;
|
||||
}
|
||||
}
|
||||
|
||||
/* possibly change the file timestamps */
|
||||
ZERO_STRUCT(unix_times);
|
||||
if (newstats.dos.create_time != h->name->dos.create_time) {
|
||||
change_mask |= FILE_NOTIFY_CHANGE_CREATION;
|
||||
}
|
||||
if (newstats.dos.access_time != h->name->dos.access_time) {
|
||||
unix_times.actime = nt_time_to_unix(newstats.dos.access_time);
|
||||
change_mask |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
|
||||
}
|
||||
if (newstats.dos.write_time != h->name->dos.write_time) {
|
||||
unix_times.modtime = nt_time_to_unix(newstats.dos.write_time);
|
||||
change_mask |= FILE_NOTIFY_CHANGE_LAST_WRITE;
|
||||
}
|
||||
if (unix_times.actime != 0 || unix_times.modtime != 0) {
|
||||
if (utime(h->name->full_name, &unix_times) == -1) {
|
||||
return pvfs_map_errno(pvfs, errno);
|
||||
}
|
||||
}
|
||||
|
||||
/* possibly change the attribute */
|
||||
if (newstats.dos.attrib != h->name->dos.attrib) {
|
||||
mode_t mode = pvfs_fileperms(pvfs, newstats.dos.attrib);
|
||||
if (!(h->name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) {
|
||||
if (fchmod(h->fd, mode) == -1) {
|
||||
return pvfs_map_errno(pvfs, errno);
|
||||
}
|
||||
}
|
||||
change_mask |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
|
||||
}
|
||||
|
||||
*h->name = newstats;
|
||||
|
||||
notify_trigger(pvfs->notify_context,
|
||||
NOTIFY_ACTION_MODIFIED,
|
||||
change_mask,
|
||||
h->name->full_name);
|
||||
|
||||
return pvfs_dosattrib_save(pvfs, h->name, h->fd);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
set info on a pathname
|
||||
*/
|
||||
NTSTATUS pvfs_setpathinfo(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_setfileinfo *info)
|
||||
{
|
||||
struct pvfs_state *pvfs = ntvfs->private_data;
|
||||
struct pvfs_filename *name;
|
||||
struct pvfs_filename newstats;
|
||||
NTSTATUS status;
|
||||
struct utimbuf unix_times;
|
||||
uint32_t access_needed;
|
||||
uint32_t change_mask = 0;
|
||||
|
||||
/* resolve the cifs name to a posix name */
|
||||
status = pvfs_resolve_name(pvfs, req, info->generic.in.file.path,
|
||||
PVFS_RESOLVE_STREAMS, &name);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
if (!name->exists) {
|
||||
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
||||
}
|
||||
|
||||
access_needed = pvfs_setfileinfo_access(info);
|
||||
status = pvfs_access_check_simple(pvfs, req, name, access_needed);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
/* we take a copy of the current file stats, then update
|
||||
newstats in each of the elements below. At the end we
|
||||
compare, and make any changes needed */
|
||||
newstats = *name;
|
||||
|
||||
switch (info->generic.level) {
|
||||
case RAW_SFILEINFO_SETATTR:
|
||||
if (!null_time(info->setattr.in.write_time)) {
|
||||
unix_to_nt_time(&newstats.dos.write_time, info->setattr.in.write_time);
|
||||
}
|
||||
if (info->setattr.in.attrib == 0) {
|
||||
newstats.dos.attrib = FILE_ATTRIBUTE_NORMAL;
|
||||
} else if (info->setattr.in.attrib != FILE_ATTRIBUTE_NORMAL) {
|
||||
newstats.dos.attrib = info->setattr.in.attrib;
|
||||
}
|
||||
break;
|
||||
|
||||
case RAW_SFILEINFO_SETATTRE:
|
||||
case RAW_SFILEINFO_STANDARD:
|
||||
if (!null_time(info->setattre.in.create_time)) {
|
||||
unix_to_nt_time(&newstats.dos.create_time, info->setattre.in.create_time);
|
||||
}
|
||||
if (!null_time(info->setattre.in.access_time)) {
|
||||
unix_to_nt_time(&newstats.dos.access_time, info->setattre.in.access_time);
|
||||
}
|
||||
if (!null_time(info->setattre.in.write_time)) {
|
||||
unix_to_nt_time(&newstats.dos.write_time, info->setattre.in.write_time);
|
||||
}
|
||||
break;
|
||||
|
||||
case RAW_SFILEINFO_EA_SET:
|
||||
return pvfs_setfileinfo_ea_set(pvfs, name, -1,
|
||||
info->ea_set.in.num_eas,
|
||||
info->ea_set.in.eas);
|
||||
|
||||
case RAW_SFILEINFO_BASIC_INFO:
|
||||
case RAW_SFILEINFO_BASIC_INFORMATION:
|
||||
if (!null_nttime(info->basic_info.in.create_time)) {
|
||||
newstats.dos.create_time = info->basic_info.in.create_time;
|
||||
}
|
||||
if (!null_nttime(info->basic_info.in.access_time)) {
|
||||
newstats.dos.access_time = info->basic_info.in.access_time;
|
||||
}
|
||||
if (!null_nttime(info->basic_info.in.write_time)) {
|
||||
newstats.dos.write_time = info->basic_info.in.write_time;
|
||||
}
|
||||
if (!null_nttime(info->basic_info.in.change_time)) {
|
||||
newstats.dos.change_time = info->basic_info.in.change_time;
|
||||
}
|
||||
if (info->basic_info.in.attrib != 0) {
|
||||
newstats.dos.attrib = info->basic_info.in.attrib;
|
||||
}
|
||||
break;
|
||||
|
||||
case RAW_SFILEINFO_ALLOCATION_INFO:
|
||||
case RAW_SFILEINFO_ALLOCATION_INFORMATION:
|
||||
if (info->allocation_info.in.alloc_size > newstats.dos.alloc_size) {
|
||||
/* strange. Increasing the allocation size via setpathinfo
|
||||
should be silently ignored */
|
||||
break;
|
||||
}
|
||||
newstats.dos.alloc_size = info->allocation_info.in.alloc_size;
|
||||
if (newstats.dos.alloc_size < newstats.st.st_size) {
|
||||
newstats.st.st_size = newstats.dos.alloc_size;
|
||||
}
|
||||
newstats.dos.alloc_size = pvfs_round_alloc_size(pvfs,
|
||||
newstats.dos.alloc_size);
|
||||
break;
|
||||
|
||||
case RAW_SFILEINFO_END_OF_FILE_INFO:
|
||||
case RAW_SFILEINFO_END_OF_FILE_INFORMATION:
|
||||
newstats.st.st_size = info->end_of_file_info.in.size;
|
||||
break;
|
||||
|
||||
case RAW_SFILEINFO_MODE_INFORMATION:
|
||||
if (info->mode_information.in.mode != 0 &&
|
||||
info->mode_information.in.mode != 2 &&
|
||||
info->mode_information.in.mode != 4 &&
|
||||
info->mode_information.in.mode != 6) {
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
return NT_STATUS_OK;
|
||||
|
||||
case RAW_SFILEINFO_RENAME_INFORMATION:
|
||||
return pvfs_setfileinfo_rename(pvfs, req, name,
|
||||
info);
|
||||
|
||||
case RAW_SFILEINFO_DISPOSITION_INFO:
|
||||
case RAW_SFILEINFO_DISPOSITION_INFORMATION:
|
||||
case RAW_SFILEINFO_POSITION_INFORMATION:
|
||||
return NT_STATUS_OK;
|
||||
|
||||
default:
|
||||
return NT_STATUS_INVALID_LEVEL;
|
||||
}
|
||||
|
||||
/* possibly change the file size */
|
||||
if (newstats.st.st_size != name->st.st_size) {
|
||||
if (name->stream_name) {
|
||||
status = pvfs_stream_truncate(pvfs, name, -1, newstats.st.st_size);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
} else if (truncate(name->full_name, newstats.st.st_size) == -1) {
|
||||
return pvfs_map_errno(pvfs, errno);
|
||||
}
|
||||
change_mask |= FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_ATTRIBUTES;
|
||||
}
|
||||
|
||||
/* possibly change the file timestamps */
|
||||
ZERO_STRUCT(unix_times);
|
||||
if (newstats.dos.create_time != name->dos.create_time) {
|
||||
change_mask |= FILE_NOTIFY_CHANGE_CREATION;
|
||||
}
|
||||
if (newstats.dos.access_time != name->dos.access_time) {
|
||||
unix_times.actime = nt_time_to_unix(newstats.dos.access_time);
|
||||
change_mask |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
|
||||
}
|
||||
if (newstats.dos.write_time != name->dos.write_time) {
|
||||
unix_times.modtime = nt_time_to_unix(newstats.dos.write_time);
|
||||
change_mask |= FILE_NOTIFY_CHANGE_LAST_WRITE;
|
||||
}
|
||||
if (unix_times.actime != 0 || unix_times.modtime != 0) {
|
||||
if (utime(name->full_name, &unix_times) == -1) {
|
||||
return pvfs_map_errno(pvfs, errno);
|
||||
}
|
||||
}
|
||||
|
||||
/* possibly change the attribute */
|
||||
newstats.dos.attrib |= (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY);
|
||||
if (newstats.dos.attrib != name->dos.attrib) {
|
||||
mode_t mode = pvfs_fileperms(pvfs, newstats.dos.attrib);
|
||||
if (chmod(name->full_name, mode) == -1) {
|
||||
return pvfs_map_errno(pvfs, errno);
|
||||
}
|
||||
change_mask |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
|
||||
}
|
||||
|
||||
*name = newstats;
|
||||
|
||||
if (change_mask != 0) {
|
||||
notify_trigger(pvfs->notify_context,
|
||||
NOTIFY_ACTION_MODIFIED,
|
||||
change_mask,
|
||||
name->full_name);
|
||||
}
|
||||
|
||||
return pvfs_dosattrib_save(pvfs, name, -1);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,695 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
POSIX NTVFS backend - 8.3 name routines
|
||||
|
||||
Copyright (C) Andrew Tridgell 2004
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 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.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "system/locale.h"
|
||||
#include "vfs_posix.h"
|
||||
|
||||
/*
|
||||
this mangling scheme uses the following format
|
||||
|
||||
Annnn~n.AAA
|
||||
|
||||
where nnnnn is a base 36 hash, and A represents characters from the original string
|
||||
|
||||
The hash is taken of the leading part of the long filename, in uppercase
|
||||
|
||||
for simplicity, we only allow ascii characters in 8.3 names
|
||||
*/
|
||||
|
||||
/*
|
||||
===============================================================================
|
||||
NOTE NOTE NOTE!!!
|
||||
|
||||
This file deliberately uses non-multibyte string functions in many places. This
|
||||
is *not* a mistake. This code is multi-byte safe, but it gets this property
|
||||
through some very subtle knowledge of the way multi-byte strings are encoded
|
||||
and the fact that this mangling algorithm only supports ascii characters in
|
||||
8.3 names.
|
||||
|
||||
please don't convert this file to use the *_m() functions!!
|
||||
===============================================================================
|
||||
*/
|
||||
|
||||
|
||||
#if 1
|
||||
#define M_DEBUG(level, x) DEBUG(level, x)
|
||||
#else
|
||||
#define M_DEBUG(level, x)
|
||||
#endif
|
||||
|
||||
/* these flags are used to mark characters in as having particular
|
||||
properties */
|
||||
#define FLAG_BASECHAR 1
|
||||
#define FLAG_ASCII 2
|
||||
#define FLAG_ILLEGAL 4
|
||||
#define FLAG_WILDCARD 8
|
||||
|
||||
/* the "possible" flags are used as a fast way to find possible DOS
|
||||
reserved filenames */
|
||||
#define FLAG_POSSIBLE1 16
|
||||
#define FLAG_POSSIBLE2 32
|
||||
#define FLAG_POSSIBLE3 64
|
||||
#define FLAG_POSSIBLE4 128
|
||||
|
||||
#define DEFAULT_MANGLE_PREFIX 4
|
||||
|
||||
#define MANGLE_BASECHARS "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
|
||||
#define FLAG_CHECK(c, flag) (ctx->char_flags[(uint8_t)(c)] & (flag))
|
||||
|
||||
static const char *reserved_names[] =
|
||||
{ "AUX", "CON", "COM1", "COM2", "COM3", "COM4",
|
||||
"LPT1", "LPT2", "LPT3", "NUL", "PRN", NULL };
|
||||
|
||||
|
||||
struct pvfs_mangle_context {
|
||||
uint8_t char_flags[256];
|
||||
/*
|
||||
this determines how many characters are used from the original
|
||||
filename in the 8.3 mangled name. A larger value leads to a weaker
|
||||
hash and more collisions. The largest possible value is 6.
|
||||
*/
|
||||
int mangle_prefix;
|
||||
uint32_t mangle_modulus;
|
||||
|
||||
/* we will use a very simple direct mapped prefix cache. The big
|
||||
advantage of this cache structure is speed and low memory usage
|
||||
|
||||
The cache is indexed by the low-order bits of the hash, and confirmed by
|
||||
hashing the resulting cache entry to match the known hash
|
||||
*/
|
||||
uint32_t cache_size;
|
||||
char **prefix_cache;
|
||||
uint32_t *prefix_cache_hashes;
|
||||
|
||||
/* this is used to reverse the base 36 mapping */
|
||||
unsigned char base_reverse[256];
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
hash a string of the specified length. The string does not need to be
|
||||
null terminated
|
||||
|
||||
this hash needs to be fast with a low collision rate (what hash doesn't?)
|
||||
*/
|
||||
static uint32_t mangle_hash(struct pvfs_mangle_context *ctx,
|
||||
const char *key, size_t length)
|
||||
{
|
||||
return pvfs_name_hash(key, length) % ctx->mangle_modulus;
|
||||
}
|
||||
|
||||
/*
|
||||
insert an entry into the prefix cache. The string might not be null
|
||||
terminated */
|
||||
static void cache_insert(struct pvfs_mangle_context *ctx,
|
||||
const char *prefix, int length, uint32_t hash)
|
||||
{
|
||||
int i = hash % ctx->cache_size;
|
||||
|
||||
if (ctx->prefix_cache[i]) {
|
||||
talloc_free(ctx->prefix_cache[i]);
|
||||
}
|
||||
|
||||
ctx->prefix_cache[i] = talloc_strndup(ctx->prefix_cache, prefix, length);
|
||||
ctx->prefix_cache_hashes[i] = hash;
|
||||
}
|
||||
|
||||
/*
|
||||
lookup an entry in the prefix cache. Return NULL if not found.
|
||||
*/
|
||||
static const char *cache_lookup(struct pvfs_mangle_context *ctx, uint32_t hash)
|
||||
{
|
||||
int i = hash % ctx->cache_size;
|
||||
|
||||
|
||||
if (!ctx->prefix_cache[i] || hash != ctx->prefix_cache_hashes[i]) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* yep, it matched */
|
||||
return ctx->prefix_cache[i];
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
determine if a string is possibly in a mangled format, ignoring
|
||||
case
|
||||
|
||||
In this algorithm, mangled names use only pure ascii characters (no
|
||||
multi-byte) so we can avoid doing a UCS2 conversion
|
||||
*/
|
||||
static BOOL is_mangled_component(struct pvfs_mangle_context *ctx,
|
||||
const char *name, size_t len)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
M_DEBUG(10,("is_mangled_component %s (len %u) ?\n", name, (unsigned int)len));
|
||||
|
||||
/* check the length */
|
||||
if (len > 12 || len < 8)
|
||||
return False;
|
||||
|
||||
/* the best distinguishing characteristic is the ~ */
|
||||
if (name[6] != '~')
|
||||
return False;
|
||||
|
||||
/* check extension */
|
||||
if (len > 8) {
|
||||
if (name[8] != '.')
|
||||
return False;
|
||||
for (i=9; name[i] && i < len; i++) {
|
||||
if (! FLAG_CHECK(name[i], FLAG_ASCII)) {
|
||||
return False;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* check lead characters */
|
||||
for (i=0;i<ctx->mangle_prefix;i++) {
|
||||
if (! FLAG_CHECK(name[i], FLAG_ASCII)) {
|
||||
return False;
|
||||
}
|
||||
}
|
||||
|
||||
/* check rest of hash */
|
||||
if (! FLAG_CHECK(name[7], FLAG_BASECHAR)) {
|
||||
return False;
|
||||
}
|
||||
for (i=ctx->mangle_prefix;i<6;i++) {
|
||||
if (! FLAG_CHECK(name[i], FLAG_BASECHAR)) {
|
||||
return False;
|
||||
}
|
||||
}
|
||||
|
||||
M_DEBUG(10,("is_mangled_component %s (len %u) -> yes\n", name, (unsigned int)len));
|
||||
|
||||
return True;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
determine if a string is possibly in a mangled format, ignoring
|
||||
case
|
||||
|
||||
In this algorithm, mangled names use only pure ascii characters (no
|
||||
multi-byte) so we can avoid doing a UCS2 conversion
|
||||
|
||||
NOTE! This interface must be able to handle a path with unix
|
||||
directory separators. It should return true if any component is
|
||||
mangled
|
||||
*/
|
||||
static BOOL is_mangled(struct pvfs_mangle_context *ctx, const char *name)
|
||||
{
|
||||
const char *p;
|
||||
const char *s;
|
||||
|
||||
M_DEBUG(10,("is_mangled %s ?\n", name));
|
||||
|
||||
for (s=name; (p=strchr(s, '/')); s=p+1) {
|
||||
if (is_mangled_component(ctx, s, PTR_DIFF(p, s))) {
|
||||
return True;
|
||||
}
|
||||
}
|
||||
|
||||
/* and the last part ... */
|
||||
return is_mangled_component(ctx, s, strlen(s));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
see if a filename is an allowable 8.3 name.
|
||||
|
||||
we are only going to allow ascii characters in 8.3 names, as this
|
||||
simplifies things greatly (it means that we know the string won't
|
||||
get larger when converted from UNIX to DOS formats)
|
||||
*/
|
||||
static BOOL is_8_3(struct pvfs_mangle_context *ctx,
|
||||
const char *name, BOOL check_case, BOOL allow_wildcards)
|
||||
{
|
||||
int len, i;
|
||||
char *dot_p;
|
||||
|
||||
/* as a special case, the names '.' and '..' are allowable 8.3 names */
|
||||
if (name[0] == '.') {
|
||||
if (!name[1] || (name[1] == '.' && !name[2])) {
|
||||
return True;
|
||||
}
|
||||
}
|
||||
|
||||
/* the simplest test is on the overall length of the
|
||||
filename. Note that we deliberately use the ascii string
|
||||
length (not the multi-byte one) as it is faster, and gives us
|
||||
the result we need in this case. Using strlen_m would not
|
||||
only be slower, it would be incorrect */
|
||||
len = strlen(name);
|
||||
if (len > 12)
|
||||
return False;
|
||||
|
||||
/* find the '.'. Note that once again we use the non-multibyte
|
||||
function */
|
||||
dot_p = strchr(name, '.');
|
||||
|
||||
if (!dot_p) {
|
||||
/* if the name doesn't contain a '.' then its length
|
||||
must be less than 8 */
|
||||
if (len > 8) {
|
||||
return False;
|
||||
}
|
||||
} else {
|
||||
int prefix_len, suffix_len;
|
||||
|
||||
/* if it does contain a dot then the prefix must be <=
|
||||
8 and the suffix <= 3 in length */
|
||||
prefix_len = PTR_DIFF(dot_p, name);
|
||||
suffix_len = len - (prefix_len+1);
|
||||
|
||||
if (prefix_len > 8 || suffix_len > 3 || suffix_len == 0) {
|
||||
return False;
|
||||
}
|
||||
|
||||
/* a 8.3 name cannot contain more than 1 '.' */
|
||||
if (strchr(dot_p+1, '.')) {
|
||||
return False;
|
||||
}
|
||||
}
|
||||
|
||||
/* the length are all OK. Now check to see if the characters themselves are OK */
|
||||
for (i=0; name[i]; i++) {
|
||||
/* note that we may allow wildcard petterns! */
|
||||
if (!FLAG_CHECK(name[i], FLAG_ASCII|(allow_wildcards ? FLAG_WILDCARD : 0)) &&
|
||||
name[i] != '.') {
|
||||
return False;
|
||||
}
|
||||
}
|
||||
|
||||
/* it is a good 8.3 name */
|
||||
return True;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
try to find a 8.3 name in the cache, and if found then
|
||||
return the original long name.
|
||||
*/
|
||||
static char *check_cache(struct pvfs_mangle_context *ctx,
|
||||
TALLOC_CTX *mem_ctx, const char *name)
|
||||
{
|
||||
uint32_t hash, multiplier;
|
||||
unsigned int i;
|
||||
const char *prefix;
|
||||
char extension[4];
|
||||
|
||||
/* make sure that this is a mangled name from this cache */
|
||||
if (!is_mangled(ctx, name)) {
|
||||
M_DEBUG(10,("check_cache: %s -> not mangled\n", name));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* we need to extract the hash from the 8.3 name */
|
||||
hash = ctx->base_reverse[(unsigned char)name[7]];
|
||||
for (multiplier=36, i=5;i>=ctx->mangle_prefix;i--) {
|
||||
uint32_t v = ctx->base_reverse[(unsigned char)name[i]];
|
||||
hash += multiplier * v;
|
||||
multiplier *= 36;
|
||||
}
|
||||
|
||||
/* now look in the prefix cache for that hash */
|
||||
prefix = cache_lookup(ctx, hash);
|
||||
if (!prefix) {
|
||||
M_DEBUG(10,("check_cache: %s -> %08X -> not found\n", name, hash));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* we found it - construct the full name */
|
||||
if (name[8] == '.') {
|
||||
strncpy(extension, name+9, 3);
|
||||
extension[3] = 0;
|
||||
} else {
|
||||
extension[0] = 0;
|
||||
}
|
||||
|
||||
if (extension[0]) {
|
||||
return talloc_asprintf(mem_ctx, "%s.%s", prefix, extension);
|
||||
}
|
||||
|
||||
return talloc_strdup(mem_ctx, prefix);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
look for a DOS reserved name
|
||||
*/
|
||||
static BOOL is_reserved_name(struct pvfs_mangle_context *ctx, const char *name)
|
||||
{
|
||||
if (FLAG_CHECK(name[0], FLAG_POSSIBLE1) &&
|
||||
FLAG_CHECK(name[1], FLAG_POSSIBLE2) &&
|
||||
FLAG_CHECK(name[2], FLAG_POSSIBLE3) &&
|
||||
FLAG_CHECK(name[3], FLAG_POSSIBLE4)) {
|
||||
/* a likely match, scan the lot */
|
||||
int i;
|
||||
for (i=0; reserved_names[i]; i++) {
|
||||
if (strcasecmp(name, reserved_names[i]) == 0) {
|
||||
return True;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return False;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
See if a filename is a legal long filename.
|
||||
A filename ending in a '.' is not legal unless it's "." or "..". JRA.
|
||||
*/
|
||||
static BOOL is_legal_name(struct pvfs_mangle_context *ctx, const char *name)
|
||||
{
|
||||
while (*name) {
|
||||
size_t c_size;
|
||||
codepoint_t c = next_codepoint(name, &c_size);
|
||||
if (c == INVALID_CODEPOINT) {
|
||||
return False;
|
||||
}
|
||||
/* all high chars are OK */
|
||||
if (c >= 128) {
|
||||
name += c_size;
|
||||
continue;
|
||||
}
|
||||
if (FLAG_CHECK(c, FLAG_ILLEGAL)) {
|
||||
return False;
|
||||
}
|
||||
name += c_size;
|
||||
}
|
||||
|
||||
return True;
|
||||
}
|
||||
|
||||
/*
|
||||
the main forward mapping function, which converts a long filename to
|
||||
a 8.3 name
|
||||
|
||||
if need83 is not set then we only do the mangling if the name is illegal
|
||||
as a long name
|
||||
|
||||
if cache83 is not set then we don't cache the result
|
||||
|
||||
return NULL if we don't need to do any conversion
|
||||
*/
|
||||
static char *name_map(struct pvfs_mangle_context *ctx,
|
||||
const char *name, BOOL need83, BOOL cache83)
|
||||
{
|
||||
char *dot_p;
|
||||
char lead_chars[7];
|
||||
char extension[4];
|
||||
unsigned int extension_length, i;
|
||||
unsigned int prefix_len;
|
||||
uint32_t hash, v;
|
||||
char *new_name;
|
||||
const char *basechars = MANGLE_BASECHARS;
|
||||
|
||||
/* reserved names are handled specially */
|
||||
if (!is_reserved_name(ctx, name)) {
|
||||
/* if the name is already a valid 8.3 name then we don't need to
|
||||
do anything */
|
||||
if (is_8_3(ctx, name, False, False)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* if the caller doesn't strictly need 8.3 then just check for illegal
|
||||
filenames */
|
||||
if (!need83 && is_legal_name(ctx, name)) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* find the '.' if any */
|
||||
dot_p = strrchr(name, '.');
|
||||
|
||||
if (dot_p) {
|
||||
/* if the extension contains any illegal characters or
|
||||
is too long or zero length then we treat it as part
|
||||
of the prefix */
|
||||
for (i=0; i<4 && dot_p[i+1]; i++) {
|
||||
if (! FLAG_CHECK(dot_p[i+1], FLAG_ASCII)) {
|
||||
dot_p = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == 0 || i == 4) dot_p = NULL;
|
||||
}
|
||||
|
||||
/* the leading characters in the mangled name is taken from
|
||||
the first characters of the name, if they are ascii otherwise
|
||||
'_' is used
|
||||
*/
|
||||
for (i=0;i<ctx->mangle_prefix && name[i];i++) {
|
||||
lead_chars[i] = name[i];
|
||||
if (! FLAG_CHECK(lead_chars[i], FLAG_ASCII)) {
|
||||
lead_chars[i] = '_';
|
||||
}
|
||||
lead_chars[i] = toupper((unsigned char)lead_chars[i]);
|
||||
}
|
||||
for (;i<ctx->mangle_prefix;i++) {
|
||||
lead_chars[i] = '_';
|
||||
}
|
||||
|
||||
/* the prefix is anything up to the first dot */
|
||||
if (dot_p) {
|
||||
prefix_len = PTR_DIFF(dot_p, name);
|
||||
} else {
|
||||
prefix_len = strlen(name);
|
||||
}
|
||||
|
||||
/* the extension of the mangled name is taken from the first 3
|
||||
ascii chars after the dot */
|
||||
extension_length = 0;
|
||||
if (dot_p) {
|
||||
for (i=1; extension_length < 3 && dot_p[i]; i++) {
|
||||
unsigned char c = dot_p[i];
|
||||
if (FLAG_CHECK(c, FLAG_ASCII)) {
|
||||
extension[extension_length++] = toupper(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* find the hash for this prefix */
|
||||
v = hash = mangle_hash(ctx, name, prefix_len);
|
||||
|
||||
new_name = talloc_array(ctx, char, 13);
|
||||
if (new_name == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* now form the mangled name. */
|
||||
for (i=0;i<ctx->mangle_prefix;i++) {
|
||||
new_name[i] = lead_chars[i];
|
||||
}
|
||||
new_name[7] = basechars[v % 36];
|
||||
new_name[6] = '~';
|
||||
for (i=5; i>=ctx->mangle_prefix; i--) {
|
||||
v = v / 36;
|
||||
new_name[i] = basechars[v % 36];
|
||||
}
|
||||
|
||||
/* add the extension */
|
||||
if (extension_length) {
|
||||
new_name[8] = '.';
|
||||
memcpy(&new_name[9], extension, extension_length);
|
||||
new_name[9+extension_length] = 0;
|
||||
} else {
|
||||
new_name[8] = 0;
|
||||
}
|
||||
|
||||
if (cache83) {
|
||||
/* put it in the cache */
|
||||
cache_insert(ctx, name, prefix_len, hash);
|
||||
}
|
||||
|
||||
M_DEBUG(10,("name_map: %s -> %08X -> %s (cache=%d)\n",
|
||||
name, hash, new_name, cache83));
|
||||
|
||||
return new_name;
|
||||
}
|
||||
|
||||
|
||||
/* initialise the flags table
|
||||
|
||||
we allow only a very restricted set of characters as 'ascii' in this
|
||||
mangling backend. This isn't a significant problem as modern clients
|
||||
use the 'long' filenames anyway, and those don't have these
|
||||
restrictions.
|
||||
*/
|
||||
static void init_tables(struct pvfs_mangle_context *ctx)
|
||||
{
|
||||
const char *basechars = MANGLE_BASECHARS;
|
||||
int i;
|
||||
/* the list of reserved dos names - all of these are illegal */
|
||||
|
||||
ZERO_STRUCT(ctx->char_flags);
|
||||
|
||||
for (i=1;i<128;i++) {
|
||||
if ((i >= '0' && i <= '9') ||
|
||||
(i >= 'a' && i <= 'z') ||
|
||||
(i >= 'A' && i <= 'Z')) {
|
||||
ctx->char_flags[i] |= (FLAG_ASCII | FLAG_BASECHAR);
|
||||
}
|
||||
if (strchr("_-$~", i)) {
|
||||
ctx->char_flags[i] |= FLAG_ASCII;
|
||||
}
|
||||
|
||||
if (strchr("*\\/?<>|\":", i)) {
|
||||
ctx->char_flags[i] |= FLAG_ILLEGAL;
|
||||
}
|
||||
|
||||
if (strchr("*?\"<>", i)) {
|
||||
ctx->char_flags[i] |= FLAG_WILDCARD;
|
||||
}
|
||||
}
|
||||
|
||||
ZERO_STRUCT(ctx->base_reverse);
|
||||
for (i=0;i<36;i++) {
|
||||
ctx->base_reverse[(uint8_t)basechars[i]] = i;
|
||||
}
|
||||
|
||||
/* fill in the reserved names flags. These are used as a very
|
||||
fast filter for finding possible DOS reserved filenames */
|
||||
for (i=0; reserved_names[i]; i++) {
|
||||
unsigned char c1, c2, c3, c4;
|
||||
|
||||
c1 = (unsigned char)reserved_names[i][0];
|
||||
c2 = (unsigned char)reserved_names[i][1];
|
||||
c3 = (unsigned char)reserved_names[i][2];
|
||||
c4 = (unsigned char)reserved_names[i][3];
|
||||
|
||||
ctx->char_flags[c1] |= FLAG_POSSIBLE1;
|
||||
ctx->char_flags[c2] |= FLAG_POSSIBLE2;
|
||||
ctx->char_flags[c3] |= FLAG_POSSIBLE3;
|
||||
ctx->char_flags[c4] |= FLAG_POSSIBLE4;
|
||||
ctx->char_flags[tolower(c1)] |= FLAG_POSSIBLE1;
|
||||
ctx->char_flags[tolower(c2)] |= FLAG_POSSIBLE2;
|
||||
ctx->char_flags[tolower(c3)] |= FLAG_POSSIBLE3;
|
||||
ctx->char_flags[tolower(c4)] |= FLAG_POSSIBLE4;
|
||||
|
||||
ctx->char_flags[(unsigned char)'.'] |= FLAG_POSSIBLE4;
|
||||
}
|
||||
|
||||
ctx->mangle_modulus = 1;
|
||||
for (i=0;i<(7-ctx->mangle_prefix);i++) {
|
||||
ctx->mangle_modulus *= 36;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
initialise the mangling code
|
||||
*/
|
||||
NTSTATUS pvfs_mangle_init(struct pvfs_state *pvfs)
|
||||
{
|
||||
struct pvfs_mangle_context *ctx;
|
||||
|
||||
ctx = talloc(pvfs, struct pvfs_mangle_context);
|
||||
if (ctx == NULL) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
/* by default have a max of 512 entries in the cache. */
|
||||
ctx->cache_size = lp_parm_int(-1, "mangle", "cachesize", 512);
|
||||
|
||||
ctx->prefix_cache = talloc_array(ctx, char *, ctx->cache_size);
|
||||
if (ctx->prefix_cache == NULL) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
ctx->prefix_cache_hashes = talloc_array(ctx, uint32_t, ctx->cache_size);
|
||||
if (ctx->prefix_cache_hashes == NULL) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
memset(ctx->prefix_cache, 0, sizeof(char *) * ctx->cache_size);
|
||||
memset(ctx->prefix_cache_hashes, 0, sizeof(uint32_t) * ctx->cache_size);
|
||||
|
||||
ctx->mangle_prefix = lp_parm_int(-1, "mangle", "prefix", -1);
|
||||
if (ctx->mangle_prefix < 0 || ctx->mangle_prefix > 6) {
|
||||
ctx->mangle_prefix = DEFAULT_MANGLE_PREFIX;
|
||||
}
|
||||
|
||||
init_tables(ctx);
|
||||
|
||||
pvfs->mangle_ctx = ctx;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
return the short name for a component of a full name
|
||||
*/
|
||||
char *pvfs_short_name_component(struct pvfs_state *pvfs, const char *name)
|
||||
{
|
||||
return name_map(pvfs->mangle_ctx, name, True, True);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
return the short name for a given entry in a directory
|
||||
*/
|
||||
const char *pvfs_short_name(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx,
|
||||
struct pvfs_filename *name)
|
||||
{
|
||||
char *p = strrchr(name->full_name, '/');
|
||||
char *ret = pvfs_short_name_component(pvfs, p+1);
|
||||
if (ret == NULL) {
|
||||
return p+1;
|
||||
}
|
||||
talloc_steal(mem_ctx, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
lookup a mangled name, returning the original long name if present
|
||||
in the cache
|
||||
*/
|
||||
char *pvfs_mangled_lookup(struct pvfs_state *pvfs, TALLOC_CTX *mem_ctx,
|
||||
const char *name)
|
||||
{
|
||||
return check_cache(pvfs->mangle_ctx, mem_ctx, name);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
look for a DOS reserved name
|
||||
*/
|
||||
BOOL pvfs_is_reserved_name(struct pvfs_state *pvfs, const char *name)
|
||||
{
|
||||
return is_reserved_name(pvfs->mangle_ctx, name);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
see if a component of a filename could be a mangled name from our
|
||||
mangling code
|
||||
*/
|
||||
BOOL pvfs_is_mangled_component(struct pvfs_state *pvfs, const char *name)
|
||||
{
|
||||
return is_mangled_component(pvfs->mangle_ctx, name, strlen(name));
|
||||
}
|
||||
@@ -0,0 +1,358 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
POSIX NTVFS backend - alternate data streams
|
||||
|
||||
Copyright (C) Andrew Tridgell 2004
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 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.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "vfs_posix.h"
|
||||
#include "librpc/gen_ndr/xattr.h"
|
||||
|
||||
|
||||
/*
|
||||
return the list of file streams for RAW_FILEINFO_STREAM_INFORMATION
|
||||
*/
|
||||
NTSTATUS pvfs_stream_information(struct pvfs_state *pvfs,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
struct pvfs_filename *name, int fd,
|
||||
struct stream_information *info)
|
||||
{
|
||||
struct xattr_DosStreams *streams;
|
||||
int i;
|
||||
NTSTATUS status;
|
||||
|
||||
streams = talloc(mem_ctx, struct xattr_DosStreams);
|
||||
if (streams == NULL) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
status = pvfs_streams_load(pvfs, name, fd, streams);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
ZERO_STRUCTP(streams);
|
||||
}
|
||||
|
||||
info->num_streams = streams->num_streams+1;
|
||||
info->streams = talloc_array(mem_ctx, struct stream_struct, info->num_streams);
|
||||
if (!info->streams) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
info->streams[0].size = name->st.st_size;
|
||||
info->streams[0].alloc_size = name->dos.alloc_size;
|
||||
info->streams[0].stream_name.s = talloc_strdup(info->streams, "::$DATA");
|
||||
|
||||
for (i=0;i<streams->num_streams;i++) {
|
||||
info->streams[i+1].size = streams->streams[i].size;
|
||||
info->streams[i+1].alloc_size = streams->streams[i].alloc_size;
|
||||
info->streams[i+1].stream_name.s = talloc_asprintf(streams->streams,
|
||||
":%s:$DATA",
|
||||
streams->streams[i].name);
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
fill in the stream information for a name
|
||||
*/
|
||||
NTSTATUS pvfs_stream_info(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd)
|
||||
{
|
||||
struct xattr_DosStreams *streams;
|
||||
int i;
|
||||
NTSTATUS status;
|
||||
|
||||
/* the NULL stream always exists */
|
||||
if (name->stream_name == NULL) {
|
||||
name->stream_exists = True;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
streams = talloc(name, struct xattr_DosStreams);
|
||||
if (streams == NULL) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
status = pvfs_streams_load(pvfs, name, fd, streams);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
talloc_free(streams);
|
||||
return status;
|
||||
}
|
||||
|
||||
for (i=0;i<streams->num_streams;i++) {
|
||||
struct xattr_DosStream *s = &streams->streams[i];
|
||||
if (strcasecmp_m(s->name, name->stream_name) == 0) {
|
||||
name->dos.alloc_size = pvfs_round_alloc_size(pvfs, s->alloc_size);
|
||||
name->st.st_size = s->size;
|
||||
name->stream_exists = True;
|
||||
talloc_free(streams);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
talloc_free(streams);
|
||||
|
||||
name->dos.alloc_size = 0;
|
||||
name->st.st_size = 0;
|
||||
name->stream_exists = False;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
update size information for a stream
|
||||
*/
|
||||
static NTSTATUS pvfs_stream_update_size(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd,
|
||||
off_t size)
|
||||
{
|
||||
struct xattr_DosStreams *streams;
|
||||
int i;
|
||||
NTSTATUS status;
|
||||
|
||||
streams = talloc(name, struct xattr_DosStreams);
|
||||
if (streams == NULL) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
status = pvfs_streams_load(pvfs, name, fd, streams);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
ZERO_STRUCTP(streams);
|
||||
}
|
||||
|
||||
for (i=0;i<streams->num_streams;i++) {
|
||||
struct xattr_DosStream *s = &streams->streams[i];
|
||||
if (strcasecmp_m(s->name, name->stream_name) == 0) {
|
||||
s->size = size;
|
||||
s->alloc_size = pvfs_round_alloc_size(pvfs, size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == streams->num_streams) {
|
||||
struct xattr_DosStream *s;
|
||||
streams->streams = talloc_realloc(streams, streams->streams,
|
||||
struct xattr_DosStream,
|
||||
streams->num_streams+1);
|
||||
if (streams->streams == NULL) {
|
||||
talloc_free(streams);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
streams->num_streams++;
|
||||
s = &streams->streams[i];
|
||||
|
||||
s->flags = XATTR_STREAM_FLAG_INTERNAL;
|
||||
s->size = size;
|
||||
s->alloc_size = pvfs_round_alloc_size(pvfs, size);
|
||||
s->name = name->stream_name;
|
||||
}
|
||||
|
||||
status = pvfs_streams_save(pvfs, name, fd, streams);
|
||||
talloc_free(streams);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
create the xattr for a alternate data stream
|
||||
*/
|
||||
NTSTATUS pvfs_stream_create(struct pvfs_state *pvfs,
|
||||
struct pvfs_filename *name,
|
||||
int fd)
|
||||
{
|
||||
NTSTATUS status;
|
||||
status = pvfs_xattr_create(pvfs, name->full_name, fd,
|
||||
XATTR_DOSSTREAM_PREFIX, name->stream_name);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
return pvfs_stream_update_size(pvfs, name, fd, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
delete the xattr for a alternate data stream
|
||||
*/
|
||||
NTSTATUS pvfs_stream_delete(struct pvfs_state *pvfs,
|
||||
struct pvfs_filename *name,
|
||||
int fd)
|
||||
{
|
||||
NTSTATUS status;
|
||||
struct xattr_DosStreams *streams;
|
||||
int i;
|
||||
|
||||
status = pvfs_xattr_delete(pvfs, name->full_name, fd,
|
||||
XATTR_DOSSTREAM_PREFIX, name->stream_name);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
streams = talloc(name, struct xattr_DosStreams);
|
||||
if (streams == NULL) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
status = pvfs_streams_load(pvfs, name, fd, streams);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
talloc_free(streams);
|
||||
return status;
|
||||
}
|
||||
|
||||
for (i=0;i<streams->num_streams;i++) {
|
||||
struct xattr_DosStream *s = &streams->streams[i];
|
||||
if (strcasecmp_m(s->name, name->stream_name) == 0) {
|
||||
memmove(s, s+1, (streams->num_streams - (i+1)) * sizeof(*s));
|
||||
streams->num_streams--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
status = pvfs_streams_save(pvfs, name, fd, streams);
|
||||
talloc_free(streams);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
the equvalent of pread() on a stream
|
||||
*/
|
||||
ssize_t pvfs_stream_read(struct pvfs_state *pvfs,
|
||||
struct pvfs_file_handle *h, void *data, size_t count, off_t offset)
|
||||
{
|
||||
NTSTATUS status;
|
||||
DATA_BLOB blob;
|
||||
if (count == 0) {
|
||||
return 0;
|
||||
}
|
||||
status = pvfs_xattr_load(pvfs, h, h->name->full_name, h->fd, XATTR_DOSSTREAM_PREFIX,
|
||||
h->name->stream_name, offset+count, &blob);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
errno = EIO;
|
||||
return -1;
|
||||
}
|
||||
if (offset >= blob.length) {
|
||||
data_blob_free(&blob);
|
||||
return 0;
|
||||
}
|
||||
if (count > blob.length - offset) {
|
||||
count = blob.length - offset;
|
||||
}
|
||||
memcpy(data, blob.data + offset, count);
|
||||
data_blob_free(&blob);
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
the equvalent of pwrite() on a stream
|
||||
*/
|
||||
ssize_t pvfs_stream_write(struct pvfs_state *pvfs,
|
||||
struct pvfs_file_handle *h, const void *data, size_t count, off_t offset)
|
||||
{
|
||||
NTSTATUS status;
|
||||
DATA_BLOB blob;
|
||||
if (count == 0) {
|
||||
return 0;
|
||||
}
|
||||
if (offset > XATTR_MAX_STREAM_SIZE) {
|
||||
errno = ENOSPC;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* we have to load the existing stream, then modify, then save */
|
||||
status = pvfs_xattr_load(pvfs, h, h->name->full_name, h->fd, XATTR_DOSSTREAM_PREFIX,
|
||||
h->name->stream_name, offset+count, &blob);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
blob = data_blob(NULL, 0);
|
||||
}
|
||||
if (count+offset > blob.length) {
|
||||
blob.data = talloc_realloc(blob.data, blob.data, uint8_t, count+offset);
|
||||
if (blob.data == NULL) {
|
||||
errno = ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
if (offset > blob.length) {
|
||||
memset(blob.data+blob.length, 0, offset - blob.length);
|
||||
}
|
||||
blob.length = count+offset;
|
||||
}
|
||||
memcpy(blob.data + offset, data, count);
|
||||
|
||||
status = pvfs_xattr_save(pvfs, h->name->full_name, h->fd, XATTR_DOSSTREAM_PREFIX,
|
||||
h->name->stream_name, &blob);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
data_blob_free(&blob);
|
||||
/* getting this error mapping right is probably
|
||||
not worth it */
|
||||
errno = ENOSPC;
|
||||
return -1;
|
||||
}
|
||||
|
||||
status = pvfs_stream_update_size(pvfs, h->name, h->fd, blob.length);
|
||||
|
||||
data_blob_free(&blob);
|
||||
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
errno = EIO;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/*
|
||||
the equvalent of truncate() on a stream
|
||||
*/
|
||||
NTSTATUS pvfs_stream_truncate(struct pvfs_state *pvfs,
|
||||
struct pvfs_filename *name, int fd, off_t length)
|
||||
{
|
||||
NTSTATUS status;
|
||||
DATA_BLOB blob;
|
||||
|
||||
if (length > XATTR_MAX_STREAM_SIZE) {
|
||||
return NT_STATUS_DISK_FULL;
|
||||
}
|
||||
|
||||
/* we have to load the existing stream, then modify, then save */
|
||||
status = pvfs_xattr_load(pvfs, name, name->full_name, fd, XATTR_DOSSTREAM_PREFIX,
|
||||
name->stream_name, length, &blob);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
if (length <= blob.length) {
|
||||
blob.length = length;
|
||||
} else if (length > blob.length) {
|
||||
blob.data = talloc_realloc(blob.data, blob.data, uint8_t, length);
|
||||
if (blob.data == NULL) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
memset(blob.data+blob.length, 0, length - blob.length);
|
||||
blob.length = length;
|
||||
}
|
||||
|
||||
status = pvfs_xattr_save(pvfs, name->full_name, fd, XATTR_DOSSTREAM_PREFIX,
|
||||
name->stream_name, &blob);
|
||||
data_blob_free(&blob);
|
||||
|
||||
if (NT_STATUS_IS_OK(status)) {
|
||||
status = pvfs_stream_update_size(pvfs, name, fd, blob.length);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
POSIX NTVFS backend - unlink
|
||||
|
||||
Copyright (C) Andrew Tridgell 2004
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 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.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "vfs_posix.h"
|
||||
#include "system/dir.h"
|
||||
|
||||
|
||||
/*
|
||||
unlink a stream
|
||||
*/
|
||||
static NTSTATUS pvfs_unlink_stream(struct pvfs_state *pvfs,
|
||||
struct ntvfs_request *req,
|
||||
struct pvfs_filename *name,
|
||||
uint16_t attrib)
|
||||
{
|
||||
NTSTATUS status;
|
||||
struct odb_lock *lck;
|
||||
|
||||
if (!name->stream_exists) {
|
||||
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
||||
}
|
||||
|
||||
/* make sure its matches the given attributes */
|
||||
status = pvfs_match_attrib(pvfs, name, attrib, 0);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
status = pvfs_can_delete(pvfs, req, name, &lck);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
return pvfs_stream_delete(pvfs, name, -1);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
unlink one file
|
||||
*/
|
||||
static NTSTATUS pvfs_unlink_one(struct pvfs_state *pvfs,
|
||||
struct ntvfs_request *req,
|
||||
const char *unix_path,
|
||||
const char *fname, uint32_t attrib)
|
||||
{
|
||||
struct pvfs_filename *name;
|
||||
NTSTATUS status;
|
||||
struct odb_lock *lck;
|
||||
|
||||
/* get a pvfs_filename object */
|
||||
status = pvfs_resolve_partial(pvfs, req,
|
||||
unix_path, fname, &name);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
/* make sure its matches the given attributes */
|
||||
status = pvfs_match_attrib(pvfs, name, attrib, 0);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
talloc_free(name);
|
||||
return status;
|
||||
}
|
||||
|
||||
status = pvfs_can_delete(pvfs, req, name, &lck);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
talloc_free(name);
|
||||
return status;
|
||||
}
|
||||
|
||||
if (name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY) {
|
||||
talloc_free(name);
|
||||
return NT_STATUS_FILE_IS_A_DIRECTORY;
|
||||
}
|
||||
|
||||
if (name->st.st_nlink == 1) {
|
||||
status = pvfs_xattr_unlink_hook(pvfs, name->full_name);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
/* finally try the actual unlink */
|
||||
if (unlink(name->full_name) == -1) {
|
||||
status = pvfs_map_errno(pvfs, errno);
|
||||
}
|
||||
|
||||
if (NT_STATUS_IS_OK(status)) {
|
||||
notify_trigger(pvfs->notify_context,
|
||||
NOTIFY_ACTION_REMOVED,
|
||||
FILE_NOTIFY_CHANGE_FILE_NAME,
|
||||
name->full_name);
|
||||
}
|
||||
|
||||
talloc_free(name);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
delete a file - the dirtype specifies the file types to include in the search.
|
||||
The name can contain CIFS wildcards, but rarely does (except with OS/2 clients)
|
||||
*/
|
||||
NTSTATUS pvfs_unlink(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
union smb_unlink *unl)
|
||||
{
|
||||
struct pvfs_state *pvfs = ntvfs->private_data;
|
||||
struct pvfs_dir *dir;
|
||||
NTSTATUS status;
|
||||
uint32_t total_deleted=0;
|
||||
struct pvfs_filename *name;
|
||||
const char *fname;
|
||||
off_t ofs;
|
||||
|
||||
/* resolve the cifs name to a posix name */
|
||||
status = pvfs_resolve_name(pvfs, req, unl->unlink.in.pattern,
|
||||
PVFS_RESOLVE_WILDCARD | PVFS_RESOLVE_STREAMS, &name);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
if (!name->exists && !name->has_wildcard) {
|
||||
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
||||
}
|
||||
|
||||
if (name->exists &&
|
||||
(name->dos.attrib & FILE_ATTRIBUTE_DIRECTORY)) {
|
||||
return NT_STATUS_FILE_IS_A_DIRECTORY;
|
||||
}
|
||||
|
||||
if (name->stream_name) {
|
||||
return pvfs_unlink_stream(pvfs, req, name, unl->unlink.in.attrib);
|
||||
}
|
||||
|
||||
/* get list of matching files */
|
||||
status = pvfs_list_start(pvfs, name, req, &dir);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
status = NT_STATUS_NO_SUCH_FILE;
|
||||
|
||||
ofs = 0;
|
||||
|
||||
while ((fname = pvfs_list_next(dir, &ofs))) {
|
||||
/* this seems to be a special case */
|
||||
if ((unl->unlink.in.attrib & FILE_ATTRIBUTE_DIRECTORY) &&
|
||||
(ISDOT(fname) || ISDOTDOT(fname))) {
|
||||
return NT_STATUS_OBJECT_NAME_INVALID;
|
||||
}
|
||||
|
||||
status = pvfs_unlink_one(pvfs, req, pvfs_list_unix_path(dir), fname, unl->unlink.in.attrib);
|
||||
if (NT_STATUS_IS_OK(status)) {
|
||||
total_deleted++;
|
||||
}
|
||||
}
|
||||
|
||||
if (total_deleted > 0) {
|
||||
status = NT_STATUS_OK;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,203 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
Copyright (C) Andrew Tridgell 2004
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
/*
|
||||
utility functions for posix backend
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "vfs_posix.h"
|
||||
|
||||
/*
|
||||
return True if a string contains one of the CIFS wildcard characters
|
||||
*/
|
||||
BOOL pvfs_has_wildcard(const char *str)
|
||||
{
|
||||
if (strpbrk(str, "*?<>\"")) {
|
||||
return True;
|
||||
}
|
||||
return False;
|
||||
}
|
||||
|
||||
/*
|
||||
map a unix errno to a NTSTATUS
|
||||
*/
|
||||
NTSTATUS pvfs_map_errno(struct pvfs_state *pvfs, int unix_errno)
|
||||
{
|
||||
return map_nt_error_from_unix(unix_errno);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
check if a filename has an attribute matching the given attribute search value
|
||||
this is used by calls like unlink and search which take an attribute
|
||||
and only include special files if they match the given attribute
|
||||
*/
|
||||
NTSTATUS pvfs_match_attrib(struct pvfs_state *pvfs, struct pvfs_filename *name,
|
||||
uint32_t attrib, uint32_t must_attrib)
|
||||
{
|
||||
if ((name->dos.attrib & ~attrib) & FILE_ATTRIBUTE_DIRECTORY) {
|
||||
return NT_STATUS_FILE_IS_A_DIRECTORY;
|
||||
}
|
||||
if ((name->dos.attrib & ~attrib) & (FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM)) {
|
||||
return NT_STATUS_NO_SUCH_FILE;
|
||||
}
|
||||
if (must_attrib & ~name->dos.attrib) {
|
||||
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
||||
}
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
normalise a file attribute
|
||||
*/
|
||||
uint32_t pvfs_attrib_normalise(uint32_t attrib, mode_t mode)
|
||||
{
|
||||
if (attrib != FILE_ATTRIBUTE_NORMAL) {
|
||||
attrib &= ~FILE_ATTRIBUTE_NORMAL;
|
||||
}
|
||||
if (S_ISDIR(mode)) {
|
||||
attrib |= FILE_ATTRIBUTE_DIRECTORY;
|
||||
} else {
|
||||
attrib &= ~FILE_ATTRIBUTE_DIRECTORY;
|
||||
}
|
||||
return attrib;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
copy a file. Caller is supposed to have already ensured that the
|
||||
operation is allowed. The destination file must not exist.
|
||||
*/
|
||||
NTSTATUS pvfs_copy_file(struct pvfs_state *pvfs,
|
||||
struct pvfs_filename *name1,
|
||||
struct pvfs_filename *name2)
|
||||
{
|
||||
int fd1, fd2;
|
||||
mode_t mode;
|
||||
NTSTATUS status;
|
||||
size_t buf_size = 0x10000;
|
||||
char *buf = talloc_size(name2, buf_size);
|
||||
|
||||
if (buf == NULL) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
fd1 = open(name1->full_name, O_RDONLY);
|
||||
if (fd1 == -1) {
|
||||
talloc_free(buf);
|
||||
return pvfs_map_errno(pvfs, errno);
|
||||
}
|
||||
|
||||
fd2 = open(name2->full_name, O_CREAT|O_EXCL|O_WRONLY, 0);
|
||||
if (fd2 == -1) {
|
||||
close(fd1);
|
||||
talloc_free(buf);
|
||||
return pvfs_map_errno(pvfs, errno);
|
||||
}
|
||||
|
||||
while (1) {
|
||||
ssize_t ret2, ret = read(fd1, buf, buf_size);
|
||||
if (ret == -1 &&
|
||||
(errno == EINTR || errno == EAGAIN)) {
|
||||
continue;
|
||||
}
|
||||
if (ret <= 0) break;
|
||||
|
||||
ret2 = write(fd2, buf, ret);
|
||||
if (ret2 == -1 &&
|
||||
(errno == EINTR || errno == EAGAIN)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ret2 != ret) {
|
||||
close(fd1);
|
||||
close(fd2);
|
||||
talloc_free(buf);
|
||||
unlink(name2->full_name);
|
||||
if (ret2 == -1) {
|
||||
return pvfs_map_errno(pvfs, errno);
|
||||
}
|
||||
return NT_STATUS_DISK_FULL;
|
||||
}
|
||||
}
|
||||
|
||||
talloc_free(buf);
|
||||
close(fd1);
|
||||
|
||||
mode = pvfs_fileperms(pvfs, name1->dos.attrib);
|
||||
if (fchmod(fd2, mode) == -1) {
|
||||
status = pvfs_map_errno(pvfs, errno);
|
||||
close(fd2);
|
||||
unlink(name2->full_name);
|
||||
return status;
|
||||
}
|
||||
|
||||
name2->st.st_mode = mode;
|
||||
name2->dos = name1->dos;
|
||||
|
||||
status = pvfs_dosattrib_save(pvfs, name2, fd2);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
close(fd2);
|
||||
unlink(name2->full_name);
|
||||
return status;
|
||||
}
|
||||
|
||||
close(fd2);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
hash a string of the specified length. The string does not need to be
|
||||
null terminated
|
||||
|
||||
hash alghorithm changed to FNV1 by idra@samba.org (Simo Sorce).
|
||||
see http://www.isthe.com/chongo/tech/comp/fnv/index.html for a
|
||||
discussion on Fowler / Noll / Vo (FNV) Hash by one of it's authors
|
||||
*/
|
||||
uint32_t pvfs_name_hash(const char *key, size_t length)
|
||||
{
|
||||
const uint32_t fnv1_prime = 0x01000193;
|
||||
const uint32_t fnv1_init = 0xa6b93095;
|
||||
uint32_t value = fnv1_init;
|
||||
|
||||
while (*key && length--) {
|
||||
size_t c_size;
|
||||
codepoint_t c = next_codepoint(key, &c_size);
|
||||
c = toupper_w(c);
|
||||
value *= fnv1_prime;
|
||||
value ^= (uint32_t)c;
|
||||
key += c_size;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
file allocation size rounding. This is required to pass ifstest
|
||||
*/
|
||||
uint64_t pvfs_round_alloc_size(struct pvfs_state *pvfs, uint64_t size)
|
||||
{
|
||||
const uint32_t round_value = pvfs->alloc_size_rounding;
|
||||
return round_value * ((size + round_value - 1)/round_value);
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
POSIX NTVFS backend - async request wait routines
|
||||
|
||||
Copyright (C) Andrew Tridgell 2004
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 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.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "lib/events/events.h"
|
||||
#include "lib/util/dlinklist.h"
|
||||
#include "vfs_posix.h"
|
||||
#include "smbd/service_stream.h"
|
||||
#include "lib/messaging/irpc.h"
|
||||
|
||||
/* the context for a single wait instance */
|
||||
struct pvfs_wait {
|
||||
struct pvfs_wait *next, *prev;
|
||||
struct pvfs_state *pvfs;
|
||||
void (*handler)(void *, enum pvfs_wait_notice);
|
||||
void *private;
|
||||
int msg_type;
|
||||
struct messaging_context *msg_ctx;
|
||||
struct event_context *ev;
|
||||
struct ntvfs_request *req;
|
||||
enum pvfs_wait_notice reason;
|
||||
};
|
||||
|
||||
/*
|
||||
called from the ntvfs layer when we have requested setup of an async
|
||||
call. this ensures that async calls runs with the right state of
|
||||
previous ntvfs handlers in the chain (such as security context)
|
||||
*/
|
||||
NTSTATUS pvfs_async_setup(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, void *private)
|
||||
{
|
||||
struct pvfs_wait *pwait = private;
|
||||
pwait->handler(pwait->private, pwait->reason);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
receive a completion message for a wait
|
||||
*/
|
||||
static void pvfs_wait_dispatch(struct messaging_context *msg, void *private, uint32_t msg_type,
|
||||
uint32_t src, DATA_BLOB *data)
|
||||
{
|
||||
struct pvfs_wait *pwait = private;
|
||||
struct ntvfs_request *req;
|
||||
|
||||
/* we need to check that this one is for us. See
|
||||
messaging_send_ptr() for the other side of this.
|
||||
*/
|
||||
if (data->length != sizeof(void *) ||
|
||||
*(void **)data->data != pwait->private) {
|
||||
return;
|
||||
}
|
||||
pwait->reason = PVFS_WAIT_EVENT;
|
||||
req = pwait->req;
|
||||
|
||||
/* the extra reference here is to ensure that the req
|
||||
structure is not destroyed when the async request reply is
|
||||
sent, which would cause problems with the other ntvfs
|
||||
modules above us */
|
||||
talloc_increase_ref_count(req);
|
||||
ntvfs_async_setup(pwait->req, pwait);
|
||||
talloc_free(req);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
receive a timeout on a message wait
|
||||
*/
|
||||
static void pvfs_wait_timeout(struct event_context *ev,
|
||||
struct timed_event *te, struct timeval t, void *private)
|
||||
{
|
||||
struct pvfs_wait *pwait = talloc_get_type(private, struct pvfs_wait);
|
||||
struct ntvfs_request *req = pwait->req;
|
||||
|
||||
pwait->reason = PVFS_WAIT_TIMEOUT;
|
||||
|
||||
talloc_increase_ref_count(req);
|
||||
ntvfs_async_setup(pwait->req, pwait);
|
||||
talloc_free(req);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
destroy a pending wait
|
||||
*/
|
||||
static int pvfs_wait_destructor(struct pvfs_wait *pwait)
|
||||
{
|
||||
if (pwait->msg_type != -1) {
|
||||
messaging_deregister(pwait->msg_ctx, pwait->msg_type, pwait);
|
||||
}
|
||||
DLIST_REMOVE(pwait->pvfs->wait_list, pwait);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
setup a request to wait on a message of type msg_type, with a
|
||||
timeout (given as an expiry time)
|
||||
|
||||
the return value is a handle. To stop waiting talloc_free this
|
||||
handle.
|
||||
|
||||
if msg_type == -1 then no message is registered, and it is assumed
|
||||
that the caller handles any messaging setup needed
|
||||
*/
|
||||
void *pvfs_wait_message(struct pvfs_state *pvfs,
|
||||
struct ntvfs_request *req,
|
||||
int msg_type,
|
||||
struct timeval end_time,
|
||||
void (*fn)(void *, enum pvfs_wait_notice),
|
||||
void *private)
|
||||
{
|
||||
struct pvfs_wait *pwait;
|
||||
|
||||
pwait = talloc(pvfs, struct pvfs_wait);
|
||||
if (pwait == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pwait->private = private;
|
||||
pwait->handler = fn;
|
||||
pwait->msg_ctx = pvfs->ntvfs->ctx->msg_ctx;
|
||||
pwait->ev = pvfs->ntvfs->ctx->event_ctx;
|
||||
pwait->msg_type = msg_type;
|
||||
pwait->req = talloc_reference(pwait, req);
|
||||
pwait->pvfs = pvfs;
|
||||
|
||||
if (!timeval_is_zero(&end_time)) {
|
||||
/* setup a timer */
|
||||
event_add_timed(pwait->ev, pwait, end_time, pvfs_wait_timeout, pwait);
|
||||
}
|
||||
|
||||
/* register with the messaging subsystem for this message
|
||||
type */
|
||||
if (msg_type != -1) {
|
||||
messaging_register(pwait->msg_ctx,
|
||||
pwait,
|
||||
msg_type,
|
||||
pvfs_wait_dispatch);
|
||||
}
|
||||
|
||||
/* tell the main smb server layer that we will be replying
|
||||
asynchronously */
|
||||
req->async_states->state |= NTVFS_ASYNC_STATE_ASYNC;
|
||||
|
||||
DLIST_ADD(pvfs->wait_list, pwait);
|
||||
|
||||
/* make sure we cleanup the timer and message handler */
|
||||
talloc_set_destructor(pwait, pvfs_wait_destructor);
|
||||
|
||||
return pwait;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
cancel an outstanding async request
|
||||
*/
|
||||
NTSTATUS pvfs_cancel(struct ntvfs_module_context *ntvfs, struct ntvfs_request *req)
|
||||
{
|
||||
struct pvfs_state *pvfs = ntvfs->private_data;
|
||||
struct pvfs_wait *pwait;
|
||||
|
||||
for (pwait=pvfs->wait_list;pwait;pwait=pwait->next) {
|
||||
if (pwait->req == req) {
|
||||
/* trigger a cancel on the request */
|
||||
pwait->reason = PVFS_WAIT_CANCEL;
|
||||
ntvfs_async_setup(pwait->req, pwait);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return NT_STATUS_DOS(ERRDOS, ERRcancelviolation);
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
POSIX NTVFS backend - write
|
||||
|
||||
Copyright (C) Andrew Tridgell 2004
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 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.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "vfs_posix.h"
|
||||
#include "librpc/gen_ndr/security.h"
|
||||
|
||||
|
||||
/*
|
||||
write to a file
|
||||
*/
|
||||
NTSTATUS pvfs_write(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_write *wr)
|
||||
{
|
||||
struct pvfs_state *pvfs = ntvfs->private_data;
|
||||
ssize_t ret;
|
||||
struct pvfs_file *f;
|
||||
NTSTATUS status;
|
||||
|
||||
if (wr->generic.level != RAW_WRITE_WRITEX) {
|
||||
return ntvfs_map_write(ntvfs, req, wr);
|
||||
}
|
||||
|
||||
f = pvfs_find_fd(pvfs, req, wr->writex.in.file.ntvfs);
|
||||
if (!f) {
|
||||
return NT_STATUS_INVALID_HANDLE;
|
||||
}
|
||||
|
||||
if (f->handle->fd == -1) {
|
||||
return NT_STATUS_FILE_IS_A_DIRECTORY;
|
||||
}
|
||||
|
||||
if (!(f->access_mask & (SEC_FILE_WRITE_DATA | SEC_FILE_APPEND_DATA))) {
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
status = pvfs_check_lock(pvfs, f, req->smbpid,
|
||||
wr->writex.in.offset,
|
||||
wr->writex.in.count,
|
||||
WRITE_LOCK);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
if (f->handle->name->stream_name) {
|
||||
ret = pvfs_stream_write(pvfs,
|
||||
f->handle,
|
||||
wr->writex.in.data,
|
||||
wr->writex.in.count,
|
||||
wr->writex.in.offset);
|
||||
} else {
|
||||
ret = pwrite(f->handle->fd,
|
||||
wr->writex.in.data,
|
||||
wr->writex.in.count,
|
||||
wr->writex.in.offset);
|
||||
}
|
||||
if (ret == -1) {
|
||||
if (errno == EFBIG) {
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
return pvfs_map_errno(pvfs, errno);
|
||||
}
|
||||
|
||||
f->handle->seek_offset = wr->writex.in.offset + ret;
|
||||
|
||||
wr->writex.out.nwritten = ret;
|
||||
wr->writex.out.remaining = 0; /* should fill this in? */
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
@@ -0,0 +1,477 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
POSIX NTVFS backend - xattr support
|
||||
|
||||
Copyright (C) Andrew Tridgell 2004
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 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.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "vfs_posix.h"
|
||||
#include "lib/util/unix_privs.h"
|
||||
#include "librpc/gen_ndr/ndr_xattr.h"
|
||||
|
||||
/*
|
||||
pull a xattr as a blob
|
||||
*/
|
||||
static NTSTATUS pull_xattr_blob(struct pvfs_state *pvfs,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const char *attr_name,
|
||||
const char *fname,
|
||||
int fd,
|
||||
size_t estimated_size,
|
||||
DATA_BLOB *blob)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
if (pvfs->ea_db) {
|
||||
return pull_xattr_blob_tdb(pvfs, mem_ctx, attr_name, fname,
|
||||
fd, estimated_size, blob);
|
||||
}
|
||||
|
||||
status = pull_xattr_blob_system(pvfs, mem_ctx, attr_name, fname,
|
||||
fd, estimated_size, blob);
|
||||
|
||||
/* if the filesystem doesn't support them, then tell pvfs not to try again */
|
||||
if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)||
|
||||
NT_STATUS_EQUAL(status, NT_STATUS_NOT_IMPLEMENTED)||
|
||||
NT_STATUS_EQUAL(status, NT_STATUS_INVALID_SYSTEM_SERVICE)) {
|
||||
DEBUG(5,("pvfs_xattr: xattr not supported in filesystem: %s\n", nt_errstr(status)));
|
||||
pvfs->flags &= ~PVFS_FLAG_XATTR_ENABLE;
|
||||
status = NT_STATUS_NOT_FOUND;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
push a xattr as a blob
|
||||
*/
|
||||
static NTSTATUS push_xattr_blob(struct pvfs_state *pvfs,
|
||||
const char *attr_name,
|
||||
const char *fname,
|
||||
int fd,
|
||||
const DATA_BLOB *blob)
|
||||
{
|
||||
if (pvfs->ea_db) {
|
||||
return push_xattr_blob_tdb(pvfs, attr_name, fname, fd, blob);
|
||||
}
|
||||
return push_xattr_blob_system(pvfs, attr_name, fname, fd, blob);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
delete a xattr
|
||||
*/
|
||||
static NTSTATUS delete_xattr(struct pvfs_state *pvfs, const char *attr_name,
|
||||
const char *fname, int fd)
|
||||
{
|
||||
if (pvfs->ea_db) {
|
||||
return delete_xattr_tdb(pvfs, attr_name, fname, fd);
|
||||
}
|
||||
return delete_xattr_system(pvfs, attr_name, fname, fd);
|
||||
}
|
||||
|
||||
/*
|
||||
a hook called on unlink - allows the tdb xattr backend to cleanup
|
||||
*/
|
||||
NTSTATUS pvfs_xattr_unlink_hook(struct pvfs_state *pvfs, const char *fname)
|
||||
{
|
||||
if (pvfs->ea_db) {
|
||||
return unlink_xattr_tdb(pvfs, fname);
|
||||
}
|
||||
return unlink_xattr_system(pvfs, fname);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
load a NDR structure from a xattr
|
||||
*/
|
||||
_PUBLIC_ NTSTATUS pvfs_xattr_ndr_load(struct pvfs_state *pvfs,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const char *fname, int fd, const char *attr_name,
|
||||
void *p, void *pull_fn)
|
||||
{
|
||||
NTSTATUS status;
|
||||
DATA_BLOB blob;
|
||||
|
||||
status = pull_xattr_blob(pvfs, mem_ctx, attr_name, fname,
|
||||
fd, XATTR_DOSATTRIB_ESTIMATED_SIZE, &blob);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
/* pull the blob */
|
||||
status = ndr_pull_struct_blob(&blob, mem_ctx, p, (ndr_pull_flags_fn_t)pull_fn);
|
||||
|
||||
data_blob_free(&blob);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
save a NDR structure into a xattr
|
||||
*/
|
||||
_PUBLIC_ NTSTATUS pvfs_xattr_ndr_save(struct pvfs_state *pvfs,
|
||||
const char *fname, int fd, const char *attr_name,
|
||||
void *p, void *push_fn)
|
||||
{
|
||||
TALLOC_CTX *mem_ctx = talloc_new(NULL);
|
||||
DATA_BLOB blob;
|
||||
NTSTATUS status;
|
||||
|
||||
status = ndr_push_struct_blob(&blob, mem_ctx, p, (ndr_push_flags_fn_t)push_fn);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
talloc_free(mem_ctx);
|
||||
return status;
|
||||
}
|
||||
|
||||
status = push_xattr_blob(pvfs, attr_name, fname, fd, &blob);
|
||||
talloc_free(mem_ctx);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
fill in file attributes from extended attributes
|
||||
*/
|
||||
NTSTATUS pvfs_dosattrib_load(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd)
|
||||
{
|
||||
NTSTATUS status;
|
||||
struct xattr_DosAttrib attrib;
|
||||
TALLOC_CTX *mem_ctx = talloc_new(name);
|
||||
struct xattr_DosInfo1 *info1;
|
||||
struct xattr_DosInfo2 *info2;
|
||||
|
||||
if (name->stream_name != NULL) {
|
||||
name->stream_exists = False;
|
||||
} else {
|
||||
name->stream_exists = True;
|
||||
}
|
||||
|
||||
if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
status = pvfs_xattr_ndr_load(pvfs, mem_ctx, name->full_name,
|
||||
fd, XATTR_DOSATTRIB_NAME,
|
||||
&attrib,
|
||||
(ndr_pull_flags_fn_t)ndr_pull_xattr_DosAttrib);
|
||||
|
||||
/* not having a DosAttrib is not an error */
|
||||
if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
|
||||
talloc_free(mem_ctx);
|
||||
return pvfs_stream_info(pvfs, name, fd);
|
||||
}
|
||||
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
talloc_free(mem_ctx);
|
||||
return status;
|
||||
}
|
||||
|
||||
switch (attrib.version) {
|
||||
case 1:
|
||||
info1 = &attrib.info.info1;
|
||||
name->dos.attrib = pvfs_attrib_normalise(info1->attrib,
|
||||
name->st.st_mode);
|
||||
name->dos.ea_size = info1->ea_size;
|
||||
if (name->st.st_size == info1->size) {
|
||||
name->dos.alloc_size =
|
||||
pvfs_round_alloc_size(pvfs, info1->alloc_size);
|
||||
}
|
||||
if (!null_nttime(info1->create_time)) {
|
||||
name->dos.create_time = info1->create_time;
|
||||
}
|
||||
if (!null_nttime(info1->change_time)) {
|
||||
name->dos.change_time = info1->change_time;
|
||||
}
|
||||
name->dos.flags = 0;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
info2 = &attrib.info.info2;
|
||||
name->dos.attrib = pvfs_attrib_normalise(info2->attrib,
|
||||
name->st.st_mode);
|
||||
name->dos.ea_size = info2->ea_size;
|
||||
if (name->st.st_size == info2->size) {
|
||||
name->dos.alloc_size =
|
||||
pvfs_round_alloc_size(pvfs, info2->alloc_size);
|
||||
}
|
||||
if (!null_nttime(info2->create_time)) {
|
||||
name->dos.create_time = info2->create_time;
|
||||
}
|
||||
if (!null_nttime(info2->change_time)) {
|
||||
name->dos.change_time = info2->change_time;
|
||||
}
|
||||
name->dos.flags = info2->flags;
|
||||
if (name->dos.flags & XATTR_ATTRIB_FLAG_STICKY_WRITE_TIME) {
|
||||
name->dos.write_time = info2->write_time;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
DEBUG(0,("ERROR: Unsupported xattr DosAttrib version %d on '%s'\n",
|
||||
attrib.version, name->full_name));
|
||||
talloc_free(mem_ctx);
|
||||
return NT_STATUS_INVALID_LEVEL;
|
||||
}
|
||||
talloc_free(mem_ctx);
|
||||
|
||||
status = pvfs_stream_info(pvfs, name, fd);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
save the file attribute into the xattr
|
||||
*/
|
||||
NTSTATUS pvfs_dosattrib_save(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd)
|
||||
{
|
||||
struct xattr_DosAttrib attrib;
|
||||
struct xattr_DosInfo2 *info2;
|
||||
|
||||
if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
attrib.version = 2;
|
||||
info2 = &attrib.info.info2;
|
||||
|
||||
name->dos.attrib = pvfs_attrib_normalise(name->dos.attrib, name->st.st_mode);
|
||||
|
||||
info2->attrib = name->dos.attrib;
|
||||
info2->ea_size = name->dos.ea_size;
|
||||
info2->size = name->st.st_size;
|
||||
info2->alloc_size = name->dos.alloc_size;
|
||||
info2->create_time = name->dos.create_time;
|
||||
info2->change_time = name->dos.change_time;
|
||||
info2->write_time = name->dos.write_time;
|
||||
info2->flags = name->dos.flags;
|
||||
info2->name = "";
|
||||
|
||||
return pvfs_xattr_ndr_save(pvfs, name->full_name, fd,
|
||||
XATTR_DOSATTRIB_NAME, &attrib,
|
||||
(ndr_push_flags_fn_t)ndr_push_xattr_DosAttrib);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
load the set of DOS EAs
|
||||
*/
|
||||
NTSTATUS pvfs_doseas_load(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd,
|
||||
struct xattr_DosEAs *eas)
|
||||
{
|
||||
NTSTATUS status;
|
||||
ZERO_STRUCTP(eas);
|
||||
if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
status = pvfs_xattr_ndr_load(pvfs, eas, name->full_name, fd, XATTR_DOSEAS_NAME,
|
||||
eas, (ndr_pull_flags_fn_t)ndr_pull_xattr_DosEAs);
|
||||
if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
save the set of DOS EAs
|
||||
*/
|
||||
NTSTATUS pvfs_doseas_save(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd,
|
||||
struct xattr_DosEAs *eas)
|
||||
{
|
||||
if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
return pvfs_xattr_ndr_save(pvfs, name->full_name, fd, XATTR_DOSEAS_NAME, eas,
|
||||
(ndr_push_flags_fn_t)ndr_push_xattr_DosEAs);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
load the set of streams from extended attributes
|
||||
*/
|
||||
NTSTATUS pvfs_streams_load(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd,
|
||||
struct xattr_DosStreams *streams)
|
||||
{
|
||||
NTSTATUS status;
|
||||
ZERO_STRUCTP(streams);
|
||||
if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
status = pvfs_xattr_ndr_load(pvfs, streams, name->full_name, fd,
|
||||
XATTR_DOSSTREAMS_NAME,
|
||||
streams,
|
||||
(ndr_pull_flags_fn_t)ndr_pull_xattr_DosStreams);
|
||||
if (NT_STATUS_EQUAL(status, NT_STATUS_NOT_FOUND)) {
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
save the set of streams into filesystem xattr
|
||||
*/
|
||||
NTSTATUS pvfs_streams_save(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd,
|
||||
struct xattr_DosStreams *streams)
|
||||
{
|
||||
if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
return pvfs_xattr_ndr_save(pvfs, name->full_name, fd,
|
||||
XATTR_DOSSTREAMS_NAME,
|
||||
streams,
|
||||
(ndr_push_flags_fn_t)ndr_push_xattr_DosStreams);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
load the current ACL from extended attributes
|
||||
*/
|
||||
NTSTATUS pvfs_acl_load(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd,
|
||||
struct xattr_NTACL *acl)
|
||||
{
|
||||
NTSTATUS status;
|
||||
ZERO_STRUCTP(acl);
|
||||
if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
|
||||
return NT_STATUS_NOT_FOUND;
|
||||
}
|
||||
status = pvfs_xattr_ndr_load(pvfs, acl, name->full_name, fd,
|
||||
XATTR_NTACL_NAME,
|
||||
acl,
|
||||
(ndr_pull_flags_fn_t)ndr_pull_xattr_NTACL);
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
save the acl for a file into filesystem xattr
|
||||
*/
|
||||
NTSTATUS pvfs_acl_save(struct pvfs_state *pvfs, struct pvfs_filename *name, int fd,
|
||||
struct xattr_NTACL *acl)
|
||||
{
|
||||
NTSTATUS status;
|
||||
void *privs;
|
||||
|
||||
if (!(pvfs->flags & PVFS_FLAG_XATTR_ENABLE)) {
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/* this xattr is in the "system" namespace, so we need
|
||||
admin privileges to set it */
|
||||
privs = root_privileges();
|
||||
status = pvfs_xattr_ndr_save(pvfs, name->full_name, fd,
|
||||
XATTR_NTACL_NAME,
|
||||
acl,
|
||||
(ndr_push_flags_fn_t)ndr_push_xattr_NTACL);
|
||||
talloc_free(privs);
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
create a zero length xattr with the given name
|
||||
*/
|
||||
NTSTATUS pvfs_xattr_create(struct pvfs_state *pvfs,
|
||||
const char *fname, int fd,
|
||||
const char *attr_prefix,
|
||||
const char *attr_name)
|
||||
{
|
||||
NTSTATUS status;
|
||||
DATA_BLOB blob = data_blob(NULL, 0);
|
||||
char *aname = talloc_asprintf(NULL, "%s%s", attr_prefix, attr_name);
|
||||
if (aname == NULL) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
status = push_xattr_blob(pvfs, aname, fname, fd, &blob);
|
||||
talloc_free(aname);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
delete a xattr with the given name
|
||||
*/
|
||||
NTSTATUS pvfs_xattr_delete(struct pvfs_state *pvfs,
|
||||
const char *fname, int fd,
|
||||
const char *attr_prefix,
|
||||
const char *attr_name)
|
||||
{
|
||||
NTSTATUS status;
|
||||
char *aname = talloc_asprintf(NULL, "%s%s", attr_prefix, attr_name);
|
||||
if (aname == NULL) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
status = delete_xattr(pvfs, aname, fname, fd);
|
||||
talloc_free(aname);
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
load a xattr with the given name
|
||||
*/
|
||||
NTSTATUS pvfs_xattr_load(struct pvfs_state *pvfs,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const char *fname, int fd,
|
||||
const char *attr_prefix,
|
||||
const char *attr_name,
|
||||
size_t estimated_size,
|
||||
DATA_BLOB *blob)
|
||||
{
|
||||
NTSTATUS status;
|
||||
char *aname = talloc_asprintf(mem_ctx, "%s%s", attr_prefix, attr_name);
|
||||
if (aname == NULL) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
status = pull_xattr_blob(pvfs, mem_ctx, aname, fname, fd, estimated_size, blob);
|
||||
talloc_free(aname);
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
save a xattr with the given name
|
||||
*/
|
||||
NTSTATUS pvfs_xattr_save(struct pvfs_state *pvfs,
|
||||
const char *fname, int fd,
|
||||
const char *attr_prefix,
|
||||
const char *attr_name,
|
||||
const DATA_BLOB *blob)
|
||||
{
|
||||
NTSTATUS status;
|
||||
char *aname = talloc_asprintf(NULL, "%s%s", attr_prefix, attr_name);
|
||||
if (aname == NULL) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
status = push_xattr_blob(pvfs, aname, fname, fd, blob);
|
||||
talloc_free(aname);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
probe for system support for xattrs
|
||||
*/
|
||||
void pvfs_xattr_probe(struct pvfs_state *pvfs)
|
||||
{
|
||||
TALLOC_CTX *tmp_ctx = talloc_new(pvfs);
|
||||
DATA_BLOB blob;
|
||||
pull_xattr_blob(pvfs, tmp_ctx, "user.XattrProbe", pvfs->base_directory,
|
||||
-1, 1, &blob);
|
||||
pull_xattr_blob(pvfs, tmp_ctx, "security.XattrProbe", pvfs->base_directory,
|
||||
-1, 1, &blob);
|
||||
talloc_free(tmp_ctx);
|
||||
}
|
||||
@@ -0,0 +1,358 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
POSIX NTVFS backend
|
||||
|
||||
Copyright (C) Andrew Tridgell 2004
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 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"
|
||||
#include "vfs_posix.h"
|
||||
#include "librpc/gen_ndr/security.h"
|
||||
#include "lib/tdb/include/tdb.h"
|
||||
#include "db_wrap.h"
|
||||
#include "libcli/security/security.h"
|
||||
#include "lib/events/events.h"
|
||||
|
||||
|
||||
/*
|
||||
setup config options for a posix share
|
||||
*/
|
||||
static void pvfs_setup_options(struct pvfs_state *pvfs)
|
||||
{
|
||||
struct share_config *scfg = pvfs->ntvfs->ctx->config;
|
||||
const char *eadb;
|
||||
|
||||
if (share_bool_option(scfg, SHARE_MAP_HIDDEN, SHARE_MAP_HIDDEN_DEFAULT))
|
||||
pvfs->flags |= PVFS_FLAG_MAP_HIDDEN;
|
||||
if (share_bool_option(scfg, SHARE_MAP_ARCHIVE, SHARE_MAP_ARCHIVE_DEFAULT))
|
||||
pvfs->flags |= PVFS_FLAG_MAP_ARCHIVE;
|
||||
if (share_bool_option(scfg, SHARE_MAP_SYSTEM, SHARE_MAP_SYSTEM_DEFAULT))
|
||||
pvfs->flags |= PVFS_FLAG_MAP_SYSTEM;
|
||||
if (share_bool_option(scfg, SHARE_READONLY, SHARE_READONLY_DEFAULT))
|
||||
pvfs->flags |= PVFS_FLAG_READONLY;
|
||||
if (share_bool_option(scfg, SHARE_STRICT_SYNC, SHARE_STRICT_SYNC_DEFAULT))
|
||||
pvfs->flags |= PVFS_FLAG_STRICT_SYNC;
|
||||
if (share_bool_option(scfg, SHARE_STRICT_LOCKING, SHARE_STRICT_LOCKING_DEFAULT))
|
||||
pvfs->flags |= PVFS_FLAG_STRICT_LOCKING;
|
||||
if (share_bool_option(scfg, SHARE_CI_FILESYSTEM, SHARE_CI_FILESYSTEM_DEFAULT))
|
||||
pvfs->flags |= PVFS_FLAG_CI_FILESYSTEM;
|
||||
if (share_bool_option(scfg, PVFS_FAKE_OPLOCKS, PVFS_FAKE_OPLOCKS_DEFAULT)) {
|
||||
pvfs->flags |= PVFS_FLAG_FAKE_OPLOCKS;
|
||||
}
|
||||
|
||||
/* this must be a power of 2 */
|
||||
pvfs->alloc_size_rounding = share_int_option(scfg,
|
||||
PVFS_ALLOCATION_ROUNDING,
|
||||
PVFS_ALLOCATION_ROUNDING_DEFAULT);
|
||||
|
||||
pvfs->search.inactivity_time = share_int_option(scfg,
|
||||
PVFS_SEARCH_INACTIVITY,
|
||||
PVFS_SEARCH_INACTIVITY_DEFAULT);
|
||||
|
||||
#if HAVE_XATTR_SUPPORT
|
||||
if (share_bool_option(scfg, PVFS_XATTR, PVFS_XATTR_DEFAULT))
|
||||
pvfs->flags |= PVFS_FLAG_XATTR_ENABLE;
|
||||
#endif
|
||||
|
||||
pvfs->sharing_violation_delay = share_int_option(scfg,
|
||||
PVFS_SHARE_DELAY,
|
||||
PVFS_SHARE_DELAY_DEFAULT);
|
||||
|
||||
pvfs->share_name = talloc_strdup(pvfs, scfg->name);
|
||||
|
||||
pvfs->fs_attribs =
|
||||
FS_ATTR_CASE_SENSITIVE_SEARCH |
|
||||
FS_ATTR_CASE_PRESERVED_NAMES |
|
||||
FS_ATTR_UNICODE_ON_DISK |
|
||||
FS_ATTR_SPARSE_FILES;
|
||||
|
||||
/* allow xattrs to be stored in a external tdb */
|
||||
eadb = share_string_option(scfg, PVFS_EADB, NULL);
|
||||
if (eadb != NULL) {
|
||||
pvfs->ea_db = tdb_wrap_open(pvfs, eadb, 50000,
|
||||
TDB_DEFAULT, O_RDWR|O_CREAT, 0600);
|
||||
if (pvfs->ea_db != NULL) {
|
||||
pvfs->flags |= PVFS_FLAG_XATTR_ENABLE;
|
||||
} else {
|
||||
DEBUG(0,("Failed to open eadb '%s' - %s\n",
|
||||
eadb, strerror(errno)));
|
||||
pvfs->flags &= ~PVFS_FLAG_XATTR_ENABLE;
|
||||
}
|
||||
}
|
||||
|
||||
if (pvfs->flags & PVFS_FLAG_XATTR_ENABLE) {
|
||||
pvfs->fs_attribs |= FS_ATTR_NAMED_STREAMS;
|
||||
}
|
||||
if (pvfs->flags & PVFS_FLAG_XATTR_ENABLE) {
|
||||
pvfs->fs_attribs |= FS_ATTR_PERSISTANT_ACLS;
|
||||
}
|
||||
|
||||
pvfs->sid_cache.creator_owner = dom_sid_parse_talloc(pvfs, SID_CREATOR_OWNER);
|
||||
pvfs->sid_cache.creator_group = dom_sid_parse_talloc(pvfs, SID_CREATOR_GROUP);
|
||||
|
||||
/* check if the system really supports xattrs */
|
||||
if (pvfs->flags & PVFS_FLAG_XATTR_ENABLE) {
|
||||
pvfs_xattr_probe(pvfs);
|
||||
}
|
||||
|
||||
/* enable an ACL backend */
|
||||
pvfs->acl_ops = pvfs_acl_backend_byname(share_string_option(scfg, PVFS_ACL, "xattr"));
|
||||
}
|
||||
|
||||
static int pvfs_state_destructor(struct pvfs_state *pvfs)
|
||||
{
|
||||
struct pvfs_file *f, *fn;
|
||||
struct pvfs_search_state *s, *sn;
|
||||
|
||||
/*
|
||||
* make sure we cleanup files and searches before anything else
|
||||
* because there destructors need to acess the pvfs_state struct
|
||||
*/
|
||||
for (f=pvfs->files.list; f; f=fn) {
|
||||
fn = f->next;
|
||||
talloc_free(f);
|
||||
}
|
||||
|
||||
for (s=pvfs->search.list; s; s=sn) {
|
||||
sn = s->next;
|
||||
talloc_free(s);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
connect to a share - used when a tree_connect operation comes
|
||||
in. For a disk based backend we needs to ensure that the base
|
||||
directory exists (tho it doesn't need to be accessible by the user,
|
||||
that comes later)
|
||||
*/
|
||||
static NTSTATUS pvfs_connect(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, const char *sharename)
|
||||
{
|
||||
struct pvfs_state *pvfs;
|
||||
struct stat st;
|
||||
char *base_directory;
|
||||
NTSTATUS status;
|
||||
|
||||
pvfs = talloc_zero(ntvfs, struct pvfs_state);
|
||||
NT_STATUS_HAVE_NO_MEMORY(pvfs);
|
||||
|
||||
/* for simplicity of path construction, remove any trailing slash now */
|
||||
base_directory = talloc_strdup(pvfs, share_string_option(ntvfs->ctx->config, SHARE_PATH, ""));
|
||||
NT_STATUS_HAVE_NO_MEMORY(base_directory);
|
||||
if (strcmp(base_directory, "/") != 0) {
|
||||
trim_string(base_directory, NULL, "/");
|
||||
}
|
||||
|
||||
pvfs->ntvfs = ntvfs;
|
||||
pvfs->base_directory = base_directory;
|
||||
|
||||
/* the directory must exist. Note that we deliberately don't
|
||||
check that it is readable */
|
||||
if (stat(pvfs->base_directory, &st) != 0 || !S_ISDIR(st.st_mode)) {
|
||||
DEBUG(0,("pvfs_connect: '%s' is not a directory, when connecting to [%s]\n",
|
||||
pvfs->base_directory, sharename));
|
||||
return NT_STATUS_BAD_NETWORK_NAME;
|
||||
}
|
||||
|
||||
ntvfs->ctx->fs_type = talloc_strdup(ntvfs->ctx, "NTFS");
|
||||
NT_STATUS_HAVE_NO_MEMORY(ntvfs->ctx->fs_type);
|
||||
|
||||
ntvfs->ctx->dev_type = talloc_strdup(ntvfs->ctx, "A:");
|
||||
NT_STATUS_HAVE_NO_MEMORY(ntvfs->ctx->dev_type);
|
||||
|
||||
ntvfs->private_data = pvfs;
|
||||
|
||||
pvfs->brl_context = brl_init(pvfs,
|
||||
pvfs->ntvfs->ctx->server_id,
|
||||
pvfs->ntvfs->ctx->msg_ctx);
|
||||
if (pvfs->brl_context == NULL) {
|
||||
return NT_STATUS_INTERNAL_DB_CORRUPTION;
|
||||
}
|
||||
|
||||
pvfs->odb_context = odb_init(pvfs, pvfs->ntvfs->ctx);
|
||||
if (pvfs->odb_context == NULL) {
|
||||
return NT_STATUS_INTERNAL_DB_CORRUPTION;
|
||||
}
|
||||
|
||||
/* allow this to be NULL - we just disable change notify */
|
||||
pvfs->notify_context = notify_init(pvfs,
|
||||
pvfs->ntvfs->ctx->server_id,
|
||||
pvfs->ntvfs->ctx->msg_ctx,
|
||||
event_context_find(pvfs),
|
||||
pvfs->ntvfs->ctx->config);
|
||||
|
||||
pvfs->sidmap = sidmap_open(pvfs);
|
||||
if (pvfs->sidmap == NULL) {
|
||||
return NT_STATUS_INTERNAL_DB_CORRUPTION;
|
||||
}
|
||||
|
||||
/* allocate the search handle -> ptr tree */
|
||||
pvfs->search.idtree = idr_init(pvfs);
|
||||
NT_STATUS_HAVE_NO_MEMORY(pvfs->search.idtree);
|
||||
|
||||
status = pvfs_mangle_init(pvfs);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
pvfs_setup_options(pvfs);
|
||||
|
||||
talloc_set_destructor(pvfs, pvfs_state_destructor);
|
||||
|
||||
#ifdef SIGXFSZ
|
||||
/* who had the stupid idea to generate a signal on a large
|
||||
file write instead of just failing it!? */
|
||||
BlockSignals(True, SIGXFSZ);
|
||||
#endif
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
disconnect from a share
|
||||
*/
|
||||
static NTSTATUS pvfs_disconnect(struct ntvfs_module_context *ntvfs)
|
||||
{
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
check if a directory exists
|
||||
*/
|
||||
static NTSTATUS pvfs_chkpath(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
union smb_chkpath *cp)
|
||||
{
|
||||
struct pvfs_state *pvfs = ntvfs->private_data;
|
||||
struct pvfs_filename *name;
|
||||
NTSTATUS status;
|
||||
|
||||
/* resolve the cifs name to a posix name */
|
||||
status = pvfs_resolve_name(pvfs, req, cp->chkpath.in.path, 0, &name);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
|
||||
if (!name->exists) {
|
||||
return NT_STATUS_OBJECT_NAME_NOT_FOUND;
|
||||
}
|
||||
|
||||
if (!S_ISDIR(name->st.st_mode)) {
|
||||
return NT_STATUS_NOT_A_DIRECTORY;
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
copy a set of files
|
||||
*/
|
||||
static NTSTATUS pvfs_copy(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, struct smb_copy *cp)
|
||||
{
|
||||
DEBUG(0,("pvfs_copy not implemented\n"));
|
||||
return NT_STATUS_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
/*
|
||||
return print queue info
|
||||
*/
|
||||
static NTSTATUS pvfs_lpq(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_lpq *lpq)
|
||||
{
|
||||
return NT_STATUS_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
/* SMBtrans - not used on file shares */
|
||||
static NTSTATUS pvfs_trans(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, struct smb_trans2 *trans2)
|
||||
{
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
/*
|
||||
initialialise the POSIX disk backend, registering ourselves with the ntvfs subsystem
|
||||
*/
|
||||
NTSTATUS ntvfs_posix_init(void)
|
||||
{
|
||||
NTSTATUS ret;
|
||||
struct ntvfs_ops ops;
|
||||
NTVFS_CURRENT_CRITICAL_SIZES(vers);
|
||||
|
||||
ZERO_STRUCT(ops);
|
||||
|
||||
ops.type = NTVFS_DISK;
|
||||
|
||||
/* fill in all the operations */
|
||||
ops.connect = pvfs_connect;
|
||||
ops.disconnect = pvfs_disconnect;
|
||||
ops.unlink = pvfs_unlink;
|
||||
ops.chkpath = pvfs_chkpath;
|
||||
ops.qpathinfo = pvfs_qpathinfo;
|
||||
ops.setpathinfo = pvfs_setpathinfo;
|
||||
ops.open = pvfs_open;
|
||||
ops.mkdir = pvfs_mkdir;
|
||||
ops.rmdir = pvfs_rmdir;
|
||||
ops.rename = pvfs_rename;
|
||||
ops.copy = pvfs_copy;
|
||||
ops.ioctl = pvfs_ioctl;
|
||||
ops.read = pvfs_read;
|
||||
ops.write = pvfs_write;
|
||||
ops.seek = pvfs_seek;
|
||||
ops.flush = pvfs_flush;
|
||||
ops.close = pvfs_close;
|
||||
ops.exit = pvfs_exit;
|
||||
ops.lock = pvfs_lock;
|
||||
ops.setfileinfo = pvfs_setfileinfo;
|
||||
ops.qfileinfo = pvfs_qfileinfo;
|
||||
ops.fsinfo = pvfs_fsinfo;
|
||||
ops.lpq = pvfs_lpq;
|
||||
ops.search_first = pvfs_search_first;
|
||||
ops.search_next = pvfs_search_next;
|
||||
ops.search_close = pvfs_search_close;
|
||||
ops.trans = pvfs_trans;
|
||||
ops.logoff = pvfs_logoff;
|
||||
ops.async_setup = pvfs_async_setup;
|
||||
ops.cancel = pvfs_cancel;
|
||||
ops.notify = pvfs_notify;
|
||||
|
||||
/* register ourselves with the NTVFS subsystem. We register
|
||||
under the name 'default' as we wish to be the default
|
||||
backend, and also register as 'posix' */
|
||||
ops.name = "default";
|
||||
ret = ntvfs_register(&ops, &vers);
|
||||
|
||||
if (!NT_STATUS_IS_OK(ret)) {
|
||||
DEBUG(0,("Failed to register POSIX backend as '%s'!\n", ops.name));
|
||||
}
|
||||
|
||||
ops.name = "posix";
|
||||
ret = ntvfs_register(&ops, &vers);
|
||||
|
||||
if (!NT_STATUS_IS_OK(ret)) {
|
||||
DEBUG(0,("Failed to register POSIX backend as '%s'!\n", ops.name));
|
||||
}
|
||||
|
||||
if (NT_STATUS_IS_OK(ret)) {
|
||||
ret = ntvfs_common_init();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,243 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
POSIX NTVFS backend - structure definitions
|
||||
|
||||
Copyright (C) Andrew Tridgell 2004
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 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.
|
||||
*/
|
||||
|
||||
#ifndef _VFS_POSIX_H_
|
||||
#define _VFS_POSIX_H_
|
||||
|
||||
#include "librpc/gen_ndr/xattr.h"
|
||||
#include "system/filesys.h"
|
||||
#include "ntvfs/ntvfs.h"
|
||||
#include "ntvfs/common/proto.h"
|
||||
#include "dsdb/samdb/samdb.h"
|
||||
|
||||
/* this is the private structure for the posix vfs backend. It is used
|
||||
to hold per-connection (per tree connect) state information */
|
||||
struct pvfs_state {
|
||||
struct ntvfs_module_context *ntvfs;
|
||||
const char *base_directory;
|
||||
struct GUID *base_fs_uuid;
|
||||
|
||||
const char *share_name;
|
||||
uint_t flags;
|
||||
|
||||
struct pvfs_mangle_context *mangle_ctx;
|
||||
|
||||
struct brl_context *brl_context;
|
||||
struct odb_context *odb_context;
|
||||
struct notify_context *notify_context;
|
||||
struct sidmap_context *sidmap;
|
||||
|
||||
/* a list of pending async requests. Needed to support
|
||||
ntcancel */
|
||||
struct pvfs_wait *wait_list;
|
||||
|
||||
/* the sharing violation timeout */
|
||||
uint_t sharing_violation_delay;
|
||||
|
||||
/* filesystem attributes (see FS_ATTR_*) */
|
||||
uint32_t fs_attribs;
|
||||
|
||||
/* if posix:eadb is set, then this gets setup */
|
||||
struct tdb_wrap *ea_db;
|
||||
|
||||
/* the allocation size rounding */
|
||||
uint32_t alloc_size_rounding;
|
||||
|
||||
struct {
|
||||
/* the open files as DLINKLIST */
|
||||
struct pvfs_file *list;
|
||||
} files;
|
||||
|
||||
struct {
|
||||
/* an id tree mapping open search ID to a pvfs_search_state structure */
|
||||
struct idr_context *idtree;
|
||||
|
||||
/* the open searches as DLINKLIST */
|
||||
struct pvfs_search_state *list;
|
||||
|
||||
/* how long to keep inactive searches around for */
|
||||
uint_t inactivity_time;
|
||||
} search;
|
||||
|
||||
/* used to accelerate acl mapping */
|
||||
struct {
|
||||
const struct dom_sid *creator_owner;
|
||||
const struct dom_sid *creator_group;
|
||||
} sid_cache;
|
||||
|
||||
/* the acl backend */
|
||||
const struct pvfs_acl_ops *acl_ops;
|
||||
};
|
||||
|
||||
/* this is the basic information needed about a file from the filesystem */
|
||||
struct pvfs_dos_fileinfo {
|
||||
NTTIME create_time;
|
||||
NTTIME access_time;
|
||||
NTTIME write_time;
|
||||
NTTIME change_time;
|
||||
uint32_t attrib;
|
||||
uint64_t alloc_size;
|
||||
uint32_t nlink;
|
||||
uint32_t ea_size;
|
||||
uint64_t file_id;
|
||||
uint32_t flags;
|
||||
};
|
||||
|
||||
/*
|
||||
this is the structure returned by pvfs_resolve_name(). It holds the posix details of
|
||||
a filename passed by the client to any function
|
||||
*/
|
||||
struct pvfs_filename {
|
||||
const char *original_name;
|
||||
char *full_name;
|
||||
const char *stream_name; /* does not include :$DATA suffix */
|
||||
uint32_t stream_id; /* this uses a hash, so is probabilistic */
|
||||
BOOL has_wildcard;
|
||||
BOOL exists; /* true if the base filename exists */
|
||||
BOOL stream_exists; /* true if the stream exists */
|
||||
struct stat st;
|
||||
struct pvfs_dos_fileinfo dos;
|
||||
};
|
||||
|
||||
|
||||
/* open file handle state - encapsulates the posix fd
|
||||
|
||||
Note that this is separated from the pvfs_file structure in order
|
||||
to cope with the openx DENY_DOS semantics where a 2nd DENY_DOS open
|
||||
on the same connection gets the same low level filesystem handle,
|
||||
rather than a new handle
|
||||
*/
|
||||
struct pvfs_file_handle {
|
||||
int fd;
|
||||
|
||||
struct pvfs_filename *name;
|
||||
|
||||
/* a unique file key to be used for open file locking */
|
||||
DATA_BLOB odb_locking_key;
|
||||
|
||||
uint32_t create_options;
|
||||
|
||||
/* this is set by the mode_information level. What does it do? */
|
||||
uint32_t mode;
|
||||
|
||||
/* yes, we need 2 independent positions ... */
|
||||
uint64_t seek_offset;
|
||||
uint64_t position;
|
||||
|
||||
BOOL have_opendb_entry;
|
||||
|
||||
/* we need this hook back to our parent for lock destruction */
|
||||
struct pvfs_state *pvfs;
|
||||
|
||||
/* have we set a sticky write time that we should remove on close */
|
||||
BOOL sticky_write_time;
|
||||
};
|
||||
|
||||
/* open file state */
|
||||
struct pvfs_file {
|
||||
struct pvfs_file *next, *prev;
|
||||
struct pvfs_file_handle *handle;
|
||||
struct ntvfs_handle *ntvfs;
|
||||
|
||||
struct pvfs_state *pvfs;
|
||||
|
||||
uint32_t impersonation;
|
||||
uint32_t share_access;
|
||||
uint32_t access_mask;
|
||||
|
||||
/* a list of pending locks - used for locking cancel operations */
|
||||
struct pvfs_pending_lock *pending_list;
|
||||
|
||||
/* a file handle to be used for byte range locking */
|
||||
struct brl_handle *brl_handle;
|
||||
|
||||
/* a count of active locks - used to avoid calling brl_close on
|
||||
file close */
|
||||
uint64_t lock_count;
|
||||
|
||||
/* for directories, a buffer of pending notify events */
|
||||
struct pvfs_notify_buffer *notify_buffer;
|
||||
|
||||
/* for directories, the state of an incomplete SMB2 Find */
|
||||
struct pvfs_search_state *search;
|
||||
};
|
||||
|
||||
/* the state of a search started with pvfs_search_first() */
|
||||
struct pvfs_search_state {
|
||||
struct pvfs_search_state *prev, *next;
|
||||
struct pvfs_state *pvfs;
|
||||
uint16_t handle;
|
||||
off_t current_index;
|
||||
uint16_t search_attrib;
|
||||
uint16_t must_attrib;
|
||||
struct pvfs_dir *dir;
|
||||
time_t last_used;
|
||||
uint_t num_ea_names;
|
||||
struct ea_name *ea_names;
|
||||
struct timed_event *te;
|
||||
};
|
||||
|
||||
/* flags to pvfs_resolve_name() */
|
||||
#define PVFS_RESOLVE_WILDCARD (1<<0)
|
||||
#define PVFS_RESOLVE_STREAMS (1<<1)
|
||||
|
||||
/* flags in pvfs->flags */
|
||||
#define PVFS_FLAG_CI_FILESYSTEM (1<<0) /* the filesystem is case insensitive */
|
||||
#define PVFS_FLAG_MAP_ARCHIVE (1<<1)
|
||||
#define PVFS_FLAG_MAP_SYSTEM (1<<2)
|
||||
#define PVFS_FLAG_MAP_HIDDEN (1<<3)
|
||||
#define PVFS_FLAG_READONLY (1<<4)
|
||||
#define PVFS_FLAG_STRICT_SYNC (1<<5)
|
||||
#define PVFS_FLAG_STRICT_LOCKING (1<<6)
|
||||
#define PVFS_FLAG_XATTR_ENABLE (1<<7)
|
||||
#define PVFS_FLAG_FAKE_OPLOCKS (1<<8)
|
||||
|
||||
/* forward declare some anonymous structures */
|
||||
struct pvfs_dir;
|
||||
|
||||
/* types of notification for pvfs wait events */
|
||||
enum pvfs_wait_notice {PVFS_WAIT_EVENT, PVFS_WAIT_TIMEOUT, PVFS_WAIT_CANCEL};
|
||||
|
||||
#define PVFS_EADB "posix:eadb"
|
||||
#define PVFS_XATTR "posix:xattr"
|
||||
#define PVFS_FAKE_OPLOCKS "posix:fakeoplocks"
|
||||
#define PVFS_SHARE_DELAY "posix:sharedelay"
|
||||
#define PVFS_ALLOCATION_ROUNDING "posix:allocationrounding"
|
||||
#define PVFS_SEARCH_INACTIVITY "posix:searchinactivity"
|
||||
#define PVFS_ACL "posix:acl"
|
||||
|
||||
#define PVFS_XATTR_DEFAULT True
|
||||
#define PVFS_FAKE_OPLOCKS_DEFAULT False
|
||||
#define PVFS_SHARE_DELAY_DEFAULT 1000000
|
||||
#define PVFS_ALLOCATION_ROUNDING_DEFAULT 512
|
||||
#define PVFS_SEARCH_INACTIVITY_DEFAULT 300
|
||||
|
||||
struct pvfs_acl_ops {
|
||||
const char *name;
|
||||
NTSTATUS (*acl_load)(struct pvfs_state *, struct pvfs_filename *, int , TALLOC_CTX *,
|
||||
struct security_descriptor **);
|
||||
NTSTATUS (*acl_save)(struct pvfs_state *, struct pvfs_filename *, int , struct security_descriptor *);
|
||||
};
|
||||
|
||||
#include "ntvfs/posix/vfs_posix_proto.h"
|
||||
|
||||
#endif /* _VFS_POSIX_H_ */
|
||||
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
POSIX NTVFS backend - xattr support using filesystem xattrs
|
||||
|
||||
Copyright (C) Andrew Tridgell 2004
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 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.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "vfs_posix.h"
|
||||
#include "lib/util/wrap_xattr.h"
|
||||
|
||||
/*
|
||||
pull a xattr as a blob, from either a file or a file descriptor
|
||||
*/
|
||||
NTSTATUS pull_xattr_blob_system(struct pvfs_state *pvfs,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const char *attr_name,
|
||||
const char *fname,
|
||||
int fd,
|
||||
size_t estimated_size,
|
||||
DATA_BLOB *blob)
|
||||
{
|
||||
int ret;
|
||||
|
||||
*blob = data_blob_talloc(mem_ctx, NULL, estimated_size+16);
|
||||
if (blob->data == NULL) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
again:
|
||||
if (fd != -1) {
|
||||
ret = wrap_fgetxattr(fd, attr_name, blob->data, estimated_size);
|
||||
} else {
|
||||
ret = wrap_getxattr(fname, attr_name, blob->data, estimated_size);
|
||||
}
|
||||
if (ret == -1 && errno == ERANGE) {
|
||||
estimated_size *= 2;
|
||||
blob->data = talloc_realloc(mem_ctx, blob->data,
|
||||
uint8_t, estimated_size);
|
||||
if (blob->data == NULL) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
blob->length = estimated_size;
|
||||
goto again;
|
||||
}
|
||||
if (ret == -1 && errno == EPERM) {
|
||||
struct stat statbuf;
|
||||
|
||||
if (fd != -1) {
|
||||
ret = fstat(fd, &statbuf);
|
||||
} else {
|
||||
ret = stat(fname, &statbuf);
|
||||
}
|
||||
if (ret == 0) {
|
||||
/* check if this is a directory and the sticky bit is set */
|
||||
if (S_ISDIR(statbuf.st_mode) && (statbuf.st_mode & S_ISVTX)) {
|
||||
/* pretend we could not find the xattr */
|
||||
|
||||
data_blob_free(blob);
|
||||
return NT_STATUS_NOT_FOUND;
|
||||
|
||||
} else {
|
||||
/* if not this was probably a legittimate error
|
||||
* reset ret and errno to the correct values */
|
||||
errno = EPERM;
|
||||
ret = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ret == -1) {
|
||||
data_blob_free(blob);
|
||||
return pvfs_map_errno(pvfs, errno);
|
||||
}
|
||||
|
||||
blob->length = ret;
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
push a xattr as a blob, from either a file or a file descriptor
|
||||
*/
|
||||
NTSTATUS push_xattr_blob_system(struct pvfs_state *pvfs,
|
||||
const char *attr_name,
|
||||
const char *fname,
|
||||
int fd,
|
||||
const DATA_BLOB *blob)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (fd != -1) {
|
||||
ret = wrap_fsetxattr(fd, attr_name, blob->data, blob->length, 0);
|
||||
} else {
|
||||
ret = wrap_setxattr(fname, attr_name, blob->data, blob->length, 0);
|
||||
}
|
||||
if (ret == -1) {
|
||||
return pvfs_map_errno(pvfs, errno);
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
delete a xattr
|
||||
*/
|
||||
NTSTATUS delete_xattr_system(struct pvfs_state *pvfs, const char *attr_name,
|
||||
const char *fname, int fd)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (fd != -1) {
|
||||
ret = wrap_fremovexattr(fd, attr_name);
|
||||
} else {
|
||||
ret = wrap_removexattr(fname, attr_name);
|
||||
}
|
||||
if (ret == -1) {
|
||||
return pvfs_map_errno(pvfs, errno);
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
unlink a file - cleanup any xattrs
|
||||
*/
|
||||
NTSTATUS unlink_xattr_system(struct pvfs_state *pvfs, const char *fname)
|
||||
{
|
||||
/* nothing needs to be done for filesystem based xattrs */
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
@@ -0,0 +1,233 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
POSIX NTVFS backend - xattr support using a tdb
|
||||
|
||||
Copyright (C) Andrew Tridgell 2004
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 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.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "vfs_posix.h"
|
||||
#include "lib/tdb/include/tdb.h"
|
||||
#include "db_wrap.h"
|
||||
|
||||
#define XATTR_LIST_ATTR ".xattr_list"
|
||||
|
||||
/*
|
||||
we need to maintain a list of attributes on each file, so that unlink
|
||||
can automatically clean them up
|
||||
*/
|
||||
static NTSTATUS xattr_tdb_add_list(struct pvfs_state *pvfs, const char *attr_name,
|
||||
const char *fname, int fd)
|
||||
{
|
||||
DATA_BLOB blob;
|
||||
TALLOC_CTX *mem_ctx;
|
||||
const char *s;
|
||||
NTSTATUS status;
|
||||
size_t len;
|
||||
|
||||
if (strcmp(attr_name, XATTR_LIST_ATTR) == 0) {
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
mem_ctx = talloc_new(pvfs);
|
||||
|
||||
status = pull_xattr_blob_tdb(pvfs, mem_ctx, XATTR_LIST_ATTR,
|
||||
fname, fd, 100, &blob);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
blob = data_blob(NULL, 0);
|
||||
}
|
||||
|
||||
for (s=(const char *)blob.data; s < (const char *)(blob.data+blob.length); s += strlen(s) + 1) {
|
||||
if (strcmp(attr_name, s) == 0) {
|
||||
talloc_free(mem_ctx);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
len = strlen(attr_name) + 1;
|
||||
|
||||
blob.data = talloc_realloc(mem_ctx, blob.data, uint8_t, blob.length + len);
|
||||
if (blob.data == NULL) {
|
||||
talloc_free(mem_ctx);
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
memcpy(blob.data + blob.length, attr_name, len);
|
||||
blob.length += len;
|
||||
|
||||
status = push_xattr_blob_tdb(pvfs, XATTR_LIST_ATTR, fname, fd, &blob);
|
||||
talloc_free(mem_ctx);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
form a key for using in the ea_db
|
||||
*/
|
||||
static NTSTATUS get_ea_db_key(TALLOC_CTX *mem_ctx,
|
||||
const char *attr_name,
|
||||
const char *fname, int fd,
|
||||
TDB_DATA *key)
|
||||
{
|
||||
struct stat st;
|
||||
size_t len = strlen(attr_name);
|
||||
|
||||
if (fd == -1) {
|
||||
if (stat(fname, &st) == -1) {
|
||||
return NT_STATUS_NOT_FOUND;
|
||||
}
|
||||
} else {
|
||||
if (fstat(fd, &st) == -1) {
|
||||
return NT_STATUS_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
|
||||
key->dptr = talloc_array(mem_ctx, uint8_t, 16 + len);
|
||||
if (key->dptr == NULL) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
key->dsize = 16 + len;
|
||||
|
||||
SBVAL(key->dptr, 0, st.st_dev);
|
||||
SBVAL(key->dptr, 8, st.st_ino);
|
||||
memcpy(key->dptr+16, attr_name, len);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
pull a xattr as a blob, using the ea_db tdb
|
||||
*/
|
||||
NTSTATUS pull_xattr_blob_tdb(struct pvfs_state *pvfs,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
const char *attr_name,
|
||||
const char *fname,
|
||||
int fd,
|
||||
size_t estimated_size,
|
||||
DATA_BLOB *blob)
|
||||
{
|
||||
TDB_DATA tkey, tdata;
|
||||
NTSTATUS status;
|
||||
|
||||
status = get_ea_db_key(mem_ctx, attr_name, fname, fd, &tkey);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
tdata = tdb_fetch(pvfs->ea_db->tdb, tkey);
|
||||
if (tdata.dptr == NULL) {
|
||||
return NT_STATUS_NOT_FOUND;
|
||||
}
|
||||
|
||||
*blob = data_blob_talloc(mem_ctx, tdata.dptr, tdata.dsize);
|
||||
free(tdata.dptr);
|
||||
if (blob->data == NULL) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
push a xattr as a blob, using ea_db
|
||||
*/
|
||||
NTSTATUS push_xattr_blob_tdb(struct pvfs_state *pvfs,
|
||||
const char *attr_name,
|
||||
const char *fname,
|
||||
int fd,
|
||||
const DATA_BLOB *blob)
|
||||
{
|
||||
TDB_DATA tkey, tdata;
|
||||
NTSTATUS status;
|
||||
|
||||
status = get_ea_db_key(pvfs, attr_name, fname, fd, &tkey);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
tdata.dptr = blob->data;
|
||||
tdata.dsize = blob->length;
|
||||
|
||||
if (tdb_chainlock(pvfs->ea_db->tdb, tkey) != 0) {
|
||||
talloc_free(tkey.dptr);
|
||||
return NT_STATUS_INTERNAL_DB_CORRUPTION;
|
||||
}
|
||||
|
||||
status = xattr_tdb_add_list(pvfs, attr_name, fname, fd);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (tdb_store(pvfs->ea_db->tdb, tkey, tdata, TDB_REPLACE) == -1) {
|
||||
status = NT_STATUS_INTERNAL_DB_CORRUPTION;
|
||||
}
|
||||
|
||||
done:
|
||||
tdb_chainunlock(pvfs->ea_db->tdb, tkey);
|
||||
talloc_free(tkey.dptr);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
delete a xattr
|
||||
*/
|
||||
NTSTATUS delete_xattr_tdb(struct pvfs_state *pvfs, const char *attr_name,
|
||||
const char *fname, int fd)
|
||||
{
|
||||
TDB_DATA tkey;
|
||||
NTSTATUS status;
|
||||
|
||||
status = get_ea_db_key(NULL, attr_name, fname, fd, &tkey);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
if (tdb_delete(pvfs->ea_db->tdb, tkey) == -1) {
|
||||
talloc_free(tkey.dptr);
|
||||
return NT_STATUS_INTERNAL_DB_CORRUPTION;
|
||||
}
|
||||
|
||||
talloc_free(tkey.dptr);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
delete all xattrs for a file
|
||||
*/
|
||||
NTSTATUS unlink_xattr_tdb(struct pvfs_state *pvfs, const char *fname)
|
||||
{
|
||||
TALLOC_CTX *mem_ctx = talloc_new(pvfs);
|
||||
DATA_BLOB blob;
|
||||
const char *s;
|
||||
NTSTATUS status;
|
||||
|
||||
status = pull_xattr_blob_tdb(pvfs, mem_ctx, XATTR_LIST_ATTR,
|
||||
fname, -1, 100, &blob);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
talloc_free(mem_ctx);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
for (s=(const char *)blob.data; s < (const char *)(blob.data+blob.length); s += strlen(s) + 1) {
|
||||
delete_xattr_tdb(pvfs, s, fname, -1);
|
||||
}
|
||||
|
||||
return delete_xattr_tdb(pvfs, XATTR_LIST_ATTR, fname, -1);
|
||||
}
|
||||
@@ -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.
|
||||
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
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"
|
||||
#include "libcli/raw/ioctl.h"
|
||||
#include "ntvfs/ntvfs.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 ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, const char *sharename)
|
||||
{
|
||||
ntvfs->ctx->fs_type = talloc_strdup(ntvfs->ctx, "NTFS");
|
||||
NT_STATUS_HAVE_NO_MEMORY(ntvfs->ctx->fs_type);
|
||||
|
||||
ntvfs->ctx->dev_type = talloc_strdup(ntvfs->ctx, "LPT1:");
|
||||
NT_STATUS_HAVE_NO_MEMORY(ntvfs->ctx->dev_type);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
disconnect from a share
|
||||
*/
|
||||
static NTSTATUS print_disconnect(struct ntvfs_module_context *ntvfs)
|
||||
{
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
lots of operations are not allowed on printing shares - mostly return NT_STATUS_ACCESS_DENIED
|
||||
*/
|
||||
static NTSTATUS print_unlink(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
union smb_unlink *unl)
|
||||
{
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
ioctl - used for job query
|
||||
*/
|
||||
static NTSTATUS print_ioctl(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_ioctl *io)
|
||||
{
|
||||
char *p;
|
||||
|
||||
if (io->generic.level != RAW_IOCTL_IOCTL) {
|
||||
return NT_STATUS_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
if (io->ioctl.in.request == IOCTL_QUERY_JOB_INFO) {
|
||||
|
||||
/* a request for the print job id of an open print job */
|
||||
io->ioctl.out.blob = data_blob_talloc(req, NULL, 32);
|
||||
|
||||
data_blob_clear(&io->ioctl.out.blob);
|
||||
|
||||
p = (char *)io->ioctl.out.blob.data;
|
||||
SSVAL(p,0, 1 /* REWRITE: fsp->rap_print_jobid */);
|
||||
push_string(p+2, lp_netbios_name(), 15, STR_TERMINATE|STR_ASCII);
|
||||
push_string(p+18, ntvfs->ctx->config->name, 13, STR_TERMINATE|STR_ASCII);
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
initialialise the print backend, registering ourselves with the ntvfs subsystem
|
||||
*/
|
||||
NTSTATUS ntvfs_print_init(void)
|
||||
{
|
||||
NTSTATUS ret;
|
||||
struct ntvfs_ops ops;
|
||||
NTVFS_CURRENT_CRITICAL_SIZES(vers);
|
||||
|
||||
ZERO_STRUCT(ops);
|
||||
|
||||
/* fill in the name and type */
|
||||
ops.name = "default";
|
||||
ops.type = NTVFS_PRINT;
|
||||
|
||||
/* 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(&ops, &vers);
|
||||
|
||||
if (!NT_STATUS_IS_OK(ret)) {
|
||||
DEBUG(0,("Failed to register PRINT backend!\n"));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
This module (ntvfs 'simple') provides a very, very simple posix backend.
|
||||
|
||||
WARNING: All file access is done as user 'root'!!!
|
||||
Only use this module for testing, with only test data!!!
|
||||
|
||||
For activating this module use:
|
||||
|
||||
[testshare]
|
||||
path = /tmp/testshare
|
||||
nfvfs handler = simple
|
||||
@@ -0,0 +1,38 @@
|
||||
|
||||
struct svfs_private {
|
||||
struct ntvfs_module_context *ntvfs;
|
||||
|
||||
/* the base directory */
|
||||
char *connectpath;
|
||||
|
||||
/* a linked list of open searches */
|
||||
struct search_state *search;
|
||||
|
||||
/* next available search handle */
|
||||
uint16_t next_search_handle;
|
||||
|
||||
struct svfs_file *open_files;
|
||||
};
|
||||
|
||||
struct svfs_dir {
|
||||
unsigned int count;
|
||||
char *unix_dir;
|
||||
struct svfs_dirfile {
|
||||
char *name;
|
||||
struct stat st;
|
||||
} *files;
|
||||
};
|
||||
|
||||
struct svfs_file {
|
||||
struct svfs_file *next, *prev;
|
||||
int fd;
|
||||
struct ntvfs_handle *handle;
|
||||
char *name;
|
||||
};
|
||||
|
||||
struct search_state {
|
||||
struct search_state *next, *prev;
|
||||
uint16_t handle;
|
||||
unsigned int current_index;
|
||||
struct svfs_dir *dir;
|
||||
};
|
||||
@@ -0,0 +1,185 @@
|
||||
/*
|
||||
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 "system/filesys.h"
|
||||
#include "svfs.h"
|
||||
#include "system/time.h"
|
||||
#include "system/dir.h"
|
||||
#include "ntvfs/ntvfs.h"
|
||||
|
||||
/*
|
||||
convert a windows path to a unix path - don't do any manging or case sensitive handling
|
||||
*/
|
||||
char *svfs_unix_path(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, const char *name)
|
||||
{
|
||||
struct svfs_private *private = ntvfs->private_data;
|
||||
char *ret;
|
||||
|
||||
if (*name != '\\') {
|
||||
ret = talloc_asprintf(req, "%s/%s", private->connectpath, name);
|
||||
} else {
|
||||
ret = talloc_asprintf(req, "%s%s", private->connectpath, name);
|
||||
}
|
||||
all_string_sub(ret, "\\", "/", 0);
|
||||
|
||||
strlower(ret + strlen(private->connectpath));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
read a directory and find all matching file names and stat info
|
||||
returned names are separate unix and DOS names. The returned names
|
||||
are relative to the directory
|
||||
*/
|
||||
struct svfs_dir *svfs_list_unix(TALLOC_CTX *mem_ctx, struct ntvfs_request *req, const char *unix_path)
|
||||
{
|
||||
char *p, *mask;
|
||||
struct svfs_dir *dir;
|
||||
DIR *odir;
|
||||
struct dirent *dent;
|
||||
uint_t allocated = 0;
|
||||
char *low_mask;
|
||||
|
||||
dir = talloc(mem_ctx, struct svfs_dir);
|
||||
if (!dir) { return NULL; }
|
||||
|
||||
dir->count = 0;
|
||||
dir->files = 0;
|
||||
|
||||
/* find the base directory */
|
||||
p = strrchr(unix_path, '/');
|
||||
if (!p) { return NULL; }
|
||||
|
||||
dir->unix_dir = talloc_strndup(mem_ctx, unix_path, PTR_DIFF(p, unix_path));
|
||||
if (!dir->unix_dir) { return NULL; }
|
||||
|
||||
/* the wildcard pattern is the last part */
|
||||
mask = p+1;
|
||||
|
||||
low_mask = 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;
|
||||
|
||||
if (strchr(dent->d_name, ':') && !strchr(unix_path, ':')) {
|
||||
/* don't show streams in dir listing */
|
||||
continue;
|
||||
}
|
||||
|
||||
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(dir, dir->files, struct svfs_dirfile, allocated);
|
||||
if (!dir->files) {
|
||||
closedir(odir);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
dir->files[i].name = low_name;
|
||||
if (!dir->files[i].name) { continue; }
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
read a directory and find all matching file names and stat info
|
||||
returned names are separate unix and DOS names. The returned names
|
||||
are relative to the directory
|
||||
*/
|
||||
struct svfs_dir *svfs_list(struct ntvfs_module_context *ntvfs, struct ntvfs_request *req, const char *pattern)
|
||||
{
|
||||
struct svfs_private *private = ntvfs->private_data;
|
||||
char *unix_path;
|
||||
|
||||
unix_path = svfs_unix_path(ntvfs, req, pattern);
|
||||
if (!unix_path) { return NULL; }
|
||||
|
||||
return svfs_list_unix(private, req, unix_path);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************
|
||||
set the time on a file via file descriptor
|
||||
*******************************************************************/
|
||||
int svfs_file_utime(int fd, struct utimbuf *times)
|
||||
{
|
||||
char *fd_path = NULL;
|
||||
int ret;
|
||||
|
||||
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_t svfs_unix_to_dos_attrib(mode_t mode)
|
||||
{
|
||||
uint16_t ret = 0;
|
||||
if (S_ISDIR(mode)) ret |= FILE_ATTRIBUTE_DIRECTORY;
|
||||
if (!(mode & S_IWUSR)) ret |= FILE_ATTRIBUTE_READONLY;
|
||||
return ret;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,5 @@
|
||||
This directory contains OS depdendent interfaces to facilities that
|
||||
are only available on a few of our target systems, and require
|
||||
substantial code to abstract.
|
||||
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
AC_CHECK_HEADERS(linux/inotify.h asm/unistd.h sys/inotify.h)
|
||||
AC_CHECK_FUNC(inotify_init)
|
||||
AC_HAVE_DECL(__NR_inotify_init, [#include <asm/unistd.h>])
|
||||
|
||||
SMB_ENABLE(sys_notify_inotify, NO)
|
||||
|
||||
if test x"$ac_cv_func_inotify_init" = x"yes" -a x"$ac_cv_header_sys_inotify_h" = x"yes"; then
|
||||
SMB_ENABLE(sys_notify_inotify, YES)
|
||||
fi
|
||||
|
||||
if test x"$ac_cv_func_inotify_init" = x"yes" -a x"$ac_cv_header_linux_inotify_h" = x"yes"; then
|
||||
SMB_ENABLE(sys_notify_inotify, YES)
|
||||
fi
|
||||
|
||||
if test x"$ac_cv_header_linux_inotify_h" = x"yes" -a x"$ac_cv_have___NR_inotify_init_decl" = x"yes"; then
|
||||
SMB_ENABLE(sys_notify_inotify, YES)
|
||||
fi
|
||||
@@ -0,0 +1,18 @@
|
||||
################################################
|
||||
# Start MODULE sys_notify_inotify
|
||||
[MODULE::sys_notify_inotify]
|
||||
SUBSYSTEM = sys_notify
|
||||
INIT_FUNCTION = sys_notify_inotify_init
|
||||
OBJ_FILES = \
|
||||
inotify.o
|
||||
# End MODULE sys_notify_inotify
|
||||
################################################
|
||||
|
||||
################################################
|
||||
# Start SUBSYSTEM sys_notify
|
||||
[SUBSYSTEM::sys_notify]
|
||||
OBJ_FILES = \
|
||||
sys_notify.o
|
||||
PUBLIC_DEPENDENCIES =
|
||||
# End SUBSYSTEM sys_notify
|
||||
################################################
|
||||
@@ -0,0 +1,419 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
Copyright (C) Andrew Tridgell 2006
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
/*
|
||||
notify implementation using inotify
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "system/filesys.h"
|
||||
#include "ntvfs/sysdep/sys_notify.h"
|
||||
#include "lib/events/events.h"
|
||||
#include "lib/util/dlinklist.h"
|
||||
#include "libcli/raw/smb.h"
|
||||
|
||||
#if HAVE_SYS_INOTIFY_H
|
||||
#include <sys/inotify.h>
|
||||
#else
|
||||
/* for older glibc varients - we can remove this eventually */
|
||||
#include <linux/inotify.h>
|
||||
#include <asm/unistd.h>
|
||||
|
||||
#ifndef HAVE_INOTIFY_INIT
|
||||
/*
|
||||
glibc doesn't define these functions yet (as of March 2006)
|
||||
*/
|
||||
static int inotify_init(void)
|
||||
{
|
||||
return syscall(__NR_inotify_init);
|
||||
}
|
||||
|
||||
static int inotify_add_watch(int fd, const char *path, __u32 mask)
|
||||
{
|
||||
return syscall(__NR_inotify_add_watch, fd, path, mask);
|
||||
}
|
||||
|
||||
static int inotify_rm_watch(int fd, int wd)
|
||||
{
|
||||
return syscall(__NR_inotify_rm_watch, fd, wd);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
/* older glibc headers don't have these defines either */
|
||||
#ifndef IN_ONLYDIR
|
||||
#define IN_ONLYDIR 0x01000000
|
||||
#endif
|
||||
#ifndef IN_MASK_ADD
|
||||
#define IN_MASK_ADD 0x20000000
|
||||
#endif
|
||||
|
||||
struct inotify_private {
|
||||
struct sys_notify_context *ctx;
|
||||
int fd;
|
||||
struct watch_context *watches;
|
||||
};
|
||||
|
||||
struct watch_context {
|
||||
struct watch_context *next, *prev;
|
||||
struct inotify_private *in;
|
||||
int wd;
|
||||
sys_notify_callback_t callback;
|
||||
void *private;
|
||||
uint32_t mask; /* the inotify mask */
|
||||
uint32_t filter; /* the windows completion filter */
|
||||
const char *path;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
destroy the inotify private context
|
||||
*/
|
||||
static int inotify_destructor(struct inotify_private *in)
|
||||
{
|
||||
close(in->fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
see if a particular event from inotify really does match a requested
|
||||
notify event in SMB
|
||||
*/
|
||||
static BOOL filter_match(struct watch_context *w, struct inotify_event *e)
|
||||
{
|
||||
if ((e->mask & w->mask) == 0) {
|
||||
/* this happens because inotify_add_watch() coalesces watches on the same
|
||||
path, oring their masks together */
|
||||
return False;
|
||||
}
|
||||
|
||||
/* SMB separates the filters for files and directories */
|
||||
if (e->mask & IN_ISDIR) {
|
||||
if ((w->filter & FILE_NOTIFY_CHANGE_DIR_NAME) == 0) {
|
||||
return False;
|
||||
}
|
||||
} else {
|
||||
if ((e->mask & IN_ATTRIB) &&
|
||||
(w->filter & (FILE_NOTIFY_CHANGE_ATTRIBUTES|
|
||||
FILE_NOTIFY_CHANGE_LAST_WRITE|
|
||||
FILE_NOTIFY_CHANGE_LAST_ACCESS|
|
||||
FILE_NOTIFY_CHANGE_EA|
|
||||
FILE_NOTIFY_CHANGE_SECURITY))) {
|
||||
return True;
|
||||
}
|
||||
if ((e->mask & IN_MODIFY) &&
|
||||
(w->filter & FILE_NOTIFY_CHANGE_ATTRIBUTES)) {
|
||||
return True;
|
||||
}
|
||||
if ((w->filter & FILE_NOTIFY_CHANGE_FILE_NAME) == 0) {
|
||||
return False;
|
||||
}
|
||||
}
|
||||
|
||||
return True;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
dispatch one inotify event
|
||||
|
||||
the cookies are used to correctly handle renames
|
||||
*/
|
||||
static void inotify_dispatch(struct inotify_private *in,
|
||||
struct inotify_event *e,
|
||||
uint32_t prev_cookie,
|
||||
struct inotify_event *e2)
|
||||
{
|
||||
struct watch_context *w, *next;
|
||||
struct notify_event ne;
|
||||
|
||||
/* ignore extraneous events, such as unmount and IN_IGNORED events */
|
||||
if ((e->mask & (IN_ATTRIB|IN_MODIFY|IN_CREATE|IN_DELETE|
|
||||
IN_MOVED_FROM|IN_MOVED_TO)) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* map the inotify mask to a action. This gets complicated for
|
||||
renames */
|
||||
if (e->mask & IN_CREATE) {
|
||||
ne.action = NOTIFY_ACTION_ADDED;
|
||||
} else if (e->mask & IN_DELETE) {
|
||||
ne.action = NOTIFY_ACTION_REMOVED;
|
||||
} else if (e->mask & IN_MOVED_FROM) {
|
||||
if (e2 != NULL && e2->cookie == e->cookie) {
|
||||
ne.action = NOTIFY_ACTION_OLD_NAME;
|
||||
} else {
|
||||
ne.action = NOTIFY_ACTION_REMOVED;
|
||||
}
|
||||
} else if (e->mask & IN_MOVED_TO) {
|
||||
if (e->cookie == prev_cookie) {
|
||||
ne.action = NOTIFY_ACTION_NEW_NAME;
|
||||
} else {
|
||||
ne.action = NOTIFY_ACTION_ADDED;
|
||||
}
|
||||
} else {
|
||||
ne.action = NOTIFY_ACTION_MODIFIED;
|
||||
}
|
||||
ne.path = e->name;
|
||||
|
||||
/* find any watches that have this watch descriptor */
|
||||
for (w=in->watches;w;w=next) {
|
||||
next = w->next;
|
||||
if (w->wd == e->wd && filter_match(w, e)) {
|
||||
w->callback(in->ctx, w->private, &ne);
|
||||
}
|
||||
}
|
||||
|
||||
/* SMB expects a file rename to generate three events, two for
|
||||
the rename and the other for a modify of the
|
||||
destination. Strange! */
|
||||
if (ne.action != NOTIFY_ACTION_NEW_NAME ||
|
||||
(e->mask & IN_ISDIR) != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
ne.action = NOTIFY_ACTION_MODIFIED;
|
||||
e->mask = IN_ATTRIB;
|
||||
|
||||
for (w=in->watches;w;w=next) {
|
||||
next = w->next;
|
||||
if (w->wd == e->wd && filter_match(w, e) &&
|
||||
!(w->filter & FILE_NOTIFY_CHANGE_CREATION)) {
|
||||
w->callback(in->ctx, w->private, &ne);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
called when the kernel has some events for us
|
||||
*/
|
||||
static void inotify_handler(struct event_context *ev, struct fd_event *fde,
|
||||
uint16_t flags, void *private)
|
||||
{
|
||||
struct inotify_private *in = talloc_get_type(private, struct inotify_private);
|
||||
int bufsize = 0;
|
||||
struct inotify_event *e0, *e;
|
||||
uint32_t prev_cookie=0;
|
||||
|
||||
/*
|
||||
we must use FIONREAD as we cannot predict the length of the
|
||||
filenames, and thus can't know how much to allocate
|
||||
otherwise
|
||||
*/
|
||||
if (ioctl(in->fd, FIONREAD, &bufsize) != 0 ||
|
||||
bufsize == 0) {
|
||||
DEBUG(0,("No data on inotify fd?!\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
e0 = e = talloc_size(in, bufsize);
|
||||
if (e == NULL) return;
|
||||
|
||||
if (read(in->fd, e0, bufsize) != bufsize) {
|
||||
DEBUG(0,("Failed to read all inotify data\n"));
|
||||
talloc_free(e0);
|
||||
return;
|
||||
}
|
||||
|
||||
/* we can get more than one event in the buffer */
|
||||
while (bufsize >= sizeof(*e)) {
|
||||
struct inotify_event *e2 = NULL;
|
||||
bufsize -= e->len + sizeof(*e);
|
||||
if (bufsize >= sizeof(*e)) {
|
||||
e2 = (struct inotify_event *)(e->len + sizeof(*e) + (char *)e);
|
||||
}
|
||||
inotify_dispatch(in, e, prev_cookie, e2);
|
||||
prev_cookie = e->cookie;
|
||||
e = e2;
|
||||
}
|
||||
|
||||
talloc_free(e0);
|
||||
}
|
||||
|
||||
/*
|
||||
setup the inotify handle - called the first time a watch is added on
|
||||
this context
|
||||
*/
|
||||
static NTSTATUS inotify_setup(struct sys_notify_context *ctx)
|
||||
{
|
||||
struct inotify_private *in;
|
||||
|
||||
if (!lp_parm_bool(-1, "notify", "inotify", True)) {
|
||||
return NT_STATUS_INVALID_SYSTEM_SERVICE;
|
||||
}
|
||||
|
||||
in = talloc(ctx, struct inotify_private);
|
||||
NT_STATUS_HAVE_NO_MEMORY(in);
|
||||
in->fd = inotify_init();
|
||||
if (in->fd == -1) {
|
||||
DEBUG(0,("Failed to init inotify - %s\n", strerror(errno)));
|
||||
talloc_free(in);
|
||||
return map_nt_error_from_unix(errno);
|
||||
}
|
||||
in->ctx = ctx;
|
||||
in->watches = NULL;
|
||||
|
||||
ctx->private = in;
|
||||
talloc_set_destructor(in, inotify_destructor);
|
||||
|
||||
/* add a event waiting for the inotify fd to be readable */
|
||||
event_add_fd(ctx->ev, in, in->fd, EVENT_FD_READ, inotify_handler, in);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
map from a change notify mask to a inotify mask. Remove any bits
|
||||
which we can handle
|
||||
*/
|
||||
static const struct {
|
||||
uint32_t notify_mask;
|
||||
uint32_t inotify_mask;
|
||||
} inotify_mapping[] = {
|
||||
{FILE_NOTIFY_CHANGE_FILE_NAME, IN_CREATE|IN_DELETE|IN_MOVED_FROM|IN_MOVED_TO},
|
||||
{FILE_NOTIFY_CHANGE_DIR_NAME, IN_CREATE|IN_DELETE|IN_MOVED_FROM|IN_MOVED_TO},
|
||||
{FILE_NOTIFY_CHANGE_ATTRIBUTES, IN_ATTRIB|IN_MOVED_TO|IN_MOVED_FROM|IN_MODIFY},
|
||||
{FILE_NOTIFY_CHANGE_LAST_WRITE, IN_ATTRIB},
|
||||
{FILE_NOTIFY_CHANGE_LAST_ACCESS, IN_ATTRIB},
|
||||
{FILE_NOTIFY_CHANGE_EA, IN_ATTRIB},
|
||||
{FILE_NOTIFY_CHANGE_SECURITY, IN_ATTRIB}
|
||||
};
|
||||
|
||||
static uint32_t inotify_map(struct notify_entry *e)
|
||||
{
|
||||
int i;
|
||||
uint32_t out=0;
|
||||
for (i=0;i<ARRAY_SIZE(inotify_mapping);i++) {
|
||||
if (inotify_mapping[i].notify_mask & e->filter) {
|
||||
out |= inotify_mapping[i].inotify_mask;
|
||||
e->filter &= ~inotify_mapping[i].notify_mask;
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/*
|
||||
destroy a watch
|
||||
*/
|
||||
static int watch_destructor(struct watch_context *w)
|
||||
{
|
||||
struct inotify_private *in = w->in;
|
||||
int wd = w->wd;
|
||||
DLIST_REMOVE(w->in->watches, w);
|
||||
|
||||
/* only rm the watch if its the last one with this wd */
|
||||
for (w=in->watches;w;w=w->next) {
|
||||
if (w->wd == wd) break;
|
||||
}
|
||||
if (w == NULL) {
|
||||
inotify_rm_watch(in->fd, wd);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
add a watch. The watch is removed when the caller calls
|
||||
talloc_free() on *handle
|
||||
*/
|
||||
static NTSTATUS inotify_watch(struct sys_notify_context *ctx, struct notify_entry *e,
|
||||
sys_notify_callback_t callback, void *private,
|
||||
void **handle)
|
||||
{
|
||||
struct inotify_private *in;
|
||||
int wd;
|
||||
uint32_t mask;
|
||||
struct watch_context *w;
|
||||
uint32_t filter = e->filter;
|
||||
|
||||
/* maybe setup the inotify fd */
|
||||
if (ctx->private == NULL) {
|
||||
NTSTATUS status;
|
||||
status = inotify_setup(ctx);
|
||||
NT_STATUS_NOT_OK_RETURN(status);
|
||||
}
|
||||
|
||||
in = talloc_get_type(ctx->private, struct inotify_private);
|
||||
|
||||
mask = inotify_map(e);
|
||||
if (mask == 0) {
|
||||
/* this filter can't be handled by inotify */
|
||||
return NT_STATUS_INVALID_PARAMETER;
|
||||
}
|
||||
|
||||
/* using IN_MASK_ADD allows us to cope with inotify() returning the same
|
||||
watch descriptor for muliple watches on the same path */
|
||||
mask |= (IN_MASK_ADD | IN_ONLYDIR);
|
||||
|
||||
/* get a new watch descriptor for this path */
|
||||
wd = inotify_add_watch(in->fd, e->path, mask);
|
||||
if (wd == -1) {
|
||||
e->filter = filter;
|
||||
return map_nt_error_from_unix(errno);
|
||||
}
|
||||
|
||||
w = talloc(in, struct watch_context);
|
||||
if (w == NULL) {
|
||||
inotify_rm_watch(in->fd, wd);
|
||||
e->filter = filter;
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
w->in = in;
|
||||
w->wd = wd;
|
||||
w->callback = callback;
|
||||
w->private = private;
|
||||
w->mask = mask;
|
||||
w->filter = filter;
|
||||
w->path = talloc_strdup(w, e->path);
|
||||
if (w->path == NULL) {
|
||||
inotify_rm_watch(in->fd, wd);
|
||||
e->filter = filter;
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
(*handle) = w;
|
||||
|
||||
DLIST_ADD(in->watches, w);
|
||||
|
||||
/* the caller frees the handle to stop watching */
|
||||
talloc_set_destructor(w, watch_destructor);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
|
||||
static struct sys_notify_backend inotify = {
|
||||
.name = "inotify",
|
||||
.notify_watch = inotify_watch
|
||||
};
|
||||
|
||||
/*
|
||||
initialialise the inotify module
|
||||
*/
|
||||
NTSTATUS sys_notify_inotify_init(void)
|
||||
{
|
||||
/* register ourselves as a system inotify module */
|
||||
return sys_notify_register(&inotify);
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
Copyright (C) Andrew Tridgell 2006
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
/*
|
||||
abstract the various kernel interfaces to change notify into a
|
||||
single Samba friendly interface
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "system/filesys.h"
|
||||
#include "ntvfs/sysdep/sys_notify.h"
|
||||
#include "lib/events/events.h"
|
||||
#include "lib/util/dlinklist.h"
|
||||
#include "build.h"
|
||||
|
||||
/* list of registered backends */
|
||||
static struct sys_notify_backend *backends;
|
||||
static uint32_t num_backends;
|
||||
|
||||
#define NOTIFY_BACKEND "notify-backend"
|
||||
|
||||
/*
|
||||
initialise a system change notify backend
|
||||
*/
|
||||
_PUBLIC_ struct sys_notify_context *sys_notify_context_create(struct share_config *scfg,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
struct event_context *ev)
|
||||
{
|
||||
struct sys_notify_context *ctx;
|
||||
const char *bname;
|
||||
int i;
|
||||
|
||||
if (num_backends == 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (ev == NULL) {
|
||||
ev = event_context_find(mem_ctx);
|
||||
}
|
||||
|
||||
ctx = talloc_zero(mem_ctx, struct sys_notify_context);
|
||||
if (ctx == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ctx->ev = ev;
|
||||
|
||||
bname = share_string_option(scfg, NOTIFY_BACKEND, NULL);
|
||||
if (!bname) {
|
||||
if (num_backends) {
|
||||
bname = backends[0].name;
|
||||
} else {
|
||||
bname = "__unknown__";
|
||||
}
|
||||
}
|
||||
|
||||
for (i=0;i<num_backends;i++) {
|
||||
if (strcasecmp(backends[i].name, bname) == 0) {
|
||||
bname = backends[i].name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ctx->name = bname;
|
||||
ctx->notify_watch = NULL;
|
||||
|
||||
if (i < num_backends) {
|
||||
ctx->notify_watch = backends[i].notify_watch;
|
||||
}
|
||||
|
||||
return ctx;
|
||||
}
|
||||
|
||||
/*
|
||||
add a watch
|
||||
|
||||
note that this call must modify the e->filter and e->subdir_filter
|
||||
bits to remove ones handled by this backend. Any remaining bits will
|
||||
be handled by the generic notify layer
|
||||
*/
|
||||
_PUBLIC_ NTSTATUS sys_notify_watch(struct sys_notify_context *ctx, struct notify_entry *e,
|
||||
sys_notify_callback_t callback, void *private, void **handle)
|
||||
{
|
||||
if (!ctx->notify_watch) {
|
||||
return NT_STATUS_INVALID_SYSTEM_SERVICE;
|
||||
}
|
||||
return ctx->notify_watch(ctx, e, callback, private, handle);
|
||||
}
|
||||
|
||||
/*
|
||||
register a notify backend
|
||||
*/
|
||||
_PUBLIC_ NTSTATUS sys_notify_register(struct sys_notify_backend *backend)
|
||||
{
|
||||
struct sys_notify_backend *b;
|
||||
b = talloc_realloc(talloc_autofree_context(), backends,
|
||||
struct sys_notify_backend, num_backends+1);
|
||||
NT_STATUS_HAVE_NO_MEMORY(b);
|
||||
backends = b;
|
||||
backends[num_backends] = *backend;
|
||||
num_backends++;
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
_PUBLIC_ NTSTATUS sys_notify_init(void)
|
||||
{
|
||||
static BOOL initialized = False;
|
||||
|
||||
init_module_fn static_init[] = STATIC_sys_notify_MODULES;
|
||||
init_module_fn *shared_init;
|
||||
|
||||
if (initialized) return NT_STATUS_OK;
|
||||
initialized = True;
|
||||
|
||||
shared_init = load_samba_modules(NULL, "sys_notify");
|
||||
|
||||
run_init_functions(static_init);
|
||||
run_init_functions(shared_init);
|
||||
|
||||
talloc_free(shared_init);
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
Copyright (C) Andrew Tridgell 2006
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*/
|
||||
|
||||
#include "librpc/gen_ndr/notify.h"
|
||||
#include "param/share.h"
|
||||
|
||||
struct sys_notify_context;
|
||||
|
||||
typedef void (*sys_notify_callback_t)(struct sys_notify_context *,
|
||||
void *, struct notify_event *ev);
|
||||
|
||||
typedef NTSTATUS (*notify_watch_t)(struct sys_notify_context *ctx,
|
||||
struct notify_entry *e,
|
||||
sys_notify_callback_t callback, void *private,
|
||||
void **handle);
|
||||
|
||||
struct sys_notify_context {
|
||||
struct event_context *ev;
|
||||
void *private; /* for use of backend */
|
||||
const char *name;
|
||||
notify_watch_t notify_watch;
|
||||
};
|
||||
|
||||
struct sys_notify_backend {
|
||||
const char *name;
|
||||
notify_watch_t notify_watch;
|
||||
};
|
||||
|
||||
NTSTATUS sys_notify_register(struct sys_notify_backend *backend);
|
||||
struct sys_notify_context *sys_notify_context_create(struct share_config *scfg,
|
||||
TALLOC_CTX *mem_ctx,
|
||||
struct event_context *ev);
|
||||
NTSTATUS sys_notify_watch(struct sys_notify_context *ctx, struct notify_entry *e,
|
||||
sys_notify_callback_t callback, void *private,
|
||||
void **handle);
|
||||
NTSTATUS sys_notify_init(void);
|
||||
@@ -0,0 +1 @@
|
||||
AC_CHECK_FUNCS(setgroups)
|
||||
@@ -0,0 +1,10 @@
|
||||
################################################
|
||||
# Start MODULE ntvfs_unixuid
|
||||
[MODULE::ntvfs_unixuid]
|
||||
INIT_FUNCTION = ntvfs_unixuid_init
|
||||
SUBSYSTEM = ntvfs
|
||||
OBJ_FILES = \
|
||||
vfs_unixuid.o
|
||||
PUBLIC_DEPENDENCIES = SAMDB
|
||||
# End MODULE ntvfs_unixuid
|
||||
################################################
|
||||
@@ -0,0 +1,697 @@
|
||||
/*
|
||||
Unix SMB/CIFS implementation.
|
||||
|
||||
a pass-thru NTVFS module to setup a security context using unix
|
||||
uid/gid
|
||||
|
||||
Copyright (C) Andrew Tridgell 2004
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 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.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
#include "system/filesys.h"
|
||||
#include "system/passwd.h"
|
||||
#include "auth/auth.h"
|
||||
#include "ntvfs/ntvfs.h"
|
||||
#include "dsdb/samdb/samdb.h"
|
||||
|
||||
struct unixuid_private {
|
||||
struct sidmap_context *sidmap;
|
||||
struct unix_sec_ctx *last_sec_ctx;
|
||||
struct security_token *last_token;
|
||||
};
|
||||
|
||||
|
||||
|
||||
struct unix_sec_ctx {
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
uint_t ngroups;
|
||||
gid_t *groups;
|
||||
};
|
||||
|
||||
/*
|
||||
pull the current security context into a unix_sec_ctx
|
||||
*/
|
||||
static struct unix_sec_ctx *save_unix_security(TALLOC_CTX *mem_ctx)
|
||||
{
|
||||
struct unix_sec_ctx *sec = talloc(mem_ctx, struct unix_sec_ctx);
|
||||
if (sec == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
sec->uid = geteuid();
|
||||
sec->gid = getegid();
|
||||
sec->ngroups = getgroups(0, NULL);
|
||||
if (sec->ngroups == -1) {
|
||||
talloc_free(sec);
|
||||
return NULL;
|
||||
}
|
||||
sec->groups = talloc_array(sec, gid_t, sec->ngroups);
|
||||
if (sec->groups == NULL) {
|
||||
talloc_free(sec);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (getgroups(sec->ngroups, sec->groups) != sec->ngroups) {
|
||||
talloc_free(sec);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return sec;
|
||||
}
|
||||
|
||||
/*
|
||||
set the current security context from a unix_sec_ctx
|
||||
*/
|
||||
static NTSTATUS set_unix_security(struct unix_sec_ctx *sec)
|
||||
{
|
||||
seteuid(0);
|
||||
|
||||
if (setgroups(sec->ngroups, sec->groups) != 0) {
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
if (setegid(sec->gid) != 0) {
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
if (seteuid(sec->uid) != 0) {
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
form a unix_sec_ctx from the current security_token
|
||||
*/
|
||||
static NTSTATUS nt_token_to_unix_security(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
struct security_token *token,
|
||||
struct unix_sec_ctx **sec)
|
||||
{
|
||||
struct unixuid_private *private = ntvfs->private_data;
|
||||
int i;
|
||||
NTSTATUS status;
|
||||
*sec = talloc(req, struct unix_sec_ctx);
|
||||
|
||||
/* we can't do unix security without a user and group */
|
||||
if (token->num_sids < 2) {
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
status = sidmap_sid_to_unixuid(private->sidmap,
|
||||
token->user_sid, &(*sec)->uid);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
status = sidmap_sid_to_unixgid(private->sidmap,
|
||||
token->group_sid, &(*sec)->gid);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
(*sec)->ngroups = token->num_sids - 2;
|
||||
(*sec)->groups = talloc_array(*sec, gid_t, (*sec)->ngroups);
|
||||
if ((*sec)->groups == NULL) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
for (i=0;i<(*sec)->ngroups;i++) {
|
||||
status = sidmap_sid_to_unixgid(private->sidmap,
|
||||
token->sids[i+2], &(*sec)->groups[i]);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
setup our unix security context according to the session authentication info
|
||||
*/
|
||||
static NTSTATUS unixuid_setup_security(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, struct unix_sec_ctx **sec)
|
||||
{
|
||||
struct unixuid_private *private = ntvfs->private_data;
|
||||
struct security_token *token;
|
||||
struct unix_sec_ctx *newsec;
|
||||
NTSTATUS status;
|
||||
|
||||
if (req->session_info == NULL) {
|
||||
return NT_STATUS_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
token = req->session_info->security_token;
|
||||
|
||||
*sec = save_unix_security(req);
|
||||
if (*sec == NULL) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
if (token == private->last_token) {
|
||||
newsec = private->last_sec_ctx;
|
||||
} else {
|
||||
status = nt_token_to_unix_security(ntvfs, req, token, &newsec);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
if (private->last_sec_ctx) {
|
||||
talloc_free(private->last_sec_ctx);
|
||||
}
|
||||
private->last_sec_ctx = newsec;
|
||||
private->last_token = token;
|
||||
talloc_steal(private, newsec);
|
||||
}
|
||||
|
||||
status = set_unix_security(newsec);
|
||||
if (!NT_STATUS_IS_OK(status)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
return NT_STATUS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
this pass through macro operates on request contexts
|
||||
*/
|
||||
#define PASS_THRU_REQ(ntvfs, req, op, args) do { \
|
||||
NTSTATUS status2; \
|
||||
struct unix_sec_ctx *sec; \
|
||||
status = unixuid_setup_security(ntvfs, req, &sec); \
|
||||
NT_STATUS_NOT_OK_RETURN(status); \
|
||||
status = ntvfs_next_##op args; \
|
||||
status2 = set_unix_security(sec); \
|
||||
if (!NT_STATUS_IS_OK(status2)) smb_panic("Unable to reset security context"); \
|
||||
} while (0)
|
||||
|
||||
|
||||
|
||||
/*
|
||||
connect to a share - used when a tree_connect operation comes in.
|
||||
*/
|
||||
static NTSTATUS unixuid_connect(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, const char *sharename)
|
||||
{
|
||||
struct unixuid_private *private;
|
||||
NTSTATUS status;
|
||||
|
||||
private = talloc(ntvfs, struct unixuid_private);
|
||||
if (!private) {
|
||||
return NT_STATUS_NO_MEMORY;
|
||||
}
|
||||
|
||||
private->sidmap = sidmap_open(private);
|
||||
if (private->sidmap == NULL) {
|
||||
return NT_STATUS_INTERNAL_DB_CORRUPTION;
|
||||
}
|
||||
|
||||
ntvfs->private_data = private;
|
||||
private->last_sec_ctx = NULL;
|
||||
private->last_token = NULL;
|
||||
|
||||
/* we don't use PASS_THRU_REQ here, as the connect operation runs with
|
||||
root privileges. This allows the backends to setup any database
|
||||
links they might need during the connect. */
|
||||
status = ntvfs_next_connect(ntvfs, req, sharename);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
disconnect from a share
|
||||
*/
|
||||
static NTSTATUS unixuid_disconnect(struct ntvfs_module_context *ntvfs)
|
||||
{
|
||||
struct unixuid_private *private = ntvfs->private_data;
|
||||
NTSTATUS status;
|
||||
|
||||
talloc_free(private);
|
||||
ntvfs->private_data = NULL;
|
||||
|
||||
status = ntvfs_next_disconnect(ntvfs);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
delete a file
|
||||
*/
|
||||
static NTSTATUS unixuid_unlink(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
union smb_unlink *unl)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
PASS_THRU_REQ(ntvfs, req, unlink, (ntvfs, req, unl));
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
ioctl interface
|
||||
*/
|
||||
static NTSTATUS unixuid_ioctl(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_ioctl *io)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
PASS_THRU_REQ(ntvfs, req, ioctl, (ntvfs, req, io));
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
check if a directory exists
|
||||
*/
|
||||
static NTSTATUS unixuid_chkpath(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
union smb_chkpath *cp)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
PASS_THRU_REQ(ntvfs, req, chkpath, (ntvfs, req, cp));
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
return info on a pathname
|
||||
*/
|
||||
static NTSTATUS unixuid_qpathinfo(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_fileinfo *info)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
PASS_THRU_REQ(ntvfs, req, qpathinfo, (ntvfs, req, info));
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
query info on a open file
|
||||
*/
|
||||
static NTSTATUS unixuid_qfileinfo(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_fileinfo *info)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
PASS_THRU_REQ(ntvfs, req, qfileinfo, (ntvfs, req, info));
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
set info on a pathname
|
||||
*/
|
||||
static NTSTATUS unixuid_setpathinfo(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_setfileinfo *st)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
PASS_THRU_REQ(ntvfs, req, setpathinfo, (ntvfs, req, st));
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
open a file
|
||||
*/
|
||||
static NTSTATUS unixuid_open(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_open *io)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
PASS_THRU_REQ(ntvfs, req, open, (ntvfs, req, io));
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
create a directory
|
||||
*/
|
||||
static NTSTATUS unixuid_mkdir(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_mkdir *md)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
PASS_THRU_REQ(ntvfs, req, mkdir, (ntvfs, req, md));
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
remove a directory
|
||||
*/
|
||||
static NTSTATUS unixuid_rmdir(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, struct smb_rmdir *rd)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
PASS_THRU_REQ(ntvfs, req, rmdir, (ntvfs, req, rd));
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
rename a set of files
|
||||
*/
|
||||
static NTSTATUS unixuid_rename(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_rename *ren)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
PASS_THRU_REQ(ntvfs, req, rename, (ntvfs, req, ren));
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
copy a set of files
|
||||
*/
|
||||
static NTSTATUS unixuid_copy(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, struct smb_copy *cp)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
PASS_THRU_REQ(ntvfs, req, copy, (ntvfs, req, cp));
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
read from a file
|
||||
*/
|
||||
static NTSTATUS unixuid_read(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_read *rd)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
PASS_THRU_REQ(ntvfs, req, read, (ntvfs, req, rd));
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
write to a file
|
||||
*/
|
||||
static NTSTATUS unixuid_write(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_write *wr)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
PASS_THRU_REQ(ntvfs, req, write, (ntvfs, req, wr));
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
seek in a file
|
||||
*/
|
||||
static NTSTATUS unixuid_seek(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
union smb_seek *io)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
PASS_THRU_REQ(ntvfs, req, seek, (ntvfs, req, io));
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
flush a file
|
||||
*/
|
||||
static NTSTATUS unixuid_flush(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
union smb_flush *io)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
PASS_THRU_REQ(ntvfs, req, flush, (ntvfs, req, io));
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
close a file
|
||||
*/
|
||||
static NTSTATUS unixuid_close(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_close *io)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
PASS_THRU_REQ(ntvfs, req, close, (ntvfs, req, io));
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
exit - closing files
|
||||
*/
|
||||
static NTSTATUS unixuid_exit(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
PASS_THRU_REQ(ntvfs, req, exit, (ntvfs, req));
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
logoff - closing files
|
||||
*/
|
||||
static NTSTATUS unixuid_logoff(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req)
|
||||
{
|
||||
struct unixuid_private *private = ntvfs->private_data;
|
||||
NTSTATUS status;
|
||||
|
||||
PASS_THRU_REQ(ntvfs, req, logoff, (ntvfs, req));
|
||||
|
||||
private->last_token = NULL;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
async setup
|
||||
*/
|
||||
static NTSTATUS unixuid_async_setup(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
void *private)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
PASS_THRU_REQ(ntvfs, req, async_setup, (ntvfs, req, private));
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
cancel an async request
|
||||
*/
|
||||
static NTSTATUS unixuid_cancel(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
PASS_THRU_REQ(ntvfs, req, cancel, (ntvfs, req));
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
change notify
|
||||
*/
|
||||
static NTSTATUS unixuid_notify(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_notify *info)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
PASS_THRU_REQ(ntvfs, req, notify, (ntvfs, req, info));
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
lock a byte range
|
||||
*/
|
||||
static NTSTATUS unixuid_lock(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_lock *lck)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
PASS_THRU_REQ(ntvfs, req, lock, (ntvfs, req, lck));
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
set info on a open file
|
||||
*/
|
||||
static NTSTATUS unixuid_setfileinfo(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req,
|
||||
union smb_setfileinfo *info)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
PASS_THRU_REQ(ntvfs, req, setfileinfo, (ntvfs, req, info));
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
return filesystem space info
|
||||
*/
|
||||
static NTSTATUS unixuid_fsinfo(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_fsinfo *fs)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
PASS_THRU_REQ(ntvfs, req, fsinfo, (ntvfs, req, fs));
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
return print queue info
|
||||
*/
|
||||
static NTSTATUS unixuid_lpq(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_lpq *lpq)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
PASS_THRU_REQ(ntvfs, req, lpq, (ntvfs, req, lpq));
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
list files in a directory matching a wildcard pattern
|
||||
*/
|
||||
static NTSTATUS unixuid_search_first(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_search_first *io,
|
||||
void *search_private,
|
||||
BOOL (*callback)(void *, union smb_search_data *))
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
PASS_THRU_REQ(ntvfs, req, search_first, (ntvfs, req, io, search_private, callback));
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/* continue a search */
|
||||
static NTSTATUS unixuid_search_next(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_search_next *io,
|
||||
void *search_private,
|
||||
BOOL (*callback)(void *, union smb_search_data *))
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
PASS_THRU_REQ(ntvfs, req, search_next, (ntvfs, req, io, search_private, callback));
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/* close a search */
|
||||
static NTSTATUS unixuid_search_close(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, union smb_search_close *io)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
PASS_THRU_REQ(ntvfs, req, search_close, (ntvfs, req, io));
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/* SMBtrans - not used on file shares */
|
||||
static NTSTATUS unixuid_trans(struct ntvfs_module_context *ntvfs,
|
||||
struct ntvfs_request *req, struct smb_trans2 *trans2)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
PASS_THRU_REQ(ntvfs, req, trans, (ntvfs, req, trans2));
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
initialise the unixuid backend, registering ourselves with the ntvfs subsystem
|
||||
*/
|
||||
NTSTATUS ntvfs_unixuid_init(void)
|
||||
{
|
||||
NTSTATUS ret;
|
||||
struct ntvfs_ops ops;
|
||||
NTVFS_CURRENT_CRITICAL_SIZES(vers);
|
||||
|
||||
ZERO_STRUCT(ops);
|
||||
|
||||
/* fill in all the operations */
|
||||
ops.connect = unixuid_connect;
|
||||
ops.disconnect = unixuid_disconnect;
|
||||
ops.unlink = unixuid_unlink;
|
||||
ops.chkpath = unixuid_chkpath;
|
||||
ops.qpathinfo = unixuid_qpathinfo;
|
||||
ops.setpathinfo = unixuid_setpathinfo;
|
||||
ops.open = unixuid_open;
|
||||
ops.mkdir = unixuid_mkdir;
|
||||
ops.rmdir = unixuid_rmdir;
|
||||
ops.rename = unixuid_rename;
|
||||
ops.copy = unixuid_copy;
|
||||
ops.ioctl = unixuid_ioctl;
|
||||
ops.read = unixuid_read;
|
||||
ops.write = unixuid_write;
|
||||
ops.seek = unixuid_seek;
|
||||
ops.flush = unixuid_flush;
|
||||
ops.close = unixuid_close;
|
||||
ops.exit = unixuid_exit;
|
||||
ops.lock = unixuid_lock;
|
||||
ops.setfileinfo = unixuid_setfileinfo;
|
||||
ops.qfileinfo = unixuid_qfileinfo;
|
||||
ops.fsinfo = unixuid_fsinfo;
|
||||
ops.lpq = unixuid_lpq;
|
||||
ops.search_first = unixuid_search_first;
|
||||
ops.search_next = unixuid_search_next;
|
||||
ops.search_close = unixuid_search_close;
|
||||
ops.trans = unixuid_trans;
|
||||
ops.logoff = unixuid_logoff;
|
||||
ops.async_setup = unixuid_async_setup;
|
||||
ops.cancel = unixuid_cancel;
|
||||
ops.notify = unixuid_notify;
|
||||
|
||||
ops.name = "unixuid";
|
||||
|
||||
/* we register under all 3 backend types, as we are not type specific */
|
||||
ops.type = NTVFS_DISK;
|
||||
ret = ntvfs_register(&ops, &vers);
|
||||
if (!NT_STATUS_IS_OK(ret)) goto failed;
|
||||
|
||||
ops.type = NTVFS_PRINT;
|
||||
ret = ntvfs_register(&ops, &vers);
|
||||
if (!NT_STATUS_IS_OK(ret)) goto failed;
|
||||
|
||||
ops.type = NTVFS_IPC;
|
||||
ret = ntvfs_register(&ops, &vers);
|
||||
if (!NT_STATUS_IS_OK(ret)) goto failed;
|
||||
|
||||
failed:
|
||||
return ret;
|
||||
}
|
||||
Reference in New Issue
Block a user