wmi-1.3.16 from opsview.com

This commit is contained in:
Are Casilla
2019-02-16 00:16:52 +01:00
parent 163fdd3d1b
commit 17b3af2911
2146 changed files with 678824 additions and 0 deletions
+26
View File
@@ -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
+40
View File
@@ -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
+12
View File
@@ -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
+688
View File
@@ -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;
}
+12
View File
@@ -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
################################################
+33
View File
@@ -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();
}
+659
View File
@@ -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 = &notify->array->depth[depth];
/* expand the entries array */
ee = talloc_realloc(notify->array->depth, d->entries, struct notify_entry,
d->num_entries+1);
NT_STATUS_HAVE_NO_MEMORY(ee);
d->entries = ee;
d->entries[d->num_entries] = *e;
d->entries[d->num_entries].private = 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 = &notify->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 = &notify->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 = &notify->array->depth[depth];
next_p = strchr(p+1, '/');
/* see if there are any entries at this depth */
if (d->num_entries == 0) continue;
/* try to skip based on the maximum mask. If next_p is
NULL then we know it will be a 'this directory'
match, otherwise it must be a subdir match */
if (next_p != NULL) {
if (0 == (filter & d->max_mask_subdir)) {
continue;
}
} else {
if (0 == (filter & d->max_mask)) {
continue;
}
}
/* we know there is an entry here worth looking
for. Use a bisection search to find the first entry
with a matching path */
min_i = 0;
max_i = d->num_entries-1;
while (min_i < max_i) {
struct notify_entry *e;
int cmp;
i = (min_i+max_i)/2;
e = &d->entries[i];
cmp = strncmp(path, e->path, p_len);
if (cmp == 0) {
if (p_len == e->path_len) {
max_i = i;
} else {
max_i = i-1;
}
} else if (cmp < 0) {
max_i = i-1;
} else {
min_i = i+1;
}
}
if (min_i != max_i) {
/* none match */
continue;
}
/* we now know that the entries start at min_i */
for (i=min_i;i<d->num_entries;i++) {
struct notify_entry *e = &d->entries[i];
if (p_len != e->path_len ||
strncmp(path, e->path, p_len) != 0) break;
if (next_p != NULL) {
if (0 == (filter & e->subdir_filter)) {
continue;
}
} else {
if (0 == (filter & e->filter)) {
continue;
}
}
notify_send(notify, e, path + e->path_len + 1, action);
}
}
}
+605
View File
@@ -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;
}
+96
View File
@@ -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
################################################
+5
View File
@@ -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.
View File
+462
View File
@@ -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;
}
View File
+83
View File
@@ -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;
}
+913
View File
@@ -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;
}
+13
View File
@@ -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
+973
View File
@@ -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;
}
+313
View File
@@ -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"
+211
View File
@@ -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
+717
View File
@@ -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);
}
+202
View File
@@ -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;
}
+31
View File
@@ -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
+61
View File
@@ -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
################################################
+739
View File
@@ -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;
}
+163
View File
@@ -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);
}
+105
View File
@@ -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);
}
+404
View File
@@ -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;
}
+121
View File
@@ -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;
}
+79
View File
@@ -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;
}
+208
View File
@@ -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;
}
+81
View File
@@ -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;
}
+395
View File
@@ -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;
}
+195
View File
@@ -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;
}
+284
View File
@@ -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
+425
View File
@@ -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;
}
+94
View File
@@ -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;
}
+478
View File
@@ -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;
}
+634
View File
@@ -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;
}
+843
View File
@@ -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;
}
+65
View File
@@ -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;
}
+635
View File
@@ -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);
}
+695
View File
@@ -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));
}
+358
View File
@@ -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;
}
+185
View File
@@ -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;
}
+203
View File
@@ -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);
}
+191
View File
@@ -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);
}
+87
View File
@@ -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;
}
+477
View File
@@ -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);
}
+358
View File
@@ -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;
}
+243
View File
@@ -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_ */
+147
View File
@@ -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;
}
+233
View File
@@ -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);
}
+3
View File
@@ -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.
+125
View File
@@ -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;
}
+10
View File
@@ -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
+38
View File
@@ -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;
};
+185
View File
@@ -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
+5
View File
@@ -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.
+17
View File
@@ -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
+18
View File
@@ -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
################################################
+419
View File
@@ -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);
}
+140
View File
@@ -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;
}
+53
View File
@@ -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);
+1
View File
@@ -0,0 +1 @@
AC_CHECK_FUNCS(setgroups)
+10
View File
@@ -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
################################################
+697
View File
@@ -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;
}