2 * $Id: file.c,v 1.141 2010-03-12 15:16:49 franklahm Exp $
4 * Copyright (c) 1990,1993 Regents of The University of Michigan.
5 * All Rights Reserved. See COPYRIGHT.
10 #endif /* HAVE_CONFIG_H */
18 #else /* STDC_HEADERS */
22 #endif /* HAVE_STRCHR */
23 char *strchr (), *strrchr ();
26 #define memcpy(d,s,n) bcopy ((s), (d), (n))
27 #define memmove(d,s,n) bcopy ((s), (d), (n))
28 #endif /* ! HAVE_MEMCPY */
29 #endif /* STDC_HEADERS */
33 #include <sys/param.h>
35 #include <atalk/adouble.h>
36 #include <atalk/vfs.h>
37 #include <atalk/logger.h>
38 #include <atalk/afp.h>
39 #include <atalk/util.h>
40 #include <atalk/cnid.h>
41 #include <atalk/unix.h>
43 #include "directory.h"
52 /* the format for the finderinfo fields (from IM: Toolbox Essentials):
53 * field bytes subfield bytes
56 * ioFlFndrInfo 16 -> type 4 type field
57 * creator 4 creator field
58 * flags 2 finder flags:
60 * location 4 location in window
61 * folder 2 window that contains file
63 * ioFlXFndrInfo 16 -> iconID 2 icon id
65 * script 1 script system
67 * commentID 2 comment id
68 * putawayID 4 home directory id
71 const u_char ufinderi[ADEDLEN_FINDERI] = {
72 0, 0, 0, 0, 0, 0, 0, 0,
73 1, 0, 0, 0, 0, 0, 0, 0,
74 0, 0, 0, 0, 0, 0, 0, 0,
75 0, 0, 0, 0, 0, 0, 0, 0
78 static const u_char old_ufinderi[] = {
79 'T', 'E', 'X', 'T', 'U', 'N', 'I', 'X'
82 /* ----------------------
84 static int default_type(void *finder)
86 if (!memcmp(finder, ufinderi, 8) || !memcmp(finder, old_ufinderi, 8))
91 /* FIXME path : unix or mac name ? (for now it's unix name ) */
92 void *get_finderinfo(const struct vol *vol, const char *upath, struct adouble *adp, void *data, int islink)
95 void *ad_finder = NULL;
99 ad_finder = ad_entry(adp, ADEID_FINDERI);
102 memcpy(data, ad_finder, ADEDLEN_FINDERI);
104 if (default_type(ad_finder))
108 memcpy(data, ufinderi, ADEDLEN_FINDERI);
110 if (vol_inv_dots(vol) && *upath == '.') { /* make it invisible */
113 ashort = htons(FINDERINFO_INVISIBLE);
114 memcpy((char *)data + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
120 memcpy(&linkflag, (char *)data + FINDERINFO_FRFLAGOFF, 2);
121 linkflag |= htons(FINDERINFO_ISALIAS);
122 memcpy((char *)data + FINDERINFO_FRFLAGOFF, &linkflag, 2);
123 memcpy((char *)data + FINDERINFO_FRTYPEOFF,"slnk",4);
124 memcpy((char *)data + FINDERINFO_FRCREATOFF,"rhap",4);
128 /** Only enter if no appledouble information and no finder information found. */
129 if (chk_ext && (em = getextmap( upath ))) {
130 memcpy(data, em->em_type, sizeof( em->em_type ));
131 memcpy((char *)data + 4, em->em_creator, sizeof(em->em_creator));
136 /* ---------------------
138 char *set_name(const struct vol *vol, char *data, cnid_t pid, char *name, cnid_t id, u_int32_t utf8)
143 aint = strlen( name );
147 if (utf8_encoding()) {
148 /* but name is an utf8 mac name */
151 /* global static variable... */
153 if (!(u = mtoupath(vol, name, pid, 1)) || !(m = utompath(vol, u, id, 0))) {
162 if (aint > MACFILELEN)
169 if (aint > 255) /* FIXME safeguard, anyway if no ascii char it's game over*/
172 utf8 = vol->v_kTextEncoding;
173 memcpy(data, &utf8, sizeof(utf8));
174 data += sizeof(utf8);
177 memcpy(data, &temp, sizeof(temp));
178 data += sizeof(temp);
181 memcpy( data, src, aint );
191 * FIXME: PDINFO is UTF8 and doesn't need adp
193 #define PARAM_NEED_ADP(b) ((b) & ((1 << FILPBIT_ATTR) |\
194 (1 << FILPBIT_CDATE) |\
195 (1 << FILPBIT_MDATE) |\
196 (1 << FILPBIT_BDATE) |\
197 (1 << FILPBIT_FINFO) |\
198 (1 << FILPBIT_RFLEN) |\
199 (1 << FILPBIT_EXTRFLEN) |\
200 (1 << FILPBIT_PDINFO) |\
201 (1 << FILPBIT_FNUM) |\
202 (1 << FILPBIT_UNIXPR)))
204 /* -------------------------- */
205 u_int32_t get_id(struct vol *vol, struct adouble *adp, const struct stat *st,
206 const cnid_t did, char *upath, const int len)
209 u_int32_t dbcnid = CNID_INVALID;
211 if (vol->v_cdb != NULL) {
212 /* prime aint with what we think is the cnid, set did to zero for
213 catching moved files */
214 adcnid = ad_getid(adp, st->st_dev, st->st_ino, 0, vol->v_stamp);
216 dbcnid = cnid_add(vol->v_cdb, st, did, upath, len, adcnid);
217 /* Throw errors if cnid_add fails. */
218 if (dbcnid == CNID_INVALID) {
220 case CNID_ERR_CLOSE: /* the db is closed */
223 LOG(log_error, logtype_afpd, "get_id: Incorrect parameters passed to cnid_add");
224 afp_errno = AFPERR_PARAM;
227 afp_errno = AFPERR_PARAM;
230 afp_errno = AFPERR_MISC;
234 else if (adp && (adcnid != dbcnid)) {
235 /* Update the ressource fork. For a folder adp is always null */
236 LOG(log_debug, logtype_afpd, "get_id: calling ad_setid. adcnid: %u, dbcnid: %u", htonl(adcnid), htonl(dbcnid));
237 if (ad_setid(adp, st->st_dev, st->st_ino, dbcnid, did, vol->v_stamp)) {
245 /* -------------------------- */
246 int getmetadata(struct vol *vol,
248 struct path *path, struct dir *dir,
249 char *buf, size_t *buflen, struct adouble *adp)
251 char *data, *l_nameoff = NULL, *upath;
252 char *utf_nameoff = NULL;
257 u_char achar, fdType[4];
263 LOG(log_debug9, logtype_afpd, "begin getmetadata:");
266 upath = path->u_name;
271 if ( ((bitmap & ( (1 << FILPBIT_FINFO)|(1 << FILPBIT_LNAME)|(1 <<FILPBIT_PDINFO) ) ) && !path->m_name)
272 || (bitmap & ( (1 << FILPBIT_LNAME) ) && utf8_encoding()) /* FIXME should be m_name utf8 filename */
273 || (bitmap & (1 << FILPBIT_FNUM))) {
275 id = get_id(vol, adp, st, dir->d_did, upath, strlen(upath));
278 if (id == CNID_INVALID)
281 path->m_name = utompath(vol, upath, id, utf8_encoding());
284 while ( bitmap != 0 ) {
285 while (( bitmap & 1 ) == 0 ) {
293 ad_getattr(adp, &ashort);
294 } else if (vol_inv_dots(vol) && *upath == '.') {
295 ashort = htons(ATTRBIT_INVISIBLE);
299 /* FIXME do we want a visual clue if the file is read only
302 accessmode( ".", &ma, dir , NULL);
303 if ((ma.ma_user & AR_UWRITE)) {
304 accessmode( upath, &ma, dir , st);
305 if (!(ma.ma_user & AR_UWRITE)) {
306 ashort |= htons(ATTRBIT_NOWRITE);
310 memcpy(data, &ashort, sizeof( ashort ));
311 data += sizeof( ashort );
315 memcpy(data, &dir->d_did, sizeof( u_int32_t ));
316 data += sizeof( u_int32_t );
320 if (!adp || (ad_getdate(adp, AD_DATE_CREATE, &aint) < 0))
321 aint = AD_DATE_FROM_UNIX(st->st_mtime);
322 memcpy(data, &aint, sizeof( aint ));
323 data += sizeof( aint );
327 if ( adp && (ad_getdate(adp, AD_DATE_MODIFY, &aint) == 0)) {
328 if ((st->st_mtime > AD_DATE_TO_UNIX(aint))) {
329 aint = AD_DATE_FROM_UNIX(st->st_mtime);
332 aint = AD_DATE_FROM_UNIX(st->st_mtime);
334 memcpy(data, &aint, sizeof( int ));
335 data += sizeof( int );
339 if (!adp || (ad_getdate(adp, AD_DATE_BACKUP, &aint) < 0))
340 aint = AD_DATE_START;
341 memcpy(data, &aint, sizeof( int ));
342 data += sizeof( int );
346 get_finderinfo(vol, upath, adp, (char *)data,S_ISLNK(st->st_mode));
347 data += ADEDLEN_FINDERI;
352 data += sizeof( u_int16_t );
356 memset(data, 0, sizeof(u_int16_t));
357 data += sizeof( u_int16_t );
361 memcpy(data, &id, sizeof( id ));
362 data += sizeof( id );
366 if (st->st_size > 0xffffffff)
369 aint = htonl( st->st_size );
370 memcpy(data, &aint, sizeof( aint ));
371 data += sizeof( aint );
376 if (adp->ad_rlen > 0xffffffff)
379 aint = htonl( adp->ad_rlen);
383 memcpy(data, &aint, sizeof( aint ));
384 data += sizeof( aint );
387 /* Current client needs ProDOS info block for this file.
388 Use simple heuristic and let the Mac "type" string tell
389 us what the PD file code should be. Everything gets a
390 subtype of 0x0000 unless the original value was hashed
391 to "pXYZ" when we created it. See IA, Ver 2.
392 <shirsch@adelphia.net> */
393 case FILPBIT_PDINFO :
394 if (afp_version >= 30) { /* UTF8 name */
395 utf8 = kTextEncodingUTF8;
397 data += sizeof( u_int16_t );
399 memcpy(data, &aint, sizeof( aint ));
400 data += sizeof( aint );
404 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
406 if ( memcmp( fdType, "TEXT", 4 ) == 0 ) {
410 else if ( memcmp( fdType, "PSYS", 4 ) == 0 ) {
414 else if ( memcmp( fdType, "PS16", 4 ) == 0 ) {
418 else if ( memcmp( fdType, "BINA", 4 ) == 0 ) {
422 else if ( fdType[0] == 'p' ) {
424 ashort = (fdType[2] * 256) + fdType[3];
438 memcpy(data, &ashort, sizeof( ashort ));
439 data += sizeof( ashort );
440 memset(data, 0, sizeof( ashort ));
441 data += sizeof( ashort );
444 case FILPBIT_EXTDFLEN:
445 aint = htonl(st->st_size >> 32);
446 memcpy(data, &aint, sizeof( aint ));
447 data += sizeof( aint );
448 aint = htonl(st->st_size);
449 memcpy(data, &aint, sizeof( aint ));
450 data += sizeof( aint );
452 case FILPBIT_EXTRFLEN:
455 aint = htonl(adp->ad_rlen >> 32);
456 memcpy(data, &aint, sizeof( aint ));
457 data += sizeof( aint );
459 aint = htonl(adp->ad_rlen);
460 memcpy(data, &aint, sizeof( aint ));
461 data += sizeof( aint );
463 case FILPBIT_UNIXPR :
464 /* accessmode may change st_mode with ACLs */
465 accessmode( upath, &ma, dir , st);
467 aint = htonl(st->st_uid);
468 memcpy( data, &aint, sizeof( aint ));
469 data += sizeof( aint );
470 aint = htonl(st->st_gid);
471 memcpy( data, &aint, sizeof( aint ));
472 data += sizeof( aint );
475 type == slnk indicates an OSX style symlink,
476 we have to add S_IFLNK to the mode, otherwise
477 10.3 clients freak out. */
481 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
482 if ( memcmp( fdType, "slnk", 4 ) == 0 ) {
488 memcpy( data, &aint, sizeof( aint ));
489 data += sizeof( aint );
491 *data++ = ma.ma_user;
492 *data++ = ma.ma_world;
493 *data++ = ma.ma_group;
494 *data++ = ma.ma_owner;
498 return( AFPERR_BITMAP );
504 ashort = htons( data - buf );
505 memcpy(l_nameoff, &ashort, sizeof( ashort ));
506 data = set_name(vol, data, dir->d_did, path->m_name, id, 0);
509 ashort = htons( data - buf );
510 memcpy(utf_nameoff, &ashort, sizeof( ashort ));
511 data = set_name(vol, data, dir->d_did, path->m_name, id, utf8);
513 *buflen = data - buf;
517 /* ----------------------- */
518 int getfilparams(struct vol *vol,
520 struct path *path, struct dir *dir,
521 char *buf, size_t *buflen )
523 struct adouble ad, *adp;
528 LOG(log_debug9, logtype_default, "begin getfilparams:");
531 opened = PARAM_NEED_ADP(bitmap);
536 int flags = (bitmap & (1 << FILPBIT_ATTR))?ADFLAGS_OPENFORKS:0;
538 adp = of_ad(vol, path, &ad);
539 upath = path->u_name;
541 if ( ad_metadata( upath, flags|ADFLAGS_CREATE, adp) < 0 ) {
544 LOG(log_error, logtype_afpd, "getfilparams(%s): %s: check resource fork permission?",
545 upath, strerror(errno));
546 return AFPERR_ACCESS;
548 LOG(log_error, logtype_afpd, "getfilparams(%s): bad resource fork", upath);
557 rc = getmetadata(vol, bitmap, path, dir, buf, buflen, adp);
559 ad_close_metadata( adp);
562 LOG(log_debug9, logtype_afpd, "end getfilparams:");
568 /* ----------------------------- */
569 int afp_createfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
571 struct adouble ad, *adp;
574 struct ofork *of = NULL;
576 int creatf, did, openf, retvalue = AFP_OK;
582 creatf = (unsigned char) *ibuf++;
584 memcpy(&vid, ibuf, sizeof( vid ));
585 ibuf += sizeof( vid );
587 if (NULL == ( vol = getvolbyvid( vid )) ) {
588 return( AFPERR_PARAM );
591 if (vol->v_flags & AFPVOL_RO)
594 memcpy(&did, ibuf, sizeof( did));
595 ibuf += sizeof( did );
597 if (NULL == ( dir = dirlookup( vol, did )) ) {
601 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
602 return get_afp_errno(AFPERR_PARAM);
605 if ( *s_path->m_name == '\0' ) {
606 return( AFPERR_BADTYPE );
609 upath = s_path->u_name;
611 /* if upath is deleted we already in trouble anyway */
612 if ((of = of_findname(s_path))) {
615 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
619 /* on a hard create, fail if file exists and is open */
622 openf = O_RDWR|O_CREAT|O_TRUNC;
624 /* on a soft create, if the file is open then ad_open won't fail
625 because open syscall is not called
630 openf = O_RDWR|O_CREAT|O_EXCL;
633 if ( ad_open( upath, ADFLAGS_DF|ADFLAGS_HF|ADFLAGS_NOHF|ADFLAGS_CREATE,
634 openf, 0666, adp) < 0 ) {
638 case ENOENT : /* we were already in 'did folder' so chdir() didn't fail */
639 return ( AFPERR_NOOBJ );
641 return( AFPERR_EXIST );
643 return( AFPERR_ACCESS );
646 return( AFPERR_DFULL );
648 return( AFPERR_PARAM );
651 if ( ad_reso_fileno( adp ) == -1 ) { /* Hard META / HF */
652 /* on noadouble volumes, just creating the data fork is ok */
653 if (vol_noadouble(vol)) {
654 ad_close( adp, ADFLAGS_DF );
655 goto createfile_done;
657 /* FIXME with hard create on an existing file, we already
658 * corrupted the data file.
660 netatalk_unlink( upath );
661 ad_close( adp, ADFLAGS_DF );
662 return AFPERR_ACCESS;
665 path = s_path->m_name;
666 ad_setname(adp, path);
668 ad_close( adp, ADFLAGS_DF|ADFLAGS_HF );
674 if (vol->v_flags & AFPVOL_DROPBOX) {
675 retvalue = matchfile2dirperms(upath, vol, did);
677 #endif /* DROPKLUDGE */
679 setvoltime(obj, vol );
684 int afp_setfilparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
690 u_int16_t vid, bitmap;
695 memcpy(&vid, ibuf, sizeof( vid ));
696 ibuf += sizeof( vid );
697 if (NULL == ( vol = getvolbyvid( vid )) ) {
698 return( AFPERR_PARAM );
701 if (vol->v_flags & AFPVOL_RO)
704 memcpy(&did, ibuf, sizeof( did ));
705 ibuf += sizeof( did );
706 if (NULL == ( dir = dirlookup( vol, did )) ) {
707 return afp_errno; /* was AFPERR_NOOBJ */
710 memcpy(&bitmap, ibuf, sizeof( bitmap ));
711 bitmap = ntohs( bitmap );
712 ibuf += sizeof( bitmap );
714 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
715 return get_afp_errno(AFPERR_PARAM);
718 if (path_isadir(s_path)) {
719 return( AFPERR_BADTYPE ); /* it's a directory */
722 if ( s_path->st_errno != 0 ) {
723 return( AFPERR_NOOBJ );
726 if ((u_long)ibuf & 1 ) {
730 if (AFP_OK == ( rc = setfilparams(vol, s_path, bitmap, ibuf )) ) {
731 setvoltime(obj, vol );
738 * cf AFP3.0.pdf page 252 for change_mdate and change_parent_mdate logic
741 extern struct path Cur_Path;
743 int setfilparams(struct vol *vol,
744 struct path *path, u_int16_t f_bitmap, char *buf )
746 struct adouble ad, *adp;
748 int bit, isad = 1, err = AFP_OK;
750 u_char achar, *fdType, xyy[4]; /* uninitialized, OK 310105 */
751 u_int16_t ashort, bshort, oshort;
754 u_int16_t upriv_bit = 0;
758 int change_mdate = 0;
759 int change_parent_mdate = 0;
764 u_int16_t bitmap = f_bitmap;
765 u_int32_t cdate,bdate;
766 u_char finder_buf[32];
769 LOG(log_debug9, logtype_afpd, "begin setfilparams:");
772 adp = of_ad(vol, path, &ad);
773 upath = path->u_name;
775 if (!vol_unix_priv(vol) && check_access(upath, OPENACC_WR ) < 0) {
776 return AFPERR_ACCESS;
779 /* with unix priv maybe we have to change adouble file priv first */
781 while ( bitmap != 0 ) {
782 while (( bitmap & 1 ) == 0 ) {
789 memcpy(&ashort, buf, sizeof( ashort ));
790 buf += sizeof( ashort );
794 memcpy(&cdate, buf, sizeof(cdate));
795 buf += sizeof( cdate );
798 memcpy(&newdate, buf, sizeof( newdate ));
799 buf += sizeof( newdate );
803 memcpy(&bdate, buf, sizeof( bdate));
804 buf += sizeof( bdate );
808 memcpy(finder_buf, buf, 32 );
809 if (memcmp(buf,"slnkrhap",8)==0 && !S_ISLNK(path->st.st_mode)){
814 char buf[PATH_MAX+1];
815 if ((fp=open(path->u_name,O_RDONLY))>=0){
816 if ((len=read(fp,buf,PATH_MAX+1))){
817 if (unlink(path->u_name)==0){
819 erc = symlink(buf, path->u_name);
828 goto setfilparam_done;
833 case FILPBIT_UNIXPR :
834 if (!vol_unix_priv(vol)) {
835 /* this volume doesn't use unix priv */
841 change_parent_mdate = 1;
843 memcpy( &aint, buf, sizeof( aint ));
844 f_uid = ntohl (aint);
845 buf += sizeof( aint );
846 memcpy( &aint, buf, sizeof( aint ));
847 f_gid = ntohl (aint);
848 buf += sizeof( aint );
849 setfilowner(vol, f_uid, f_gid, path);
851 memcpy( &upriv, buf, sizeof( upriv ));
852 buf += sizeof( upriv );
853 upriv = ntohl (upriv);
854 if ((upriv & S_IWUSR)) {
855 setfilunixmode(vol, path, upriv);
862 case FILPBIT_PDINFO :
863 if (afp_version < 30) { /* else it's UTF8 name */
866 /* Keep special case to support crlf translations */
867 if ((unsigned int) achar == 0x04) {
868 fdType = (u_char *)"TEXT";
871 xyy[0] = ( u_char ) 'p';
882 /* break while loop */
891 /* second try with adouble open
893 if ( ad_open_metadata( upath, 0, O_CREAT, adp) < 0) {
894 LOG(log_debug, logtype_afpd, "setfilparams: ad_open_metadata error");
896 * For some things, we don't need an adouble header:
897 * - change of modification date
898 * - UNIX privs (Bug-ID #2863424)
900 if (!vol_noadouble(vol) && (f_bitmap & ~(1<<FILPBIT_MDATE | 1<<FILPBIT_UNIXPR))) {
901 LOG(log_debug, logtype_afpd, "setfilparams: need adouble access");
902 return AFPERR_ACCESS;
904 LOG(log_debug, logtype_afpd, "setfilparams: no adouble perms, but only FILPBIT_MDATE and/or FILPBIT_UNIXPR");
906 } else if ((ad_get_HF_flags( adp ) & O_CREAT) ) {
907 ad_setname(adp, path->m_name);
912 while ( bitmap != 0 ) {
913 while (( bitmap & 1 ) == 0 ) {
920 ad_getattr(adp, &bshort);
922 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
923 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
927 if ((bshort & htons(ATTRBIT_INVISIBLE)) != (oshort & htons(ATTRBIT_INVISIBLE)))
928 change_parent_mdate = 1;
929 ad_setattr(adp, bshort);
932 ad_setdate(adp, AD_DATE_CREATE, cdate);
937 ad_setdate(adp, AD_DATE_BACKUP, bdate);
940 if (default_type( ad_entry( adp, ADEID_FINDERI ))
942 ((em = getextmap( path->m_name )) &&
943 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
944 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
945 || ((em = getdefextmap()) &&
946 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
947 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
949 memcpy(finder_buf, ufinderi, 8 );
951 memcpy(ad_entry( adp, ADEID_FINDERI ), finder_buf, 32 );
953 case FILPBIT_UNIXPR :
955 setfilunixmode(vol, path, upriv);
958 case FILPBIT_PDINFO :
959 if (afp_version < 30) { /* else it's UTF8 name */
960 memcpy(ad_entry( adp, ADEID_FINDERI ), fdType, 4 );
961 memcpy(ad_entry( adp, ADEID_FINDERI ) + 4, "pdos", 4 );
967 goto setfilparam_done;
974 if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
975 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
979 ad_setdate(adp, AD_DATE_MODIFY, newdate);
980 ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
986 ad_close_metadata( adp);
990 if (change_parent_mdate && gettimeofday(&tv, NULL) == 0) {
991 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
992 bitmap = 1<<FILPBIT_MDATE;
993 setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
997 LOG(log_debug9, logtype_afpd, "end setfilparams:");
1003 * renamefile and copyfile take the old and new unix pathnames
1004 * and the new mac name.
1006 * sdir_fd source dir fd to which src path is relative (for openat et al semantics)
1007 * passing -1 means this is not used, src path is a full path
1008 * src the source path
1009 * dst the dest filename in current dir
1010 * newname the dest mac name
1011 * adp adouble struct of src file, if open, or & zeroed one
1014 int renamefile(const struct vol *vol, int sdir_fd, char *src, char *dst, char *newname, struct adouble *adp)
1018 if ( unix_rename( sdir_fd, src, -1, dst ) < 0 ) {
1021 return( AFPERR_NOOBJ );
1024 return( AFPERR_ACCESS );
1026 return AFPERR_VLOCK;
1027 case EXDEV : /* Cross device move -- try copy */
1028 /* NOTE: with open file it's an error because after the copy we will
1029 * get two files, it's fixable for our process (eg reopen the new file, get the
1030 * locks, and so on. But it doesn't solve the case with a second process
1032 if (adp->ad_open_forks) {
1033 /* FIXME warning in syslog so admin'd know there's a conflict ?*/
1034 return AFPERR_OLOCK; /* little lie */
1036 if (AFP_OK != ( rc = copyfile(vol, vol, sdir_fd, src, dst, newname, NULL )) ) {
1037 /* on error copyfile delete dest */
1040 return deletefile(vol, sdir_fd, src, 0);
1042 return( AFPERR_PARAM );
1046 if (vol->vfs->vfs_renamefile(vol, sdir_fd, src, dst) < 0 ) {
1050 /* try to undo the data fork rename,
1051 * we know we are on the same device
1054 unix_rename(-1, dst, sdir_fd, src );
1055 /* return the first error */
1058 return AFPERR_NOOBJ;
1061 return AFPERR_ACCESS ;
1063 return AFPERR_VLOCK;
1065 return AFPERR_PARAM ;
1070 /* don't care if we can't open the newly renamed ressource fork
1072 if (!ad_open( dst, ADFLAGS_HF, O_RDWR, 0666, adp)) {
1073 ad_setname(adp, newname);
1075 ad_close( adp, ADFLAGS_HF );
1082 convert a Mac long name to an utf8 name,
1084 size_t mtoUTF8(const struct vol *vol, const char *src, size_t srclen, char *dest, size_t destlen)
1088 if ((size_t)-1 == (outlen = convert_string ( vol->v_maccharset, CH_UTF8_MAC, src, srclen, dest, destlen)) ) {
1094 /* ---------------- */
1095 int copy_path_name(const struct vol *vol, char *newname, char *ibuf)
1102 if ( type != 2 && !(afp_version >= 30 && type == 3) ) {
1108 if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
1109 if (afp_version >= 30) {
1110 /* convert it to UTF8
1112 if ((plen = mtoUTF8(vol, ibuf, plen, newname, AFPOBJ_TMPSIZ)) == (size_t)-1)
1116 strncpy( newname, ibuf, plen );
1117 newname[ plen ] = '\0';
1119 if (strlen(newname) != plen) {
1120 /* there's \0 in newname, e.g. it's a pathname not
1128 memcpy(&hint, ibuf, sizeof(hint));
1129 ibuf += sizeof(hint);
1131 memcpy(&len16, ibuf, sizeof(len16));
1132 ibuf += sizeof(len16);
1133 plen = ntohs(len16);
1136 if (plen > AFPOBJ_TMPSIZ) {
1139 strncpy( newname, ibuf, plen );
1140 newname[ plen ] = '\0';
1141 if (strlen(newname) != plen) {
1150 /* -----------------------------------
1152 int afp_copyfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1154 struct vol *s_vol, *d_vol;
1156 char *newname, *p, *upath;
1157 struct path *s_path;
1158 u_int32_t sdid, ddid;
1159 int err, retvalue = AFP_OK;
1160 u_int16_t svid, dvid;
1162 struct adouble ad, *adp;
1168 memcpy(&svid, ibuf, sizeof( svid ));
1169 ibuf += sizeof( svid );
1170 if (NULL == ( s_vol = getvolbyvid( svid )) ) {
1171 return( AFPERR_PARAM );
1174 memcpy(&sdid, ibuf, sizeof( sdid ));
1175 ibuf += sizeof( sdid );
1176 if (NULL == ( dir = dirlookup( s_vol, sdid )) ) {
1180 memcpy(&dvid, ibuf, sizeof( dvid ));
1181 ibuf += sizeof( dvid );
1182 memcpy(&ddid, ibuf, sizeof( ddid ));
1183 ibuf += sizeof( ddid );
1185 if (NULL == ( s_path = cname( s_vol, dir, &ibuf )) ) {
1186 return get_afp_errno(AFPERR_PARAM);
1188 if ( path_isadir(s_path) ) {
1189 return( AFPERR_BADTYPE );
1192 /* don't allow copies when the file is open.
1193 * XXX: the spec only calls for read/deny write access.
1194 * however, copyfile doesn't have any of that info,
1195 * and locks need to stay coherent. as a result,
1196 * we just balk if the file is opened already. */
1198 adp = of_ad(s_vol, s_path, &ad);
1200 if (ad_open(s_path->u_name , ADFLAGS_DF |ADFLAGS_HF | ADFLAGS_NOHF, O_RDONLY, 0, adp) < 0) {
1201 return AFPERR_DENYCONF;
1203 denyreadset = (getforkmode(adp, ADEID_DFORK, AD_FILELOCK_DENY_RD) != 0 ||
1204 getforkmode(adp, ADEID_RFORK, AD_FILELOCK_DENY_RD) != 0 );
1207 retvalue = AFPERR_DENYCONF;
1211 newname = obj->newtmp;
1212 strcpy( newname, s_path->m_name );
1214 p = ctoupath( s_vol, curdir, newname );
1216 retvalue = AFPERR_PARAM;
1221 /* FIXME svid != dvid && dvid's user can't read svid */
1223 if (NULL == ( d_vol = getvolbyvid( dvid )) ) {
1224 retvalue = AFPERR_PARAM;
1228 if (d_vol->v_flags & AFPVOL_RO) {
1229 retvalue = AFPERR_VLOCK;
1233 if (NULL == ( dir = dirlookup( d_vol, ddid )) ) {
1234 retvalue = afp_errno;
1238 if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) {
1239 retvalue = get_afp_errno(AFPERR_NOOBJ);
1243 if ( *s_path->m_name != '\0' ) {
1244 retvalue =path_error(s_path, AFPERR_NOOBJ);
1248 /* one of the handful of places that knows about the path type */
1249 if (copy_path_name(d_vol, newname, ibuf) < 0) {
1250 retvalue = AFPERR_PARAM;
1253 /* newname is always only a filename so curdir *is* its
1256 if (NULL == (upath = mtoupath(d_vol, newname, curdir->d_did, utf8_encoding()))) {
1257 retvalue =AFPERR_PARAM;
1261 if ( (err = copyfile(s_vol, d_vol, -1, p, upath , newname, adp)) < 0 ) {
1268 if (vol->v_flags & AFPVOL_DROPBOX) {
1269 retvalue=matchfile2dirperms(upath, vol, ddid); /* FIXME sdir or ddid */
1271 #endif /* DROPKLUDGE */
1273 setvoltime(obj, d_vol );
1276 ad_close( adp, ADFLAGS_DF |ADFLAGS_HF );
1280 /* ----------------------- */
1281 static int copy_all(const int dfd, const void *buf,
1287 LOG(log_debug9, logtype_afpd, "begin copy_all:");
1290 while (buflen > 0) {
1291 if ((cc = write(dfd, buf, buflen)) < 0) {
1303 LOG(log_debug9, logtype_afpd, "end copy_all:");
1309 /* --------------------------
1310 * copy only the fork data stream
1312 static int copy_fork(int eid, struct adouble *add, struct adouble *ads)
1319 if (eid == ADEID_DFORK) {
1320 sfd = ad_data_fileno(ads);
1321 dfd = ad_data_fileno(add);
1324 sfd = ad_reso_fileno(ads);
1325 dfd = ad_reso_fileno(add);
1328 if ((off_t)-1 == lseek(sfd, ad_getentryoff(ads, eid), SEEK_SET))
1331 if ((off_t)-1 == lseek(dfd, ad_getentryoff(add, eid), SEEK_SET))
1334 #if 0 /* ifdef SENDFILE_FLAVOR_LINUX */
1335 /* doesn't work With 2.6 FIXME, only check for EBADFD ? */
1339 #define BUF 128*1024*1024
1341 if (fstat(sfd, &st) == 0) {
1344 if ( offset >= st.st_size) {
1347 size = (st.st_size -offset > BUF)?BUF:st.st_size -offset;
1348 if ((cc = sys_sendfile(dfd, sfd, &offset, size)) < 0) {
1351 case EINVAL: /* there's no guarantee that all fs support sendfile */
1360 lseek(sfd, offset, SEEK_SET);
1364 if ((cc = read(sfd, filebuf, sizeof(filebuf))) < 0) {
1371 if (!cc || ((err = copy_all(dfd, filebuf, cc)) < 0)) {
1378 /* ----------------------------------
1379 * if newname is NULL (from directory.c) we don't want to copy the resource fork.
1380 * because we are doing it elsewhere.
1381 * currently if newname is NULL then adp is NULL.
1383 int copyfile(const struct vol *s_vol,
1384 const struct vol *d_vol,
1389 struct adouble *adp)
1391 struct adouble ads, add;
1398 LOG(log_debug, logtype_afpd, "copyfile(sfd:%d,s:'%s',d:'%s',n:'%s')",
1399 sfd, src, dst, newname);
1402 ad_init(&ads, s_vol->v_adouble, s_vol->v_ad_options);
1406 adflags = ADFLAGS_DF;
1408 adflags |= ADFLAGS_HF;
1411 if (ad_openat(sfd, src, adflags | ADFLAGS_NOHF, O_RDONLY, 0, adp) < 0) {
1416 if (ad_meta_fileno(adp) == -1 && ad_reso_fileno(adp) == -1) { /* META / HF */
1417 /* no resource fork, don't create one for dst file */
1418 adflags &= ~ADFLAGS_HF;
1421 stat_result = fstat(ad_data_fileno(adp), &st); /* saving stat exit code, thus saving us on one more stat later on */
1423 if (stat_result < 0) {
1424 /* unlikely but if fstat fails, the default file mode will be 0666. */
1425 st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
1428 ad_init(&add, d_vol->v_adouble, d_vol->v_ad_options);
1429 if (ad_open(dst , adflags, O_RDWR|O_CREAT|O_EXCL, st.st_mode, &add) < 0) {
1431 ad_close( adp, adflags );
1432 if (EEXIST != ret_err) {
1433 deletefile(d_vol, -1, dst, 0);
1436 return AFPERR_EXIST;
1440 * XXX if the source and the dest don't use the same resource type it's broken
1442 if (ad_reso_fileno(adp) == -1 || 0 == (err = copy_fork(ADEID_RFORK, &add, adp))){
1443 /* copy the data fork */
1444 if ((err = copy_fork(ADEID_DFORK, &add, adp)) == 0) {
1445 err = d_vol->vfs->vfs_copyfile(d_vol, sfd, src, dst);
1453 if (!ret_err && newname && (adflags & ADFLAGS_HF)) {
1454 /* set the new name in the resource fork */
1455 ad_copy_header(&add, adp);
1456 ad_setname(&add, newname);
1459 ad_close( adp, adflags );
1461 if (ad_close( &add, adflags ) <0) {
1466 deletefile(d_vol, -1, dst, 0);
1468 else if (stat_result == 0) {
1469 /* set dest modification date to src date */
1472 ut.actime = ut.modtime = st.st_mtime;
1474 /* FIXME netatalk doesn't use resource fork file date
1475 * but maybe we should set its modtime too.
1480 switch ( ret_err ) {
1486 return AFPERR_DFULL;
1488 return AFPERR_NOOBJ;
1490 return AFPERR_ACCESS;
1492 return AFPERR_VLOCK;
1494 return AFPERR_PARAM;
1498 /* -----------------------------------
1499 vol: not NULL delete cnid entry. then we are in curdir and file is a only filename
1500 checkAttrib: 1 check kFPDeleteInhibitBit (deletfile called by afp_delete)
1502 when deletefile is called we don't have lock on it, file is closed (for us)
1503 untrue if called by renamefile
1505 ad_open always try to open file RDWR first and ad_lock takes care of
1506 WRITE lock on read only file.
1509 static int check_attrib(struct adouble *adp)
1511 u_int16_t bshort = 0;
1513 ad_getattr(adp, &bshort);
1515 * Does kFPDeleteInhibitBit (bit 8) set?
1517 if ((bshort & htons(ATTRBIT_NODELETE))) {
1518 return AFPERR_OLOCK;
1520 if ((bshort & htons(ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
1526 * dirfd can be used for unlinkat semantics
1528 int deletefile(const struct vol *vol, int dirfd, char *file, int checkAttrib)
1531 struct adouble *adp = NULL;
1532 int adflags, err = AFP_OK;
1535 LOG(log_debug, logtype_afpd, "deletefile('%s')", file);
1537 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
1539 /* was EACCESS error try to get only metadata */
1540 /* we never want to create a resource fork here, we are going to delete it
1541 * moreover sometimes deletefile is called with a no existent file and
1542 * ad_open would create a 0 byte resource fork
1544 if ( ad_metadataat(dirfd, file, ADFLAGS_OPENFORKS, &ad) == 0 ) {
1545 if ((err = check_attrib(&ad))) {
1546 ad_close_metadata(&ad);
1553 /* try to open both forks at once */
1554 adflags = ADFLAGS_DF;
1555 if ( ad_openat(dirfd, file, adflags |ADFLAGS_HF|ADFLAGS_NOHF, O_RDONLY, 0, &ad ) < 0 ) {
1560 case EACCES: /* maybe it's a file with no write mode for us */
1561 break; /* was return AFPERR_ACCESS;*/
1574 if ( adp && ad_reso_fileno( adp ) != -1 ) { /* there's a resource fork */
1575 adflags |= ADFLAGS_HF;
1576 /* FIXME we have a pb here because we want to know if a file is open
1577 * there's a 'priority inversion' if you can't open the ressource fork RW
1578 * you can delete it if it's open because you can't get a write lock.
1580 * ADLOCK_FILELOCK means the whole ressource fork, not only after the
1583 * FIXME it doesn't work for RFORK open read only and fork open without deny mode
1585 if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
1591 if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
1593 } else if (!(err = vol->vfs->vfs_deletefile(vol, dirfd, file)) && !(err = netatalk_unlinkat(dirfd, file )) ) {
1595 if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file)))) {
1596 cnid_delete(vol->v_cdb, id);
1602 ad_close_metadata(&ad);
1605 ad_close( &ad, adflags ); /* ad_close removes locks if any */
1610 /* ------------------------------------ */
1611 /* return a file id */
1612 int afp_createid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1621 struct path *s_path;
1627 memcpy(&vid, ibuf, sizeof(vid));
1628 ibuf += sizeof(vid);
1630 if (NULL == ( vol = getvolbyvid( vid )) ) {
1631 return( AFPERR_PARAM);
1634 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1638 if (vol->v_flags & AFPVOL_RO)
1639 return AFPERR_VLOCK;
1641 memcpy(&did, ibuf, sizeof( did ));
1642 ibuf += sizeof(did);
1644 if (NULL == ( dir = dirlookup( vol, did )) ) {
1645 return afp_errno; /* was AFPERR_PARAM */
1648 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
1649 return get_afp_errno(AFPERR_NOOBJ); /* was AFPERR_PARAM */
1652 if ( path_isadir(s_path) ) {
1653 return( AFPERR_BADTYPE );
1656 upath = s_path->u_name;
1657 switch (s_path->st_errno) {
1659 break; /* success */
1662 return AFPERR_ACCESS;
1664 return AFPERR_NOOBJ;
1666 return AFPERR_PARAM;
1669 if ((id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath)))) {
1670 memcpy(rbuf, &id, sizeof(id));
1671 *rbuflen = sizeof(id);
1672 return AFPERR_EXISTID;
1675 if ((id = get_id(vol, NULL, st, did, upath, len)) != CNID_INVALID) {
1676 memcpy(rbuf, &id, sizeof(id));
1677 *rbuflen = sizeof(id);
1684 /* ------------------------------- */
1690 static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
1693 struct reenum *param = data;
1694 struct vol *vol = param->vol;
1695 cnid_t did = param->did;
1698 if ( lstat(de->d_name, &path.st)<0 )
1701 /* update or add to cnid */
1702 aint = cnid_add(vol->v_cdb, &path.st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */
1704 #if AD_VERSION > AD_VERSION1
1705 if (aint != CNID_INVALID && !S_ISDIR(path.st.st_mode)) {
1706 struct adouble ad, *adp;
1710 path.u_name = de->d_name;
1712 adp = of_ad(vol, &path, &ad);
1714 if ( ad_open_metadata( de->d_name, 0, 0, adp ) < 0 ) {
1717 if (ad_setid(adp, path.st.st_dev, path.st.st_ino, aint, did, vol->v_stamp)) {
1720 ad_close_metadata(adp);
1722 #endif /* AD_VERSION > AD_VERSION1 */
1727 /* --------------------
1728 * Ok the db is out of synch with the dir.
1729 * but if it's a deleted file we don't want to do it again and again.
1732 reenumerate_id(struct vol *vol, char *name, struct dir *dir)
1738 if (vol->v_cdb == NULL) {
1742 /* FIXME use of_statdir ? */
1743 if (lstat(name, &st)) {
1747 if (dirreenumerate(dir, &st)) {
1748 /* we already did it once and the dir haven't been modified */
1753 data.did = dir->d_did;
1754 if ((ret = for_each_dirent(vol, name, reenumerate_loop, (void *)&data)) >= 0) {
1755 setdiroffcnt(curdir, &st, ret);
1756 dir->d_flags |= DIRF_CNID;
1762 /* ------------------------------
1763 resolve a file id */
1764 int afp_resolveid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1773 u_int16_t vid, bitmap;
1775 static char buffer[12 + MAXPATHLEN + 1];
1776 int len = 12 + MAXPATHLEN + 1;
1781 memcpy(&vid, ibuf, sizeof(vid));
1782 ibuf += sizeof(vid);
1784 if (NULL == ( vol = getvolbyvid( vid )) ) {
1785 return( AFPERR_PARAM);
1788 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1792 memcpy(&id, ibuf, sizeof( id ));
1797 /* some MacOS versions after a catsearch do a *lot* of afp_resolveid with 0 */
1801 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1802 return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
1805 if (NULL == ( dir = dirlookup( vol, id )) ) {
1806 return AFPERR_NOID; /* idem AFPERR_PARAM */
1808 if (movecwd(vol, dir) < 0) {
1812 return AFPERR_ACCESS;
1816 return AFPERR_PARAM;
1820 memset(&path, 0, sizeof(path));
1821 path.u_name = upath;
1822 if ( of_stat(&path) < 0 ) {
1824 /* with nfs and our working directory is deleted */
1825 if (errno == ESTALE) {
1829 if ( errno == ENOENT && !retry) {
1830 /* cnid db is out of sync, reenumerate the directory and update ids */
1831 reenumerate_id(vol, ".", dir);
1839 return AFPERR_ACCESS;
1843 return AFPERR_PARAM;
1847 /* directories are bad */
1848 if (S_ISDIR(path.st.st_mode)) {
1849 /* OS9 and OSX don't return the same error code */
1850 return (afp_version >=30)?AFPERR_NOID:AFPERR_BADTYPE;
1853 memcpy(&bitmap, ibuf, sizeof(bitmap));
1854 bitmap = ntohs( bitmap );
1855 if (NULL == (path.m_name = utompath(vol, upath, cnid, utf8_encoding()))) {
1859 if (AFP_OK != (err = getfilparams(vol, bitmap, &path , curdir,
1860 rbuf + sizeof(bitmap), &buflen))) {
1863 *rbuflen = buflen + sizeof(bitmap);
1864 memcpy(rbuf, ibuf, sizeof(bitmap));
1869 /* ------------------------------ */
1870 int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1880 static char buffer[12 + MAXPATHLEN + 1];
1881 int len = 12 + MAXPATHLEN + 1;
1886 memcpy(&vid, ibuf, sizeof(vid));
1887 ibuf += sizeof(vid);
1889 if (NULL == ( vol = getvolbyvid( vid )) ) {
1890 return( AFPERR_PARAM);
1893 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1897 if (vol->v_flags & AFPVOL_RO)
1898 return AFPERR_VLOCK;
1900 memcpy(&id, ibuf, sizeof( id ));
1904 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1908 if (NULL == ( dir = dirlookup( vol, id )) ) {
1909 return( AFPERR_PARAM );
1913 if ((movecwd(vol, dir) < 0) || (lstat(upath, &st) < 0)) {
1917 return AFPERR_ACCESS;
1922 /* still try to delete the id */
1926 return AFPERR_PARAM;
1929 else if (S_ISDIR(st.st_mode)) /* directories are bad */
1930 return AFPERR_BADTYPE;
1932 if (cnid_delete(vol->v_cdb, fileid)) {
1935 return AFPERR_VLOCK;
1938 return AFPERR_ACCESS;
1940 return AFPERR_PARAM;
1947 /* ------------------------------ */
1948 static struct adouble *find_adouble(struct path *path, struct ofork **of, struct adouble *adp)
1952 if (path->st_errno) {
1953 switch (path->st_errno) {
1955 afp_errno = AFPERR_NOID;
1959 afp_errno = AFPERR_ACCESS;
1962 afp_errno = AFPERR_PARAM;
1967 /* we use file_access both for legacy Mac perm and
1968 * for unix privilege, rename will take care of folder perms
1970 if (file_access(path, OPENACC_WR ) < 0) {
1971 afp_errno = AFPERR_ACCESS;
1975 if ((*of = of_findname(path))) {
1976 /* reuse struct adouble so it won't break locks */
1980 ret = ad_open( path->u_name, ADFLAGS_HF, O_RDONLY, 0, adp);
1982 if ( !ret && ad_reso_fileno(adp) != -1 && !(adp->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
1984 * The user must have the Read & Write privilege for both files in order to use this command.
1986 ad_close(adp, ADFLAGS_HF);
1987 afp_errno = AFPERR_ACCESS;
1994 #define APPLETEMP ".AppleTempXXXXXX"
1996 int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1998 struct stat srcst, destst;
2000 struct dir *dir, *sdir;
2001 char *spath, temp[17], *p;
2002 char *supath, *upath;
2007 struct adouble *adsp = NULL;
2008 struct adouble *addp = NULL;
2009 struct ofork *s_of = NULL;
2010 struct ofork *d_of = NULL;
2023 memcpy(&vid, ibuf, sizeof(vid));
2024 ibuf += sizeof(vid);
2026 if (NULL == ( vol = getvolbyvid( vid )) ) {
2027 return( AFPERR_PARAM);
2030 if ((vol->v_flags & AFPVOL_RO))
2031 return AFPERR_VLOCK;
2033 /* source and destination dids */
2034 memcpy(&sid, ibuf, sizeof(sid));
2035 ibuf += sizeof(sid);
2036 memcpy(&did, ibuf, sizeof(did));
2037 ibuf += sizeof(did);
2040 if (NULL == (dir = dirlookup( vol, sid )) ) {
2041 return afp_errno; /* was AFPERR_PARAM */
2044 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2045 return get_afp_errno(AFPERR_NOOBJ);
2048 if ( path_isadir(path) ) {
2049 return AFPERR_BADTYPE; /* it's a dir */
2052 /* save some stuff */
2055 spath = obj->oldtmp;
2056 supath = obj->newtmp;
2057 strcpy(spath, path->m_name);
2058 strcpy(supath, path->u_name); /* this is for the cnid changing */
2059 p = absupath( vol, sdir, supath);
2061 /* pathname too long */
2062 return AFPERR_PARAM ;
2065 ad_init(&ads, vol->v_adouble, vol->v_ad_options);
2066 if (!(adsp = find_adouble( path, &s_of, &ads))) {
2070 /* ***** from here we may have resource fork open **** */
2072 /* look for the source cnid. if it doesn't exist, don't worry about
2074 sid = cnid_lookup(vol->v_cdb, &srcst, sdir->d_did, supath,slen = strlen(supath));
2076 if (NULL == ( dir = dirlookup( vol, did )) ) {
2077 err = afp_errno; /* was AFPERR_PARAM */
2078 goto err_exchangefile;
2081 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2082 err = get_afp_errno(AFPERR_NOOBJ);
2083 goto err_exchangefile;
2086 if ( path_isadir(path) ) {
2087 err = AFPERR_BADTYPE;
2088 goto err_exchangefile;
2091 /* FPExchangeFiles is the only call that can return the SameObj
2093 if ((curdir == sdir) && strcmp(spath, path->m_name) == 0) {
2094 err = AFPERR_SAMEOBJ;
2095 goto err_exchangefile;
2098 ad_init(&add, vol->v_adouble, vol->v_ad_options);
2099 if (!(addp = find_adouble( path, &d_of, &add))) {
2101 goto err_exchangefile;
2105 /* they are not on the same device and at least one is open
2106 * FIXME broken for for crossdev and adouble v2
2109 crossdev = (srcst.st_dev != destst.st_dev);
2110 if (/* (d_of || s_of) && */ crossdev) {
2112 goto err_exchangefile;
2115 /* look for destination id. */
2116 upath = path->u_name;
2117 did = cnid_lookup(vol->v_cdb, &destst, curdir->d_did, upath, dlen = strlen(upath));
2119 /* construct a temp name.
2120 * NOTE: the temp file will be in the dest file's directory. it
2121 * will also be inaccessible from AFP. */
2122 memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
2123 if (!mktemp(temp)) {
2125 goto err_exchangefile;
2129 /* FIXME we need to close fork for copy, both s_of and d_of are null */
2130 ad_close(adsp, ADFLAGS_HF);
2131 ad_close(addp, ADFLAGS_HF);
2134 /* now, quickly rename the file. we error if we can't. */
2135 if ((err = renamefile(vol, -1, p, temp, temp, adsp)) != AFP_OK)
2136 goto err_exchangefile;
2137 of_rename(vol, s_of, sdir, spath, curdir, temp);
2139 /* rename destination to source */
2140 if ((err = renamefile(vol, -1, upath, p, spath, addp)) != AFP_OK)
2141 goto err_src_to_tmp;
2142 of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
2144 /* rename temp to destination */
2145 if ((err = renamefile(vol, -1, temp, upath, path->m_name, adsp)) != AFP_OK)
2146 goto err_dest_to_src;
2147 of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
2149 /* id's need switching. src -> dest and dest -> src.
2150 * we need to re-stat() if it was a cross device copy.
2153 cnid_delete(vol->v_cdb, sid);
2156 cnid_delete(vol->v_cdb, did);
2158 if ((did && ( (crossdev && lstat( upath, &srcst) < 0) ||
2159 cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
2161 (sid && ( (crossdev && lstat(p, &destst) < 0) ||
2162 cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
2167 err = AFPERR_ACCESS;
2172 goto err_temp_to_dest;
2175 /* here we need to reopen if crossdev */
2176 if (sid && ad_setid(addp, destst.st_dev, destst.st_ino, sid, sdir->d_did, vol->v_stamp))
2181 if (did && ad_setid(adsp, srcst.st_dev, srcst.st_ino, did, curdir->d_did, vol->v_stamp))
2186 /* change perms, src gets dest perm and vice versa */
2191 LOG(log_error, logtype_afpd, "seteuid failed %s", strerror(errno));
2192 err = AFP_OK; /* ignore error */
2193 goto err_temp_to_dest;
2197 * we need to exchange ACL entries as well
2199 /* exchange_acls(vol, p, upath); */
2204 path->m_name = NULL;
2205 path->u_name = upath;
2207 setfilunixmode(vol, path, destst.st_mode);
2208 setfilowner(vol, destst.st_uid, destst.st_gid, path);
2215 setfilunixmode(vol, path, srcst.st_mode);
2216 setfilowner(vol, srcst.st_uid, srcst.st_gid, path);
2218 if ( setegid(gid) < 0 || seteuid(uid) < 0) {
2219 LOG(log_error, logtype_afpd, "can't seteuid back %s", strerror(errno));
2224 goto err_exchangefile;
2226 /* all this stuff is so that we can unwind a failed operation
2229 /* rename dest to temp */
2230 renamefile(vol, -1, upath, temp, temp, adsp);
2231 of_rename(vol, s_of, curdir, upath, curdir, temp);
2234 /* rename source back to dest */
2235 renamefile(vol, -1, p, upath, path->m_name, addp);
2236 of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
2239 /* rename temp back to source */
2240 renamefile(vol, -1, temp, p, spath, adsp);
2241 of_rename(vol, s_of, curdir, temp, sdir, spath);
2244 if ( !s_of && adsp && ad_meta_fileno(adsp) != -1 ) { /* META */
2245 ad_close(adsp, ADFLAGS_HF);
2247 if ( !d_of && addp && ad_meta_fileno(addp) != -1 ) {/* META */
2248 ad_close(addp, ADFLAGS_HF);