diff options
| -rw-r--r-- | examples/VFS/block/smb.conf | 7 | ||||
| -rw-r--r-- | examples/VFS/netatalk/Makefile.in | 28 | ||||
| -rw-r--r-- | examples/VFS/netatalk/README | 18 | ||||
| -rw-r--r-- | examples/VFS/netatalk/netatalk.c | 430 | 
4 files changed, 476 insertions, 7 deletions
diff --git a/examples/VFS/block/smb.conf b/examples/VFS/block/smb.conf index 368155f1f8..931196f402 100644 --- a/examples/VFS/block/smb.conf +++ b/examples/VFS/block/smb.conf @@ -4,10 +4,3 @@     browseable = yes     writable = yes - - - - - - - diff --git a/examples/VFS/netatalk/Makefile.in b/examples/VFS/netatalk/Makefile.in new file mode 100644 index 0000000000..a9a5d69b51 --- /dev/null +++ b/examples/VFS/netatalk/Makefile.in @@ -0,0 +1,28 @@ +CC		= @CC@ +CFLAGS		= @CFLAGS@ +CPPFLAGS	= @CPPFLAGS@ +LDFLAGS		= @LDFLAGS@ +LDSHFLAGS	= -shared +srcdir		= @builddir@ +FLAGS		=  $(CFLAGS) -Iinclude -I$(srcdir)/include -I$(srcdir)/ubiqx -I$(srcdir)/smbwrapper  -I. $(CPPFLAGS) -I$(srcdir) + +VFS_OBJS	= netatalk.so + +# Default target + +default: $(VFS_OBJS) + +# Pattern rules + +%.so: %.o +	$(CC) $(LDSHFLAGS) $(LDFLAGS) -o $@ $< + +%.o: %.c +	$(CC) $(FLAGS) -c $< + +# Misc targets + +clean: +	rm -rf .libs +	rm -f core *~ *% *.bak \ +	$(VFS_OBJ) $(VFS_OBJS) diff --git a/examples/VFS/netatalk/README b/examples/VFS/netatalk/README new file mode 100644 index 0000000000..70f6eea316 --- /dev/null +++ b/examples/VFS/netatalk/README @@ -0,0 +1,18 @@ +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. diff --git a/examples/VFS/netatalk/netatalk.c b/examples/VFS/netatalk/netatalk.c new file mode 100644 index 0000000000..353be36e6f --- /dev/null +++ b/examples/VFS/netatalk/netatalk.c @@ -0,0 +1,430 @@ +/*  + * 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")); +}  | 
