2 * $Id: file.c,v 1.99 2005-06-02 10:23:05 didg Exp $
4 * Copyright (c) 1990,1993 Regents of The University of Michigan.
5 * All Rights Reserved. See COPYRIGHT.
10 #endif /* HAVE_CONFIG_H */
18 #else /* STDC_HEADERS */
22 #endif /* HAVE_STRCHR */
23 char *strchr (), *strrchr ();
26 #define memcpy(d,s,n) bcopy ((s), (d), (n))
27 #define memmove(d,s,n) bcopy ((s), (d), (n))
28 #endif /* ! HAVE_MEMCPY */
29 #endif /* STDC_HEADERS */
31 #include <atalk/adouble.h>
36 #include <atalk/logger.h>
37 #include <sys/param.h>
40 #include <atalk/afp.h>
41 #include <atalk/util.h>
42 #include <atalk/cnid.h>
43 #include "directory.h"
52 /* the format for the finderinfo fields (from IM: Toolbox Essentials):
53 * field bytes subfield bytes
56 * ioFlFndrInfo 16 -> type 4 type field
57 * creator 4 creator field
58 * flags 2 finder flags:
60 * location 4 location in window
61 * folder 2 window that contains file
63 * ioFlXFndrInfo 16 -> iconID 2 icon id
65 * script 1 script system
67 * commentID 2 comment id
68 * putawayID 4 home directory id
71 const u_char ufinderi[ADEDLEN_FINDERI] = {
72 0, 0, 0, 0, 0, 0, 0, 0,
73 1, 0, 0, 0, 0, 0, 0, 0,
74 0, 0, 0, 0, 0, 0, 0, 0,
75 0, 0, 0, 0, 0, 0, 0, 0
78 static const u_char old_ufinderi[] = {
79 'T', 'E', 'X', 'T', 'U', 'N', 'I', 'X'
82 /* ----------------------
84 static int default_type(void *finder)
86 if (!memcmp(finder, ufinderi, 8) || !memcmp(finder, old_ufinderi, 8))
91 /* FIXME path : unix or mac name ? (for now it's unix name ) */
92 void *get_finderinfo(const char *upath, struct adouble *adp, void *data)
95 void *ad_finder = NULL;
99 ad_finder = ad_entry(adp, ADEID_FINDERI);
102 memcpy(data, ad_finder, ADEDLEN_FINDERI);
104 if (default_type(ad_finder))
108 memcpy(data, ufinderi, ADEDLEN_FINDERI);
110 if (*upath == '.') { /* make it invisible */
113 ashort = htons(FINDERINFO_INVISIBLE);
114 memcpy(data + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
117 /** Only enter if no appledouble information and no finder information found. */
118 if (chk_ext && (em = getextmap( upath ))) {
119 memcpy(data, em->em_type, sizeof( em->em_type ));
120 memcpy((char *)data + 4, em->em_creator, sizeof(em->em_creator));
125 /* ---------------------
127 char *set_name(const struct vol *vol, char *data, cnid_t pid, char *name, cnid_t id, u_int32_t utf8)
132 aint = strlen( name );
136 if (utf8_encoding()) {
137 /* but name is an utf8 mac name */
140 /* global static variable... */
142 if (!(u = mtoupath(vol, name, pid, 1)) || !(m = utompath(vol, u, id, 0))) {
151 if (aint > MACFILELEN)
158 if (aint > 255) /* FIXME safeguard, anyway if no ascii char it's game over*/
161 utf8 = vol->v_mac?htonl(vol->v_mac->kTextEncoding):0; /* htonl(utf8) */
162 memcpy(data, &utf8, sizeof(utf8));
163 data += sizeof(utf8);
166 memcpy(data, &temp, sizeof(temp));
167 data += sizeof(temp);
170 memcpy( data, src, aint );
180 * FIXME: PDINFO is UTF8 and doesn't need adp
182 #define PARAM_NEED_ADP(b) ((b) & ((1 << FILPBIT_ATTR) |\
183 (1 << FILPBIT_CDATE) |\
184 (1 << FILPBIT_MDATE) |\
185 (1 << FILPBIT_BDATE) |\
186 (1 << FILPBIT_FINFO) |\
187 (1 << FILPBIT_RFLEN) |\
188 (1 << FILPBIT_EXTRFLEN) |\
189 (1 << FILPBIT_PDINFO) |\
190 (1 << FILPBIT_UNIXPR)))
192 /* -------------------------- */
193 u_int32_t get_id(struct vol *vol, struct adouble *adp, const struct stat *st,
194 const cnid_t did, char *upath, const int len)
198 #if AD_VERSION > AD_VERSION1
200 if ((aint = ad_getid(adp, st->st_dev, st->st_ino, did, vol->v_stamp))) {
205 if (vol->v_cdb != NULL) {
206 aint = cnid_add(vol->v_cdb, st, did, upath, len, aint);
207 /* Throw errors if cnid_add fails. */
208 if (aint == CNID_INVALID) {
210 case CNID_ERR_CLOSE: /* the db is closed */
213 LOG(log_error, logtype_afpd, "get_id: Incorrect parameters passed to cnid_add");
214 afp_errno = AFPERR_PARAM;
217 afp_errno = AFPERR_PARAM;
220 afp_errno = AFPERR_MISC;
224 #if AD_VERSION > AD_VERSION1
226 /* update the ressource fork
227 * for a folder adp is always null
229 if (ad_setid(adp, st->st_dev, st->st_ino, aint, did, vol->v_stamp)) {
230 ad_flush(adp, ADFLAGS_HF);
238 /* -------------------------- */
239 int getmetadata(struct vol *vol,
241 struct path *path, struct dir *dir,
242 char *buf, int *buflen, struct adouble *adp, int attrbits )
244 char *data, *l_nameoff = NULL, *upath;
245 char *utf_nameoff = NULL;
250 u_char achar, fdType[4];
256 LOG(log_info, logtype_afpd, "begin getmetadata:");
259 upath = path->u_name;
264 if ( ((bitmap & ( (1 << FILPBIT_FINFO)|(1 << FILPBIT_LNAME)|(1 <<FILPBIT_PDINFO) ) ) && !path->m_name)
265 || (bitmap & ( (1 << FILPBIT_LNAME) ) && utf8_encoding()) /* FIXME should be m_name utf8 filename */
266 || (bitmap & (1 << FILPBIT_FNUM))) {
268 id = get_id(vol, adp, st, dir->d_did, upath, strlen(upath));
274 path->m_name = utompath(vol, upath, id, utf8_encoding());
277 while ( bitmap != 0 ) {
278 while (( bitmap & 1 ) == 0 ) {
286 ad_getattr(adp, &ashort);
287 } else if (*upath == '.') {
288 ashort = htons(ATTRBIT_INVISIBLE);
292 /* FIXME do we want a visual clue if the file is read only
295 accessmode( ".", &ma, dir , NULL);
296 if ((ma.ma_user & AR_UWRITE)) {
297 accessmode( upath, &ma, dir , st);
298 if (!(ma.ma_user & AR_UWRITE)) {
299 attrbits |= ATTRBIT_NOWRITE;
304 ashort = htons(ntohs(ashort) | attrbits);
305 memcpy(data, &ashort, sizeof( ashort ));
306 data += sizeof( ashort );
310 memcpy(data, &dir->d_did, sizeof( u_int32_t ));
311 data += sizeof( u_int32_t );
315 if (!adp || (ad_getdate(adp, AD_DATE_CREATE, &aint) < 0))
316 aint = AD_DATE_FROM_UNIX(st->st_mtime);
317 memcpy(data, &aint, sizeof( aint ));
318 data += sizeof( aint );
322 if ( adp && (ad_getdate(adp, AD_DATE_MODIFY, &aint) == 0)) {
323 if ((st->st_mtime > AD_DATE_TO_UNIX(aint))) {
324 aint = AD_DATE_FROM_UNIX(st->st_mtime);
327 aint = AD_DATE_FROM_UNIX(st->st_mtime);
329 memcpy(data, &aint, sizeof( int ));
330 data += sizeof( int );
334 if (!adp || (ad_getdate(adp, AD_DATE_BACKUP, &aint) < 0))
335 aint = AD_DATE_START;
336 memcpy(data, &aint, sizeof( int ));
337 data += sizeof( int );
341 get_finderinfo(upath, adp, (char *)data);
342 data += ADEDLEN_FINDERI;
347 data += sizeof( u_int16_t );
351 memset(data, 0, sizeof(u_int16_t));
352 data += sizeof( u_int16_t );
356 memcpy(data, &id, sizeof( id ));
357 data += sizeof( id );
361 if (st->st_size > 0xffffffff)
364 aint = htonl( st->st_size );
365 memcpy(data, &aint, sizeof( aint ));
366 data += sizeof( aint );
371 if (adp->ad_rlen > 0xffffffff)
374 aint = htonl( adp->ad_rlen);
378 memcpy(data, &aint, sizeof( aint ));
379 data += sizeof( aint );
382 /* Current client needs ProDOS info block for this file.
383 Use simple heuristic and let the Mac "type" string tell
384 us what the PD file code should be. Everything gets a
385 subtype of 0x0000 unless the original value was hashed
386 to "pXYZ" when we created it. See IA, Ver 2.
387 <shirsch@adelphia.net> */
388 case FILPBIT_PDINFO :
389 if (afp_version >= 30) { /* UTF8 name */
390 utf8 = kTextEncodingUTF8;
392 data += sizeof( u_int16_t );
394 memcpy(data, &aint, sizeof( aint ));
395 data += sizeof( aint );
399 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
401 if ( memcmp( fdType, "TEXT", 4 ) == 0 ) {
405 else if ( memcmp( fdType, "PSYS", 4 ) == 0 ) {
409 else if ( memcmp( fdType, "PS16", 4 ) == 0 ) {
413 else if ( memcmp( fdType, "BINA", 4 ) == 0 ) {
417 else if ( fdType[0] == 'p' ) {
419 ashort = (fdType[2] * 256) + fdType[3];
433 memcpy(data, &ashort, sizeof( ashort ));
434 data += sizeof( ashort );
435 memset(data, 0, sizeof( ashort ));
436 data += sizeof( ashort );
439 case FILPBIT_EXTDFLEN:
440 aint = htonl(st->st_size >> 32);
441 memcpy(data, &aint, sizeof( aint ));
442 data += sizeof( aint );
443 aint = htonl(st->st_size);
444 memcpy(data, &aint, sizeof( aint ));
445 data += sizeof( aint );
447 case FILPBIT_EXTRFLEN:
450 aint = htonl(adp->ad_rlen >> 32);
451 memcpy(data, &aint, sizeof( aint ));
452 data += sizeof( aint );
454 aint = htonl(adp->ad_rlen);
455 memcpy(data, &aint, sizeof( aint ));
456 data += sizeof( aint );
458 case FILPBIT_UNIXPR :
459 /* accessmode may change st_mode with ACLs */
460 accessmode( upath, &ma, dir , st);
462 aint = htonl(st->st_uid);
463 memcpy( data, &aint, sizeof( aint ));
464 data += sizeof( aint );
465 aint = htonl(st->st_gid);
466 memcpy( data, &aint, sizeof( aint ));
467 data += sizeof( aint );
470 type == slnk indicates an OSX style symlink,
471 we have to add S_IFLNK to the mode, otherwise
472 10.3 clients freak out. */
476 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
477 if ( memcmp( fdType, "slnk", 4 ) == 0 ) {
483 memcpy( data, &aint, sizeof( aint ));
484 data += sizeof( aint );
486 *data++ = ma.ma_user;
487 *data++ = ma.ma_world;
488 *data++ = ma.ma_group;
489 *data++ = ma.ma_owner;
493 return( AFPERR_BITMAP );
499 ashort = htons( data - buf );
500 memcpy(l_nameoff, &ashort, sizeof( ashort ));
501 data = set_name(vol, data, dir->d_did, path->m_name, id, 0);
504 ashort = htons( data - buf );
505 memcpy(utf_nameoff, &ashort, sizeof( ashort ));
506 data = set_name(vol, data, dir->d_did, path->m_name, id, utf8);
508 *buflen = data - buf;
512 /* ----------------------- */
513 int getfilparams(struct vol *vol,
515 struct path *path, struct dir *dir,
516 char *buf, int *buflen )
518 struct adouble ad, *adp;
521 u_int16_t attrbits = 0;
526 LOG(log_info, logtype_default, "begin getfilparams:");
529 opened = PARAM_NEED_ADP(bitmap);
532 upath = path->u_name;
533 if ((of = of_findname(path))) {
535 attrbits = ((of->of_ad->ad_df.adf_refcount > 0) ? ATTRBIT_DOPEN : 0);
536 attrbits |= ((of->of_ad->ad_hf.adf_refcount > of->of_ad->ad_df.adf_refcount)? ATTRBIT_ROPEN : 0);
538 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
542 if ( ad_metadata( upath, 0, adp) < 0 ) {
545 LOG(log_error, logtype_afpd, "getfilparams(%s): %s: check resource fork permission?",
546 upath, strerror(errno));
547 return AFPERR_ACCESS;
549 LOG(log_error, logtype_afpd, "getfilparams(%s): bad resource fork", upath);
559 we need to check if the file is open by another process.
560 it's slow so we only do it if we have to:
561 - bitmap is requested.
562 - we don't already have the answer!
564 if ((bitmap & (1 << FILPBIT_ATTR))) {
565 if (!(attrbits & ATTRBIT_ROPEN)) {
566 attrbits |= ad_testlock(adp, ADEID_RFORK, AD_FILELOCK_OPEN_RD) > 0? ATTRBIT_ROPEN : 0;
567 attrbits |= ad_testlock(adp, ADEID_RFORK, AD_FILELOCK_OPEN_WR) > 0? ATTRBIT_ROPEN : 0;
569 if (!(attrbits & ATTRBIT_DOPEN)) {
570 attrbits |= ad_testlock(adp, ADEID_DFORK, AD_FILELOCK_OPEN_RD) > 0? ATTRBIT_DOPEN : 0;
571 attrbits |= ad_testlock(adp, ADEID_DFORK, AD_FILELOCK_OPEN_WR) > 0? ATTRBIT_DOPEN : 0;
576 rc = getmetadata(vol, bitmap, path, dir, buf, buflen, adp, attrbits);
578 ad_close_metadata( adp);
581 LOG(log_info, logtype_afpd, "end getfilparams:");
587 /* ----------------------------- */
588 int afp_createfile(obj, ibuf, ibuflen, rbuf, rbuflen )
590 char *ibuf, *rbuf _U_;
591 int ibuflen _U_, *rbuflen;
593 struct adouble ad, *adp;
596 struct ofork *of = NULL;
598 int creatf, did, openf, retvalue = AFP_OK;
604 LOG(log_info, logtype_afpd, "begin afp_createfile:");
609 creatf = (unsigned char) *ibuf++;
611 memcpy(&vid, ibuf, sizeof( vid ));
612 ibuf += sizeof( vid );
614 if (NULL == ( vol = getvolbyvid( vid )) ) {
615 return( AFPERR_PARAM );
618 if (vol->v_flags & AFPVOL_RO)
621 memcpy(&did, ibuf, sizeof( did));
622 ibuf += sizeof( did );
624 if (NULL == ( dir = dirlookup( vol, did )) ) {
628 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
629 return get_afp_errno(AFPERR_PARAM);
632 if ( *s_path->m_name == '\0' ) {
633 return( AFPERR_BADTYPE );
636 upath = s_path->u_name;
637 if (0 != (ret = check_name(vol, upath)))
640 /* if upath is deleted we already in trouble anyway */
641 if ((of = of_findname(s_path))) {
644 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
648 /* on a hard create, fail if file exists and is open */
651 openf = O_RDWR|O_CREAT|O_TRUNC;
653 /* on a soft create, if the file is open then ad_open won't fail
654 because open syscall is not called
659 openf = O_RDWR|O_CREAT|O_EXCL;
662 if ( ad_open( upath, vol_noadouble(vol)|ADFLAGS_DF|ADFLAGS_HF|ADFLAGS_NOHF,
663 openf, 0666, adp) < 0 ) {
667 case ENOENT : /* we were already in 'did folder' so chdir() didn't fail */
668 return ( AFPERR_NOOBJ );
670 return( AFPERR_EXIST );
672 return( AFPERR_ACCESS );
674 return( AFPERR_PARAM );
677 if ( ad_hfileno( adp ) == -1 ) {
678 /* on noadouble volumes, just creating the data fork is ok */
679 if (vol_noadouble(vol)) {
680 ad_close( adp, ADFLAGS_DF );
681 goto createfile_done;
683 /* FIXME with hard create on an existing file, we already
684 * corrupted the data file.
686 netatalk_unlink( upath );
687 ad_close( adp, ADFLAGS_DF );
688 return AFPERR_ACCESS;
691 path = s_path->m_name;
692 ad_setname(adp, path);
693 ad_flush( adp, ADFLAGS_DF|ADFLAGS_HF );
694 ad_close( adp, ADFLAGS_DF|ADFLAGS_HF );
700 if (vol->v_flags & AFPVOL_DROPBOX) {
701 retvalue = matchfile2dirperms(upath, vol, did);
703 #endif /* DROPKLUDGE */
705 setvoltime(obj, vol );
708 LOG(log_info, logtype_afpd, "end afp_createfile");
714 int afp_setfilparams(obj, ibuf, ibuflen, rbuf, rbuflen )
716 char *ibuf, *rbuf _U_;
717 int ibuflen _U_, *rbuflen;
723 u_int16_t vid, bitmap;
726 LOG(log_info, logtype_afpd, "begin afp_setfilparams:");
732 memcpy(&vid, ibuf, sizeof( vid ));
733 ibuf += sizeof( vid );
734 if (NULL == ( vol = getvolbyvid( vid )) ) {
735 return( AFPERR_PARAM );
738 if (vol->v_flags & AFPVOL_RO)
741 memcpy(&did, ibuf, sizeof( did ));
742 ibuf += sizeof( did );
743 if (NULL == ( dir = dirlookup( vol, did )) ) {
744 return afp_errno; /* was AFPERR_NOOBJ */
747 memcpy(&bitmap, ibuf, sizeof( bitmap ));
748 bitmap = ntohs( bitmap );
749 ibuf += sizeof( bitmap );
751 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
752 return get_afp_errno(AFPERR_PARAM);
755 if (path_isadir(s_path)) {
756 return( AFPERR_BADTYPE ); /* it's a directory */
759 if ( s_path->st_errno != 0 ) {
760 return( AFPERR_NOOBJ );
763 if ((u_long)ibuf & 1 ) {
767 if (AFP_OK == ( rc = setfilparams(vol, s_path, bitmap, ibuf )) ) {
768 setvoltime(obj, vol );
772 LOG(log_info, logtype_afpd, "end afp_setfilparams:");
779 * cf AFP3.0.pdf page 252 for change_mdate and change_parent_mdate logic
782 extern struct path Cur_Path;
784 int setfilparams(struct vol *vol,
785 struct path *path, u_int16_t f_bitmap, char *buf )
787 struct adouble ad, *adp;
789 int bit, isad = 1, err = AFP_OK;
791 u_char achar, *fdType, xyy[4]; /* uninitialized, OK 310105 */
792 u_int16_t ashort, bshort;
795 u_int16_t upriv_bit = 0;
799 int change_mdate = 0;
800 int change_parent_mdate = 0;
805 u_int16_t bitmap = f_bitmap;
806 u_int32_t cdate,bdate;
807 u_char finder_buf[32];
810 LOG(log_info, logtype_afpd, "begin setfilparams:");
813 upath = path->u_name;
814 adp = of_ad(vol, path, &ad);
817 if (!vol_unix_priv(vol) && check_access(upath, OPENACC_WR ) < 0) {
818 return AFPERR_ACCESS;
821 /* with unix priv maybe we have to change adouble file priv first */
823 while ( bitmap != 0 ) {
824 while (( bitmap & 1 ) == 0 ) {
831 memcpy(&ashort, buf, sizeof( ashort ));
832 buf += sizeof( ashort );
836 memcpy(&cdate, buf, sizeof(cdate));
837 buf += sizeof( cdate );
840 memcpy(&newdate, buf, sizeof( newdate ));
841 buf += sizeof( newdate );
845 memcpy(&bdate, buf, sizeof( bdate));
846 buf += sizeof( bdate );
850 memcpy(finder_buf, buf, 32 );
853 case FILPBIT_UNIXPR :
854 if (!vol_unix_priv(vol)) {
855 /* this volume doesn't use unix priv */
861 change_parent_mdate = 1;
863 memcpy( &aint, buf, sizeof( aint ));
864 f_uid = ntohl (aint);
865 buf += sizeof( aint );
866 memcpy( &aint, buf, sizeof( aint ));
867 f_gid = ntohl (aint);
868 buf += sizeof( aint );
869 setfilowner(vol, f_uid, f_gid, path);
871 memcpy( &upriv, buf, sizeof( upriv ));
872 buf += sizeof( upriv );
873 upriv = ntohl (upriv);
874 if ((upriv & S_IWUSR)) {
875 setfilunixmode(vol, path, upriv);
882 case FILPBIT_PDINFO :
883 if (afp_version < 30) { /* else it's UTF8 name */
886 /* Keep special case to support crlf translations */
887 if ((unsigned int) achar == 0x04) {
888 fdType = (u_char *)"TEXT";
891 xyy[0] = ( u_char ) 'p';
902 /* break while loop */
911 /* second try with adouble open
913 if ( ad_open_metadata( upath, vol_noadouble(vol), O_CREAT, adp) < 0) {
914 /* for some things, we don't need an adouble header */
915 if (f_bitmap & ~(1<<FILPBIT_MDATE)) {
916 return vol_noadouble(vol) ? AFP_OK : AFPERR_ACCESS;
919 } else if ((ad_get_HF_flags( adp ) & O_CREAT) ) {
920 ad_setname(adp, path->m_name);
925 while ( bitmap != 0 ) {
926 while (( bitmap & 1 ) == 0 ) {
933 ad_getattr(adp, &bshort);
934 if ((bshort & htons(ATTRBIT_INVISIBLE)) !=
935 (ashort & htons(ATTRBIT_INVISIBLE) & htons(ATTRBIT_SETCLR)) )
936 change_parent_mdate = 1;
937 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
938 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
942 ad_setattr(adp, bshort);
945 ad_setdate(adp, AD_DATE_CREATE, cdate);
950 ad_setdate(adp, AD_DATE_BACKUP, bdate);
953 if (default_type( ad_entry( adp, ADEID_FINDERI ))
955 ((em = getextmap( path->m_name )) &&
956 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
957 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
958 || ((em = getdefextmap()) &&
959 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
960 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
962 memcpy(finder_buf, ufinderi, 8 );
964 memcpy(ad_entry( adp, ADEID_FINDERI ), finder_buf, 32 );
966 case FILPBIT_UNIXPR :
968 setfilunixmode(vol, path, upriv);
971 case FILPBIT_PDINFO :
972 if (afp_version < 30) { /* else it's UTF8 name */
973 memcpy(ad_entry( adp, ADEID_FINDERI ), fdType, 4 );
974 memcpy(ad_entry( adp, ADEID_FINDERI ) + 4, "pdos", 4 );
980 goto setfilparam_done;
987 if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
988 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
992 ad_setdate(adp, AD_DATE_MODIFY, newdate);
993 ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
998 ad_flush_metadata( adp);
999 ad_close_metadata( adp);
1003 if (change_parent_mdate && gettimeofday(&tv, NULL) == 0) {
1004 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1005 bitmap = 1<<FILPBIT_MDATE;
1006 setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
1010 LOG(log_info, logtype_afpd, "end setfilparams:");
1016 * renamefile and copyfile take the old and new unix pathnames
1017 * and the new mac name.
1019 * src the source path
1020 * dst the dest filename in current dir
1021 * newname the dest mac name
1022 * adp adouble struct of src file, if open, or & zeroed one
1025 int renamefile(vol, src, dst, newname, adp )
1026 const struct vol *vol;
1027 char *src, *dst, *newname;
1028 struct adouble *adp;
1033 LOG(log_info, logtype_afpd, "begin renamefile:");
1036 if ( unix_rename( src, dst ) < 0 ) {
1039 return( AFPERR_NOOBJ );
1042 return( AFPERR_ACCESS );
1044 return AFPERR_VLOCK;
1045 case EXDEV : /* Cross device move -- try copy */
1046 /* NOTE: with open file it's an error because after the copy we will
1047 * get two files, it's fixable for our process (eg reopen the new file, get the
1048 * locks, and so on. But it doesn't solve the case with a second process
1050 if (adp->ad_df.adf_refcount || adp->ad_hf.adf_refcount) {
1051 /* FIXME warning in syslog so admin'd know there's a conflict ?*/
1052 return AFPERR_OLOCK; /* little lie */
1054 if (AFP_OK != ( rc = copyfile(vol, vol, src, dst, newname, NULL )) ) {
1055 /* on error copyfile delete dest */
1058 return deletefile(vol, src, 0);
1060 return( AFPERR_PARAM );
1064 if (vol->vfs->rf_renamefile(vol, src, dst) < 0 ) {
1068 /* try to undo the data fork rename,
1069 * we know we are on the same device
1072 unix_rename( dst, src );
1073 /* return the first error */
1076 return AFPERR_NOOBJ;
1079 return AFPERR_ACCESS ;
1081 return AFPERR_VLOCK;
1083 return AFPERR_PARAM ;
1088 /* don't care if we can't open the newly renamed ressource fork
1090 if (!ad_open( dst, ADFLAGS_HF, O_RDWR, 0666, adp)) {
1091 ad_setname(adp, newname);
1092 ad_flush( adp, ADFLAGS_HF );
1093 ad_close( adp, ADFLAGS_HF );
1096 LOG(log_info, logtype_afpd, "end renamefile:");
1103 convert a Mac long name to an utf8 name,
1105 size_t mtoUTF8(const struct vol *vol, const char *src, size_t srclen, char *dest, size_t destlen)
1109 if ((size_t)-1 == (outlen = convert_string ( vol->v_maccharset, CH_UTF8_MAC, src, srclen, dest, destlen)) ) {
1115 /* ---------------- */
1116 int copy_path_name(const struct vol *vol, char *newname, char *ibuf)
1123 if ( type != 2 && !(afp_version >= 30 && type == 3) ) {
1129 if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
1130 if (afp_version >= 30) {
1131 /* convert it to UTF8
1133 if ((plen = mtoUTF8(vol, ibuf, plen, newname, AFPOBJ_TMPSIZ)) == (size_t)-1)
1137 strncpy( newname, ibuf, plen );
1138 newname[ plen ] = '\0';
1140 if (strlen(newname) != plen) {
1141 /* there's \0 in newname, e.g. it's a pathname not
1149 memcpy(&hint, ibuf, sizeof(hint));
1150 ibuf += sizeof(hint);
1152 memcpy(&len16, ibuf, sizeof(len16));
1153 ibuf += sizeof(len16);
1154 plen = ntohs(len16);
1157 if (plen > AFPOBJ_TMPSIZ) {
1160 strncpy( newname, ibuf, plen );
1161 newname[ plen ] = '\0';
1162 if (strlen(newname) != plen) {
1171 /* -----------------------------------
1173 int afp_copyfile(obj, ibuf, ibuflen, rbuf, rbuflen )
1175 char *ibuf, *rbuf _U_;
1176 int ibuflen _U_, *rbuflen;
1178 struct vol *s_vol, *d_vol;
1180 char *newname, *p, *upath;
1181 struct path *s_path;
1182 u_int32_t sdid, ddid;
1183 int err, retvalue = AFP_OK;
1184 u_int16_t svid, dvid;
1186 struct adouble ad, *adp;
1190 LOG(log_info, logtype_afpd, "begin afp_copyfile:");
1196 memcpy(&svid, ibuf, sizeof( svid ));
1197 ibuf += sizeof( svid );
1198 if (NULL == ( s_vol = getvolbyvid( svid )) ) {
1199 return( AFPERR_PARAM );
1202 memcpy(&sdid, ibuf, sizeof( sdid ));
1203 ibuf += sizeof( sdid );
1204 if (NULL == ( dir = dirlookup( s_vol, sdid )) ) {
1208 memcpy(&dvid, ibuf, sizeof( dvid ));
1209 ibuf += sizeof( dvid );
1210 memcpy(&ddid, ibuf, sizeof( ddid ));
1211 ibuf += sizeof( ddid );
1213 if (NULL == ( s_path = cname( s_vol, dir, &ibuf )) ) {
1214 return get_afp_errno(AFPERR_PARAM);
1216 if ( path_isadir(s_path) ) {
1217 return( AFPERR_BADTYPE );
1220 /* don't allow copies when the file is open.
1221 * XXX: the spec only calls for read/deny write access.
1222 * however, copyfile doesn't have any of that info,
1223 * and locks need to stay coherent. as a result,
1224 * we just balk if the file is opened already. */
1226 adp = of_ad(s_vol, s_path, &ad);
1228 if (ad_open(s_path->u_name , ADFLAGS_DF |ADFLAGS_HF | ADFLAGS_NOHF, O_RDONLY, 0, adp) < 0) {
1229 return AFPERR_DENYCONF;
1231 denyreadset = (getforkmode(adp, ADEID_DFORK, AD_FILELOCK_DENY_RD) != 0 ||
1232 getforkmode(adp, ADEID_RFORK, AD_FILELOCK_DENY_RD) != 0 );
1233 ad_close( adp, ADFLAGS_DF |ADFLAGS_HF );
1235 return AFPERR_DENYCONF;
1238 newname = obj->newtmp;
1239 strcpy( newname, s_path->m_name );
1241 p = ctoupath( s_vol, curdir, newname );
1243 return AFPERR_PARAM;
1247 /* FIXME svid != dvid && dvid's user can't read svid */
1249 if (NULL == ( d_vol = getvolbyvid( dvid )) ) {
1250 return( AFPERR_PARAM );
1253 if (d_vol->v_flags & AFPVOL_RO)
1254 return AFPERR_VLOCK;
1256 if (NULL == ( dir = dirlookup( d_vol, ddid )) ) {
1260 if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) {
1261 return get_afp_errno(AFPERR_NOOBJ);
1263 if ( *s_path->m_name != '\0' ) {
1264 path_error(s_path, AFPERR_PARAM);
1267 /* one of the handful of places that knows about the path type */
1268 if (copy_path_name(d_vol, newname, ibuf) < 0) {
1269 return( AFPERR_PARAM );
1271 /* newname is always only a filename so curdir *is* its
1274 if (NULL == (upath = mtoupath(d_vol, newname, curdir->d_did, utf8_encoding()))) {
1275 return( AFPERR_PARAM );
1277 if ( (err = copyfile(s_vol, d_vol, p, upath , newname, adp)) < 0 ) {
1283 if (vol->v_flags & AFPVOL_DROPBOX) {
1284 retvalue=matchfile2dirperms(upath, vol, ddid); /* FIXME sdir or ddid */
1286 #endif /* DROPKLUDGE */
1288 setvoltime(obj, d_vol );
1291 LOG(log_info, logtype_afpd, "end afp_copyfile:");
1297 /* ----------------------- */
1298 static __inline__ int copy_all(const int dfd, const void *buf,
1304 LOG(log_info, logtype_afpd, "begin copy_all:");
1307 while (buflen > 0) {
1308 if ((cc = write(dfd, buf, buflen)) < 0) {
1320 LOG(log_info, logtype_afpd, "end copy_all:");
1326 /* -------------------------- */
1327 static int copy_fd(int dfd, int sfd)
1333 #if 0 /* ifdef SENDFILE_FLAVOR_LINUX */
1334 /* doesn't work With 2.6 FIXME, only check for EBADFD ? */
1338 #define BUF 128*1024*1024
1340 if (fstat(sfd, &st) == 0) {
1343 if ( offset >= st.st_size) {
1346 size = (st.st_size -offset > BUF)?BUF:st.st_size -offset;
1347 if ((cc = sys_sendfile(dfd, sfd, &offset, size)) < 0) {
1350 case EINVAL: /* there's no guarantee that all fs support sendfile */
1359 lseek(sfd, offset, SEEK_SET);
1363 if ((cc = read(sfd, filebuf, sizeof(filebuf))) < 0) {
1370 if (!cc || ((err = copy_all(dfd, filebuf, cc)) < 0)) {
1377 /* ----------------------------------
1378 * if newname is NULL (from directory.c) we don't want to copy ressource fork.
1379 * because we are doing it elsewhere.
1380 * currently if newname is NULL then adp is NULL.
1382 int copyfile(s_vol, d_vol, src, dst, newname, adp )
1383 const struct vol *s_vol, *d_vol;
1384 char *src, *dst, *newname;
1385 struct adouble *adp;
1387 struct adouble ads, add;
1391 int noadouble = vol_noadouble(d_vol);
1396 LOG(log_info, logtype_afpd, "begin copyfile:");
1400 ad_init(&ads, s_vol->v_adouble, s_vol->v_ad_options);
1403 ad_init(&add, d_vol->v_adouble, d_vol->v_ad_options);
1404 adflags = ADFLAGS_DF;
1406 adflags |= ADFLAGS_HF;
1409 if (ad_open(src , adflags | ADFLAGS_NOHF, O_RDONLY, 0, adp) < 0) {
1414 if (ad_hfileno(adp) == -1) {
1415 /* no resource fork, don't create one for dst file */
1416 adflags &= ~ADFLAGS_HF;
1419 stat_result = fstat(ad_dfileno(adp), &st); /* saving stat exit code, thus saving us on one more stat later on */
1421 if (stat_result < 0) {
1422 /* unlikely but if fstat fails, the default file mode will be 0666. */
1423 st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
1426 if (ad_open(dst , adflags | noadouble, O_RDWR|O_CREAT|O_EXCL, st.st_mode, &add) < 0) {
1428 ad_close( adp, adflags );
1429 if (EEXIST != ret_err) {
1430 deletefile(d_vol, dst, 0);
1433 return AFPERR_EXIST;
1435 /* XXX if the source and the dest don't use the same resource type it's broken
1437 if (ad_hfileno(adp) == -1 || 0 == (err = copy_fd(ad_hfileno(&add), ad_hfileno(adp)))){
1438 /* copy the data fork */
1439 err = copy_fd(ad_dfileno(&add), ad_dfileno(adp));
1442 /* Now, reopen destination file */
1446 ad_close( adp, adflags );
1448 if (ad_close( &add, adflags ) <0) {
1449 deletefile(d_vol, dst, 0);
1454 if (!ret_err && newname && (adflags & ADFLAGS_HF)) {
1455 /* set the new name in the resource fork */
1456 ad_init(&add, d_vol->v_adouble, d_vol->v_ad_options);
1457 if (ad_open_metadata(dst , noadouble, 0, &add) < 0) {
1461 ad_setname(&add, newname);
1462 ad_flush_metadata( &add);
1463 if (ad_close_metadata( &add)) {
1470 deletefile(d_vol, dst, 0);
1472 else if (stat_result == 0) {
1473 /* set dest modification date to src date */
1476 ut.actime = ut.modtime = st.st_mtime;
1478 /* FIXME netatalk doesn't use resource fork file date
1479 * but maybe we should set its modtime too.
1484 LOG(log_info, logtype_afpd, "end copyfile:");
1488 switch ( ret_err ) {
1494 return AFPERR_DFULL;
1496 return AFPERR_NOOBJ;
1498 return AFPERR_ACCESS;
1500 return AFPERR_VLOCK;
1502 return AFPERR_PARAM;
1506 /* -----------------------------------
1507 vol: not NULL delete cnid entry. then we are in curdir and file is a only filename
1508 checkAttrib: 1 check kFPDeleteInhibitBit (deletfile called by afp_delete)
1510 when deletefile is called we don't have lock on it, file is closed (for us)
1511 untrue if called by renamefile
1513 ad_open always try to open file RDWR first and ad_lock takes care of
1514 WRITE lock on read only file.
1516 int deletefile( vol, file, checkAttrib )
1517 const struct vol *vol;
1522 struct adouble *adp = &ad;
1523 int adflags, err = AFP_OK;
1526 LOG(log_info, logtype_afpd, "begin deletefile:");
1529 /* try to open both forks at once */
1530 adflags = ADFLAGS_DF|ADFLAGS_HF;
1531 ad_init(&ad, vol->v_adouble, vol->v_ad_options); /* OK */
1533 if ( ad_open( file, adflags, O_RDONLY, 0, &ad ) < 0 ) {
1536 if (adflags == ADFLAGS_DF)
1537 return AFPERR_NOOBJ;
1539 /* that failed. now try to open just the data fork */
1540 adflags = ADFLAGS_DF;
1544 adp = NULL; /* maybe it's a file with no write mode for us */
1545 break; /* was return AFPERR_ACCESS;*/
1547 return AFPERR_VLOCK;
1549 return( AFPERR_PARAM );
1552 break; /* from the while */
1555 * Does kFPDeleteInhibitBit (bit 8) set?
1560 if ( ad_metadata( file , 0, &ad) == 0 ) {
1561 ad_getattr(&ad, &bshort);
1562 ad_close_metadata( &ad);
1563 if ((bshort & htons(ATTRBIT_NODELETE))) {
1564 return AFPERR_OLOCK;
1569 if (adp && (adflags & ADFLAGS_HF) ) {
1570 /* FIXME we have a pb here because we want to know if a file is open
1571 * there's a 'priority inversion' if you can't open the ressource fork RW
1572 * you can delete it if it's open because you can't get a write lock.
1574 * ADLOCK_FILELOCK means the whole ressource fork, not only after the
1577 * FIXME it doesn't work for RFORK open read only and fork open without deny mode
1579 if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
1580 ad_close( &ad, adflags );
1581 return( AFPERR_BUSY );
1585 if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
1588 else if (!(err = vol->vfs->rf_deletefile(vol, file)) && !(err = netatalk_unlink( file )) ) {
1590 if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file))))
1592 cnid_delete(vol->v_cdb, id);
1596 ad_close( &ad, adflags ); /* ad_close removes locks if any */
1599 LOG(log_info, logtype_afpd, "end deletefile:");
1605 /* ------------------------------------ */
1606 /* return a file id */
1607 int afp_createid(obj, ibuf, ibuflen, rbuf, rbuflen )
1610 int ibuflen _U_, *rbuflen;
1619 struct path *s_path;
1622 LOG(log_info, logtype_afpd, "begin afp_createid:");
1629 memcpy(&vid, ibuf, sizeof(vid));
1630 ibuf += sizeof(vid);
1632 if (NULL == ( vol = getvolbyvid( vid )) ) {
1633 return( AFPERR_PARAM);
1636 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1640 if (vol->v_flags & AFPVOL_RO)
1641 return AFPERR_VLOCK;
1643 memcpy(&did, ibuf, sizeof( did ));
1644 ibuf += sizeof(did);
1646 if (NULL == ( dir = dirlookup( vol, did )) ) {
1647 return afp_errno; /* was AFPERR_PARAM */
1650 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
1651 return get_afp_errno(AFPERR_NOOBJ); /* was AFPERR_PARAM */
1654 if ( path_isadir(s_path) ) {
1655 return( AFPERR_BADTYPE );
1658 upath = s_path->u_name;
1659 switch (s_path->st_errno) {
1661 break; /* success */
1664 return AFPERR_ACCESS;
1666 return AFPERR_NOOBJ;
1668 return AFPERR_PARAM;
1671 if ((id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath)))) {
1672 memcpy(rbuf, &id, sizeof(id));
1673 *rbuflen = sizeof(id);
1674 return AFPERR_EXISTID;
1677 if ((id = get_id(vol, NULL, st, did, upath, len)) != CNID_INVALID) {
1678 memcpy(rbuf, &id, sizeof(id));
1679 *rbuflen = sizeof(id);
1684 LOG(log_info, logtype_afpd, "ending afp_createid...:");
1689 /* ------------------------------- */
1695 static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
1698 struct reenum *param = data;
1699 struct vol *vol = param->vol;
1700 cnid_t did = param->did;
1703 memset(&path, 0, sizeof(path));
1705 if ( stat(de->d_name, &path.st)<0 )
1708 /* update or add to cnid */
1709 aint = cnid_add(vol->v_cdb, &path.st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */
1711 #if AD_VERSION > AD_VERSION1
1712 if (aint != CNID_INVALID && !S_ISDIR(path.st.st_mode)) {
1713 struct adouble ad, *adp;
1717 path.u_name = de->d_name;
1719 adp = of_ad(vol, &path, &ad);
1721 if ( ad_open_metadata( de->d_name, 0, 0, adp ) < 0 ) {
1724 if (ad_setid(adp, path.st.st_dev, path.st.st_ino, aint, did, vol->v_stamp)) {
1725 ad_flush_metadata(adp);
1727 ad_close_metadata(adp);
1729 #endif /* AD_VERSION > AD_VERSION1 */
1734 /* --------------------
1735 * Ok the db is out of synch with the dir.
1736 * but if it's a deleted file we don't want to do it again and again.
1739 reenumerate_id(struct vol *vol, char *name, struct dir *dir)
1745 if (vol->v_cdb == NULL) {
1749 /* FIXME use of_statdir ? */
1750 if (stat(name, &st)) {
1754 if (dirreenumerate(dir, &st)) {
1755 /* we already did it once and the dir haven't been modified */
1760 data.did = dir->d_did;
1761 if ((ret = for_each_dirent(vol, name, reenumerate_loop, (void *)&data)) >= 0) {
1762 setdiroffcnt(curdir, &st, ret);
1763 dir->d_flags |= DIRF_CNID;
1769 /* ------------------------------
1770 resolve a file id */
1771 int afp_resolveid(obj, ibuf, ibuflen, rbuf, rbuflen )
1774 int ibuflen _U_, *rbuflen;
1780 int err, buflen, retry=0;
1782 u_int16_t vid, bitmap;
1784 static char buffer[12 + MAXPATHLEN + 1];
1785 int len = 12 + MAXPATHLEN + 1;
1788 LOG(log_info, logtype_afpd, "begin afp_resolveid:");
1794 memcpy(&vid, ibuf, sizeof(vid));
1795 ibuf += sizeof(vid);
1797 if (NULL == ( vol = getvolbyvid( vid )) ) {
1798 return( AFPERR_PARAM);
1801 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1805 memcpy(&id, ibuf, sizeof( id ));
1810 /* some MacOS versions after a catsearch do a *lot* of afp_resolveid with 0 */
1814 memset(&path, 0, sizeof(path));
1815 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1816 return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
1819 if (NULL == ( dir = dirlookup( vol, id )) ) {
1820 return AFPERR_NOID; /* idem AFPERR_PARAM */
1822 path.u_name = upath;
1823 if (movecwd(vol, dir) < 0) {
1827 return AFPERR_ACCESS;
1831 return AFPERR_PARAM;
1835 if ( of_stat(&path) < 0 ) {
1837 /* with nfs and our working directory is deleted */
1838 if (errno == ESTALE) {
1842 if ( errno == ENOENT && !retry) {
1843 /* cnid db is out of sync, reenumerate the directory and update ids */
1844 reenumerate_id(vol, ".", dir);
1852 return AFPERR_ACCESS;
1856 return AFPERR_PARAM;
1860 /* directories are bad */
1861 if (S_ISDIR(path.st.st_mode))
1862 return AFPERR_BADTYPE;
1864 memcpy(&bitmap, ibuf, sizeof(bitmap));
1865 bitmap = ntohs( bitmap );
1866 if (NULL == (path.m_name = utompath(vol, upath, cnid, utf8_encoding()))) {
1870 if (AFP_OK != (err = getfilparams(vol, bitmap, &path , curdir,
1871 rbuf + sizeof(bitmap), &buflen))) {
1874 *rbuflen = buflen + sizeof(bitmap);
1875 memcpy(rbuf, ibuf, sizeof(bitmap));
1878 LOG(log_info, logtype_afpd, "end afp_resolveid:");
1884 /* ------------------------------ */
1885 int afp_deleteid(obj, ibuf, ibuflen, rbuf, rbuflen )
1887 char *ibuf, *rbuf _U_;
1888 int ibuflen _U_, *rbuflen;
1898 static char buffer[12 + MAXPATHLEN + 1];
1899 int len = 12 + MAXPATHLEN + 1;
1902 LOG(log_info, logtype_afpd, "begin afp_deleteid:");
1908 memcpy(&vid, ibuf, sizeof(vid));
1909 ibuf += sizeof(vid);
1911 if (NULL == ( vol = getvolbyvid( vid )) ) {
1912 return( AFPERR_PARAM);
1915 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1919 if (vol->v_flags & AFPVOL_RO)
1920 return AFPERR_VLOCK;
1922 memcpy(&id, ibuf, sizeof( id ));
1926 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1930 if (NULL == ( dir = dirlookup( vol, id )) ) {
1931 return( AFPERR_PARAM );
1935 if ((movecwd(vol, dir) < 0) || (stat(upath, &st) < 0)) {
1939 return AFPERR_ACCESS;
1944 /* still try to delete the id */
1948 return AFPERR_PARAM;
1951 else if (S_ISDIR(st.st_mode)) /* directories are bad */
1952 return AFPERR_BADTYPE;
1954 if (cnid_delete(vol->v_cdb, fileid)) {
1957 return AFPERR_VLOCK;
1960 return AFPERR_ACCESS;
1962 return AFPERR_PARAM;
1967 LOG(log_info, logtype_afpd, "end afp_deleteid:");
1973 /* ------------------------------ */
1974 static struct adouble *find_adouble(struct path *path, struct ofork **of, struct adouble *adp)
1978 if (path->st_errno) {
1979 switch (path->st_errno) {
1981 afp_errno = AFPERR_NOID;
1985 afp_errno = AFPERR_ACCESS;
1988 afp_errno = AFPERR_PARAM;
1993 /* we use file_access both for legacy Mac perm and
1994 * for unix privilege, rename will take care of folder perms
1996 if (file_access(path, OPENACC_WR ) < 0) {
1997 afp_errno = AFPERR_ACCESS;
2001 if ((*of = of_findname(path))) {
2002 /* reuse struct adouble so it won't break locks */
2006 ret = ad_open( path->u_name, ADFLAGS_HF, O_RDONLY, 0, adp);
2007 if ( !ret && ad_hfileno(adp) != -1 && !(adp->ad_hf.adf_flags & ( O_RDWR | O_WRONLY))) {
2009 * The user must have the Read & Write privilege for both files in order to use this command.
2011 ad_close(adp, ADFLAGS_HF);
2012 afp_errno = AFPERR_ACCESS;
2019 #define APPLETEMP ".AppleTempXXXXXX"
2021 int afp_exchangefiles(obj, ibuf, ibuflen, rbuf, rbuflen )
2023 char *ibuf, *rbuf _U_ ;
2024 int ibuflen _U_, *rbuflen;
2026 struct stat srcst, destst;
2028 struct dir *dir, *sdir;
2029 char *spath, temp[17], *p;
2030 char *supath, *upath;
2035 struct adouble *adsp = NULL;
2036 struct adouble *addp = NULL;
2037 struct ofork *s_of = NULL;
2038 struct ofork *d_of = NULL;
2049 LOG(log_info, logtype_afpd, "begin afp_exchangefiles:");
2055 memcpy(&vid, ibuf, sizeof(vid));
2056 ibuf += sizeof(vid);
2058 if (NULL == ( vol = getvolbyvid( vid )) ) {
2059 return( AFPERR_PARAM);
2062 if ((vol->v_flags & AFPVOL_RO))
2063 return AFPERR_VLOCK;
2065 /* source and destination dids */
2066 memcpy(&sid, ibuf, sizeof(sid));
2067 ibuf += sizeof(sid);
2068 memcpy(&did, ibuf, sizeof(did));
2069 ibuf += sizeof(did);
2072 if (NULL == (dir = dirlookup( vol, sid )) ) {
2073 return afp_errno; /* was AFPERR_PARAM */
2076 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2077 return get_afp_errno(AFPERR_NOOBJ);
2080 if ( path_isadir(path) ) {
2081 return AFPERR_BADTYPE; /* it's a dir */
2084 /* save some stuff */
2087 spath = obj->oldtmp;
2088 supath = obj->newtmp;
2089 strcpy(spath, path->m_name);
2090 strcpy(supath, path->u_name); /* this is for the cnid changing */
2091 p = absupath( vol, sdir, supath);
2093 /* pathname too long */
2094 return AFPERR_PARAM ;
2097 ad_init(&ads, vol->v_adouble, vol->v_ad_options);
2098 if (!(adsp = find_adouble( path, &s_of, &ads))) {
2102 /* ***** from here we may have resource fork open **** */
2104 /* look for the source cnid. if it doesn't exist, don't worry about
2106 sid = cnid_lookup(vol->v_cdb, &srcst, sdir->d_did, supath,slen = strlen(supath));
2108 if (NULL == ( dir = dirlookup( vol, did )) ) {
2109 err = afp_errno; /* was AFPERR_PARAM */
2110 goto err_exchangefile;
2113 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2114 err = get_afp_errno(AFPERR_NOOBJ);
2115 goto err_exchangefile;
2118 if ( path_isadir(path) ) {
2119 err = AFPERR_BADTYPE;
2120 goto err_exchangefile;
2123 /* FPExchangeFiles is the only call that can return the SameObj
2125 if ((curdir == sdir) && strcmp(spath, path->m_name) == 0) {
2126 err = AFPERR_SAMEOBJ;
2127 goto err_exchangefile;
2130 ad_init(&add, vol->v_adouble, vol->v_ad_options);
2131 if (!(addp = find_adouble( path, &d_of, &add))) {
2133 goto err_exchangefile;
2137 /* they are not on the same device and at least one is open
2138 * FIXME broken for for crossdev and adouble v2
2141 crossdev = (srcst.st_dev != destst.st_dev);
2142 if (/* (d_of || s_of) && */ crossdev) {
2144 goto err_exchangefile;
2147 /* look for destination id. */
2148 upath = path->u_name;
2149 did = cnid_lookup(vol->v_cdb, &destst, curdir->d_did, upath, dlen = strlen(upath));
2151 /* construct a temp name.
2152 * NOTE: the temp file will be in the dest file's directory. it
2153 * will also be inaccessible from AFP. */
2154 memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
2155 if (!mktemp(temp)) {
2157 goto err_exchangefile;
2161 /* FIXME we need to close fork for copy, both s_of and d_of are null */
2162 ad_close(adsp, ADFLAGS_HF);
2163 ad_close(addp, ADFLAGS_HF);
2166 /* now, quickly rename the file. we error if we can't. */
2167 if ((err = renamefile(vol, p, temp, temp, adsp)) != AFP_OK)
2168 goto err_exchangefile;
2169 of_rename(vol, s_of, sdir, spath, curdir, temp);
2171 /* rename destination to source */
2172 if ((err = renamefile(vol, upath, p, spath, addp)) != AFP_OK)
2173 goto err_src_to_tmp;
2174 of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
2176 /* rename temp to destination */
2177 if ((err = renamefile(vol, temp, upath, path->m_name, adsp)) != AFP_OK)
2178 goto err_dest_to_src;
2179 of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
2181 /* id's need switching. src -> dest and dest -> src.
2182 * we need to re-stat() if it was a cross device copy.
2185 cnid_delete(vol->v_cdb, sid);
2188 cnid_delete(vol->v_cdb, did);
2190 if ((did && ( (crossdev && stat( upath, &srcst) < 0) ||
2191 cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
2193 (sid && ( (crossdev && stat(p, &destst) < 0) ||
2194 cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
2199 err = AFPERR_ACCESS;
2204 goto err_temp_to_dest;
2207 /* here we need to reopen if crossdev */
2208 if (sid && ad_setid(addp, destst.st_dev, destst.st_ino, sid, sdir->d_did, vol->v_stamp))
2210 ad_flush( addp, ADFLAGS_HF );
2213 if (did && ad_setid(adsp, srcst.st_dev, srcst.st_ino, did, curdir->d_did, vol->v_stamp))
2215 ad_flush( adsp, ADFLAGS_HF );
2218 /* change perms, src gets dest perm and vice versa */
2223 LOG(log_error, logtype_afpd, "seteuid failed %s", strerror(errno));
2224 err = AFP_OK; /* ignore error */
2225 goto err_temp_to_dest;
2229 * we need to exchange ACL entries as well
2231 /* exchange_acls(vol, p, upath); */
2236 path->m_name = NULL;
2237 path->u_name = upath;
2239 setfilunixmode(vol, path, destst.st_mode);
2240 setfilowner(vol, destst.st_uid, destst.st_gid, path);
2247 setfilunixmode(vol, path, srcst.st_mode);
2248 setfilowner(vol, srcst.st_uid, srcst.st_gid, path);
2250 if ( setegid(gid) < 0 || seteuid(uid) < 0) {
2251 LOG(log_error, logtype_afpd, "can't seteuid back %s", strerror(errno));
2256 LOG(log_info, logtype_afpd, "ending afp_exchangefiles:");
2260 goto err_exchangefile;
2262 /* all this stuff is so that we can unwind a failed operation
2265 /* rename dest to temp */
2266 renamefile(vol, upath, temp, temp, adsp);
2267 of_rename(vol, s_of, curdir, upath, curdir, temp);
2270 /* rename source back to dest */
2271 renamefile(vol, p, upath, path->m_name, addp);
2272 of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
2275 /* rename temp back to source */
2276 renamefile(vol, temp, p, spath, adsp);
2277 of_rename(vol, s_of, curdir, temp, sdir, spath);
2280 if ( !s_of && adsp && ad_hfileno(adsp) != -1 ) {
2281 ad_close(adsp, ADFLAGS_HF);
2283 if ( !d_of && addp && ad_hfileno(addp) != -1 ) {
2284 ad_close(addp, ADFLAGS_HF);