hi there!

this is simple netatalk vfs module against stackable VFS
interface.

regards,
-- 
Alexey Kotovich
/* Software developer, SaM-Solutions Ltd. */
-- fortune says --
Why use Windows, since there is a door?
/* 
 * AppleTalk VFS module for Samba. 
 *
 * Copyright (C) Alexey 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 APPLEPARENT	".Parent"
#define ADOUBLEMODE	0777
#define HEADERMODE	0775

#define AID_DATA	1
#define AID_RESOURCE	2
#define	AID_REALNAME	3
#define	AID_COMMENT	4
#define AID_ICON_MONO	5
#define AID_ICON_COLOR	6
#define AID_INFO_FILE	7 /* for v1? */
		//#define AID_INFO_FILE   8 /* for v2 */
#define	AID_INFO_FINDER	9
#define	AID_INFO_MAC	10
#define	AID_INFO_PRODOS	11
#define AID_INFO_MSDOS	12
#define AID_AFP_NAME	13
#define	AID_AFP_INFO	14
#define	AID_AFP_DID	15	/* directory ID */

#define FILE_TYPE	"TEXT"
#define FILE_CREATOR	"UNIX"
#define MAX_ENTRIES	16

#define ADOUBLE_MAGIC		{0x00, 0x05, 0x16, 0x07}
#define ADOUBLE_VERSION1	{0x00, 0x01, 0x00, 0x00}
#define ADOUBLE_VERSION2	{0x00, 0x02, 0x00, 0x00}
#define ADOUBLE_LOCK		{0x00, 0x00, 0x01, 0xa0}
#define ADOUBLE_MAX_FNAME	255
#define ADOUBLE_MAX_COMMENT	200
#define ADOUBLE_COMMENT		"created by Samba"

typedef uint8 a_uint8;
typedef uint8 a_uint16[2];
typedef uint8 a_uint32[4];
typedef uint8 a_int8;
typedef uint8 a_int16[2];
typedef uint8 a_int32[4];

typedef struct adouble_entry
{
	a_uint32	eid;
	a_uint32	offset;
	a_uint32	length;
	size_t		item_size;
	void		*item;
} a_entry;
#define ADOUBLE_ENTRY_SIZE 12

typedef struct adouble_header 
{
	a_uint32	magic;
	a_uint32	version;
	a_uint8		empty[16];
	a_uint16	num_entries;
	a_entry		entries[MAX_ENTRIES];
} a_header;
#define ADOUBLE_HEADER_SIZE 26
#define ADOUBLE_HEADER_FULL_SIZE (26 + (MAX_ENTRIES * 12))

typedef struct adouble_file_info
{
	a_int32	date_create;
	a_int32	date_modify;
	a_int32	date_backup;
	a_int32	date_access;
} a_filei;
#define ADOUBLE_FILEI_SIZE 16

typedef struct adouble_file_attr
{
	a_uint8 empty[3];
	a_uint8 attrs;
} a_filea;
#define ADOUBLE_FILEA_SIZE 4

typedef struct adouble_finder_info
{
	a_uint8  file_type[4];
	a_uint8  file_creator[4];
	a_uint16 flags1;
	a_int16  file_y_pos;	/* location in the folder */
	a_int16  file_x_pos;
	a_int16  file_folder;	/* window */
	/* extended finder info */
	a_int16  icon_id;
	a_int16  empty[3];
	a_int8   flags2;
	a_int8   flags3;
	a_int16  comment_id;
	a_int32  home_id; /* home directory ID */
} a_finderi;
#define ADOUBLE_FINDERI_SIZE 32

#define MAX_UINT16	65536U
#define MAX_UINT32	4294967296U
#define MAX_INT16	32767
#define MIN_INT16	-32768
#define MAX_INT32	2147483647
#define MIN_INT32	-2147483647

#define UINT16_BE
#define UINT32_BE
#define UINT32_LE

#ifdef UINT16_BE
static __inline__ void uint16_be_encode(uint8 *field, uint16 value)
{
	field[0] = (value >> 8) & 0xff;
	field[1] = value & 0xff;
}
#endif

#ifdef UINT32_BE
static __inline__ void uint32_be_encode(uint8 *field, uint32 value)
{
	field[0] = (value >> 24) & 0xff;
	field[1] = (value >> 16) & 0xff;
	field[2] = (value >> 8) & 0xff;
	field[3] = value & 0xff;
}
#endif

