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=88f1ad12cdc3c5045663a9c2c726db059c7c960a;hb=2bf71d3ccf20c072bc67a9d075b6ac8b0798021e;hpb=dae33a574ac2963d8bd279eaf96e08b5b48afc03 diff --git a/etc/afpd/file.c b/etc/afpd/file.c index 88f1ad12..915219f5 100644 --- a/etc/afpd/file.c +++ b/etc/afpd/file.c @@ -1,5 +1,5 @@ /* - * $Id: file.c,v 1.77 2003-01-24 07:08:42 didg 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,6 +47,7 @@ char *strchr (), *strrchr (); #include "file.h" #include "filedir.h" #include "globals.h" +#include "unix.h" /* the format for the finderinfo fields (from IM: Toolbox Essentials): * field bytes subfield bytes @@ -78,45 +68,96 @@ 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 }; -/* FIXME mpath : unix or mac name ? (for now it's mac name ) */ -void *get_finderinfo(const char *mpath, struct adouble *adp, void *data) +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) { struct extmap *em; + void *ad_finder = NULL; + int chk_ext = 0; + + if (adp) + ad_finder = ad_entry(adp, ADEID_FINDERI); - if (adp) { - memcpy(data, ad_entry(adp, ADEID_FINDERI), 32); + if (ad_finder) { + memcpy(data, ad_finder, ADEDLEN_FINDERI); + /* default type ? */ + if (default_type(ad_finder)) + chk_ext = 1; } else { - memcpy(data, ufinderi, 32); + 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 ((!adp || !memcmp(ad_entry(adp, ADEID_FINDERI),ufinderi , 8 )) - && (em = getextmap( mpath )) - ) { + 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(data + 4, em->em_creator, sizeof(em->em_creator)); + memcpy((char *)data + 4, em->em_creator, sizeof(em->em_creator)); } return data; } /* --------------------- */ -char *set_name(char *data, const char *name, u_int32_t utf8) +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; - + u_int32_t aint; + char *tp = NULL; + char *src = name; aint = strlen( name ); if (!utf8) { - if (afp_version >= 30) { - /* the name is in 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; @@ -128,7 +169,7 @@ char *set_name(char *data, const char *name, u_int32_t utf8) if (aint > 255) /* FIXME safeguard, anyway if no ascii char it's game over*/ aint = 255; - utf8 = htonl(utf8); + utf8 = vol->v_kTextEncoding; memcpy(data, &utf8, sizeof(utf8)); data += sizeof(utf8); @@ -137,9 +178,12 @@ char *set_name(char *data, const char *name, u_int32_t utf8) data += sizeof(temp); } - memcpy( data, name, aint ); + memcpy( data, src, aint ); data += aint; - + if (tp) { + strcpy(name, tp); + free(tp); + } return data; } @@ -153,33 +197,90 @@ char *set_name(char *data, const char *name, u_int32_t utf8) (1 << FILPBIT_FINFO) |\ (1 << FILPBIT_RFLEN) |\ (1 << FILPBIT_EXTRFLEN) |\ - (1 << FILPBIT_PDINFO))) + (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, - char *path, struct dir *dir, struct stat *st, - char *buf, int *buflen, struct adouble *adp, int attrbits ) + struct path *path, struct dir *dir, + char *buf, size_t *buflen, struct adouble *adp) { -#ifndef USE_LASTDID - struct stat lst, *lstp; -#endif /* USE_LASTDID */ 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_afpd, "begin getmetadata:"); + LOG(log_debug9, logtype_afpd, "begin getmetadata:"); #endif /* DEBUG */ - upath = mtoupath(vol, path); + 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; @@ -190,7 +291,7 @@ int getmetadata(struct vol *vol, case FILPBIT_ATTR : if ( adp ) { ad_getattr(adp, &ashort); - } else if (*upath == '.') { + } else if (vol_inv_dots(vol) && *upath == '.') { ashort = htons(ATTRBIT_INVISIBLE); } else ashort = 0; @@ -202,12 +303,10 @@ int getmetadata(struct vol *vol, if ((ma.ma_user & AR_UWRITE)) { accessmode( upath, &ma, dir , st); if (!(ma.ma_user & AR_UWRITE)) { - attrbits |= ATTRBIT_NOWRITE; + ashort |= htons(ATTRBIT_NOWRITE); } } #endif - if (attrbits) - ashort = htons(ntohs(ashort) | attrbits); memcpy(data, &ashort, sizeof( ashort )); data += sizeof( ashort ); break; @@ -244,15 +343,8 @@ int getmetadata(struct vol *vol, break; case FILPBIT_FINFO : - get_finderinfo(path, adp, (char *)data); - if (!adp) { - if (*upath == '.') { /* make it invisible */ - ashort = htons(FINDERINFO_INVISIBLE); - memcpy(data + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort)); - } - } - - data += 32; + get_finderinfo(vol, upath, adp, (char *)data,S_ISLNK(st->st_mode)); + data += ADEDLEN_FINDERI; break; case FILPBIT_LNAME : @@ -266,68 +358,8 @@ int getmetadata(struct vol *vol, break; case FILPBIT_FNUM : - aint = 0; -#if AD_VERSION > AD_VERSION1 - /* look in AD v2 header */ - if (adp) - 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_afpd, "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; - aint = htonl(CNID(lstp, 1)); -#endif /* USE_LASTDID */ - } - - memcpy(data, &aint, sizeof( aint )); - data += sizeof( aint ); + memcpy(data, &id, sizeof( id )); + data += sizeof( id ); break; case FILPBIT_DFLEN : @@ -428,6 +460,40 @@ int getmetadata(struct vol *vol, 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 : return( AFPERR_BITMAP ); } @@ -437,12 +503,12 @@ int getmetadata(struct vol *vol, if ( l_nameoff ) { ashort = htons( data - buf ); memcpy(l_nameoff, &ashort, sizeof( ashort )); - data = set_name(data, path, 0); + data = set_name(vol, data, dir->d_did, path->m_name, id, 0); } if ( utf_nameoff ) { ashort = htons( data - buf ); memcpy(utf_nameoff, &ashort, sizeof( ashort )); - data = set_name(data, path, utf8); + data = set_name(vol, data, dir->d_did, path->m_name, id, utf8); } *buflen = data - buf; return (AFP_OK); @@ -452,68 +518,56 @@ int getmetadata(struct vol *vol, int getfilparams(struct vol *vol, u_int16_t bitmap, struct path *path, struct dir *dir, - char *buf, int *buflen ) + char *buf, size_t *buflen ) { struct adouble ad, *adp; - struct ofork *of; - char *upath; - u_int16_t attrbits = 0; int opened = 0; int rc; #ifdef DEBUG - LOG(log_info, logtype_default, "begin getfilparams:"); + LOG(log_debug9, logtype_default, "begin getfilparams:"); #endif /* DEBUG */ 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 ((of = of_findname(path))) { - adp = of->of_ad; - attrbits = ((of->of_ad->ad_df.adf_refcount > 0) ? ATTRBIT_DOPEN : 0); - attrbits |= ((of->of_ad->ad_hf.adf_refcount > of->of_ad->ad_df.adf_refcount)? ATTRBIT_ROPEN : 0); - } else { - memset(&ad, 0, sizeof(ad)); - adp = &ad; - } - if ( ad_open( upath, ADFLAGS_HF, O_RDONLY, 0, adp) < 0 ) { - adp = NULL; + 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; + } } - else { - /* FIXME - we need to check if the file is open by another process. - it's slow so we only do it if we have to: - - bitmap is requested. - - we don't already have the answer! - */ - if ((bitmap & (1 << FILPBIT_ATTR))) { - if (!(attrbits & ATTRBIT_ROPEN)) { - } - if (!(attrbits & ATTRBIT_DOPEN)) { - } - } - } } - rc = getmetadata(vol, bitmap, path->m_name, dir, &path->st, buf, buflen, adp, attrbits); + rc = getmetadata(vol, bitmap, path, dir, buf, buflen, adp); if ( adp ) { - ad_close( adp, ADFLAGS_HF ); + ad_close_metadata( adp); } #ifdef DEBUG - LOG(log_info, logtype_afpd, "end getfilparams:"); + 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; @@ -521,13 +575,8 @@ int ibuflen, *rbuflen; char *path, *upath; int creatf, did, openf, retvalue = AFP_OK; u_int16_t vid; - int ret; struct path *s_path; -#ifdef DEBUG - LOG(log_info, logtype_afpd, "begin afp_createfile:"); -#endif /* DEBUG */ - *rbuflen = 0; ibuf++; creatf = (unsigned char) *ibuf++; @@ -550,7 +599,7 @@ int ibuflen, *rbuflen; } if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) { - return afp_errno; + return get_afp_errno(AFPERR_PARAM); } if ( *s_path->m_name == '\0' ) { @@ -558,14 +607,12 @@ int ibuflen, *rbuflen; } upath = s_path->u_name; - if (0 != (ret = check_name(vol, upath))) - return ret; /* 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) { @@ -583,28 +630,41 @@ int ibuflen, *rbuflen; openf = O_RDWR|O_CREAT|O_EXCL; } - 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 : return( AFPERR_EXIST ); case EACCES : return( AFPERR_ACCESS ); - case ENOENT: - /* on noadouble volumes, just creating the data fork is ok */ - st = &s_path->st; - if (vol_noadouble(vol) && (stat(upath, st) == 0)) - goto createfile_done; - /* fallthrough */ + case EDQUOT: + case ENOSPC : + return( AFPERR_DFULL ); default : return( AFPERR_PARAM ); } } + 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_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 ); + ad_setname(adp, path); + ad_flush( adp); ad_close( adp, ADFLAGS_DF|ADFLAGS_HF ); createfile_done: @@ -618,17 +678,10 @@ createfile_done: setvoltime(obj, vol ); -#ifdef DEBUG - LOG(log_info, logtype_afpd, "end afp_createfile"); -#endif /* DEBUG */ - 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; @@ -636,10 +689,6 @@ int ibuflen, *rbuflen; int did, rc; u_int16_t vid, bitmap; -#ifdef DEBUG - LOG(log_info, logtype_afpd, "begin afp_setfilparams:"); -#endif /* DEBUG */ - *rbuflen = 0; ibuf += 2; @@ -663,13 +712,17 @@ int ibuflen, *rbuflen; ibuf += sizeof( bitmap ); if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) { - return afp_errno; + 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 ); + } + if ((u_long)ibuf & 1 ) { ibuf++; } @@ -678,132 +731,133 @@ int ibuflen, *rbuflen; setvoltime(obj, vol ); } -#ifdef DEBUG - LOG(log_info, logtype_afpd, "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, - struct path *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; 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_afpd, "begin setfilparams:"); + LOG(log_debug9, logtype_afpd, "begin setfilparams:"); #endif /* DEBUG */ + adp = of_ad(vol, path, &ad); upath = path->u_name; - if ((of = of_findname(path))) { - adp = of->of_ad; - } else { - memset(&ad, 0, sizeof(ad)); - adp = &ad; - } - if (check_access(upath, OPENACC_WR ) < 0) { + if (!vol_unix_priv(vol) && check_access(upath, OPENACC_WR ) < 0) { return AFPERR_ACCESS; } - 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<m_name )); - memcpy(ad_entry( adp, ADEID_NAME ), path->m_name, - ad_getentrylen( adp, ADEID_NAME )); - } - + /* with unix priv maybe we have to change adouble file priv first */ + bit = 0; while ( bitmap != 0 ) { while (( bitmap & 1 ) == 0 ) { bitmap = bitmap>>1; bit++; } - switch( bit ) { case FILPBIT_ATTR : change_mdate = 1; memcpy(&ashort, buf, sizeof( ashort )); - ad_getattr(adp, &bshort); - if ( ntohs( ashort ) & ATTRBIT_SETCLR ) { - bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR ); - } else { - bshort &= ~ashort; - } - if ((ashort & htons(ATTRBIT_INVISIBLE))) - change_parent_mdate = 1; - ad_setattr(adp, bshort); buf += sizeof( ashort ); break; - case FILPBIT_CDATE : change_mdate = 1; - memcpy(&aint, buf, sizeof(aint)); - ad_setdate(adp, AD_DATE_CREATE, aint); - buf += sizeof( aint ); + 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(&aint, buf, sizeof(aint)); - ad_setdate(adp, AD_DATE_BACKUP, aint); - buf += sizeof( aint ); + memcpy(&bdate, buf, sizeof( bdate)); + buf += sizeof( bdate ); break; - case FILPBIT_FINFO : change_mdate = 1; - - if (!memcmp( ad_entry( adp, ADEID_FINDERI ), ufinderi, 8 ) - && ( - ((em = getextmap( path->m_name )) && - !memcmp(buf, em->em_type, sizeof( em->em_type )) && - !memcmp(buf + 4, em->em_creator,sizeof( em->em_creator))) - || ((em = getdefextmap()) && - !memcmp(buf, em->em_type, sizeof( em->em_type )) && - !memcmp(buf + 4, em->em_creator,sizeof( em->em_creator))) - )) { - memcpy(buf, ufinderi, 8 ); + 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; + } } - - memcpy(ad_entry( adp, ADEID_FINDERI ), buf, 32 ); 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); - /* Client needs to set the ProDOS file info for this file. - Use a defined string for TEXT to support crlf - translations and convert all else into pXYY per Inside - Appletalk. Always set the creator as "pdos". Changes - from original by Marsha Jackson. */ + 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; @@ -819,6 +873,89 @@ int setfilparams(struct vol *vol, 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; + bit++; + } + + switch( bit ) { + case FILPBIT_ATTR : + 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); + break; + case FILPBIT_CDATE : + ad_setdate(adp, AD_DATE_CREATE, cdate); + break; + case FILPBIT_MDATE : + break; + case FILPBIT_BDATE : + ad_setdate(adp, AD_DATE_BACKUP, bdate); + break; + case FILPBIT_FINFO : + 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); + } + break; + case FILPBIT_PDINFO : + 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; @@ -828,7 +965,6 @@ int setfilparams(struct vol *vol, err = AFPERR_BITMAP; goto setfilparam_done; } - bitmap = bitmap>>1; bit++; } @@ -845,8 +981,8 @@ setfilparam_done: } if (isad) { - ad_flush( adp, ADFLAGS_HF ); - ad_close( adp, ADFLAGS_HF ); + ad_flush( adp); + ad_close_metadata( adp); } @@ -857,7 +993,7 @@ setfilparam_done: } #ifdef DEBUG - LOG(log_info, logtype_afpd, "end setfilparams:"); + LOG(log_debug9, logtype_afpd, "end setfilparams:"); #endif /* DEBUG */ return err; } @@ -865,8 +1001,6 @@ setfilparam_done: /* * renamefile and copyfile take the old and new unix pathnames * and the new mac name. - * NOTE: if we have to copy a file instead of renaming it, locks - * will break. Anyway it's an error because then we have 2 files. * * src the source path * dst the dest filename in current dir @@ -874,25 +1008,12 @@ setfilparam_done: * adp adouble struct of src file, if open, or & zeroed one * */ -int renamefile(src, dst, newname, noadouble, adp ) -char *src, *dst, *newname; -const int noadouble; -struct adouble *adp; +int renamefile(const struct vol *vol, char *src, char *dst, char *newname, struct adouble *adp) { - char adsrc[ MAXPATHLEN + 1]; - int len, rc; - - /* - * Note that this is only checking the existance of the data file, - * not the header file. The thinking is that if the data file doesn't - * exist, but the header file does, the right thing to do is remove - * the data file silently. - */ - - /* existence check moved to afp_moveandrename */ + int rc; #ifdef DEBUG - LOG(log_info, logtype_afpd, "begin renamefile:"); + LOG(log_debug9, logtype_afpd, "begin renamefile:"); #endif /* DEBUG */ if ( unix_rename( src, dst ) < 0 ) { @@ -905,69 +1026,77 @@ struct adouble *adp; case EROFS: return AFPERR_VLOCK; case EXDEV : /* Cross device move -- try copy */ - if (( rc = copyfile(src, dst, newname, noadouble )) != AFP_OK ) { - deletefile( dst, 0 ); + /* NOTE: with open file it's an error because after the copy we will + * get two files, it's fixable for our process (eg reopen the new file, get the + * locks, and so on. But it doesn't solve the case with a second process + */ + if (adp->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, 0); + return deletefile(vol, src, 0); default : return( AFPERR_PARAM ); } } - strcpy( adsrc, ad_path( src, 0 )); - rc = 0; -rename_retry: - if (unix_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. */ - if (rc || stat(adsrc, &st) || - (ad_open(dst, ADFLAGS_HF, O_RDWR | O_CREAT, 0666, adp) < 0)) - return AFP_OK; - rc++; - ad_close(adp, 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 ; + } } } - if ( ad_open( dst, ADFLAGS_HF, O_RDWR, 0666, adp) < 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( adp, ADEID_NAME, len ); - memcpy(ad_entry( adp, ADEID_NAME ), newname, len ); - ad_flush( adp, ADFLAGS_HF ); - ad_close( adp, ADFLAGS_HF ); - #ifdef DEBUG - LOG(log_info, logtype_afpd, "end renamefile:"); + LOG(log_debug9, logtype_afpd, "end renamefile:"); #endif /* DEBUG */ return( AFP_OK ); } -int copy_path_name(char *newname, char *ibuf) +/* ---------------- + 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) +{ +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; @@ -981,8 +1110,16 @@ u_int32_t hint; switch (type) { case 2: if (( plen = (unsigned char)*ibuf++ ) != 0 ) { - strncpy( newname, ibuf, plen ); - newname[ plen ] = '\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. @@ -998,10 +1135,14 @@ u_int32_t 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 (strchr(newname,'/')) { + if (strlen(newname) != plen) { return -1; } } @@ -1012,12 +1153,9 @@ u_int32_t hint; /* ----------------------------------- */ -int afp_copyfile(obj, ibuf, ibuflen, rbuf, rbuflen ) -AFPObj *obj; -char *ibuf, *rbuf; -int ibuflen, *rbuflen; +int afp_copyfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen) { - struct vol *vol; + struct vol *s_vol, *d_vol; struct dir *dir; char *newname, *p, *upath; struct path *s_path; @@ -1025,22 +1163,21 @@ int ibuflen, *rbuflen; int err, retvalue = AFP_OK; u_int16_t svid, dvid; -#ifdef DEBUG - LOG(log_info, logtype_afpd, "begin afp_copyfile:"); -#endif /* DEBUG */ - + struct adouble ad, *adp; + int denyreadset; + *rbuflen = 0; ibuf += 2; memcpy(&svid, ibuf, sizeof( svid )); ibuf += sizeof( svid ); - if (NULL == ( vol = getvolbyvid( svid )) ) { + if (NULL == ( s_vol = getvolbyvid( svid )) ) { return( AFPERR_PARAM ); } memcpy(&sdid, ibuf, sizeof( sdid )); ibuf += sizeof( sdid ); - if (NULL == ( dir = dirlookup( vol, sdid )) ) { + if (NULL == ( dir = dirlookup( s_vol, sdid )) ) { return afp_errno; } @@ -1049,8 +1186,8 @@ int ibuflen, *rbuflen; memcpy(&ddid, ibuf, sizeof( ddid )); ibuf += sizeof( ddid ); - if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) { - return afp_errno; + if (NULL == ( s_path = cname( s_vol, dir, &ibuf )) ) { + return get_afp_errno(AFPERR_PARAM); } if ( path_isadir(s_path) ) { return( AFPERR_BADTYPE ); @@ -1062,13 +1199,22 @@ int ibuflen, *rbuflen; * and locks need to stay coherent. as a result, * we just balk if the file is opened already. */ - newname = obj->newtmp; - strcpy( newname, s_path->m_name ); + adp = of_ad(s_vol, s_path, &ad); - if (of_findname(s_path)) + 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, s_path->m_name ); - p = ctoupath( vol, curdir, newname ); + p = ctoupath( s_vol, curdir, newname ); if (!p) { return AFPERR_PARAM; @@ -1076,31 +1222,35 @@ int ibuflen, *rbuflen; #ifdef FORCE_UIDGID /* FIXME svid != dvid && dvid's user can't read svid */ #endif - if (NULL == ( vol = getvolbyvid( dvid )) ) { + 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 (NULL == ( dir = dirlookup( vol, ddid )) ) { + if (NULL == ( dir = dirlookup( d_vol, ddid )) ) { return afp_errno; } - if (( s_path = cname( vol, dir, &ibuf )) == NULL ) { - return afp_errno; + if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) { + return get_afp_errno(AFPERR_NOOBJ); } if ( *s_path->m_name != '\0' ) { - return (path_isadir( s_path))? AFPERR_PARAM:AFPERR_BADTYPE ; + path_error(s_path, AFPERR_PARAM); } /* one of the handful of places that knows about the path type */ - if (copy_path_name(newname, ibuf) < 0) { + if (copy_path_name(d_vol, newname, ibuf) < 0) { return( AFPERR_PARAM ); } - - upath = mtoupath(vol, newname); - if ( (err = copyfile(p, upath , newname, vol_noadouble(vol))) < 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(s_vol, d_vol, p, upath , newname, adp)) < 0 ) { return err; } curdir->offcnt++; @@ -1111,23 +1261,19 @@ int ibuflen, *rbuflen; } #endif /* DROPKLUDGE */ - setvoltime(obj, vol ); - -#ifdef DEBUG - LOG(log_info, logtype_afpd, "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_afpd, "begin copy_all:"); + LOG(log_debug9, logtype_afpd, "begin copy_all:"); #endif /* DEBUG */ while (buflen > 0) { @@ -1135,273 +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_afpd, "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; -#ifdef SENDFILE_FLAVOR_LINUX + 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; -#endif - char filebuf[8192]; - int sfd, dfd, len, err = AFP_OK; - ssize_t cc; - char dpath[ MAXPATHLEN + 1]; - int admode; -#ifdef DEBUG - LOG(log_info, logtype_afpd, "begin copyfile:"); -#endif /* DEBUG */ + #define BUF 128*1024*1024 - strcpy(dpath, ad_path( dst, ADFLAGS_HF )); - admode = ad_mode( dst, 0666 ); - 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( dpath, O_WRONLY|O_CREAT,ad_hf_mode(admode))) < 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(dpath); - 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, admode)) < 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(dpath); - unlink(dst); - return err; + ret_err = errno; } - 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 (!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 (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_afpd, "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; } /* ----------------------------------- - checkAttrib: 1 check kFPDeleteInhibitBit - ie deletfile called by afp_delete + 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. */ -int deletefile( file, checkAttrib ) -char *file; -int checkAttrib; + +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_afpd, "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: @@ -1410,20 +1561,8 @@ int checkAttrib; } break; /* from the while */ } - /* - * Does kFPDeleteInhibitBit (bit 8) set? - */ - if (checkAttrib && (adflags & ADFLAGS_HF)) { - u_int16_t bshort; - ad_getattr(&ad, &bshort); - if ((bshort & htons(ATTRBIT_NODELETE))) { - ad_close( &ad, adflags ); - return(AFPERR_OLOCK); - } - } - - if ((adflags & ADFLAGS_HF) ) { + 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. @@ -1431,78 +1570,39 @@ int checkAttrib; * ADLOCK_FILELOCK means the whole ressource fork, not only after the * metadatas * - * FIXME it doesn't for RFORK open read only and fork open without deny mode + * FIXME it doesn't work for RFORK open read only and fork open without deny mode */ - if (ad_tmplock(&ad, ADEID_RFORK, locktype |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) { + 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 ) < 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; + 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); } } - - 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; - } - } - -delete_unlock: - if (adflags & ADFLAGS_HF) - ad_tmplock(&ad, ADEID_RFORK, ADLOCK_CLR |ADLOCK_FILELOCK, 0, 0, 0); - ad_tmplock(&ad, ADEID_DFORK, ADLOCK_CLR, 0, 0, 0); - ad_close( &ad, adflags ); + if (adp) + ad_close( &ad, adflags ); /* ad_close removes locks if any */ #ifdef DEBUG - LOG(log_info, logtype_afpd, "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; -#if AD_VERSION > AD_VERSION1 - struct adouble ad; -#endif struct vol *vol; struct dir *dir; char *upath; @@ -1511,11 +1611,8 @@ int ibuflen, *rbuflen; u_short vid; struct path *s_path; -#ifdef DEBUG - LOG(log_info, logtype_afpd, "begin afp_createid:"); -#endif /* DEBUG */ - *rbuflen = 0; + ibuf += 2; memcpy(&vid, ibuf, sizeof(vid)); @@ -1525,6 +1622,10 @@ int ibuflen, *rbuflen; 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; @@ -1532,11 +1633,11 @@ int ibuflen, *rbuflen; ibuf += sizeof(did); if (NULL == ( dir = dirlookup( vol, did )) ) { - return( AFPERR_PARAM ); + return afp_errno; /* was AFPERR_PARAM */ } - if (( s_path = cname( vol, dir, &ibuf )) == NULL ) { - return afp_errno; /* was AFPERR_PARAM */ + if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) { + return get_afp_errno(AFPERR_NOOBJ); /* was AFPERR_PARAM */ } if ( path_isadir(s_path) ) { @@ -1556,66 +1657,115 @@ int ibuflen, *rbuflen; return AFPERR_PARAM; } st = &s_path->st; - if ((id = cnid_lookup(vol->v_db, st, did, upath, len = strlen(upath)))) { + 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_afpd, "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_afpd, "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; +} + +/* -------------------- + * 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(obj, ibuf, ibuflen, rbuf, rbuflen ) -AFPObj *obj; -char *ibuf, *rbuf; -int ibuflen, *rbuflen; +int afp_resolveid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen) { struct vol *vol; struct dir *dir; char *upath; struct path path; - int err, buflen; - cnid_t id; + int err, retry=0; + size_t buflen; + cnid_t id, cnid; u_int16_t vid, bitmap; static char buffer[12 + MAXPATHLEN + 1]; int len = 12 + MAXPATHLEN + 1; -#ifdef DEBUG - LOG(log_info, logtype_afpd, "begin afp_resolveid:"); -#endif /* DEBUG */ - *rbuflen = 0; ibuf += 2; @@ -1626,18 +1776,54 @@ int ibuflen, *rbuflen; 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 (NULL == (upath = cnid_resolve(vol->v_db, &id, buffer, len)) ) { + 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 = dirlookup( vol, id )) == NULL ) { + 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; + } + } + + memset(&path, 0, sizeof(path)); path.u_name = upath; - if (movecwd(vol, dir) < 0 || of_stat(&path) < 0) { + 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: @@ -1648,32 +1834,31 @@ int ibuflen, *rbuflen; return AFPERR_PARAM; } } + /* directories are bad */ - if (S_ISDIR(path.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 ); - path.m_name = utompath(vol, upath); - if ((err = getfilparams(vol, bitmap, &path , curdir, - 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_afpd, "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; @@ -1686,10 +1871,6 @@ int ibuflen, *rbuflen; static char buffer[12 + MAXPATHLEN + 1]; int len = 12 + MAXPATHLEN + 1; -#ifdef DEBUG - LOG(log_info, logtype_afpd, "begin afp_deleteid:"); -#endif /* DEBUG */ - *rbuflen = 0; ibuf += 2; @@ -1700,6 +1881,10 @@ int ibuflen, *rbuflen; 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; @@ -1707,20 +1892,23 @@ int ibuflen, *rbuflen; ibuf += sizeof(id); fileid = id; - if (NULL == (upath = cnid_resolve(vol->v_db, &id, buffer, len)) ) { + if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) { return AFPERR_NOID; } - if (( dir = dirlookup( 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; @@ -1729,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; @@ -1746,20 +1932,59 @@ int ibuflen, *rbuflen; } } -#ifdef DEBUG - LOG(log_info, logtype_afpd, "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; @@ -1770,20 +1995,18 @@ int ibuflen, *rbuflen; int err; struct adouble ads; struct adouble add; - struct adouble *adsp; - struct adouble *addp; - struct ofork *s_of; - struct ofork *d_of; + struct adouble *adsp = NULL; + struct adouble *addp = NULL; + struct ofork *s_of = NULL; + struct ofork *d_of = NULL; + int crossdev; -#ifdef CNID_DB int slen, dlen; -#endif /* CNID_DB */ u_int32_t sid, did; u_int16_t vid; -#ifdef DEBUG - LOG(log_info, logtype_afpd, "begin afp_exchangefiles:"); -#endif /* DEBUG */ + uid_t uid; + gid_t gid; *rbuflen = 0; ibuf += 2; @@ -1795,7 +2018,7 @@ int ibuflen, *rbuflen; return( AFPERR_PARAM); } - if (vol->v_flags & AFPVOL_RO) + if ((vol->v_flags & AFPVOL_RO)) return AFPERR_VLOCK; /* source and destination dids */ @@ -1806,130 +2029,129 @@ int ibuflen, *rbuflen; /* source file */ if (NULL == (dir = dirlookup( vol, sid )) ) { - return( AFPERR_PARAM ); + return afp_errno; /* was AFPERR_PARAM */ } if (NULL == ( path = cname( vol, dir, &ibuf )) ) { - return afp_errno; /* was AFPERR_PARAM */ + return get_afp_errno(AFPERR_NOOBJ); } if ( path_isadir(path) ) { - return( AFPERR_BADTYPE ); /* it's a dir */ + return AFPERR_BADTYPE; /* it's a dir */ } - upath = path->u_name; - switch (path->st_errno) { - case 0: - break; - case ENOENT: - return AFPERR_NOID; - case EPERM: - case EACCES: - return AFPERR_ACCESS; - default: - return AFPERR_PARAM; - } - memset(&ads, 0, sizeof(ads)); - adsp = &ads; - if ((s_of = of_findname(path))) { - /* reuse struct adouble so it won't break locks */ - adsp = s_of->of_ad; - } - memcpy(&srcst, &path->st, sizeof(struct stat)); /* save some stuff */ + srcst = path->st; sdir = curdir; spath = obj->oldtmp; supath = obj->newtmp; strcpy(spath, path->m_name); - strcpy(supath, upath); /* this is for the cnid changing */ - p = absupath( vol, sdir, upath); + 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 (NULL == ( dir = dirlookup( vol, did )) ) { - return( AFPERR_PARAM ); + err = afp_errno; /* was AFPERR_PARAM */ + goto err_exchangefile; } if (NULL == ( path = cname( vol, dir, &ibuf )) ) { - return( AFPERR_PARAM ); + err = get_afp_errno(AFPERR_NOOBJ); + goto err_exchangefile; } if ( path_isadir(path) ) { - return( AFPERR_BADTYPE ); + err = AFPERR_BADTYPE; + goto err_exchangefile; } /* FPExchangeFiles is the only call that can return the SameObj * error */ - if ((curdir == sdir) && strcmp(spath, path->m_name) == 0) - return AFPERR_SAMEOBJ; - memcpy(&srcst, &path->st, sizeof(struct stat)); - - switch (errno) { - case 0: - break; - case ENOENT: - return AFPERR_NOID; - case EPERM: - case EACCES: - return AFPERR_ACCESS; - default: - return AFPERR_PARAM; + if ((curdir == sdir) && strcmp(spath, path->m_name) == 0) { + err = AFPERR_SAMEOBJ; + goto err_exchangefile; } - memset(&add, 0, sizeof(add)); - addp = &add; - if ((d_of = of_findname( path))) { - /* reuse struct adouble so it won't break locks */ - addp = d_of->of_ad; + + ad_init(&add, vol->v_adouble, vol->v_ad_options); + if (!(addp = find_adouble( path, &d_of, &add))) { + err = afp_errno; + goto err_exchangefile; } - memcpy(&destst, &path->st, sizeof(struct stat)); + 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 */ - if ((d_of || s_of) && srcst.st_dev != destst.st_dev) - return AFPERR_MISC; - - upath = path->u_name; -#ifdef CNID_DB + crossdev = (srcst.st_dev != destst.st_dev); + if (/* (d_of || s_of) && */ crossdev) { + err = AFPERR_MISC; + goto err_exchangefile; + } + /* 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), adsp)) < 0) + if ((err = renamefile(vol, p, temp, temp, adsp)) != AFP_OK) goto err_exchangefile; of_rename(vol, s_of, sdir, spath, curdir, temp); /* rename destination to source */ - if ((err = renamefile(upath, p, spath, vol_noadouble(vol), addp)) < 0) + if ((err = renamefile(vol, upath, p, spath, addp)) != AFP_OK) goto err_src_to_tmp; of_rename(vol, d_of, curdir, path->m_name, sdir, spath); /* rename temp to destination */ - if ((err = renamefile(temp, upath, path->m_name, vol_noadouble(vol), adsp)) < 0) + if ((err = renamefile(vol, temp, upath, path->m_name, adsp)) != AFP_OK) goto err_dest_to_src; of_rename(vol, s_of, curdir, temp, curdir, path->m_name); -#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)) { + /* 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: @@ -1940,50 +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_afpd, "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. */ -#ifdef CNID_DB err_temp_to_dest: -#endif /* rename dest to temp */ - renamefile(upath, temp, temp, vol_noadouble(vol), adsp); + 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->m_name, vol_noadouble(vol), addp); + 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), adsp); + 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; }