1
0
mirror of https://github.com/samba-team/samba.git synced 2025-03-09 08:58:35 +03:00

Sync with HEAD:

- Move working VFS modules to source/
- Move documentation to SGML
This commit is contained in:
Jelmer Vernooij -
parent fadada3555
commit be4894815c
6 changed files with 9 additions and 1374 deletions

View File

@ -1,53 +1,14 @@
README for Samba Virtual File System (VFS) Examples
README for Samba Virtual File System (VFS) Example
===================================================
This directory contains some sample code to demonstrate VFS
construction. The following VFS modules are given:
This directory contains a skeleton VFS module. When used,
this module simply passes all requests back to the disk functions
(i.e it operates as a passthrough filter). It should be
useful as a starting point for developing new VFS
modules.
skel
A skeleton VFS module. When used, this module simply
passes all requests back to the disk functions (i.e it
operates as a passthrough filter). It should be
useful as a starting point for developing new VFS
modules.
audit
A simple module to audit file access to the syslog
facility. The following operations are logged: share
connect/disconnect, directory opens/create/remove,
file open/close/rename/unlink/chmod.
recycle
A recycle-bin like modules. When used any unlink call
will be intercepted and files moved to the recycle
directory nstead of beeing deleted.
block
A simple module to block access to certain mount points or
directories. This module only hides the specified directories
and all directories beneath them. It should NOT be used to
secure directories. If the name of a file in one of those
directories is known, the file can still be opened.
netatalk
A netatalk module, that will ease co-existence of samba and
netatalk file sharing services.
Looka t the README for more informations.
You may have problems to compile these modules, as shared libraries are
compiled and linked in different ways on different systems.
I currently tested them against GNU/linux and IRIX.
To use the VFS modules, create a share similar to the one below. The
important parameter is the 'vfs object' parameter which must point to
the exact pathname of the shared library object.
[audit]
comment = Audited /data directory
path = /data
vfs object = /path/to/audit.so
writeable = yes
browseable = yes
Please read the VFS chapter in the HOWTO collection for general help
on the usage of VFS modules.
Further documentation on writing VFS modules for Samba can be found in
docs directory of the Samba source distribution.
Samba Developers Guide.

View File

@ -1,51 +0,0 @@
This file contains a listing of various other VFS modules that
have been posted but don't currently reside in the Samba CVS
tree for one reason ot another (e.g. it is easy for the maintainer
to have his or her own CVS tree).
No statemets about the stability or functionality any module
should be implied due to its presence here.
------------------------------------------------------------
URL: http://www.css.tayloru.edu/~elorimer/databasefs/index.php
Date: Sat, 28 Sep 2002 23:41:31 -0500
From: Eric Lorimer <elorimer@css.tayloru.edu>
To: samba-technical@lists.samba.org
Subject: DatabaseFS VFS module
Hello,
I have created a VFS module which implements a fairly complete read-only
filesystem. It presents information from a database as a filesystem in
a modular and generic way to allow different databases to be used
(originally designed for organizing MP3s under directories such as
"Artists," "Song Keywords," etc... I have since applied it to a student
roster database very easily). The directory structure is stored in the
database itself and the module makes no assumptions about the database
structure beyond the table it requires to run. You can find it at:
http://www.css.tayloru.edu/~elorimer/databasefs/index.php
Any feedback would be appreciated: comments, suggestions, patches,
etc... If nothing else, hopefully it might prove useful for someone
else who wishes to create a virtual filesystem.
Thanks for the great product and keep up the good work.
- Eric Lorimer
------------------------------------------------------------
URL: http://www.openantivirus.org/
"samba-vscan is a proof-of-concept module for Samba, which
uses the VFS (virtual file system) features of Samba 2.2.x/3.0
alphaX. Of couse, Samba has to be compiled with VFS support.
samba-vscan supports various virus scanners and is maintained
by Rainer Link".
------------------------------------------------------------

View File

@ -1,18 +0,0 @@
There is the new netatalk module both for HEAD.
This one has some difference from previous module:
-- it doesn't care about creating of .AppleDouble forks, just keeps ones in
sync;
-- if share in smb.conf doesn't contain .AppleDouble item in hide or veto
list, it will be added automatically.
To my way of thinking, module became more lightweight and speedy.
How to compile:
you should place proper netatalk.c into examples/VFS/ then run 'configure'
from source/ and then run 'make' from examples/VFS/.
add string 'vfs object = <path_to_netatalk_so>/netatlk.so' to smb.conf. It may
be defined either as global or as share-specific parameter.

View File

