2 * $Id: file.c,v 1.126.2.1 2010-01-02 10:22:32 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));
119 linkflag=htons(FINDERINFO_ISALIAS);
120 memcpy((char *)data + FINDERINFO_FRTYPEOFF,"slnk",4);
121 memcpy((char *)data + FINDERINFO_FRCREATOFF,"rhap",4);
122 *((u_int16_t*)((char *)data+FINDERINFO_FRFLAGOFF))|=linkflag;
125 /** Only enter if no appledouble information and no finder information found. */
126 if (chk_ext && (em = getextmap( upath ))) {
127 memcpy(data, em->em_type, sizeof( em->em_type ));
128 memcpy((char *)data + 4, em->em_creator, sizeof(em->em_creator));
133 /* ---------------------
135 char *set_name(const struct vol *vol, char *data, cnid_t pid, char *name, cnid_t id, u_int32_t utf8)
140 aint = strlen( name );
144 if (utf8_encoding()) {
145 /* but name is an utf8 mac name */
148 /* global static variable... */
150 if (!(u = mtoupath(vol, name, pid, 1)) || !(m = utompath(vol, u, id, 0))) {
159 if (aint > MACFILELEN)
166 if (aint > 255) /* FIXME safeguard, anyway if no ascii char it's game over*/
169 utf8 = vol->v_kTextEncoding;
170 memcpy(data, &utf8, sizeof(utf8));
171 data += sizeof(utf8);
174 memcpy(data, &temp, sizeof(temp));
175 data += sizeof(temp);
178 memcpy( data, src, aint );
188 * FIXME: PDINFO is UTF8 and doesn't need adp
190 #define PARAM_NEED_ADP(b) ((b) & ((1 << FILPBIT_ATTR) |\
191 (1 << FILPBIT_CDATE) |\
192 (1 << FILPBIT_MDATE) |\
193 (1 << FILPBIT_BDATE) |\
194 (1 << FILPBIT_FINFO) |\
195 (1 << FILPBIT_RFLEN) |\
196 (1 << FILPBIT_EXTRFLEN) |\
197 (1 << FILPBIT_PDINFO) |\
198 (1 << FILPBIT_FNUM) |\
199 (1 << FILPBIT_UNIXPR)))
201 /* -------------------------- */
202 u_int32_t get_id(struct vol *vol, struct adouble *adp, const struct stat *st,
203 const cnid_t did, char *upath, const int len)
207 if (vol->v_cdb != NULL) {
208 /* prime aint with what we think is the cnid, set did to zero for
209 catching moved files */
210 aint = ad_getid(adp, st->st_dev, st->st_ino, 0, vol->v_stamp);
212 aint = cnid_add(vol->v_cdb, st, did, upath, len, aint);
213 /* Throw errors if cnid_add fails. */
214 if (aint == CNID_INVALID) {
216 case CNID_ERR_CLOSE: /* the db is closed */
219 LOG(log_error, logtype_afpd, "get_id: Incorrect parameters passed to cnid_add");
220 afp_errno = AFPERR_PARAM;
223 afp_errno = AFPERR_PARAM;
226 afp_errno = AFPERR_MISC;
231 /* update the ressource fork
232 * for a folder adp is always null
234 if (ad_setid(adp, st->st_dev, st->st_ino, aint, did, vol->v_stamp)) {
242 /* -------------------------- */
243 int getmetadata(struct vol *vol,
245 struct path *path, struct dir *dir,
246 char *buf, size_t *buflen, struct adouble *adp)
248 char *data, *l_nameoff = NULL, *upath;
249 char *utf_nameoff = NULL;
254 u_char achar, fdType[4];
260 LOG(log_debug9, logtype_afpd, "begin getmetadata:");
263 upath = path->u_name;
268 if ( ((bitmap & ( (1 << FILPBIT_FINFO)|(1 << FILPBIT_LNAME)|(1 <<FILPBIT_PDINFO) ) ) && !path->m_name)
269 || (bitmap & ( (1 << FILPBIT_LNAME) ) && utf8_encoding()) /* FIXME should be m_name utf8 filename */
270 || (bitmap & (1 << FILPBIT_FNUM))) {
272 id = get_id(vol, adp, st, dir->d_did, upath, strlen(upath));
278 path->m_name = utompath(vol, upath, id, utf8_encoding());
281 while ( bitmap != 0 ) {
282 while (( bitmap & 1 ) == 0 ) {
290 ad_getattr(adp, &ashort);
291 } else if (vol_inv_dots(vol) && *upath == '.') {
292 ashort = htons(ATTRBIT_INVISIBLE);
296 /* FIXME do we want a visual clue if the file is read only
299 accessmode( ".", &ma, dir , NULL);
300 if ((ma.ma_user & AR_UWRITE)) {
301 accessmode( upath, &ma, dir , st);
302 if (!(ma.ma_user & AR_UWRITE)) {
303 ashort |= htons(ATTRBIT_NOWRITE);
307 memcpy(data, &ashort, sizeof( ashort ));
308 data += sizeof( ashort );
312 memcpy(data, &dir->d_did, sizeof( u_int32_t ));
313 data += sizeof( u_int32_t );
317 if (!adp || (ad_getdate(adp, AD_DATE_CREATE, &aint) < 0))
318 aint = AD_DATE_FROM_UNIX(st->st_mtime);
319 memcpy(data, &aint, sizeof( aint ));
320 data += sizeof( aint );
324 if ( adp && (ad_getdate(adp, AD_DATE_MODIFY, &aint) == 0)) {
325 if ((st->st_mtime > AD_DATE_TO_UNIX(aint))) {
326 aint = AD_DATE_FROM_UNIX(st->st_mtime);
329 aint = AD_DATE_FROM_UNIX(st->st_mtime);
331 memcpy(data, &aint, sizeof( int ));
332 data += sizeof( int );
336 if (!adp || (ad_getdate(adp, AD_DATE_BACKUP, &aint) < 0))
337 aint = AD_DATE_START;
338 memcpy(data, &aint, sizeof( int ));
339 data += sizeof( int );
343 get_finderinfo(vol, upath, adp, (char *)data,S_ISLNK(st->st_mode));
344 data += ADEDLEN_FINDERI;
349 data += sizeof( u_int16_t );
353 memset(data, 0, sizeof(u_int16_t));
354 data += sizeof( u_int16_t );
358 memcpy(data, &id, sizeof( id ));
359 data += sizeof( id );
363 if (st->st_size > 0xffffffff)
366 aint = htonl( st->st_size );
367 memcpy(data, &aint, sizeof( aint ));
368 data += sizeof( aint );
373 if (adp->ad_rlen > 0xffffffff)
376 aint = htonl( adp->ad_rlen);
380 memcpy(data, &aint, sizeof( aint ));
381 data += sizeof( aint );
384 /* Current client needs ProDOS info block for this file.
385 Use simple heuristic and let the Mac "type" string tell
386 us what the PD file code should be. Everything gets a
387 subtype of 0x0000 unless the original value was hashed
388 to "pXYZ" when we created it. See IA, Ver 2.
389 <shirsch@adelphia.net> */
390 case FILPBIT_PDINFO :
391 if (afp_version >= 30) { /* UTF8 name */
392 utf8 = kTextEncodingUTF8;
394 data += sizeof( u_int16_t );
396 memcpy(data, &aint, sizeof( aint ));
397 data += sizeof( aint );
401 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
403 if ( memcmp( fdType, "TEXT", 4 ) == 0 ) {
407 else if ( memcmp( fdType, "PSYS", 4 ) == 0 ) {
411 else if ( memcmp( fdType, "PS16", 4 ) == 0 ) {
415 else if ( memcmp( fdType, "BINA", 4 ) == 0 ) {
419 else if ( fdType[0] == 'p' ) {
421 ashort = (fdType[2] * 256) + fdType[3];
435 memcpy(data, &ashort, sizeof( ashort ));
436 data += sizeof( ashort );
437 memset(data, 0, sizeof( ashort ));
438 data += sizeof( ashort );
441 case FILPBIT_EXTDFLEN:
442 aint = htonl(st->st_size >> 32);
443 memcpy(data, &aint, sizeof( aint ));
444 data += sizeof( aint );
445 aint = htonl(st->st_size);
446 memcpy(data, &aint, sizeof( aint ));
447 data += sizeof( aint );
449 case FILPBIT_EXTRFLEN:
452 aint = htonl(adp->ad_rlen >> 32);
453 memcpy(data, &aint, sizeof( aint ));
454 data += sizeof( aint );
456 aint = htonl(adp->ad_rlen);
457 memcpy(data, &aint, sizeof( aint ));
458 data += sizeof( aint );
460 case FILPBIT_UNIXPR :
461 /* accessmode may change st_mode with ACLs */
462 accessmode( upath, &ma, dir , st);
464 aint = htonl(st->st_uid);
465 memcpy( data, &aint, sizeof( aint ));
466 data += sizeof( aint );
467 aint = htonl(st->st_gid);
468 memcpy( data, &aint, sizeof( aint ));
469 data += sizeof( aint );
472 type == slnk indicates an OSX style symlink,
473 we have to add S_IFLNK to the mode, otherwise
474 10.3 clients freak out. */
478 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
479 if ( memcmp( fdType, "slnk", 4 ) == 0 ) {
485 memcpy( data, &aint, sizeof( aint ));
486 data += sizeof( aint );
488 *data++ = ma.ma_user;
489 *data++ = ma.ma_world;
490 *data++ = ma.ma_group;
491 *data++ = ma.ma_owner;
495 return( AFPERR_BITMAP );
501 ashort = htons( data - buf );
502 memcpy(l_nameoff, &ashort, sizeof( ashort ));
503 data = set_name(vol, data, dir->d_did, path->m_name, id, 0);
506 ashort = htons( data - buf );
507 memcpy(utf_nameoff, &ashort, sizeof( ashort ));
508 data = set_name(vol, data, dir->d_did, path->m_name, id, utf8);
510 *buflen = data - buf;
514 /* ----------------------- */
515 int getfilparams(struct vol *vol,
517 struct path *path, struct dir *dir,
518 char *buf, size_t *buflen )
520 struct adouble ad, *adp;
525 LOG(log_debug9, logtype_default, "begin getfilparams:");
528 opened = PARAM_NEED_ADP(bitmap);
533 int flags = (bitmap & (1 << FILPBIT_ATTR))?ADFLAGS_OPENFORKS:0;
535 adp = of_ad(vol, path, &ad);
536 upath = path->u_name;
538 if ( ad_metadata( upath, vol_noadouble(vol) | flags, adp) < 0 ) {
541 LOG(log_error, logtype_afpd, "getfilparams(%s): %s: check resource fork permission?",
542 upath, strerror(errno));
543 return AFPERR_ACCESS;
545 LOG(log_error, logtype_afpd, "getfilparams(%s): bad resource fork", upath);
554 rc = getmetadata(vol, bitmap, path, dir, buf, buflen, adp);
556 ad_close_metadata( adp);
559 LOG(log_debug9, logtype_afpd, "end getfilparams:");
565 /* ----------------------------- */
566 int afp_createfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
568 struct adouble ad, *adp;
571 struct ofork *of = NULL;
573 int creatf, did, openf, retvalue = AFP_OK;
579 creatf = (unsigned char) *ibuf++;
581 memcpy(&vid, ibuf, sizeof( vid ));
582 ibuf += sizeof( vid );
584 if (NULL == ( vol = getvolbyvid( vid )) ) {
585 return( AFPERR_PARAM );
588 if (vol->v_flags & AFPVOL_RO)
591 memcpy(&did, ibuf, sizeof( did));
592 ibuf += sizeof( did );
594 if (NULL == ( dir = dirlookup( vol, did )) ) {
598 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
599 return get_afp_errno(AFPERR_PARAM);
602 if ( *s_path->m_name == '\0' ) {
603 return( AFPERR_BADTYPE );
606 upath = s_path->u_name;
608 /* if upath is deleted we already in trouble anyway */
609 if ((of = of_findname(s_path))) {
612 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
616 /* on a hard create, fail if file exists and is open */
619 openf = O_RDWR|O_CREAT|O_TRUNC;
621 /* on a soft create, if the file is open then ad_open won't fail
622 because open syscall is not called
627 openf = O_RDWR|O_CREAT|O_EXCL;
630 if ( ad_open( upath, vol_noadouble(vol)|ADFLAGS_DF|ADFLAGS_HF|ADFLAGS_NOHF|ADFLAGS_CREATE,
631 openf, 0666, adp) < 0 ) {
635 case ENOENT : /* we were already in 'did folder' so chdir() didn't fail */
636 return ( AFPERR_NOOBJ );
638 return( AFPERR_EXIST );
640 return( AFPERR_ACCESS );
643 return( AFPERR_DFULL );
645 return( AFPERR_PARAM );
648 if ( ad_reso_fileno( adp ) == -1 ) { /* Hard META / HF */
649 /* on noadouble volumes, just creating the data fork is ok */
650 if (vol_noadouble(vol)) {
651 ad_close( adp, ADFLAGS_DF );
652 goto createfile_done;
654 /* FIXME with hard create on an existing file, we already
655 * corrupted the data file.
657 netatalk_unlink( upath );
658 ad_close( adp, ADFLAGS_DF );
659 return AFPERR_ACCESS;
662 path = s_path->m_name;
663 ad_setname(adp, path);
665 ad_close( adp, ADFLAGS_DF|ADFLAGS_HF );
671 if (vol->v_flags & AFPVOL_DROPBOX) {
672 retvalue = matchfile2dirperms(upath, vol, did);
674 #endif /* DROPKLUDGE */
676 setvoltime(obj, vol );
681 int afp_setfilparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
687 u_int16_t vid, bitmap;
692 memcpy(&vid, ibuf, sizeof( vid ));
693 ibuf += sizeof( vid );
694 if (NULL == ( vol = getvolbyvid( vid )) ) {
695 return( AFPERR_PARAM );
698 if (vol->v_flags & AFPVOL_RO)
701 memcpy(&did, ibuf, sizeof( did ));
702 ibuf += sizeof( did );
703 if (NULL == ( dir = dirlookup( vol, did )) ) {
704 return afp_errno; /* was AFPERR_NOOBJ */
707 memcpy(&bitmap, ibuf, sizeof( bitmap ));
708 bitmap = ntohs( bitmap );
709 ibuf += sizeof( bitmap );
711 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
712 return get_afp_errno(AFPERR_PARAM);
715 if (path_isadir(s_path)) {
716 return( AFPERR_BADTYPE ); /* it's a directory */
719 if ( s_path->st_errno != 0 ) {
720 return( AFPERR_NOOBJ );
723 if ((u_long)ibuf & 1 ) {
727 if (AFP_OK == ( rc = setfilparams(vol, s_path, bitmap, ibuf )) ) {
728 setvoltime(obj, vol );
735 * cf AFP3.0.pdf page 252 for change_mdate and change_parent_mdate logic
738 extern struct path Cur_Path;
740 int setfilparams(struct vol *vol,
741 struct path *path, u_int16_t f_bitmap, char *buf )
743 struct adouble ad, *adp;
745 int bit, isad = 1, err = AFP_OK;
747 u_char achar, *fdType, xyy[4]; /* uninitialized, OK 310105 */
748 u_int16_t ashort, bshort;
751 u_int16_t upriv_bit = 0;
755 int change_mdate = 0;
756 int change_parent_mdate = 0;
761 u_int16_t bitmap = f_bitmap;
762 u_int32_t cdate,bdate;
763 u_char finder_buf[32];
766 LOG(log_debug9, logtype_afpd, "begin setfilparams:");
769 adp = of_ad(vol, path, &ad);
770 upath = path->u_name;
772 if (!vol_unix_priv(vol) && check_access(upath, OPENACC_WR ) < 0) {
773 return AFPERR_ACCESS;
776 /* with unix priv maybe we have to change adouble file priv first */
778 while ( bitmap != 0 ) {
779 while (( bitmap & 1 ) == 0 ) {
786 memcpy(&ashort, buf, sizeof( ashort ));
787 buf += sizeof( ashort );
791 memcpy(&cdate, buf, sizeof(cdate));
792 buf += sizeof( cdate );
795 memcpy(&newdate, buf, sizeof( newdate ));
796 buf += sizeof( newdate );
800 memcpy(&bdate, buf, sizeof( bdate));
801 buf += sizeof( bdate );
805 memcpy(finder_buf, buf, 32 );
806 if (memcmp(buf,"slnkrhap",8)==0 && !S_ISLNK(path->st.st_mode)){
811 char buf[PATH_MAX+1];
812 if ((fp=open(path->u_name,O_RDONLY))>=0){
813 if (len=read(fp,buf,PATH_MAX+1)){
814 if (unlink(path->u_name)==0){
816 erc=symlink(buf,path->u_name);
817 lstat(path->u_name,&(path->st));
824 goto setfilparam_done;
829 case FILPBIT_UNIXPR :
830 if (!vol_unix_priv(vol)) {
831 /* this volume doesn't use unix priv */
837 change_parent_mdate = 1;
839 memcpy( &aint, buf, sizeof( aint ));
840 f_uid = ntohl (aint);
841 buf += sizeof( aint );
842 memcpy( &aint, buf, sizeof( aint ));
843 f_gid = ntohl (aint);
844 buf += sizeof( aint );
845 setfilowner(vol, f_uid, f_gid, path);
847 memcpy( &upriv, buf, sizeof( upriv ));
848 buf += sizeof( upriv );
849 upriv = ntohl (upriv);
850 if ((upriv & S_IWUSR)) {
851 setfilunixmode(vol, path, upriv);
858 case FILPBIT_PDINFO :
859 if (afp_version < 30) { /* else it's UTF8 name */
862 /* Keep special case to support crlf translations */
863 if ((unsigned int) achar == 0x04) {
864 fdType = (u_char *)"TEXT";
867 xyy[0] = ( u_char ) 'p';
878 /* break while loop */
887 /* second try with adouble open
889 if ( ad_open_metadata( upath, vol_noadouble(vol), O_CREAT, adp) < 0) {
890 LOG(log_debug, logtype_afpd, "setfilparams: ad_open_metadata error");
892 * For some things, we don't need an adouble header:
893 * - change of modification date
894 * - UNIX privs (Bug-ID #2863424)
896 if ( (f_bitmap & ~(1<<FILPBIT_MDATE | 1<<FILPBIT_UNIXPR))) {
897 LOG(log_debug, logtype_afpd, "setfilparams: need adouble access");
898 return vol_noadouble(vol) ? AFP_OK : AFPERR_ACCESS;
900 LOG(log_debug, logtype_afpd, "setfilparams: no adouble perms, but only FILPBIT_MDATE and/or FILPBIT_UNIXPR");
902 } else if ((ad_get_HF_flags( adp ) & O_CREAT) ) {
903 ad_setname(adp, path->m_name);
908 while ( bitmap != 0 ) {
909 while (( bitmap & 1 ) == 0 ) {
916 ad_getattr(adp, &bshort);
917 if ((bshort & htons(ATTRBIT_INVISIBLE)) !=
918 (ashort & htons(ATTRBIT_INVISIBLE) & htons(ATTRBIT_SETCLR)) )
919 change_parent_mdate = 1;
920 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
921 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
925 ad_setattr(adp, bshort);
928 ad_setdate(adp, AD_DATE_CREATE, cdate);
933 ad_setdate(adp, AD_DATE_BACKUP, bdate);
936 if (default_type( ad_entry( adp, ADEID_FINDERI ))
938 ((em = getextmap( path->m_name )) &&
939 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
940 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
941 || ((em = getdefextmap()) &&
942 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
943 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
945 memcpy(finder_buf, ufinderi, 8 );
947 memcpy(ad_entry( adp, ADEID_FINDERI ), finder_buf, 32 );
949 case FILPBIT_UNIXPR :
951 setfilunixmode(vol, path, upriv);
954 case FILPBIT_PDINFO :
955 if (afp_version < 30) { /* else it's UTF8 name */
956 memcpy(ad_entry( adp, ADEID_FINDERI ), fdType, 4 );
957 memcpy(ad_entry( adp, ADEID_FINDERI ) + 4, "pdos", 4 );
963 goto setfilparam_done;
970 if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
971 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
975 ad_setdate(adp, AD_DATE_MODIFY, newdate);
976 ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
982 ad_close_metadata( adp);
986 if (change_parent_mdate && gettimeofday(&tv, NULL) == 0) {
987 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
988 bitmap = 1<<FILPBIT_MDATE;
989 setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
993 LOG(log_debug9, logtype_afpd, "end setfilparams:");
999 * renamefile and copyfile take the old and new unix pathnames
1000 * and the new mac name.
1002 * src the source path
1003 * dst the dest filename in current dir
1004 * newname the dest mac name
1005 * adp adouble struct of src file, if open, or & zeroed one
1008 int renamefile(const struct vol *vol, char *src, char *dst, char *newname, struct adouble *adp)
1013 LOG(log_debug9, logtype_afpd, "begin renamefile:");
1016 if ( unix_rename( src, dst ) < 0 ) {
1019 return( AFPERR_NOOBJ );
1022 return( AFPERR_ACCESS );
1024 return AFPERR_VLOCK;
1025 case EXDEV : /* Cross device move -- try copy */
1026 /* NOTE: with open file it's an error because after the copy we will
1027 * get two files, it's fixable for our process (eg reopen the new file, get the
1028 * locks, and so on. But it doesn't solve the case with a second process
1030 if (adp->ad_open_forks) {
1031 /* FIXME warning in syslog so admin'd know there's a conflict ?*/
1032 return AFPERR_OLOCK; /* little lie */
1034 if (AFP_OK != ( rc = copyfile(vol, vol, src, dst, newname, NULL )) ) {
1035 /* on error copyfile delete dest */
1038 return deletefile(vol, src, 0);
1040 return( AFPERR_PARAM );
1044 if (vol->vfs->vfs_renamefile(vol, src, dst) < 0 ) {
1048 /* try to undo the data fork rename,
1049 * we know we are on the same device
1052 unix_rename( dst, src );
1053 /* return the first error */
1056 return AFPERR_NOOBJ;
1059 return AFPERR_ACCESS ;
1061 return AFPERR_VLOCK;
1063 return AFPERR_PARAM ;
1068 /* don't care if we can't open the newly renamed ressource fork
1070 if (!ad_open( dst, ADFLAGS_HF, O_RDWR, 0666, adp)) {
1071 ad_setname(adp, newname);
1073 ad_close( adp, ADFLAGS_HF );
1076 LOG(log_debug9, logtype_afpd, "end renamefile:");
1083 convert a Mac long name to an utf8 name,
1085 size_t mtoUTF8(const struct vol *vol, const char *src, size_t srclen, char *dest, size_t destlen)
1089 if ((size_t)-1 == (outlen = convert_string ( vol->v_maccharset, CH_UTF8_MAC, src, srclen, dest, destlen)) ) {
1095 /* ---------------- */
1096 int copy_path_name(const struct vol *vol, char *newname, char *ibuf)
1103 if ( type != 2 && !(afp_version >= 30 && type == 3) ) {
1109 if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
1110 if (afp_version >= 30) {
1111 /* convert it to UTF8
1113 if ((plen = mtoUTF8(vol, ibuf, plen, newname, AFPOBJ_TMPSIZ)) == (size_t)-1)
1117 strncpy( newname, ibuf, plen );
1118 newname[ plen ] = '\0';
1120 if (strlen(newname) != plen) {
1121 /* there's \0 in newname, e.g. it's a pathname not
1129 memcpy(&hint, ibuf, sizeof(hint));
1130 ibuf += sizeof(hint);
1132 memcpy(&len16, ibuf, sizeof(len16));
1133 ibuf += sizeof(len16);
1134 plen = ntohs(len16);
1137 if (plen > AFPOBJ_TMPSIZ) {
1140 strncpy( newname, ibuf, plen );
1141 newname[ plen ] = '\0';
1142 if (strlen(newname) != plen) {
1151 /* -----------------------------------
1153 int afp_copyfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1155 struct vol *s_vol, *d_vol;
1157 char *newname, *p, *upath;
1158 struct path *s_path;
1159 u_int32_t sdid, ddid;
1160 int err, retvalue = AFP_OK;
1161 u_int16_t svid, dvid;
1163 struct adouble ad, *adp;
1169 memcpy(&svid, ibuf, sizeof( svid ));
1170 ibuf += sizeof( svid );
1171 if (NULL == ( s_vol = getvolbyvid( svid )) ) {
1172 return( AFPERR_PARAM );
1175 memcpy(&sdid, ibuf, sizeof( sdid ));
1176 ibuf += sizeof( sdid );
1177 if (NULL == ( dir = dirlookup( s_vol, sdid )) ) {
1181 memcpy(&dvid, ibuf, sizeof( dvid ));
1182 ibuf += sizeof( dvid );
1183 memcpy(&ddid, ibuf, sizeof( ddid ));
1184 ibuf += sizeof( ddid );
1186 if (NULL == ( s_path = cname( s_vol, dir, &ibuf )) ) {
1187 return get_afp_errno(AFPERR_PARAM);
1189 if ( path_isadir(s_path) ) {
1190 return( AFPERR_BADTYPE );
1193 /* don't allow copies when the file is open.
1194 * XXX: the spec only calls for read/deny write access.
1195 * however, copyfile doesn't have any of that info,
1196 * and locks need to stay coherent. as a result,
1197 * we just balk if the file is opened already. */
1199 adp = of_ad(s_vol, s_path, &ad);
1201 if (ad_open(s_path->u_name , ADFLAGS_DF |ADFLAGS_HF | ADFLAGS_NOHF, O_RDONLY, 0, adp) < 0) {
1202 return AFPERR_DENYCONF;
1204 denyreadset = (getforkmode(adp, ADEID_DFORK, AD_FILELOCK_DENY_RD) != 0 ||
1205 getforkmode(adp, ADEID_RFORK, AD_FILELOCK_DENY_RD) != 0 );
1206 ad_close( adp, ADFLAGS_DF |ADFLAGS_HF );
1208 return AFPERR_DENYCONF;
1211 newname = obj->newtmp;
1212 strcpy( newname, s_path->m_name );
1214 p = ctoupath( s_vol, curdir, newname );
1216 return AFPERR_PARAM;
1220 /* FIXME svid != dvid && dvid's user can't read svid */
1222 if (NULL == ( d_vol = getvolbyvid( dvid )) ) {
1223 return( AFPERR_PARAM );
1226 if (d_vol->v_flags & AFPVOL_RO)
1227 return AFPERR_VLOCK;
1229 if (NULL == ( dir = dirlookup( d_vol, ddid )) ) {
1233 if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) {
1234 return get_afp_errno(AFPERR_NOOBJ);
1236 if ( *s_path->m_name != '\0' ) {
1237 path_error(s_path, AFPERR_PARAM);
1240 /* one of the handful of places that knows about the path type */
1241 if (copy_path_name(d_vol, newname, ibuf) < 0) {
1242 return( AFPERR_PARAM );
1244 /* newname is always only a filename so curdir *is* its
1247 if (NULL == (upath = mtoupath(d_vol, newname, curdir->d_did, utf8_encoding()))) {
1248 return( AFPERR_PARAM );
1250 if ( (err = copyfile(s_vol, d_vol, p, upath , newname, adp)) < 0 ) {
1256 if (vol->v_flags & AFPVOL_DROPBOX) {
1257 retvalue=matchfile2dirperms(upath, vol, ddid); /* FIXME sdir or ddid */
1259 #endif /* DROPKLUDGE */
1261 setvoltime(obj, d_vol );
1266 /* ----------------------- */
1267 static int copy_all(const int dfd, const void *buf,
1273 LOG(log_debug9, logtype_afpd, "begin copy_all:");
1276 while (buflen > 0) {
1277 if ((cc = write(dfd, buf, buflen)) < 0) {
1289 LOG(log_debug9, logtype_afpd, "end copy_all:");
1295 /* --------------------------
1296 * copy only the fork data stream
1298 static int copy_fork(int eid, struct adouble *add, struct adouble *ads)
1305 if (eid == ADEID_DFORK) {
1306 sfd = ad_data_fileno(ads);
1307 dfd = ad_data_fileno(add);
1310 sfd = ad_reso_fileno(ads);
1311 dfd = ad_reso_fileno(add);
1314 if ((off_t)-1 == lseek(sfd, ad_getentryoff(ads, eid), SEEK_SET))
1317 if ((off_t)-1 == lseek(dfd, ad_getentryoff(add, eid), SEEK_SET))
1320 #if 0 /* ifdef SENDFILE_FLAVOR_LINUX */
1321 /* doesn't work With 2.6 FIXME, only check for EBADFD ? */
1325 #define BUF 128*1024*1024
1327 if (fstat(sfd, &st) == 0) {
1330 if ( offset >= st.st_size) {
1333 size = (st.st_size -offset > BUF)?BUF:st.st_size -offset;
1334 if ((cc = sys_sendfile(dfd, sfd, &offset, size)) < 0) {
1337 case EINVAL: /* there's no guarantee that all fs support sendfile */
1346 lseek(sfd, offset, SEEK_SET);
1350 if ((cc = read(sfd, filebuf, sizeof(filebuf))) < 0) {
1357 if (!cc || ((err = copy_all(dfd, filebuf, cc)) < 0)) {
1364 /* ----------------------------------
1365 * if newname is NULL (from directory.c) we don't want to copy the resource fork.
1366 * because we are doing it elsewhere.
1367 * currently if newname is NULL then adp is NULL.
1369 int copyfile(const struct vol *s_vol, const struct vol*d_vol,
1370 char *src, char *dst, char *newname, struct adouble *adp)
1372 struct adouble ads, add;
1380 LOG(log_debug9, logtype_afpd, "begin copyfile:");
1384 ad_init(&ads, s_vol->v_adouble, s_vol->v_ad_options);
1387 ad_init(&add, d_vol->v_adouble, d_vol->v_ad_options);
1388 adflags = ADFLAGS_DF;
1390 adflags |= ADFLAGS_HF;
1393 if (ad_open(src , adflags | ADFLAGS_NOHF, O_RDONLY, 0, adp) < 0) {
1398 if (ad_meta_fileno(adp) == -1 && ad_reso_fileno(adp) == -1) { /* META / HF */
1399 /* no resource fork, don't create one for dst file */
1400 adflags &= ~ADFLAGS_HF;
1403 stat_result = fstat(ad_data_fileno(adp), &st); /* saving stat exit code, thus saving us on one more stat later on */
1405 if (stat_result < 0) {
1406 /* unlikely but if fstat fails, the default file mode will be 0666. */
1407 st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
1410 if (ad_open(dst , adflags, O_RDWR|O_CREAT|O_EXCL, st.st_mode, &add) < 0) {
1412 ad_close( adp, adflags );
1413 if (EEXIST != ret_err) {
1414 deletefile(d_vol, dst, 0);
1417 return AFPERR_EXIST;
1421 * XXX if the source and the dest don't use the same resource type it's broken
1423 if (ad_reso_fileno(adp) == -1 || 0 == (err = copy_fork(ADEID_RFORK, &add, adp))){
1424 /* copy the data fork */
1425 if ((err = copy_fork(ADEID_DFORK, &add, adp)) == 0) {
1426 err = d_vol->vfs->vfs_copyfile(d_vol, src, dst);
1434 if (!ret_err && newname && (adflags & ADFLAGS_HF)) {
1435 /* set the new name in the resource fork */
1436 ad_copy_header(&add, adp);
1437 ad_setname(&add, newname);
1440 ad_close( adp, adflags );
1442 if (ad_close( &add, adflags ) <0) {
1447 deletefile(d_vol, dst, 0);
1449 else if (stat_result == 0) {
1450 /* set dest modification date to src date */
1453 ut.actime = ut.modtime = st.st_mtime;
1455 /* FIXME netatalk doesn't use resource fork file date
1456 * but maybe we should set its modtime too.
1461 LOG(log_debug9, logtype_afpd, "end copyfile:");
1465 switch ( ret_err ) {
1471 return AFPERR_DFULL;
1473 return AFPERR_NOOBJ;
1475 return AFPERR_ACCESS;
1477 return AFPERR_VLOCK;
1479 return AFPERR_PARAM;
1483 /* -----------------------------------
1484 vol: not NULL delete cnid entry. then we are in curdir and file is a only filename
1485 checkAttrib: 1 check kFPDeleteInhibitBit (deletfile called by afp_delete)
1487 when deletefile is called we don't have lock on it, file is closed (for us)
1488 untrue if called by renamefile
1490 ad_open always try to open file RDWR first and ad_lock takes care of
1491 WRITE lock on read only file.
1494 static int check_attrib(struct adouble *adp)
1496 u_int16_t bshort = 0;
1498 ad_getattr(adp, &bshort);
1500 * Does kFPDeleteInhibitBit (bit 8) set?
1502 if ((bshort & htons(ATTRBIT_NODELETE))) {
1503 return AFPERR_OLOCK;
1505 if ((bshort & htons(ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
1511 int deletefile(const struct vol *vol, char *file, int checkAttrib)
1514 struct adouble *adp = &ad;
1515 int adflags, err = AFP_OK;
1518 LOG(log_debug9, logtype_afpd, "begin deletefile:");
1521 /* try to open both forks at once */
1522 adflags = ADFLAGS_DF|ADFLAGS_HF;
1524 /* was EACCESS error try to get only metadata */
1525 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
1526 /* we never want to create a resource fork here, we are going to delete it
1527 * moreover sometimes deletefile is called with a no existent file and
1528 * ad_open would create a 0 byte resource fork
1530 if ( ad_metadata( file , ADFLAGS_NOADOUBLE | ADFLAGS_OPENFORKS, &ad) == 0 ) {
1531 ad_close( &ad, adflags );
1532 if ((err = check_attrib(&ad))) {
1539 ad_init(&ad, vol->v_adouble, vol->v_ad_options); /* OK */
1540 if ( ad_open( file, adflags, O_RDONLY, 0, &ad ) < 0 ) {
1543 if (adflags == ADFLAGS_DF)
1544 return AFPERR_NOOBJ;
1546 /* that failed. now try to open just the data fork */
1547 adflags = ADFLAGS_DF;
1551 adp = NULL; /* maybe it's a file with no write mode for us */
1552 break; /* was return AFPERR_ACCESS;*/
1554 return AFPERR_VLOCK;
1556 return( AFPERR_PARAM );
1559 break; /* from the while */
1562 if (adp && (adflags & ADFLAGS_HF) ) {
1563 /* FIXME we have a pb here because we want to know if a file is open
1564 * there's a 'priority inversion' if you can't open the ressource fork RW
1565 * you can delete it if it's open because you can't get a write lock.
1567 * ADLOCK_FILELOCK means the whole ressource fork, not only after the
1570 * FIXME it doesn't work for RFORK open read only and fork open without deny mode
1572 if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
1573 ad_close( &ad, adflags );
1574 return( AFPERR_BUSY );
1578 if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
1581 else if (!(err = vol->vfs->vfs_deletefile(vol, file)) && !(err = netatalk_unlink( file )) ) {
1583 if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file))))
1585 cnid_delete(vol->v_cdb, id);
1589 ad_close( &ad, adflags ); /* ad_close removes locks if any */
1592 LOG(log_debug9, logtype_afpd, "end deletefile:");
1598 /* ------------------------------------ */
1599 /* return a file id */
1600 int afp_createid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1609 struct path *s_path;
1615 memcpy(&vid, ibuf, sizeof(vid));
1616 ibuf += sizeof(vid);
1618 if (NULL == ( vol = getvolbyvid( vid )) ) {
1619 return( AFPERR_PARAM);
1622 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1626 if (vol->v_flags & AFPVOL_RO)
1627 return AFPERR_VLOCK;
1629 memcpy(&did, ibuf, sizeof( did ));
1630 ibuf += sizeof(did);
1632 if (NULL == ( dir = dirlookup( vol, did )) ) {
1633 return afp_errno; /* was AFPERR_PARAM */
1636 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
1637 return get_afp_errno(AFPERR_NOOBJ); /* was AFPERR_PARAM */
1640 if ( path_isadir(s_path) ) {
1641 return( AFPERR_BADTYPE );
1644 upath = s_path->u_name;
1645 switch (s_path->st_errno) {
1647 break; /* success */
1650 return AFPERR_ACCESS;
1652 return AFPERR_NOOBJ;
1654 return AFPERR_PARAM;
1657 if ((id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath)))) {
1658 memcpy(rbuf, &id, sizeof(id));
1659 *rbuflen = sizeof(id);
1660 return AFPERR_EXISTID;
1663 if ((id = get_id(vol, NULL, st, did, upath, len)) != CNID_INVALID) {
1664 memcpy(rbuf, &id, sizeof(id));
1665 *rbuflen = sizeof(id);
1672 /* ------------------------------- */
1678 static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
1681 struct reenum *param = data;
1682 struct vol *vol = param->vol;
1683 cnid_t did = param->did;
1686 if ( lstat(de->d_name, &path.st)<0 )
1689 /* update or add to cnid */
1690 aint = cnid_add(vol->v_cdb, &path.st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */
1692 #if AD_VERSION > AD_VERSION1
1693 if (aint != CNID_INVALID && !S_ISDIR(path.st.st_mode)) {
1694 struct adouble ad, *adp;
1698 path.u_name = de->d_name;
1700 adp = of_ad(vol, &path, &ad);
1702 if ( ad_open_metadata( de->d_name, 0, 0, adp ) < 0 ) {
1705 if (ad_setid(adp, path.st.st_dev, path.st.st_ino, aint, did, vol->v_stamp)) {
1708 ad_close_metadata(adp);
1710 #endif /* AD_VERSION > AD_VERSION1 */
1715 /* --------------------
1716 * Ok the db is out of synch with the dir.
1717 * but if it's a deleted file we don't want to do it again and again.
1720 reenumerate_id(struct vol *vol, char *name, struct dir *dir)
1726 if (vol->v_cdb == NULL) {
1730 /* FIXME use of_statdir ? */
1731 if (lstat(name, &st)) {
1735 if (dirreenumerate(dir, &st)) {
1736 /* we already did it once and the dir haven't been modified */
1741 data.did = dir->d_did;
1742 if ((ret = for_each_dirent(vol, name, reenumerate_loop, (void *)&data)) >= 0) {
1743 setdiroffcnt(curdir, &st, ret);
1744 dir->d_flags |= DIRF_CNID;
1750 /* ------------------------------
1751 resolve a file id */
1752 int afp_resolveid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1761 u_int16_t vid, bitmap;
1763 static char buffer[12 + MAXPATHLEN + 1];
1764 int len = 12 + MAXPATHLEN + 1;
1769 memcpy(&vid, ibuf, sizeof(vid));
1770 ibuf += sizeof(vid);
1772 if (NULL == ( vol = getvolbyvid( vid )) ) {
1773 return( AFPERR_PARAM);
1776 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1780 memcpy(&id, ibuf, sizeof( id ));
1785 /* some MacOS versions after a catsearch do a *lot* of afp_resolveid with 0 */
1789 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1790 return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
1793 if (NULL == ( dir = dirlookup( vol, id )) ) {
1794 return AFPERR_NOID; /* idem AFPERR_PARAM */
1796 if (movecwd(vol, dir) < 0) {
1800 return AFPERR_ACCESS;
1804 return AFPERR_PARAM;
1808 memset(&path, 0, sizeof(path));
1809 path.u_name = upath;
1810 if ( of_stat(&path) < 0 ) {
1812 /* with nfs and our working directory is deleted */
1813 if (errno == ESTALE) {
1817 if ( errno == ENOENT && !retry) {
1818 /* cnid db is out of sync, reenumerate the directory and update ids */
1819 reenumerate_id(vol, ".", dir);
1827 return AFPERR_ACCESS;
1831 return AFPERR_PARAM;
1835 /* directories are bad */
1836 if (S_ISDIR(path.st.st_mode)) {
1837 /* OS9 and OSX don't return the same error code */
1838 return (afp_version >=30)?AFPERR_NOID:AFPERR_BADTYPE;
1841 memcpy(&bitmap, ibuf, sizeof(bitmap));
1842 bitmap = ntohs( bitmap );
1843 if (NULL == (path.m_name = utompath(vol, upath, cnid, utf8_encoding()))) {
1847 if (AFP_OK != (err = getfilparams(vol, bitmap, &path , curdir,
1848 rbuf + sizeof(bitmap), &buflen))) {
1851 *rbuflen = buflen + sizeof(bitmap);
1852 memcpy(rbuf, ibuf, sizeof(bitmap));
1857 /* ------------------------------ */
1858 int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1868 static char buffer[12 + MAXPATHLEN + 1];
1869 int len = 12 + MAXPATHLEN + 1;
1874 memcpy(&vid, ibuf, sizeof(vid));
1875 ibuf += sizeof(vid);
1877 if (NULL == ( vol = getvolbyvid( vid )) ) {
1878 return( AFPERR_PARAM);
1881 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1885 if (vol->v_flags & AFPVOL_RO)
1886 return AFPERR_VLOCK;
1888 memcpy(&id, ibuf, sizeof( id ));
1892 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1896 if (NULL == ( dir = dirlookup( vol, id )) ) {
1897 return( AFPERR_PARAM );
1901 if ((movecwd(vol, dir) < 0) || (lstat(upath, &st) < 0)) {
1905 return AFPERR_ACCESS;
1910 /* still try to delete the id */
1914 return AFPERR_PARAM;
1917 else if (S_ISDIR(st.st_mode)) /* directories are bad */
1918 return AFPERR_BADTYPE;
1920 if (cnid_delete(vol->v_cdb, fileid)) {
1923 return AFPERR_VLOCK;
1926 return AFPERR_ACCESS;
1928 return AFPERR_PARAM;
1935 /* ------------------------------ */
1936 static struct adouble *find_adouble(struct path *path, struct ofork **of, struct adouble *adp)
1940 if (path->st_errno) {
1941 switch (path->st_errno) {
1943 afp_errno = AFPERR_NOID;
1947 afp_errno = AFPERR_ACCESS;
1950 afp_errno = AFPERR_PARAM;
1955 /* we use file_access both for legacy Mac perm and
1956 * for unix privilege, rename will take care of folder perms
1958 if (file_access(path, OPENACC_WR ) < 0) {
1959 afp_errno = AFPERR_ACCESS;
1963 if ((*of = of_findname(path))) {
1964 /* reuse struct adouble so it won't break locks */
1968 ret = ad_open( path->u_name, ADFLAGS_HF, O_RDONLY, 0, adp);
1970 if ( !ret && ad_reso_fileno(adp) != -1 && !(adp->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
1972 * The user must have the Read & Write privilege for both files in order to use this command.
1974 ad_close(adp, ADFLAGS_HF);
1975 afp_errno = AFPERR_ACCESS;
1982 #define APPLETEMP ".AppleTempXXXXXX"
1984 int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1986 struct stat srcst, destst;
1988 struct dir *dir, *sdir;
1989 char *spath, temp[17], *p;
1990 char *supath, *upath;
1995 struct adouble *adsp = NULL;
1996 struct adouble *addp = NULL;
1997 struct ofork *s_of = NULL;
1998 struct ofork *d_of = NULL;
2011 memcpy(&vid, ibuf, sizeof(vid));
2012 ibuf += sizeof(vid);
2014 if (NULL == ( vol = getvolbyvid( vid )) ) {
2015 return( AFPERR_PARAM);
2018 if ((vol->v_flags & AFPVOL_RO))
2019 return AFPERR_VLOCK;
2021 /* source and destination dids */
2022 memcpy(&sid, ibuf, sizeof(sid));
2023 ibuf += sizeof(sid);
2024 memcpy(&did, ibuf, sizeof(did));
2025 ibuf += sizeof(did);
2028 if (NULL == (dir = dirlookup( vol, sid )) ) {
2029 return afp_errno; /* was AFPERR_PARAM */
2032 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2033 return get_afp_errno(AFPERR_NOOBJ);
2036 if ( path_isadir(path) ) {
2037 return AFPERR_BADTYPE; /* it's a dir */
2040 /* save some stuff */
2043 spath = obj->oldtmp;
2044 supath = obj->newtmp;
2045 strcpy(spath, path->m_name);
2046 strcpy(supath, path->u_name); /* this is for the cnid changing */
2047 p = absupath( vol, sdir, supath);
2049 /* pathname too long */
2050 return AFPERR_PARAM ;
2053 ad_init(&ads, vol->v_adouble, vol->v_ad_options);
2054 if (!(adsp = find_adouble( path, &s_of, &ads))) {
2058 /* ***** from here we may have resource fork open **** */
2060 /* look for the source cnid. if it doesn't exist, don't worry about
2062 sid = cnid_lookup(vol->v_cdb, &srcst, sdir->d_did, supath,slen = strlen(supath));
2064 if (NULL == ( dir = dirlookup( vol, did )) ) {
2065 err = afp_errno; /* was AFPERR_PARAM */
2066 goto err_exchangefile;
2069 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2070 err = get_afp_errno(AFPERR_NOOBJ);
2071 goto err_exchangefile;
2074 if ( path_isadir(path) ) {
2075 err = AFPERR_BADTYPE;
2076 goto err_exchangefile;
2079 /* FPExchangeFiles is the only call that can return the SameObj
2081 if ((curdir == sdir) && strcmp(spath, path->m_name) == 0) {
2082 err = AFPERR_SAMEOBJ;
2083 goto err_exchangefile;
2086 ad_init(&add, vol->v_adouble, vol->v_ad_options);
2087 if (!(addp = find_adouble( path, &d_of, &add))) {
2089 goto err_exchangefile;
2093 /* they are not on the same device and at least one is open
2094 * FIXME broken for for crossdev and adouble v2
2097 crossdev = (srcst.st_dev != destst.st_dev);
2098 if (/* (d_of || s_of) && */ crossdev) {
2100 goto err_exchangefile;
2103 /* look for destination id. */
2104 upath = path->u_name;
2105 did = cnid_lookup(vol->v_cdb, &destst, curdir->d_did, upath, dlen = strlen(upath));
2107 /* construct a temp name.
2108 * NOTE: the temp file will be in the dest file's directory. it
2109 * will also be inaccessible from AFP. */
2110 memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
2111 if (!mktemp(temp)) {
2113 goto err_exchangefile;
2117 /* FIXME we need to close fork for copy, both s_of and d_of are null */
2118 ad_close(adsp, ADFLAGS_HF);
2119 ad_close(addp, ADFLAGS_HF);
2122 /* now, quickly rename the file. we error if we can't. */
2123 if ((err = renamefile(vol, p, temp, temp, adsp)) != AFP_OK)
2124 goto err_exchangefile;
2125 of_rename(vol, s_of, sdir, spath, curdir, temp);
2127 /* rename destination to source */
2128 if ((err = renamefile(vol, upath, p, spath, addp)) != AFP_OK)
2129 goto err_src_to_tmp;
2130 of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
2132 /* rename temp to destination */
2133 if ((err = renamefile(vol, temp, upath, path->m_name, adsp)) != AFP_OK)
2134 goto err_dest_to_src;
2135 of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
2137 /* id's need switching. src -> dest and dest -> src.
2138 * we need to re-stat() if it was a cross device copy.
2141 cnid_delete(vol->v_cdb, sid);
2144 cnid_delete(vol->v_cdb, did);
2146 if ((did && ( (crossdev && lstat( upath, &srcst) < 0) ||
2147 cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
2149 (sid && ( (crossdev && lstat(p, &destst) < 0) ||
2150 cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
2155 err = AFPERR_ACCESS;
2160 goto err_temp_to_dest;
2163 /* here we need to reopen if crossdev */
2164 if (sid && ad_setid(addp, destst.st_dev, destst.st_ino, sid, sdir->d_did, vol->v_stamp))
2169 if (did && ad_setid(adsp, srcst.st_dev, srcst.st_ino, did, curdir->d_did, vol->v_stamp))
2174 /* change perms, src gets dest perm and vice versa */
2179 LOG(log_error, logtype_afpd, "seteuid failed %s", strerror(errno));
2180 err = AFP_OK; /* ignore error */
2181 goto err_temp_to_dest;
2185 * we need to exchange ACL entries as well
2187 /* exchange_acls(vol, p, upath); */
2192 path->m_name = NULL;
2193 path->u_name = upath;
2195 setfilunixmode(vol, path, destst.st_mode);
2196 setfilowner(vol, destst.st_uid, destst.st_gid, path);
2203 setfilunixmode(vol, path, srcst.st_mode);
2204 setfilowner(vol, srcst.st_uid, srcst.st_gid, path);
2206 if ( setegid(gid) < 0 || seteuid(uid) < 0) {
2207 LOG(log_error, logtype_afpd, "can't seteuid back %s", strerror(errno));
2212 goto err_exchangefile;
2214 /* all this stuff is so that we can unwind a failed operation
2217 /* rename dest to temp */
2218 renamefile(vol, upath, temp, temp, adsp);
2219 of_rename(vol, s_of, curdir, upath, curdir, temp);
2222 /* rename source back to dest */
2223 renamefile(vol, p, upath, path->m_name, addp);
2224 of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
2227 /* rename temp back to source */
2228 renamefile(vol, temp, p, spath, adsp);
2229 of_rename(vol, s_of, curdir, temp, sdir, spath);
2232 if ( !s_of && adsp && ad_meta_fileno(adsp) != -1 ) { /* META */
2233 ad_close(adsp, ADFLAGS_HF);
2235 if ( !d_of && addp && ad_meta_fileno(addp) != -1 ) {/* META */
2236 ad_close(addp, ADFLAGS_HF);