#ifdef INT16_BE
static __inline__ void int16_be_encode(uint8 *field, int16 value)
{
	uint16 u_value = value >= 0 ? value : value + MAX_UINT16;

	field[0] = (u_value >> 8) & 0xff;
	field[1] = value & 0xff;	
}
#endif

#ifdef INT32_BE
static __inline__ void int32_be_encode(uint8 *field, int32 value)
{
	uint32 u_value = value >= 0 ? value : value + MAX_UINT32;

	field[0] = (u_value >> 24) & 0xff;
	field[1] = (u_value >> 16) & 0xff;
	field[2] = (u_value >> 8) & 0xff;
	field[3] = u_value & 0xff;
}
#endif

#ifdef UIN16_LE
static __inline__ uint16 uint16_le_encode(uint8 *field)
{
	return (uint16) field[1] << 8 | (uint16) field[0];
}
#endif

#ifdef UINT32_LE
static __inline__ uint32 uint32_le_encode(uint8 *field)
{
	uint16 big = 0;
	uint16 little = 0;
	uint32 u_value = 0;
	
	big    = (uint16) (field[1] << 8) | (uint16) (field[0]);
	little = (uint16) (field[2] << 8) | (uint16) (field[3]);

	u_value |= little;
	
	if (big != 0)
		u_value |= (uint32) (big << 8);

	return u_value;
}
#endif

#ifdef INT16_LE
static __inline__ int16 int16_le_encode(uint8 *field)
{
	uint16 u_value = field[0] | field[1];

	return u_value <= MAX_INT16 ? (int16) u_value : (int16) (u_value + MIN_INT16) - MAX_INT16 - 1;
}
#endif

#ifdef INT32_LE
static __inline__ int32 int32_le_encode(uint8 *field)
{
	uint32 u_value = field[0] | field[1] | field[2] | field[3];

	return u_value <= MAX_INT32 ? (int32) u_value : (int32) (u_value + MIN_INT32) - MAX_INT32 - 1;
}
#endif

/* 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_set_perms(const char *path, SMB_STRUCT_STAT *info);
static int atalk_create_ddir(TALLOC_CTX *ctx, const char *path, 
  const char *origpath, SMB_STRUCT_STAT *info);

static int atalk_create_file(TALLOC_CTX *ctx, const char *path, 
  const char *type, SMB_STRUCT_STAT *info);

static a_header *atalk_init_header(TALLOC_CTX *ctx, uint8 version, 
  const char *type, const char *fname, SMB_STRUCT_STAT *info);

static a_header *atalk_get_header(TALLOC_CTX *ctx, const char *path);
static int atalk_flush_file(a_header *header, const char *path);
static int atalk_set_fname(TALLOC_CTX *ctx, const char *path, const char *name);
static int atalk_unlink_file(const char *path);
		/*
static int atalk_lock_file(TALLOC_CTX *ctx, const char *path);
static void atalk_dump_hex(char *buf, size_t size);
static char *atalk_rep(TALLOC_CTX *ctx, char *instr, char ch);
		*/

static struct vfs_ops default_vfs_ops;	/* For passthrough operation */
static int atalk_id;

		/*
static void atalk_dump_hex(char *buf, size_t size)
{
	int i = 0;
	
	for (i = 0; i < size; i ++)
		DEBUG(0, ("0x%02x ", buf[i]));
	DEBUG(0, ("\n"));
}
		*/
/* remove repeated characters */
/*
static char *atalk_rep(TALLOC_CTX *ctx, char *instr, char ch)
{
	int   i = 0;
	int   j = 0;
	int   n = 0;
	int   len = 0;
	char *outstr;

	if (!instr) return 0;

	if (!(outstr = talloc_zero(ctx, strlen(instr) + 1)))
		return 0;

	len = strlen(instr);

	if (instr[0] == ch) {
		outstr[0] = instr[0];
		n = 1;
	}
	
	for (i = n, j = n; i < len; i ++)
		if (instr[i] != ch) {
			outstr[j] = instr[i];
			j ++;
		} else {
			if (i > 0) {
				outstr[j] = instr[i];
				j ++;
				while (instr[i] == ch && i < len)
					i ++;
				i --;
			}
		}
	return outstr;
}
*/
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;