@ -1,268 +0,0 @@
/*
* Auditing VFS module for samba. Log selected file operations to syslog
* facility.
*
* Copyright (C) Tim Potter, 1999-2000
* Copyright (C) Alexander Bokovoy, 2002
*
* 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 "config.h"
#include <stdio.h>
#include <sys/stat.h>
#ifdef HAVE_UTIME_H
#include <utime.h>
#endif
#ifdef HAVE_DIRENT_H
#include <dirent.h>
#endif
#include <syslog.h>
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#include <errno.h>
#include <string.h>
#include <includes.h>
#include <vfs.h>
#ifndef SYSLOG_FACILITY
#define SYSLOG_FACILITY LOG_USER
#endif
#ifndef SYSLOG_PRIORITY
#define SYSLOG_PRIORITY LOG_NOTICE
#endif
/* Function prototypes */
static int audit_connect(struct connection_struct *conn, const char *svc, const char *user);
static void audit_disconnect(struct connection_struct *conn);
static DIR *audit_opendir(struct connection_struct *conn, const char *fname);
static int audit_mkdir(struct connection_struct *conn, const char *path, mode_t mode);
static int audit_rmdir(struct connection_struct *conn, const char *path);
static int audit_open(struct connection_struct *conn, const char *fname, int flags, mode_t mode);
static int audit_close(struct files_struct *fsp, int fd);
static int audit_rename(struct connection_struct *conn, const char *old, const char *new);
static int audit_unlink(struct connection_struct *conn, const char *path);
static int audit_chmod(struct connection_struct *conn, const char *path, mode_t mode);
static int audit_chmod_acl(struct connection_struct *conn, const char *name, mode_t mode);
static int audit_fchmod(struct files_struct *fsp, int fd, mode_t mode);
static int audit_fchmod_acl(struct files_struct *fsp, int fd, mode_t mode);
/* VFS operations */
static struct vfs_ops default_vfs_ops; /* For passthrough operation */
static struct smb_vfs_handle_struct *audit_handle;
static vfs_op_tuple audit_ops[] = {
/* Disk operations */
{audit_connect, SMB_VFS_OP_CONNECT, SMB_VFS_LAYER_LOGGER},
{audit_disconnect, SMB_VFS_OP_DISCONNECT, SMB_VFS_LAYER_LOGGER},
/* Directory operations */
{audit_opendir, SMB_VFS_OP_OPENDIR, SMB_VFS_LAYER_LOGGER},
{audit_mkdir, SMB_VFS_OP_MKDIR, SMB_VFS_LAYER_LOGGER},
{audit_rmdir, SMB_VFS_OP_RMDIR, SMB_VFS_LAYER_LOGGER},
/* File operations */
{audit_open, SMB_VFS_OP_OPEN, SMB_VFS_LAYER_LOGGER},
{audit_close, SMB_VFS_OP_CLOSE, SMB_VFS_LAYER_LOGGER},
{audit_rename, SMB_VFS_OP_RENAME, SMB_VFS_LAYER_LOGGER},
{audit_unlink, SMB_VFS_OP_UNLINK, SMB_VFS_LAYER_LOGGER},
{audit_chmod, SMB_VFS_OP_CHMOD, SMB_VFS_LAYER_LOGGER},
{audit_fchmod, SMB_VFS_OP_FCHMOD, SMB_VFS_LAYER_LOGGER},
{audit_chmod_acl, SMB_VFS_OP_CHMOD_ACL, SMB_VFS_LAYER_LOGGER},
{audit_fchmod_acl, SMB_VFS_OP_FCHMOD_ACL, SMB_VFS_LAYER_LOGGER},
/* Finish VFS operations definition */
{NULL, SMB_VFS_OP_NOOP, SMB_VFS_LAYER_NOOP}
};
/* VFS initialisation function. Return vfs_op_tuple array back to SAMBA. */
vfs_op_tuple *vfs_init(int *vfs_version, struct vfs_ops *def_vfs_ops,
struct smb_vfs_handle_struct *vfs_handle)
{
*vfs_version = SMB_VFS_INTERFACE_VERSION;
memcpy(&default_vfs_ops, def_vfs_ops, sizeof(struct vfs_ops));
audit_handle = vfs_handle;
openlog("smbd_audit", LOG_PID, SYSLOG_FACILITY);
syslog(SYSLOG_PRIORITY, "VFS_INIT: vfs_ops loaded\n");
return audit_ops;
}
/* VFS finalization function. */
void vfs_done(connection_struct *conn)
{
syslog(SYSLOG_PRIORITY, "VFS_DONE: vfs module unloaded\n");
}
/* Implementation of vfs_ops. Pass everything on to the default
operation but log event first. */
static int audit_connect(struct connection_struct *conn, const char *svc, const char *user)
{
syslog(SYSLOG_PRIORITY, "connect to service %s by user %s\n",
svc, user);
return default_vfs_ops.connect(conn, svc, user);
}
static void audit_disconnect(struct connection_struct *conn)
{
syslog(SYSLOG_PRIORITY, "disconnected\n");
default_vfs_ops.disconnect(conn);
}
static DIR *audit_opendir(struct connection_struct *conn, const char *fname)
{
DIR *result = default_vfs_ops.opendir(conn, fname);
syslog(SYSLOG_PRIORITY, "opendir %s %s%s\n",
fname,
(result == NULL) ? "failed: " : "",
(result == NULL) ? strerror(errno) : "");
return result;
}
static int audit_mkdir(struct connection_struct *conn, const char *path, mode_t mode)
{
int result = default_vfs_ops.mkdir(conn, path, mode);
syslog(SYSLOG_PRIORITY, "mkdir %s %s%s\n",
path,
(result < 0) ? "failed: " : "",
(result < 0) ? strerror(errno) : "");
return result;
}
static int audit_rmdir(struct connection_struct *conn, const char *path)
{
int result = default_vfs_ops.rmdir(conn, path);
syslog(SYSLOG_PRIORITY, "rmdir %s %s%s\n",
path,
(result < 0) ? "failed: " : "",
(result < 0) ? strerror(errno) : "");
return result;
}
static int audit_open(struct connection_struct *conn, const char *fname, int flags, mode_t mode)
{
int result = default_vfs_ops.open(conn, fname, flags, mode);
syslog(SYSLOG_PRIORITY, "open %s (fd %d) %s%s%s\n",
fname, result,
((flags & O_WRONLY) || (flags & O_RDWR)) ? "for writing " : "",
(result < 0) ? "failed: " : "",
(result < 0) ? strerror(errno) : "");
return result;
}
static int audit_close(struct files_struct *fsp, int fd)
{
int result = default_vfs_ops.close(fsp, fd);
syslog(SYSLOG_PRIORITY, "close fd %d %s%s\n",
fd,
(result < 0) ? "failed: " : "",
(result < 0) ? strerror(errno) : "");
return result;
}
static int audit_rename(struct connection_struct *conn, const char *old, const char *new)
{
int result = default_vfs_ops.rename(conn, old, new);
syslog(SYSLOG_PRIORITY, "rename %s -> %s %s%s\n",
old, new,
(result < 0) ? "failed: " : "",
(result < 0) ? strerror(errno) : "");
return result;
}
static int audit_unlink(struct connection_struct *conn, const char *path)
{
int result = default_vfs_ops.unlink(conn, path);
syslog(SYSLOG_PRIORITY, "unlink %s %s%s\n",
path,
(result < 0) ? "failed: " : "",
(result < 0) ? strerror(errno) : "");
return result;
}
static int audit_chmod(struct connection_struct *conn, const char *path, mode_t mode)
{
int result = default_vfs_ops.chmod(conn, path, mode);
syslog(SYSLOG_PRIORITY, "chmod %s mode 0x%x %s%s\n",
path, mode,
(result < 0) ? "failed: " : "",
(result < 0) ? strerror(errno) : "");
return result;
}
static int audit_chmod_acl(struct connection_struct *conn, const char *path, mode_t mode)
{
int result = default_vfs_ops.chmod_acl(conn, path, mode);
syslog(SYSLOG_PRIORITY, "chmod_acl %s mode 0x%x %s%s\n",
path, mode,
(result < 0) ? "failed: " : "",
(result < 0) ? strerror(errno) : "");
return result;
}
static int audit_fchmod(struct files_struct *fsp, int fd, mode_t mode)
{
int result = default_vfs_ops.fchmod(fsp, fd, mode);
syslog(SYSLOG_PRIORITY, "fchmod %s mode 0x%x %s%s\n",
fsp->fsp_name, mode,
(result < 0) ? "failed: " : "",
(result < 0) ? strerror(errno) : "");
return result;
}
static int audit_fchmod_acl(struct files_struct *fsp, int fd, mode_t mode)
{
int result = default_vfs_ops.fchmod_acl(fsp, fd, mode);
syslog(SYSLOG_PRIORITY, "fchmod_acl %s mode 0x%x %s%s\n",
fsp->fsp_name, mode,
(result < 0) ? "failed: " : "",
(result < 0) ? strerror(errno) : "");
return result;
}

