2 * $Id: file.c,v 1.141 2010/03/12 15:16:49 franklahm Exp $
4 * Copyright (c) 1990,1993 Regents of The University of Michigan.
5 * All Rights Reserved. See COPYRIGHT.
10 #endif /* HAVE_CONFIG_H */
18 #else /* STDC_HEADERS */
22 #endif /* HAVE_STRCHR */
23 char *strchr (), *strrchr ();
26 #define memcpy(d,s,n) bcopy ((s), (d), (n))
27 #define memmove(d,s,n) bcopy ((s), (d), (n))
28 #endif /* ! HAVE_MEMCPY */
29 #endif /* STDC_HEADERS */
33 #include <sys/param.h>
35 #include <atalk/adouble.h>
36 #include <atalk/vfs.h>
37 #include <atalk/logger.h>
38 #include <atalk/afp.h>
39 #include <atalk/util.h>
40 #include <atalk/cnid.h>
41 #include <atalk/unix.h>
43 #include "directory.h"
52 /* the format for the finderinfo fields (from IM: Toolbox Essentials):
53 * field bytes subfield bytes
56 * ioFlFndrInfo 16 -> type 4 type field
57 * creator 4 creator field
58 * flags 2 finder flags:
60 * location 4 location in window
61 * folder 2 window that contains file
63 * ioFlXFndrInfo 16 -> iconID 2 icon id
65 * script 1 script system
67 * commentID 2 comment id
68 * putawayID 4 home directory id
71 const u_char ufinderi[ADEDLEN_FINDERI] = {
72 0, 0, 0, 0, 0, 0, 0, 0,
73 1, 0, 0, 0, 0, 0, 0, 0,
74 0, 0, 0, 0, 0, 0, 0, 0,
75 0, 0, 0, 0, 0, 0, 0, 0
78 static const u_char old_ufinderi[] = {
79 'T', 'E', 'X', 'T', 'U', 'N', 'I', 'X'
82 /* ----------------------
84 static int default_type(void *finder)
86 if (!memcmp(finder, ufinderi, 8) || !memcmp(finder, old_ufinderi, 8))
91 /* FIXME path : unix or mac name ? (for now it's unix name ) */
92 void *get_finderinfo(const struct vol *vol, const char *upath, struct adouble *adp, void *data, int islink)
95 void *ad_finder = NULL;
99 ad_finder = ad_entry(adp, ADEID_FINDERI);
102 memcpy(data, ad_finder, ADEDLEN_FINDERI);
104 if (default_type(ad_finder))
108 memcpy(data, ufinderi, ADEDLEN_FINDERI);
110 if (vol_inv_dots(vol) && *upath == '.') { /* make it invisible */
113 ashort = htons(FINDERINFO_INVISIBLE);
114 memcpy((char *)data + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
120 memcpy(&linkflag, (char *)data + FINDERINFO_FRFLAGOFF, 2);
121 linkflag |= htons(FINDERINFO_ISALIAS);
122 memcpy((char *)data + FINDERINFO_FRFLAGOFF, &linkflag, 2);
123 memcpy((char *)data + FINDERINFO_FRTYPEOFF,"slnk",4);
124 memcpy((char *)data + FINDERINFO_FRCREATOFF,"rhap",4);
128 /** Only enter if no appledouble information and no finder information found. */
129 if (chk_ext && (em = getextmap( upath ))) {
130 memcpy(data, em->em_type, sizeof( em->em_type ));
131 memcpy((char *)data + 4, em->em_creator, sizeof(em->em_creator));
136 /* ---------------------
138 char *set_name(const struct vol *vol, char *data, cnid_t pid, char *name, cnid_t id, u_int32_t utf8)
143 aint = strlen( name );
147 if (utf8_encoding()) {
148 /* but name is an utf8 mac name */
151 /* global static variable... */
153 if (!(u = mtoupath(vol, name, pid, 1)) || !(m = utompath(vol, u, id, 0))) {
162 if (aint > MACFILELEN)
169 if (aint > 255) /* FIXME safeguard, anyway if no ascii char it's game over*/
172 utf8 = vol->v_kTextEncoding;
173 memcpy(data, &utf8, sizeof(utf8));
174 data += sizeof(utf8);
177 memcpy(data, &temp, sizeof(temp));
178 data += sizeof(temp);
181 memcpy( data, src, aint );
191 * FIXME: PDINFO is UTF8 and doesn't need adp
193 #define PARAM_NEED_ADP(b) ((b) & ((1 << FILPBIT_ATTR) |\
194 (1 << FILPBIT_CDATE) |\
195 (1 << FILPBIT_MDATE) |\
196 (1 << FILPBIT_BDATE) |\
197 (1 << FILPBIT_FINFO) |\
198 (1 << FILPBIT_RFLEN) |\
199 (1 << FILPBIT_EXTRFLEN) |\
200 (1 << FILPBIT_PDINFO) |\
201 (1 << FILPBIT_FNUM) |\
202 (1 << FILPBIT_UNIXPR)))
204 /* -------------------------- */
205 u_int32_t get_id(const 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 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));
275 if (id == CNID_INVALID)
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 );
309 LOG(log_debug, logtype_afpd, "metadata('%s'): AFP Attributes: %04x",
310 path->u_name, ntohs(ashort));
314 memcpy(data, &dir->d_did, sizeof( u_int32_t ));
315 data += sizeof( u_int32_t );
316 LOG(log_debug, logtype_afpd, "metadata('%s'): Parent DID: %u",
317 path->u_name, ntohl(dir->d_did));
321 if (!adp || (ad_getdate(adp, AD_DATE_CREATE, &aint) < 0))
322 aint = AD_DATE_FROM_UNIX(st->st_mtime);
323 memcpy(data, &aint, sizeof( aint ));
324 data += sizeof( aint );
328 if ( adp && (ad_getdate(adp, AD_DATE_MODIFY, &aint) == 0)) {
329 if ((st->st_mtime > AD_DATE_TO_UNIX(aint))) {
330 aint = AD_DATE_FROM_UNIX(st->st_mtime);
333 aint = AD_DATE_FROM_UNIX(st->st_mtime);
335 memcpy(data, &aint, sizeof( int ));
336 data += sizeof( int );
340 if (!adp || (ad_getdate(adp, AD_DATE_BACKUP, &aint) < 0))
341 aint = AD_DATE_START;
342 memcpy(data, &aint, sizeof( int ));
343 data += sizeof( int );
347 get_finderinfo(vol, upath, adp, (char *)data,S_ISLNK(st->st_mode));
348 data += ADEDLEN_FINDERI;
353 data += sizeof( u_int16_t );
357 memset(data, 0, sizeof(u_int16_t));
358 data += sizeof( u_int16_t );
362 memcpy(data, &id, sizeof( id ));
363 data += sizeof( id );
364 LOG(log_debug, logtype_afpd, "metadata('%s'): CNID: %u",
365 path->u_name, ntohl(id));
369 if (st->st_size > 0xffffffff)
372 aint = htonl( st->st_size );
373 memcpy(data, &aint, sizeof( aint ));
374 data += sizeof( aint );
379 if (adp->ad_rlen > 0xffffffff)
382 aint = htonl( adp->ad_rlen);
386 memcpy(data, &aint, sizeof( aint ));
387 data += sizeof( aint );
390 /* Current client needs ProDOS info block for this file.
391 Use simple heuristic and let the Mac "type" string tell
392 us what the PD file code should be. Everything gets a
393 subtype of 0x0000 unless the original value was hashed
394 to "pXYZ" when we created it. See IA, Ver 2.
395 <shirsch@adelphia.net> */
396 case FILPBIT_PDINFO :
397 if (afp_version >= 30) { /* UTF8 name */
398 utf8 = kTextEncodingUTF8;
400 data += sizeof( u_int16_t );
402 memcpy(data, &aint, sizeof( aint ));
403 data += sizeof( aint );
407 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
409 if ( memcmp( fdType, "TEXT", 4 ) == 0 ) {
413 else if ( memcmp( fdType, "PSYS", 4 ) == 0 ) {
417 else if ( memcmp( fdType, "PS16", 4 ) == 0 ) {
421 else if ( memcmp( fdType, "BINA", 4 ) == 0 ) {
425 else if ( fdType[0] == 'p' ) {
427 ashort = (fdType[2] * 256) + fdType[3];
441 memcpy(data, &ashort, sizeof( ashort ));
442 data += sizeof( ashort );
443 memset(data, 0, sizeof( ashort ));
444 data += sizeof( ashort );
447 case FILPBIT_EXTDFLEN:
448 aint = htonl(st->st_size >> 32);
449 memcpy(data, &aint, sizeof( aint ));
450 data += sizeof( aint );
451 aint = htonl(st->st_size);
452 memcpy(data, &aint, sizeof( aint ));
453 data += sizeof( aint );
455 case FILPBIT_EXTRFLEN:
458 aint = htonl(adp->ad_rlen >> 32);
459 memcpy(data, &aint, sizeof( aint ));
460 data += sizeof( aint );
462 aint = htonl(adp->ad_rlen);
463 memcpy(data, &aint, sizeof( aint ));
464 data += sizeof( aint );
466 case FILPBIT_UNIXPR :
467 /* accessmode may change st_mode with ACLs */
468 accessmode( upath, &ma, dir , st);
470 aint = htonl(st->st_uid);
471 memcpy( data, &aint, sizeof( aint ));
472 data += sizeof( aint );
473 aint = htonl(st->st_gid);
474 memcpy( data, &aint, sizeof( aint ));
475 data += sizeof( aint );
478 type == slnk indicates an OSX style symlink,
479 we have to add S_IFLNK to the mode, otherwise
480 10.3 clients freak out. */
484 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
485 if ( memcmp( fdType, "slnk", 4 ) == 0 ) {
491 memcpy( data, &aint, sizeof( aint ));
492 data += sizeof( aint );
494 *data++ = ma.ma_user;
495 *data++ = ma.ma_world;
496 *data++ = ma.ma_group;
497 *data++ = ma.ma_owner;
501 return( AFPERR_BITMAP );
507 ashort = htons( data - buf );
508 memcpy(l_nameoff, &ashort, sizeof( ashort ));
509 data = set_name(vol, data, dir->d_did, path->m_name, id, 0);
512 ashort = htons( data - buf );
513 memcpy(utf_nameoff, &ashort, sizeof( ashort ));
514 data = set_name(vol, data, dir->d_did, path->m_name, id, utf8);
516 *buflen = data - buf;
520 /* ----------------------- */
521 int getfilparams(struct vol *vol,
523 struct path *path, struct dir *dir,
524 char *buf, size_t *buflen )
526 struct adouble ad, *adp;
530 opened = PARAM_NEED_ADP(bitmap);
535 int flags = (bitmap & (1 << FILPBIT_ATTR))?ADFLAGS_OPENFORKS:0;
537 adp = of_ad(vol, path, &ad);
538 upath = path->u_name;
540 if ( ad_metadata( upath, flags|ADFLAGS_CREATE, adp) < 0 ) {
543 LOG(log_error, logtype_afpd, "getfilparams(%s): %s: check resource fork permission?",
544 upath, strerror(errno));
545 return AFPERR_ACCESS;
547 LOG(log_error, logtype_afpd, "getfilparams(%s): bad resource fork", upath);
556 rc = getmetadata(vol, bitmap, path, dir, buf, buflen, adp);
558 ad_close_metadata( adp);
564 /* ----------------------------- */
565 int afp_createfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
567 struct adouble ad, *adp;
570 struct ofork *of = NULL;
572 int creatf, did, openf, retvalue = AFP_OK;
578 creatf = (unsigned char) *ibuf++;
580 memcpy(&vid, ibuf, sizeof( vid ));
581 ibuf += sizeof( vid );
583 if (NULL == ( vol = getvolbyvid( vid )) ) {
584 return( AFPERR_PARAM );
587 if (vol->v_flags & AFPVOL_RO)
590 memcpy(&did, ibuf, sizeof( did));
591 ibuf += sizeof( did );
593 if (NULL == ( dir = dirlookup( vol, did )) ) {
597 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
598 return get_afp_errno(AFPERR_PARAM);
601 if ( *s_path->m_name == '\0' ) {
602 return( AFPERR_BADTYPE );
605 upath = s_path->u_name;
607 /* if upath is deleted we already in trouble anyway */
608 if ((of = of_findname(s_path))) {
611 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
615 /* on a hard create, fail if file exists and is open */
618 openf = O_RDWR|O_CREAT|O_TRUNC;
620 /* on a soft create, if the file is open then ad_open won't fail
621 because open syscall is not called
626 openf = O_RDWR|O_CREAT|O_EXCL;
629 if ( ad_open( upath, ADFLAGS_DF|ADFLAGS_HF|ADFLAGS_NOHF|ADFLAGS_CREATE,
630 openf, 0666, adp) < 0 ) {
634 case ENOENT : /* we were already in 'did folder' so chdir() didn't fail */
635 return ( AFPERR_NOOBJ );
637 return( AFPERR_EXIST );
639 return( AFPERR_ACCESS );
642 return( AFPERR_DFULL );
644 return( AFPERR_PARAM );
647 if ( ad_reso_fileno( adp ) == -1 ) { /* Hard META / HF */
648 /* on noadouble volumes, just creating the data fork is ok */
649 if (vol_noadouble(vol)) {
650 ad_close( adp, ADFLAGS_DF );
651 goto createfile_done;
653 /* FIXME with hard create on an existing file, we already
654 * corrupted the data file.
656 netatalk_unlink( upath );
657 ad_close( adp, ADFLAGS_DF );
658 return AFPERR_ACCESS;
661 path = s_path->m_name;
662 ad_setname(adp, path);
664 ad_close( adp, ADFLAGS_DF|ADFLAGS_HF );
670 if (vol->v_flags & AFPVOL_DROPBOX) {
671 retvalue = matchfile2dirperms(upath, vol, did);
673 #endif /* DROPKLUDGE */
675 setvoltime(obj, vol );
680 int afp_setfilparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
686 u_int16_t vid, bitmap;
691 memcpy(&vid, ibuf, sizeof( vid ));
692 ibuf += sizeof( vid );
693 if (NULL == ( vol = getvolbyvid( vid )) ) {
694 return( AFPERR_PARAM );
697 if (vol->v_flags & AFPVOL_RO)
700 memcpy(&did, ibuf, sizeof( did ));
701 ibuf += sizeof( did );
702 if (NULL == ( dir = dirlookup( vol, did )) ) {
703 return afp_errno; /* was AFPERR_NOOBJ */
706 memcpy(&bitmap, ibuf, sizeof( bitmap ));
707 bitmap = ntohs( bitmap );
708 ibuf += sizeof( bitmap );
710 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
711 return get_afp_errno(AFPERR_PARAM);
714 if (path_isadir(s_path)) {
715 return( AFPERR_BADTYPE ); /* it's a directory */
718 if ( s_path->st_errno != 0 ) {
719 return( AFPERR_NOOBJ );
722 if ((u_long)ibuf & 1 ) {
726 if (AFP_OK == ( rc = setfilparams(vol, s_path, bitmap, ibuf )) ) {
727 setvoltime(obj, vol );
734 * cf AFP3.0.pdf page 252 for change_mdate and change_parent_mdate logic
737 extern struct path Cur_Path;
739 int setfilparams(struct vol *vol,
740 struct path *path, u_int16_t f_bitmap, char *buf )
742 struct adouble ad, *adp;
744 int bit, isad = 1, err = AFP_OK;
746 u_char achar, *fdType, xyy[4]; /* uninitialized, OK 310105 */
747 u_int16_t ashort, bshort, oshort;
750 u_int16_t upriv_bit = 0;
754 int change_mdate = 0;
755 int change_parent_mdate = 0;
760 u_int16_t bitmap = f_bitmap;
761 u_int32_t cdate,bdate;
762 u_char finder_buf[32];
765 LOG(log_debug9, logtype_afpd, "begin setfilparams:");
768 adp = of_ad(vol, path, &ad);
769 upath = path->u_name;
771 if (!vol_unix_priv(vol) && check_access(upath, OPENACC_WR ) < 0) {
772 return AFPERR_ACCESS;
775 /* with unix priv maybe we have to change adouble file priv first */
777 while ( bitmap != 0 ) {
778 while (( bitmap & 1 ) == 0 ) {
785 memcpy(&ashort, buf, sizeof( ashort ));
786 buf += sizeof( ashort );
790 memcpy(&cdate, buf, sizeof(cdate));
791 buf += sizeof( cdate );
794 memcpy(&newdate, buf, sizeof( newdate ));
795 buf += sizeof( newdate );
799 memcpy(&bdate, buf, sizeof( bdate));
800 buf += sizeof( bdate );
804 memcpy(finder_buf, buf, 32 );
805 if (memcmp(buf,"slnkrhap",8)==0 && !S_ISLNK(path->st.st_mode)){
810 char buf[PATH_MAX+1];
811 if ((fp=open(path->u_name,O_RDONLY))>=0){
812 if ((len=read(fp,buf,PATH_MAX+1))){
813 if (unlink(path->u_name)==0){
815 erc = symlink(buf, path->u_name);
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, 0, 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 (!vol_noadouble(vol) && (f_bitmap & ~(1<<FILPBIT_MDATE | 1<<FILPBIT_UNIXPR))) {
897 LOG(log_debug, logtype_afpd, "setfilparams: need adouble access");
898 return 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);
918 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
919 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
923 if ((bshort & htons(ATTRBIT_INVISIBLE)) != (oshort & htons(ATTRBIT_INVISIBLE)))
924 change_parent_mdate = 1;
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 * sdir_fd source dir fd to which src path is relative (for openat et al semantics)
1003 * passing -1 means this is not used, src path is a full path
1004 * src the source path
1005 * dst the dest filename in current dir
1006 * newname the dest mac name
1007 * adp adouble struct of src file, if open, or & zeroed one
1010 int renamefile(const struct vol *vol, int sdir_fd, char *src, char *dst, char *newname, struct adouble *adp)
1014 if ( unix_rename( sdir_fd, src, -1, dst ) < 0 ) {
1017 return( AFPERR_NOOBJ );
1020 return( AFPERR_ACCESS );
1022 return AFPERR_VLOCK;
1023 case EXDEV : /* Cross device move -- try copy */
1024 /* NOTE: with open file it's an error because after the copy we will
1025 * get two files, it's fixable for our process (eg reopen the new file, get the
1026 * locks, and so on. But it doesn't solve the case with a second process
1028 if (adp->ad_open_forks) {
1029 /* FIXME warning in syslog so admin'd know there's a conflict ?*/
1030 return AFPERR_OLOCK; /* little lie */
1032 if (AFP_OK != ( rc = copyfile(vol, vol, sdir_fd, src, dst, newname, NULL )) ) {
1033 /* on error copyfile delete dest */
1036 return deletefile(vol, sdir_fd, src, 0);
1038 return( AFPERR_PARAM );
1042 if (vol->vfs->vfs_renamefile(vol, sdir_fd, src, dst) < 0 ) {
1046 /* try to undo the data fork rename,
1047 * we know we are on the same device
1050 unix_rename(-1, dst, sdir_fd, src );
1051 /* return the first error */
1054 return AFPERR_NOOBJ;
1057 return AFPERR_ACCESS ;
1059 return AFPERR_VLOCK;
1061 return AFPERR_PARAM ;
1066 /* don't care if we can't open the newly renamed ressource fork
1068 if (!ad_open( dst, ADFLAGS_HF, O_RDWR, 0666, adp)) {
1069 ad_setname(adp, newname);
1071 ad_close( adp, ADFLAGS_HF );
1078 convert a Mac long name to an utf8 name,
1080 size_t mtoUTF8(const struct vol *vol, const char *src, size_t srclen, char *dest, size_t destlen)
1084 if ((size_t)-1 == (outlen = convert_string ( vol->v_maccharset, CH_UTF8_MAC, src, srclen, dest, destlen)) ) {
1090 /* ---------------- */
1091 int copy_path_name(const struct vol *vol, char *newname, char *ibuf)
1098 if ( type != 2 && !(afp_version >= 30 && type == 3) ) {
1104 if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
1105 if (afp_version >= 30) {
1106 /* convert it to UTF8
1108 if ((plen = mtoUTF8(vol, ibuf, plen, newname, AFPOBJ_TMPSIZ)) == (size_t)-1)
1112 strncpy( newname, ibuf, plen );
1113 newname[ plen ] = '\0';
1115 if (strlen(newname) != plen) {
1116 /* there's \0 in newname, e.g. it's a pathname not
1124 memcpy(&hint, ibuf, sizeof(hint));
1125 ibuf += sizeof(hint);
1127 memcpy(&len16, ibuf, sizeof(len16));
1128 ibuf += sizeof(len16);
1129 plen = ntohs(len16);
1132 if (plen > AFPOBJ_TMPSIZ) {
1135 strncpy( newname, ibuf, plen );
1136 newname[ plen ] = '\0';
1137 if (strlen(newname) != plen) {
1146 /* -----------------------------------
1148 int afp_copyfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1150 struct vol *s_vol, *d_vol;
1152 char *newname, *p, *upath;
1153 struct path *s_path;
1154 u_int32_t sdid, ddid;
1155 int err, retvalue = AFP_OK;
1156 u_int16_t svid, dvid;
1158 struct adouble ad, *adp;
1164 memcpy(&svid, ibuf, sizeof( svid ));
1165 ibuf += sizeof( svid );
1166 if (NULL == ( s_vol = getvolbyvid( svid )) ) {
1167 return( AFPERR_PARAM );
1170 memcpy(&sdid, ibuf, sizeof( sdid ));
1171 ibuf += sizeof( sdid );
1172 if (NULL == ( dir = dirlookup( s_vol, sdid )) ) {
1176 memcpy(&dvid, ibuf, sizeof( dvid ));
1177 ibuf += sizeof( dvid );
1178 memcpy(&ddid, ibuf, sizeof( ddid ));
1179 ibuf += sizeof( ddid );
1181 if (NULL == ( s_path = cname( s_vol, dir, &ibuf )) ) {
1182 return get_afp_errno(AFPERR_PARAM);
1184 if ( path_isadir(s_path) ) {
1185 return( AFPERR_BADTYPE );
1188 /* don't allow copies when the file is open.
1189 * XXX: the spec only calls for read/deny write access.
1190 * however, copyfile doesn't have any of that info,
1191 * and locks need to stay coherent. as a result,
1192 * we just balk if the file is opened already. */
1194 adp = of_ad(s_vol, s_path, &ad);
1196 if (ad_open(s_path->u_name , ADFLAGS_DF |ADFLAGS_HF | ADFLAGS_NOHF, O_RDONLY, 0, adp) < 0) {
1197 return AFPERR_DENYCONF;
1199 denyreadset = (getforkmode(adp, ADEID_DFORK, AD_FILELOCK_DENY_RD) != 0 ||
1200 getforkmode(adp, ADEID_RFORK, AD_FILELOCK_DENY_RD) != 0 );
1203 retvalue = AFPERR_DENYCONF;
1207 newname = obj->newtmp;
1208 strcpy( newname, s_path->m_name );
1210 p = ctoupath( s_vol, curdir, newname );
1212 retvalue = AFPERR_PARAM;
1217 /* FIXME svid != dvid && dvid's user can't read svid */
1219 if (NULL == ( d_vol = getvolbyvid( dvid )) ) {
1220 retvalue = AFPERR_PARAM;
1224 if (d_vol->v_flags & AFPVOL_RO) {
1225 retvalue = AFPERR_VLOCK;
1229 if (NULL == ( dir = dirlookup( d_vol, ddid )) ) {
1230 retvalue = afp_errno;
1234 if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) {
1235 retvalue = get_afp_errno(AFPERR_NOOBJ);
1239 if ( *s_path->m_name != '\0' ) {
1240 retvalue =path_error(s_path, AFPERR_NOOBJ);
1244 /* one of the handful of places that knows about the path type */
1245 if (copy_path_name(d_vol, newname, ibuf) < 0) {
1246 retvalue = AFPERR_PARAM;
1249 /* newname is always only a filename so curdir *is* its
1252 if (NULL == (upath = mtoupath(d_vol, newname, curdir->d_did, utf8_encoding()))) {
1253 retvalue =AFPERR_PARAM;
1257 if ( (err = copyfile(s_vol, d_vol, -1, p, upath , newname, adp)) < 0 ) {
1264 if (vol->v_flags & AFPVOL_DROPBOX) {
1265 retvalue=matchfile2dirperms(upath, vol, ddid); /* FIXME sdir or ddid */
1267 #endif /* DROPKLUDGE */
1269 setvoltime(obj, d_vol );
1272 ad_close( adp, ADFLAGS_DF |ADFLAGS_HF );
1276 /* ----------------------- */
1277 static int copy_all(const int dfd, const void *buf,
1283 LOG(log_debug9, logtype_afpd, "begin copy_all:");
1286 while (buflen > 0) {
1287 if ((cc = write(dfd, buf, buflen)) < 0) {
1299 LOG(log_debug9, logtype_afpd, "end copy_all:");
1305 /* --------------------------
1306 * copy only the fork data stream
1308 static int copy_fork(int eid, struct adouble *add, struct adouble *ads)
1315 if (eid == ADEID_DFORK) {
1316 sfd = ad_data_fileno(ads);
1317 dfd = ad_data_fileno(add);
1320 sfd = ad_reso_fileno(ads);
1321 dfd = ad_reso_fileno(add);
1324 if ((off_t)-1 == lseek(sfd, ad_getentryoff(ads, eid), SEEK_SET))
1327 if ((off_t)-1 == lseek(dfd, ad_getentryoff(add, eid), SEEK_SET))
1330 #if 0 /* ifdef SENDFILE_FLAVOR_LINUX */
1331 /* doesn't work With 2.6 FIXME, only check for EBADFD ? */
1335 #define BUF 128*1024*1024
1337 if (fstat(sfd, &st) == 0) {
1340 if ( offset >= st.st_size) {
1343 size = (st.st_size -offset > BUF)?BUF:st.st_size -offset;
1344 if ((cc = sys_sendfile(dfd, sfd, &offset, size)) < 0) {
1347 case EINVAL: /* there's no guarantee that all fs support sendfile */
1356 lseek(sfd, offset, SEEK_SET);
1360 if ((cc = read(sfd, filebuf, sizeof(filebuf))) < 0) {
1367 if (!cc || ((err = copy_all(dfd, filebuf, cc)) < 0)) {
1374 /* ----------------------------------
1375 * if newname is NULL (from directory.c) we don't want to copy the resource fork.
1376 * because we are doing it elsewhere.
1377 * currently if newname is NULL then adp is NULL.
1379 int copyfile(const struct vol *s_vol,
1380 const struct vol *d_vol,
1385 struct adouble *adp)
1387 struct adouble ads, add;
1394 LOG(log_debug, logtype_afpd, "copyfile(sfd:%d,s:'%s',d:'%s',n:'%s')",
1395 sfd, src, dst, newname);
1398 ad_init(&ads, s_vol->v_adouble, s_vol->v_ad_options);
1402 adflags = ADFLAGS_DF;
1404 adflags |= ADFLAGS_HF;
1407 if (ad_openat(sfd, src, adflags | ADFLAGS_NOHF, O_RDONLY, 0, adp) < 0) {
1412 if (ad_meta_fileno(adp) == -1 && ad_reso_fileno(adp) == -1) { /* META / HF */
1413 /* no resource fork, don't create one for dst file */
1414 adflags &= ~ADFLAGS_HF;
1417 stat_result = fstat(ad_data_fileno(adp), &st); /* saving stat exit code, thus saving us on one more stat later on */
1419 if (stat_result < 0) {
1420 /* unlikely but if fstat fails, the default file mode will be 0666. */
1421 st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
1424 ad_init(&add, d_vol->v_adouble, d_vol->v_ad_options);
1425 if (ad_open(dst , adflags, O_RDWR|O_CREAT|O_EXCL, st.st_mode, &add) < 0) {
1427 ad_close( adp, adflags );
1428 if (EEXIST != ret_err) {
1429 deletefile(d_vol, -1, dst, 0);
1432 return AFPERR_EXIST;
1436 * XXX if the source and the dest don't use the same resource type it's broken
1438 if (ad_reso_fileno(adp) == -1 || 0 == (err = copy_fork(ADEID_RFORK, &add, adp))){
1439 /* copy the data fork */
1440 if ((err = copy_fork(ADEID_DFORK, &add, adp)) == 0) {
1441 err = d_vol->vfs->vfs_copyfile(d_vol, sfd, src, dst);
1449 if (!ret_err && newname && (adflags & ADFLAGS_HF)) {
1450 /* set the new name in the resource fork */
1451 ad_copy_header(&add, adp);
1452 ad_setname(&add, newname);
1455 ad_close( adp, adflags );
1457 if (ad_close( &add, adflags ) <0) {
1462 deletefile(d_vol, -1, dst, 0);
1464 else if (stat_result == 0) {
1465 /* set dest modification date to src date */
1468 ut.actime = ut.modtime = st.st_mtime;
1470 /* FIXME netatalk doesn't use resource fork file date
1471 * but maybe we should set its modtime too.
1476 switch ( ret_err ) {
1482 return AFPERR_DFULL;
1484 return AFPERR_NOOBJ;
1486 return AFPERR_ACCESS;
1488 return AFPERR_VLOCK;
1490 return AFPERR_PARAM;
1494 /* -----------------------------------
1495 vol: not NULL delete cnid entry. then we are in curdir and file is a only filename
1496 checkAttrib: 1 check kFPDeleteInhibitBit (deletfile called by afp_delete)
1498 when deletefile is called we don't have lock on it, file is closed (for us)
1499 untrue if called by renamefile
1501 ad_open always try to open file RDWR first and ad_lock takes care of
1502 WRITE lock on read only file.
1505 static int check_attrib(struct adouble *adp)
1507 u_int16_t bshort = 0;
1509 ad_getattr(adp, &bshort);
1511 * Does kFPDeleteInhibitBit (bit 8) set?
1513 if ((bshort & htons(ATTRBIT_NODELETE))) {
1514 return AFPERR_OLOCK;
1516 if ((bshort & htons(ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
1522 * dirfd can be used for unlinkat semantics
1524 int deletefile(const struct vol *vol, int dirfd, char *file, int checkAttrib)
1527 struct adouble *adp = NULL;
1528 int adflags, err = AFP_OK;
1531 LOG(log_debug, logtype_afpd, "deletefile('%s')", file);
1533 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
1535 /* was EACCESS error try to get only metadata */
1536 /* we never want to create a resource fork here, we are going to delete it
1537 * moreover sometimes deletefile is called with a no existent file and
1538 * ad_open would create a 0 byte resource fork
1540 if ( ad_metadataat(dirfd, file, ADFLAGS_OPENFORKS, &ad) == 0 ) {
1541 if ((err = check_attrib(&ad))) {
1542 ad_close_metadata(&ad);
1549 /* try to open both forks at once */
1550 adflags = ADFLAGS_DF;
1551 if ( ad_openat(dirfd, file, adflags |ADFLAGS_HF|ADFLAGS_NOHF, O_RDONLY, 0, &ad ) < 0 ) {
1556 case EACCES: /* maybe it's a file with no write mode for us */
1557 break; /* was return AFPERR_ACCESS;*/
1570 if ( adp && ad_reso_fileno( adp ) != -1 ) { /* there's a resource fork */
1571 adflags |= ADFLAGS_HF;
1572 /* FIXME we have a pb here because we want to know if a file is open
1573 * there's a 'priority inversion' if you can't open the ressource fork RW
1574 * you can delete it if it's open because you can't get a write lock.
1576 * ADLOCK_FILELOCK means the whole ressource fork, not only after the
1579 * FIXME it doesn't work for RFORK open read only and fork open without deny mode
1581 if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
1587 if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
1589 } else if (!(err = vol->vfs->vfs_deletefile(vol, dirfd, file)) && !(err = netatalk_unlinkat(dirfd, file )) ) {
1591 if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file)))) {
1592 cnid_delete(vol->v_cdb, id);
1598 ad_close_metadata(&ad);
1601 ad_close( &ad, adflags ); /* ad_close removes locks if any */
1606 /* ------------------------------------ */
1607 /* return a file id */
1608 int afp_createid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1617 struct path *s_path;
1623 memcpy(&vid, ibuf, sizeof(vid));
1624 ibuf += sizeof(vid);
1626 if (NULL == ( vol = getvolbyvid( vid )) ) {
1627 return( AFPERR_PARAM);
1630 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1634 if (vol->v_flags & AFPVOL_RO)
1635 return AFPERR_VLOCK;
1637 memcpy(&did, ibuf, sizeof( did ));
1638 ibuf += sizeof(did);
1640 if (NULL == ( dir = dirlookup( vol, did )) ) {
1641 return afp_errno; /* was AFPERR_PARAM */
1644 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
1645 return get_afp_errno(AFPERR_NOOBJ); /* was AFPERR_PARAM */
1648 if ( path_isadir(s_path) ) {
1649 return( AFPERR_BADTYPE );
1652 upath = s_path->u_name;
1653 switch (s_path->st_errno) {
1655 break; /* success */
1658 return AFPERR_ACCESS;
1660 return AFPERR_NOOBJ;
1662 return AFPERR_PARAM;
1665 if ((id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath)))) {
1666 memcpy(rbuf, &id, sizeof(id));
1667 *rbuflen = sizeof(id);
1668 return AFPERR_EXISTID;
1671 if ((id = get_id(vol, NULL, st, did, upath, len)) != CNID_INVALID) {
1672 memcpy(rbuf, &id, sizeof(id));
1673 *rbuflen = sizeof(id);
1680 /* ------------------------------- */
1686 static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
1689 struct reenum *param = data;
1690 struct vol *vol = param->vol;
1691 cnid_t did = param->did;
1694 if ( lstat(de->d_name, &path.st)<0 )
1697 /* update or add to cnid */
1698 aint = cnid_add(vol->v_cdb, &path.st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */
1700 #if AD_VERSION > AD_VERSION1
1701 if (aint != CNID_INVALID && !S_ISDIR(path.st.st_mode)) {
1702 struct adouble ad, *adp;
1706 path.u_name = de->d_name;
1708 adp = of_ad(vol, &path, &ad);
1710 if ( ad_open_metadata( de->d_name, 0, 0, adp ) < 0 ) {
1713 if (ad_setid(adp, path.st.st_dev, path.st.st_ino, aint, did, vol->v_stamp)) {
1716 ad_close_metadata(adp);
1718 #endif /* AD_VERSION > AD_VERSION1 */
1723 /* --------------------
1724 * Ok the db is out of synch with the dir.
1725 * but if it's a deleted file we don't want to do it again and again.
1728 reenumerate_id(struct vol *vol, char *name, struct dir *dir)
1734 if (vol->v_cdb == NULL) {
1738 /* FIXME use of_statdir ? */
1739 if (lstat(name, &st)) {
1743 if (dirreenumerate(dir, &st)) {
1744 /* we already did it once and the dir haven't been modified */
1749 data.did = dir->d_did;
1750 if ((ret = for_each_dirent(vol, name, reenumerate_loop, (void *)&data)) >= 0) {
1751 setdiroffcnt(curdir, &st, ret);
1752 dir->d_flags |= DIRF_CNID;
1758 /* ------------------------------
1759 resolve a file id */
1760 int afp_resolveid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1769 u_int16_t vid, bitmap;
1771 static char buffer[12 + MAXPATHLEN + 1];
1772 int len = 12 + MAXPATHLEN + 1;
1777 memcpy(&vid, ibuf, sizeof(vid));
1778 ibuf += sizeof(vid);
1780 if (NULL == ( vol = getvolbyvid( vid )) ) {
1781 return( AFPERR_PARAM);
1784 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1788 memcpy(&id, ibuf, sizeof( id ));
1793 /* some MacOS versions after a catsearch do a *lot* of afp_resolveid with 0 */
1797 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1798 return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
1801 if (NULL == ( dir = dirlookup( vol, id )) ) {
1802 return AFPERR_NOID; /* idem AFPERR_PARAM */
1804 if (movecwd(vol, dir) < 0) {
1808 return AFPERR_ACCESS;
1812 return AFPERR_PARAM;
1816 memset(&path, 0, sizeof(path));
1817 path.u_name = upath;
1818 if ( of_stat(&path) < 0 ) {
1820 /* with nfs and our working directory is deleted */
1821 if (errno == ESTALE) {
1825 if ( errno == ENOENT && !retry) {
1826 /* cnid db is out of sync, reenumerate the directory and update ids */
1827 reenumerate_id(vol, ".", dir);
1835 return AFPERR_ACCESS;
1839 return AFPERR_PARAM;
1843 /* directories are bad */
1844 if (S_ISDIR(path.st.st_mode)) {
1845 /* OS9 and OSX don't return the same error code */
1846 return (afp_version >=30)?AFPERR_NOID:AFPERR_BADTYPE;
1849 memcpy(&bitmap, ibuf, sizeof(bitmap));
1850 bitmap = ntohs( bitmap );
1851 if (NULL == (path.m_name = utompath(vol, upath, cnid, utf8_encoding()))) {
1855 if (AFP_OK != (err = getfilparams(vol, bitmap, &path , curdir,
1856 rbuf + sizeof(bitmap), &buflen))) {
1859 *rbuflen = buflen + sizeof(bitmap);
1860 memcpy(rbuf, ibuf, sizeof(bitmap));
1865 /* ------------------------------ */
1866 int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1876 static char buffer[12 + MAXPATHLEN + 1];
1877 int len = 12 + MAXPATHLEN + 1;
1882 memcpy(&vid, ibuf, sizeof(vid));
1883 ibuf += sizeof(vid);
1885 if (NULL == ( vol = getvolbyvid( vid )) ) {
1886 return( AFPERR_PARAM);
1889 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1893 if (vol->v_flags & AFPVOL_RO)
1894 return AFPERR_VLOCK;
1896 memcpy(&id, ibuf, sizeof( id ));
1900 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1904 if (NULL == ( dir = dirlookup( vol, id )) ) {
1905 if (afp_errno == AFPERR_NOOBJ) {
1909 return( AFPERR_PARAM );
1913 if ((movecwd(vol, dir) < 0) || (lstat(upath, &st) < 0)) {
1917 return AFPERR_ACCESS;
1922 /* still try to delete the id */
1926 return AFPERR_PARAM;
1929 else if (S_ISDIR(st.st_mode)) /* directories are bad */
1930 return AFPERR_BADTYPE;
1933 if (cnid_delete(vol->v_cdb, fileid)) {
1936 return AFPERR_VLOCK;
1939 return AFPERR_ACCESS;
1941 return AFPERR_PARAM;
1948 /* ------------------------------ */
1949 static struct adouble *find_adouble(struct path *path, struct ofork **of, struct adouble *adp)
1953 if (path->st_errno) {
1954 switch (path->st_errno) {
1956 afp_errno = AFPERR_NOID;
1960 afp_errno = AFPERR_ACCESS;
1963 afp_errno = AFPERR_PARAM;
1968 /* we use file_access both for legacy Mac perm and
1969 * for unix privilege, rename will take care of folder perms
1971 if (file_access(path, OPENACC_WR ) < 0) {
1972 afp_errno = AFPERR_ACCESS;
1976 if ((*of = of_findname(path))) {
1977 /* reuse struct adouble so it won't break locks */
1981 ret = ad_open( path->u_name, ADFLAGS_HF, O_RDONLY, 0, adp);
1983 if ( !ret && ad_reso_fileno(adp) != -1 && !(adp->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
1985 * The user must have the Read & Write privilege for both files in order to use this command.
1987 ad_close(adp, ADFLAGS_HF);
1988 afp_errno = AFPERR_ACCESS;
1995 #define APPLETEMP ".AppleTempXXXXXX"
1997 int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1999 struct stat srcst, destst;
2001 struct dir *dir, *sdir;
2002 char *spath, temp[17], *p;
2003 char *supath, *upath;
2008 struct adouble *adsp = NULL;
2009 struct adouble *addp = NULL;
2010 struct ofork *s_of = NULL;
2011 struct ofork *d_of = NULL;
2024 memcpy(&vid, ibuf, sizeof(vid));
2025 ibuf += sizeof(vid);
2027 if (NULL == ( vol = getvolbyvid( vid )) ) {
2028 return( AFPERR_PARAM);
2031 if ((vol->v_flags & AFPVOL_RO))
2032 return AFPERR_VLOCK;
2034 /* source and destination dids */
2035 memcpy(&sid, ibuf, sizeof(sid));
2036 ibuf += sizeof(sid);
2037 memcpy(&did, ibuf, sizeof(did));
2038 ibuf += sizeof(did);
2041 if (NULL == (dir = dirlookup( vol, sid )) ) {
2042 return afp_errno; /* was AFPERR_PARAM */
2045 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2046 return get_afp_errno(AFPERR_NOOBJ);
2049 if ( path_isadir(path) ) {
2050 return AFPERR_BADTYPE; /* it's a dir */
2053 /* save some stuff */
2056 spath = obj->oldtmp;
2057 supath = obj->newtmp;
2058 strcpy(spath, path->m_name);
2059 strcpy(supath, path->u_name); /* this is for the cnid changing */
2060 p = absupath( vol, sdir, supath);
2062 /* pathname too long */
2063 return AFPERR_PARAM ;
2066 ad_init(&ads, vol->v_adouble, vol->v_ad_options);
2067 if (!(adsp = find_adouble( path, &s_of, &ads))) {
2071 /* ***** from here we may have resource fork open **** */
2073 /* look for the source cnid. if it doesn't exist, don't worry about
2075 sid = cnid_lookup(vol->v_cdb, &srcst, sdir->d_did, supath,slen = strlen(supath));
2077 if (NULL == ( dir = dirlookup( vol, did )) ) {
2078 err = afp_errno; /* was AFPERR_PARAM */
2079 goto err_exchangefile;
2082 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2083 err = get_afp_errno(AFPERR_NOOBJ);
2084 goto err_exchangefile;
2087 if ( path_isadir(path) ) {
2088 err = AFPERR_BADTYPE;
2089 goto err_exchangefile;
2092 /* FPExchangeFiles is the only call that can return the SameObj
2094 if ((curdir == sdir) && strcmp(spath, path->m_name) == 0) {
2095 err = AFPERR_SAMEOBJ;
2096 goto err_exchangefile;
2099 ad_init(&add, vol->v_adouble, vol->v_ad_options);
2100 if (!(addp = find_adouble( path, &d_of, &add))) {
2102 goto err_exchangefile;
2106 /* they are not on the same device and at least one is open
2107 * FIXME broken for for crossdev and adouble v2
2110 crossdev = (srcst.st_dev != destst.st_dev);
2111 if (/* (d_of || s_of) && */ crossdev) {
2113 goto err_exchangefile;
2116 /* look for destination id. */
2117 upath = path->u_name;
2118 did = cnid_lookup(vol->v_cdb, &destst, curdir->d_did, upath, dlen = strlen(upath));
2120 /* construct a temp name.
2121 * NOTE: the temp file will be in the dest file's directory. it
2122 * will also be inaccessible from AFP. */
2123 memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
2124 if (!mktemp(temp)) {
2126 goto err_exchangefile;
2130 /* FIXME we need to close fork for copy, both s_of and d_of are null */
2131 ad_close(adsp, ADFLAGS_HF);
2132 ad_close(addp, ADFLAGS_HF);
2135 /* now, quickly rename the file. we error if we can't. */
2136 if ((err = renamefile(vol, -1, p, temp, temp, adsp)) != AFP_OK)
2137 goto err_exchangefile;
2138 of_rename(vol, s_of, sdir, spath, curdir, temp);
2140 /* rename destination to source */
2141 if ((err = renamefile(vol, -1, upath, p, spath, addp)) != AFP_OK)
2142 goto err_src_to_tmp;
2143 of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
2145 /* rename temp to destination */
2146 if ((err = renamefile(vol, -1, temp, upath, path->m_name, adsp)) != AFP_OK)
2147 goto err_dest_to_src;
2148 of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
2150 /* id's need switching. src -> dest and dest -> src.
2151 * we need to re-stat() if it was a cross device copy.
2154 cnid_delete(vol->v_cdb, sid);
2157 cnid_delete(vol->v_cdb, did);
2159 if ((did && ( (crossdev && lstat( upath, &srcst) < 0) ||
2160 cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
2162 (sid && ( (crossdev && lstat(p, &destst) < 0) ||
2163 cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
2168 err = AFPERR_ACCESS;
2173 goto err_temp_to_dest;
2176 /* here we need to reopen if crossdev */
2177 if (sid && ad_setid(addp, destst.st_dev, destst.st_ino, sid, sdir->d_did, vol->v_stamp))
2182 if (did && ad_setid(adsp, srcst.st_dev, srcst.st_ino, did, curdir->d_did, vol->v_stamp))
2187 /* change perms, src gets dest perm and vice versa */
2192 LOG(log_error, logtype_afpd, "seteuid failed %s", strerror(errno));
2193 err = AFP_OK; /* ignore error */
2194 goto err_temp_to_dest;
2198 * we need to exchange ACL entries as well
2200 /* exchange_acls(vol, p, upath); */
2205 path->m_name = NULL;
2206 path->u_name = upath;
2208 setfilunixmode(vol, path, destst.st_mode);
2209 setfilowner(vol, destst.st_uid, destst.st_gid, path);
2216 setfilunixmode(vol, path, srcst.st_mode);
2217 setfilowner(vol, srcst.st_uid, srcst.st_gid, path);
2219 if ( setegid(gid) < 0 || seteuid(uid) < 0) {
2220 LOG(log_error, logtype_afpd, "can't seteuid back %s", strerror(errno));
2225 goto err_exchangefile;
2227 /* all this stuff is so that we can unwind a failed operation
2230 /* rename dest to temp */
2231 renamefile(vol, -1, upath, temp, temp, adsp);
2232 of_rename(vol, s_of, curdir, upath, curdir, temp);
2235 /* rename source back to dest */
2236 renamefile(vol, -1, p, upath, path->m_name, addp);
2237 of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
2240 /* rename temp back to source */
2241 renamefile(vol, -1, temp, p, spath, adsp);
2242 of_rename(vol, s_of, curdir, temp, sdir, spath);
2245 if ( !s_of && adsp && ad_meta_fileno(adsp) != -1 ) { /* META */
2246 ad_close(adsp, ADFLAGS_HF);
2248 if ( !d_of && addp && ad_meta_fileno(addp) != -1 ) {/* META */
2249 ad_close(addp, ADFLAGS_HF);