/*	DEBUG(3, ("ATALK: PATH: %s[%s]\n", path, fname)); */

	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]);
	}
/*	DEBUG(3, ("ATALK: DEBUG:\n%s\n%s\n", *orig_path, *adbl_path)); */
	sys_lstat(*adbl_path, adbl_info);
	return 0;
}

static int atalk_set_perms(const char *path, SMB_STRUCT_STAT *info)
{
	become_root();
	if ((chown(path, info->st_uid, info->st_gid) != 0)) {
		unbecome_root();
		return -1;
	}

	if ((chmod(path, ADOUBLEMODE) != 0)) {
		unbecome_root();
		return -1;
	}
	unbecome_root();
	return 0;
}

static int atalk_create_ddir(TALLOC_CTX *ctx, const char *path,
  const char *origpath, SMB_STRUCT_STAT *info)
{
	int ptr = 0;
	char *dname = 0;
	char *fname = 0;

	if (!ctx || !path) return -1;

	ptr = atalk_get_path_ptr((char *) path);

	if (!path[ptr + 1]) {
		dname = (char *) path;
	} else {
		if (!(dname = talloc_memdup(ctx, path, ptr + 1)))
			return -1;
		dname[ptr] = '\0';
	}

	become_root();
	if ((mkdir(dname, info->st_mode) < 0) && (errno != EEXIST)) {
		DEBUG(3, ("ATALK: an error on creating '%s': %s\n", dname, strerror(errno)));
		unbecome_root();
		return -1;
	}
	unbecome_root();

	atalk_set_perms(dname, info);

	if (!(fname = talloc_asprintf(ctx, "%s/%s", dname, APPLEPARENT)))
		return -1;

	if ((atalk_create_file(ctx, fname, 0, info) != 0))
		return -1;

	ptr = atalk_get_path_ptr((char *) origpath);

	if (!origpath[ptr + 1])
		dname = (char *) origpath;
	else {
		if (!(dname = talloc_memdup(ctx, origpath, ptr + 1)))
			return 0;
		dname[ptr] = '\0';
	}

	ptr = atalk_get_path_ptr(dname);
	atalk_set_fname(ctx, fname, &dname[ptr + 1]);

	return 0;
}

static int atalk_create_file(TALLOC_CTX *ctx, const char *path, 
  const char *type, SMB_STRUCT_STAT *info)
{
	int ptr = 0;
	a_header *header;

	if (!ctx || !path) return -1;

	ptr = atalk_get_path_ptr((char *) path);

	if (!path[ptr + 1]) return -1;

	if (!(header = atalk_init_header(ctx, 1, type, &path[ptr + 1], info)))
		return -1;
	
	if ((atalk_flush_file(header, path) != 0))
		return -1;

	atalk_set_perms(path, info);
	return 0;	
}

static int atalk_flush_file(a_header *header, const char *path)
{
	int i = 0;
	uint32 eid = 0;
	FILE *file = 0;

	if (!header || !path) return -1;

	become_root();
	if (!(file = fopen(path, "w"))) {
		DEBUG(3, ("ATALK: Unable to open file %s: ERRNO: %s\n", path, strerror(errno)));
		unbecome_root();
		return -1;
	}

	fwrite(header, 1, ADOUBLE_HEADER_SIZE, file);

	for (i = 0; i < MAX_ENTRIES; i ++) {
		eid = uint32_le_encode(header->entries[i].eid);
		if (eid != 0) {
			fwrite(&header->entries[i], 1, ADOUBLE_ENTRY_SIZE, file);
		}
	}
	for (i = 0; i < MAX_ENTRIES; i ++) {
		eid = uint32_le_encode(header->entries[i].eid);
		if (eid != 0) {
			fwrite(header->entries[i].item, 1, 
			  header->entries[i].item_size, file);
		}
	}
	fclose(file);
	unbecome_root();
	return 0;
}

static int atalk_unlink_file(const char *path)
{
	int ret = 0;

	become_root();
	ret = unlink(path);
	unbecome_root();
	
	return ret;
}

