2 * $Id: file.c,v 1.103 2006-09-29 09:39:16 didg 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 */
31 #include <atalk/adouble.h>
36 #include <atalk/logger.h>
37 #include <sys/param.h>
40 #include <atalk/afp.h>
41 #include <atalk/util.h>
42 #include <atalk/cnid.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)
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(data + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
117 /** Only enter if no appledouble information and no finder information found. */
118 if (chk_ext && (em = getextmap( upath ))) {
119 memcpy(data, em->em_type, sizeof( em->em_type ));
120 memcpy((char *)data + 4, em->em_creator, sizeof(em->em_creator));
125 /* ---------------------
127 char *set_name(const struct vol *vol, char *data, cnid_t pid, char *name, cnid_t id, u_int32_t utf8)
132 aint = strlen( name );
136 if (utf8_encoding()) {
137 /* but name is an utf8 mac name */
140 /* global static variable... */
142 if (!(u = mtoupath(vol, name, pid, 1)) || !(m = utompath(vol, u, id, 0))) {
151 if (aint > MACFILELEN)
158 if (aint > 255) /* FIXME safeguard, anyway if no ascii char it's game over*/
161 utf8 = vol->v_mac?htonl(vol->v_mac->kTextEncoding):0; /* htonl(utf8) */
162 memcpy(data, &utf8, sizeof(utf8));
163 data += sizeof(utf8);
166 memcpy(data, &temp, sizeof(temp));
167 data += sizeof(temp);
170 memcpy( data, src, aint );
180 * FIXME: PDINFO is UTF8 and doesn't need adp
182 #define PARAM_NEED_ADP(b) ((b) & ((1 << FILPBIT_ATTR) |\
183 (1 << FILPBIT_CDATE) |\
184 (1 << FILPBIT_MDATE) |\
185 (1 << FILPBIT_BDATE) |\
186 (1 << FILPBIT_FINFO) |\
187 (1 << FILPBIT_RFLEN) |\
188 (1 << FILPBIT_EXTRFLEN) |\
189 (1 << FILPBIT_PDINFO) |\
190 (1 << FILPBIT_UNIXPR)))
192 /* -------------------------- */
193 u_int32_t get_id(struct vol *vol, struct adouble *adp, const struct stat *st,
194 const cnid_t did, char *upath, const int len)
198 #if AD_VERSION > AD_VERSION1
200 if ((aint = ad_getid(adp, st->st_dev, st->st_ino, did, vol->v_stamp))) {
205 if (vol->v_cdb != NULL) {
206 aint = cnid_add(vol->v_cdb, st, did, upath, len, aint);
207 /* Throw errors if cnid_add fails. */
208 if (aint == CNID_INVALID) {
210 case CNID_ERR_CLOSE: /* the db is closed */
213 LOG(log_error, logtype_afpd, "get_id: Incorrect parameters passed to cnid_add");
214 afp_errno = AFPERR_PARAM;
217 afp_errno = AFPERR_PARAM;
220 afp_errno = AFPERR_MISC;
224 #if AD_VERSION > AD_VERSION1
226 /* update the ressource fork
227 * for a folder adp is always null
229 if (ad_setid(adp, st->st_dev, st->st_ino, aint, did, vol->v_stamp)) {
238 /* -------------------------- */
239 int getmetadata(struct vol *vol,
241 struct path *path, struct dir *dir,
242 char *buf, int *buflen, struct adouble *adp)
244 char *data, *l_nameoff = NULL, *upath;
245 char *utf_nameoff = NULL;
250 u_char achar, fdType[4];
256 LOG(log_info, logtype_afpd, "begin getmetadata:");
259 upath = path->u_name;
264 if ( ((bitmap & ( (1 << FILPBIT_FINFO)|(1 << FILPBIT_LNAME)|(1 <<FILPBIT_PDINFO) ) ) && !path->m_name)
265 || (bitmap & ( (1 << FILPBIT_LNAME) ) && utf8_encoding()) /* FIXME should be m_name utf8 filename */
266 || (bitmap & (1 << FILPBIT_FNUM))) {
268 id = get_id(vol, adp, st, dir->d_did, upath, strlen(upath));
274 path->m_name = utompath(vol, upath, id, utf8_encoding());
277 while ( bitmap != 0 ) {
278 while (( bitmap & 1 ) == 0 ) {
286 ad_getattr(adp, &ashort);
287 } else if (vol_inv_dots(vol) && *upath == '.') {
288 ashort = htons(ATTRBIT_INVISIBLE);
292 /* FIXME do we want a visual clue if the file is read only
295 accessmode( ".", &ma, dir , NULL);
296 if ((ma.ma_user & AR_UWRITE)) {
297 accessmode( upath, &ma, dir , st);
298 if (!(ma.ma_user & AR_UWRITE)) {
299 ashort |= htons(ATTRBIT_NOWRITE);
303 memcpy(data, &ashort, sizeof( ashort ));
304 data += sizeof( ashort );
308 memcpy(data, &dir->d_did, sizeof( u_int32_t ));
309 data += sizeof( u_int32_t );
313 if (!adp || (ad_getdate(adp, AD_DATE_CREATE, &aint) < 0))
314 aint = AD_DATE_FROM_UNIX(st->st_mtime);
315 memcpy(data, &aint, sizeof( aint ));
316 data += sizeof( aint );
320 if ( adp && (ad_getdate(adp, AD_DATE_MODIFY, &aint) == 0)) {
321 if ((st->st_mtime > AD_DATE_TO_UNIX(aint))) {
322 aint = AD_DATE_FROM_UNIX(st->st_mtime);
325 aint = AD_DATE_FROM_UNIX(st->st_mtime);
327 memcpy(data, &aint, sizeof( int ));
328 data += sizeof( int );
332 if (!adp || (ad_getdate(adp, AD_DATE_BACKUP, &aint) < 0))
333 aint = AD_DATE_START;
334 memcpy(data, &aint, sizeof( int ));
335 data += sizeof( int );
339 get_finderinfo(vol, upath, adp, (char *)data);
340 data += ADEDLEN_FINDERI;
345 data += sizeof( u_int16_t );
349 memset(data, 0, sizeof(u_int16_t));
350 data += sizeof( u_int16_t );
354 memcpy(data, &id, sizeof( id ));
355 data += sizeof( id );
359 if (st->st_size > 0xffffffff)
362 aint = htonl( st->st_size );
363 memcpy(data, &aint, sizeof( aint ));
364 data += sizeof( aint );
369 if (adp->ad_rlen > 0xffffffff)
372 aint = htonl( adp->ad_rlen);
376 memcpy(data, &aint, sizeof( aint ));
377 data += sizeof( aint );
380 /* Current client needs ProDOS info block for this file.
381 Use simple heuristic and let the Mac "type" string tell
382 us what the PD file code should be. Everything gets a
383 subtype of 0x0000 unless the original value was hashed
384 to "pXYZ" when we created it. See IA, Ver 2.
385 <shirsch@adelphia.net> */
386 case FILPBIT_PDINFO :
387 if (afp_version >= 30) { /* UTF8 name */
388 utf8 = kTextEncodingUTF8;
390 data += sizeof( u_int16_t );
392 memcpy(data, &aint, sizeof( aint ));
393 data += sizeof( aint );
397 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
399 if ( memcmp( fdType, "TEXT", 4 ) == 0 ) {
403 else if ( memcmp( fdType, "PSYS", 4 ) == 0 ) {
407 else if ( memcmp( fdType, "PS16", 4 ) == 0 ) {
411 else if ( memcmp( fdType, "BINA", 4 ) == 0 ) {
415 else if ( fdType[0] == 'p' ) {
417 ashort = (fdType[2] * 256) + fdType[3];
431 memcpy(data, &ashort, sizeof( ashort ));
432 data += sizeof( ashort );
433 memset(data, 0, sizeof( ashort ));
434 data += sizeof( ashort );
437 case FILPBIT_EXTDFLEN:
438 aint = htonl(st->st_size >> 32);
439 memcpy(data, &aint, sizeof( aint ));
440 data += sizeof( aint );
441 aint = htonl(st->st_size);
442 memcpy(data, &aint, sizeof( aint ));
443 data += sizeof( aint );
445 case FILPBIT_EXTRFLEN:
448 aint = htonl(adp->ad_rlen >> 32);
449 memcpy(data, &aint, sizeof( aint ));
450 data += sizeof( aint );
452 aint = htonl(adp->ad_rlen);
453 memcpy(data, &aint, sizeof( aint ));
454 data += sizeof( aint );
456 case FILPBIT_UNIXPR :
457 /* accessmode may change st_mode with ACLs */
458 accessmode( upath, &ma, dir , st);
460 aint = htonl(st->st_uid);
461 memcpy( data, &aint, sizeof( aint ));
462 data += sizeof( aint );
463 aint = htonl(st->st_gid);
464 memcpy( data, &aint, sizeof( aint ));
465 data += sizeof( aint );
468 type == slnk indicates an OSX style symlink,
469 we have to add S_IFLNK to the mode, otherwise
470 10.3 clients freak out. */
474 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
475 if ( memcmp( fdType, "slnk", 4 ) == 0 ) {
481 memcpy( data, &aint, sizeof( aint ));
482 data += sizeof( aint );
484 *data++ = ma.ma_user;
485 *data++ = ma.ma_world;
486 *data++ = ma.ma_group;
487 *data++ = ma.ma_owner;
491 return( AFPERR_BITMAP );
497 ashort = htons( data - buf );
498 memcpy(l_nameoff, &ashort, sizeof( ashort ));
499 data = set_name(vol, data, dir->d_did, path->m_name, id, 0);
502 ashort = htons( data - buf );
503 memcpy(utf_nameoff, &ashort, sizeof( ashort ));
504 data = set_name(vol, data, dir->d_did, path->m_name, id, utf8);
506 *buflen = data - buf;
510 /* ----------------------- */
511 int getfilparams(struct vol *vol,
513 struct path *path, struct dir *dir,
514 char *buf, int *buflen )
516 struct adouble ad, *adp;
523 LOG(log_info, logtype_default, "begin getfilparams:");
526 opened = PARAM_NEED_ADP(bitmap);
530 int flags = (bitmap & (1 << FILPBIT_ATTR))?ADFLAGS_OPENFORKS:0;
531 upath = path->u_name;
532 if ((of = of_findname(path))) {
535 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
539 if ( ad_metadata( upath, flags, adp) < 0 ) {
542 LOG(log_error, logtype_afpd, "getfilparams(%s): %s: check resource fork permission?",
543 upath, strerror(errno));
544 return AFPERR_ACCESS;
546 LOG(log_error, logtype_afpd, "getfilparams(%s): bad resource fork", upath);
555 rc = getmetadata(vol, bitmap, path, dir, buf, buflen, adp);
557 ad_close_metadata( adp);
560 LOG(log_info, logtype_afpd, "end getfilparams:");
566 /* ----------------------------- */
567 int afp_createfile(obj, ibuf, ibuflen, rbuf, rbuflen )
569 char *ibuf, *rbuf _U_;
570 int ibuflen _U_, *rbuflen;
572 struct adouble ad, *adp;
575 struct ofork *of = NULL;
577 int creatf, did, openf, retvalue = AFP_OK;
583 LOG(log_info, logtype_afpd, "begin afp_createfile:");
588 creatf = (unsigned char) *ibuf++;
590 memcpy(&vid, ibuf, sizeof( vid ));
591 ibuf += sizeof( vid );
593 if (NULL == ( vol = getvolbyvid( vid )) ) {
594 return( AFPERR_PARAM );
597 if (vol->v_flags & AFPVOL_RO)
600 memcpy(&did, ibuf, sizeof( did));
601 ibuf += sizeof( did );
603 if (NULL == ( dir = dirlookup( vol, did )) ) {
607 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
608 return get_afp_errno(AFPERR_PARAM);
611 if ( *s_path->m_name == '\0' ) {
612 return( AFPERR_BADTYPE );
615 upath = s_path->u_name;
616 if (0 != (ret = check_name(vol, upath)))
619 /* if upath is deleted we already in trouble anyway */
620 if ((of = of_findname(s_path))) {
623 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
627 /* on a hard create, fail if file exists and is open */
630 openf = O_RDWR|O_CREAT|O_TRUNC;
632 /* on a soft create, if the file is open then ad_open won't fail
633 because open syscall is not called
638 openf = O_RDWR|O_CREAT|O_EXCL;
641 if ( ad_open( upath, vol_noadouble(vol)|ADFLAGS_DF|ADFLAGS_HF|ADFLAGS_NOHF,
642 openf, 0666, adp) < 0 ) {
646 case ENOENT : /* we were already in 'did folder' so chdir() didn't fail */
647 return ( AFPERR_NOOBJ );
649 return( AFPERR_EXIST );
651 return( AFPERR_ACCESS );
654 return( AFPERR_DFULL );
656 return( AFPERR_PARAM );
659 if ( ad_reso_fileno( adp ) == -1 ) { /* Hard META / HF */
660 /* on noadouble volumes, just creating the data fork is ok */
661 if (vol_noadouble(vol)) {
662 ad_close( adp, ADFLAGS_DF );
663 goto createfile_done;
665 /* FIXME with hard create on an existing file, we already
666 * corrupted the data file.
668 netatalk_unlink( upath );
669 ad_close( adp, ADFLAGS_DF );
670 return AFPERR_ACCESS;
673 path = s_path->m_name;
674 ad_setname(adp, path);
676 ad_close( adp, ADFLAGS_DF|ADFLAGS_HF );
682 if (vol->v_flags & AFPVOL_DROPBOX) {
683 retvalue = matchfile2dirperms(upath, vol, did);
685 #endif /* DROPKLUDGE */
687 setvoltime(obj, vol );
690 LOG(log_info, logtype_afpd, "end afp_createfile");
696 int afp_setfilparams(obj, ibuf, ibuflen, rbuf, rbuflen )
698 char *ibuf, *rbuf _U_;
699 int ibuflen _U_, *rbuflen;
705 u_int16_t vid, bitmap;
708 LOG(log_info, logtype_afpd, "begin afp_setfilparams:");
714 memcpy(&vid, ibuf, sizeof( vid ));
715 ibuf += sizeof( vid );
716 if (NULL == ( vol = getvolbyvid( vid )) ) {
717 return( AFPERR_PARAM );
720 if (vol->v_flags & AFPVOL_RO)
723 memcpy(&did, ibuf, sizeof( did ));
724 ibuf += sizeof( did );
725 if (NULL == ( dir = dirlookup( vol, did )) ) {
726 return afp_errno; /* was AFPERR_NOOBJ */
729 memcpy(&bitmap, ibuf, sizeof( bitmap ));
730 bitmap = ntohs( bitmap );
731 ibuf += sizeof( bitmap );
733 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
734 return get_afp_errno(AFPERR_PARAM);
737 if (path_isadir(s_path)) {
738 return( AFPERR_BADTYPE ); /* it's a directory */
741 if ( s_path->st_errno != 0 ) {
742 return( AFPERR_NOOBJ );
745 if ((u_long)ibuf & 1 ) {
749 if (AFP_OK == ( rc = setfilparams(vol, s_path, bitmap, ibuf )) ) {
750 setvoltime(obj, vol );
754 LOG(log_info, logtype_afpd, "end afp_setfilparams:");
761 * cf AFP3.0.pdf page 252 for change_mdate and change_parent_mdate logic
764 extern struct path Cur_Path;
766 int setfilparams(struct vol *vol,
767 struct path *path, u_int16_t f_bitmap, char *buf )
769 struct adouble ad, *adp;
771 int bit, isad = 1, err = AFP_OK;
773 u_char achar, *fdType, xyy[4]; /* uninitialized, OK 310105 */
774 u_int16_t ashort, bshort;
777 u_int16_t upriv_bit = 0;
781 int change_mdate = 0;
782 int change_parent_mdate = 0;
787 u_int16_t bitmap = f_bitmap;
788 u_int32_t cdate,bdate;
789 u_char finder_buf[32];
792 LOG(log_info, logtype_afpd, "begin setfilparams:");
795 upath = path->u_name;
796 adp = of_ad(vol, path, &ad);
799 if (!vol_unix_priv(vol) && check_access(upath, OPENACC_WR ) < 0) {
800 return AFPERR_ACCESS;
803 /* with unix priv maybe we have to change adouble file priv first */
805 while ( bitmap != 0 ) {
806 while (( bitmap & 1 ) == 0 ) {
813 memcpy(&ashort, buf, sizeof( ashort ));
814 buf += sizeof( ashort );
818 memcpy(&cdate, buf, sizeof(cdate));
819 buf += sizeof( cdate );
822 memcpy(&newdate, buf, sizeof( newdate ));
823 buf += sizeof( newdate );
827 memcpy(&bdate, buf, sizeof( bdate));
828 buf += sizeof( bdate );
832 memcpy(finder_buf, buf, 32 );
835 case FILPBIT_UNIXPR :
836 if (!vol_unix_priv(vol)) {
837 /* this volume doesn't use unix priv */
843 change_parent_mdate = 1;
845 memcpy( &aint, buf, sizeof( aint ));
846 f_uid = ntohl (aint);
847 buf += sizeof( aint );
848 memcpy( &aint, buf, sizeof( aint ));
849 f_gid = ntohl (aint);
850 buf += sizeof( aint );
851 setfilowner(vol, f_uid, f_gid, path);
853 memcpy( &upriv, buf, sizeof( upriv ));
854 buf += sizeof( upriv );
855 upriv = ntohl (upriv);
856 if ((upriv & S_IWUSR)) {
857 setfilunixmode(vol, path, upriv);
864 case FILPBIT_PDINFO :
865 if (afp_version < 30) { /* else it's UTF8 name */
868 /* Keep special case to support crlf translations */
869 if ((unsigned int) achar == 0x04) {
870 fdType = (u_char *)"TEXT";
873 xyy[0] = ( u_char ) 'p';
884 /* break while loop */
893 /* second try with adouble open
895 if ( ad_open_metadata( upath, vol_noadouble(vol), O_CREAT, adp) < 0) {
896 /* for some things, we don't need an adouble header */
897 if (f_bitmap & ~(1<<FILPBIT_MDATE)) {
898 return vol_noadouble(vol) ? AFP_OK : AFPERR_ACCESS;
901 } else if ((ad_get_HF_flags( adp ) & O_CREAT) ) {
902 ad_setname(adp, path->m_name);
907 while ( bitmap != 0 ) {
908 while (( bitmap & 1 ) == 0 ) {
915 ad_getattr(adp, &bshort);
916 if ((bshort & htons(ATTRBIT_INVISIBLE)) !=
917 (ashort & htons(ATTRBIT_INVISIBLE) & htons(ATTRBIT_SETCLR)) )
918 change_parent_mdate = 1;
919 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
920 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
924 ad_setattr(adp, bshort);
927 ad_setdate(adp, AD_DATE_CREATE, cdate);
932 ad_setdate(adp, AD_DATE_BACKUP, bdate);
935 if (default_type( ad_entry( adp, ADEID_FINDERI ))
937 ((em = getextmap( path->m_name )) &&
938 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
939 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
940 || ((em = getdefextmap()) &&
941 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
942 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
944 memcpy(finder_buf, ufinderi, 8 );
946 memcpy(ad_entry( adp, ADEID_FINDERI ), finder_buf, 32 );
948 case FILPBIT_UNIXPR :
950 setfilunixmode(vol, path, upriv);
953 case FILPBIT_PDINFO :
954 if (afp_version < 30) { /* else it's UTF8 name */
955 memcpy(ad_entry( adp, ADEID_FINDERI ), fdType, 4 );
956 memcpy(ad_entry( adp, ADEID_FINDERI ) + 4, "pdos", 4 );
962 goto setfilparam_done;
969 if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
970 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
974 ad_setdate(adp, AD_DATE_MODIFY, newdate);
975 ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
981 ad_close_metadata( adp);
985 if (change_parent_mdate && gettimeofday(&tv, NULL) == 0) {
986 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
987 bitmap = 1<<FILPBIT_MDATE;
988 setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
992 LOG(log_info, logtype_afpd, "end setfilparams:");
998 * renamefile and copyfile take the old and new unix pathnames
999 * and the new mac name.
1001 * src the source path
1002 * dst the dest filename in current dir
1003 * newname the dest mac name
1004 * adp adouble struct of src file, if open, or & zeroed one
1007 int renamefile(vol, src, dst, newname, adp )
1008 const struct vol *vol;
1009 char *src, *dst, *newname;
1010 struct adouble *adp;
1015 LOG(log_info, logtype_afpd, "begin renamefile:");
1018 if ( unix_rename( src, 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, src, dst, newname, NULL )) ) {
1037 /* on error copyfile delete dest */
1040 return deletefile(vol, src, 0);
1042 return( AFPERR_PARAM );
1046 if (vol->vfs->rf_renamefile(vol, src, dst) < 0 ) {
1050 /* try to undo the data fork rename,
1051 * we know we are on the same device
1054 unix_rename( dst, 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 );
1078 LOG(log_info, logtype_afpd, "end renamefile:");
1085 convert a Mac long name to an utf8 name,
1087 size_t mtoUTF8(const struct vol *vol, const char *src, size_t srclen, char *dest, size_t destlen)
1091 if ((size_t)-1 == (outlen = convert_string ( vol->v_maccharset, CH_UTF8_MAC, src, srclen, dest, destlen)) ) {
1097 /* ---------------- */
1098 int copy_path_name(const struct vol *vol, char *newname, char *ibuf)
1105 if ( type != 2 && !(afp_version >= 30 && type == 3) ) {
1111 if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
1112 if (afp_version >= 30) {
1113 /* convert it to UTF8
1115 if ((plen = mtoUTF8(vol, ibuf, plen, newname, AFPOBJ_TMPSIZ)) == (size_t)-1)
1119 strncpy( newname, ibuf, plen );
1120 newname[ plen ] = '\0';
1122 if (strlen(newname) != plen) {
1123 /* there's \0 in newname, e.g. it's a pathname not
1131 memcpy(&hint, ibuf, sizeof(hint));
1132 ibuf += sizeof(hint);
1134 memcpy(&len16, ibuf, sizeof(len16));
1135 ibuf += sizeof(len16);
1136 plen = ntohs(len16);
1139 if (plen > AFPOBJ_TMPSIZ) {
1142 strncpy( newname, ibuf, plen );
1143 newname[ plen ] = '\0';
1144 if (strlen(newname) != plen) {
1153 /* -----------------------------------
1155 int afp_copyfile(obj, ibuf, ibuflen, rbuf, rbuflen )
1157 char *ibuf, *rbuf _U_;
1158 int ibuflen _U_, *rbuflen;
1160 struct vol *s_vol, *d_vol;
1162 char *newname, *p, *upath;
1163 struct path *s_path;
1164 u_int32_t sdid, ddid;
1165 int err, retvalue = AFP_OK;
1166 u_int16_t svid, dvid;
1168 struct adouble ad, *adp;
1172 LOG(log_info, logtype_afpd, "begin afp_copyfile:");
1178 memcpy(&svid, ibuf, sizeof( svid ));
1179 ibuf += sizeof( svid );
1180 if (NULL == ( s_vol = getvolbyvid( svid )) ) {
1181 return( AFPERR_PARAM );
1184 memcpy(&sdid, ibuf, sizeof( sdid ));
1185 ibuf += sizeof( sdid );
1186 if (NULL == ( dir = dirlookup( s_vol, sdid )) ) {
1190 memcpy(&dvid, ibuf, sizeof( dvid ));
1191 ibuf += sizeof( dvid );
1192 memcpy(&ddid, ibuf, sizeof( ddid ));
1193 ibuf += sizeof( ddid );
1195 if (NULL == ( s_path = cname( s_vol, dir, &ibuf )) ) {
1196 return get_afp_errno(AFPERR_PARAM);
1198 if ( path_isadir(s_path) ) {
1199 return( AFPERR_BADTYPE );
1202 /* don't allow copies when the file is open.
1203 * XXX: the spec only calls for read/deny write access.
1204 * however, copyfile doesn't have any of that info,
1205 * and locks need to stay coherent. as a result,
1206 * we just balk if the file is opened already. */
1208 adp = of_ad(s_vol, s_path, &ad);
1210 if (ad_open(s_path->u_name , ADFLAGS_DF |ADFLAGS_HF | ADFLAGS_NOHF, O_RDONLY, 0, adp) < 0) {
1211 return AFPERR_DENYCONF;
1213 denyreadset = (getforkmode(adp, ADEID_DFORK, AD_FILELOCK_DENY_RD) != 0 ||
1214 getforkmode(adp, ADEID_RFORK, AD_FILELOCK_DENY_RD) != 0 );
1215 ad_close( adp, ADFLAGS_DF |ADFLAGS_HF );
1217 return AFPERR_DENYCONF;
1220 newname = obj->newtmp;
1221 strcpy( newname, s_path->m_name );
1223 p = ctoupath( s_vol, curdir, newname );
1225 return AFPERR_PARAM;
1229 /* FIXME svid != dvid && dvid's user can't read svid */
1231 if (NULL == ( d_vol = getvolbyvid( dvid )) ) {
1232 return( AFPERR_PARAM );
1235 if (d_vol->v_flags & AFPVOL_RO)
1236 return AFPERR_VLOCK;
1238 if (NULL == ( dir = dirlookup( d_vol, ddid )) ) {
1242 if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) {
1243 return get_afp_errno(AFPERR_NOOBJ);
1245 if ( *s_path->m_name != '\0' ) {
1246 path_error(s_path, AFPERR_PARAM);
1249 /* one of the handful of places that knows about the path type */
1250 if (copy_path_name(d_vol, newname, ibuf) < 0) {
1251 return( 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 return( AFPERR_PARAM );
1259 if ( (err = copyfile(s_vol, d_vol, p, upath , newname, adp)) < 0 ) {
1265 if (vol->v_flags & AFPVOL_DROPBOX) {
1266 retvalue=matchfile2dirperms(upath, vol, ddid); /* FIXME sdir or ddid */
1268 #endif /* DROPKLUDGE */
1270 setvoltime(obj, d_vol );
1273 LOG(log_info, logtype_afpd, "end afp_copyfile:");
1279 /* ----------------------- */
1280 static __inline__ int copy_all(const int dfd, const void *buf,
1286 LOG(log_info, logtype_afpd, "begin copy_all:");
1289 while (buflen > 0) {
1290 if ((cc = write(dfd, buf, buflen)) < 0) {
1302 LOG(log_info, logtype_afpd, "end copy_all:");
1308 /* --------------------------
1309 * copy only the fork data stream
1311 static int copy_fork(int eid, struct adouble *add, struct adouble *ads)
1318 if (eid == ADEID_DFORK) {
1319 sfd = ad_data_fileno(ads);
1320 dfd = ad_data_fileno(add);
1323 sfd = ad_reso_fileno(ads);
1324 dfd = ad_reso_fileno(add);
1327 if ((off_t)-1 == lseek(sfd, ad_getentryoff(ads, eid), SEEK_SET))
1330 if ((off_t)-1 == lseek(dfd, ad_getentryoff(add, eid), SEEK_SET))
1333 #if 0 /* ifdef SENDFILE_FLAVOR_LINUX */
1334 /* doesn't work With 2.6 FIXME, only check for EBADFD ? */
1338 #define BUF 128*1024*1024
1340 if (fstat(sfd, &st) == 0) {
1343 if ( offset >= st.st_size) {
1346 size = (st.st_size -offset > BUF)?BUF:st.st_size -offset;
1347 if ((cc = sys_sendfile(dfd, sfd, &offset, size)) < 0) {
1350 case EINVAL: /* there's no guarantee that all fs support sendfile */
1359 lseek(sfd, offset, SEEK_SET);
1363 if ((cc = read(sfd, filebuf, sizeof(filebuf))) < 0) {
1370 if (!cc || ((err = copy_all(dfd, filebuf, cc)) < 0)) {
1377 /* ----------------------------------
1378 * if newname is NULL (from directory.c) we don't want to copy the resource fork.
1379 * because we are doing it elsewhere.
1380 * currently if newname is NULL then adp is NULL.
1382 int copyfile(s_vol, d_vol, src, dst, newname, adp )
1383 const struct vol *s_vol, *d_vol;
1384 char *src, *dst, *newname;
1385 struct adouble *adp;
1387 struct adouble ads, add;
1391 int noadouble = vol_noadouble(d_vol);
1396 LOG(log_info, logtype_afpd, "begin copyfile:");
1400 ad_init(&ads, s_vol->v_adouble, s_vol->v_ad_options);
1403 ad_init(&add, d_vol->v_adouble, d_vol->v_ad_options);
1404 adflags = ADFLAGS_DF;
1406 adflags |= ADFLAGS_HF;
1409 if (ad_open(src , adflags | ADFLAGS_NOHF, O_RDONLY, 0, adp) < 0) {
1414 if (ad_meta_fileno(adp) == -1 && ad_reso_fileno(adp) == -1) { /* META / HF */
1415 /* no resource fork, don't create one for dst file */
1416 adflags &= ~ADFLAGS_HF;
1419 stat_result = fstat(ad_data_fileno(adp), &st); /* saving stat exit code, thus saving us on one more stat later on */
1421 if (stat_result < 0) {
1422 /* unlikely but if fstat fails, the default file mode will be 0666. */
1423 st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
1426 if (ad_open(dst , adflags | noadouble, O_RDWR|O_CREAT|O_EXCL, st.st_mode, &add) < 0) {
1428 ad_close( adp, adflags );
1429 if (EEXIST != ret_err) {
1430 deletefile(d_vol, dst, 0);
1433 return AFPERR_EXIST;
1435 /* XXX if the source and the dest don't use the same resource type it's broken
1437 if (ad_reso_fileno(adp) == -1 || 0 == (err = copy_fork(ADEID_RFORK, &add, adp))){
1438 /* copy the data fork */
1439 err = copy_fork(ADEID_DFORK, &add, adp);
1446 if (!ret_err && newname && (adflags & ADFLAGS_HF)) {
1447 /* set the new name in the resource fork */
1448 ad_copy_header(&add, adp);
1449 ad_setname(&add, newname);
1452 ad_close( adp, adflags );
1454 if (ad_close( &add, adflags ) <0) {
1459 deletefile(d_vol, dst, 0);
1461 else if (stat_result == 0) {
1462 /* set dest modification date to src date */
1465 ut.actime = ut.modtime = st.st_mtime;
1467 /* FIXME netatalk doesn't use resource fork file date
1468 * but maybe we should set its modtime too.
1473 LOG(log_info, logtype_afpd, "end copyfile:");
1477 switch ( ret_err ) {
1483 return AFPERR_DFULL;
1485 return AFPERR_NOOBJ;
1487 return AFPERR_ACCESS;
1489 return AFPERR_VLOCK;
1491 return AFPERR_PARAM;
1495 /* -----------------------------------
1496 vol: not NULL delete cnid entry. then we are in curdir and file is a only filename
1497 checkAttrib: 1 check kFPDeleteInhibitBit (deletfile called by afp_delete)
1499 when deletefile is called we don't have lock on it, file is closed (for us)
1500 untrue if called by renamefile
1502 ad_open always try to open file RDWR first and ad_lock takes care of
1503 WRITE lock on read only file.
1506 static int check_attrib(struct adouble *adp)
1508 u_int16_t bshort = 0;
1510 ad_getattr(adp, &bshort);
1512 * Does kFPDeleteInhibitBit (bit 8) set?
1514 if ((bshort & htons(ATTRBIT_NODELETE))) {
1515 return AFPERR_OLOCK;
1517 if ((bshort & htons(ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
1523 int deletefile( vol, file, checkAttrib )
1524 const struct vol *vol;
1529 struct adouble *adp = &ad;
1530 int adflags, err = AFP_OK;
1533 LOG(log_info, logtype_afpd, "begin deletefile:");
1536 /* try to open both forks at once */
1537 adflags = ADFLAGS_DF|ADFLAGS_HF;
1539 /* was EACCESS error try to get only metadata */
1540 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
1541 if ( ad_metadata( file , ADFLAGS_OPENFORKS, &ad) == 0 ) {
1542 ad_close( &ad, adflags );
1543 if ((err = check_attrib(&ad))) {
1550 ad_init(&ad, vol->v_adouble, vol->v_ad_options); /* OK */
1551 if ( ad_open( file, adflags, O_RDONLY, 0, &ad ) < 0 ) {
1554 if (adflags == ADFLAGS_DF)
1555 return AFPERR_NOOBJ;
1557 /* that failed. now try to open just the data fork */
1558 adflags = ADFLAGS_DF;
1562 adp = NULL; /* maybe it's a file with no write mode for us */
1563 break; /* was return AFPERR_ACCESS;*/
1565 return AFPERR_VLOCK;
1567 return( AFPERR_PARAM );
1570 break; /* from the while */
1573 if (adp && (adflags & ADFLAGS_HF) ) {
1574 /* FIXME we have a pb here because we want to know if a file is open
1575 * there's a 'priority inversion' if you can't open the ressource fork RW
1576 * you can delete it if it's open because you can't get a write lock.
1578 * ADLOCK_FILELOCK means the whole ressource fork, not only after the
1581 * FIXME it doesn't work for RFORK open read only and fork open without deny mode
1583 if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
1584 ad_close( &ad, adflags );
1585 return( AFPERR_BUSY );
1589 if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
1592 else if (!(err = vol->vfs->rf_deletefile(vol, file)) && !(err = netatalk_unlink( file )) ) {
1594 if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file))))
1596 cnid_delete(vol->v_cdb, id);
1600 ad_close( &ad, adflags ); /* ad_close removes locks if any */
1603 LOG(log_info, logtype_afpd, "end deletefile:");
1609 /* ------------------------------------ */
1610 /* return a file id */
1611 int afp_createid(obj, ibuf, ibuflen, rbuf, rbuflen )
1614 int ibuflen _U_, *rbuflen;
1623 struct path *s_path;
1626 LOG(log_info, logtype_afpd, "begin afp_createid:");
1633 memcpy(&vid, ibuf, sizeof(vid));
1634 ibuf += sizeof(vid);
1636 if (NULL == ( vol = getvolbyvid( vid )) ) {
1637 return( AFPERR_PARAM);
1640 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1644 if (vol->v_flags & AFPVOL_RO)
1645 return AFPERR_VLOCK;
1647 memcpy(&did, ibuf, sizeof( did ));
1648 ibuf += sizeof(did);
1650 if (NULL == ( dir = dirlookup( vol, did )) ) {
1651 return afp_errno; /* was AFPERR_PARAM */
1654 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
1655 return get_afp_errno(AFPERR_NOOBJ); /* was AFPERR_PARAM */
1658 if ( path_isadir(s_path) ) {
1659 return( AFPERR_BADTYPE );
1662 upath = s_path->u_name;
1663 switch (s_path->st_errno) {
1665 break; /* success */
1668 return AFPERR_ACCESS;
1670 return AFPERR_NOOBJ;
1672 return AFPERR_PARAM;
1675 if ((id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath)))) {
1676 memcpy(rbuf, &id, sizeof(id));
1677 *rbuflen = sizeof(id);
1678 return AFPERR_EXISTID;
1681 if ((id = get_id(vol, NULL, st, did, upath, len)) != CNID_INVALID) {
1682 memcpy(rbuf, &id, sizeof(id));
1683 *rbuflen = sizeof(id);
1688 LOG(log_info, logtype_afpd, "ending afp_createid...:");
1693 /* ------------------------------- */
1699 static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
1702 struct reenum *param = data;
1703 struct vol *vol = param->vol;
1704 cnid_t did = param->did;
1707 memset(&path, 0, sizeof(path));
1709 if ( stat(de->d_name, &path.st)<0 )
1712 /* update or add to cnid */
1713 aint = cnid_add(vol->v_cdb, &path.st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */
1715 #if AD_VERSION > AD_VERSION1
1716 if (aint != CNID_INVALID && !S_ISDIR(path.st.st_mode)) {
1717 struct adouble ad, *adp;
1721 path.u_name = de->d_name;
1723 adp = of_ad(vol, &path, &ad);
1725 if ( ad_open_metadata( de->d_name, 0, 0, adp ) < 0 ) {
1728 if (ad_setid(adp, path.st.st_dev, path.st.st_ino, aint, did, vol->v_stamp)) {
1731 ad_close_metadata(adp);
1733 #endif /* AD_VERSION > AD_VERSION1 */
1738 /* --------------------
1739 * Ok the db is out of synch with the dir.
1740 * but if it's a deleted file we don't want to do it again and again.
1743 reenumerate_id(struct vol *vol, char *name, struct dir *dir)
1749 if (vol->v_cdb == NULL) {
1753 /* FIXME use of_statdir ? */
1754 if (stat(name, &st)) {
1758 if (dirreenumerate(dir, &st)) {
1759 /* we already did it once and the dir haven't been modified */
1764 data.did = dir->d_did;
1765 if ((ret = for_each_dirent(vol, name, reenumerate_loop, (void *)&data)) >= 0) {
1766 setdiroffcnt(curdir, &st, ret);
1767 dir->d_flags |= DIRF_CNID;
1773 /* ------------------------------
1774 resolve a file id */
1775 int afp_resolveid(obj, ibuf, ibuflen, rbuf, rbuflen )
1778 int ibuflen _U_, *rbuflen;
1784 int err, buflen, retry=0;
1786 u_int16_t vid, bitmap;
1788 static char buffer[12 + MAXPATHLEN + 1];
1789 int len = 12 + MAXPATHLEN + 1;
1792 LOG(log_info, logtype_afpd, "begin afp_resolveid:");
1798 memcpy(&vid, ibuf, sizeof(vid));
1799 ibuf += sizeof(vid);
1801 if (NULL == ( vol = getvolbyvid( vid )) ) {
1802 return( AFPERR_PARAM);
1805 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1809 memcpy(&id, ibuf, sizeof( id ));
1814 /* some MacOS versions after a catsearch do a *lot* of afp_resolveid with 0 */
1818 memset(&path, 0, sizeof(path));
1819 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1820 return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
1823 if (NULL == ( dir = dirlookup( vol, id )) ) {
1824 return AFPERR_NOID; /* idem AFPERR_PARAM */
1826 path.u_name = upath;
1827 if (movecwd(vol, dir) < 0) {
1831 return AFPERR_ACCESS;
1835 return AFPERR_PARAM;
1839 if ( of_stat(&path) < 0 ) {
1841 /* with nfs and our working directory is deleted */
1842 if (errno == ESTALE) {
1846 if ( errno == ENOENT && !retry) {
1847 /* cnid db is out of sync, reenumerate the directory and update ids */
1848 reenumerate_id(vol, ".", dir);
1856 return AFPERR_ACCESS;
1860 return AFPERR_PARAM;
1864 /* directories are bad */
1865 if (S_ISDIR(path.st.st_mode))
1866 return AFPERR_BADTYPE;
1868 memcpy(&bitmap, ibuf, sizeof(bitmap));
1869 bitmap = ntohs( bitmap );
1870 if (NULL == (path.m_name = utompath(vol, upath, cnid, utf8_encoding()))) {
1874 if (AFP_OK != (err = getfilparams(vol, bitmap, &path , curdir,
1875 rbuf + sizeof(bitmap), &buflen))) {
1878 *rbuflen = buflen + sizeof(bitmap);
1879 memcpy(rbuf, ibuf, sizeof(bitmap));
1882 LOG(log_info, logtype_afpd, "end afp_resolveid:");
1888 /* ------------------------------ */
1889 int afp_deleteid(obj, ibuf, ibuflen, rbuf, rbuflen )
1891 char *ibuf, *rbuf _U_;
1892 int ibuflen _U_, *rbuflen;
1902 static char buffer[12 + MAXPATHLEN + 1];
1903 int len = 12 + MAXPATHLEN + 1;
1906 LOG(log_info, logtype_afpd, "begin afp_deleteid:");
1912 memcpy(&vid, ibuf, sizeof(vid));
1913 ibuf += sizeof(vid);
1915 if (NULL == ( vol = getvolbyvid( vid )) ) {
1916 return( AFPERR_PARAM);
1919 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1923 if (vol->v_flags & AFPVOL_RO)
1924 return AFPERR_VLOCK;
1926 memcpy(&id, ibuf, sizeof( id ));
1930 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1934 if (NULL == ( dir = dirlookup( vol, id )) ) {
1935 return( AFPERR_PARAM );
1939 if ((movecwd(vol, dir) < 0) || (stat(upath, &st) < 0)) {
1943 return AFPERR_ACCESS;
1948 /* still try to delete the id */
1952 return AFPERR_PARAM;
1955 else if (S_ISDIR(st.st_mode)) /* directories are bad */
1956 return AFPERR_BADTYPE;
1958 if (cnid_delete(vol->v_cdb, fileid)) {
1961 return AFPERR_VLOCK;
1964 return AFPERR_ACCESS;
1966 return AFPERR_PARAM;
1971 LOG(log_info, logtype_afpd, "end afp_deleteid:");
1977 /* ------------------------------ */
1978 static struct adouble *find_adouble(struct path *path, struct ofork **of, struct adouble *adp)
1982 if (path->st_errno) {
1983 switch (path->st_errno) {
1985 afp_errno = AFPERR_NOID;
1989 afp_errno = AFPERR_ACCESS;
1992 afp_errno = AFPERR_PARAM;
1997 /* we use file_access both for legacy Mac perm and
1998 * for unix privilege, rename will take care of folder perms
2000 if (file_access(path, OPENACC_WR ) < 0) {
2001 afp_errno = AFPERR_ACCESS;
2005 if ((*of = of_findname(path))) {
2006 /* reuse struct adouble so it won't break locks */
2010 ret = ad_open( path->u_name, ADFLAGS_HF, O_RDONLY, 0, adp);
2012 if ( !ret && ad_reso_fileno(adp) != -1 && !(adp->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
2014 * The user must have the Read & Write privilege for both files in order to use this command.
2016 ad_close(adp, ADFLAGS_HF);
2017 afp_errno = AFPERR_ACCESS;
2024 #define APPLETEMP ".AppleTempXXXXXX"
2026 int afp_exchangefiles(obj, ibuf, ibuflen, rbuf, rbuflen )
2028 char *ibuf, *rbuf _U_ ;
2029 int ibuflen _U_, *rbuflen;
2031 struct stat srcst, destst;
2033 struct dir *dir, *sdir;
2034 char *spath, temp[17], *p;
2035 char *supath, *upath;
2040 struct adouble *adsp = NULL;
2041 struct adouble *addp = NULL;
2042 struct ofork *s_of = NULL;
2043 struct ofork *d_of = NULL;
2054 LOG(log_info, logtype_afpd, "begin afp_exchangefiles:");
2060 memcpy(&vid, ibuf, sizeof(vid));
2061 ibuf += sizeof(vid);
2063 if (NULL == ( vol = getvolbyvid( vid )) ) {
2064 return( AFPERR_PARAM);
2067 if ((vol->v_flags & AFPVOL_RO))
2068 return AFPERR_VLOCK;
2070 /* source and destination dids */
2071 memcpy(&sid, ibuf, sizeof(sid));
2072 ibuf += sizeof(sid);
2073 memcpy(&did, ibuf, sizeof(did));
2074 ibuf += sizeof(did);
2077 if (NULL == (dir = dirlookup( vol, sid )) ) {
2078 return afp_errno; /* was AFPERR_PARAM */
2081 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2082 return get_afp_errno(AFPERR_NOOBJ);
2085 if ( path_isadir(path) ) {
2086 return AFPERR_BADTYPE; /* it's a dir */
2089 /* save some stuff */
2092 spath = obj->oldtmp;
2093 supath = obj->newtmp;
2094 strcpy(spath, path->m_name);
2095 strcpy(supath, path->u_name); /* this is for the cnid changing */
2096 p = absupath( vol, sdir, supath);
2098 /* pathname too long */
2099 return AFPERR_PARAM ;
2102 ad_init(&ads, vol->v_adouble, vol->v_ad_options);
2103 if (!(adsp = find_adouble( path, &s_of, &ads))) {
2107 /* ***** from here we may have resource fork open **** */
2109 /* look for the source cnid. if it doesn't exist, don't worry about
2111 sid = cnid_lookup(vol->v_cdb, &srcst, sdir->d_did, supath,slen = strlen(supath));
2113 if (NULL == ( dir = dirlookup( vol, did )) ) {
2114 err = afp_errno; /* was AFPERR_PARAM */
2115 goto err_exchangefile;
2118 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2119 err = get_afp_errno(AFPERR_NOOBJ);
2120 goto err_exchangefile;
2123 if ( path_isadir(path) ) {
2124 err = AFPERR_BADTYPE;
2125 goto err_exchangefile;
2128 /* FPExchangeFiles is the only call that can return the SameObj
2130 if ((curdir == sdir) && strcmp(spath, path->m_name) == 0) {
2131 err = AFPERR_SAMEOBJ;
2132 goto err_exchangefile;
2135 ad_init(&add, vol->v_adouble, vol->v_ad_options);
2136 if (!(addp = find_adouble( path, &d_of, &add))) {
2138 goto err_exchangefile;
2142 /* they are not on the same device and at least one is open
2143 * FIXME broken for for crossdev and adouble v2
2146 crossdev = (srcst.st_dev != destst.st_dev);
2147 if (/* (d_of || s_of) && */ crossdev) {
2149 goto err_exchangefile;
2152 /* look for destination id. */
2153 upath = path->u_name;
2154 did = cnid_lookup(vol->v_cdb, &destst, curdir->d_did, upath, dlen = strlen(upath));
2156 /* construct a temp name.
2157 * NOTE: the temp file will be in the dest file's directory. it
2158 * will also be inaccessible from AFP. */
2159 memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
2160 if (!mktemp(temp)) {
2162 goto err_exchangefile;
2166 /* FIXME we need to close fork for copy, both s_of and d_of are null */
2167 ad_close(adsp, ADFLAGS_HF);
2168 ad_close(addp, ADFLAGS_HF);
2171 /* now, quickly rename the file. we error if we can't. */
2172 if ((err = renamefile(vol, p, temp, temp, adsp)) != AFP_OK)
2173 goto err_exchangefile;
2174 of_rename(vol, s_of, sdir, spath, curdir, temp);
2176 /* rename destination to source */
2177 if ((err = renamefile(vol, upath, p, spath, addp)) != AFP_OK)
2178 goto err_src_to_tmp;
2179 of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
2181 /* rename temp to destination */
2182 if ((err = renamefile(vol, temp, upath, path->m_name, adsp)) != AFP_OK)
2183 goto err_dest_to_src;
2184 of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
2186 /* id's need switching. src -> dest and dest -> src.
2187 * we need to re-stat() if it was a cross device copy.
2190 cnid_delete(vol->v_cdb, sid);
2193 cnid_delete(vol->v_cdb, did);
2195 if ((did && ( (crossdev && stat( upath, &srcst) < 0) ||
2196 cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
2198 (sid && ( (crossdev && stat(p, &destst) < 0) ||
2199 cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
2204 err = AFPERR_ACCESS;
2209 goto err_temp_to_dest;
2212 /* here we need to reopen if crossdev */
2213 if (sid && ad_setid(addp, destst.st_dev, destst.st_ino, sid, sdir->d_did, vol->v_stamp))
2218 if (did && ad_setid(adsp, srcst.st_dev, srcst.st_ino, did, curdir->d_did, vol->v_stamp))
2223 /* change perms, src gets dest perm and vice versa */
2228 LOG(log_error, logtype_afpd, "seteuid failed %s", strerror(errno));
2229 err = AFP_OK; /* ignore error */
2230 goto err_temp_to_dest;
2234 * we need to exchange ACL entries as well
2236 /* exchange_acls(vol, p, upath); */
2241 path->m_name = NULL;
2242 path->u_name = upath;
2244 setfilunixmode(vol, path, destst.st_mode);
2245 setfilowner(vol, destst.st_uid, destst.st_gid, path);
2252 setfilunixmode(vol, path, srcst.st_mode);
2253 setfilowner(vol, srcst.st_uid, srcst.st_gid, path);
2255 if ( setegid(gid) < 0 || seteuid(uid) < 0) {
2256 LOG(log_error, logtype_afpd, "can't seteuid back %s", strerror(errno));
2261 LOG(log_info, logtype_afpd, "ending afp_exchangefiles:");
2265 goto err_exchangefile;
2267 /* all this stuff is so that we can unwind a failed operation
2270 /* rename dest to temp */
2271 renamefile(vol, upath, temp, temp, adsp);
2272 of_rename(vol, s_of, curdir, upath, curdir, temp);
2275 /* rename source back to dest */
2276 renamefile(vol, p, upath, path->m_name, addp);
2277 of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
2280 /* rename temp back to source */
2281 renamefile(vol, temp, p, spath, adsp);
2282 of_rename(vol, s_of, curdir, temp, sdir, spath);
2285 if ( !s_of && adsp && ad_meta_fileno(adsp) != -1 ) { /* META */
2286 ad_close(adsp, ADFLAGS_HF);
2288 if ( !d_of && addp && ad_meta_fileno(addp) != -1 ) {/* META */
2289 ad_close(addp, ADFLAGS_HF);