From: bfernhomberg Date: Wed, 9 Jun 2004 01:15:19 +0000 (+0000) Subject: write a .volinfo file containing volume setup to .AppleDesktop, for shell utils and... X-Git-Tag: netatalk-2-0-beta2~38 X-Git-Url: https://arthur.barton.de/cgi-bin/gitweb.cgi?p=netatalk.git;a=commitdiff_plain;h=d6aad9f4b9c9682a9bb47bd237008e0288aee892 write a .volinfo file containing volume setup to .AppleDesktop, for shell utils and 3rd party --- diff --git a/etc/afpd/volume.c b/etc/afpd/volume.c index 069e0c54..57d6d148 100644 --- a/etc/afpd/volume.c +++ b/etc/afpd/volume.c @@ -1,5 +1,5 @@ /* - * $Id: volume.c,v 1.51.2.7.2.30 2004-05-11 08:30:07 didg Exp $ + * $Id: volume.c,v 1.51.2.7.2.31 2004-06-09 01:15:21 bfernhomberg Exp $ * * Copyright (c) 1990,1993 Regents of The University of Michigan. * All Rights Reserved. See COPYRIGHT. @@ -141,13 +141,47 @@ typedef struct _special_folder { static const _special_folder special_folders[] = { {"Network Trash Folder", 1, 0777, 1}, {"Temporary Items", 1, 0777, 1}, + {".AppleDesktop", 1, 0777, 0}, #if 0 {"TheFindByContentFolder", 0, 0, 1}, {"TheVolumeSettingsFolder", 0, 0, 1}, #endif {NULL, 0, 0, 0}}; +typedef struct _volopt_name { + const u_int32_t option; + const char *name; +} _vol_opt_name; + +static const _vol_opt_name vol_opt_names[] = { + {AFPVOL_A2VOL, "PRODOS"}, /* prodos volume */ + {AFPVOL_CRLF, "CRLF"}, /* cr/lf translation */ + {AFPVOL_NOADOUBLE, "NOADOUBLE"}, /* don't create .AppleDouble by default */ + {AFPVOL_RO, "READONLY"}, /* read-only volume */ + {AFPVOL_MSWINDOWS, "MSWINDOWS"}, /* deal with ms-windows yuckiness. this is going away. */ + {AFPVOL_NOHEX, "NOHEX"}, /* don't do :hex translation */ + {AFPVOL_USEDOTS, "USEDOTS"}, /* use real dots */ + {AFPVOL_LIMITSIZE, "LIMITSIZE"}, /* limit size for older macs */ + {AFPVOL_MAPASCII, "MAPASCII"}, /* map the ascii range as well */ + {AFPVOL_DROPBOX, "DROPBOX"}, /* dropkludge dropbox support */ + {AFPVOL_NOFILEID, "NOFILEID"}, /* don't advertise createid resolveid and deleteid calls */ + {AFPVOL_NOSTAT, "NOSTAT"}, /* advertise the volume even if we can't stat() it + * maybe because it will be mounted later in preexec */ + {AFPVOL_UNIX_PRIV, "UNIXPRIV"}, /* support unix privileges */ + {AFPVOL_NODEV, "NODEV"}, /* always use 0 for device number in cnid calls */ + {0, NULL} +}; + +static const _vol_opt_name vol_opt_casefold[] = { + {AFPVOL_MTOUUPPER, "MTOULOWER"}, + {AFPVOL_MTOULOWER, "MTOULOWER"}, + {AFPVOL_UTOMUPPER, "UTOMUPPER"}, + {AFPVOL_UTOMLOWER, "UTOMLOWER"}, + {0, NULL} +}; + static void handle_special_folders (const struct vol *); +static int savevoloptions (const struct vol *); static __inline__ void volfree(struct vol_option *options, const struct vol_option *save) @@ -1713,6 +1747,7 @@ int ibuflen, *rbuflen; if (ret == AFP_OK) { handle_special_folders( volume ); + savevoloptions( volume); /* * If you mount a volume twice, the second time the trash appears on @@ -2139,3 +2174,118 @@ static void handle_special_folders (const struct vol * vol) } } +/* + * Save the volume options to a file, used by + * shell utilities. + * Writing the file everytime a volume is opened is + * unnecessary, but it shouldn't hurt much. + */ +static int savevoloptions (const struct vol *vol) +{ + char buf[16348]; + char item[MAXPATHLEN]; + int fd; + int ret = 0; + struct flock lock; + const _vol_opt_name *op = &vol_opt_names[0]; + const _vol_opt_name *cf = &vol_opt_casefold[0]; + + strlcpy (item, vol->v_path, sizeof(item)); + strlcat (item, "/.AppleDesktop/", sizeof(item)); + strlcat (item, VOLINFOFILE, sizeof(item)); + + if ((fd = open( item, O_RDWR | O_CREAT , 0666)) <0 ) { + LOG(log_debug, logtype_afpd,"Error opening %s: %s", item, strerror(errno)); + return (-1); + } + + /* try to get a lock */ + lock.l_start = 0; + lock.l_whence = SEEK_SET; + lock.l_len = 0; + lock.l_type = F_WRLCK; + + if (fcntl(fd, F_SETLK, &lock) < 0) { + if (errno == EACCES || errno == EAGAIN) { + /* ignore, other process already writing the file */ + return 0; + } else { + LOG(log_error, logtype_cnid, "savevoloptions: cannot get lock: %s", strerror(errno)); + return (-1); + } + } + + /* write volume options */ + snprintf(buf, sizeof(buf), "MAC_CHARSET:%s\n", vol->v_maccodepage); + snprintf(item, sizeof(item), "VOL_CHARSET:%s\n", vol->v_volcodepage); + strlcat(buf, item, sizeof(buf)); + + switch (vol->v_adouble) { + case AD_VERSION1: + strlcat(buf, "ADOUBLE_VER:v1\n", sizeof(buf)); + break; + case AD_VERSION2: + strlcat(buf, "ADOUBLE_VER:v2\n", sizeof(buf)); + break; + case AD_VERSION2_OSX: + strlcat(buf, "ADOUBLE_VER:osx\n", sizeof(buf)); + break; + } + + strlcat(buf, "CNIDBACKEND:", sizeof(buf)); + strlcat(buf, vol->v_cnidscheme, sizeof(buf)); + strlcat(buf, "\n", sizeof(buf)); + + strlcat(buf, "CNIDDBDHOST:", sizeof(buf)); + strlcat(buf, Cnid_srv, sizeof(buf)); + strlcat(buf, "\n", sizeof(buf)); + + snprintf(item, sizeof(item), "CNIDDBDPORT:%u\n", Cnid_port); + strlcat(buf, item, sizeof(buf)); + + strcpy(item, "CNID_DBPATH:"); + if (vol->v_dbpath) + strlcat(item, vol->v_dbpath, sizeof(item)); + else + strlcat(item, vol->v_path, sizeof(item)); + strlcat(item, "\n", sizeof(item)); + strlcat(buf, item, sizeof(buf)); + + /* volume flags */ + strcpy(item, "VOLUME_OPTS:"); + for (;op->name; op++) { + if ( vol->v_flags & op->option ) { + strlcat(item, op->name, sizeof(item)); + strlcat(item, " ", sizeof(item)); + } + } + strlcat(item, "\n", sizeof(item)); + strlcat(buf, item, sizeof(buf)); + + /* casefold flags */ + strcpy(item, "VOLCASEFOLD:"); + for (;cf->name; cf++) { + if ( vol->v_casefold & cf->option ) { + strlcat(item, cf->name, sizeof(item)); + strlcat(item, " ", sizeof(item)); + } + } + strlcat(item, "\n", sizeof(item)); + strlcat(buf, item, sizeof(buf)); + + if (strlen(buf) >= sizeof(buf)-1) + LOG(log_debug, logtype_afpd,"Error writing .volinfo file: buffer too small, %s", buf); + + + if (write( fd, buf, strlen(buf)) < 0) { + LOG(log_debug, logtype_afpd,"Error writing .volinfo file: %s", strerror(errno)); + goto done; + } + ftruncate(fd, strlen(buf)); + +done: + lock.l_type = F_UNLCK; + fcntl(fd, F_SETLK, &lock); + close (fd); + return ret; +} diff --git a/include/atalk/util.h b/include/atalk/util.h index 11074d3f..2650f2d5 100644 --- a/include/atalk/util.h +++ b/include/atalk/util.h @@ -1,5 +1,5 @@ /* - * $Id: util.h,v 1.7.10.6 2004-05-04 15:38:26 didg Exp $ + * $Id: util.h,v 1.7.10.7 2004-06-09 01:15:19 bfernhomberg Exp $ */ #ifndef _ATALK_UTIL_H @@ -11,6 +11,7 @@ #include #endif /* HAVE_UNISTD_H */ #include +#include /* exit error codes */ #define EXITERR_CLNT 1 /* client related error */ @@ -80,4 +81,29 @@ extern void *mod_symbol __P((void *, const char *)); #define mod_close(a) dlclose(a) #endif /* ! HAVE_DLFCN_H */ + +/* volinfo for shell utilities */ + +#define VOLINFOFILE ".volinfo" + +struct volinfo { + char *v_name; + char *v_path; + int v_flags; + int v_casefold; + char *v_cnidscheme; + char *v_dbpath; + char *v_volcodepage; + charset_t v_volcharset; + char *v_maccodepage; + charset_t v_maccharset; + int v_adouble; /* default adouble format */ + char *(*ad_path)(const char *, int); + char *v_dbd_host; + int v_dbd_port; +}; + +extern int loadvolinfo __P((char *path, struct volinfo *vol)); +extern int vol_load_charsets __P(( struct volinfo *vol)); + #endif diff --git a/include/atalk/volinfo.h b/include/atalk/volinfo.h new file mode 100644 index 00000000..9590cc8d --- /dev/null +++ b/include/atalk/volinfo.h @@ -0,0 +1,40 @@ +/* + * $Id: volinfo.h,v 1.1.2.1 2004-06-09 01:15:20 bfernhomberg Exp $ + */ + +#ifndef _ATALK_VOLINFO_H +#define _ATALK_VOLINFO_H 1 + +/* FIXME: following duplicated from etc/afpd/volume.h */ + +/* flags that alter volume behaviour. */ +#define AFPVOL_A2VOL (1 << 5) /* prodos volume */ +#define AFPVOL_CRLF (1 << 6) /* cr/lf translation */ +#define AFPVOL_NOADOUBLE (1 << 7) /* don't create .AppleDouble by default */ +#define AFPVOL_RO (1 << 8) /* read-only volume */ +#define AFPVOL_MSWINDOWS (1 << 9) /* deal with ms-windows yuckiness. this is going away. */ +#define AFPVOL_NOHEX (1 << 10) /* don't do :hex translation */ +#define AFPVOL_USEDOTS (1 << 11) /* use real dots */ +#define AFPVOL_LIMITSIZE (1 << 12) /* limit size for older macs */ +#define AFPVOL_MAPASCII (1 << 13) /* map the ascii range as well */ +#define AFPVOL_DROPBOX (1 << 14) /* dropkludge dropbox support */ +#define AFPVOL_NOFILEID (1 << 15) /* don't advertise createid resolveid and deleteid calls */ +#define AFPVOL_NOSTAT (1 << 16) /* advertise the volume even if we can't stat() it + * maybe because it will be mounted later in preexec */ +#define AFPVOL_UNIX_PRIV (1 << 17) /* support unix privileges */ +#define AFPVOL_NODEV (1 << 18) /* always use 0 for device number in cnid calls + * help if device number is notconsistent across reboot + * NOTE symlink to a different device will return an ACCESS error + */ +/* handle casefolding */ +#define AFPVOL_MTOUUPPER (1 << 0) +#define AFPVOL_MTOULOWER (1 << 1) +#define AFPVOL_UTOMUPPER (1 << 2) +#define AFPVOL_UTOMLOWER (1 << 3) +#define AFPVOL_UMLOWER (AFPVOL_MTOULOWER | AFPVOL_UTOMLOWER) +#define AFPVOL_UMUPPER (AFPVOL_MTOUUPPER | AFPVOL_UTOMUPPER) +#define AFPVOL_UUPPERMLOWER (AFPVOL_MTOUUPPER | AFPVOL_UTOMLOWER) +#define AFPVOL_ULOWERMUPPER (AFPVOL_MTOULOWER | AFPVOL_UTOMUPPER) + + +#endif diff --git a/libatalk/util/Makefile.am b/libatalk/util/Makefile.am index c8991b7f..3a83debc 100644 --- a/libatalk/util/Makefile.am +++ b/libatalk/util/Makefile.am @@ -16,6 +16,7 @@ libutil_la_SOURCES = \ strcasestr.c \ strdicasecmp.c \ strlcpy.c \ - fault.c + fault.c \ + volinfo.c # util_unicode.c diff --git a/libatalk/util/volinfo.c b/libatalk/util/volinfo.c new file mode 100644 index 00000000..2b049604 --- /dev/null +++ b/libatalk/util/volinfo.c @@ -0,0 +1,380 @@ +/* + 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. + + .volinfo file handling, command line utilities + copyright Bjoern Fernhomberg, 2004 +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif /* HAVE_CONFIG_H */ + + +#include +#include +#include +#include +#include +#ifdef HAVE_STRINGS_H +#include +#endif +#ifdef STDC_HEADERS +#include +#endif +#include + +#include +#include +#include +#include +#ifdef CNID_DB +#include +#endif /* CNID_DB*/ + +#define MAC_CHARSET 0 +#define VOL_CHARSET 1 +#define ADOUBLE_VER 2 +#define CNIDBACKEND 3 +#define CNIDDBDHOST 4 +#define CNIDDBDPORT 5 +#define CNID_DBPATH 6 +#define VOLUME_OPTS 7 +#define VOLCASEFOLD 8 + +typedef struct _info_option { + const char *name; + int type; +} _info_option; + +static const _info_option info_options[] = { + {"MAC_CHARSET", MAC_CHARSET}, + {"VOL_CHARSET", VOL_CHARSET}, + {"ADOUBLE_VER", ADOUBLE_VER}, + {"CNIDBACKEND", CNIDBACKEND}, + {"CNIDDBDHOST", CNIDDBDHOST}, + {"CNIDDBDPORT", CNIDDBDPORT}, + {"CNID_DBPATH", CNID_DBPATH}, + {"VOLUME_OPTS", VOLUME_OPTS}, + {"VOLCASEFOLD", VOLCASEFOLD}, + {NULL, 0} +}; + +typedef struct _vol_opt_name { + const u_int32_t option; + const char *name; +} _vol_opt_name; + +static const _vol_opt_name vol_opt_names[] = { + {AFPVOL_A2VOL, "PRODOS"}, /* prodos volume */ + {AFPVOL_CRLF, "CRLF"}, /* cr/lf translation */ + {AFPVOL_NOADOUBLE, "NOADOUBLE"}, /* don't create .AppleDouble by default */ + {AFPVOL_RO, "READONLY"}, /* read-only volume */ + {AFPVOL_MSWINDOWS, "MSWINDOWS"}, /* deal with ms-windows yuckiness. this is going away. */ + {AFPVOL_NOHEX, "NOHEX"}, /* don't do :hex translation */ + {AFPVOL_USEDOTS, "USEDOTS"}, /* use real dots */ + {AFPVOL_LIMITSIZE, "LIMITSIZE"}, /* limit size for older macs */ + {AFPVOL_MAPASCII, "MAPASCII"}, /* map the ascii range as well */ + {AFPVOL_DROPBOX, "DROPBOX"}, /* dropkludge dropbox support */ + {AFPVOL_NOFILEID, "NOFILEID"}, /* don't advertise createid resolveid and deleteid calls */ + {AFPVOL_NOSTAT, "NOSTAT"}, /* advertise the volume even if we can't stat() it + * maybe because it will be mounted later in preexec */ + {AFPVOL_UNIX_PRIV, "UNIXPRIV"}, /* support unix privileges */ + {AFPVOL_NODEV, "NODEV"}, /* always use 0 for device number in cnid calls */ + {0, NULL} +}; + +static const _vol_opt_name vol_opt_casefold[] = { + {AFPVOL_MTOUUPPER, "MTOULOWER"}, + {AFPVOL_MTOULOWER, "MTOULOWER"}, + {AFPVOL_UTOMUPPER, "UTOMUPPER"}, + {AFPVOL_UTOMLOWER, "UTOMLOWER"}, + {0, NULL} +}; + +static char* find_in_path( char *path, char *subdir, size_t maxlen) +{ + char *pos; + struct stat st; + + strlcat(path, subdir, maxlen); + pos = strrchr(path, '/'); + + while ( stat(path, &st) != 0) { +#ifdef DEBUG + fprintf(stderr, "searching in path %s\n", path); +#endif + path[pos-path]=0; + if ((pos = strrchr(path, '/'))) { + path[pos-path]=0; + strlcat(path, subdir, maxlen); + } + else { + return NULL; + } + } + + path[pos-path] = '/'; + path[pos-path+1] = 0; +#ifdef DEBUG + fprintf(stderr, "%s path %s\n", subdir, path); +#endif + return path; +} + +static char * make_path_absolute(char *path, size_t bufsize) +{ + struct stat st; + char savecwd[MAXPATHLEN]; + char abspath[MAXPATHLEN]; + char *p; + + if (stat(path, &st) != 0) { + return NULL; + } + + strlcpy (abspath, path, sizeof(abspath)); + + if (!S_ISDIR(st.st_mode)) { + if (NULL == (p=strrchr(abspath, '/')) ) + return NULL; + *p = '\0'; + } + + getcwd(savecwd, sizeof(savecwd)); + if ((chdir(abspath)) < 0) + return NULL; + + getcwd(abspath, sizeof(abspath)); + chdir (savecwd); + + if (strlen(abspath) > bufsize) + return NULL; + + strlcpy(path, abspath, bufsize); + + return path; +} + +static char * find_volumeroot(char *path, size_t maxlen) +{ + char *volume = make_path_absolute(path, maxlen); + + if (volume == NULL) + return NULL; + + if (NULL == (find_in_path(volume, "/.AppleDesktop", maxlen)) ) + return NULL; + + return volume; +} + +int vol_load_charsets( struct volinfo *vol) +{ + if ( (charset_t) -1 == ( vol->v_maccharset = add_charset(vol->v_maccodepage)) ) { + fprintf( stderr, "Setting codepage %s as Mac codepage failed", vol->v_maccodepage); + return (-1); + } + + if ( (charset_t) -1 == ( vol->v_volcharset = add_charset(vol->v_volcodepage)) ) { + fprintf( stderr, "Setting codepage %s as volume codepage failed", vol->v_volcodepage); + return (-1); + } + + return 0; +} + +static int parse_options (char *buf, int *flags, const _vol_opt_name* options) +{ + char *p, *q; + const _vol_opt_name *op; + + q = p = buf; + + while ( *p != '\0') { + if (*p == ' ') { + *p = '\0'; + op = options; + for (;op->name; op++) { + if ( !strcmp(op->name, q )) { + *flags |= op->option; + break; + } + } + q = p+1; + } + p++; + } + + return 0; +} + + + +static int parseline ( char *buf, struct volinfo *vol) +{ + char *value; + size_t len; + int option=-1; + const _info_option *p = &info_options[0]; + + if (NULL == ( value = strchr(buf, ':')) ) + return 1; + + *value = '\0'; + value++; + + if ( 0 == (len = strlen(value)) ) + return 0; + + if (value[len-1] == '\n') + value[len-1] = '\0'; + + for (;p->name; p++) { + if ( !strcmp(p->name, buf )) { + option=p->type; + break; + } + } + + switch (option) { + case MAC_CHARSET: + if ((vol->v_maccodepage = strdup(value)) == NULL) { + fprintf (stderr, "strdup: %m"); + return -1; + } + break; + case VOL_CHARSET: + if ((vol->v_volcodepage = strdup(value)) == NULL) { + fprintf (stderr, "strdup: %m"); + return -1; + } + break; + case CNIDBACKEND: + if ((vol->v_cnidscheme = strdup(value)) == NULL) { + fprintf (stderr, "strdup: %m"); + return -1; + } + break; + case CNIDDBDHOST: + if ((vol->v_dbd_host = strdup(value)) == NULL) { + fprintf (stderr, "strdup: %m"); + return -1; + } + break; + case CNIDDBDPORT: + vol->v_dbd_port = atoi(value); + break; + case CNID_DBPATH: + if ((vol->v_dbpath = strdup(value)) == NULL) { + fprintf (stderr, "strdup: %m"); + return -1; + } + break; + case ADOUBLE_VER: + if (strcasecmp(value, "v1") == 0) { + vol->v_adouble = AD_VERSION1; + vol->ad_path = ad_path; + } +#if AD_VERSION == AD_VERSION2 + else if (strcasecmp(value, "v2") == 0) { + vol->ad_path = ad_path; + vol->v_adouble = AD_VERSION2; + } + else if (strcasecmp(value, "osx") == 0) { + vol->v_adouble = AD_VERSION2_OSX; + vol->ad_path = ad_path_osx; + } +#endif + else { + fprintf (stderr, "unknown adouble version: %s, %s", buf, value); + return -1; + } + break; + case VOLUME_OPTS: + parse_options(value, &vol->v_flags, &vol_opt_names[0]); + break; + case VOLCASEFOLD: + parse_options(value, &vol->v_casefold, &vol_opt_casefold[0]); + break; + default: + fprintf (stderr, "unknown volume information: %s, %s", buf, value); + return (-1); + break; + } +#ifdef DEBUG + printf ("volume information: %s, %s", buf, value); +#endif + + return 0; +} + + +int loadvolinfo (char *path, struct volinfo *vol) +{ + + char volinfofile[MAXPATHLEN]; + char buf[MAXPATHLEN]; + struct flock lock; + int fd; + FILE *fp; + + if ( !path || !vol) + return -1; + + memset(vol, 0, sizeof(struct volinfo)); + strlcpy(volinfofile, path, sizeof(volinfofile)); + + /* volinfo file is in .AppleDesktop */ + if ( NULL == find_volumeroot(volinfofile, sizeof(volinfofile))) + return -1; + + if ((vol->v_path = strdup(volinfofile)) == NULL ) { + fprintf (stderr, "strdup: %m"); + return (-1); + } + strlcat(volinfofile, ".AppleDesktop/", sizeof(volinfofile)); + strlcat(volinfofile, VOLINFOFILE, sizeof(volinfofile)); + + /* open the file read only */ + if ( NULL == (fp = fopen( volinfofile, "r")) ) { + fprintf (stderr, "error opening volinfo (%s): %m", volinfofile); + return (-1); + } + fd = fileno(fp); + + /* try to get a read lock */ + lock.l_start = 0; + lock.l_whence = SEEK_SET; + lock.l_len = 0; + lock.l_type = F_RDLCK; + + /* wait for read lock */ + if (fcntl(fd, F_SETLKW, &lock) < 0) { + fclose(fp); + return (-1); + } + + /* read the file */ + while (NULL != fgets(buf, sizeof(buf), fp)) { + parseline(buf, vol); + } + + /* unlock file */ + lock.l_type = F_UNLCK; + fcntl(fd, F_SETLK, &lock); + + fclose(fp); + return 0; +}