static a_header *atalk_init_header(TALLOC_CTX *ctx, uint8 version, 
  const char *type, const char *fname, SMB_STRUCT_STAT *info)
{
	int    i     = 0;
	uint32 p     = 0;
	uint8 magic[] = ADOUBLE_MAGIC;
	uint8 *a_version = 0;
	uint8 version1[] = ADOUBLE_VERSION1;
	uint8 version2[] = ADOUBLE_VERSION2;

	a_header *header;

	if (!fname) return 0;

	if (!(header = (a_header *) talloc_zero(ctx, sizeof(a_header))))
		return 0;

	for (i = 0; i < sizeof(header->magic); i ++)
		header->magic[i] = magic[i];

	if (version == 1) {
		a_version = version1;
	} else if (version == 2) {
		a_version = version2;
	} else {
		DEBUG(3, ("ATALK: WARNING: unknown header version: %d\n", version));
	}
	for (i = 0; i < sizeof(header->version); i ++)
		header->version[i] = a_version[i];	

	memset(header->empty, 0, sizeof(header->empty));
	p += ADOUBLE_HEADER_SIZE;
	if (version == 1) {
		/* for v1 we have 5 entries */
		uint16_be_encode(header->num_entries, 5);
		p += 5 * ADOUBLE_ENTRY_SIZE;
		/* real name, ID = 3, ADOUBLE_MAX_FNAME bytes will be reserved */
		uint32_be_encode(header->entries[AID_REALNAME].eid, AID_REALNAME);
		uint32_be_encode(header->entries[AID_REALNAME].offset, p);
		uint32_be_encode(header->entries[AID_REALNAME].length, strlen(fname));
		p += ADOUBLE_MAX_FNAME;
		if (!(header->entries[AID_REALNAME].item = talloc_zero(ctx, ADOUBLE_MAX_FNAME))) {
			DEBUG(3, ("ATALK: WARNING: Unable allocate memory for entry ID %d", AID_REALNAME));			
		} else {
			header->entries[AID_REALNAME].item_size = ADOUBLE_MAX_FNAME;
			memcpy(header->entries[AID_REALNAME].item, fname, strlen(fname));
		}
		/* comment, ID = 4, ADOUBLE_MAX_COMMENT bytes will be reserved */
		uint32_be_encode(header->entries[AID_COMMENT].eid, AID_COMMENT);
		uint32_be_encode(header->entries[AID_COMMENT].offset, p);
		uint32_be_encode(header->entries[AID_COMMENT].length, sizeof(ADOUBLE_COMMENT));
		p += ADOUBLE_MAX_COMMENT;
		if (!(header->entries[AID_COMMENT].item = 
		      talloc_zero(ctx, ADOUBLE_MAX_COMMENT))) {
			DEBUG(3, ("ATALK: WARNING: Unable allocate memory for entry ID %d", AID_COMMENT));
		} else {
			header->entries[AID_COMMENT].item_size = ADOUBLE_MAX_COMMENT;
			memcpy(header->entries[AID_COMMENT].item, 
			  ADOUBLE_COMMENT, sizeof(ADOUBLE_COMMENT));
		}
		/* file dates info, ID = 8 */
		uint32_be_encode(header->entries[AID_INFO_FILE].eid, AID_INFO_FILE);
		uint32_be_encode(header->entries[AID_INFO_FILE].offset, p);
		uint32_be_encode(header->entries[AID_INFO_FILE].length, ADOUBLE_FILEI_SIZE);		
		p += ADOUBLE_FILEI_SIZE;
		if (!(header->entries[AID_INFO_FILE].item = 
		      (a_filei *) talloc_zero(ctx, ADOUBLE_FILEI_SIZE))) {
			DEBUG(3, ("ATALK: WARNING: Unable allocate memory for entry ID %d", AID_INFO_FILE));
		} else {
			a_filei *filei = 0;
			header->entries[AID_INFO_FILE].item_size = ADOUBLE_FILEI_SIZE;
			memset(header->entries[AID_INFO_FILE].item, 0, ADOUBLE_FILEI_SIZE);
			filei = header->entries[AID_INFO_FILE].item;
			uint32_be_encode(filei->date_create, info->st_ctime);
			uint32_be_encode(filei->date_modify, info->st_mtime);
		}
		/* finder info, ID = 9 */
		uint32_be_encode(header->entries[AID_INFO_FINDER].eid, AID_INFO_FINDER);
		uint32_be_encode(header->entries[AID_INFO_FINDER].offset, p);
		uint32_be_encode(header->entries[AID_INFO_FINDER].length, ADOUBLE_FINDERI_SIZE);		
		p += ADOUBLE_FINDERI_SIZE;
		if (!(header->entries[AID_INFO_FINDER].item = 
		      (a_finderi *) talloc_zero(ctx, ADOUBLE_FINDERI_SIZE))) {
			DEBUG(3, ("ATALK: WARNING: Unable allocate memory for entry ID %d", AID_INFO_FILE));
		} else {
			a_finderi *finderi = header->entries[AID_INFO_FINDER].item;
			header->entries[AID_INFO_FINDER].item_size = ADOUBLE_FINDERI_SIZE;
			if (type)
				memcpy(finderi->file_type, type, strlen(type));
			else
				memset(finderi->file_type, 0, ADOUBLE_FINDERI_SIZE);
			memcpy(finderi->file_creator, FILE_CREATOR, strlen(FILE_CREATOR));
		}
		/* resource, ID = 2 */
		uint32_be_encode(header->entries[AID_RESOURCE].eid, AID_RESOURCE);
		uint32_be_encode(header->entries[AID_RESOURCE].offset, p);
		uint32_be_encode(header->entries[AID_RESOURCE].length, 0);
		header->entries[AID_RESOURCE].item = 0;
		header->entries[AID_RESOURCE].item_size = 0;		
	} else if (version == 2) {
		/* for v2 we have 9 entries */
		/* not implemented */
		return 0;
	}
	return header;
}

