#include <atalk/util.h>
#include <atalk/logger.h>
#include <atalk/volinfo.h>
+#include <atalk/volume.h>
#ifdef CNID_DB
#include <atalk/cnid.h>
#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[] = {
+static const vol_opt_name_t vol_opt_names[] = {
{AFPVOL_A2VOL, "PRODOS"}, /* prodos volume */
{AFPVOL_CRLF, "CRLF"}, /* cr/lf translation */
{AFPVOL_NOADOUBLE, "NOADOUBLE"}, /* don't create .AppleDouble by default */
{AFPVOL_EILSEQ, "ILLEGALSEQ"}, /* encode illegal sequence */
{AFPVOL_CACHE, "CACHEID"}, /* Use adouble v2 CNID caching, default don't use it */
{AFPVOL_INV_DOTS, "INVISIBLEDOTS"},
- {AFPVOL_EXT_ATTRS, "EXT_ATTRS"}, /* Vol supports Extened Attributes */
{AFPVOL_ACLS, "ACLS"}, /* Vol supports ACLs */
+ {AFPVOL_TM, "TM"}, /* Set "kSupportsTMLockSteal" is volume attributes */
{0, NULL}
};
-static const _vol_opt_name vol_opt_casefold[] = {
+static const vol_opt_name_t vol_opt_casefold[] = {
{AFPVOL_MTOUUPPER, "MTOULOWER"},
{AFPVOL_MTOULOWER, "MTOULOWER"},
{AFPVOL_UTOMUPPER, "UTOMUPPER"},
{0, NULL}
};
+typedef struct {
+ const char *name;
+ int type;
+} info_option_t;
+
+#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
+#define EXTATTRTYPE 9
+
+static const info_option_t 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},
+ {"EXTATTRTYPE", EXTATTRTYPE},
+ {NULL, 0}
+};
+
static char* find_in_path( char *path, char *subdir, size_t maxlen)
{
char *pos;
char abspath[MAXPATHLEN];
char *p;
- if (stat(path, &st) != 0) {
- return NULL;
- }
+ strlcpy(abspath, path, sizeof(abspath));
- strlcpy (abspath, path, sizeof(abspath));
+ /* we might be called from `ad cp ...` with non existing target */
+ if (stat(abspath, &st) != 0) {
+ if (errno != ENOENT)
+ return NULL;
- if (!S_ISDIR(st.st_mode)) {
- if (NULL == (p=strrchr(abspath, '/')) )
+ if (NULL == (p = strrchr(abspath, '/')) )
+ /* single component `ad cp SOURCEFILE TARGETFILE`, use "." instead */
strcpy(abspath, ".");
else
+ /* try without the last path element */
+ *p = '\0';
+
+ if (stat(abspath, &st) != 0) {
+ return NULL;
+ }
+ }
+
+ if (S_ISREG(st.st_mode)) {
+ /* single file copy SOURCE */
+ if (NULL == (p = strrchr(abspath, '/')) )
+ /* no path, use "." instead */
+ strcpy(abspath, ".");
+ else
+ /* try without the last path element */
*p = '\0';
}
return 0;
}
-static int parse_options (char *buf, int *flags, const _vol_opt_name* options)
+static int parse_options (char *buf, int *flags, const vol_opt_name_t *options)
{
char *p, *q;
- const _vol_opt_name *op;
+ const vol_opt_name_t *op;
q = p = buf;
char *value;
size_t len;
int option=-1;
- const _info_option *p = &info_options[0];
+ const info_option_t *p = &info_options[0];
if (NULL == ( value = strchr(buf, ':')) )
return 1;
}
break;
case CNIDDBDPORT:
- vol->v_dbd_port = atoi(value);
- break;
- case CNID_DBPATH:
- if ((vol->v_dbpath = strdup(value)) == NULL) {
+ if ((vol->v_dbd_port = strdup(value)) == NULL) {
fprintf (stderr, "strdup: %s", strerror(errno));
- return -1;
+ return -1;
}
break;
+ case CNID_DBPATH:
+ if ((vol->v_dbpath = malloc(MAXPATHLEN+1)) == NULL)
+ return -1;
+ strcpy(vol->v_dbpath, value);
+ break;
case ADOUBLE_VER:
if (strcasecmp(value, "v1") == 0) {
vol->v_adouble = AD_VERSION1;
case VOLCASEFOLD:
parse_options(value, &vol->v_casefold, &vol_opt_casefold[0]);
break;
+ case EXTATTRTYPE:
+ if (strcasecmp(value, "AFPVOL_EA_AD") == 0)
+ vol->v_vfs_ea = AFPVOL_EA_AD;
+ else if (strcasecmp(value, "AFPVOL_EA_SYS") == 0)
+ vol->v_vfs_ea = AFPVOL_EA_SYS;
+ break;
default:
fprintf (stderr, "unknown volume information: %s, %s", buf, value);
return (-1);
char volinfofile[MAXPATHLEN];
char buf[MAXPATHLEN];
struct flock lock;
- int fd;
+ int fd, len;
FILE *fp;
if ( !path || !vol)
return -1;
if ((vol->v_path = strdup(volinfofile)) == NULL ) {
- fprintf (stderr, "strdup: %s", strerror(errno));
+ fprintf (stderr, "strdup: %s", strerror(errno));
return (-1);
}
+ /* Remove trailing slashes */
+ len = strlen(vol->v_path);
+ while (len && (vol->v_path[len-1] == '/')) {
+ vol->v_path[len-1] = 0;
+ len--;
+ }
+
strlcat(volinfofile, ".AppleDesktop/", sizeof(volinfofile));
strlcat(volinfofile, VOLINFOFILE, sizeof(volinfofile));
if ((vol->v_flags & AFPVOL_INV_DOTS))
vol->v_ad_options |= ADVOL_INVDOTS;
+ vol->retaincount = 1;
+
fclose(fp);
return 0;
}
+
+/*!
+ * Allocate a struct volinfo object for refcounting usage with retain and close, and
+ * call loadvolinfo with it
+ */
+struct volinfo *allocvolinfo(char *path)
+{
+ struct volinfo *p = malloc(sizeof(struct volinfo));
+ if (p == NULL)
+ return NULL;
+
+ if (loadvolinfo(path, p) == -1)
+ return NULL;
+
+ p->malloced = 1;
+
+ return p;
+}
+
+void retainvolinfo(struct volinfo *vol)
+{
+ vol->retaincount++;
+}
+
+/*!
+ * Decrement retain count, free resources when retaincount reaches 0
+ */
+int closevolinfo(struct volinfo *volinfo)
+{
+ if (volinfo->retaincount <= 0)
+ abort();
+
+ volinfo->retaincount--;
+
+ if (volinfo->retaincount == 0) {
+ free(volinfo->v_name); volinfo->v_name = NULL;
+ free(volinfo->v_path); volinfo->v_path = NULL;
+ free(volinfo->v_cnidscheme); volinfo->v_cnidscheme = NULL;
+ free(volinfo->v_dbpath); volinfo->v_dbpath = NULL;
+ free(volinfo->v_volcodepage); volinfo->v_volcodepage = NULL;
+ free(volinfo->v_maccodepage); volinfo->v_maccodepage = NULL;
+ free(volinfo->v_dbd_host); volinfo->v_dbd_host = NULL;
+ free(volinfo->v_dbd_port); volinfo->v_dbd_port = NULL;
+ if (volinfo->malloced) {
+ volinfo->malloced = 0;
+ free(volinfo);
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * 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.
+ */
+int savevolinfo(const struct vol *vol, const char *Cnid_srv, const char *Cnid_port)
+{
+ char buf[16348];
+ char item[MAXPATHLEN];
+ int fd;
+ int ret = 0;
+ struct flock lock;
+ const vol_opt_name_t *op = &vol_opt_names[0];
+ const vol_opt_name_t *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;
+ case AD_VERSION1_SFM:
+ strlcat(buf, "ADOUBLE_VER:sfm\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));
+
+ strlcat(buf, "CNIDDBDPORT:", sizeof(buf));
+ strlcat(buf, Cnid_port, sizeof(buf));
+ strlcat(buf, "\n", 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));
+
+ /* ExtendedAttributes */
+ strcpy(item, "EXTATTRTYPE:");
+ switch (vol->v_vfs_ea) {
+ case AFPVOL_EA_SYS:
+ strlcat(item, "AFPVOL_EA_SYS\n", sizeof(item));
+ break;
+ case AFPVOL_EA_AD:
+ strlcat(item, "AFPVOL_EA_AD\n", sizeof(item));
+ break;
+ case AFPVOL_EA_NONE:
+ strlcat(item, "AFPVOL_EA_NONE\n", sizeof(item));
+ break;
+ default:
+ strlcat(item, "AFPVOL_EA_UNKNOWN\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 || ftruncate(fd, strlen(buf)) < 0 ) {
+ LOG(log_debug, logtype_afpd,"Error writing .volinfo file: %s", strerror(errno));
+ }
+
+ lock.l_type = F_UNLCK;
+ fcntl(fd, F_SETLK, &lock);
+ close (fd);
+ return ret;
+}