X-Git-Url: https://arthur.barton.de/cgi-bin/gitweb.cgi?p=netatalk.git;a=blobdiff_plain;f=etc%2Fafpd%2Ffile.c;h=915219f595d11d7ea53876adc269a26a8b484b0b;hp=0f3280fe814b64ccfda02c1038862be305bc1ed3;hb=2bf71d3ccf20c072bc67a9d075b6ac8b0798021e;hpb=91fad6bd493f0cdd0726f697d5ef64f3df90d075 diff --git a/etc/afpd/file.c b/etc/afpd/file.c index 0f3280fe..915219f5 100644 --- a/etc/afpd/file.c +++ b/etc/afpd/file.c @@ -1,5 +1,5 @@ /* - * $Id: file.c,v 1.40 2002-03-05 02:04:46 jmarcus Exp $ + * $Id: file.c,v 1.134 2010-02-10 14:05:37 franklahm Exp $ * * Copyright (c) 1990,1993 Regents of The University of Michigan. * All Rights Reserved. See COPYRIGHT. @@ -11,9 +11,6 @@ #include #include -#ifdef HAVE_UNISTD_H -#include -#endif /* HAVE_UNISTD_H */ /* STDC check */ #if STDC_HEADERS @@ -24,6 +21,7 @@ #define strrchr index #endif /* HAVE_STRCHR */ char *strchr (), *strrchr (); + #ifndef HAVE_MEMCPY #define memcpy(d,s,n) bcopy ((s), (d), (n)) #define memmove(d,s,n) bcopy ((s), (d), (n)) @@ -31,26 +29,17 @@ char *strchr (), *strrchr (); #endif /* STDC_HEADERS */ #include -#ifdef HAVE_FCNTL_H -#include -#endif /* HAVE_FCNTL_H */ -#include -#include #include - -#include -#include -#include #include -#include -#include #include +#include +#include #include #include -#ifdef CNID_DB #include -#endif /* CNID_DB */ +#include + #include "directory.h" #include "desktop.h" #include "volume.h" @@ -58,16 +47,7 @@ char *strchr (), *strrchr (); #include "file.h" #include "filedir.h" #include "globals.h" - -/* check for mtab DID code */ -#ifdef DID_MTAB -#include "parse_mtab.h" -#endif /* DID_MTAB */ - -#ifdef FORCE_UIDGID -#warning UIDGID -#include "uid.h" -#endif /* FORCE_UIDGID */ +#include "unix.h" /* the format for the finderinfo fields (from IM: Toolbox Essentials): * field bytes subfield bytes @@ -88,51 +68,219 @@ char *strchr (), *strrchr (); * putawayID 4 home directory id */ -const u_char ufinderi[] = { - 'T', 'E', 'X', 'T', 'U', 'N', 'I', 'X', +const u_char ufinderi[ADEDLEN_FINDERI] = { 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; -int getfilparams(struct vol *vol, - u_int16_t bitmap, - char *path, struct dir *dir, struct stat *st, - char *buf, int *buflen ) +static const u_char old_ufinderi[] = { + 'T', 'E', 'X', 'T', 'U', 'N', 'I', 'X' + }; + +/* ---------------------- +*/ +static int default_type(void *finder) +{ + if (!memcmp(finder, ufinderi, 8) || !memcmp(finder, old_ufinderi, 8)) + return 1; + return 0; +} + +/* FIXME path : unix or mac name ? (for now it's unix name ) */ +void *get_finderinfo(const struct vol *vol, const char *upath, struct adouble *adp, void *data, int islink) { -#ifndef USE_LASTDID - struct stat hst, lst, *lstp; -#else /* USE_LASTDID */ - struct stat hst; -#endif /* USE_LASTDID */ - struct adouble ad, *adp; - struct ofork *of; struct extmap *em; - char *data, *nameoff = NULL, *upath; - int bit = 0, isad = 1; + void *ad_finder = NULL; + int chk_ext = 0; + + if (adp) + ad_finder = ad_entry(adp, ADEID_FINDERI); + + if (ad_finder) { + memcpy(data, ad_finder, ADEDLEN_FINDERI); + /* default type ? */ + if (default_type(ad_finder)) + chk_ext = 1; + } + else { + memcpy(data, ufinderi, ADEDLEN_FINDERI); + chk_ext = 1; + if (vol_inv_dots(vol) && *upath == '.') { /* make it invisible */ + u_int16_t ashort; + + ashort = htons(FINDERINFO_INVISIBLE); + memcpy((char *)data + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort)); + } + } + + if (islink){ + u_int16_t linkflag; + memcpy(&linkflag, (char *)data + FINDERINFO_FRFLAGOFF, 2); + linkflag |= htons(FINDERINFO_ISALIAS); + memcpy((char *)data + FINDERINFO_FRFLAGOFF, &linkflag, 2); + memcpy((char *)data + FINDERINFO_FRTYPEOFF,"slnk",4); + memcpy((char *)data + FINDERINFO_FRCREATOFF,"rhap",4); + chk_ext = 0; + } + + /** Only enter if no appledouble information and no finder information found. */ + if (chk_ext && (em = getextmap( upath ))) { + memcpy(data, em->em_type, sizeof( em->em_type )); + memcpy((char *)data + 4, em->em_creator, sizeof(em->em_creator)); + } + return data; +} + +/* --------------------- +*/ +char *set_name(const struct vol *vol, char *data, cnid_t pid, char *name, cnid_t id, u_int32_t utf8) +{ + u_int32_t aint; + char *tp = NULL; + char *src = name; + aint = strlen( name ); + + if (!utf8) { + /* want mac name */ + if (utf8_encoding()) { + /* but name is an utf8 mac name */ + char *u, *m; + + /* global static variable... */ + tp = strdup(name); + if (!(u = mtoupath(vol, name, pid, 1)) || !(m = utompath(vol, u, id, 0))) { + aint = 0; + } + else { + aint = strlen(m); + src = m; + } + + } + if (aint > MACFILELEN) + aint = MACFILELEN; + *data++ = aint; + } + else { + u_int16_t temp; + + if (aint > 255) /* FIXME safeguard, anyway if no ascii char it's game over*/ + aint = 255; + + utf8 = vol->v_kTextEncoding; + memcpy(data, &utf8, sizeof(utf8)); + data += sizeof(utf8); + + temp = htons(aint); + memcpy(data, &temp, sizeof(temp)); + data += sizeof(temp); + } + + memcpy( data, src, aint ); + data += aint; + if (tp) { + strcpy(name, tp); + free(tp); + } + return data; +} + +/* + * FIXME: PDINFO is UTF8 and doesn't need adp +*/ +#define PARAM_NEED_ADP(b) ((b) & ((1 << FILPBIT_ATTR) |\ + (1 << FILPBIT_CDATE) |\ + (1 << FILPBIT_MDATE) |\ + (1 << FILPBIT_BDATE) |\ + (1 << FILPBIT_FINFO) |\ + (1 << FILPBIT_RFLEN) |\ + (1 << FILPBIT_EXTRFLEN) |\ + (1 << FILPBIT_PDINFO) |\ + (1 << FILPBIT_FNUM) |\ + (1 << FILPBIT_UNIXPR))) + +/* -------------------------- */ +u_int32_t get_id(struct vol *vol, struct adouble *adp, const struct stat *st, + const cnid_t did, char *upath, const int len) +{ + u_int32_t adcnid; + u_int32_t dbcnid = CNID_INVALID; + + if (vol->v_cdb != NULL) { + /* prime aint with what we think is the cnid, set did to zero for + catching moved files */ + adcnid = ad_getid(adp, st->st_dev, st->st_ino, 0, vol->v_stamp); + + dbcnid = cnid_add(vol->v_cdb, st, did, upath, len, adcnid); + /* Throw errors if cnid_add fails. */ + if (dbcnid == CNID_INVALID) { + switch (errno) { + case CNID_ERR_CLOSE: /* the db is closed */ + break; + case CNID_ERR_PARAM: + LOG(log_error, logtype_afpd, "get_id: Incorrect parameters passed to cnid_add"); + afp_errno = AFPERR_PARAM; + return CNID_INVALID; + case CNID_ERR_PATH: + afp_errno = AFPERR_PARAM; + return CNID_INVALID; + default: + afp_errno = AFPERR_MISC; + return CNID_INVALID; + } + } + else if (adp && (adcnid != dbcnid)) { + /* Update the ressource fork. For a folder adp is always null */ + LOG(log_debug, logtype_afpd, "get_id: calling ad_setid. adcnid: %u, dbcnid: %u", htonl(adcnid), htonl(dbcnid)); + if (ad_setid(adp, st->st_dev, st->st_ino, dbcnid, did, vol->v_stamp)) { + ad_flush(adp); + } + } + } + return dbcnid; +} + +/* -------------------------- */ +int getmetadata(struct vol *vol, + u_int16_t bitmap, + struct path *path, struct dir *dir, + char *buf, size_t *buflen, struct adouble *adp) +{ + char *data, *l_nameoff = NULL, *upath; + char *utf_nameoff = NULL; + int bit = 0; u_int32_t aint; + cnid_t id = 0; u_int16_t ashort; u_char achar, fdType[4]; + u_int32_t utf8 = 0; + struct stat *st; + struct maccess ma; #ifdef DEBUG - LOG(log_info, logtype_default, "begin getfilparams:"); + LOG(log_debug9, logtype_afpd, "begin getmetadata:"); #endif /* DEBUG */ - upath = mtoupath(vol, path); - if ((of = of_findname(vol, curdir, path))) { - adp = of->of_ad; - } else { - memset(&ad, 0, sizeof(ad)); - adp = &ad; - } - - if ( ad_open( upath, ADFLAGS_HF, O_RDONLY, 0, adp) < 0 ) { - isad = 0; - } else if ( fstat( ad_hfileno( adp ), &hst ) < 0 ) { - LOG(log_error, logtype_default, "getfilparams fstat: %s", strerror(errno) ); - } + upath = path->u_name; + st = &path->st; data = buf; + + if ( ((bitmap & ( (1 << FILPBIT_FINFO)|(1 << FILPBIT_LNAME)|(1 <m_name) + || (bitmap & ( (1 << FILPBIT_LNAME) ) && utf8_encoding()) /* FIXME should be m_name utf8 filename */ + || (bitmap & (1 << FILPBIT_FNUM))) { + if (!path->id) + id = get_id(vol, adp, st, dir->d_did, upath, strlen(upath)); + else + id = path->id; + if (id == CNID_INVALID) + return afp_errno; + if (!path->m_name) { + path->m_name = utompath(vol, upath, id, utf8_encoding()); + } + } while ( bitmap != 0 ) { while (( bitmap & 1 ) == 0 ) { bitmap = bitmap>>1; @@ -141,14 +289,26 @@ int getfilparams(struct vol *vol, switch ( bit ) { case FILPBIT_ATTR : - if ( isad ) { + if ( adp ) { ad_getattr(adp, &ashort); - } else if (*upath == '.') { + } else if (vol_inv_dots(vol) && *upath == '.') { ashort = htons(ATTRBIT_INVISIBLE); } else ashort = 0; +#if 0 + /* FIXME do we want a visual clue if the file is read only + */ + struct maccess ma; + accessmode( ".", &ma, dir , NULL); + if ((ma.ma_user & AR_UWRITE)) { + accessmode( upath, &ma, dir , st); + if (!(ma.ma_user & AR_UWRITE)) { + ashort |= htons(ATTRBIT_NOWRITE); + } + } +#endif memcpy(data, &ashort, sizeof( ashort )); - data += sizeof( u_short ); + data += sizeof( ashort ); break; case FILPBIT_PDID : @@ -157,17 +317,16 @@ int getfilparams(struct vol *vol, break; case FILPBIT_CDATE : - if (!isad || (ad_getdate(adp, AD_DATE_CREATE, &aint) < 0)) + if (!adp || (ad_getdate(adp, AD_DATE_CREATE, &aint) < 0)) aint = AD_DATE_FROM_UNIX(st->st_mtime); memcpy(data, &aint, sizeof( aint )); data += sizeof( aint ); break; case FILPBIT_MDATE : - if ( isad && (ad_getdate(adp, AD_DATE_MODIFY, &aint) == 0)) { - if ((st->st_mtime > AD_DATE_TO_UNIX(aint)) && - (hst.st_mtime < st->st_mtime)) { - aint = AD_DATE_FROM_UNIX(st->st_mtime); + if ( adp && (ad_getdate(adp, AD_DATE_MODIFY, &aint) == 0)) { + if ((st->st_mtime > AD_DATE_TO_UNIX(aint))) { + aint = AD_DATE_FROM_UNIX(st->st_mtime); } } else { aint = AD_DATE_FROM_UNIX(st->st_mtime); @@ -177,34 +336,19 @@ int getfilparams(struct vol *vol, break; case FILPBIT_BDATE : - if (!isad || (ad_getdate(adp, AD_DATE_BACKUP, &aint) < 0)) + if (!adp || (ad_getdate(adp, AD_DATE_BACKUP, &aint) < 0)) aint = AD_DATE_START; memcpy(data, &aint, sizeof( int )); data += sizeof( int ); break; case FILPBIT_FINFO : - if (isad) - memcpy(data, ad_entry(adp, ADEID_FINDERI), 32); - else { - memcpy(data, ufinderi, 32); - if (*upath == '.') { /* make it invisible */ - ashort = htons(FINDERINFO_INVISIBLE); - memcpy(data + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort)); - } - } - - if ((!isad || (memcmp(ad_entry(adp, ADEID_FINDERI), - ufinderi, 8 ) == 0)) && - (em = getextmap( path ))) { - memcpy(data, em->em_type, sizeof( em->em_type )); - memcpy(data + 4, em->em_creator, sizeof(em->em_creator)); - } - data += 32; + get_finderinfo(vol, upath, adp, (char *)data,S_ISLNK(st->st_mode)); + data += ADEDLEN_FINDERI; break; case FILPBIT_LNAME : - nameoff = data; + l_nameoff = data; data += sizeof( u_int16_t ); break; @@ -214,83 +358,25 @@ int getfilparams(struct vol *vol, break; case FILPBIT_FNUM : - aint = 0; -#if AD_VERSION > AD_VERSION1 - /* look in AD v2 header */ - if (isad) - memcpy(&aint, ad_entry(adp, ADEID_DID), sizeof(aint)); -#endif /* AD_VERSION > AD_VERSION1 */ - -#ifdef CNID_DB - aint = cnid_add(vol->v_db, st, dir->d_did, upath, - strlen(upath), aint); - /* Throw errors if cnid_add fails. */ - if (aint == CNID_INVALID) { - switch (errno) { - case CNID_ERR_PARAM: - LOG(log_error, logtype_default, "getfilparams: Incorrect parameters passed to cnid_add"); - return(AFPERR_PARAM); - case CNID_ERR_PATH: - return(AFPERR_PARAM); - case CNID_ERR_DB: - case CNID_ERR_MAX: - return(AFPERR_MISC); - } - } -#endif /* CNID_DB */ - - if (aint == 0) { - /* - * What a fucking mess. First thing: DID and FNUMs are - * in the same space for purposes of enumerate (and several - * other wierd places). While we consider this Apple's bug, - * this is the work-around: In order to maintain constant and - * unique DIDs and FNUMs, we monotonically generate the DIDs - * during the session, and derive the FNUMs from the filesystem. - * Since the DIDs are small, we insure that the FNUMs are fairly - * large by setting thier high bits to the device number. - * - * AFS already does something very similar to this for the - * inode number, so we don't repeat the procedure. - * - * new algorithm: - * due to complaints over did's being non-persistent, - * here's the current hack to provide semi-persistent - * did's: - * 1) we reserve the first bit for file ids. - * 2) the next 7 bits are for the device. - * 3) the remaining 24 bits are for the inode. - * - * both the inode and device information are actually hashes - * that are then truncated to the requisite bit length. - * - * it should be okay to use lstat to deal with symlinks. - */ -#ifdef USE_LASTDID - aint = htonl(( st->st_dev << 16 ) | (st->st_ino & 0x0000ffff)); -#else /* USE_LASTDID */ - lstp = lstat(upath, &lst) < 0 ? st : &lst; -#ifdef DID_MTAB - aint = htonl( afpd_st_cnid ( lstp ) ); -#else /* DID_MTAB */ - aint = htonl(CNID(lstp, 1)); -#endif /* DID_MTAB */ -#endif /* USE_LASTDID */ - } - - memcpy(data, &aint, sizeof( aint )); - data += sizeof( aint ); + memcpy(data, &id, sizeof( id )); + data += sizeof( id ); break; case FILPBIT_DFLEN : - aint = htonl( st->st_size ); + if (st->st_size > 0xffffffff) + aint = 0xffffffff; + else + aint = htonl( st->st_size ); memcpy(data, &aint, sizeof( aint )); data += sizeof( aint ); break; case FILPBIT_RFLEN : - if ( isad ) { - aint = htonl( ad_getentrylen( adp, ADEID_RFORK )); + if ( adp ) { + if (adp->ad_rlen > 0xffffffff) + aint = 0xffffffff; + else + aint = htonl( adp->ad_rlen); } else { aint = 0; } @@ -303,100 +389,194 @@ int getfilparams(struct vol *vol, us what the PD file code should be. Everything gets a subtype of 0x0000 unless the original value was hashed to "pXYZ" when we created it. See IA, Ver 2. - */ + */ case FILPBIT_PDINFO : - if ( isad ) { - memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 ); + if (afp_version >= 30) { /* UTF8 name */ + utf8 = kTextEncodingUTF8; + utf_nameoff = data; + data += sizeof( u_int16_t ); + aint = 0; + memcpy(data, &aint, sizeof( aint )); + data += sizeof( aint ); + } + else { + if ( adp ) { + memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 ); - if ( memcmp( fdType, "TEXT", 4 ) == 0 ) { - achar = '\x04'; - ashort = 0x0000; - } - else if ( memcmp( fdType, "PSYS", 4 ) == 0 ) { - achar = '\xff'; - ashort = 0x0000; - } - else if ( memcmp( fdType, "PS16", 4 ) == 0 ) { - achar = '\xb3'; - ashort = 0x0000; - } - else if ( memcmp( fdType, "BINA", 4 ) == 0 ) { - achar = '\x00'; - ashort = 0x0000; - } - else if ( fdType[0] == 'p' ) { - achar = fdType[1]; - ashort = (fdType[2] * 256) + fdType[3]; + if ( memcmp( fdType, "TEXT", 4 ) == 0 ) { + achar = '\x04'; + ashort = 0x0000; + } + else if ( memcmp( fdType, "PSYS", 4 ) == 0 ) { + achar = '\xff'; + ashort = 0x0000; + } + else if ( memcmp( fdType, "PS16", 4 ) == 0 ) { + achar = '\xb3'; + ashort = 0x0000; + } + else if ( memcmp( fdType, "BINA", 4 ) == 0 ) { + achar = '\x00'; + ashort = 0x0000; + } + else if ( fdType[0] == 'p' ) { + achar = fdType[1]; + ashort = (fdType[2] * 256) + fdType[3]; + } + else { + achar = '\x00'; + ashort = 0x0000; + } } else { achar = '\x00'; ashort = 0x0000; } - } - else { - achar = '\x00'; - ashort = 0x0000; - } - *data++ = achar; - *data++ = 0; - memcpy(data, &ashort, sizeof( ashort )); - data += sizeof( ashort ); - memset(data, 0, sizeof( ashort )); - data += sizeof( ashort ); + *data++ = achar; + *data++ = 0; + memcpy(data, &ashort, sizeof( ashort )); + data += sizeof( ashort ); + memset(data, 0, sizeof( ashort )); + data += sizeof( ashort ); + } + break; + case FILPBIT_EXTDFLEN: + aint = htonl(st->st_size >> 32); + memcpy(data, &aint, sizeof( aint )); + data += sizeof( aint ); + aint = htonl(st->st_size); + memcpy(data, &aint, sizeof( aint )); + data += sizeof( aint ); break; + case FILPBIT_EXTRFLEN: + aint = 0; + if (adp) + aint = htonl(adp->ad_rlen >> 32); + memcpy(data, &aint, sizeof( aint )); + data += sizeof( aint ); + if (adp) + aint = htonl(adp->ad_rlen); + memcpy(data, &aint, sizeof( aint )); + data += sizeof( aint ); + break; + case FILPBIT_UNIXPR : + /* accessmode may change st_mode with ACLs */ + accessmode( upath, &ma, dir , st); + + aint = htonl(st->st_uid); + memcpy( data, &aint, sizeof( aint )); + data += sizeof( aint ); + aint = htonl(st->st_gid); + memcpy( data, &aint, sizeof( aint )); + data += sizeof( aint ); + /* FIXME: ugly hack + type == slnk indicates an OSX style symlink, + we have to add S_IFLNK to the mode, otherwise + 10.3 clients freak out. */ + + aint = st->st_mode; + if (adp) { + memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 ); + if ( memcmp( fdType, "slnk", 4 ) == 0 ) { + aint |= S_IFLNK; + } + } + aint = htonl(aint); + + memcpy( data, &aint, sizeof( aint )); + data += sizeof( aint ); + + *data++ = ma.ma_user; + *data++ = ma.ma_world; + *data++ = ma.ma_group; + *data++ = ma.ma_owner; + break; + default : - if ( isad ) { - ad_close( adp, ADFLAGS_HF ); - } return( AFPERR_BITMAP ); } bitmap = bitmap>>1; bit++; } - if ( nameoff ) { + if ( l_nameoff ) { ashort = htons( data - buf ); - memcpy(nameoff, &ashort, sizeof( ashort )); - if ((aint = strlen( path )) > MACFILELEN) - aint = MACFILELEN; - *data++ = aint; - memcpy(data, path, aint ); - data += aint; + memcpy(l_nameoff, &ashort, sizeof( ashort )); + data = set_name(vol, data, dir->d_did, path->m_name, id, 0); } - if ( isad ) { - ad_close( adp, ADFLAGS_HF ); + if ( utf_nameoff ) { + ashort = htons( data - buf ); + memcpy(utf_nameoff, &ashort, sizeof( ashort )); + data = set_name(vol, data, dir->d_did, path->m_name, id, utf8); } *buflen = data - buf; + return (AFP_OK); +} + +/* ----------------------- */ +int getfilparams(struct vol *vol, + u_int16_t bitmap, + struct path *path, struct dir *dir, + char *buf, size_t *buflen ) +{ + struct adouble ad, *adp; + int opened = 0; + int rc; #ifdef DEBUG - LOG(log_info, logtype_default, "end getfilparams:"); + LOG(log_debug9, logtype_default, "begin getfilparams:"); #endif /* DEBUG */ - return( AFP_OK ); + opened = PARAM_NEED_ADP(bitmap); + adp = NULL; + + if (opened) { + char *upath; + int flags = (bitmap & (1 << FILPBIT_ATTR))?ADFLAGS_OPENFORKS:0; + + adp = of_ad(vol, path, &ad); + upath = path->u_name; + + if ( ad_metadata( upath, flags|ADFLAGS_CREATE, adp) < 0 ) { + switch (errno) { + case EACCES: + LOG(log_error, logtype_afpd, "getfilparams(%s): %s: check resource fork permission?", + upath, strerror(errno)); + return AFPERR_ACCESS; + case EIO: + LOG(log_error, logtype_afpd, "getfilparams(%s): bad resource fork", upath); + /* fall through */ + case ENOENT: + default: + adp = NULL; + break; + } + } + } + rc = getmetadata(vol, bitmap, path, dir, buf, buflen, adp); + if ( adp ) { + ad_close_metadata( adp); + } +#ifdef DEBUG + LOG(log_debug9, logtype_afpd, "end getfilparams:"); +#endif /* DEBUG */ + + return( rc ); } -int afp_createfile(obj, ibuf, ibuflen, rbuf, rbuflen ) -AFPObj *obj; -char *ibuf, *rbuf; -int ibuflen, *rbuflen; +/* ----------------------------- */ +int afp_createfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen) { - struct stat st; struct adouble ad, *adp; struct vol *vol; struct dir *dir; - struct ofork *of; + struct ofork *of = NULL; char *path, *upath; int creatf, did, openf, retvalue = AFP_OK; u_int16_t vid; -#ifdef FORCE_UIDGID - uidgidset *uidgid; -#endif /* FORCE_UIDGID */ - -#ifdef DEBUG - LOG(log_info, logtype_default, "begin afp_createfile:"); -#endif /* DEBUG */ - + struct path *s_path; + *rbuflen = 0; ibuf++; creatf = (unsigned char) *ibuf++; @@ -404,7 +584,7 @@ int ibuflen, *rbuflen; memcpy(&vid, ibuf, sizeof( vid )); ibuf += sizeof( vid ); - if (( vol = getvolbyvid( vid )) == NULL ) { + if (NULL == ( vol = getvolbyvid( vid )) ) { return( AFPERR_PARAM ); } @@ -414,90 +594,81 @@ int ibuflen, *rbuflen; memcpy(&did, ibuf, sizeof( did)); ibuf += sizeof( did ); - if (( dir = dirsearch( vol, did )) == NULL ) { - return( AFPERR_NOOBJ ); + if (NULL == ( dir = dirlookup( vol, did )) ) { + return afp_errno; } - if (( path = cname( vol, dir, &ibuf )) == NULL ) { - return( AFPERR_NOOBJ ); + if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) { + return get_afp_errno(AFPERR_PARAM); } - if (!wincheck(vol, path)) - return AFPERR_PARAM; - - upath = mtoupath(vol, path); - - if ((vol->v_flags & AFPVOL_NOHEX) && strchr(upath, '/')) - return AFPERR_PARAM; - - if (!validupath(vol, upath)) - return AFPERR_EXIST; - - /* check for vetoed filenames */ - if (veto_file(vol->v_veto, upath)) - return AFPERR_EXIST; + if ( *s_path->m_name == '\0' ) { + return( AFPERR_BADTYPE ); + } - if ((of = of_findname(vol, curdir, path))) { + upath = s_path->u_name; + + /* if upath is deleted we already in trouble anyway */ + if ((of = of_findname(s_path))) { adp = of->of_ad; } else { - memset(&ad, 0, sizeof(ad)); + ad_init(&ad, vol->v_adouble, vol->v_ad_options); adp = &ad; } if ( creatf) { /* on a hard create, fail if file exists and is open */ - if ((stat(upath, &st) == 0) && of) + if (of) return AFPERR_BUSY; openf = O_RDWR|O_CREAT|O_TRUNC; } else { + /* on a soft create, if the file is open then ad_open won't fail + because open syscall is not called + */ + if (of) { + return AFPERR_EXIST; + } openf = O_RDWR|O_CREAT|O_EXCL; } -#ifdef FORCE_UIDGID - - /* preserve current euid, egid */ - save_uidgid ( uidgid ); - - /* perform all switching of users */ - set_uidgid ( vol ); - -#endif /* FORCE_UIDGID */ - - if ( ad_open( upath, vol_noadouble(vol)|ADFLAGS_DF|ADFLAGS_HF, + if ( ad_open( upath, ADFLAGS_DF|ADFLAGS_HF|ADFLAGS_NOHF|ADFLAGS_CREATE, openf, 0666, adp) < 0 ) { switch ( errno ) { + case EROFS: + return AFPERR_VLOCK; + case ENOENT : /* we were already in 'did folder' so chdir() didn't fail */ + return ( AFPERR_NOOBJ ); case EEXIST : -#ifdef FORCE_UIDGID - /* bring everything back to old euid, egid */ - restore_uidgid ( uidgid ); -#endif /* FORCE_UIDGID */ return( AFPERR_EXIST ); case EACCES : -#ifdef FORCE_UIDGID - /* bring everything back to old euid, egid */ - restore_uidgid ( uidgid ); -#endif /* FORCE_UIDGID */ return( AFPERR_ACCESS ); - case ENOENT: - /* on noadouble volumes, just creating the data fork is ok */ - if (vol_noadouble(vol) && (stat(upath, &st) == 0)) - goto createfile_done; - /* fallthrough */ + case EDQUOT: + case ENOSPC : + return( AFPERR_DFULL ); default : -#ifdef FORCE_UIDGID - /* bring everything back to old euid, egid */ - restore_uidgid ( uidgid ); -#endif /* FORCE_UIDGID */ return( AFPERR_PARAM ); } } - - ad_setentrylen( adp, ADEID_NAME, strlen( path )); - memcpy(ad_entry( adp, ADEID_NAME ), path, - ad_getentrylen( adp, ADEID_NAME )); - ad_flush( adp, ADFLAGS_DF|ADFLAGS_HF ); + if ( ad_reso_fileno( adp ) == -1 ) { /* Hard META / HF */ + /* on noadouble volumes, just creating the data fork is ok */ + if (vol_noadouble(vol)) { + ad_close( adp, ADFLAGS_DF ); + goto createfile_done; + } + /* FIXME with hard create on an existing file, we already + * corrupted the data file. + */ + netatalk_unlink( upath ); + ad_close( adp, ADFLAGS_DF ); + return AFPERR_ACCESS; + } + + path = s_path->m_name; + ad_setname(adp, path); + ad_flush( adp); ad_close( adp, ADFLAGS_DF|ADFLAGS_HF ); createfile_done: + curdir->offcnt++; #ifdef DROPKLUDGE if (vol->v_flags & AFPVOL_DROPBOX) { @@ -507,39 +678,23 @@ createfile_done: setvoltime(obj, vol ); -#ifdef DEBUG - LOG(log_info, logtype_default, "end afp_createfile"); -#endif /* DEBUG */ - -#ifdef FORCE_UIDGID - /* bring everything back to old euid, egid */ - restore_uidgid ( uidgid ); -#endif /* FORCE_UIDGID */ - return (retvalue); } -int afp_setfilparams(obj, ibuf, ibuflen, rbuf, rbuflen ) -AFPObj *obj; -char *ibuf, *rbuf; -int ibuflen, *rbuflen; +int afp_setfilparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen) { struct vol *vol; struct dir *dir; - char *path; + struct path *s_path; int did, rc; u_int16_t vid, bitmap; -#ifdef DEBUG - LOG(log_info, logtype_default, "begin afp_setfilparams:"); -#endif /* DEBUG */ - *rbuflen = 0; ibuf += 2; memcpy(&vid, ibuf, sizeof( vid )); ibuf += sizeof( vid ); - if (( vol = getvolbyvid( vid )) == NULL ) { + if (NULL == ( vol = getvolbyvid( vid )) ) { return( AFPERR_PARAM ); } @@ -548,15 +703,23 @@ int ibuflen, *rbuflen; memcpy(&did, ibuf, sizeof( did )); ibuf += sizeof( did ); - if (( dir = dirsearch( vol, did )) == NULL ) { - return( AFPERR_NOOBJ ); + if (NULL == ( dir = dirlookup( vol, did )) ) { + return afp_errno; /* was AFPERR_NOOBJ */ } memcpy(&bitmap, ibuf, sizeof( bitmap )); bitmap = ntohs( bitmap ); ibuf += sizeof( bitmap ); - if (( path = cname( vol, dir, &ibuf )) == NULL ) { + if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) { + return get_afp_errno(AFPERR_PARAM); + } + + if (path_isadir(s_path)) { + return( AFPERR_BADTYPE ); /* it's a directory */ + } + + if ( s_path->st_errno != 0 ) { return( AFPERR_NOOBJ ); } @@ -564,70 +727,187 @@ int ibuflen, *rbuflen; ibuf++; } - if (( rc = setfilparams(vol, path, bitmap, ibuf )) == AFP_OK ) { + if (AFP_OK == ( rc = setfilparams(vol, s_path, bitmap, ibuf )) ) { setvoltime(obj, vol ); } -#ifdef DEBUG - LOG(log_info, logtype_default, "end afp_setfilparams:"); -#endif /* DEBUG */ - return( rc ); } +/* + * cf AFP3.0.pdf page 252 for change_mdate and change_parent_mdate logic + * +*/ +extern struct path Cur_Path; int setfilparams(struct vol *vol, - char *path, u_int16_t bitmap, char *buf ) + struct path *path, u_int16_t f_bitmap, char *buf ) { struct adouble ad, *adp; - struct ofork *of; struct extmap *em; - int bit = 0, isad = 1, err = AFP_OK; + int bit, isad = 1, err = AFP_OK; char *upath; - u_char achar, *fdType, xyy[4]; - u_int16_t ashort, bshort; + u_char achar, *fdType, xyy[4]; /* uninitialized, OK 310105 */ + u_int16_t ashort, bshort, oshort; u_int32_t aint; + u_int32_t upriv; + u_int16_t upriv_bit = 0; + struct utimbuf ut; -#ifdef FORCE_UIDGID - uidgidset *uidgid; - - uidgid = malloc(sizeof(uidgidset)); -#endif /* FORCE_UIDGID */ + int change_mdate = 0; + int change_parent_mdate = 0; + int newdate = 0; + struct timeval tv; + uid_t f_uid; + gid_t f_gid; + u_int16_t bitmap = f_bitmap; + u_int32_t cdate,bdate; + u_char finder_buf[32]; #ifdef DEBUG - LOG(log_info, logtype_default, "begin setfilparams:"); + LOG(log_debug9, logtype_afpd, "begin setfilparams:"); #endif /* DEBUG */ - upath = mtoupath(vol, path); - if ((of = of_findname(vol, curdir, path))) { - adp = of->of_ad; - } else { - memset(&ad, 0, sizeof(ad)); - adp = &ad; + adp = of_ad(vol, path, &ad); + upath = path->u_name; + + if (!vol_unix_priv(vol) && check_access(upath, OPENACC_WR ) < 0) { + return AFPERR_ACCESS; } -#ifdef FORCE_UIDGID - save_uidgid ( uidgid ); - set_uidgid ( vol ); -#endif /* FORCE_UIDGID */ - - if (ad_open( upath, vol_noadouble(vol) | ADFLAGS_HF, - O_RDWR|O_CREAT, 0666, adp) < 0) { - /* for some things, we don't need an adouble header */ - if (bitmap & ~(1<>1; + bit++; + } + switch( bit ) { + case FILPBIT_ATTR : + change_mdate = 1; + memcpy(&ashort, buf, sizeof( ashort )); + buf += sizeof( ashort ); + break; + case FILPBIT_CDATE : + change_mdate = 1; + memcpy(&cdate, buf, sizeof(cdate)); + buf += sizeof( cdate ); + break; + case FILPBIT_MDATE : + memcpy(&newdate, buf, sizeof( newdate )); + buf += sizeof( newdate ); + break; + case FILPBIT_BDATE : + change_mdate = 1; + memcpy(&bdate, buf, sizeof( bdate)); + buf += sizeof( bdate ); + break; + case FILPBIT_FINFO : + change_mdate = 1; + memcpy(finder_buf, buf, 32 ); + if (memcmp(buf,"slnkrhap",8)==0 && !S_ISLNK(path->st.st_mode)){ + // SLFINFO + int fp; + ssize_t len; + int erc=1; + char buf[PATH_MAX+1]; + if ((fp=open(path->u_name,O_RDONLY))>=0){ + if (len=read(fp,buf,PATH_MAX+1)){ + if (unlink(path->u_name)==0){ + buf[len]=0; + erc=symlink(buf,path->u_name); + lstat(path->u_name,&(path->st)); + } + } + close(fp); + } + if (erc!=0){ + err=AFPERR_BITMAP; + goto setfilparam_done; + } + } + buf += 32; + break; + case FILPBIT_UNIXPR : + if (!vol_unix_priv(vol)) { + /* this volume doesn't use unix priv */ + err = AFPERR_BITMAP; + bitmap = 0; + break; + } + change_mdate = 1; + change_parent_mdate = 1; + + memcpy( &aint, buf, sizeof( aint )); + f_uid = ntohl (aint); + buf += sizeof( aint ); + memcpy( &aint, buf, sizeof( aint )); + f_gid = ntohl (aint); + buf += sizeof( aint ); + setfilowner(vol, f_uid, f_gid, path); + + memcpy( &upriv, buf, sizeof( upriv )); + buf += sizeof( upriv ); + upriv = ntohl (upriv); + if ((upriv & S_IWUSR)) { + setfilunixmode(vol, path, upriv); + } + else { + /* do it later */ + upriv_bit = 1; + } + break; + case FILPBIT_PDINFO : + if (afp_version < 30) { /* else it's UTF8 name */ + achar = *buf; + buf += 2; + /* Keep special case to support crlf translations */ + if ((unsigned int) achar == 0x04) { + fdType = (u_char *)"TEXT"; + buf += 2; + } else { + xyy[0] = ( u_char ) 'p'; + xyy[1] = achar; + xyy[3] = *buf++; + xyy[2] = *buf++; + fdType = xyy; + } + break; + } + /* fallthrough */ + default : + err = AFPERR_BITMAP; + /* break while loop */ + bitmap = 0; + break; + } + + bitmap = bitmap>>1; + bit++; + } + + /* second try with adouble open + */ + if ( ad_open_metadata( upath, 0, O_CREAT, adp) < 0) { + LOG(log_debug, logtype_afpd, "setfilparams: ad_open_metadata error"); + /* + * For some things, we don't need an adouble header: + * - change of modification date + * - UNIX privs (Bug-ID #2863424) + */ + if ( (f_bitmap & ~(1<m_name); } - + + bit = 0; + bitmap = f_bitmap; while ( bitmap != 0 ) { while (( bitmap & 1 ) == 0 ) { bitmap = bitmap>>1; @@ -636,148 +916,107 @@ int setfilparams(struct vol *vol, switch( bit ) { case FILPBIT_ATTR : - memcpy(&ashort, buf, sizeof( ashort )); ad_getattr(adp, &bshort); + oshort = bshort; if ( ntohs( ashort ) & ATTRBIT_SETCLR ) { bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR ); } else { bshort &= ~ashort; } + if ((bshort & htons(ATTRBIT_INVISIBLE)) != (oshort & htons(ATTRBIT_INVISIBLE))) + change_parent_mdate = 1; ad_setattr(adp, bshort); - buf += sizeof( ashort ); break; - case FILPBIT_CDATE : - memcpy(&aint, buf, sizeof(aint)); - ad_setdate(adp, AD_DATE_CREATE, aint); - buf += sizeof( aint ); + ad_setdate(adp, AD_DATE_CREATE, cdate); break; - case FILPBIT_MDATE : - memcpy(&aint, buf, sizeof( aint )); - if (isad) - ad_setdate(adp, AD_DATE_MODIFY, aint); - ut.actime = ut.modtime = AD_DATE_TO_UNIX(aint); - utime(upath, &ut); - buf += sizeof( aint ); break; - case FILPBIT_BDATE : - memcpy(&aint, buf, sizeof(aint)); - ad_setdate(adp, AD_DATE_BACKUP, aint); - buf += sizeof( aint ); + ad_setdate(adp, AD_DATE_BACKUP, bdate); break; - case FILPBIT_FINFO : - if ((memcmp( ad_entry( adp, ADEID_FINDERI ), ufinderi, 8 ) == 0) - && (em = getextmap( path )) && - (memcmp(buf, em->em_type, sizeof( em->em_type )) == 0) && - (memcmp(buf + 4, em->em_creator, - sizeof( em->em_creator )) == 0)) { - memcpy(buf, ufinderi, 8 ); + if (default_type( ad_entry( adp, ADEID_FINDERI )) + && ( + ((em = getextmap( path->m_name )) && + !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) && + !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator))) + || ((em = getdefextmap()) && + !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) && + !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator))) + )) { + memcpy(finder_buf, ufinderi, 8 ); + } + memcpy(ad_entry( adp, ADEID_FINDERI ), finder_buf, 32 ); + break; + case FILPBIT_UNIXPR : + if (upriv_bit) { + setfilunixmode(vol, path, upriv); } - memcpy(ad_entry( adp, ADEID_FINDERI ), buf, 32 ); - buf += 32; break; - - /* Client needs to set the ProDOS file info for this file. - Use defined strings for the simple cases, and convert - all else into pXYY per Inside Appletalk. Always set - the creator as "pdos". */ case FILPBIT_PDINFO : - achar = *buf; - buf += 2; - memcpy(&ashort, buf, sizeof( ashort )); - ashort = ntohs( ashort ); - buf += 2; - - switch ( (unsigned int) achar ) - { - case 0x04 : - fdType = ( u_char *) "TEXT"; - break; - - case 0xff : - fdType = ( u_char *) "PSYS"; - break; - - case 0xb3 : - fdType = ( u_char *) "PS16"; - break; - - case 0x00 : - fdType = ( u_char *) "BINA"; - break; - - default : - xyy[0] = ( u_char ) 'p'; - xyy[1] = achar; - xyy[2] = ( u_char ) ( ashort >> 8 ) & 0xff; - xyy[3] = ( u_char ) ashort & 0xff; - fdType = xyy; + if (afp_version < 30) { /* else it's UTF8 name */ + memcpy(ad_entry( adp, ADEID_FINDERI ), fdType, 4 ); + memcpy(ad_entry( adp, ADEID_FINDERI ) + 4, "pdos", 4 ); break; } - - memcpy(ad_entry( adp, ADEID_FINDERI ), fdType, 4 ); - memcpy(ad_entry( adp, ADEID_FINDERI ) + 4, "pdos", 4 ); - break; - - + /* fallthrough */ default : err = AFPERR_BITMAP; goto setfilparam_done; } - bitmap = bitmap>>1; bit++; } setfilparam_done: + if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) { + newdate = AD_DATE_FROM_UNIX(tv.tv_sec); + } + if (newdate) { + if (isad) + ad_setdate(adp, AD_DATE_MODIFY, newdate); + ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate); + utime(upath, &ut); + } + if (isad) { - ad_flush( adp, ADFLAGS_HF ); - ad_close( adp, ADFLAGS_HF ); + ad_flush( adp); + ad_close_metadata( adp); -#ifdef FORCE_UIDGID - restore_uidgid ( uidgid ); -#endif /* FORCE_UIDGID */ + } + if (change_parent_mdate && gettimeofday(&tv, NULL) == 0) { + newdate = AD_DATE_FROM_UNIX(tv.tv_sec); + bitmap = 1<ad_open_forks) { + /* FIXME warning in syslog so admin'd know there's a conflict ?*/ + return AFPERR_OLOCK; /* little lie */ + } + if (AFP_OK != ( rc = copyfile(vol, vol, src, dst, newname, NULL )) ) { + /* on error copyfile delete dest */ return( rc ); } - return deletefile( src ); + return deletefile(vol, src, 0); default : return( AFPERR_PARAM ); } } - strcpy( adsrc, ad_path( src, 0 )); - rc = 0; -rename_retry: - if (rename( adsrc, ad_path( dst, 0 )) < 0 ) { - struct stat st; - - switch ( errno ) { - case ENOENT : - /* check for a source appledouble header. if it exists, make - * a dest appledouble directory and do the rename again. */ - memset(&ad, 0, sizeof(ad)); - if (rc || stat(adsrc, &st) || - (ad_open(dst, ADFLAGS_HF, O_RDWR | O_CREAT, 0666, &ad) < 0)) - return AFP_OK; - rc++; - ad_close(&ad, ADFLAGS_HF); - goto rename_retry; - case EPERM: - case EACCES : - return( AFPERR_ACCESS ); - case EROFS: - return AFPERR_VLOCK; - default : - return( AFPERR_PARAM ); + if (vol->vfs->vfs_renamefile(vol, src, dst) < 0 ) { + int err; + + err = errno; + /* try to undo the data fork rename, + * we know we are on the same device + */ + if (err) { + unix_rename( dst, src ); + /* return the first error */ + switch ( err) { + case ENOENT : + return AFPERR_NOOBJ; + case EPERM: + case EACCES : + return AFPERR_ACCESS ; + case EROFS: + return AFPERR_VLOCK; + default : + return AFPERR_PARAM ; + } } } - memset(&ad, 0, sizeof(ad)); - if ( ad_open( dst, ADFLAGS_HF, O_RDWR, 0666, &ad) < 0 ) { - switch ( errno ) { - case ENOENT : - return( AFPERR_NOOBJ ); - case EACCES : - return( AFPERR_ACCESS ); - case EROFS: - return AFPERR_VLOCK; - default : - return( AFPERR_PARAM ); - } + /* don't care if we can't open the newly renamed ressource fork + */ + if (!ad_open( dst, ADFLAGS_HF, O_RDWR, 0666, adp)) { + ad_setname(adp, newname); + ad_flush( adp ); + ad_close( adp, ADFLAGS_HF ); } - - len = strlen( newname ); - ad_setentrylen( &ad, ADEID_NAME, len ); - memcpy(ad_entry( &ad, ADEID_NAME ), newname, len ); - ad_flush( &ad, ADFLAGS_HF ); - ad_close( &ad, ADFLAGS_HF ); - #ifdef DEBUG - LOG(log_info, logtype_default, "end renamefile:"); + LOG(log_debug9, logtype_afpd, "end renamefile:"); #endif /* DEBUG */ return( AFP_OK ); } -int afp_copyfile(obj, ibuf, ibuflen, rbuf, rbuflen ) -AFPObj *obj; -char *ibuf, *rbuf; -int ibuflen, *rbuflen; +/* ---------------- + convert a Mac long name to an utf8 name, +*/ +size_t mtoUTF8(const struct vol *vol, const char *src, size_t srclen, char *dest, size_t destlen) { - struct vol *vol; +size_t outlen; + + if ((size_t)-1 == (outlen = convert_string ( vol->v_maccharset, CH_UTF8_MAC, src, srclen, dest, destlen)) ) { + return -1; + } + return outlen; +} + +/* ---------------- */ +int copy_path_name(const struct vol *vol, char *newname, char *ibuf) +{ +char type = *ibuf; +size_t plen = 0; +u_int16_t len16; +u_int32_t hint; + + if ( type != 2 && !(afp_version >= 30 && type == 3) ) { + return -1; + } + ibuf++; + switch (type) { + case 2: + if (( plen = (unsigned char)*ibuf++ ) != 0 ) { + if (afp_version >= 30) { + /* convert it to UTF8 + */ + if ((plen = mtoUTF8(vol, ibuf, plen, newname, AFPOBJ_TMPSIZ)) == (size_t)-1) + return -1; + } + else { + strncpy( newname, ibuf, plen ); + newname[ plen ] = '\0'; + } + if (strlen(newname) != plen) { + /* there's \0 in newname, e.g. it's a pathname not + * only a filename. + */ + return -1; + } + } + break; + case 3: + memcpy(&hint, ibuf, sizeof(hint)); + ibuf += sizeof(hint); + + memcpy(&len16, ibuf, sizeof(len16)); + ibuf += sizeof(len16); + plen = ntohs(len16); + + if (plen) { + if (plen > AFPOBJ_TMPSIZ) { + return -1; + } + strncpy( newname, ibuf, plen ); + newname[ plen ] = '\0'; + if (strlen(newname) != plen) { + return -1; + } + } + break; + } + return plen; +} + +/* ----------------------------------- +*/ +int afp_copyfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen) +{ + struct vol *s_vol, *d_vol; struct dir *dir; - char *newname, *path, *p; + char *newname, *p, *upath; + struct path *s_path; u_int32_t sdid, ddid; - int plen, err, retvalue = AFP_OK; + int err, retvalue = AFP_OK; u_int16_t svid, dvid; -#ifdef DEBUG - LOG(log_info, logtype_default, "begin afp_copyfile:"); -#endif /* DEBUG */ - + struct adouble ad, *adp; + int denyreadset; + *rbuflen = 0; ibuf += 2; memcpy(&svid, ibuf, sizeof( svid )); ibuf += sizeof( svid ); - if (( vol = getvolbyvid( svid )) == NULL ) { + if (NULL == ( s_vol = getvolbyvid( svid )) ) { return( AFPERR_PARAM ); } memcpy(&sdid, ibuf, sizeof( sdid )); ibuf += sizeof( sdid ); - if (( dir = dirsearch( vol, sdid )) == NULL ) { - return( AFPERR_PARAM ); + if (NULL == ( dir = dirlookup( s_vol, sdid )) ) { + return afp_errno; } memcpy(&dvid, ibuf, sizeof( dvid )); @@ -887,10 +1186,10 @@ int ibuflen, *rbuflen; memcpy(&ddid, ibuf, sizeof( ddid )); ibuf += sizeof( ddid ); - if (( path = cname( vol, dir, &ibuf )) == NULL ) { - return( AFPERR_NOOBJ ); + if (NULL == ( s_path = cname( s_vol, dir, &ibuf )) ) { + return get_afp_errno(AFPERR_PARAM); } - if ( *path == '\0' ) { + if ( path_isadir(s_path) ) { return( AFPERR_BADTYPE ); } @@ -899,69 +1198,82 @@ int ibuflen, *rbuflen; * however, copyfile doesn't have any of that info, * and locks need to stay coherent. as a result, * we just balk if the file is opened already. */ - if (of_findname(vol, curdir, path)) + + adp = of_ad(s_vol, s_path, &ad); + + if (ad_open(s_path->u_name , ADFLAGS_DF |ADFLAGS_HF | ADFLAGS_NOHF, O_RDONLY, 0, adp) < 0) { + return AFPERR_DENYCONF; + } + denyreadset = (getforkmode(adp, ADEID_DFORK, AD_FILELOCK_DENY_RD) != 0 || + getforkmode(adp, ADEID_RFORK, AD_FILELOCK_DENY_RD) != 0 ); + ad_close( adp, ADFLAGS_DF |ADFLAGS_HF ); + if (denyreadset) { return AFPERR_DENYCONF; + } newname = obj->newtmp; - strcpy( newname, path ); + strcpy( newname, s_path->m_name ); - p = ctoupath( vol, curdir, newname ); - - if (( vol = getvolbyvid( dvid )) == NULL ) { + p = ctoupath( s_vol, curdir, newname ); + if (!p) { + return AFPERR_PARAM; + + } +#ifdef FORCE_UIDGID + /* FIXME svid != dvid && dvid's user can't read svid */ +#endif + if (NULL == ( d_vol = getvolbyvid( dvid )) ) { return( AFPERR_PARAM ); } - if (vol->v_flags & AFPVOL_RO) + if (d_vol->v_flags & AFPVOL_RO) return AFPERR_VLOCK; - if (( dir = dirsearch( vol, ddid )) == NULL ) { - return( AFPERR_PARAM ); + if (NULL == ( dir = dirlookup( d_vol, ddid )) ) { + return afp_errno; } - if (( path = cname( vol, dir, &ibuf )) == NULL ) { - return( AFPERR_NOOBJ ); + if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) { + return get_afp_errno(AFPERR_NOOBJ); } - if ( *path != '\0' ) { - return( AFPERR_BADTYPE ); + if ( *s_path->m_name != '\0' ) { + path_error(s_path, AFPERR_PARAM); } /* one of the handful of places that knows about the path type */ - if ( *ibuf++ != 2 ) { + if (copy_path_name(d_vol, newname, ibuf) < 0) { return( AFPERR_PARAM ); } - if (( plen = (unsigned char)*ibuf++ ) != 0 ) { - strncpy( newname, ibuf, plen ); - newname[ plen ] = '\0'; + /* newname is always only a filename so curdir *is* its + * parent folder + */ + if (NULL == (upath = mtoupath(d_vol, newname, curdir->d_did, utf8_encoding()))) { + return( AFPERR_PARAM ); } - - if ( (err = copyfile(p, mtoupath(vol, newname ), newname, - vol_noadouble(vol))) < 0 ) { + if ( (err = copyfile(s_vol, d_vol, p, upath , newname, adp)) < 0 ) { return err; } - - setvoltime(obj, vol ); + curdir->offcnt++; #ifdef DROPKLUDGE if (vol->v_flags & AFPVOL_DROPBOX) { - retvalue=matchfile2dirperms(newname, vol, sdid); + retvalue=matchfile2dirperms(upath, vol, ddid); /* FIXME sdir or ddid */ } #endif /* DROPKLUDGE */ -#ifdef DEBUG - LOG(log_info, logtype_default, "end afp_copyfile:"); -#endif /* DEBUG */ + setvoltime(obj, d_vol ); return( retvalue ); } - -static __inline__ int copy_all(const int dfd, const void *buf, +/* ----------------------- */ +static int copy_all(const int dfd, const void *buf, size_t buflen) { ssize_t cc; #ifdef DEBUG - LOG(log_info, logtype_default, "begin copy_all:"); + LOG(log_debug9, logtype_afpd, "begin copy_all:"); #endif /* DEBUG */ while (buflen > 0) { @@ -969,261 +1281,278 @@ static __inline__ int copy_all(const int dfd, const void *buf, switch (errno) { case EINTR: continue; - case EDQUOT: - case EFBIG: - case ENOSPC: - return AFPERR_DFULL; - case EROFS: - return AFPERR_VLOCK; default: - return AFPERR_PARAM; + return -1; } } buflen -= cc; } #ifdef DEBUG - LOG(log_info, logtype_default, "end copy_all:"); + LOG(log_debug9, logtype_afpd, "end copy_all:"); #endif /* DEBUG */ - return AFP_OK; + return 0; } -/* XXX: this needs to use ad_open and ad_lock. so, we need to - * pass in vol and path */ -int copyfile(src, dst, newname, noadouble ) -char *src, *dst, *newname; -const int noadouble; +/* -------------------------- + * copy only the fork data stream +*/ +static int copy_fork(int eid, struct adouble *add, struct adouble *ads) { - struct adouble ad; + ssize_t cc; + int err = 0; + char filebuf[8192]; + int sfd, dfd; + + if (eid == ADEID_DFORK) { + sfd = ad_data_fileno(ads); + dfd = ad_data_fileno(add); + } + else { + sfd = ad_reso_fileno(ads); + dfd = ad_reso_fileno(add); + } + + if ((off_t)-1 == lseek(sfd, ad_getentryoff(ads, eid), SEEK_SET)) + return -1; + + if ((off_t)-1 == lseek(dfd, ad_getentryoff(add, eid), SEEK_SET)) + return -1; + +#if 0 /* ifdef SENDFILE_FLAVOR_LINUX */ + /* doesn't work With 2.6 FIXME, only check for EBADFD ? */ + off_t offset = 0; + size_t size; struct stat st; - char filebuf[8192]; - int sfd, dfd, len, err = AFP_OK; - ssize_t cc; - -#ifdef DEBUG - LOG(log_info, logtype_default, "begin copyfile:"); -#endif /* DEBUG */ + #define BUF 128*1024*1024 - if (newname) { - if ((sfd = open( ad_path( src, ADFLAGS_HF ), O_RDONLY, 0 )) < 0 ) { - switch ( errno ) { - case ENOENT : - break; /* just copy the data fork */ - case EACCES : - return( AFPERR_ACCESS ); - default : - return( AFPERR_PARAM ); - } - } else { - if (( dfd = open( ad_path( dst, ADFLAGS_HF ), O_WRONLY|O_CREAT, - ad_mode( ad_path( dst, ADFLAGS_HF ), 0666 ))) < 0 ) { - close( sfd ); - switch ( errno ) { - case ENOENT : - return( AFPERR_NOOBJ ); - case EACCES : - return( AFPERR_ACCESS ); - case EROFS: - return AFPERR_VLOCK; - default : - return( AFPERR_PARAM ); - } + if (fstat(sfd, &st) == 0) { + + while (1) { + if ( offset >= st.st_size) { + return 0; } - - /* copy the file */ -#ifdef SENDFILE_FLAVOR_LINUX - if (fstat(sfd, &st) == 0) { - if ((cc = sendfile(dfd, sfd, NULL, st.st_size)) < 0) { - switch (errno) { - case EDQUOT: - case EFBIG: - case ENOSPC: - err = AFPERR_DFULL; - break; - case EROFS: - err = AFPERR_VLOCK; - break; - default: - err = AFPERR_PARAM; - } + size = (st.st_size -offset > BUF)?BUF:st.st_size -offset; + if ((cc = sys_sendfile(dfd, sfd, &offset, size)) < 0) { + switch (errno) { + case ENOSYS: + case EINVAL: /* there's no guarantee that all fs support sendfile */ + goto no_sendfile; + default: + return -1; } - goto copyheader_done; } -#endif /* SENDFILE_FLAVOR_LINUX */ - while (1) { - if ((cc = read(sfd, filebuf, sizeof(filebuf))) < 0) { - if (errno == EINTR) - continue; - err = AFPERR_PARAM; - break; - } + } + } + no_sendfile: + lseek(sfd, offset, SEEK_SET); +#endif - if (!cc || ((err = copy_all(dfd, filebuf, cc)) < 0)) - break; - } + while (1) { + if ((cc = read(sfd, filebuf, sizeof(filebuf))) < 0) { + if (errno == EINTR) + continue; + err = -1; + break; + } -copyheader_done: - close(sfd); - close(dfd); - if (err < 0) { - unlink(ad_path(dst, ADFLAGS_HF)); - return err; - } + if (!cc || ((err = copy_all(dfd, filebuf, cc)) < 0)) { + break; } } + return err; +} - /* data fork copying */ - if (( sfd = open( src, O_RDONLY, 0 )) < 0 ) { - switch ( errno ) { - case ENOENT : - return( AFPERR_NOOBJ ); - case EACCES : - return( AFPERR_ACCESS ); - default : - return( AFPERR_PARAM ); - } +/* ---------------------------------- + * if newname is NULL (from directory.c) we don't want to copy the resource fork. + * because we are doing it elsewhere. + * currently if newname is NULL then adp is NULL. + */ +int copyfile(const struct vol *s_vol, const struct vol*d_vol, + char *src, char *dst, char *newname, struct adouble *adp) +{ + struct adouble ads, add; + int err = 0; + int ret_err = 0; + int adflags; + int stat_result; + struct stat st; + +#ifdef DEBUG + LOG(log_debug9, logtype_afpd, "begin copyfile:"); +#endif /* DEBUG */ + + if (adp == NULL) { + ad_init(&ads, s_vol->v_adouble, s_vol->v_ad_options); + adp = &ads; + } + ad_init(&add, d_vol->v_adouble, d_vol->v_ad_options); + adflags = ADFLAGS_DF; + if (newname) { + adflags |= ADFLAGS_HF; } - if (( dfd = open( dst, O_WRONLY|O_CREAT, ad_mode( dst, 0666 ))) < 0 ) { - close( sfd ); - switch ( errno ) { - case ENOENT : - return( AFPERR_NOOBJ ); - case EACCES : - return( AFPERR_ACCESS ); - case EROFS: - return AFPERR_VLOCK; - default : - return( AFPERR_PARAM ); - } + if (ad_open(src , adflags | ADFLAGS_NOHF, O_RDONLY, 0, adp) < 0) { + ret_err = errno; + goto done; } -#ifdef SENDFILE_FLAVOR_LINUX - if (fstat(sfd, &st) == 0) { - if ((cc = sendfile(dfd, sfd, NULL, st.st_size)) < 0) { - switch (errno) { - case EDQUOT: - case EFBIG: - case ENOSPC: - err = AFPERR_DFULL; - break; - default: - err = AFPERR_PARAM; - } - } - goto copydata_done; + if (ad_meta_fileno(adp) == -1 && ad_reso_fileno(adp) == -1) { /* META / HF */ + /* no resource fork, don't create one for dst file */ + adflags &= ~ADFLAGS_HF; } -#endif /* SENDFILE_FLAVOR_LINUX */ - while (1) { - if ((cc = read( sfd, filebuf, sizeof( filebuf ))) < 0) { - if (errno == EINTR) - continue; + stat_result = fstat(ad_data_fileno(adp), &st); /* saving stat exit code, thus saving us on one more stat later on */ - err = AFPERR_PARAM; - break; - } + if (stat_result < 0) { + /* unlikely but if fstat fails, the default file mode will be 0666. */ + st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; + } - if (!cc || ((err = copy_all(dfd, filebuf, cc)) < 0)) { - break; + if (ad_open(dst , adflags, O_RDWR|O_CREAT|O_EXCL, st.st_mode, &add) < 0) { + ret_err = errno; + ad_close( adp, adflags ); + if (EEXIST != ret_err) { + deletefile(d_vol, dst, 0); + goto done; + } + return AFPERR_EXIST; + } + + /* + * XXX if the source and the dest don't use the same resource type it's broken + */ + if (ad_reso_fileno(adp) == -1 || 0 == (err = copy_fork(ADEID_RFORK, &add, adp))){ + /* copy the data fork */ + if ((err = copy_fork(ADEID_DFORK, &add, adp)) == 0) { + err = d_vol->vfs->vfs_copyfile(d_vol, src, dst); } } -copydata_done: - close(sfd); - close(dfd); - if (err < 0) { - unlink(ad_path(dst, ADFLAGS_HF)); - unlink(dst); - return err; + if (err < 0) { + ret_err = errno; + } + + if (!ret_err && newname && (adflags & ADFLAGS_HF)) { + /* set the new name in the resource fork */ + ad_copy_header(&add, adp); + ad_setname(&add, newname); + ad_flush( &add ); } + ad_close( adp, adflags ); - if (newname) { - memset(&ad, 0, sizeof(ad)); - if ( ad_open( dst, noadouble | ADFLAGS_HF, O_RDWR|O_CREAT, - 0666, &ad) < 0 ) { - switch ( errno ) { - case ENOENT : - return noadouble ? AFP_OK : AFPERR_NOOBJ; - case EACCES : - return( AFPERR_ACCESS ); - case EROFS: - return AFPERR_VLOCK; - default : - return( AFPERR_PARAM ); - } - } + if (ad_close( &add, adflags ) <0) { + ret_err = errno; + } + + if (ret_err) { + deletefile(d_vol, dst, 0); + } + else if (stat_result == 0) { + /* set dest modification date to src date */ + struct utimbuf ut; - len = strlen( newname ); - ad_setentrylen( &ad, ADEID_NAME, len ); - memcpy(ad_entry( &ad, ADEID_NAME ), newname, len ); - ad_flush( &ad, ADFLAGS_HF ); - ad_close( &ad, ADFLAGS_HF ); + ut.actime = ut.modtime = st.st_mtime; + utime(dst, &ut); + /* FIXME netatalk doesn't use resource fork file date + * but maybe we should set its modtime too. + */ } #ifdef DEBUG - LOG(log_info, logtype_default, "end copyfile:"); + LOG(log_debug9, logtype_afpd, "end copyfile:"); #endif /* DEBUG */ - return( AFP_OK ); +done: + switch ( ret_err ) { + case 0: + return AFP_OK; + case EDQUOT: + case EFBIG: + case ENOSPC: + return AFPERR_DFULL; + case ENOENT: + return AFPERR_NOOBJ; + case EACCES: + return AFPERR_ACCESS; + case EROFS: + return AFPERR_VLOCK; + } + return AFPERR_PARAM; } -int deletefile( file ) -char *file; +/* ----------------------------------- + vol: not NULL delete cnid entry. then we are in curdir and file is a only filename + checkAttrib: 1 check kFPDeleteInhibitBit (deletfile called by afp_delete) + + when deletefile is called we don't have lock on it, file is closed (for us) + untrue if called by renamefile + + ad_open always try to open file RDWR first and ad_lock takes care of + WRITE lock on read only file. +*/ + +static int check_attrib(struct adouble *adp) +{ +u_int16_t bshort = 0; + + ad_getattr(adp, &bshort); + /* + * Does kFPDeleteInhibitBit (bit 8) set? + */ + if ((bshort & htons(ATTRBIT_NODELETE))) { + return AFPERR_OLOCK; + } + if ((bshort & htons(ATTRBIT_DOPEN | ATTRBIT_ROPEN))) { + return AFPERR_BUSY; + } + return 0; +} + +int deletefile(const struct vol *vol, char *file, int checkAttrib) { struct adouble ad; + struct adouble *adp = &ad; int adflags, err = AFP_OK; - int locktype = ADLOCK_WR; - int openmode = O_RDWR; #ifdef DEBUG - LOG(log_info, logtype_default, "begin deletefile:"); + LOG(log_debug9, logtype_afpd, "begin deletefile:"); #endif /* DEBUG */ + /* try to open both forks at once */ + adflags = ADFLAGS_DF|ADFLAGS_HF; + if (checkAttrib) { + /* was EACCESS error try to get only metadata */ + ad_init(&ad, vol->v_adouble, vol->v_ad_options); + /* we never want to create a resource fork here, we are going to delete it + * moreover sometimes deletefile is called with a no existent file and + * ad_open would create a 0 byte resource fork + */ + if ( ad_metadata( file, ADFLAGS_OPENFORKS, &ad) == 0 ) { + ad_close( &ad, adflags ); + if ((err = check_attrib(&ad))) { + return err; + } + } + } + while(1) { - /* - * If can't open read/write then try again read-only. If it's open - * read-only, we must do a read lock instead of a write lock. - */ - /* try to open both at once */ - adflags = ADFLAGS_DF|ADFLAGS_HF; - memset(&ad, 0, sizeof(ad)); - if ( ad_open( file, adflags, openmode, 0, &ad ) < 0 ) { + ad_init(&ad, vol->v_adouble, vol->v_ad_options); /* OK */ + if ( ad_open( file, adflags, O_RDONLY, 0, &ad ) < 0 ) { switch (errno) { case ENOENT: - adflags = ADFLAGS_DF; + if (adflags == ADFLAGS_DF) + return AFPERR_NOOBJ; + /* that failed. now try to open just the data fork */ - memset(&ad, 0, sizeof(ad)); - if ( ad_open( file, adflags, openmode, 0, &ad ) < 0 ) { - switch (errno) { - case ENOENT: - return AFPERR_NOOBJ; - case EACCES: - if(openmode == O_RDWR) { - openmode = O_RDONLY; - locktype = ADLOCK_RD; - continue; - } else { - return AFPERR_ACCESS; - } - case EROFS: - return AFPERR_VLOCK; - default: - return AFPERR_PARAM; - } - } - break; + adflags = ADFLAGS_DF; + continue; case EACCES: - if(openmode == O_RDWR) { - openmode = O_RDONLY; - locktype = ADLOCK_RD; - continue; - } else { - return AFPERR_ACCESS; - } + adp = NULL; /* maybe it's a file with no write mode for us */ + break; /* was return AFPERR_ACCESS;*/ case EROFS: return AFPERR_VLOCK; default: @@ -1233,116 +1562,92 @@ char *file; break; /* from the while */ } - if ((adflags & ADFLAGS_HF) && - (ad_tmplock(&ad, ADEID_RFORK, locktype, 0, 0) < 0 )) { - ad_close( &ad, adflags ); - return( AFPERR_BUSY ); + if (adp && (adflags & ADFLAGS_HF) ) { + /* FIXME we have a pb here because we want to know if a file is open + * there's a 'priority inversion' if you can't open the ressource fork RW + * you can delete it if it's open because you can't get a write lock. + * + * ADLOCK_FILELOCK means the whole ressource fork, not only after the + * metadatas + * + * FIXME it doesn't work for RFORK open read only and fork open without deny mode + */ + if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) { + ad_close( &ad, adflags ); + return( AFPERR_BUSY ); + } } - if (ad_tmplock( &ad, ADEID_DFORK, locktype, 0, 0 ) < 0) { + if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) { err = AFPERR_BUSY; - goto delete_unlock; - } - - if ( unlink( ad_path( file, ADFLAGS_HF )) < 0 ) { - switch ( errno ) { - case EPERM: - case EACCES : - err = AFPERR_ACCESS; - goto delete_unlock; - case EROFS: - err = AFPERR_VLOCK; - goto delete_unlock; - case ENOENT : - break; - default : - err = AFPERR_PARAM; - goto delete_unlock; - } } - - if ( unlink( file ) < 0 ) { - switch ( errno ) { - case EPERM: - case EACCES : - err = AFPERR_ACCESS; - break; - case EROFS: - err = AFPERR_VLOCK; - break; - case ENOENT : - break; - default : - err = AFPERR_PARAM; - break; + else if (!(err = vol->vfs->vfs_deletefile(vol, file)) && !(err = netatalk_unlink( file )) ) { + cnid_t id; + if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file)))) + { + cnid_delete(vol->v_cdb, id); } } - -delete_unlock: - if (adflags & ADFLAGS_HF) - ad_tmplock(&ad, ADEID_RFORK, ADLOCK_CLR, 0, 0); - ad_tmplock(&ad, ADEID_DFORK, ADLOCK_CLR, 0, 0); - ad_close( &ad, adflags ); + if (adp) + ad_close( &ad, adflags ); /* ad_close removes locks if any */ #ifdef DEBUG - LOG(log_info, logtype_default, "end deletefile:"); + LOG(log_debug9, logtype_afpd, "end deletefile:"); #endif /* DEBUG */ return err; } - -#ifdef CNID_DB +/* ------------------------------------ */ /* return a file id */ -int afp_createid(obj, ibuf, ibuflen, rbuf, rbuflen ) -AFPObj *obj; -char *ibuf, *rbuf; -int ibuflen, *rbuflen; +int afp_createid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen) { - struct stat st; - struct adouble ad; + struct stat *st; struct vol *vol; struct dir *dir; - char *path, *upath; + char *upath; int len; cnid_t did, id; u_short vid; - -#ifdef DEBUG - LOG(log_info, logtype_default, "begin afp_createid:"); -#endif /* DEBUG */ + struct path *s_path; *rbuflen = 0; + ibuf += 2; memcpy(&vid, ibuf, sizeof(vid)); ibuf += sizeof(vid); - if (( vol = getvolbyvid( vid )) == NULL ) { + if (NULL == ( vol = getvolbyvid( vid )) ) { return( AFPERR_PARAM); } + if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) { + return AFPERR_NOOP; + } + if (vol->v_flags & AFPVOL_RO) return AFPERR_VLOCK; memcpy(&did, ibuf, sizeof( did )); ibuf += sizeof(did); - if (( dir = dirsearch( vol, did )) == NULL ) { - return( AFPERR_PARAM ); + if (NULL == ( dir = dirlookup( vol, did )) ) { + return afp_errno; /* was AFPERR_PARAM */ } - if (( path = cname( vol, dir, &ibuf )) == NULL ) { - return( AFPERR_PARAM ); + if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) { + return get_afp_errno(AFPERR_NOOBJ); /* was AFPERR_PARAM */ } - if ( *path == '\0' ) { + if ( path_isadir(s_path) ) { return( AFPERR_BADTYPE ); } - upath = mtoupath(vol, path); - if (stat(upath, &st) < 0) { - switch (errno) { + upath = s_path->u_name; + switch (s_path->st_errno) { + case 0: + break; /* success */ case EPERM: case EACCES: return AFPERR_ACCESS; @@ -1350,64 +1655,116 @@ int ibuflen, *rbuflen; return AFPERR_NOOBJ; default: return AFPERR_PARAM; - } } - - if (id = cnid_lookup(vol->v_db, &st, did, upath, len = strlen(upath))) { + st = &s_path->st; + if ((id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath)))) { memcpy(rbuf, &id, sizeof(id)); *rbuflen = sizeof(id); return AFPERR_EXISTID; } -#if AD_VERSION > AD_VERSION1 - memset(&ad, 0, sizeof(ad)); - if (ad_open( upath, ADFLAGS_HF, O_RDONLY, 0, &ad ) >= 0) { - memcpy(&id, ad_entry(&ad, ADEID_DID), sizeof(id)); - ad_close(&ad, ADFLAGS_HF); - } -#endif /* AD_VERSION > AD_VERSION1 */ - - if (id = cnid_add(vol->v_db, &st, did, upath, len, id) != CNID_INVALID) { + if ((id = get_id(vol, NULL, st, did, upath, len)) != CNID_INVALID) { memcpy(rbuf, &id, sizeof(id)); *rbuflen = sizeof(id); return AFP_OK; } -#ifdef DEBUG - LOG(log_info, logtype_default, "ending afp_createid...:"); -#endif /* DEBUG */ + return afp_errno; +} - switch (errno) { - case EROFS: - return AFPERR_VLOCK; - break; - case EPERM: - case EACCES: - return AFPERR_ACCESS; - break; - default: - LOG(log_error, logtype_default, "afp_createid: cnid_add: %s", strerror(errno)); - return AFPERR_PARAM; +/* ------------------------------- */ +struct reenum { + struct vol *vol; + cnid_t did; +}; + +static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data) +{ + struct path path; + struct reenum *param = data; + struct vol *vol = param->vol; + cnid_t did = param->did; + cnid_t aint; + + if ( lstat(de->d_name, &path.st)<0 ) + return 0; + + /* update or add to cnid */ + aint = cnid_add(vol->v_cdb, &path.st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */ + +#if AD_VERSION > AD_VERSION1 + if (aint != CNID_INVALID && !S_ISDIR(path.st.st_mode)) { + struct adouble ad, *adp; + + path.st_errno = 0; + path.st_valid = 1; + path.u_name = de->d_name; + + adp = of_ad(vol, &path, &ad); + + if ( ad_open_metadata( de->d_name, 0, 0, adp ) < 0 ) { + return 0; + } + if (ad_setid(adp, path.st.st_dev, path.st.st_ino, aint, did, vol->v_stamp)) { + ad_flush(adp); + } + ad_close_metadata(adp); } +#endif /* AD_VERSION > AD_VERSION1 */ + + return 0; } -/* resolve a file id */ -int afp_resolveid(obj, ibuf, ibuflen, rbuf, rbuflen ) -AFPObj *obj; -char *ibuf, *rbuf; -int ibuflen, *rbuflen; +/* -------------------- + * Ok the db is out of synch with the dir. + * but if it's a deleted file we don't want to do it again and again. +*/ +static int +reenumerate_id(struct vol *vol, char *name, struct dir *dir) +{ + int ret; + struct reenum data; + struct stat st; + + if (vol->v_cdb == NULL) { + return -1; + } + + /* FIXME use of_statdir ? */ + if (lstat(name, &st)) { + return -1; + } + + if (dirreenumerate(dir, &st)) { + /* we already did it once and the dir haven't been modified */ + return dir->offcnt; + } + + data.vol = vol; + data.did = dir->d_did; + if ((ret = for_each_dirent(vol, name, reenumerate_loop, (void *)&data)) >= 0) { + setdiroffcnt(curdir, &st, ret); + dir->d_flags |= DIRF_CNID; + } + + return ret; +} + +/* ------------------------------ + resolve a file id */ +int afp_resolveid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen) { - struct stat st; struct vol *vol; struct dir *dir; char *upath; - int err, buflen; - cnid_t id; + struct path path; + int err, retry=0; + size_t buflen; + cnid_t id, cnid; u_int16_t vid, bitmap; -#ifdef DEBUG - LOG(log_info, logtype_default, "begin afp_resolveid:"); -#endif /* DEBUG */ + static char buffer[12 + MAXPATHLEN + 1]; + int len = 12 + MAXPATHLEN + 1; *rbuflen = 0; ibuf += 2; @@ -1415,22 +1772,58 @@ int ibuflen, *rbuflen; memcpy(&vid, ibuf, sizeof(vid)); ibuf += sizeof(vid); - if (( vol = getvolbyvid( vid )) == NULL ) { + if (NULL == ( vol = getvolbyvid( vid )) ) { return( AFPERR_PARAM); } + if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) { + return AFPERR_NOOP; + } + memcpy(&id, ibuf, sizeof( id )); ibuf += sizeof(id); - - if ((upath = cnid_resolve(vol->v_db, &id)) == NULL) { - return AFPERR_BADID; + cnid = id; + + if (!id) { + /* some MacOS versions after a catsearch do a *lot* of afp_resolveid with 0 */ + return AFPERR_NOID; + } +retry: + if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) { + return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */ } - if (( dir = dirsearch( vol, id )) == NULL ) { - return( AFPERR_PARAM ); + if (NULL == ( dir = dirlookup( vol, id )) ) { + return AFPERR_NOID; /* idem AFPERR_PARAM */ + } + if (movecwd(vol, dir) < 0) { + switch (errno) { + case EACCES: + case EPERM: + return AFPERR_ACCESS; + case ENOENT: + return AFPERR_NOID; + default: + return AFPERR_PARAM; + } } - if ((movecwd(vol, dir) < 0) || (stat(upath, &st) < 0)) { + memset(&path, 0, sizeof(path)); + path.u_name = upath; + if ( of_stat(&path) < 0 ) { +#ifdef ESTALE + /* with nfs and our working directory is deleted */ + if (errno == ESTALE) { + errno = ENOENT; + } +#endif + if ( errno == ENOENT && !retry) { + /* cnid db is out of sync, reenumerate the directory and update ids */ + reenumerate_id(vol, ".", dir); + id = cnid; + retry = 1; + goto retry; + } switch (errno) { case EACCES: case EPERM: @@ -1443,30 +1836,29 @@ int ibuflen, *rbuflen; } /* directories are bad */ - if (S_ISDIR(st.st_mode)) - return AFPERR_BADTYPE; + if (S_ISDIR(path.st.st_mode)) { + /* OS9 and OSX don't return the same error code */ + return (afp_version >=30)?AFPERR_NOID:AFPERR_BADTYPE; + } memcpy(&bitmap, ibuf, sizeof(bitmap)); bitmap = ntohs( bitmap ); - - if ((err = getfilparams(vol, bitmap, utompath(vol, upath), curdir, &st, - rbuf + sizeof(bitmap), &buflen)) != AFP_OK) + if (NULL == (path.m_name = utompath(vol, upath, cnid, utf8_encoding()))) { + return AFPERR_NOID; + } + path.id = cnid; + if (AFP_OK != (err = getfilparams(vol, bitmap, &path , curdir, + rbuf + sizeof(bitmap), &buflen))) { return err; - + } *rbuflen = buflen + sizeof(bitmap); memcpy(rbuf, ibuf, sizeof(bitmap)); -#ifdef DEBUG - LOG(log_info, logtype_default, "end afp_resolveid:"); -#endif /* DEBUG */ - return AFP_OK; } -int afp_deleteid(obj, ibuf, ibuflen, rbuf, rbuflen ) -AFPObj *obj; -char *ibuf, *rbuf; -int ibuflen, *rbuflen; +/* ------------------------------ */ +int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen) { struct stat st; struct vol *vol; @@ -1476,10 +1868,8 @@ int ibuflen, *rbuflen; cnid_t id; cnid_t fileid; u_short vid; - -#ifdef DEBUG - LOG(log_info, logtype_default, "begin afp_deleteid:"); -#endif /* DEBUG */ + static char buffer[12 + MAXPATHLEN + 1]; + int len = 12 + MAXPATHLEN + 1; *rbuflen = 0; ibuf += 2; @@ -1487,31 +1877,38 @@ int ibuflen, *rbuflen; memcpy(&vid, ibuf, sizeof(vid)); ibuf += sizeof(vid); - if (( vol = getvolbyvid( vid )) == NULL ) { + if (NULL == ( vol = getvolbyvid( vid )) ) { return( AFPERR_PARAM); } + if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) { + return AFPERR_NOOP; + } + if (vol->v_flags & AFPVOL_RO) return AFPERR_VLOCK; memcpy(&id, ibuf, sizeof( id )); ibuf += sizeof(id); fileid = id; - - if ((upath = cnid_resolve(vol->v_db, &id)) == NULL) { + + if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) { return AFPERR_NOID; } - if (( dir = dirsearch( vol, id )) == NULL ) { + if (NULL == ( dir = dirlookup( vol, id )) ) { return( AFPERR_PARAM ); } err = AFP_OK; - if ((movecwd(vol, dir) < 0) || (stat(upath, &st) < 0)) { + if ((movecwd(vol, dir) < 0) || (lstat(upath, &st) < 0)) { switch (errno) { case EACCES: case EPERM: return AFPERR_ACCESS; +#ifdef ESTALE + case ESTALE: +#endif case ENOENT: /* still try to delete the id */ err = AFPERR_NOOBJ; @@ -1520,12 +1917,10 @@ int ibuflen, *rbuflen; return AFPERR_PARAM; } } - - /* directories are bad */ - if (S_ISDIR(st.st_mode)) + else if (S_ISDIR(st.st_mode)) /* directories are bad */ return AFPERR_BADTYPE; - if (cnid_delete(vol->v_db, fileid)) { + if (cnid_delete(vol->v_cdb, fileid)) { switch (errno) { case EROFS: return AFPERR_VLOCK; @@ -1537,36 +1932,81 @@ int ibuflen, *rbuflen; } } -#ifdef DEBUG - LOG(log_info, logtype_default, "end afp_deleteid:"); -#endif /* DEBUG */ - return err; } -#endif /* CNID_DB */ + +/* ------------------------------ */ +static struct adouble *find_adouble(struct path *path, struct ofork **of, struct adouble *adp) +{ + int ret; + + if (path->st_errno) { + switch (path->st_errno) { + case ENOENT: + afp_errno = AFPERR_NOID; + break; + case EPERM: + case EACCES: + afp_errno = AFPERR_ACCESS; + break; + default: + afp_errno = AFPERR_PARAM; + break; + } + return NULL; + } + /* we use file_access both for legacy Mac perm and + * for unix privilege, rename will take care of folder perms + */ + if (file_access(path, OPENACC_WR ) < 0) { + afp_errno = AFPERR_ACCESS; + return NULL; + } + + if ((*of = of_findname(path))) { + /* reuse struct adouble so it won't break locks */ + adp = (*of)->of_ad; + } + else { + ret = ad_open( path->u_name, ADFLAGS_HF, O_RDONLY, 0, adp); + /* META and HF */ + if ( !ret && ad_reso_fileno(adp) != -1 && !(adp->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) { + /* from AFP spec. + * The user must have the Read & Write privilege for both files in order to use this command. + */ + ad_close(adp, ADFLAGS_HF); + afp_errno = AFPERR_ACCESS; + return NULL; + } + } + return adp; +} #define APPLETEMP ".AppleTempXXXXXX" -int afp_exchangefiles(obj, ibuf, ibuflen, rbuf, rbuflen ) -AFPObj *obj; -char *ibuf, *rbuf; -int ibuflen, *rbuflen; +int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen) { struct stat srcst, destst; struct vol *vol; struct dir *dir, *sdir; - char *spath, temp[17], *path, *p; + char *spath, temp[17], *p; char *supath, *upath; + struct path *path; int err; -#ifdef CNID_DB + struct adouble ads; + struct adouble add; + struct adouble *adsp = NULL; + struct adouble *addp = NULL; + struct ofork *s_of = NULL; + struct ofork *d_of = NULL; + int crossdev; + int slen, dlen; -#endif /* CNID_DB */ u_int32_t sid, did; u_int16_t vid; -#ifdef DEBUG - LOG(log_info, logtype_default, "begin afp_exchangefiles:"); -#endif /* DEBUG */ + uid_t uid; + gid_t gid; *rbuflen = 0; ibuf += 2; @@ -1574,11 +2014,11 @@ int ibuflen, *rbuflen; memcpy(&vid, ibuf, sizeof(vid)); ibuf += sizeof(vid); - if (( vol = getvolbyvid( vid )) == NULL ) { + if (NULL == ( vol = getvolbyvid( vid )) ) { return( AFPERR_PARAM); } - if (vol->v_flags & AFPVOL_RO) + if ((vol->v_flags & AFPVOL_RO)) return AFPERR_VLOCK; /* source and destination dids */ @@ -1588,108 +2028,130 @@ int ibuflen, *rbuflen; ibuf += sizeof(did); /* source file */ - if ((dir = dirsearch( vol, sid )) == NULL ) { - return( AFPERR_PARAM ); + if (NULL == (dir = dirlookup( vol, sid )) ) { + return afp_errno; /* was AFPERR_PARAM */ } - if (( path = cname( vol, dir, &ibuf )) == NULL ) { - return( AFPERR_PARAM ); - } - - if ( *path == '\0' ) { - return( AFPERR_BADTYPE ); + if (NULL == ( path = cname( vol, dir, &ibuf )) ) { + return get_afp_errno(AFPERR_NOOBJ); } - upath = mtoupath(vol, path); - if (stat(upath, &srcst) < 0) { - switch (errno) { - case ENOENT: - return AFPERR_NOID; - case EPERM: - case EACCES: - return AFPERR_ACCESS; - default: - return AFPERR_PARAM; - } + if ( path_isadir(path) ) { + return AFPERR_BADTYPE; /* it's a dir */ } /* save some stuff */ + srcst = path->st; sdir = curdir; spath = obj->oldtmp; supath = obj->newtmp; - strcpy(spath, path); - strcpy(supath, upath); /* this is for the cnid changing */ - p = ctoupath( vol, sdir, spath); + strcpy(spath, path->m_name); + strcpy(supath, path->u_name); /* this is for the cnid changing */ + p = absupath( vol, sdir, supath); + if (!p) { + /* pathname too long */ + return AFPERR_PARAM ; + } + + ad_init(&ads, vol->v_adouble, vol->v_ad_options); + if (!(adsp = find_adouble( path, &s_of, &ads))) { + return afp_errno; + } + /* ***** from here we may have resource fork open **** */ + /* look for the source cnid. if it doesn't exist, don't worry about * it. */ -#ifdef CNID_DB - sid = cnid_lookup(vol->v_db, &srcst, sdir->d_did, supath, - slen = strlen(supath)); -#endif /* CNID_DB */ + sid = cnid_lookup(vol->v_cdb, &srcst, sdir->d_did, supath,slen = strlen(supath)); - if (( dir = dirsearch( vol, did )) == NULL ) { - return( AFPERR_PARAM ); + if (NULL == ( dir = dirlookup( vol, did )) ) { + err = afp_errno; /* was AFPERR_PARAM */ + goto err_exchangefile; } - if (( path = cname( vol, dir, &ibuf )) == NULL ) { - return( AFPERR_PARAM ); + if (NULL == ( path = cname( vol, dir, &ibuf )) ) { + err = get_afp_errno(AFPERR_NOOBJ); + goto err_exchangefile; } - if ( *path == '\0' ) { - return( AFPERR_BADTYPE ); + if ( path_isadir(path) ) { + err = AFPERR_BADTYPE; + goto err_exchangefile; } /* FPExchangeFiles is the only call that can return the SameObj * error */ - if ((curdir == sdir) && strcmp(spath, path) == 0) - return AFPERR_SAMEOBJ; + if ((curdir == sdir) && strcmp(spath, path->m_name) == 0) { + err = AFPERR_SAMEOBJ; + goto err_exchangefile; + } - upath = mtoupath(vol, path); - if (stat(upath, &destst) < 0) { - switch (errno) { - case ENOENT: - return AFPERR_NOID; - case EPERM: - case EACCES: - return AFPERR_ACCESS; - default: - return AFPERR_PARAM; - } + ad_init(&add, vol->v_adouble, vol->v_ad_options); + if (!(addp = find_adouble( path, &d_of, &add))) { + err = afp_errno; + goto err_exchangefile; + } + destst = path->st; + + /* they are not on the same device and at least one is open + * FIXME broken for for crossdev and adouble v2 + * return an error + */ + crossdev = (srcst.st_dev != destst.st_dev); + if (/* (d_of || s_of) && */ crossdev) { + err = AFPERR_MISC; + goto err_exchangefile; } -#ifdef CNID_DB /* look for destination id. */ - did = cnid_lookup(vol->v_db, &destst, curdir->d_did, upath, - dlen = strlen(upath)); -#endif /* CNID_DB */ + upath = path->u_name; + did = cnid_lookup(vol->v_cdb, &destst, curdir->d_did, upath, dlen = strlen(upath)); /* construct a temp name. * NOTE: the temp file will be in the dest file's directory. it * will also be inaccessible from AFP. */ memcpy(temp, APPLETEMP, sizeof(APPLETEMP)); - if (!mktemp(temp)) - return AFPERR_MISC; + if (!mktemp(temp)) { + err = AFPERR_MISC; + goto err_exchangefile; + } + + if (crossdev) { + /* FIXME we need to close fork for copy, both s_of and d_of are null */ + ad_close(adsp, ADFLAGS_HF); + ad_close(addp, ADFLAGS_HF); + } /* now, quickly rename the file. we error if we can't. */ - if ((err = renamefile(p, temp, temp, vol_noadouble(vol))) < 0) + if ((err = renamefile(vol, p, temp, temp, adsp)) != AFP_OK) goto err_exchangefile; - of_rename(vol, sdir, spath, curdir, temp); + of_rename(vol, s_of, sdir, spath, curdir, temp); /* rename destination to source */ - if ((err = renamefile(path, p, spath, vol_noadouble(vol))) < 0) + if ((err = renamefile(vol, upath, p, spath, addp)) != AFP_OK) goto err_src_to_tmp; - of_rename(vol, curdir, path, sdir, spath); + of_rename(vol, d_of, curdir, path->m_name, sdir, spath); /* rename temp to destination */ - if ((err = renamefile(temp, upath, path, vol_noadouble(vol))) < 0) + if ((err = renamefile(vol, temp, upath, path->m_name, adsp)) != AFP_OK) goto err_dest_to_src; - of_rename(vol, curdir, temp, curdir, path); - -#ifdef CNID_DB - /* id's need switching. src -> dest and dest -> src. */ - if (sid && (cnid_update(vol->v_db, sid, &destst, curdir->d_did, - upath, dlen) < 0)) { + of_rename(vol, s_of, curdir, temp, curdir, path->m_name); + + /* id's need switching. src -> dest and dest -> src. + * we need to re-stat() if it was a cross device copy. + */ + if (sid) { + cnid_delete(vol->v_cdb, sid); + } + if (did) { + cnid_delete(vol->v_cdb, did); + } + if ((did && ( (crossdev && lstat( upath, &srcst) < 0) || + cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0)) + || + (sid && ( (crossdev && lstat(p, &destst) < 0) || + cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0)) + ) { switch (errno) { case EPERM: case EACCES: @@ -1700,48 +2162,82 @@ int ibuflen, *rbuflen; } goto err_temp_to_dest; } + + /* here we need to reopen if crossdev */ + if (sid && ad_setid(addp, destst.st_dev, destst.st_ino, sid, sdir->d_did, vol->v_stamp)) + { + ad_flush( addp ); + } + + if (did && ad_setid(adsp, srcst.st_dev, srcst.st_ino, did, curdir->d_did, vol->v_stamp)) + { + ad_flush( adsp ); + } - if (did && (cnid_update(vol->v_db, did, &srcst, sdir->d_did, - supath, slen) < 0)) { - switch (errno) { - case EPERM: - case EACCES: - err = AFPERR_ACCESS; - break; - default: - err = AFPERR_PARAM; - } + /* change perms, src gets dest perm and vice versa */ - if (sid) - cnid_update(vol->v_db, sid, &srcst, sdir->d_did, supath, slen); + uid = geteuid(); + gid = getegid(); + if (seteuid(0)) { + LOG(log_error, logtype_afpd, "seteuid failed %s", strerror(errno)); + err = AFP_OK; /* ignore error */ goto err_temp_to_dest; } -#endif /* CNID_DB */ -#ifdef DEBUG - LOG(log_info, logtype_default, "ending afp_exchangefiles:"); -#endif /* DEBUG */ + /* + * we need to exchange ACL entries as well + */ + /* exchange_acls(vol, p, upath); */ - return AFP_OK; + path->st = srcst; + path->st_valid = 1; + path->st_errno = 0; + path->m_name = NULL; + path->u_name = upath; + + setfilunixmode(vol, path, destst.st_mode); + setfilowner(vol, destst.st_uid, destst.st_gid, path); + path->st = destst; + path->st_valid = 1; + path->st_errno = 0; + path->u_name = p; + + setfilunixmode(vol, path, srcst.st_mode); + setfilowner(vol, srcst.st_uid, srcst.st_gid, path); + + if ( setegid(gid) < 0 || seteuid(uid) < 0) { + LOG(log_error, logtype_afpd, "can't seteuid back %s", strerror(errno)); + exit(EXITERR_SYS); + } + + err = AFP_OK; + goto err_exchangefile; /* all this stuff is so that we can unwind a failed operation * properly. */ err_temp_to_dest: /* rename dest to temp */ - renamefile(upath, temp, temp, vol_noadouble(vol)); - of_rename(vol, curdir, upath, curdir, temp); + renamefile(vol, upath, temp, temp, adsp); + of_rename(vol, s_of, curdir, upath, curdir, temp); err_dest_to_src: /* rename source back to dest */ - renamefile(p, upath, path, vol_noadouble(vol)); - of_rename(vol, sdir, spath, curdir, path); + renamefile(vol, p, upath, path->m_name, addp); + of_rename(vol, d_of, sdir, spath, curdir, path->m_name); err_src_to_tmp: /* rename temp back to source */ - renamefile(temp, p, spath, vol_noadouble(vol)); - of_rename(vol, curdir, temp, sdir, spath); + renamefile(vol, temp, p, spath, adsp); + of_rename(vol, s_of, curdir, temp, sdir, spath); err_exchangefile: + if ( !s_of && adsp && ad_meta_fileno(adsp) != -1 ) { /* META */ + ad_close(adsp, ADFLAGS_HF); + } + if ( !d_of && addp && ad_meta_fileno(addp) != -1 ) {/* META */ + ad_close(addp, ADFLAGS_HF); + } + return err; }