static a_header *atalk_get_header(TALLOC_CTX *ctx, const char *path)
{
	uint32  ver  = 0;
	int     i    = 0;
	char   *ptr  = 0;
	char   *off  = 0;
	char   *buf  = 0;
	FILE   *file = 0;
	SMB_STRUCT_STAT info;	
	a_header *header;

	if (!ctx || !path) return 0;

	sys_lstat(path, &info);

	if (!S_ISREG(info.st_mode)) {
		DEBUG(3, ("ATALK: GET HEADER: %s does not exist\n", path));
		return 0;
	}

	if (!(buf = talloc_zero(ctx, info.st_size)))
		return 0;
	
	if (!(header = (a_header *) talloc_zero(ctx, sizeof(a_header))))
		return 0;

	become_root();
	if (!(file = fopen(path, "r+"))) {
		unbecome_root();
		return 0;
	}
	
	fread(buf, 1, info.st_size, file);
	fclose(file);
	unbecome_root();
	
	memcpy(header, buf, ADOUBLE_HEADER_SIZE);

	ver = uint32_le_encode(header->version);

	if (ver != 0x10000) {
		DEBUG(3, ("ATALK: Unsupported header version: 0x%x\n", ver));
		return 0;
	}
	
	off = buf;
	off += ADOUBLE_HEADER_SIZE;
	for (i = 0; i < MAX_ENTRIES; i ++) {
		switch (i) {
		  case AID_RESOURCE:
			memcpy(&header->entries[i], off, ADOUBLE_ENTRY_SIZE);
			header->entries[i].item = 0;
			header->entries[i].item_size = 0;
			off += ADOUBLE_ENTRY_SIZE;
			break;
		  case AID_REALNAME:			
			memcpy(&header->entries[i], off, ADOUBLE_ENTRY_SIZE);
			ptr = buf;
			ptr += uint32_le_encode(header->entries[i].offset);
			header->entries[i].item_size = ADOUBLE_MAX_FNAME;
			if (!(header->entries[i].item = 
			      (char *) talloc_zero(ctx, ADOUBLE_MAX_FNAME)))
				header->entries[i].item = 0;
			else
				memcpy(header->entries[i].item, ptr, ADOUBLE_MAX_FNAME);
			off += ADOUBLE_ENTRY_SIZE;
			break;
		  case AID_COMMENT:
			memcpy(&header->entries[i], off, ADOUBLE_ENTRY_SIZE);
			ptr = buf;
			ptr += uint32_le_encode(header->entries[i].offset);
			header->entries[i].item_size = ADOUBLE_MAX_COMMENT;
			if (!(header->entries[i].item = 
			      (char *) talloc_zero(ctx, ADOUBLE_MAX_COMMENT)))
				header->entries[i].item = 0;
			else
				memcpy(header->entries[i].item, ptr, ADOUBLE_MAX_COMMENT);
			off += ADOUBLE_ENTRY_SIZE;
			break;
		  case AID_INFO_FILE:
			memcpy(&header->entries[i], off, ADOUBLE_ENTRY_SIZE);
			ptr = buf;
			ptr += uint32_le_encode(header->entries[i].offset);
			header->entries[i].item_size = ADOUBLE_FILEI_SIZE;
			if (!(header->entries[i].item = 
			      (a_filei *) talloc_zero(ctx, ADOUBLE_FILEI_SIZE)))
				header->entries[i].item = 0;
			else
				memcpy(header->entries[i].item, ptr, ADOUBLE_FILEI_SIZE);
			off += ADOUBLE_ENTRY_SIZE;
			break;
		  case AID_INFO_FINDER:
			memcpy(&header->entries[i], off, ADOUBLE_ENTRY_SIZE);
			ptr = buf;
			ptr += uint32_le_encode(header->entries[i].offset);
			header->entries[i].item_size = ADOUBLE_FINDERI_SIZE;
			if (!(header->entries[i].item = 
			      (a_finderi *) talloc_zero(ctx, ADOUBLE_FINDERI_SIZE)))
				header->entries[i].item = 0;
			else
				memcpy(header->entries[i].item, ptr, ADOUBLE_FINDERI_SIZE);
			off += ADOUBLE_ENTRY_SIZE;
			break;
		}
	}
	return header;
}
		/*
static int atalk_lock_file(TALLOC_CTX *ctx, const char *path)
{
	a_header *header = 0;
	a_filei  *filei  = 0;
	uint8 lock_info[] = ADOUBLE_LOCK;

	if (!ctx || !path) return -1;

	if (!(header = atalk_get_header(ctx, path)))
		return -1;

	filei = (a_filei *) header->entries[AID_INFO_FILE].item;
	memcpy(filei->date_access, lock_info, 4);

	return 0;
}
		*/
