]> arthur.barton.de Git - netatalk.git/commitdiff
write a .volinfo file containing volume setup to .AppleDesktop, for shell utils and...
authorbfernhomberg <bfernhomberg>
Wed, 9 Jun 2004 01:15:19 +0000 (01:15 +0000)
committerbfernhomberg <bfernhomberg>
Wed, 9 Jun 2004 01:15:19 +0000 (01:15 +0000)
etc/afpd/volume.c
include/atalk/util.h
include/atalk/volinfo.h [new file with mode: 0644]
libatalk/util/Makefile.am
libatalk/util/volinfo.c [new file with mode: 0644]

index 069e0c540d4514ba90bf7d9537493c9ef3f00ee8..57d6d1483cd7dae54569a2b15f67bb5c554471fe 100644 (file)
@@ -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;
+}
index 11074d3fa745ce4bbd05c07945e56e3b98cf6b79..2650f2d51cb363c6966a2059533fc5db8f94a4ce 100644 (file)
@@ -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 <unistd.h>
 #endif /* HAVE_UNISTD_H */
 #include <netatalk/at.h>
+#include <atalk/unicode.h>
 
 /* 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 (file)
index 0000000..9590cc8
--- /dev/null
@@ -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
index c8991b7f42ffb560f00c8b445417462129a1f45e..3a83debc1fdc7b5b7e1c67159e367c462039500b 100644 (file)
@@ -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 (file)
index 0000000..2b04960
--- /dev/null
@@ -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 <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+#ifdef STDC_HEADERS
+#include <string.h>
+#endif
+#include <sys/param.h>
+
+#include <atalk/adouble.h>
+#include <atalk/util.h>
+#include <atalk/logger.h>
+#include <atalk/volinfo.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[] = {
+    {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;
+}