View File

@ -1,430 +0,0 @@
/*
* AppleTalk VFS module for Samba-3.x
*
* Copyright (C) Alexei Kotovich, 2002
*
* 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 "config.h"
#include <stdio.h>
#include <sys/stat.h>
#ifdef HAVE_UTIME_H
#include <utime.h>
#endif
#ifdef HAVE_DIRENT_H
#include <dirent.h>
#endif
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#include <errno.h>
#include <string.h>
#include <includes.h>
#include <vfs.h>
#define APPLEDOUBLE ".AppleDouble"
#define ADOUBLEMODE 0777
/* atalk functions */
static int atalk_build_paths(TALLOC_CTX *ctx, const char *path,
const char *fname, char **adbl_path, char **orig_path,
SMB_STRUCT_STAT *adbl_info, SMB_STRUCT_STAT *orig_info);
static int atalk_unlink_file(const char *path);
static struct vfs_ops default_vfs_ops; /* For passthrough operation */
static struct smb_vfs_handle_struct *atalk_handle;
static int atalk_get_path_ptr(char *path)
{
int i = 0;
int ptr = 0;
for (i = 0; path[i]; i ++) {
if (path[i] == '/')
ptr = i;
/* get out some 'spam';) from win32's file name */
else if (path[i] == ':') {
path[i] = '\0';
break;
}
}
return ptr;
}
static int atalk_build_paths(TALLOC_CTX *ctx, const char *path, const char *fname,
char **adbl_path, char **orig_path,
SMB_STRUCT_STAT *adbl_info, SMB_STRUCT_STAT *orig_info)
{
int ptr0 = 0;
int ptr1 = 0;
char *dname = 0;
char *name = 0;
if (!ctx || !path || !fname || !adbl_path || !orig_path ||
!adbl_info || !orig_info)
return -1;
#if 0
DEBUG(3, ("ATALK: PATH: %s[%s]\n", path, fname));
#endif
if (strstr(path, APPLEDOUBLE) || strstr(fname, APPLEDOUBLE)) {
DEBUG(3, ("ATALK: path %s[%s] already contains %s\n", path, fname, APPLEDOUBLE));
return -1;
}
if (fname[0] == '.') ptr0 ++;
if (fname[1] == '/') ptr0 ++;
*orig_path = talloc_asprintf(ctx, "%s/%s", path, &fname[ptr0]);
/* get pointer to last '/' */
ptr1 = atalk_get_path_ptr(*orig_path);
sys_lstat(*orig_path, orig_info);
if (S_ISDIR(orig_info->st_mode)) {
*adbl_path = talloc_asprintf(ctx, "%s/%s/%s/",
path, &fname[ptr0], APPLEDOUBLE);
} else {
dname = talloc_strdup(ctx, *orig_path);
dname[ptr1] = '\0';
name = *orig_path;
*adbl_path = talloc_asprintf(ctx, "%s/%s/%s",
dname, APPLEDOUBLE, &name[ptr1 + 1]);
}
#if 0
DEBUG(3, ("ATALK: DEBUG:\n%s\n%s\n", *orig_path, *adbl_path));
#endif
sys_lstat(*adbl_path, adbl_info);
return 0;
}
static int atalk_unlink_file(const char *path)
{
int ret = 0;
become_root();
ret = unlink(path);
unbecome_root();
return ret;
}
static void atalk_add_to_list(name_compare_entry **list)
{
int i, count = 0;
name_compare_entry *new_list = 0;
name_compare_entry *cur_list = 0;
cur_list = *list;
if (cur_list) {
for (i = 0, count = 0; cur_list[i].name; i ++, count ++) {
if (strstr(cur_list[i].name, APPLEDOUBLE))
return;
}
}
if (!(new_list = calloc(1,
(count == 0 ? 1 : count + 1) * sizeof(name_compare_entry))))
return;
for (i = 0; i < count; i ++) {
new_list[i].name = strdup(cur_list[i].name);
new_list[i].is_wild = cur_list[i].is_wild;
}
new_list[i].name = strdup(APPLEDOUBLE);
new_list[i].is_wild = False;
free_namearray(*list);
*list = new_list;
new_list = 0;
cur_list = 0;
}
static void atalk_rrmdir(TALLOC_CTX *ctx, char *path)
{
int n;
char *dpath;
struct dirent **namelist;
if (!path) return;
n = scandir(path, &namelist, 0, alphasort);
if (n < 0) {
return;
} else {
while (n --) {
if (strcmp(namelist[n]->d_name, ".") == 0 ||
strcmp(namelist[n]->d_name, "..") == 0)
continue;
if (!(dpath = talloc_asprintf(ctx, "%s/%s",
path, namelist[n]->d_name)))
continue;
atalk_unlink_file(dpath);
free(namelist[n]);
}
}
}
/* Disk operations */
/* Directory operations */
DIR *atalk_opendir(struct connection_struct *conn, const char *fname)
{
DIR *ret = 0;
ret = default_vfs_ops.opendir(conn, fname);
/*
* when we try to perform delete operation upon file which has fork
* in ./.AppleDouble and this directory wasn't hidden by Samba,
* MS Windows explorer causes the error: "Cannot find the specified file"
* There is some workaround to avoid this situation, i.e. if
* connection has not .AppleDouble entry in either veto or hide
* list then it would be nice to add one.
*/
atalk_add_to_list(&conn->hide_list);
atalk_add_to_list(&conn->veto_list);
return ret;
}
static int atalk_rmdir(struct connection_struct *conn, const char *path)
{
BOOL add = False;
TALLOC_CTX *ctx = 0;
char *dpath;
if (!conn || !conn->origpath || !path) goto exit_rmdir;
/* due to there is no way to change bDeleteVetoFiles variable
* from this module, gotta use talloc stuff..
*/
strstr(path, APPLEDOUBLE) ? (add = False) : (add = True);
if (!(ctx = talloc_init_named("remove_directory")))
goto exit_rmdir;
if (!(dpath = talloc_asprintf(ctx, "%s/%s%s",
conn->origpath, path, add ? "/"APPLEDOUBLE : "")))
goto exit_rmdir;
atalk_rrmdir(ctx, dpath);
exit_rmdir:
talloc_destroy(ctx);
return default_vfs_ops.rmdir(conn, path);
}
/* File operations */
static int atalk_rename(struct connection_struct *conn, const char *old, const char *new)
{
int ret = 0;
char *adbl_path = 0;
char *orig_path = 0;
SMB_STRUCT_STAT adbl_info;
SMB_STRUCT_STAT orig_info;
TALLOC_CTX *ctx;
ret = default_vfs_ops.rename(conn, old, new);
if (!conn || !old) return ret;
if (!(ctx = talloc_init_named("rename_file")))
return ret;
if (atalk_build_paths(ctx, conn->origpath, old, &adbl_path, &orig_path,
&adbl_info, &orig_info) != 0)
return ret;
if (S_ISDIR(orig_info.st_mode) || S_ISREG(orig_info.st_mode)) {
DEBUG(3, ("ATALK: %s has passed..\n", adbl_path));
goto exit_rename;
}
atalk_unlink_file(adbl_path);
exit_rename:
talloc_destroy(ctx);
return ret;
}
static int atalk_unlink(struct connection_struct *conn, const char *path)
{
int ret = 0, i;
char *adbl_path = 0;
char *orig_path = 0;
SMB_STRUCT_STAT adbl_info;
SMB_STRUCT_STAT orig_info;
TALLOC_CTX *ctx;
ret = default_vfs_ops.unlink(conn, path);
if (!conn || !path) return ret;
/* no .AppleDouble sync if veto or hide list is empty,
* otherwise "Cannot find the specified file" error will be caused
*/
if (!conn->veto_list) return ret;
if (!conn->hide_list) return ret;
for (i = 0; conn->veto_list[i].name; i ++) {
if (strstr(conn->veto_list[i].name, APPLEDOUBLE))
break;
}
if (!conn->veto_list[i].name) {
for (i = 0; conn->hide_list[i].name; i ++) {
if (strstr(conn->hide_list[i].name, APPLEDOUBLE))
break;
else {
DEBUG(3, ("ATALK: %s is not hidden, skipped..\n",
APPLEDOUBLE));
return ret;
}
}
}
if (!(ctx = talloc_init_named("unlink_file")))
return ret;
if (atalk_build_paths(ctx, conn->origpath, path, &adbl_path, &orig_path,
&adbl_info, &orig_info) != 0)
return ret;
if (S_ISDIR(orig_info.st_mode) || S_ISREG(orig_info.st_mode)) {
DEBUG(3, ("ATALK: %s has passed..\n", adbl_path));
goto exit_unlink;
}
atalk_unlink_file(adbl_path);
exit_unlink:
talloc_destroy(ctx);
return ret;
}
static int atalk_chmod(struct connection_struct *conn, const char *path, mode_t mode)
{
int ret = 0;
char *adbl_path = 0;
char *orig_path = 0;
SMB_STRUCT_STAT adbl_info;
SMB_STRUCT_STAT orig_info;
TALLOC_CTX *ctx;
ret = default_vfs_ops.chmod(conn, path, mode);
if (!conn || !path) return ret;
if (!(ctx = talloc_init_named("chmod_file")))
return ret;
if (atalk_build_paths(ctx, conn->origpath, path, &adbl_path, &orig_path,
&adbl_info, &orig_info) != 0)
return ret;
if (!S_ISDIR(orig_info.st_mode) && !S_ISREG(orig_info.st_mode)) {
DEBUG(3, ("ATALK: %s has passed..\n", orig_path));
goto exit_chmod;
}
chmod(adbl_path, ADOUBLEMODE);
exit_chmod:
talloc_destroy(ctx);
return ret;
}
static int atalk_chown(struct connection_struct *conn, const char *path, uid_t uid, gid_t gid)
{
int ret = 0;
char *adbl_path = 0;
char *orig_path = 0;
SMB_STRUCT_STAT adbl_info;
SMB_STRUCT_STAT orig_info;
TALLOC_CTX *ctx;
ret = default_vfs_ops.chown(conn, path, uid, gid);
if (!conn || !path) return ret;
if (!(ctx = talloc_init_named("chown_file")))
return ret;
if (atalk_build_paths(ctx, conn->origpath, path, &adbl_path, &orig_path,
&adbl_info, &orig_info) != 0)
return ret;
if (!S_ISDIR(orig_info.st_mode) && !S_ISREG(orig_info.st_mode)) {
DEBUG(3, ("ATALK: %s has passed..\n", orig_path));
goto exit_chown;
}
chown(adbl_path, uid, gid);
exit_chown:
talloc_destroy(ctx);
return ret;
}
static vfs_op_tuple atalk_ops[] = {
/* Directory operations */
{atalk_opendir, SMB_VFS_OP_OPENDIR, SMB_VFS_LAYER_TRANSPARENT},
{atalk_rmdir, SMB_VFS_OP_RMDIR, SMB_VFS_LAYER_TRANSPARENT},
/* File operations */
{atalk_rename, SMB_VFS_OP_RENAME, SMB_VFS_LAYER_TRANSPARENT},
{atalk_unlink, SMB_VFS_OP_UNLINK, SMB_VFS_LAYER_TRANSPARENT},
{atalk_chmod, SMB_VFS_OP_CHMOD, SMB_VFS_LAYER_TRANSPARENT},
{atalk_chown, SMB_VFS_OP_CHOWN, SMB_VFS_LAYER_TRANSPARENT},
/* Finish VFS operations definition */
{NULL, SMB_VFS_OP_NOOP, SMB_VFS_LAYER_NOOP}
};
/* VFS initialisation function. Return vfs_op_tuple array back to SAMBA. */
vfs_op_tuple *vfs_init(int *vfs_version, struct vfs_ops *def_vfs_ops,
struct smb_vfs_handle_struct *vfs_handle)
{
*vfs_version = SMB_VFS_INTERFACE_VERSION;
memcpy(&default_vfs_ops, def_vfs_ops, sizeof(struct vfs_ops));
atalk_handle = vfs_handle;
DEBUG(3, ("ATALK: vfs module loaded\n"));
return atalk_ops;
}
/* VFS finalization function. */
void vfs_done(connection_struct *conn)
{
DEBUG(3, ("ATALK: vfs module unloaded\n"));
}