static int atalk_set_fname(TALLOC_CTX *ctx, const char *path, const char *name)
{
	size_t len;
	a_header *header = 0;
	
	if (!ctx || !path || !name) return -1;

	if (!(header = atalk_get_header(ctx, path)))
		return -1;

	len = strlen(name);

	uint32_be_encode(header->entries[AID_REALNAME].length, len);

	memset(header->entries[AID_REALNAME].item, 0, ADOUBLE_MAX_FNAME);
	memcpy(header->entries[AID_REALNAME].item, name, len);

	if ((atalk_flush_file(header, path) != 0))
		return -1;

	return 0;
}

/* Disk operations */

/* Directory operations */

DIR *atalk_opendir(struct connection_struct *conn, const char *fname)
{
	DIR *ret = 0;
	
	ret = default_vfs_ops.opendir(conn, fname);

	return ret;
}

int atalk_mkdir(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.mkdir(conn, path, mode);
	
	if (!conn || !path) return ret;

	if (!(ctx = talloc_init_named("make_directory")))
		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)) {
		DEBUG(3, ("ATALK: %s has passed..\n", adbl_path));		
		goto exit_mkdir;
	}

	if (S_ISDIR(adbl_info.st_mode) || S_ISREG(adbl_info.st_mode)) {
		DEBUG(3, ("ATALK: %s has passed..\n", adbl_path));		
		goto exit_mkdir;
	}

	atalk_create_ddir(ctx, adbl_path, orig_path, &orig_info);

exit_mkdir:
	talloc_destroy(ctx);
	return ret;
}

/* File operations */

