X-Git-Url: https://arthur.barton.de/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=libatalk%2Fadouble%2Fad_open.c;h=086b5d850fd02c0996aacd4a6c90e2e046a00b52;hb=9d78f9b52ea18b442fc91c6a1e8d8b607f2f90e8;hp=51a72a66ba06d7c2331a94e88f8a90235a0c6e63;hpb=7a6286113e00bd5b072e73f10b73ec1cd9a2aa88;p=netatalk.git diff --git a/libatalk/adouble/ad_open.c b/libatalk/adouble/ad_open.c index 51a72a66..086b5d85 100644 --- a/libatalk/adouble/ad_open.c +++ b/libatalk/adouble/ad_open.c @@ -1,5 +1,5 @@ /* - * $Id: ad_open.c,v 1.30.6.10 2004-05-03 14:04:35 didg Exp $ + * $Id: ad_open.c,v 1.30.6.18.2.8 2008-11-25 15:16:33 didg Exp $ * * Copyright (c) 1999 Adrian Sun (asun@u.washington.edu) * Copyright (c) 1990,1991 Regents of The University of Michigan. @@ -41,6 +41,7 @@ #include #include "ad_private.h" +#include #ifndef MAX #define MAX(a, b) ((a) < (b) ? (b) : (a)) @@ -118,21 +119,8 @@ /* we keep local copies of a bunch of stuff so that we can initialize things * correctly. */ -/* Bits in the finderinfo data. - * see etc/afpd/{directory.c,file.c} for the finderinfo structure - * layout. */ -#define FINDERINFO_CUSTOMICON 0x4 -#define FINDERINFO_CLOSEDVIEW 0x100 - -/* offsets in finderinfo */ -#define FINDERINFO_FRTYPEOFF 0 -#define FINDERINFO_FRCREATOFF 4 -#define FINDERINFO_FRFLAGOFF 8 -#define FINDERINFO_FRVIEWOFF 14 - /* invisible bit for dot files */ #define ATTRBIT_INVISIBLE (1 << 0) -#define FINDERINFO_INVISIBLE (1 << 14) /* this is to prevent changing timezones from causing problems with localtime volumes. the screw-up is 30 years. we use a delta of 5 @@ -209,6 +197,7 @@ static const struct entry entry_order_osx[ADEID_NUM_OSX +1] = { #if AD_VERSION == AD_VERSION2 +/* update a version 2 adouble resource fork with our private entries */ static int ad_update(struct adouble *ad, const char *path) { struct stat st; @@ -218,13 +207,20 @@ static int ad_update(struct adouble *ad, const char *path) static off_t entry_len[ADEID_MAX]; static char databuf[ADEID_MAX][256], *buf; int fd; + int ret = -1; /* check to see if we should convert this header. */ - if (!path || (ad->ad_flags != AD_VERSION2)) + if (!path || ad->ad_flags != AD_VERSION2) return 0; - if (ad->ad_eid[ADEID_RFORK].ade_off) - shiftdata = ADEDOFF_RFORK_V2 -ad->ad_eid[ADEID_RFORK].ade_off; + if (!(ad->ad_hf.adf_flags & O_RDWR)) { + /* we were unable to open the file read write the last time */ + return 0; + } + + if (ad->ad_eid[ADEID_RFORK].ade_off) { + shiftdata = ADEDOFF_RFORK_V2 -ad->ad_eid[ADEID_RFORK].ade_off; + } memcpy(&nentries, ad->ad_data + ADEDOFF_NENTRIES, sizeof( nentries )); nentries = ntohs( nentries ); @@ -239,26 +235,42 @@ static int ad_update(struct adouble *ad, const char *path) if (ad_tmplock(ad, ADEID_RFORK, ADLOCK_WR, 0, 0, 0) < 0) goto bail_err; - if ((fd = open(path, O_RDWR)) < 0) - goto bail_lock; + fd = ad->ad_hf.adf_fd; - if (fstat(fd, &st) || - sys_ftruncate(fd, st.st_size + shiftdata) < 0) { - goto bail_open; + if (fstat(fd, &st)) { + goto bail_lock; } + if (st.st_size > 0x7fffffff) { - LOG(log_debug, logtype_default, "ad_update: file '%s' too big for update.", path); - goto bail_truncate; + LOG(log_debug, logtype_default, "ad_update: file '%s' too big for update.", path); + errno = EIO; + goto bail_lock; } - /* last place for failure. */ + + off = ad->ad_eid[ADEID_RFORK].ade_off; + if (off > st.st_size) { + LOG(log_error, logtype_default, "ad_update: invalid resource fork offset. (off: %u)", off); + errno = EIO; + goto bail_lock; + } + + if (ad->ad_eid[ADEID_RFORK].ade_len > st.st_size - off) { + LOG(log_error, logtype_default, "ad_update: invalid resource fork length. (rfork len: %u)", ad->ad_eid[ADEID_RFORK].ade_len); + errno = EIO; + goto bail_lock; + } + if ((void *) (buf = (char *) mmap(NULL, st.st_size + shiftdata, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) { - goto bail_truncate; + goto bail_lock; } - off = ad->ad_eid[ADEID_RFORK].ade_off; + /* last place for failure. */ + if (sys_ftruncate(fd, st.st_size + shiftdata) < 0) { + goto bail_lock; + } /* move the RFORK. this assumes that the RFORK is at the end */ if (off) { @@ -266,7 +278,6 @@ static int ad_update(struct adouble *ad, const char *path) } munmap(buf, st.st_size + shiftdata); - close(fd); /* now, fix up our copy of the header */ memset(ad->ad_filler, 0, sizeof(ad->ad_filler)); @@ -296,48 +307,46 @@ static int ad_update(struct adouble *ad, const char *path) } /* rebuild the header and cleanup */ - ad_flush(ad, ADFLAGS_HF ); - ad_tmplock(ad, ADEID_RFORK, ADLOCK_CLR, 0, 0, 0); - LOG(log_debug, logtype_default, "updated AD2 header %s", path); + ad_flush(ad, ADFLAGS_HF ); + ret = 0; - return 0; - -bail_truncate: - sys_ftruncate(fd, st.st_size); -bail_open: - close(fd); bail_lock: ad_tmplock(ad, ADEID_RFORK, ADLOCK_CLR, 0, 0, 0); bail_err: - return -1; + return ret; } - -/* FIXME work only if < 2GB */ -static int ad_v1tov2(struct adouble *ad, const char *path) +/* ------------------------------------------ + FIXME work only if < 2GB +*/ +static int ad_convert(struct adouble *ad, const char *path) { struct stat st; u_int16_t attr; char *buf; int fd, off; + int ret = -1; /* use resource fork offset from file */ int shiftdata; + int toV2; + int toV1; - /* check to see if we should convert this header. */ - if (!path || (ad->ad_version != AD_VERSION1)) - return 0; - - /* we want version1 anyway */ - if (ad->ad_flags != AD_VERSION2) + if (!path) { return 0; + } + + if (!(ad->ad_hf.adf_flags & ( O_RDWR))) { + /* we were unable to open the file read write the last time */ + return 0; + } + /* check to see if we should convert this header. */ + toV2 = ad->ad_version == AD_VERSION1 && ad->ad_flags == AD_VERSION2; + toV1 = ad->ad_version == AD_VERSION2 && ad->ad_flags == AD_VERSION1; - if (!ad->ad_flags) { - /* we don't really know what we want */ - ad->ad_flags = ad->ad_version; + if (!toV2 && !toV1) return 0; - } /* convert from v1 to v2. what does this mean? * 1) change FILEI into FILEDATESI @@ -350,9 +359,9 @@ static int ad_v1tov2(struct adouble *ad, const char *path) /* bail if we can't get a lock */ if (ad_tmplock(ad, ADEID_RFORK, ADLOCK_WR, 0, 0, 0) < 0) goto bail_err; - - if ((fd = open(path, O_RDWR)) < 0) - goto bail_lock; + + /* we reuse fd from the resource fork */ + fd = ad->ad_hf.adf_fd; if (ad->ad_eid[ADEID_RFORK].ade_off) { shiftdata = ADEDOFF_RFORK_V2 -ad->ad_eid[ADEID_RFORK].ade_off; @@ -361,32 +370,49 @@ static int ad_v1tov2(struct adouble *ad, const char *path) shiftdata = ADEDOFF_RFORK_V2 -ADEDOFF_RFORK_V1; /* 136 */ } - if (fstat(fd, &st) || - sys_ftruncate(fd, st.st_size + shiftdata) < 0) { - goto bail_open; + if (fstat(fd, &st)) { + goto bail_lock; } - if (st.st_size > 0x7fffffff) { + + if (st.st_size > 0x7fffffff -shiftdata) { LOG(log_debug, logtype_default, "ad_v1tov2: file too big."); - goto bail_truncate; + errno = EIO; + goto bail_lock; + } + + off = ad->ad_eid[ADEID_RFORK].ade_off; + + if (off > st.st_size) { + LOG(log_error, logtype_default, "ad_v1tov2: invalid resource fork offset. (off: %u)", off); + errno = EIO; + goto bail_lock; + } + + if (ad->ad_eid[ADEID_RFORK].ade_len > st.st_size - off) { + LOG(log_error, logtype_default, "ad_v1tov2: invalid resource fork length. (rfork len: %u)", ad->ad_eid[ADEID_RFORK].ade_len); + errno = EIO; + goto bail_lock; } - /* last place for failure. */ if ((void *) (buf = (char *) mmap(NULL, st.st_size + shiftdata, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) { - goto bail_truncate; + goto bail_lock; } - - off = ad->ad_eid[ADEID_RFORK].ade_off; + /* last place for failure. */ + + if (sys_ftruncate(fd, st.st_size + shiftdata) < 0) { + goto bail_lock; + } + /* move the RFORK. this assumes that the RFORK is at the end */ if (off) { memmove(buf + ADEDOFF_RFORK_V2, buf + off, ad->ad_eid[ADEID_RFORK].ade_len); } munmap(buf, st.st_size + shiftdata); - close(fd); /* now, fix up our copy of the header */ memset(ad->ad_filler, 0, sizeof(ad->ad_filler)); @@ -423,8 +449,8 @@ static int ad_v1tov2(struct adouble *ad, const char *path) ad->ad_eid[ADEID_FINDERI].ade_off = ADEDOFF_FINDERI_V2; ad->ad_eid[ADEID_RFORK].ade_off = ADEDOFF_RFORK_V2; - /* switch to v2 */ - ad->ad_version = AD_VERSION2; + /* switch to dest version */ + ad->ad_version = (toV2)?AD_VERSION2:AD_VERSION1; /* move our data buffer to make space for the new entries. */ memmove(ad->ad_data + ADEDOFF_NAME_V2, ad->ad_data + ADEDOFF_NAME_V1, @@ -441,24 +467,24 @@ static int ad_v1tov2(struct adouble *ad, const char *path) /* rebuild the header and cleanup */ ad_flush(ad, ADFLAGS_HF ); - ad_tmplock(ad, ADEID_RFORK, ADLOCK_CLR, 0, 0, 0); - - return 0; + ret = 0; -bail_truncate: - sys_ftruncate(fd, st.st_size); -bail_open: - close(fd); bail_lock: ad_tmplock(ad, ADEID_RFORK, ADLOCK_CLR, 0, 0, 0); bail_err: - return -1; + return ret; } #endif /* AD_VERSION == AD_VERSION2 */ +/* --------------------------- */ #ifdef ATACC mode_t ad_hf_mode (mode_t mode) { + /* we always need RW mode for file owner */ +#if 0 + mode |= S_IRUSR; +#endif + mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH); /* fnctl lock need write access */ if ((mode & S_IRUSR)) mode |= S_IWUSR; @@ -500,12 +526,13 @@ static void parse_entries(struct adouble *ad, char *buf, len = ntohl( len ); buf += sizeof( len ); - if ( 0 < eid && eid < ADEID_MAX ) { + if (eid && eid < ADEID_MAX && ( (off < sizeof(ad->ad_data) && + off +len <= sizeof(ad->ad_data)) || eid == ADEID_RFORK)) { ad->ad_eid[ eid ].ade_off = off; ad->ad_eid[ eid ].ade_len = len; } else if (!warning) { warning = 1; - LOG(log_debug, logtype_default, "ad_refresh: nentries %hd eid %d\n", + LOG(log_debug, logtype_default, "ad_refresh: nentries %hd eid %d", nentries, eid ); } } @@ -599,8 +626,9 @@ static int ad_header_read(struct adouble *ad, struct stat *hst) if (!ad_getentryoff(ad, ADEID_RFORK) || (ad_getentryoff(ad, ADEID_RFORK) > sizeof(ad->ad_data)) ) { - LOG(log_debug, logtype_default, "ad_header_read: problem with rfork entry offset."); - return -1; + errno = EIO; + LOG(log_debug, logtype_default, "ad_header_read: problem with rfork entry offset."); + return -1; } if (ad_getentryoff(ad, ADEID_RFORK) > header_len) { @@ -684,9 +712,7 @@ ad_path( path, adflags ) * ._name */ char * -ad_path_osx( path, adflags ) - const char *path; - int adflags; +ad_path_osx( const char *path, int adflags _U_) { static char pathbuf[ MAXPATHLEN + 1]; char c, *slash, buf[MAXPATHLEN + 1]; @@ -696,19 +722,19 @@ ad_path_osx( path, adflags ) getcwd(buf, MAXPATHLEN); } else { - strncpy(buf, path, MAXPATHLEN); + strlcpy(buf, path, MAXPATHLEN +1); } if (NULL != ( slash = strrchr( buf, '/' )) ) { c = *++slash; *slash = '\0'; - strncpy( pathbuf, buf, MAXPATHLEN); + strlcpy( pathbuf, buf, MAXPATHLEN +1); *slash = c; } else { pathbuf[ 0 ] = '\0'; slash = buf; } - strncat( pathbuf, "._", MAXPATHLEN - strlen(pathbuf)); - strncat( pathbuf, slash, MAXPATHLEN - strlen(pathbuf)); + strlcat( pathbuf, "._", MAXPATHLEN +1); + strlcat( pathbuf, slash, MAXPATHLEN +1); return pathbuf; } @@ -805,7 +831,7 @@ int ret = 0; #ifdef EMULATE_SUIDDIR uid_t id; - if (default_uid != -1) { + 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 ); @@ -871,12 +897,14 @@ struct stat stbuf; /* ----------------- */ static int ad_error(struct adouble *ad, int adflags) { +int err = errno; if ((adflags & ADFLAGS_NOHF)) { /* FIXME double check : set header offset ?*/ return 0; } if ((adflags & ADFLAGS_DF)) { ad_close( ad, ADFLAGS_DF ); + err = errno; } return -1 ; } @@ -889,7 +917,7 @@ static int new_rfork(const char *path, struct adouble *ad, int adflags); #define AD_SET(a) a = 0 #endif -void ad_init(struct adouble *ad, int flags) +void ad_init(struct adouble *ad, int flags, int options) { memset( ad, 0, sizeof( struct adouble ) ); ad->ad_flags = flags; @@ -899,6 +927,7 @@ void ad_init(struct adouble *ad, int flags) else { ad->ad_path = ad_path; } + ad->ad_options = options; } /* ------------------- @@ -913,7 +942,7 @@ int ad_open( path, adflags, oflags, mode, ad ) struct stat st; char *slash, *ad_p; int hoflags, admode; - int st_invalid; + int st_invalid = -1; int open_df = 0; if (ad->ad_inited != AD_INITED) { @@ -929,12 +958,17 @@ int ad_open( path, adflags, oflags, mode, ad ) if (ad_dfileno(ad) == -1) { hoflags = (oflags & ~(O_RDONLY | O_WRONLY)) | O_RDWR; admode = mode; - st_invalid = ad_mode_st(path, &admode, &st); + if ((oflags & O_CREAT)) { + st_invalid = ad_mode_st(path, &admode, &st); + if ((ad->ad_options & ADVOL_UNIXPRIV)) { + admode = mode; + } + } ad->ad_df.adf_fd =open( path, hoflags, admode ); if (ad->ad_df.adf_fd < 0 ) { if ((errno == EACCES || errno == EROFS) && !(oflags & O_RDWR)) { hoflags = oflags; - ad->ad_df.adf_fd =open( path, hoflags, admode ); + ad->ad_df.adf_fd = open( path, hoflags, admode ); } } if ( ad->ad_df.adf_fd < 0) @@ -942,7 +976,7 @@ int ad_open( path, adflags, oflags, mode, ad ) AD_SET(ad->ad_df.adf_off); ad->ad_df.adf_flags = hoflags; - if ((oflags & O_CREAT) && !st_invalid) { + if (!st_invalid) { /* just created, set owner if admin (root) */ ad_chown(path, &st); } @@ -988,7 +1022,9 @@ int ad_open( path, adflags, oflags, mode, ad ) ad_p = ad->ad_path( path, adflags ); hoflags = oflags & ~O_CREAT; - hoflags = (hoflags & ~(O_RDONLY | O_WRONLY)) | O_RDWR; + if (!(adflags & ADFLAGS_RDONLY)) { + hoflags = (hoflags & ~(O_RDONLY | O_WRONLY)) | O_RDWR; + } ad->ad_hf.adf_fd = open( ad_p, hoflags, 0 ); if (ad->ad_hf.adf_fd < 0 ) { if ((errno == EACCES || errno == EROFS) && !(oflags & O_RDWR)) { @@ -1005,31 +1041,35 @@ int ad_open( path, adflags, oflags, mode, ad ) * if ((oflags & O_CREAT) ==> (oflags & O_RDWR) */ admode = mode; + errno = 0; st_invalid = ad_mode_st(ad_p, &admode, &st); + if ((ad->ad_options & ADVOL_UNIXPRIV)) { + admode = mode; + } admode = ad_hf_mode(admode); - errno = 0; - ad->ad_hf.adf_fd = open( ad_p, oflags,admode ); - if ( ad->ad_hf.adf_fd < 0 && ad->ad_flags != AD_VERSION2_OSX) { + if ( errno == ENOENT && !(adflags & ADFLAGS_NOADOUBLE) && ad->ad_flags != AD_VERSION2_OSX) { /* * Probably .AppleDouble doesn't exist, try to * mkdir it. */ - if (errno == ENOENT && (adflags & ADFLAGS_NOADOUBLE) == 0) { - if (NULL == ( slash = strrchr( ad_p, '/' )) ) { - return ad_error(ad, adflags); - } - *slash = '\0'; - errno = 0; - if ( ad_mkdir( ad_p, 0777 ) < 0 ) { - return ad_error(ad, adflags); - } - *slash = '/'; - admode = mode; - st_invalid = ad_mode_st(ad_p, &admode, &st); - admode = ad_hf_mode(admode); - ad->ad_hf.adf_fd = open( ad_p, oflags, admode); + if (NULL == ( slash = strrchr( ad_p, '/' )) ) { + return ad_error(ad, adflags); } + *slash = '\0'; + errno = 0; + if ( ad_mkdir( ad_p, 0777 ) < 0 ) { + return ad_error(ad, adflags); + } + *slash = '/'; + admode = mode; + st_invalid = ad_mode_st(ad_p, &admode, &st); + if ((ad->ad_options & ADVOL_UNIXPRIV)) { + admode = mode; + } + admode = ad_hf_mode(admode); } + /* retry with O_CREAT */ + ad->ad_hf.adf_fd = open( ad_p, oflags,admode ); if ( ad->ad_hf.adf_fd < 0 ) { return ad_error(ad, adflags); } @@ -1058,24 +1098,56 @@ int ad_open( path, adflags, oflags, mode, ad ) * instead of reading it. */ if (new_rfork(path, ad, adflags) < 0) { + int err = errno; /* the file is already deleted, perm, whatever, so return an error*/ ad_close(ad, adflags); + errno = err; return -1; } } else { /* Read the adouble header in and parse it.*/ if ((ad_header_read( ad , &st) < 0) #if AD_VERSION == AD_VERSION2 - || (ad_v1tov2(ad, ad_p) < 0) || (ad_update(ad, ad_p) < 0) + || (ad_convert(ad, ad_p) < 0) || (ad_update(ad, ad_p) < 0) #endif /* AD_VERSION == AD_VERSION2 */ ) { + int err = errno; + ad_close( ad, adflags ); - return( -1 ); + errno = err; + return -1; } } return 0 ; } +/* ----------------------------------- + * return only metadata but try very hard +*/ +int ad_metadata(const char *name, int flags, struct adouble *adp) +{ + uid_t uid; + int ret, err; + + if ((ret = ad_open(name, ADFLAGS_HF | (flags), O_RDONLY, 0, adp)) < 0 && errno == EACCES) { + uid = geteuid(); + if (seteuid(0)) { + LOG(log_error, logtype_default, "ad_metadata(%s): seteuid failed %s", name, strerror(errno)); + errno = EACCES; + return -1; + } + /* we are root open read only */ + ret = ad_open(name, ADFLAGS_HF|ADFLAGS_RDONLY|(flags), O_RDONLY, 0, adp); + err = errno; + if ( seteuid(uid) < 0) { + LOG(log_error, logtype_default, "ad_metadata: can't seteuid back"); + exit(EXITERR_SYS); + } + errno = err; + } + return ret; +} + /* ----------------------------------- */ static int new_rfork(const char *path, struct adouble *ad, int adflags) { @@ -1115,17 +1187,18 @@ static int new_rfork(const char *path, struct adouble *ad, int adflags) &ashort, sizeof(ashort)); } else { /* set default creator/type fields */ - memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRTYPEOFF,"TEXT", 4); - memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRCREATOFF,"UNIX", 4); + memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRTYPEOFF,"\0\0\0\0", 4); + memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRCREATOFF,"\0\0\0\0", 4); } /* make things invisible */ - if ((*path == '.') && strcmp(path, ".") && strcmp(path, "..")) { + if ((ad->ad_options & ADVOL_INVDOTS) && !(adflags & ADFLAGS_CREATE) && + (*path == '.') && strcmp(path, ".") && strcmp(path, "..")) + { ashort = htons(ATTRBIT_INVISIBLE); ad_setattr(ad, ashort); ashort = htons(FINDERINFO_INVISIBLE); - memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRFLAGOFF, - &ashort, sizeof(ashort)); + memcpy(ad_entry(ad, ADEID_FINDERI) + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort)); } if (stat(path, &st) < 0) { @@ -1145,7 +1218,7 @@ static int new_rfork(const char *path, struct adouble *ad, int adflags) int ad_refresh(struct adouble *ad) { - if (ad->ad_hf.adf_fd < -1) + if (ad->ad_hf.adf_fd < 0) return -1; return ad_header_read(ad, NULL);