View File

@ -1,559 +0,0 @@
/*
* Recycle bin VFS module for Samba.
*
* Copyright (C) 2001, Brandon Stone, Amherst College, <bbstone@amherst.edu>.
* Copyright (C) 2002, Jeremy Allison - modified to make a VFS module.
* Copyright (C) 2002, Alexander Bokovoy - cascaded VFS adoption,
* Copyright (C) 2002, Juergen Hasch - added some options.
* Copyright (C) 2002, Simo Sorce
*
* 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"
#define ALLOC_CHECK(ptr, label) do { if ((ptr) == NULL) { DEBUG(0, ("recycle.bin: out of memory!\n")); errno = ENOMEM; goto label; } } while(0)
static int vfs_recycle_debug_level = DBGC_VFS;
#undef DBGC_CLASS
#define DBGC_CLASS vfs_recycle_debug_level
static const char *delimiter = "|"; /* delimiter for options */
/* One per connection */
typedef struct recycle_bin_struct
{
TALLOC_CTX *ctx;
char *repository; /* name of the recycle bin directory */
BOOL keep_dir_tree; /* keep directory structure of deleted file in recycle bin */
BOOL versions; /* create versions of deleted files with identical name */
BOOL touch; /* touch access date of deleted file */
char *exclude; /* which files to exclude */
char *exclude_dir; /* which directories to exclude */
char *noversions; /* which files to exclude from versioning */
SMB_OFF_T maxsize; /* maximum file size to be saved */
} recycle_bin_struct;
/* VFS operations */
static struct vfs_ops default_vfs_ops; /* For passthrough operation */
static int recycle_connect(struct connection_struct *conn, const char *service, const char *user);
static void recycle_disconnect(struct connection_struct *conn);
static int recycle_unlink(connection_struct *, const char *);
#define VFS_OP(x) ((void *) x)
static vfs_op_tuple recycle_ops[] = {
/* Disk operations */
{VFS_OP(recycle_connect), SMB_VFS_OP_CONNECT, SMB_VFS_LAYER_TRANSPARENT},
{VFS_OP(recycle_disconnect), SMB_VFS_OP_DISCONNECT, SMB_VFS_LAYER_TRANSPARENT},
/* File operations */
{VFS_OP(recycle_unlink), SMB_VFS_OP_UNLINK, SMB_VFS_LAYER_TRANSPARENT},
{NULL, SMB_VFS_OP_NOOP, SMB_VFS_LAYER_NOOP}
};
static BOOL check_bool_param(const char *value)
{
if (strwicmp(value, "yes") == 0 ||
strwicmp(value, "true") == 0 ||
strwicmp(value, "1") == 0)
return True;
return False;
}
/**
* VFS initialisation function.
*
* @retval initialised vfs_op_tuple array
**/
vfs_op_tuple *vfs_init(int *vfs_version, struct vfs_ops *def_vfs_ops,
struct smb_vfs_handle_struct *vfs_handle)
{
DEBUG(10, ("Initializing VFS module recycle\n"));
*vfs_version = SMB_VFS_INTERFACE_VERSION;
memcpy(&default_vfs_ops, def_vfs_ops, sizeof(struct vfs_ops));
vfs_recycle_debug_level = debug_add_class("vfs_recycle_bin");
if (vfs_recycle_debug_level == -1) {
vfs_recycle_debug_level = DBGC_VFS;
DEBUG(0, ("vfs_recycle: Couldn't register custom debugging class!\n"));
} else {
DEBUG(0, ("vfs_recycle: Debug class number of 'vfs_recycle': %d\n", vfs_recycle_debug_level));
}
return recycle_ops;
}
/**
* VFS finalization function.
*
**/
void vfs_done(connection_struct *conn)
{
DEBUG(10,("Called for connection %d\n", SNUM(conn)));
}
static int recycle_connect(struct connection_struct *conn, const char *service, const char *user)
{
TALLOC_CTX *ctx = NULL;
recycle_bin_struct *recbin;
char *servicename;
char *tmp_str;
DEBUG(10, ("Called for service %s (%d) as user %s\n", service, SNUM(conn), user));
if (!(ctx = talloc_init_named("recycle bin"))) {
DEBUG(0, ("Failed to allocate memory in VFS module recycle_bin\n"));
return 0;
}
recbin = talloc(ctx,sizeof(recycle_bin_struct));
if ( recbin == NULL) {
DEBUG(0, ("Failed to allocate memory in VFS module recycle_bin\n"));
return -1;
}
recbin->ctx = ctx;
/* Set defaults */
recbin->repository = talloc_strdup(ctx, ".recycle");
ALLOC_CHECK(recbin->repository, error);
recbin->keep_dir_tree = False;
recbin->versions = False;
recbin->touch = False;
recbin->exclude = "";
recbin->exclude_dir = "";
recbin->noversions = "";
recbin->maxsize = 0;
/* parse configuration options */
servicename = talloc_strdup(recbin->ctx, lp_servicename(SNUM(conn)));
DEBUG(10, ("servicename = %s\n",servicename));
if ((tmp_str = lp_parm_string(servicename, "vfs_recycle_bin", "repository")) != NULL) {
recbin->repository = talloc_sub_conn(ctx, conn, tmp_str);
ALLOC_CHECK(recbin->repository, error);
trim_string(recbin->repository, "/", "/");
DEBUG(5, ("recycle.bin: repository = %s\n", recbin->repository));
}
if ((tmp_str = lp_parm_string(servicename, "vfs_recycle_bin", "keeptree")) != NULL) {
if (check_bool_param(tmp_str) == True)
recbin->keep_dir_tree = True;
DEBUG(5, ("recycle.bin: keeptree = %s\n", tmp_str));
}
if ((tmp_str = lp_parm_string(servicename, "vfs_recycle_bin", "versions")) != NULL) {
if (check_bool_param(tmp_str) == True)
recbin->versions = True;
DEBUG(5, ("recycle.bin: versions = %s\n", tmp_str));
}
if ((tmp_str = lp_parm_string(servicename, "vfs_recycle_bin", "touch")) != NULL) {
if (check_bool_param(tmp_str) == True)
recbin->touch = True;
DEBUG(5, ("recycle.bin: touch = %s\n", tmp_str));
}
if ((tmp_str = lp_parm_string(servicename, "vfs_recycle_bin", "maxsize")) != NULL) {
recbin->maxsize = strtoul(tmp_str, NULL, 10);
if (recbin->maxsize == 0) {
recbin->maxsize = -1;
DEBUG(5, ("recycle.bin: maxsize = -infinite-\n"));
} else {
DEBUG(5, ("recycle.bin: maxsize = %ld\n", (long int)recbin->maxsize));
}
}
if ((tmp_str = lp_parm_string(servicename, "vfs_recycle_bin", "exclude")) != NULL) {
recbin->exclude = talloc_strdup(ctx, tmp_str);
ALLOC_CHECK(recbin->exclude, error);
DEBUG(5, ("recycle.bin: exclude = %s\n", recbin->exclude));
}
if ((tmp_str = lp_parm_string(servicename,"vfs_recycle_bin", "exclude_dir")) != NULL) {
recbin->exclude_dir = talloc_strdup(ctx, tmp_str);
ALLOC_CHECK(recbin->exclude_dir, error);
DEBUG(5, ("recycle.bin: exclude_dir = %s\n", recbin->exclude_dir));
}
if ((tmp_str = lp_parm_string(servicename,"vfs_recycle_bin", "noversions")) != NULL) {
recbin->noversions = talloc_strdup(ctx, tmp_str);
ALLOC_CHECK(recbin->noversions, error);
DEBUG(5, ("recycle.bin: noversions = %s\n", recbin->noversions));
}
conn->vfs_private = (void *)recbin;
return default_vfs_ops.connect(conn, service, user);
error:
talloc_destroy(ctx);
return -1;
}
static void recycle_disconnect(struct connection_struct *conn)
{
DEBUG(10, ("Disconnecting VFS module recycle bin\n"));
if (conn->vfs_private) {
talloc_destroy(((recycle_bin_struct *)conn->vfs_private)->ctx);
conn->vfs_private = NULL;
}
default_vfs_ops.disconnect(conn);
}
static BOOL recycle_directory_exist(connection_struct *conn, const char *dname)
{
SMB_STRUCT_STAT st;
if (default_vfs_ops.stat(conn, dname, &st) == 0) {
if (S_ISDIR(st.st_mode)) {
return True;
}
}
return False;
}
static BOOL recycle_file_exist(connection_struct *conn, const char *fname)
{
SMB_STRUCT_STAT st;
if (default_vfs_ops.stat(conn, fname, &st) == 0) {
if (S_ISREG(st.st_mode)) {
return True;
}
}
return False;
}
/**
* Return file size
* @param conn connection
* @param fname file name
* @return size in bytes
**/
static SMB_OFF_T recycle_get_file_size(connection_struct *conn, const char *fname)
{
SMB_STRUCT_STAT st;
if (default_vfs_ops.stat(conn, fname, &st) != 0) {
DEBUG(0,("recycle.bin: stat for %s returned %s\n", fname, strerror(errno)));
return (SMB_OFF_T)0;
}
return(st.st_size);
}
/**
* Create directory tree
* @param conn connection
* @param dname Directory tree to be created
* @return Returns True for success
**/
static BOOL recycle_create_dir(connection_struct *conn, const char *dname)
{
int len;
mode_t mode;
char *new_dir = NULL;
char *tmp_str = NULL;
char *token;
char *tok_str;
BOOL ret = False;
mode = S_IREAD | S_IWRITE | S_IEXEC;
tmp_str = strdup(dname);
ALLOC_CHECK(tmp_str, done);
tok_str = tmp_str;
len = strlen(dname);
new_dir = (char *)malloc(len + 1);
ALLOC_CHECK(new_dir, done);
*new_dir = '\0';
/* Create directory tree if neccessary */
for(token = strtok(tok_str, "/"); token; token = strtok(NULL, "/")) {
safe_strcat(new_dir, token, len);
if (recycle_directory_exist(conn, new_dir))
DEBUG(10, ("recycle.bin: dir %s already exists\n", new_dir));
else {
DEBUG(5, ("recycle.bin: creating new dir %s\n", new_dir));
if (default_vfs_ops.mkdir(conn, new_dir, mode) != 0) {
DEBUG(1,("recycle.bin: mkdir failed for %s with error: %s\n", new_dir, strerror(errno)));
ret = False;
goto done;
}
}
safe_strcat(new_dir, "/", len);
}
ret = True;
done:
SAFE_FREE(tmp_str);
SAFE_FREE(new_dir);
return ret;
}
/**
* Check if needle is contained exactly in haystack
* @param haystack list of parameters separated by delimimiter character
* @param needle string to be matched exactly to haystack
* @return True if found
**/
static BOOL checkparam(const char *haystack, const char *needle)
{
char *token;
char *tok_str;
char *tmp_str;
BOOL ret = False;
if (haystack == NULL || strlen(haystack) == 0 || needle == NULL || strlen(needle) == 0) {
return False;
}
tmp_str = strdup(haystack);
ALLOC_CHECK(tmp_str, done);
token = tok_str = tmp_str;
for(token = strtok(tok_str, delimiter); token; token = strtok(NULL, delimiter)) {
if(strcmp(token, needle) == 0) {
ret = True;
goto done;
}
}
done:
SAFE_FREE(tmp_str);
return ret;
}
/**
* Check if needle is contained in haystack, * and ? patterns are resolved
* @param haystack list of parameters separated by delimimiter character
* @param needle string to be matched exectly to haystack including pattern matching
* @return True if found
**/
static BOOL matchparam(const char *haystack, const char *needle)
{
char *token;
char *tok_str;
char *tmp_str;
BOOL ret = False;
if (haystack == NULL || strlen(haystack) == 0 || needle == NULL || strlen(needle) == 0) {
return False;
}
tmp_str = strdup(haystack);
ALLOC_CHECK(tmp_str, done);
token = tok_str = tmp_str;
for(token = strtok(tok_str, delimiter); token; token = strtok(NULL, delimiter)) {
if (!unix_wild_match(token, needle)) {
ret = True;
goto done;
}
}
done:
SAFE_FREE(tmp_str);
return ret;
}
/**
* Touch access date
**/
static void recycle_touch(connection_struct *conn, const char *fname)
{
SMB_STRUCT_STAT st;
struct utimbuf tb;
time_t currtime;
if (default_vfs_ops.stat(conn, fname, &st) != 0) {
DEBUG(0,("recycle.bin: stat for %s returned %s\n", fname, strerror(errno)));
return;
}
currtime = time(&currtime);
tb.actime = currtime;
tb.modtime = st.st_mtime;
if (default_vfs_ops.utime(conn, fname, &tb) == -1 )
DEBUG(0, ("recycle.bin: touching %s failed, reason = %s\n", fname, strerror(errno)));
}
/**
* Check if file should be recycled
**/
static int recycle_unlink(connection_struct *conn, const char *inname)
{
recycle_bin_struct *recbin;
char *file_name = NULL;
char *path_name = NULL;
char *temp_name = NULL;
char *final_name = NULL;
char *base;
int i;
SMB_BIG_UINT dfree, dsize, bsize;
SMB_OFF_T file_size, space_avail;
BOOL exist;
int rc = -1;
file_name = strdup(inname);
ALLOC_CHECK(file_name, done);
if (conn->vfs_private)
recbin = (recycle_bin_struct *)conn->vfs_private;
else {
DEBUG(0, ("Recycle bin not initialized!\n"));
rc = default_vfs_ops.unlink(conn, file_name);
goto done;
}
if(!recbin->repository || *(recbin->repository) == '\0') {
DEBUG(3, ("Recycle path not set, purging %s...\n", file_name));
rc = default_vfs_ops.unlink(conn, file_name);
goto done;
}
/* we don't recycle the recycle bin... */
if (strncmp(file_name, recbin->repository, strlen(recbin->repository)) == 0) {
DEBUG(3, ("File is within recycling bin, unlinking ...\n"));
rc = default_vfs_ops.unlink(conn, file_name);
goto done;
}
file_size = recycle_get_file_size(conn, file_name);
/* it is wrong to purge filenames only because they are empty imho
* --- simo
*
if(fsize == 0) {
DEBUG(3, ("File %s is empty, purging...\n", file_name));
rc = default_vfs_ops.unlink(conn,file_name);
goto done;
}
*/
/* FIXME: this is wrong, we should check the hole size of the recycle bin is
* not greater then maxsize, not the size of the single file, also it is better
* to remove older files
*/
if(recbin->maxsize > 0 && file_size > recbin->maxsize) {
DEBUG(3, ("File %s exceeds maximum recycle size, purging... \n", file_name));
rc = default_vfs_ops.unlink(conn, file_name);
goto done;
}
/* FIXME: this is wrong: moving files with rename does not change the disk space
* allocation
*
space_avail = default_vfs_ops.disk_free(conn, ".", True, &bsize, &dfree, &dsize) * 1024L;
DEBUG(5, ("space_avail = %Lu, file_size = %Lu\n", space_avail, file_size));
if(space_avail < file_size) {
DEBUG(3, ("Not enough diskspace, purging file %s\n", file_name));
rc = default_vfs_ops.unlink(conn, file_name);
goto done;
}
*/
/* extract filename and path */
path_name = (char *)malloc(PATH_MAX);
ALLOC_CHECK(path_name, done);
*path_name = '\0';
safe_strcpy(path_name, file_name, PATH_MAX);
base = strrchr(path_name, '/');
if (base == NULL) {
base = file_name;
safe_strcpy(path_name, "/", PATH_MAX);
}
else {
*base = '\0';
base++;
}
DEBUG(10, ("recycle.bin: fname = %s\n", file_name)); /* original filename with path */
DEBUG(10, ("recycle.bin: fpath = %s\n", path_name)); /* original path */
DEBUG(10, ("recycle.bin: base = %s\n", base)); /* filename without path */
if (matchparam(recbin->exclude, base)) {
DEBUG(3, ("recycle.bin: file %s is excluded \n", base));
rc = default_vfs_ops.unlink(conn, file_name);
goto done;
}
/* FIXME: this check will fail if we have more than one level of directories,
* we shoud check for every level 1, 1/2, 1/2/3, 1/2/3/4 ....
* ---simo
*/
if (checkparam(recbin->exclude_dir, path_name)) {
DEBUG(3, ("recycle.bin: directory %s is excluded \n", path_name));
rc = default_vfs_ops.unlink(conn, file_name);
goto done;
}
temp_name = (char *)malloc(PATH_MAX);
ALLOC_CHECK(temp_name, done);
safe_strcpy(temp_name, recbin->repository, PATH_MAX);
/* see if we need to recreate the original directory structure in the recycle bin */
if (recbin->keep_dir_tree == True) {
safe_strcat(temp_name, "/", PATH_MAX);
safe_strcat(temp_name, path_name, PATH_MAX);
}
exist = recycle_directory_exist(conn, temp_name);
if (exist) {
DEBUG(10, ("recycle.bin: Directory already exists\n"));
} else {
DEBUG(10, ("recycle.bin: Creating directory %s\n", temp_name));
if (recycle_create_dir(conn, temp_name) == False) {
DEBUG(3, ("Could not create directory, purging %s...\n", file_name));
rc = default_vfs_ops.unlink(conn, file_name);
goto done;
}
}
final_name = (char *)malloc(PATH_MAX);
ALLOC_CHECK(final_name, done);
snprintf(final_name, PATH_MAX, "%s/%s", temp_name, base);
DEBUG(10, ("recycle.bin: recycled file name%s\n", temp_name)); /* new filename with path */
/* check if we should delete file from recycle bin */
if (recycle_file_exist(conn, final_name)) {
if (recbin->versions == False || matchparam(recbin->noversions, base) == True) {
DEBUG(3, ("recycle.bin: Removing old file %s from recycle bin\n", final_name));
if (default_vfs_ops.unlink(conn, final_name) != 0) {
DEBUG(1, ("recycle.bin: Error deleting old file: %s\n", strerror(errno)));
}
}
}
/* rename file we move to recycle bin */
i = 1;
while (recycle_file_exist(conn, final_name)) {
snprintf(final_name, PATH_MAX, "%s/Copy #%d of %s", temp_name, i++, base);
}
DEBUG(10, ("recycle.bin: Moving %s to %s\n", file_name, final_name));
rc = default_vfs_ops.rename(conn, file_name, final_name);
if (rc != 0) {
DEBUG(3, ("recycle.bin: Move error %d (%s), purging file %s (%s)\n", errno, strerror(errno), file_name, final_name));
rc = default_vfs_ops.unlink(conn, file_name);
goto done;
}
/* touch access date of moved file */
if (recbin->touch == True )
recycle_touch(conn, final_name);
done:
SAFE_FREE(file_name);
SAFE_FREE(path_name);
SAFE_FREE(temp_name);
SAFE_FREE(final_name);
return rc;
}