int atalk_open(struct connection_struct *conn, const char *fname, int flags, 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.open(conn, fname, flags, mode);

	if (!conn || !fname) return ret;

	if (!(ctx = talloc_init_named("open_file")))
		return ret;

	if (atalk_build_paths(ctx, conn->origpath, fname, &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_open;
	}

	if (S_ISDIR(adbl_info.st_mode) || S_ISREG(adbl_info.st_mode)) {
		DEBUG(3, ("ATALK: %s has passed..\n", adbl_path));		
		goto exit_open;
	}

	if ((atalk_create_ddir(ctx, adbl_path, orig_path, &orig_info) != 0))
		goto exit_open;

	atalk_create_file(ctx, adbl_path, "TEXT", &orig_info);

exit_open:
	talloc_destroy(ctx);
	return ret;
}
		/*
int atalk_close(struct files_struct *fsp, int fd)
{
	int ret = 0;
	
	DEBUG(10, ("ATALK: ENTERED: CLOSE: %d\n", fd));
	ret = default_vfs_ops.close(fsp, fd);

	return ret;
}
		*/
int atalk_rename(struct connection_struct *conn, const char *old, const char *new)
{
	int ret = 0;
	int ptr = 0;
	char *adbl_path_old = 0;
	char *orig_path_old = 0;
	char *adbl_path_new = 0;
	char *orig_path_new = 0;
	SMB_STRUCT_STAT adbl_info_old;
	SMB_STRUCT_STAT orig_info_old;
	SMB_STRUCT_STAT adbl_info_new;
	SMB_STRUCT_STAT orig_info_new;
	TALLOC_CTX *ctx;

	ret = default_vfs_ops.rename(conn, old, new);
	
	if (!conn || !old || !new) return ret;

	if (!(ctx = talloc_init_named("rename_file")))
		return ret;

	if (atalk_build_paths(ctx, conn->origpath, old, &adbl_path_old, &orig_path_old,
	  &adbl_info_old, &orig_info_old) != 0)
		return ret;

	if (atalk_build_paths(ctx, conn->origpath, new, &adbl_path_new, &orig_path_new,
	  &adbl_info_new, &orig_info_new) != 0)
		return ret;

	if (!S_ISDIR(orig_info_new.st_mode) && !S_ISREG(orig_info_new.st_mode)) {
		DEBUG(3, ("ATALK: %s has passed..\n", orig_path_new));		
		goto exit_rename;
	}

	if (atalk_create_ddir(ctx, adbl_path_new, orig_path_new, &orig_info_new) != 0)
		goto exit_rename;

	/* check whether the goal is a directory */
	if (S_ISDIR(orig_info_new.st_mode)) {
		if (!(adbl_path_new = 
		      talloc_asprintf_append(ctx, adbl_path_new, APPLEPARENT)))
			goto exit_rename;

		ptr = atalk_get_path_ptr((char *) new);

		/* just rename */
		atalk_set_fname(ctx, adbl_path_new, &new[ptr + 1]);
		goto exit_rename;
	}

	if (atalk_create_file(ctx, adbl_path_new, "TEXT", &orig_info_new) != 0)
		goto exit_rename;

	atalk_unlink_file(adbl_path_old);

exit_rename:
	talloc_destroy(ctx);
	return ret;
}

int atalk_unlink(struct connection_struct *conn, const char *path)
{
	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.unlink(conn, path);
	
	if (!conn || !path) 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;
}

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;
}

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;
}
		/*
BOOL atalk_lock(struct files_struct *fsp, int fd, int op, SMB_OFF_T offset, SMB_OFF_T count, int type)
{
	BOOL 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.lock(fsp, fd, op, offset, count, type);

	DEBUG(0, ("ATALK: ENTERED: LOCK: %d: NAME: %s\nPATH: %s\nDIR: %s\n", 
		fd, fsp->fsp_name, fsp->conn->origpath, fsp->conn->dirpath));

	return ret;
}
		*/

static vfs_op_tuple atalk_ops[] = {
    
	/* Directory operations */

	{atalk_opendir, 	SMB_VFS_OP_OPENDIR, 	SMB_VFS_LAYER_TRANSPARENT},
	{atalk_mkdir, 		SMB_VFS_OP_MKDIR, 	SMB_VFS_LAYER_TRANSPARENT},

	/* File operations */

	{atalk_open, 		SMB_VFS_OP_OPEN, 	SMB_VFS_LAYER_TRANSPARENT},
	{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_netatalk(int *vfs_version, struct vfs_ops *def_vfs_ops, int vfs_id)
{
	*vfs_version = SMB_VFS_INTERFACE_VERSION;
	memcpy(&default_vfs_ops, def_vfs_ops, sizeof(struct vfs_ops));
	
	atalk_id = vfs_id;

	DEBUG(3, ("ATALK: vfs module loaded\n"));
	return atalk_ops;
}

/* VFS finalization function. */
void vfs_done_netatalk(connection_struct *conn)
{
	DEBUG(3, ("ATALK: vfs module unloaded\n"));
}

Reply via email to