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