2 * $Id: file.c,v 1.140 2010-03-02 12:45:31 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 */
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 * src the source path
1007 * dst the dest filename in current dir
1008 * newname the dest mac name
1009 * adp adouble struct of src file, if open, or & zeroed one
1012 int renamefile(const struct vol *vol, char *src, char *dst, char *newname, struct adouble *adp)
1017 LOG(log_debug9, logtype_afpd, "begin renamefile:");
1020 if ( unix_rename( src, dst ) < 0 ) {
1023 return( AFPERR_NOOBJ );
1026 return( AFPERR_ACCESS );
1028 return AFPERR_VLOCK;
1029 case EXDEV : /* Cross device move -- try copy */
1030 /* NOTE: with open file it's an error because after the copy we will
1031 * get two files, it's fixable for our process (eg reopen the new file, get the
1032 * locks, and so on. But it doesn't solve the case with a second process
1034 if (adp->ad_open_forks) {
1035 /* FIXME warning in syslog so admin'd know there's a conflict ?*/
1036 return AFPERR_OLOCK; /* little lie */
1038 if (AFP_OK != ( rc = copyfile(vol, vol, src, dst, newname, NULL )) ) {
1039 /* on error copyfile delete dest */
1042 return deletefile(vol, src, 0);
1044 return( AFPERR_PARAM );
1048 if (vol->vfs->vfs_renamefile(vol, src, dst) < 0 ) {
1052 /* try to undo the data fork rename,
1053 * we know we are on the same device
1056 unix_rename( dst, src );
1057 /* return the first error */
1060 return AFPERR_NOOBJ;
1063 return AFPERR_ACCESS ;
1065 return AFPERR_VLOCK;
1067 return AFPERR_PARAM ;
1072 /* don't care if we can't open the newly renamed ressource fork
1074 if (!ad_open( dst, ADFLAGS_HF, O_RDWR, 0666, adp)) {
1075 ad_setname(adp, newname);
1077 ad_close( adp, ADFLAGS_HF );
1080 LOG(log_debug9, logtype_afpd, "end renamefile:");
1087 convert a Mac long name to an utf8 name,
1089 size_t mtoUTF8(const struct vol *vol, const char *src, size_t srclen, char *dest, size_t destlen)
1093 if ((size_t)-1 == (outlen = convert_string ( vol->v_maccharset, CH_UTF8_MAC, src, srclen, dest, destlen)) ) {
1099 /* ---------------- */
1100 int copy_path_name(const struct vol *vol, char *newname, char *ibuf)
1107 if ( type != 2 && !(afp_version >= 30 && type == 3) ) {
1113 if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
1114 if (afp_version >= 30) {
1115 /* convert it to UTF8
1117 if ((plen = mtoUTF8(vol, ibuf, plen, newname, AFPOBJ_TMPSIZ)) == (size_t)-1)
1121 strncpy( newname, ibuf, plen );
1122 newname[ plen ] = '\0';
1124 if (strlen(newname) != plen) {
1125 /* there's \0 in newname, e.g. it's a pathname not
1133 memcpy(&hint, ibuf, sizeof(hint));
1134 ibuf += sizeof(hint);
1136 memcpy(&len16, ibuf, sizeof(len16));
1137 ibuf += sizeof(len16);
1138 plen = ntohs(len16);
1141 if (plen > AFPOBJ_TMPSIZ) {
1144 strncpy( newname, ibuf, plen );
1145 newname[ plen ] = '\0';
1146 if (strlen(newname) != plen) {
1155 /* -----------------------------------
1157 int afp_copyfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1159 struct vol *s_vol, *d_vol;
1161 char *newname, *p, *upath;
1162 struct path *s_path;
1163 u_int32_t sdid, ddid;
1164 int err, retvalue = AFP_OK;
1165 u_int16_t svid, dvid;
1167 struct adouble ad, *adp;
1173 memcpy(&svid, ibuf, sizeof( svid ));
1174 ibuf += sizeof( svid );
1175 if (NULL == ( s_vol = getvolbyvid( svid )) ) {
1176 return( AFPERR_PARAM );
1179 memcpy(&sdid, ibuf, sizeof( sdid ));
1180 ibuf += sizeof( sdid );
1181 if (NULL == ( dir = dirlookup( s_vol, sdid )) ) {
1185 memcpy(&dvid, ibuf, sizeof( dvid ));
1186 ibuf += sizeof( dvid );
1187 memcpy(&ddid, ibuf, sizeof( ddid ));
1188 ibuf += sizeof( ddid );
1190 if (NULL == ( s_path = cname( s_vol, dir, &ibuf )) ) {
1191 return get_afp_errno(AFPERR_PARAM);
1193 if ( path_isadir(s_path) ) {
1194 return( AFPERR_BADTYPE );
1197 /* don't allow copies when the file is open.
1198 * XXX: the spec only calls for read/deny write access.
1199 * however, copyfile doesn't have any of that info,
1200 * and locks need to stay coherent. as a result,
1201 * we just balk if the file is opened already. */
1203 adp = of_ad(s_vol, s_path, &ad);
1205 if (ad_open(s_path->u_name , ADFLAGS_DF |ADFLAGS_HF | ADFLAGS_NOHF, O_RDONLY, 0, adp) < 0) {
1206 return AFPERR_DENYCONF;
1208 denyreadset = (getforkmode(adp, ADEID_DFORK, AD_FILELOCK_DENY_RD) != 0 ||
1209 getforkmode(adp, ADEID_RFORK, AD_FILELOCK_DENY_RD) != 0 );
1212 retvalue = AFPERR_DENYCONF;
1216 newname = obj->newtmp;
1217 strcpy( newname, s_path->m_name );
1219 p = ctoupath( s_vol, curdir, newname );
1221 retvalue = AFPERR_PARAM;
1226 /* FIXME svid != dvid && dvid's user can't read svid */
1228 if (NULL == ( d_vol = getvolbyvid( dvid )) ) {
1229 retvalue = AFPERR_PARAM;
1233 if (d_vol->v_flags & AFPVOL_RO) {
1234 retvalue = AFPERR_VLOCK;
1238 if (NULL == ( dir = dirlookup( d_vol, ddid )) ) {
1239 retvalue = afp_errno;
1243 if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) {
1244 retvalue = get_afp_errno(AFPERR_NOOBJ);
1248 if ( *s_path->m_name != '\0' ) {
1249 retvalue =path_error(s_path, AFPERR_NOOBJ);
1253 /* one of the handful of places that knows about the path type */
1254 if (copy_path_name(d_vol, newname, ibuf) < 0) {
1255 retvalue = AFPERR_PARAM;
1258 /* newname is always only a filename so curdir *is* its
1261 if (NULL == (upath = mtoupath(d_vol, newname, curdir->d_did, utf8_encoding()))) {
1262 retvalue =AFPERR_PARAM;
1266 if ( (err = copyfile(s_vol, d_vol, p, upath , newname, adp)) < 0 ) {
1273 if (vol->v_flags & AFPVOL_DROPBOX) {
1274 retvalue=matchfile2dirperms(upath, vol, ddid); /* FIXME sdir or ddid */
1276 #endif /* DROPKLUDGE */
1278 setvoltime(obj, d_vol );
1281 ad_close( adp, ADFLAGS_DF |ADFLAGS_HF );
1285 /* ----------------------- */
1286 static int copy_all(const int dfd, const void *buf,
1292 LOG(log_debug9, logtype_afpd, "begin copy_all:");
1295 while (buflen > 0) {
1296 if ((cc = write(dfd, buf, buflen)) < 0) {
1308 LOG(log_debug9, logtype_afpd, "end copy_all:");
1314 /* --------------------------
1315 * copy only the fork data stream
1317 static int copy_fork(int eid, struct adouble *add, struct adouble *ads)
1324 if (eid == ADEID_DFORK) {
1325 sfd = ad_data_fileno(ads);
1326 dfd = ad_data_fileno(add);
1329 sfd = ad_reso_fileno(ads);
1330 dfd = ad_reso_fileno(add);
1333 if ((off_t)-1 == lseek(sfd, ad_getentryoff(ads, eid), SEEK_SET))
1336 if ((off_t)-1 == lseek(dfd, ad_getentryoff(add, eid), SEEK_SET))
1339 #if 0 /* ifdef SENDFILE_FLAVOR_LINUX */
1340 /* doesn't work With 2.6 FIXME, only check for EBADFD ? */
1344 #define BUF 128*1024*1024
1346 if (fstat(sfd, &st) == 0) {
1349 if ( offset >= st.st_size) {
1352 size = (st.st_size -offset > BUF)?BUF:st.st_size -offset;
1353 if ((cc = sys_sendfile(dfd, sfd, &offset, size)) < 0) {
1356 case EINVAL: /* there's no guarantee that all fs support sendfile */
1365 lseek(sfd, offset, SEEK_SET);
1369 if ((cc = read(sfd, filebuf, sizeof(filebuf))) < 0) {
1376 if (!cc || ((err = copy_all(dfd, filebuf, cc)) < 0)) {
1383 /* ----------------------------------
1384 * if newname is NULL (from directory.c) we don't want to copy the resource fork.
1385 * because we are doing it elsewhere.
1386 * currently if newname is NULL then adp is NULL.
1388 int copyfile(const struct vol *s_vol, const struct vol*d_vol,
1389 char *src, char *dst, char *newname, struct adouble *adp)
1391 struct adouble ads, add;
1399 LOG(log_debug9, logtype_afpd, "begin copyfile:");
1403 ad_init(&ads, s_vol->v_adouble, s_vol->v_ad_options);
1407 adflags = ADFLAGS_DF;
1409 adflags |= ADFLAGS_HF;
1412 if (ad_open(src , adflags | ADFLAGS_NOHF, O_RDONLY, 0, adp) < 0) {
1417 if (ad_meta_fileno(adp) == -1 && ad_reso_fileno(adp) == -1) { /* META / HF */
1418 /* no resource fork, don't create one for dst file */
1419 adflags &= ~ADFLAGS_HF;
1422 stat_result = fstat(ad_data_fileno(adp), &st); /* saving stat exit code, thus saving us on one more stat later on */
1424 if (stat_result < 0) {
1425 /* unlikely but if fstat fails, the default file mode will be 0666. */
1426 st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
1429 ad_init(&add, d_vol->v_adouble, d_vol->v_ad_options);
1430 if (ad_open(dst , adflags, O_RDWR|O_CREAT|O_EXCL, st.st_mode, &add) < 0) {
1432 ad_close( adp, adflags );
1433 if (EEXIST != ret_err) {
1434 deletefile(d_vol, dst, 0);
1437 return AFPERR_EXIST;
1441 * XXX if the source and the dest don't use the same resource type it's broken
1443 if (ad_reso_fileno(adp) == -1 || 0 == (err = copy_fork(ADEID_RFORK, &add, adp))){
1444 /* copy the data fork */
1445 if ((err = copy_fork(ADEID_DFORK, &add, adp)) == 0) {
1446 err = d_vol->vfs->vfs_copyfile(d_vol, src, dst);
1454 if (!ret_err && newname && (adflags & ADFLAGS_HF)) {
1455 /* set the new name in the resource fork */
1456 ad_copy_header(&add, adp);
1457 ad_setname(&add, newname);
1460 ad_close( adp, adflags );
1462 if (ad_close( &add, adflags ) <0) {
1467 deletefile(d_vol, dst, 0);
1469 else if (stat_result == 0) {
1470 /* set dest modification date to src date */
1473 ut.actime = ut.modtime = st.st_mtime;
1475 /* FIXME netatalk doesn't use resource fork file date
1476 * but maybe we should set its modtime too.
1481 LOG(log_debug9, logtype_afpd, "end copyfile:");
1485 switch ( ret_err ) {
1491 return AFPERR_DFULL;
1493 return AFPERR_NOOBJ;
1495 return AFPERR_ACCESS;
1497 return AFPERR_VLOCK;
1499 return AFPERR_PARAM;
1503 /* -----------------------------------
1504 vol: not NULL delete cnid entry. then we are in curdir and file is a only filename
1505 checkAttrib: 1 check kFPDeleteInhibitBit (deletfile called by afp_delete)
1507 when deletefile is called we don't have lock on it, file is closed (for us)
1508 untrue if called by renamefile
1510 ad_open always try to open file RDWR first and ad_lock takes care of
1511 WRITE lock on read only file.
1514 static int check_attrib(struct adouble *adp)
1516 u_int16_t bshort = 0;
1518 ad_getattr(adp, &bshort);
1520 * Does kFPDeleteInhibitBit (bit 8) set?
1522 if ((bshort & htons(ATTRBIT_NODELETE))) {
1523 return AFPERR_OLOCK;
1525 if ((bshort & htons(ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
1531 int deletefile(const struct vol *vol, char *file, int checkAttrib)
1534 struct adouble *adp = NULL;
1535 int adflags, err = AFP_OK;
1539 LOG(log_debug9, logtype_afpd, "begin deletefile:");
1542 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
1544 /* was EACCESS error try to get only metadata */
1545 /* we never want to create a resource fork here, we are going to delete it
1546 * moreover sometimes deletefile is called with a no existent file and
1547 * ad_open would create a 0 byte resource fork
1549 if ( ad_metadata( file, ADFLAGS_OPENFORKS, &ad) == 0 ) {
1550 if ((err = check_attrib(&ad))) {
1551 ad_close_metadata(&ad);
1558 /* try to open both forks at once */
1559 adflags = ADFLAGS_DF;
1560 if ( ad_open( file, adflags |ADFLAGS_HF|ADFLAGS_NOHF, O_RDONLY, 0, &ad ) < 0 ) {
1565 case EACCES: /* maybe it's a file with no write mode for us */
1566 break; /* was return AFPERR_ACCESS;*/
1579 if ( adp && ad_reso_fileno( adp ) != -1 ) { /* there's a resource fork */
1580 adflags |= ADFLAGS_HF;
1581 /* FIXME we have a pb here because we want to know if a file is open
1582 * there's a 'priority inversion' if you can't open the ressource fork RW
1583 * you can delete it if it's open because you can't get a write lock.
1585 * ADLOCK_FILELOCK means the whole ressource fork, not only after the
1588 * FIXME it doesn't work for RFORK open read only and fork open without deny mode
1590 if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
1596 if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
1599 else if (!(err = vol->vfs->vfs_deletefile(vol, file)) && !(err = netatalk_unlink( file )) ) {
1601 if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file))))
1603 cnid_delete(vol->v_cdb, id);
1609 ad_close_metadata(&ad);
1612 ad_close( &ad, adflags ); /* ad_close removes locks if any */
1616 LOG(log_debug9, logtype_afpd, "end deletefile:");
1622 /* ------------------------------------ */
1623 /* return a file id */
1624 int afp_createid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1633 struct path *s_path;
1639 memcpy(&vid, ibuf, sizeof(vid));
1640 ibuf += sizeof(vid);
1642 if (NULL == ( vol = getvolbyvid( vid )) ) {
1643 return( AFPERR_PARAM);
1646 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1650 if (vol->v_flags & AFPVOL_RO)
1651 return AFPERR_VLOCK;
1653 memcpy(&did, ibuf, sizeof( did ));
1654 ibuf += sizeof(did);
1656 if (NULL == ( dir = dirlookup( vol, did )) ) {
1657 return afp_errno; /* was AFPERR_PARAM */
1660 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
1661 return get_afp_errno(AFPERR_NOOBJ); /* was AFPERR_PARAM */
1664 if ( path_isadir(s_path) ) {
1665 return( AFPERR_BADTYPE );
1668 upath = s_path->u_name;
1669 switch (s_path->st_errno) {
1671 break; /* success */
1674 return AFPERR_ACCESS;
1676 return AFPERR_NOOBJ;
1678 return AFPERR_PARAM;
1681 if ((id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath)))) {
1682 memcpy(rbuf, &id, sizeof(id));
1683 *rbuflen = sizeof(id);
1684 return AFPERR_EXISTID;
1687 if ((id = get_id(vol, NULL, st, did, upath, len)) != CNID_INVALID) {
1688 memcpy(rbuf, &id, sizeof(id));
1689 *rbuflen = sizeof(id);
1696 /* ------------------------------- */
1702 static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
1705 struct reenum *param = data;
1706 struct vol *vol = param->vol;
1707 cnid_t did = param->did;
1710 if ( lstat(de->d_name, &path.st)<0 )
1713 /* update or add to cnid */
1714 aint = cnid_add(vol->v_cdb, &path.st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */
1716 #if AD_VERSION > AD_VERSION1
1717 if (aint != CNID_INVALID && !S_ISDIR(path.st.st_mode)) {
1718 struct adouble ad, *adp;
1722 path.u_name = de->d_name;
1724 adp = of_ad(vol, &path, &ad);
1726 if ( ad_open_metadata( de->d_name, 0, 0, adp ) < 0 ) {
1729 if (ad_setid(adp, path.st.st_dev, path.st.st_ino, aint, did, vol->v_stamp)) {
1732 ad_close_metadata(adp);
1734 #endif /* AD_VERSION > AD_VERSION1 */
1739 /* --------------------
1740 * Ok the db is out of synch with the dir.
1741 * but if it's a deleted file we don't want to do it again and again.
1744 reenumerate_id(struct vol *vol, char *name, struct dir *dir)
1750 if (vol->v_cdb == NULL) {
1754 /* FIXME use of_statdir ? */
1755 if (lstat(name, &st)) {
1759 if (dirreenumerate(dir, &st)) {
1760 /* we already did it once and the dir haven't been modified */
1765 data.did = dir->d_did;
1766 if ((ret = for_each_dirent(vol, name, reenumerate_loop, (void *)&data)) >= 0) {
1767 setdiroffcnt(curdir, &st, ret);
1768 dir->d_flags |= DIRF_CNID;
1774 /* ------------------------------
1775 resolve a file id */
1776 int afp_resolveid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1785 u_int16_t vid, bitmap;
1787 static char buffer[12 + MAXPATHLEN + 1];
1788 int len = 12 + MAXPATHLEN + 1;
1793 memcpy(&vid, ibuf, sizeof(vid));
1794 ibuf += sizeof(vid);
1796 if (NULL == ( vol = getvolbyvid( vid )) ) {
1797 return( AFPERR_PARAM);
1800 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1804 memcpy(&id, ibuf, sizeof( id ));
1809 /* some MacOS versions after a catsearch do a *lot* of afp_resolveid with 0 */
1813 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1814 return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
1817 if (NULL == ( dir = dirlookup( vol, id )) ) {
1818 return AFPERR_NOID; /* idem AFPERR_PARAM */
1820 if (movecwd(vol, dir) < 0) {
1824 return AFPERR_ACCESS;
1828 return AFPERR_PARAM;
1832 memset(&path, 0, sizeof(path));
1833 path.u_name = upath;
1834 if ( of_stat(&path) < 0 ) {
1836 /* with nfs and our working directory is deleted */
1837 if (errno == ESTALE) {
1841 if ( errno == ENOENT && !retry) {
1842 /* cnid db is out of sync, reenumerate the directory and update ids */
1843 reenumerate_id(vol, ".", dir);
1851 return AFPERR_ACCESS;
1855 return AFPERR_PARAM;
1859 /* directories are bad */
1860 if (S_ISDIR(path.st.st_mode)) {
1861 /* OS9 and OSX don't return the same error code */
1862 return (afp_version >=30)?AFPERR_NOID:AFPERR_BADTYPE;
1865 memcpy(&bitmap, ibuf, sizeof(bitmap));
1866 bitmap = ntohs( bitmap );
1867 if (NULL == (path.m_name = utompath(vol, upath, cnid, utf8_encoding()))) {
1871 if (AFP_OK != (err = getfilparams(vol, bitmap, &path , curdir,
1872 rbuf + sizeof(bitmap), &buflen))) {
1875 *rbuflen = buflen + sizeof(bitmap);
1876 memcpy(rbuf, ibuf, sizeof(bitmap));
1881 /* ------------------------------ */
1882 int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1892 static char buffer[12 + MAXPATHLEN + 1];
1893 int len = 12 + MAXPATHLEN + 1;
1898 memcpy(&vid, ibuf, sizeof(vid));
1899 ibuf += sizeof(vid);
1901 if (NULL == ( vol = getvolbyvid( vid )) ) {
1902 return( AFPERR_PARAM);
1905 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1909 if (vol->v_flags & AFPVOL_RO)
1910 return AFPERR_VLOCK;
1912 memcpy(&id, ibuf, sizeof( id ));
1916 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1920 if (NULL == ( dir = dirlookup( vol, id )) ) {
1921 return( AFPERR_PARAM );
1925 if ((movecwd(vol, dir) < 0) || (lstat(upath, &st) < 0)) {
1929 return AFPERR_ACCESS;
1934 /* still try to delete the id */
1938 return AFPERR_PARAM;
1941 else if (S_ISDIR(st.st_mode)) /* directories are bad */
1942 return AFPERR_BADTYPE;
1944 if (cnid_delete(vol->v_cdb, fileid)) {
1947 return AFPERR_VLOCK;
1950 return AFPERR_ACCESS;
1952 return AFPERR_PARAM;
1959 /* ------------------------------ */
1960 static struct adouble *find_adouble(struct path *path, struct ofork **of, struct adouble *adp)
1964 if (path->st_errno) {
1965 switch (path->st_errno) {
1967 afp_errno = AFPERR_NOID;
1971 afp_errno = AFPERR_ACCESS;
1974 afp_errno = AFPERR_PARAM;
1979 /* we use file_access both for legacy Mac perm and
1980 * for unix privilege, rename will take care of folder perms
1982 if (file_access(path, OPENACC_WR ) < 0) {
1983 afp_errno = AFPERR_ACCESS;
1987 if ((*of = of_findname(path))) {
1988 /* reuse struct adouble so it won't break locks */
1992 ret = ad_open( path->u_name, ADFLAGS_HF, O_RDONLY, 0, adp);
1994 if ( !ret && ad_reso_fileno(adp) != -1 && !(adp->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
1996 * The user must have the Read & Write privilege for both files in order to use this command.
1998 ad_close(adp, ADFLAGS_HF);
1999 afp_errno = AFPERR_ACCESS;
2006 #define APPLETEMP ".AppleTempXXXXXX"
2008 int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
2010 struct stat srcst, destst;
2012 struct dir *dir, *sdir;
2013 char *spath, temp[17], *p;
2014 char *supath, *upath;
2019 struct adouble *adsp = NULL;
2020 struct adouble *addp = NULL;
2021 struct ofork *s_of = NULL;
2022 struct ofork *d_of = NULL;
2035 memcpy(&vid, ibuf, sizeof(vid));
2036 ibuf += sizeof(vid);
2038 if (NULL == ( vol = getvolbyvid( vid )) ) {
2039 return( AFPERR_PARAM);
2042 if ((vol->v_flags & AFPVOL_RO))
2043 return AFPERR_VLOCK;
2045 /* source and destination dids */
2046 memcpy(&sid, ibuf, sizeof(sid));
2047 ibuf += sizeof(sid);
2048 memcpy(&did, ibuf, sizeof(did));
2049 ibuf += sizeof(did);
2052 if (NULL == (dir = dirlookup( vol, sid )) ) {
2053 return afp_errno; /* was AFPERR_PARAM */
2056 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2057 return get_afp_errno(AFPERR_NOOBJ);
2060 if ( path_isadir(path) ) {
2061 return AFPERR_BADTYPE; /* it's a dir */
2064 /* save some stuff */
2067 spath = obj->oldtmp;
2068 supath = obj->newtmp;
2069 strcpy(spath, path->m_name);
2070 strcpy(supath, path->u_name); /* this is for the cnid changing */
2071 p = absupath( vol, sdir, supath);
2073 /* pathname too long */
2074 return AFPERR_PARAM ;
2077 ad_init(&ads, vol->v_adouble, vol->v_ad_options);
2078 if (!(adsp = find_adouble( path, &s_of, &ads))) {
2082 /* ***** from here we may have resource fork open **** */
2084 /* look for the source cnid. if it doesn't exist, don't worry about
2086 sid = cnid_lookup(vol->v_cdb, &srcst, sdir->d_did, supath,slen = strlen(supath));
2088 if (NULL == ( dir = dirlookup( vol, did )) ) {
2089 err = afp_errno; /* was AFPERR_PARAM */
2090 goto err_exchangefile;
2093 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2094 err = get_afp_errno(AFPERR_NOOBJ);
2095 goto err_exchangefile;
2098 if ( path_isadir(path) ) {
2099 err = AFPERR_BADTYPE;
2100 goto err_exchangefile;
2103 /* FPExchangeFiles is the only call that can return the SameObj
2105 if ((curdir == sdir) && strcmp(spath, path->m_name) == 0) {
2106 err = AFPERR_SAMEOBJ;
2107 goto err_exchangefile;
2110 ad_init(&add, vol->v_adouble, vol->v_ad_options);
2111 if (!(addp = find_adouble( path, &d_of, &add))) {
2113 goto err_exchangefile;
2117 /* they are not on the same device and at least one is open
2118 * FIXME broken for for crossdev and adouble v2
2121 crossdev = (srcst.st_dev != destst.st_dev);
2122 if (/* (d_of || s_of) && */ crossdev) {
2124 goto err_exchangefile;
2127 /* look for destination id. */
2128 upath = path->u_name;
2129 did = cnid_lookup(vol->v_cdb, &destst, curdir->d_did, upath, dlen = strlen(upath));
2131 /* construct a temp name.
2132 * NOTE: the temp file will be in the dest file's directory. it
2133 * will also be inaccessible from AFP. */
2134 memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
2135 if (!mktemp(temp)) {
2137 goto err_exchangefile;
2141 /* FIXME we need to close fork for copy, both s_of and d_of are null */
2142 ad_close(adsp, ADFLAGS_HF);
2143 ad_close(addp, ADFLAGS_HF);
2146 /* now, quickly rename the file. we error if we can't. */
2147 if ((err = renamefile(vol, p, temp, temp, adsp)) != AFP_OK)
2148 goto err_exchangefile;
2149 of_rename(vol, s_of, sdir, spath, curdir, temp);
2151 /* rename destination to source */
2152 if ((err = renamefile(vol, upath, p, spath, addp)) != AFP_OK)
2153 goto err_src_to_tmp;
2154 of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
2156 /* rename temp to destination */
2157 if ((err = renamefile(vol, temp, upath, path->m_name, adsp)) != AFP_OK)
2158 goto err_dest_to_src;
2159 of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
2161 /* id's need switching. src -> dest and dest -> src.
2162 * we need to re-stat() if it was a cross device copy.
2165 cnid_delete(vol->v_cdb, sid);
2168 cnid_delete(vol->v_cdb, did);
2170 if ((did && ( (crossdev && lstat( upath, &srcst) < 0) ||
2171 cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
2173 (sid && ( (crossdev && lstat(p, &destst) < 0) ||
2174 cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
2179 err = AFPERR_ACCESS;
2184 goto err_temp_to_dest;
2187 /* here we need to reopen if crossdev */
2188 if (sid && ad_setid(addp, destst.st_dev, destst.st_ino, sid, sdir->d_did, vol->v_stamp))
2193 if (did && ad_setid(adsp, srcst.st_dev, srcst.st_ino, did, curdir->d_did, vol->v_stamp))
2198 /* change perms, src gets dest perm and vice versa */
2203 LOG(log_error, logtype_afpd, "seteuid failed %s", strerror(errno));
2204 err = AFP_OK; /* ignore error */
2205 goto err_temp_to_dest;
2209 * we need to exchange ACL entries as well
2211 /* exchange_acls(vol, p, upath); */
2216 path->m_name = NULL;
2217 path->u_name = upath;
2219 setfilunixmode(vol, path, destst.st_mode);
2220 setfilowner(vol, destst.st_uid, destst.st_gid, path);
2227 setfilunixmode(vol, path, srcst.st_mode);
2228 setfilowner(vol, srcst.st_uid, srcst.st_gid, path);
2230 if ( setegid(gid) < 0 || seteuid(uid) < 0) {
2231 LOG(log_error, logtype_afpd, "can't seteuid back %s", strerror(errno));
2236 goto err_exchangefile;
2238 /* all this stuff is so that we can unwind a failed operation
2241 /* rename dest to temp */
2242 renamefile(vol, upath, temp, temp, adsp);
2243 of_rename(vol, s_of, curdir, upath, curdir, temp);
2246 /* rename source back to dest */
2247 renamefile(vol, p, upath, path->m_name, addp);
2248 of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
2251 /* rename temp back to source */
2252 renamefile(vol, temp, p, spath, adsp);
2253 of_rename(vol, s_of, curdir, temp, sdir, spath);
2256 if ( !s_of && adsp && ad_meta_fileno(adsp) != -1 ) { /* META */
2257 ad_close(adsp, ADFLAGS_HF);
2259 if ( !d_of && addp && ad_meta_fileno(addp) != -1 ) {/* META */
2260 ad_close(addp, ADFLAGS_HF);