/*
- * $Id: ad_open.c,v 1.51 2009-10-21 13:28:17 didg Exp $
+ * $Id: ad_open.c,v 1.72 2010-03-12 15:16:49 franklahm Exp $
*
* Copyright (c) 1999 Adrian Sun (asun@u.washington.edu)
* Copyright (c) 1990,1991 Regents of The University of Michigan.
* +1-313-763-0525
* netatalk@itd.umich.edu
*
- * NOTE: I don't use inline because a good compiler should be
- * able to optimize all the static below. Didier
+ */
+
+/*!
+ * @file
+ * Part of Netatalk's AppleDouble implementatation
+ * @note We don't use inlines because a good compiler should be
+ * able to optimize all the static funcs below.
+ * @sa include/atalk/adouble.h
*/
#ifdef HAVE_CONFIG_H
&& (ad->ad_version != AD_VERSION2)
#endif /* AD_VERSION == AD_VERSION2 */
)) {
- errno = EIO;
LOG(log_debug, logtype_default, "ad_open: can't parse AppleDouble header.");
+ errno = EIO;
return -1;
}
buf += AD_HEADER_LEN;
if (len > header_len - AD_HEADER_LEN) {
- errno = EIO;
LOG(log_debug, logtype_default, "ad_header_read: can't read entry info.");
+ errno = EIO;
return -1;
}
if (!ad_getentryoff(ad, ADEID_RFORK)
|| (ad_getentryoff(ad, ADEID_RFORK) > sizeof(ad->ad_data))
) {
- errno = EIO;
LOG(log_debug, logtype_default, "ad_header_read: problem with rfork entry offset.");
+ errno = EIO;
return -1;
}
if (ad_getentryoff(ad, ADEID_RFORK) > header_len) {
- errno = EIO;
LOG(log_debug, logtype_default, "ad_header_read: can't read in entries.");
+ errno = EIO;
return -1;
}
ad_path( const char *path, int adflags)
{
static char pathbuf[ MAXPATHLEN + 1];
- char c, *slash, buf[MAXPATHLEN + 1];
- size_t l;
+ const char *slash;
+ size_t l ;
- l = strlcpy(buf, path, MAXPATHLEN +1);
if ( adflags & ADFLAGS_DIR ) {
- strcpy( pathbuf, buf);
- if ( *buf != '\0' && l < MAXPATHLEN) {
+ l = strlcpy( pathbuf, path, sizeof(pathbuf));
+
+ if ( l && l < MAXPATHLEN) {
pathbuf[l++] = '/';
- pathbuf[l] = 0;
}
- slash = ".Parent";
+ strlcpy(pathbuf +l, ".AppleDouble/.Parent", sizeof(pathbuf) -l);
} else {
- if (NULL != ( slash = strrchr( buf, '/' )) ) {
- c = *++slash;
- *slash = '\0';
- strcpy( pathbuf, buf);
- *slash = c;
+ if (NULL != ( slash = strrchr( path, '/' )) ) {
+ slash++;
+ l = slash - path;
+ /* XXX we must return NULL here and test in the caller */
+ if (l > MAXPATHLEN)
+ l = MAXPATHLEN;
+ memcpy( pathbuf, path, l);
} else {
- pathbuf[ 0 ] = '\0';
- slash = buf;
+ l = 0;
+ slash = path;
}
+ l += strlcpy( pathbuf +l, ".AppleDouble/", sizeof(pathbuf) -l);
+ strlcpy( pathbuf + l, slash, sizeof(pathbuf) -l);
}
- strlcat( pathbuf, ".AppleDouble/", MAXPATHLEN +1);
- strlcat( pathbuf, slash, MAXPATHLEN +1);
return( pathbuf );
}
{
static char modebuf[ MAXPATHLEN + 1];
char *slash;
- size_t len;
-
- if ( (len = strlen( path )) >= MAXPATHLEN ) {
- errno = ENAMETOOLONG;
- return NULL; /* can't do it */
- }
-
/*
* For a path with directories in it, remove the final component
* (path or subdirectory name) to get the name we want to stat.
* For a path which is just a filename, use "." instead.
*/
- strcpy( modebuf, path );
- slash = strrchr( modebuf, '/' );
- /* is last char a '/' */
- if (slash && slash[1] == 0) {
- while (modebuf < slash && slash[-1] == '/') {
- --slash;
+ slash = strrchr( path, '/' );
+ if (slash) {
+ size_t len;
+
+ len = slash - path;
+ if (len >= MAXPATHLEN) {
+ errno = ENAMETOOLONG;
+ return NULL; /* can't do it */
}
- if (modebuf < slash) {
+ memcpy( modebuf, path, len );
+ modebuf[len] = '\0';
+ /* is last char a '/' ? */
+ if (slash[1] == 0) {
+ slash = modebuf+ len;
+ /* remove them */
+ while (modebuf < slash && slash[-1] == '/') {
+ --slash;
+ }
+ if (modebuf == slash) {
+ goto use_cur;
+ }
+ *slash = '\0';
+ while (modebuf < slash && *slash != '/') {
+ --slash;
+ }
+ if (modebuf == slash) {
+ goto use_cur;
+ }
*slash = '\0'; /* remove pathname component */
- slash = strrchr( modebuf, '/' );
}
+ return modebuf;
}
- if (slash) {
- *slash = '\0'; /* remove pathname component */
- } else {
- modebuf[0] = '.'; /* use current directory */
- modebuf[1] = '\0';
- }
+use_cur:
+ modebuf[0] = '.'; /* use current directory */
+ modebuf[1] = '\0';
return modebuf;
}
if (!p) {
return -1;
}
-
- return stat( p, stbuf );
+//FIXME!
+ return lstat( p, stbuf );
}
/* ----------------
if (default_uid != (uid_t)-1) {
/* we are root (admin) */
id = (default_uid)?default_uid:stbuf->st_uid;
- ret = chown( path, id, stbuf->st_gid );
+ ret = lchown( path, id, stbuf->st_gid );
}
#endif
return ret;
int st_invalid;
struct stat stbuf;
-#ifdef DEBUG
- LOG(log_info, logtype_default, "ad_mkdir: Creating directory with mode %d", mode);
-#endif /* DEBUG */
+ LOG(log_debug, logtype_default, "ad_mkdir: creating ad-directory '%s/%s' with mode %04o",
+ getcwdpath(), path, mode);
st_invalid = ad_mode_st(path, &mode, &stbuf);
ret = mkdir( path, mode );
void ad_init(struct adouble *ad, int flags, int options)
{
- memset( ad, 0, sizeof( struct adouble ) );
+ ad->ad_inited = 0;
ad->ad_flags = flags;
if (flags == AD_VERSION2_OSX) {
ad->ad_ops = &ad_osx;
+ ad->ad_md = &ad->ad_resource_fork;
}
else if (flags == AD_VERSION1_SFM) {
ad->ad_ops = &ad_sfm;
+ ad->ad_md = &ad->ad_metadata_fork;
}
else {
ad->ad_ops = &ad_adouble;
+ ad->ad_md = &ad->ad_resource_fork;
}
ad->ad_options = options;
+
+ ad_data_fileno(ad) = -1;
+ ad_reso_fileno(ad) = -1;
+ ad_meta_fileno(ad) = -1;
+ /* following can be read even if there's no
+ * meda data.
+ */
+ memset(ad->ad_eid, 0, sizeof( ad->ad_eid ));
+ ad->ad_rlen = 0;
}
-/* -------------------
- * It's not possible to open the header file O_RDONLY -- the read
- * will fail and return an error. this refcounts things now.
+/*!
+ * Open data-, metadata(header)- or ressource fork
+ *
+ * You must call ad_init() before ad_open, usually you'll just call it like this: \n
+ * @code
+ * struct adoube ad;
+ * ad_init(&ad, vol->v_adouble, vol->v_ad_options);
+ * @endcode
+ *
+ * @param path Path to file or directory
+ *
+ * @param adflags ADFLAGS_DF: open data file/fork\n
+ * ADFLAGS_HF: open header (metadata) file\n
+ * ADFLAGS_RF: open ressource fork *** FIXME: not used ?! *** \n
+ * ADFLAGS_CREATE: indicate creation\n
+ * ADFLAGS_NOHF: it's not an error if header file couldn't be created\n
+ * ADFLAGS_DIR: if path is a directory you MUST or ADFLAGS_DIR to adflags\n
+ * ADFLAGS_NOADOUBLE: dont create adouble files if not necessary\n
+ * ADFLAGS_RDONLY: open read only\n
+ * ADFLAGS_OPENFORKS: check for open forks from other processes\n
+ * ADFLAGS_MD: alias for ADFLAGS_HF\n
+ * ADFLAGS_V1COMPAT: obsolete
+ *
+ * @param oflags flags passed through to open syscall: \n
+ * O_RDONLY: *** FIXME *** \n
+ * O_RDWR: *** FIXME *** \n
+ * O_CREAT: create fork\n
+ * O_EXCL: fail if exists with O_CREAT
+ *
+ * @param mode passed to open with O_CREAT
+ *
+ * @param ad pointer to struct adouble
+ *
+ * @returns 0 on success
+ *
+ * @note It's not possible to open the header file O_RDONLY -- the read
+ * will fail and return an error. this refcounts things now.\n
+ * metadata(ressource)-fork only gets created with O_CREAT.
*/
int ad_open( const char *path, int adflags, int oflags, int mode, struct adouble *ad)
{
int open_df = 0;
if (ad->ad_inited != AD_INITED) {
- ad_data_fileno(ad) = -1;
- ad_reso_fileno(ad) = -1;
- adf_lock_init(&ad->ad_data_fork);
- adf_lock_init(&ad->ad_resource_fork);
- if (ad->ad_flags != AD_VERSION1_SFM) {
- ad->ad_md = &ad->ad_resource_fork;
- }
- else {
- adf_lock_init(&ad->ad_metadata_fork);
- ad->ad_md = &ad->ad_metadata_fork;
- ad_meta_fileno(ad) = -1;
- }
ad->ad_inited = AD_INITED;
ad->ad_refcount = 1;
ad->ad_open_forks = 0;
ad->ad_adflags = adflags;
+ ad->ad_resource_fork.adf_refcount = 0;
+ ad->ad_data_fork.adf_refcount = 0;
+ ad->ad_data_fork.adf_syml=0;
}
else {
ad->ad_open_forks = ((ad->ad_data_fork.adf_refcount > 0) ? ATTRBIT_DOPEN : 0);
admode = mode;
}
}
- ad->ad_data_fork.adf_fd =open( path, hoflags, admode );
+
+ ad->ad_data_fork.adf_fd =open( path, hoflags | O_NOFOLLOW, admode );
+
if (ad->ad_data_fork.adf_fd < 0 ) {
if ((errno == EACCES || errno == EROFS) && !(oflags & O_RDWR)) {
hoflags = oflags;
- ad->ad_data_fork.adf_fd = open( path, hoflags, admode );
+ ad->ad_data_fork.adf_fd = open( path, hoflags | O_NOFOLLOW, admode );
+ }
+ if (ad->ad_data_fork.adf_fd < 0 && errno == ELOOP) {
+ int lsz;
+
+ if (oflags != O_RDONLY)
+ return -1;
+
+ ad->ad_data_fork.adf_syml = malloc(PATH_MAX+1);
+ lsz = readlink(path, ad->ad_data_fork.adf_syml, PATH_MAX);
+ if (lsz <= 0) {
+ free(ad->ad_data_fork.adf_syml);
+ return -1;
+ }
+ ad->ad_data_fork.adf_syml[lsz]=0;
+ ad->ad_data_fork.adf_syml = realloc(ad->ad_data_fork.adf_syml,lsz+1);
+ // XX
+ ad->ad_data_fork.adf_fd = 0;
}
}
+
if ( ad->ad_data_fork.adf_fd < 0)
return -1;
/* just created, set owner if admin (root) */
ad_chown(path, &st_dir);
}
+ adf_lock_init(&ad->ad_data_fork);
}
else {
/* the file is already open... but */
return -1;
}
ad_refresh(ad);
+ /* it's not new anymore */
+ ad->ad_md->adf_flags &= ~( O_TRUNC | O_CREAT );
ad->ad_md->adf_refcount++;
goto sfm;
}
+ memset(ad->ad_eid, 0, sizeof( ad->ad_eid ));
+ ad->ad_rlen = 0;
ad_p = ad->ad_ops->ad_path( path, adflags );
- hoflags = oflags & ~O_CREAT;
+ hoflags = oflags & ~(O_CREAT | O_EXCL);
if (!(adflags & ADFLAGS_RDONLY)) {
hoflags = (hoflags & ~(O_RDONLY | O_WRONLY)) | O_RDWR;
}
- ad->ad_md->adf_fd = open( ad_p, hoflags, 0 );
+ ad->ad_md->adf_fd = open( ad_p, hoflags | O_NOFOLLOW, 0 );
if (ad->ad_md->adf_fd < 0 ) {
if ((errno == EACCES || errno == EROFS) && !(oflags & O_RDWR)) {
- hoflags = oflags & ~O_CREAT;
- ad->ad_md->adf_fd = open( ad_p, hoflags, 0 );
+ hoflags = oflags & ~(O_CREAT | O_EXCL);
+ ad->ad_md->adf_fd = open( ad_p, hoflags | O_NOFOLLOW, 0 );
}
}
* here.
* if ((oflags & O_CREAT) ==> (oflags & O_RDWR)
*/
+ LOG(log_debug, logtype_default, "ad_open: creating new adouble file: %s/%s", getcwdpath(), ad_p);
admode = mode;
errno = 0;
st_invalid = ad_mode_st(ad_p, &admode, &st_dir);
admode = mode;
}
admode = ad_hf_mode(admode);
- if ( errno == ENOENT && !(adflags & ADFLAGS_NOADOUBLE) && ad->ad_flags != AD_VERSION2_OSX) {
+ if ((errno == ENOENT) && (ad->ad_flags != AD_VERSION2_OSX)) {
if (ad->ad_ops->ad_mkrf( ad_p) < 0) {
return ad_error(ad, adflags);
}
}
AD_SET(ad->ad_md->adf_off);
- memset(ad->ad_eid, 0, sizeof( ad->ad_eid ));
- ad->ad_md->adf_refcount++;
+ ad->ad_md->adf_refcount = 1;
+ adf_lock_init(ad->ad_md);
if ((ad->ad_md->adf_flags & ( O_TRUNC | O_CREAT ))) {
/*
* This is a new adouble header file. Initialize the structure,
ad_p = ad->ad_ops->ad_path( path, ADFLAGS_RF );
- hoflags = (oflags & ~(O_RDONLY | O_WRONLY)) | O_RDWR;
- ad->ad_resource_fork.adf_fd = open( ad_p, hoflags, admode );
admode = mode;
st_invalid = ad_mode_st(ad_p, &admode, &st_dir);
admode = mode;
}
+ hoflags = (oflags & ~(O_RDONLY | O_WRONLY)) | O_RDWR;
+ ad->ad_resource_fork.adf_fd = open( ad_p, hoflags, admode );
+
if (ad->ad_resource_fork.adf_fd < 0 ) {
if ((errno == EACCES || errno == EROFS) && !(oflags & O_RDWR)) {
hoflags = oflags;
errno = err;
return -1;
}
-
+ adf_lock_init(&ad->ad_resource_fork);
AD_SET(ad->ad_resource_fork.adf_off);
ad->ad_resource_fork.adf_flags = hoflags;
if ((oflags & O_CREAT) && !st_invalid) {
return 0 ;
}
-/* -----------------------------------
- * return only metadata but try very hard
+/*!
+ * @brief open metadata, possibly as root
+ *
+ * Return only metadata but try very hard ie at first try as user, then try as root.
+ *
+ * @param name name of file/dir
+ * @param flags ADFLAGS_DIR: name is a directory \n
+ * ADFLAGS_CREATE: force creation of header file, but only as user, not as root\n
+ * ADFLAGS_OPENFORKS: test if name is open by another afpd process
+ *
+ * @param adp pointer to struct adouble
+ *
+ * @note caller MUST pass ADFLAGS_DIR for directories. Whether ADFLAGS_CREATE really creates
+ * a adouble file depends on various other volume options, eg. ADVOL_CACHE
*/
int ad_metadata(const char *name, int flags, struct adouble *adp)
{
uid_t uid;
- int ret, err;
- int dir = flags & ADFLAGS_DIR;
+ int ret, err, dir;
+ int create = O_RDONLY;
- /* Open with O_CREAT, thus enumarating a dir will create missing adouble files, see: */
- /* http://marc.info/?l=netatalk-devel&m=124039156832408&w=2 */
- if ((ret = ad_open(name, ADFLAGS_HF | dir, O_RDWR | O_CREAT, 0666, adp)) < 0 && errno == EACCES) {
+ dir = flags & ADFLAGS_DIR;
+
+ /* Check if we shall call ad_open with O_CREAT */
+ if ( (adp->ad_options & ADVOL_CACHE)
+ && ! (adp->ad_options & ADVOL_NOADOUBLE)
+ && (flags & ADFLAGS_CREATE) ) {
+ create = O_CREAT | O_RDWR;
+ }
+ if ((ret = ad_open(name, ADFLAGS_HF | dir, create, 0666, adp)) < 0 && errno == EACCES) {
uid = geteuid();
if (seteuid(0)) {
LOG(log_error, logtype_default, "ad_metadata(%s): seteuid failed %s", name, strerror(errno));
return ret;
}
+/*
+ * @brief openat like wrapper for ad_metadata
+ */
+int ad_metadataat(int dirfd, const char *name, int flags, struct adouble *adp)
+{
+ int ret = 0;
+ int cwdfd = -1;
+
+ if (dirfd != -1) {
+ if ((cwdfd = open(".", O_RDONLY) == -1) || (fchdir(dirfd) != 0)) {
+ ret = -1;
+ goto exit;
+ }
+ }
+
+ if (ad_metadata(name, flags, adp) < 0) {
+ ret = -1;
+ goto exit;
+ }
+
+ if (dirfd != -1) {
+ if (fchdir(cwdfd) != 0) {
+ LOG(log_error, logtype_afpd, "ad_openat: cant chdir back, exiting");
+ exit(EXITERR_SYS);
+ }
+ }
+
+exit:
+ if (cwdfd != -1)
+ close(cwdfd);
+
+ return ret;
+
+}
+
/* ----------------------------------- */
static int new_rfork(const char *path, struct adouble *ad, int adflags)
{
memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
}
- if (stat(path, &st) < 0) {
+ if (lstat(path, &st) < 0) {
return -1;
}
return ad->ad_ops->ad_header_read(ad, NULL);
}
+
+int ad_openat(int dirfd, /* dir fd openat like */
+ const char *path,
+ int adflags,
+ int oflags,
+ int mode,
+ struct adouble *ad)
+{
+ int ret = 0;
+ int cwdfd = -1;
+
+ if (dirfd != -1) {
+ if ((cwdfd = open(".", O_RDONLY) == -1) || (fchdir(dirfd) != 0)) {
+ ret = -1;
+ goto exit;
+ }
+ }
+
+ if (ad_open(path, adflags, oflags, mode, ad) < 0) {
+ ret = -1;
+ goto exit;
+ }
+
+ if (dirfd != -1) {
+ if (fchdir(cwdfd) != 0) {
+ LOG(log_error, logtype_afpd, "ad_openat: cant chdir back, exiting");
+ exit(EXITERR_SYS);
+ }
+ }
+
+exit:
+ if (cwdfd != -1)
+ close(cwdfd);
+
+ return ret;
+}