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"
53 /* the format for the finderinfo fields (from IM: Toolbox Essentials):
54 * field bytes subfield bytes
57 * ioFlFndrInfo 16 -> type 4 type field
58 * creator 4 creator field
59 * flags 2 finder flags:
61 * location 4 location in window
62 * folder 2 window that contains file
64 * ioFlXFndrInfo 16 -> iconID 2 icon id
66 * script 1 script system
68 * commentID 2 comment id
69 * putawayID 4 home directory id
72 const u_char ufinderi[ADEDLEN_FINDERI] = {
73 0, 0, 0, 0, 0, 0, 0, 0,
74 1, 0, 0, 0, 0, 0, 0, 0,
75 0, 0, 0, 0, 0, 0, 0, 0,
76 0, 0, 0, 0, 0, 0, 0, 0
79 static const u_char old_ufinderi[] = {
80 'T', 'E', 'X', 'T', 'U', 'N', 'I', 'X'
83 /* ----------------------
85 static int default_type(void *finder)
87 if (!memcmp(finder, ufinderi, 8) || !memcmp(finder, old_ufinderi, 8))
92 /* FIXME path : unix or mac name ? (for now it's unix name ) */
93 void *get_finderinfo(const struct vol *vol, const char *upath, struct adouble *adp, void *data, int islink)
96 void *ad_finder = NULL;
100 ad_finder = ad_entry(adp, ADEID_FINDERI);
103 memcpy(data, ad_finder, ADEDLEN_FINDERI);
105 if (default_type(ad_finder))
109 memcpy(data, ufinderi, ADEDLEN_FINDERI);
111 if (vol_inv_dots(vol) && *upath == '.') { /* make it invisible */
114 ashort = htons(FINDERINFO_INVISIBLE);
115 memcpy((char *)data + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
121 memcpy(&linkflag, (char *)data + FINDERINFO_FRFLAGOFF, 2);
122 linkflag |= htons(FINDERINFO_ISALIAS);
123 memcpy((char *)data + FINDERINFO_FRFLAGOFF, &linkflag, 2);
124 memcpy((char *)data + FINDERINFO_FRTYPEOFF,"slnk",4);
125 memcpy((char *)data + FINDERINFO_FRCREATOFF,"rhap",4);
129 /** Only enter if no appledouble information and no finder information found. */
130 if (chk_ext && (em = getextmap( upath ))) {
131 memcpy(data, em->em_type, sizeof( em->em_type ));
132 memcpy((char *)data + 4, em->em_creator, sizeof(em->em_creator));
137 /* ---------------------
139 char *set_name(const struct vol *vol, char *data, cnid_t pid, char *name, cnid_t id, u_int32_t utf8)
144 aint = strlen( name );
148 if (utf8_encoding()) {
149 /* but name is an utf8 mac name */
152 /* global static variable... */
154 if (!(u = mtoupath(vol, name, pid, 1)) || !(m = utompath(vol, u, id, 0))) {
163 if (aint > MACFILELEN)
170 if (aint > 255) /* FIXME safeguard, anyway if no ascii char it's game over*/
173 utf8 = vol->v_kTextEncoding;
174 memcpy(data, &utf8, sizeof(utf8));
175 data += sizeof(utf8);
178 memcpy(data, &temp, sizeof(temp));
179 data += sizeof(temp);
182 memcpy( data, src, aint );
192 * FIXME: PDINFO is UTF8 and doesn't need adp
194 #define PARAM_NEED_ADP(b) ((b) & ((1 << FILPBIT_ATTR) |\
195 (1 << FILPBIT_CDATE) |\
196 (1 << FILPBIT_MDATE) |\
197 (1 << FILPBIT_BDATE) |\
198 (1 << FILPBIT_FINFO) |\
199 (1 << FILPBIT_RFLEN) |\
200 (1 << FILPBIT_EXTRFLEN) |\
201 (1 << FILPBIT_PDINFO) |\
202 (1 << FILPBIT_FNUM) |\
203 (1 << FILPBIT_UNIXPR)))
205 /* -------------------------- */
206 u_int32_t get_id(struct vol *vol, struct adouble *adp, const struct stat *st,
207 const cnid_t did, char *upath, const int len)
209 static int first = 1; /* mark if this func is called the first time */
211 u_int32_t dbcnid = CNID_INVALID;
214 if (vol->v_cdb != NULL) {
215 /* prime aint with what we think is the cnid, set did to zero for
216 catching moved files */
217 adcnid = ad_getid(adp, st->st_dev, st->st_ino, 0, vol->v_stamp);
219 dbcnid = cnid_add(vol->v_cdb, st, did, upath, len, adcnid);
220 /* Throw errors if cnid_add fails. */
221 if (dbcnid == CNID_INVALID) {
223 case CNID_ERR_CLOSE: /* the db is closed */
226 LOG(log_error, logtype_afpd, "get_id: Incorrect parameters passed to cnid_add");
227 afp_errno = AFPERR_PARAM;
230 afp_errno = AFPERR_PARAM;
233 /* Close CNID backend if "dbd" and switch to temp in-memory "tdb" */
234 /* we have to do it here for "dbd" because it uses "lazy opening" */
235 /* In order to not end in a loop somehow with goto restart below */
237 if (first && (strcmp(vol->v_cnidscheme, "dbd") == 0)) {
238 cnid_close(vol->v_cdb);
239 free(vol->v_cnidscheme);
240 vol->v_cnidscheme = strdup("tdb");
242 int flags = CNID_FLAG_MEMORY;
243 if ((vol->v_flags & AFPVOL_NODEV)) {
244 flags |= CNID_FLAG_NODEV;
246 LOG(log_error, logtype_afpd, "Reopen volume %s using in memory temporary CNID DB.",
248 vol->v_cdb = cnid_open(vol->v_path, vol->v_umask, "tdb", flags, NULL, NULL);
250 /* deactivate cnid caching/storing in AppleDouble files and set ro mode*/
251 vol->v_flags &= ~AFPVOL_CACHE;
252 vol->v_flags |= AFPVOL_RO;
254 /* kill ourself with SIGUSR2 aka msg pending */
255 setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB instead."
256 "Check server messages for details. Switching to read-only mode.");
257 kill(getpid(), SIGUSR2);
259 goto restart; /* not try again with the temp CNID db */
262 setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB failed too!"
263 "Check server messages for details, can't recover from this state!");
267 afp_errno = AFPERR_MISC;
271 else if (adp && (adcnid != dbcnid)) {
272 /* Update the ressource fork. For a folder adp is always null */
273 LOG(log_debug, logtype_afpd, "get_id: calling ad_setid. adcnid: %u, dbcnid: %u", htonl(adcnid), htonl(dbcnid));
274 if (ad_setid(adp, st->st_dev, st->st_ino, dbcnid, did, vol->v_stamp)) {
285 /* -------------------------- */
286 int getmetadata(struct vol *vol,
288 struct path *path, struct dir *dir,
289 char *buf, size_t *buflen, struct adouble *adp)
291 char *data, *l_nameoff = NULL, *upath;
292 char *utf_nameoff = NULL;
297 u_char achar, fdType[4];
303 LOG(log_debug9, logtype_afpd, "begin getmetadata:");
306 upath = path->u_name;
311 if ( ((bitmap & ( (1 << FILPBIT_FINFO)|(1 << FILPBIT_LNAME)|(1 <<FILPBIT_PDINFO) ) ) && !path->m_name)
312 || (bitmap & ( (1 << FILPBIT_LNAME) ) && utf8_encoding()) /* FIXME should be m_name utf8 filename */
313 || (bitmap & (1 << FILPBIT_FNUM))) {
315 id = get_id(vol, adp, st, dir->d_did, upath, strlen(upath));
318 if (id == CNID_INVALID)
321 path->m_name = utompath(vol, upath, id, utf8_encoding());
324 while ( bitmap != 0 ) {
325 while (( bitmap & 1 ) == 0 ) {
333 ad_getattr(adp, &ashort);
334 } else if (vol_inv_dots(vol) && *upath == '.') {
335 ashort = htons(ATTRBIT_INVISIBLE);
339 /* FIXME do we want a visual clue if the file is read only
342 accessmode( ".", &ma, dir , NULL);
343 if ((ma.ma_user & AR_UWRITE)) {
344 accessmode( upath, &ma, dir , st);
345 if (!(ma.ma_user & AR_UWRITE)) {
346 ashort |= htons(ATTRBIT_NOWRITE);
350 memcpy(data, &ashort, sizeof( ashort ));
351 data += sizeof( ashort );
355 memcpy(data, &dir->d_did, sizeof( u_int32_t ));
356 data += sizeof( u_int32_t );
360 if (!adp || (ad_getdate(adp, AD_DATE_CREATE, &aint) < 0))
361 aint = AD_DATE_FROM_UNIX(st->st_mtime);
362 memcpy(data, &aint, sizeof( aint ));
363 data += sizeof( aint );
367 if ( adp && (ad_getdate(adp, AD_DATE_MODIFY, &aint) == 0)) {
368 if ((st->st_mtime > AD_DATE_TO_UNIX(aint))) {
369 aint = AD_DATE_FROM_UNIX(st->st_mtime);
372 aint = AD_DATE_FROM_UNIX(st->st_mtime);
374 memcpy(data, &aint, sizeof( int ));
375 data += sizeof( int );
379 if (!adp || (ad_getdate(adp, AD_DATE_BACKUP, &aint) < 0))
380 aint = AD_DATE_START;
381 memcpy(data, &aint, sizeof( int ));
382 data += sizeof( int );
386 get_finderinfo(vol, upath, adp, (char *)data,S_ISLNK(st->st_mode));
387 data += ADEDLEN_FINDERI;
392 data += sizeof( u_int16_t );
396 memset(data, 0, sizeof(u_int16_t));
397 data += sizeof( u_int16_t );
401 memcpy(data, &id, sizeof( id ));
402 data += sizeof( id );
406 if (st->st_size > 0xffffffff)
409 aint = htonl( st->st_size );
410 memcpy(data, &aint, sizeof( aint ));
411 data += sizeof( aint );
416 if (adp->ad_rlen > 0xffffffff)
419 aint = htonl( adp->ad_rlen);
423 memcpy(data, &aint, sizeof( aint ));
424 data += sizeof( aint );
427 /* Current client needs ProDOS info block for this file.
428 Use simple heuristic and let the Mac "type" string tell
429 us what the PD file code should be. Everything gets a
430 subtype of 0x0000 unless the original value was hashed
431 to "pXYZ" when we created it. See IA, Ver 2.
432 <shirsch@adelphia.net> */
433 case FILPBIT_PDINFO :
434 if (afp_version >= 30) { /* UTF8 name */
435 utf8 = kTextEncodingUTF8;
437 data += sizeof( u_int16_t );
439 memcpy(data, &aint, sizeof( aint ));
440 data += sizeof( aint );
444 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
446 if ( memcmp( fdType, "TEXT", 4 ) == 0 ) {
450 else if ( memcmp( fdType, "PSYS", 4 ) == 0 ) {
454 else if ( memcmp( fdType, "PS16", 4 ) == 0 ) {
458 else if ( memcmp( fdType, "BINA", 4 ) == 0 ) {
462 else if ( fdType[0] == 'p' ) {
464 ashort = (fdType[2] * 256) + fdType[3];
478 memcpy(data, &ashort, sizeof( ashort ));
479 data += sizeof( ashort );
480 memset(data, 0, sizeof( ashort ));
481 data += sizeof( ashort );
484 case FILPBIT_EXTDFLEN:
485 aint = htonl(st->st_size >> 32);
486 memcpy(data, &aint, sizeof( aint ));
487 data += sizeof( aint );
488 aint = htonl(st->st_size);
489 memcpy(data, &aint, sizeof( aint ));
490 data += sizeof( aint );
492 case FILPBIT_EXTRFLEN:
495 aint = htonl(adp->ad_rlen >> 32);
496 memcpy(data, &aint, sizeof( aint ));
497 data += sizeof( aint );
499 aint = htonl(adp->ad_rlen);
500 memcpy(data, &aint, sizeof( aint ));
501 data += sizeof( aint );
503 case FILPBIT_UNIXPR :
504 /* accessmode may change st_mode with ACLs */
505 accessmode( upath, &ma, dir , st);
507 aint = htonl(st->st_uid);
508 memcpy( data, &aint, sizeof( aint ));
509 data += sizeof( aint );
510 aint = htonl(st->st_gid);
511 memcpy( data, &aint, sizeof( aint ));
512 data += sizeof( aint );
515 type == slnk indicates an OSX style symlink,
516 we have to add S_IFLNK to the mode, otherwise
517 10.3 clients freak out. */
521 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
522 if ( memcmp( fdType, "slnk", 4 ) == 0 ) {
528 memcpy( data, &aint, sizeof( aint ));
529 data += sizeof( aint );
531 *data++ = ma.ma_user;
532 *data++ = ma.ma_world;
533 *data++ = ma.ma_group;
534 *data++ = ma.ma_owner;
538 return( AFPERR_BITMAP );
544 ashort = htons( data - buf );
545 memcpy(l_nameoff, &ashort, sizeof( ashort ));
546 data = set_name(vol, data, dir->d_did, path->m_name, id, 0);
549 ashort = htons( data - buf );
550 memcpy(utf_nameoff, &ashort, sizeof( ashort ));
551 data = set_name(vol, data, dir->d_did, path->m_name, id, utf8);
553 *buflen = data - buf;
557 /* ----------------------- */
558 int getfilparams(struct vol *vol,
560 struct path *path, struct dir *dir,
561 char *buf, size_t *buflen )
563 struct adouble ad, *adp;
568 LOG(log_debug9, logtype_default, "begin getfilparams:");
571 opened = PARAM_NEED_ADP(bitmap);
576 int flags = (bitmap & (1 << FILPBIT_ATTR))?ADFLAGS_OPENFORKS:0;
578 adp = of_ad(vol, path, &ad);
579 upath = path->u_name;
581 if ( ad_metadata( upath, flags|ADFLAGS_CREATE, adp) < 0 ) {
584 LOG(log_error, logtype_afpd, "getfilparams(%s): %s: check resource fork permission?",
585 upath, strerror(errno));
586 return AFPERR_ACCESS;
588 LOG(log_error, logtype_afpd, "getfilparams(%s): bad resource fork", upath);
597 rc = getmetadata(vol, bitmap, path, dir, buf, buflen, adp);
599 ad_close_metadata( adp);
602 LOG(log_debug9, logtype_afpd, "end getfilparams:");
608 /* ----------------------------- */
609 int afp_createfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
611 struct adouble ad, *adp;
614 struct ofork *of = NULL;
616 int creatf, did, openf, retvalue = AFP_OK;
622 creatf = (unsigned char) *ibuf++;
624 memcpy(&vid, ibuf, sizeof( vid ));
625 ibuf += sizeof( vid );
627 if (NULL == ( vol = getvolbyvid( vid )) ) {
628 return( AFPERR_PARAM );
631 if (vol->v_flags & AFPVOL_RO)
634 memcpy(&did, ibuf, sizeof( did));
635 ibuf += sizeof( did );
637 if (NULL == ( dir = dirlookup( vol, did )) ) {
641 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
642 return get_afp_errno(AFPERR_PARAM);
645 if ( *s_path->m_name == '\0' ) {
646 return( AFPERR_BADTYPE );
649 upath = s_path->u_name;
651 /* if upath is deleted we already in trouble anyway */
652 if ((of = of_findname(s_path))) {
655 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
659 /* on a hard create, fail if file exists and is open */
662 openf = O_RDWR|O_CREAT|O_TRUNC;
664 /* on a soft create, if the file is open then ad_open won't fail
665 because open syscall is not called
670 openf = O_RDWR|O_CREAT|O_EXCL;
673 if ( ad_open( upath, ADFLAGS_DF|ADFLAGS_HF|ADFLAGS_NOHF|ADFLAGS_CREATE,
674 openf, 0666, adp) < 0 ) {
678 case ENOENT : /* we were already in 'did folder' so chdir() didn't fail */
679 return ( AFPERR_NOOBJ );
681 return( AFPERR_EXIST );
683 return( AFPERR_ACCESS );
686 return( AFPERR_DFULL );
688 return( AFPERR_PARAM );
691 if ( ad_reso_fileno( adp ) == -1 ) { /* Hard META / HF */
692 /* on noadouble volumes, just creating the data fork is ok */
693 if (vol_noadouble(vol)) {
694 ad_close( adp, ADFLAGS_DF );
695 goto createfile_done;
697 /* FIXME with hard create on an existing file, we already
698 * corrupted the data file.
700 netatalk_unlink( upath );
701 ad_close( adp, ADFLAGS_DF );
702 return AFPERR_ACCESS;
705 path = s_path->m_name;
706 ad_setname(adp, path);
709 fce_register_new_file(s_path);
711 ad_close( adp, ADFLAGS_DF|ADFLAGS_HF );
717 if (vol->v_flags & AFPVOL_DROPBOX) {
718 retvalue = matchfile2dirperms(upath, vol, did);
720 #endif /* DROPKLUDGE */
722 setvoltime(obj, vol );
727 int afp_setfilparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
733 u_int16_t vid, bitmap;
738 memcpy(&vid, ibuf, sizeof( vid ));
739 ibuf += sizeof( vid );
740 if (NULL == ( vol = getvolbyvid( vid )) ) {
741 return( AFPERR_PARAM );
744 if (vol->v_flags & AFPVOL_RO)
747 memcpy(&did, ibuf, sizeof( did ));
748 ibuf += sizeof( did );
749 if (NULL == ( dir = dirlookup( vol, did )) ) {
750 return afp_errno; /* was AFPERR_NOOBJ */
753 memcpy(&bitmap, ibuf, sizeof( bitmap ));
754 bitmap = ntohs( bitmap );
755 ibuf += sizeof( bitmap );
757 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
758 return get_afp_errno(AFPERR_PARAM);
761 if (path_isadir(s_path)) {
762 return( AFPERR_BADTYPE ); /* it's a directory */
765 if ( s_path->st_errno != 0 ) {
766 return( AFPERR_NOOBJ );
769 if ((u_long)ibuf & 1 ) {
773 if (AFP_OK == ( rc = setfilparams(vol, s_path, bitmap, ibuf )) ) {
774 setvoltime(obj, vol );
781 * cf AFP3.0.pdf page 252 for change_mdate and change_parent_mdate logic
784 extern struct path Cur_Path;
786 int setfilparams(struct vol *vol,
787 struct path *path, u_int16_t f_bitmap, char *buf )
789 struct adouble ad, *adp;
791 int bit, isad = 1, err = AFP_OK;
793 u_char achar, *fdType, xyy[4]; /* uninitialized, OK 310105 */
794 u_int16_t ashort, bshort, oshort;
797 u_int16_t upriv_bit = 0;
801 int change_mdate = 0;
802 int change_parent_mdate = 0;
807 u_int16_t bitmap = f_bitmap;
808 u_int32_t cdate,bdate;
809 u_char finder_buf[32];
812 LOG(log_debug9, logtype_afpd, "begin setfilparams:");
815 adp = of_ad(vol, path, &ad);
816 upath = path->u_name;
818 if (!vol_unix_priv(vol) && check_access(upath, OPENACC_WR ) < 0) {
819 return AFPERR_ACCESS;
822 /* with unix priv maybe we have to change adouble file priv first */
824 while ( bitmap != 0 ) {
825 while (( bitmap & 1 ) == 0 ) {
832 memcpy(&ashort, buf, sizeof( ashort ));
833 buf += sizeof( ashort );
837 memcpy(&cdate, buf, sizeof(cdate));
838 buf += sizeof( cdate );
841 memcpy(&newdate, buf, sizeof( newdate ));
842 buf += sizeof( newdate );
846 memcpy(&bdate, buf, sizeof( bdate));
847 buf += sizeof( bdate );
851 memcpy(finder_buf, buf, 32 );
852 if (memcmp(buf,"slnkrhap",8)==0 && !S_ISLNK(path->st.st_mode)){
857 char buf[PATH_MAX+1];
858 if ((fp=open(path->u_name,O_RDONLY))>=0){
859 if ((len=read(fp,buf,PATH_MAX+1))){
860 if (unlink(path->u_name)==0){
862 erc = symlink(buf, path->u_name);
871 goto setfilparam_done;
876 case FILPBIT_UNIXPR :
877 if (!vol_unix_priv(vol)) {
878 /* this volume doesn't use unix priv */
884 change_parent_mdate = 1;
886 memcpy( &aint, buf, sizeof( aint ));
887 f_uid = ntohl (aint);
888 buf += sizeof( aint );
889 memcpy( &aint, buf, sizeof( aint ));
890 f_gid = ntohl (aint);
891 buf += sizeof( aint );
892 setfilowner(vol, f_uid, f_gid, path);
894 memcpy( &upriv, buf, sizeof( upriv ));
895 buf += sizeof( upriv );
896 upriv = ntohl (upriv);
897 if ((upriv & S_IWUSR)) {
898 setfilunixmode(vol, path, upriv);
905 case FILPBIT_PDINFO :
906 if (afp_version < 30) { /* else it's UTF8 name */
909 /* Keep special case to support crlf translations */
910 if ((unsigned int) achar == 0x04) {
911 fdType = (u_char *)"TEXT";
914 xyy[0] = ( u_char ) 'p';
925 /* break while loop */
934 /* second try with adouble open
936 if ( ad_open_metadata( upath, 0, O_CREAT, adp) < 0) {
937 LOG(log_debug, logtype_afpd, "setfilparams: ad_open_metadata error");
939 * For some things, we don't need an adouble header:
940 * - change of modification date
941 * - UNIX privs (Bug-ID #2863424)
943 if (!vol_noadouble(vol) && (f_bitmap & ~(1<<FILPBIT_MDATE | 1<<FILPBIT_UNIXPR))) {
944 LOG(log_debug, logtype_afpd, "setfilparams: need adouble access");
945 return AFPERR_ACCESS;
947 LOG(log_debug, logtype_afpd, "setfilparams: no adouble perms, but only FILPBIT_MDATE and/or FILPBIT_UNIXPR");
949 } else if ((ad_get_HF_flags( adp ) & O_CREAT) ) {
950 ad_setname(adp, path->m_name);
955 while ( bitmap != 0 ) {
956 while (( bitmap & 1 ) == 0 ) {
963 ad_getattr(adp, &bshort);
965 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
966 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
970 if ((bshort & htons(ATTRBIT_INVISIBLE)) != (oshort & htons(ATTRBIT_INVISIBLE)))
971 change_parent_mdate = 1;
972 ad_setattr(adp, bshort);
975 ad_setdate(adp, AD_DATE_CREATE, cdate);
980 ad_setdate(adp, AD_DATE_BACKUP, bdate);
983 if (default_type( ad_entry( adp, ADEID_FINDERI ))
985 ((em = getextmap( path->m_name )) &&
986 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
987 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
988 || ((em = getdefextmap()) &&
989 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
990 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
992 memcpy(finder_buf, ufinderi, 8 );
994 memcpy(ad_entry( adp, ADEID_FINDERI ), finder_buf, 32 );
996 case FILPBIT_UNIXPR :
998 setfilunixmode(vol, path, upriv);
1001 case FILPBIT_PDINFO :
1002 if (afp_version < 30) { /* else it's UTF8 name */
1003 memcpy(ad_entry( adp, ADEID_FINDERI ), fdType, 4 );
1004 memcpy(ad_entry( adp, ADEID_FINDERI ) + 4, "pdos", 4 );
1009 err = AFPERR_BITMAP;
1010 goto setfilparam_done;
1017 if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
1018 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1022 ad_setdate(adp, AD_DATE_MODIFY, newdate);
1023 ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
1029 ad_close_metadata( adp);
1033 if (change_parent_mdate && gettimeofday(&tv, NULL) == 0) {
1034 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1035 bitmap = 1<<FILPBIT_MDATE;
1036 setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
1040 LOG(log_debug9, logtype_afpd, "end setfilparams:");
1046 * renamefile and copyfile take the old and new unix pathnames
1047 * and the new mac name.
1049 * sdir_fd source dir fd to which src path is relative (for openat et al semantics)
1050 * passing -1 means this is not used, src path is a full path
1051 * src the source path
1052 * dst the dest filename in current dir
1053 * newname the dest mac name
1054 * adp adouble struct of src file, if open, or & zeroed one
1057 int renamefile(const struct vol *vol, int sdir_fd, char *src, char *dst, char *newname, struct adouble *adp)
1061 if ( unix_rename( sdir_fd, src, -1, dst ) < 0 ) {
1064 return( AFPERR_NOOBJ );
1067 return( AFPERR_ACCESS );
1069 return AFPERR_VLOCK;
1070 case EXDEV : /* Cross device move -- try copy */
1071 /* NOTE: with open file it's an error because after the copy we will
1072 * get two files, it's fixable for our process (eg reopen the new file, get the
1073 * locks, and so on. But it doesn't solve the case with a second process
1075 if (adp->ad_open_forks) {
1076 /* FIXME warning in syslog so admin'd know there's a conflict ?*/
1077 return AFPERR_OLOCK; /* little lie */
1079 if (AFP_OK != ( rc = copyfile(vol, vol, sdir_fd, src, dst, newname, NULL )) ) {
1080 /* on error copyfile delete dest */
1083 return deletefile(vol, sdir_fd, src, 0);
1085 return( AFPERR_PARAM );
1089 if (vol->vfs->vfs_renamefile(vol, sdir_fd, src, dst) < 0 ) {
1093 /* try to undo the data fork rename,
1094 * we know we are on the same device
1097 unix_rename(-1, dst, sdir_fd, src );
1098 /* return the first error */
1101 return AFPERR_NOOBJ;
1104 return AFPERR_ACCESS ;
1106 return AFPERR_VLOCK;
1108 return AFPERR_PARAM ;
1113 /* don't care if we can't open the newly renamed ressource fork
1115 if (!ad_open( dst, ADFLAGS_HF, O_RDWR, 0666, adp)) {
1116 ad_setname(adp, newname);
1118 ad_close( adp, ADFLAGS_HF );
1125 convert a Mac long name to an utf8 name,
1127 size_t mtoUTF8(const struct vol *vol, const char *src, size_t srclen, char *dest, size_t destlen)
1131 if ((size_t)-1 == (outlen = convert_string ( vol->v_maccharset, CH_UTF8_MAC, src, srclen, dest, destlen)) ) {
1137 /* ---------------- */
1138 int copy_path_name(const struct vol *vol, char *newname, char *ibuf)
1145 if ( type != 2 && !(afp_version >= 30 && type == 3) ) {
1151 if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
1152 if (afp_version >= 30) {
1153 /* convert it to UTF8
1155 if ((plen = mtoUTF8(vol, ibuf, plen, newname, AFPOBJ_TMPSIZ)) == (size_t)-1)
1159 strncpy( newname, ibuf, plen );
1160 newname[ plen ] = '\0';
1162 if (strlen(newname) != plen) {
1163 /* there's \0 in newname, e.g. it's a pathname not
1171 memcpy(&hint, ibuf, sizeof(hint));
1172 ibuf += sizeof(hint);
1174 memcpy(&len16, ibuf, sizeof(len16));
1175 ibuf += sizeof(len16);
1176 plen = ntohs(len16);
1179 if (plen > AFPOBJ_TMPSIZ) {
1182 strncpy( newname, ibuf, plen );
1183 newname[ plen ] = '\0';
1184 if (strlen(newname) != plen) {
1193 /* -----------------------------------
1195 int afp_copyfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1197 struct vol *s_vol, *d_vol;
1199 char *newname, *p, *upath;
1200 struct path *s_path;
1201 u_int32_t sdid, ddid;
1202 int err, retvalue = AFP_OK;
1203 u_int16_t svid, dvid;
1205 struct adouble ad, *adp;
1211 memcpy(&svid, ibuf, sizeof( svid ));
1212 ibuf += sizeof( svid );
1213 if (NULL == ( s_vol = getvolbyvid( svid )) ) {
1214 return( AFPERR_PARAM );
1217 memcpy(&sdid, ibuf, sizeof( sdid ));
1218 ibuf += sizeof( sdid );
1219 if (NULL == ( dir = dirlookup( s_vol, sdid )) ) {
1223 memcpy(&dvid, ibuf, sizeof( dvid ));
1224 ibuf += sizeof( dvid );
1225 memcpy(&ddid, ibuf, sizeof( ddid ));
1226 ibuf += sizeof( ddid );
1228 if (NULL == ( s_path = cname( s_vol, dir, &ibuf )) ) {
1229 return get_afp_errno(AFPERR_PARAM);
1231 if ( path_isadir(s_path) ) {
1232 return( AFPERR_BADTYPE );
1235 /* don't allow copies when the file is open.
1236 * XXX: the spec only calls for read/deny write access.
1237 * however, copyfile doesn't have any of that info,
1238 * and locks need to stay coherent. as a result,
1239 * we just balk if the file is opened already. */
1241 adp = of_ad(s_vol, s_path, &ad);
1243 if (ad_open(s_path->u_name , ADFLAGS_DF |ADFLAGS_HF | ADFLAGS_NOHF, O_RDONLY, 0, adp) < 0) {
1244 return AFPERR_DENYCONF;
1246 denyreadset = (getforkmode(adp, ADEID_DFORK, AD_FILELOCK_DENY_RD) != 0 ||
1247 getforkmode(adp, ADEID_RFORK, AD_FILELOCK_DENY_RD) != 0 );
1250 retvalue = AFPERR_DENYCONF;
1254 newname = obj->newtmp;
1255 strcpy( newname, s_path->m_name );
1257 p = ctoupath( s_vol, curdir, newname );
1259 retvalue = AFPERR_PARAM;
1264 /* FIXME svid != dvid && dvid's user can't read svid */
1266 if (NULL == ( d_vol = getvolbyvid( dvid )) ) {
1267 retvalue = AFPERR_PARAM;
1271 if (d_vol->v_flags & AFPVOL_RO) {
1272 retvalue = AFPERR_VLOCK;
1276 if (NULL == ( dir = dirlookup( d_vol, ddid )) ) {
1277 retvalue = afp_errno;
1281 if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) {
1282 retvalue = get_afp_errno(AFPERR_NOOBJ);
1286 if ( *s_path->m_name != '\0' ) {
1287 retvalue =path_error(s_path, AFPERR_NOOBJ);
1291 /* one of the handful of places that knows about the path type */
1292 if (copy_path_name(d_vol, newname, ibuf) < 0) {
1293 retvalue = AFPERR_PARAM;
1296 /* newname is always only a filename so curdir *is* its
1299 if (NULL == (upath = mtoupath(d_vol, newname, curdir->d_did, utf8_encoding()))) {
1300 retvalue =AFPERR_PARAM;
1304 if ( (err = copyfile(s_vol, d_vol, -1, p, upath , newname, adp)) < 0 ) {
1311 if (vol->v_flags & AFPVOL_DROPBOX) {
1312 retvalue=matchfile2dirperms(upath, vol, ddid); /* FIXME sdir or ddid */
1314 #endif /* DROPKLUDGE */
1316 setvoltime(obj, d_vol );
1319 ad_close( adp, ADFLAGS_DF |ADFLAGS_HF );
1323 /* ----------------------- */
1324 static int copy_all(const int dfd, const void *buf,
1330 LOG(log_debug9, logtype_afpd, "begin copy_all:");
1333 while (buflen > 0) {
1334 if ((cc = write(dfd, buf, buflen)) < 0) {
1346 LOG(log_debug9, logtype_afpd, "end copy_all:");
1352 /* --------------------------
1353 * copy only the fork data stream
1355 static int copy_fork(int eid, struct adouble *add, struct adouble *ads)
1362 if (eid == ADEID_DFORK) {
1363 sfd = ad_data_fileno(ads);
1364 dfd = ad_data_fileno(add);
1367 sfd = ad_reso_fileno(ads);
1368 dfd = ad_reso_fileno(add);
1371 if ((off_t)-1 == lseek(sfd, ad_getentryoff(ads, eid), SEEK_SET))
1374 if ((off_t)-1 == lseek(dfd, ad_getentryoff(add, eid), SEEK_SET))
1377 #if 0 /* ifdef SENDFILE_FLAVOR_LINUX */
1378 /* doesn't work With 2.6 FIXME, only check for EBADFD ? */
1382 #define BUF 128*1024*1024
1384 if (fstat(sfd, &st) == 0) {
1387 if ( offset >= st.st_size) {
1390 size = (st.st_size -offset > BUF)?BUF:st.st_size -offset;
1391 if ((cc = sys_sendfile(dfd, sfd, &offset, size)) < 0) {
1394 case EINVAL: /* there's no guarantee that all fs support sendfile */
1403 lseek(sfd, offset, SEEK_SET);
1407 if ((cc = read(sfd, filebuf, sizeof(filebuf))) < 0) {
1414 if (!cc || ((err = copy_all(dfd, filebuf, cc)) < 0)) {
1421 /* ----------------------------------
1422 * if newname is NULL (from directory.c) we don't want to copy the resource fork.
1423 * because we are doing it elsewhere.
1424 * currently if newname is NULL then adp is NULL.
1426 int copyfile(const struct vol *s_vol,
1427 const struct vol *d_vol,
1432 struct adouble *adp)
1434 struct adouble ads, add;
1441 LOG(log_debug, logtype_afpd, "copyfile(sfd:%d,s:'%s',d:'%s',n:'%s')",
1442 sfd, src, dst, newname);
1445 ad_init(&ads, s_vol->v_adouble, s_vol->v_ad_options);
1449 adflags = ADFLAGS_DF;
1451 adflags |= ADFLAGS_HF;
1454 if (ad_openat(sfd, src, adflags | ADFLAGS_NOHF, O_RDONLY, 0, adp) < 0) {
1459 if (ad_meta_fileno(adp) == -1 && ad_reso_fileno(adp) == -1) { /* META / HF */
1460 /* no resource fork, don't create one for dst file */
1461 adflags &= ~ADFLAGS_HF;
1464 stat_result = fstat(ad_data_fileno(adp), &st); /* saving stat exit code, thus saving us on one more stat later on */
1466 if (stat_result < 0) {
1467 /* unlikely but if fstat fails, the default file mode will be 0666. */
1468 st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
1471 ad_init(&add, d_vol->v_adouble, d_vol->v_ad_options);
1472 if (ad_open(dst , adflags, O_RDWR|O_CREAT|O_EXCL, st.st_mode, &add) < 0) {
1474 ad_close( adp, adflags );
1475 if (EEXIST != ret_err) {
1476 deletefile(d_vol, -1, dst, 0);
1479 return AFPERR_EXIST;
1483 * XXX if the source and the dest don't use the same resource type it's broken
1485 if (ad_reso_fileno(adp) == -1 || 0 == (err = copy_fork(ADEID_RFORK, &add, adp))){
1486 /* copy the data fork */
1487 if ((err = copy_fork(ADEID_DFORK, &add, adp)) == 0) {
1488 err = d_vol->vfs->vfs_copyfile(d_vol, sfd, src, dst);
1496 if (!ret_err && newname && (adflags & ADFLAGS_HF)) {
1497 /* set the new name in the resource fork */
1498 ad_copy_header(&add, adp);
1499 ad_setname(&add, newname);
1502 ad_close( adp, adflags );
1504 if (ad_close( &add, adflags ) <0) {
1509 deletefile(d_vol, -1, dst, 0);
1511 else if (stat_result == 0) {
1512 /* set dest modification date to src date */
1515 ut.actime = ut.modtime = st.st_mtime;
1517 /* FIXME netatalk doesn't use resource fork file date
1518 * but maybe we should set its modtime too.
1523 switch ( ret_err ) {
1529 return AFPERR_DFULL;
1531 return AFPERR_NOOBJ;
1533 return AFPERR_ACCESS;
1535 return AFPERR_VLOCK;
1537 return AFPERR_PARAM;
1541 /* -----------------------------------
1542 vol: not NULL delete cnid entry. then we are in curdir and file is a only filename
1543 checkAttrib: 1 check kFPDeleteInhibitBit (deletfile called by afp_delete)
1545 when deletefile is called we don't have lock on it, file is closed (for us)
1546 untrue if called by renamefile
1548 ad_open always try to open file RDWR first and ad_lock takes care of
1549 WRITE lock on read only file.
1552 static int check_attrib(struct adouble *adp)
1554 u_int16_t bshort = 0;
1556 ad_getattr(adp, &bshort);
1558 * Does kFPDeleteInhibitBit (bit 8) set?
1560 if ((bshort & htons(ATTRBIT_NODELETE))) {
1561 return AFPERR_OLOCK;
1563 if ((bshort & htons(ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
1569 * dirfd can be used for unlinkat semantics
1571 int deletefile(const struct vol *vol, int dirfd, char *file, int checkAttrib)
1574 struct adouble *adp = NULL;
1575 int adflags, err = AFP_OK;
1578 LOG(log_debug, logtype_afpd, "deletefile('%s')", file);
1580 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
1582 /* was EACCESS error try to get only metadata */
1583 /* we never want to create a resource fork here, we are going to delete it
1584 * moreover sometimes deletefile is called with a no existent file and
1585 * ad_open would create a 0 byte resource fork
1587 if ( ad_metadataat(dirfd, file, ADFLAGS_OPENFORKS, &ad) == 0 ) {
1588 if ((err = check_attrib(&ad))) {
1589 ad_close_metadata(&ad);
1596 /* try to open both forks at once */
1597 adflags = ADFLAGS_DF;
1598 if ( ad_openat(dirfd, file, adflags |ADFLAGS_HF|ADFLAGS_NOHF, O_RDONLY, 0, &ad ) < 0 ) {
1603 case EACCES: /* maybe it's a file with no write mode for us */
1604 break; /* was return AFPERR_ACCESS;*/
1617 if ( adp && ad_reso_fileno( adp ) != -1 ) { /* there's a resource fork */
1618 adflags |= ADFLAGS_HF;
1619 /* FIXME we have a pb here because we want to know if a file is open
1620 * there's a 'priority inversion' if you can't open the ressource fork RW
1621 * you can delete it if it's open because you can't get a write lock.
1623 * ADLOCK_FILELOCK means the whole ressource fork, not only after the
1626 * FIXME it doesn't work for RFORK open read only and fork open without deny mode
1628 if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
1634 if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
1636 } else if (!(err = vol->vfs->vfs_deletefile(vol, dirfd, file)) && !(err = netatalk_unlinkat(dirfd, file )) ) {
1638 if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file)))) {
1639 cnid_delete(vol->v_cdb, id);
1645 ad_close_metadata(&ad);
1648 ad_close( &ad, adflags ); /* ad_close removes locks if any */
1653 /* ------------------------------------ */
1654 /* return a file id */
1655 int afp_createid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1664 struct path *s_path;
1670 memcpy(&vid, ibuf, sizeof(vid));
1671 ibuf += sizeof(vid);
1673 if (NULL == ( vol = getvolbyvid( vid )) ) {
1674 return( AFPERR_PARAM);
1677 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1681 if (vol->v_flags & AFPVOL_RO)
1682 return AFPERR_VLOCK;
1684 memcpy(&did, ibuf, sizeof( did ));
1685 ibuf += sizeof(did);
1687 if (NULL == ( dir = dirlookup( vol, did )) ) {
1688 return afp_errno; /* was AFPERR_PARAM */
1691 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
1692 return get_afp_errno(AFPERR_NOOBJ); /* was AFPERR_PARAM */
1695 if ( path_isadir(s_path) ) {
1696 return( AFPERR_BADTYPE );
1699 upath = s_path->u_name;
1700 switch (s_path->st_errno) {
1702 break; /* success */
1705 return AFPERR_ACCESS;
1707 return AFPERR_NOOBJ;
1709 return AFPERR_PARAM;
1712 if ((id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath)))) {
1713 memcpy(rbuf, &id, sizeof(id));
1714 *rbuflen = sizeof(id);
1715 return AFPERR_EXISTID;
1718 if ((id = get_id(vol, NULL, st, did, upath, len)) != CNID_INVALID) {
1719 memcpy(rbuf, &id, sizeof(id));
1720 *rbuflen = sizeof(id);
1727 /* ------------------------------- */
1733 static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
1736 struct reenum *param = data;
1737 struct vol *vol = param->vol;
1738 cnid_t did = param->did;
1741 if ( lstat(de->d_name, &path.st)<0 )
1744 /* update or add to cnid */
1745 aint = cnid_add(vol->v_cdb, &path.st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */
1747 #if AD_VERSION > AD_VERSION1
1748 if (aint != CNID_INVALID && !S_ISDIR(path.st.st_mode)) {
1749 struct adouble ad, *adp;
1753 path.u_name = de->d_name;
1755 adp = of_ad(vol, &path, &ad);
1757 if ( ad_open_metadata( de->d_name, 0, 0, adp ) < 0 ) {
1760 if (ad_setid(adp, path.st.st_dev, path.st.st_ino, aint, did, vol->v_stamp)) {
1763 ad_close_metadata(adp);
1765 #endif /* AD_VERSION > AD_VERSION1 */
1770 /* --------------------
1771 * Ok the db is out of synch with the dir.
1772 * but if it's a deleted file we don't want to do it again and again.
1775 reenumerate_id(struct vol *vol, char *name, struct dir *dir)
1781 if (vol->v_cdb == NULL) {
1785 /* FIXME use of_statdir ? */
1786 if (lstat(name, &st)) {
1790 if (dirreenumerate(dir, &st)) {
1791 /* we already did it once and the dir haven't been modified */
1796 data.did = dir->d_did;
1797 if ((ret = for_each_dirent(vol, name, reenumerate_loop, (void *)&data)) >= 0) {
1798 setdiroffcnt(curdir, &st, ret);
1799 dir->d_flags |= DIRF_CNID;
1805 /* ------------------------------
1806 resolve a file id */
1807 int afp_resolveid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1816 u_int16_t vid, bitmap;
1818 static char buffer[12 + MAXPATHLEN + 1];
1819 int len = 12 + MAXPATHLEN + 1;
1824 memcpy(&vid, ibuf, sizeof(vid));
1825 ibuf += sizeof(vid);
1827 if (NULL == ( vol = getvolbyvid( vid )) ) {
1828 return( AFPERR_PARAM);
1831 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1835 memcpy(&id, ibuf, sizeof( id ));
1840 /* some MacOS versions after a catsearch do a *lot* of afp_resolveid with 0 */
1844 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1845 return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
1848 if (NULL == ( dir = dirlookup( vol, id )) ) {
1849 return AFPERR_NOID; /* idem AFPERR_PARAM */
1851 if (movecwd(vol, dir) < 0) {
1855 return AFPERR_ACCESS;
1859 return AFPERR_PARAM;
1863 memset(&path, 0, sizeof(path));
1864 path.u_name = upath;
1865 if ( of_stat(&path) < 0 ) {
1867 /* with nfs and our working directory is deleted */
1868 if (errno == ESTALE) {
1872 if ( errno == ENOENT && !retry) {
1873 /* cnid db is out of sync, reenumerate the directory and update ids */
1874 reenumerate_id(vol, ".", dir);
1882 return AFPERR_ACCESS;
1886 return AFPERR_PARAM;
1890 /* directories are bad */
1891 if (S_ISDIR(path.st.st_mode)) {
1892 /* OS9 and OSX don't return the same error code */
1893 return (afp_version >=30)?AFPERR_NOID:AFPERR_BADTYPE;
1896 memcpy(&bitmap, ibuf, sizeof(bitmap));
1897 bitmap = ntohs( bitmap );
1898 if (NULL == (path.m_name = utompath(vol, upath, cnid, utf8_encoding()))) {
1902 if (AFP_OK != (err = getfilparams(vol, bitmap, &path , curdir,
1903 rbuf + sizeof(bitmap), &buflen))) {
1906 *rbuflen = buflen + sizeof(bitmap);
1907 memcpy(rbuf, ibuf, sizeof(bitmap));
1912 /* ------------------------------ */
1913 int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1923 static char buffer[12 + MAXPATHLEN + 1];
1924 int len = 12 + MAXPATHLEN + 1;
1929 memcpy(&vid, ibuf, sizeof(vid));
1930 ibuf += sizeof(vid);
1932 if (NULL == ( vol = getvolbyvid( vid )) ) {
1933 return( AFPERR_PARAM);
1936 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1940 if (vol->v_flags & AFPVOL_RO)
1941 return AFPERR_VLOCK;
1943 memcpy(&id, ibuf, sizeof( id ));
1947 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1951 if (NULL == ( dir = dirlookup( vol, id )) ) {
1952 return( AFPERR_PARAM );
1956 if ((movecwd(vol, dir) < 0) || (lstat(upath, &st) < 0)) {
1960 return AFPERR_ACCESS;
1965 /* still try to delete the id */
1969 return AFPERR_PARAM;
1972 else if (S_ISDIR(st.st_mode)) /* directories are bad */
1973 return AFPERR_BADTYPE;
1975 if (cnid_delete(vol->v_cdb, fileid)) {
1978 return AFPERR_VLOCK;
1981 return AFPERR_ACCESS;
1983 return AFPERR_PARAM;
1990 /* ------------------------------ */
1991 static struct adouble *find_adouble(struct path *path, struct ofork **of, struct adouble *adp)
1995 if (path->st_errno) {
1996 switch (path->st_errno) {
1998 afp_errno = AFPERR_NOID;
2002 afp_errno = AFPERR_ACCESS;
2005 afp_errno = AFPERR_PARAM;
2010 /* we use file_access both for legacy Mac perm and
2011 * for unix privilege, rename will take care of folder perms
2013 if (file_access(path, OPENACC_WR ) < 0) {
2014 afp_errno = AFPERR_ACCESS;
2018 if ((*of = of_findname(path))) {
2019 /* reuse struct adouble so it won't break locks */
2023 ret = ad_open( path->u_name, ADFLAGS_HF, O_RDONLY, 0, adp);
2025 if ( !ret && ad_reso_fileno(adp) != -1 && !(adp->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
2027 * The user must have the Read & Write privilege for both files in order to use this command.
2029 ad_close(adp, ADFLAGS_HF);
2030 afp_errno = AFPERR_ACCESS;
2037 #define APPLETEMP ".AppleTempXXXXXX"
2039 int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
2041 struct stat srcst, destst;
2043 struct dir *dir, *sdir;
2044 char *spath, temp[17], *p;
2045 char *supath, *upath;
2050 struct adouble *adsp = NULL;
2051 struct adouble *addp = NULL;
2052 struct ofork *s_of = NULL;
2053 struct ofork *d_of = NULL;
2066 memcpy(&vid, ibuf, sizeof(vid));
2067 ibuf += sizeof(vid);
2069 if (NULL == ( vol = getvolbyvid( vid )) ) {
2070 return( AFPERR_PARAM);
2073 if ((vol->v_flags & AFPVOL_RO))
2074 return AFPERR_VLOCK;
2076 /* source and destination dids */
2077 memcpy(&sid, ibuf, sizeof(sid));
2078 ibuf += sizeof(sid);
2079 memcpy(&did, ibuf, sizeof(did));
2080 ibuf += sizeof(did);
2083 if (NULL == (dir = dirlookup( vol, sid )) ) {
2084 return afp_errno; /* was AFPERR_PARAM */
2087 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2088 return get_afp_errno(AFPERR_NOOBJ);
2091 if ( path_isadir(path) ) {
2092 return AFPERR_BADTYPE; /* it's a dir */
2095 /* save some stuff */
2098 spath = obj->oldtmp;
2099 supath = obj->newtmp;
2100 strcpy(spath, path->m_name);
2101 strcpy(supath, path->u_name); /* this is for the cnid changing */
2102 p = absupath( vol, sdir, supath);
2104 /* pathname too long */
2105 return AFPERR_PARAM ;
2108 ad_init(&ads, vol->v_adouble, vol->v_ad_options);
2109 if (!(adsp = find_adouble( path, &s_of, &ads))) {
2113 /* ***** from here we may have resource fork open **** */
2115 /* look for the source cnid. if it doesn't exist, don't worry about
2117 sid = cnid_lookup(vol->v_cdb, &srcst, sdir->d_did, supath,slen = strlen(supath));
2119 if (NULL == ( dir = dirlookup( vol, did )) ) {
2120 err = afp_errno; /* was AFPERR_PARAM */
2121 goto err_exchangefile;
2124 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2125 err = get_afp_errno(AFPERR_NOOBJ);
2126 goto err_exchangefile;
2129 if ( path_isadir(path) ) {
2130 err = AFPERR_BADTYPE;
2131 goto err_exchangefile;
2134 /* FPExchangeFiles is the only call that can return the SameObj
2136 if ((curdir == sdir) && strcmp(spath, path->m_name) == 0) {
2137 err = AFPERR_SAMEOBJ;
2138 goto err_exchangefile;
2141 ad_init(&add, vol->v_adouble, vol->v_ad_options);
2142 if (!(addp = find_adouble( path, &d_of, &add))) {
2144 goto err_exchangefile;
2148 /* they are not on the same device and at least one is open
2149 * FIXME broken for for crossdev and adouble v2
2152 crossdev = (srcst.st_dev != destst.st_dev);
2153 if (/* (d_of || s_of) && */ crossdev) {
2155 goto err_exchangefile;
2158 /* look for destination id. */
2159 upath = path->u_name;
2160 did = cnid_lookup(vol->v_cdb, &destst, curdir->d_did, upath, dlen = strlen(upath));
2162 /* construct a temp name.
2163 * NOTE: the temp file will be in the dest file's directory. it
2164 * will also be inaccessible from AFP. */
2165 memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
2166 if (!mktemp(temp)) {
2168 goto err_exchangefile;
2172 /* FIXME we need to close fork for copy, both s_of and d_of are null */
2173 ad_close(adsp, ADFLAGS_HF);
2174 ad_close(addp, ADFLAGS_HF);
2177 /* now, quickly rename the file. we error if we can't. */
2178 if ((err = renamefile(vol, -1, p, temp, temp, adsp)) != AFP_OK)
2179 goto err_exchangefile;
2180 of_rename(vol, s_of, sdir, spath, curdir, temp);
2182 /* rename destination to source */
2183 if ((err = renamefile(vol, -1, upath, p, spath, addp)) != AFP_OK)
2184 goto err_src_to_tmp;
2185 of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
2187 /* rename temp to destination */
2188 if ((err = renamefile(vol, -1, temp, upath, path->m_name, adsp)) != AFP_OK)
2189 goto err_dest_to_src;
2190 of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
2192 /* id's need switching. src -> dest and dest -> src.
2193 * we need to re-stat() if it was a cross device copy.
2196 cnid_delete(vol->v_cdb, sid);
2199 cnid_delete(vol->v_cdb, did);
2201 if ((did && ( (crossdev && lstat( upath, &srcst) < 0) ||
2202 cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
2204 (sid && ( (crossdev && lstat(p, &destst) < 0) ||
2205 cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
2210 err = AFPERR_ACCESS;
2215 goto err_temp_to_dest;
2218 /* here we need to reopen if crossdev */
2219 if (sid && ad_setid(addp, destst.st_dev, destst.st_ino, sid, sdir->d_did, vol->v_stamp))
2224 if (did && ad_setid(adsp, srcst.st_dev, srcst.st_ino, did, curdir->d_did, vol->v_stamp))
2229 /* change perms, src gets dest perm and vice versa */
2234 LOG(log_error, logtype_afpd, "seteuid failed %s", strerror(errno));
2235 err = AFP_OK; /* ignore error */
2236 goto err_temp_to_dest;
2240 * we need to exchange ACL entries as well
2242 /* exchange_acls(vol, p, upath); */
2247 path->m_name = NULL;
2248 path->u_name = upath;
2250 setfilunixmode(vol, path, destst.st_mode);
2251 setfilowner(vol, destst.st_uid, destst.st_gid, path);
2258 setfilunixmode(vol, path, srcst.st_mode);
2259 setfilowner(vol, srcst.st_uid, srcst.st_gid, path);
2261 if ( setegid(gid) < 0 || seteuid(uid) < 0) {
2262 LOG(log_error, logtype_afpd, "can't seteuid back %s", strerror(errno));
2267 goto err_exchangefile;
2269 /* all this stuff is so that we can unwind a failed operation
2272 /* rename dest to temp */
2273 renamefile(vol, -1, upath, temp, temp, adsp);
2274 of_rename(vol, s_of, curdir, upath, curdir, temp);
2277 /* rename source back to dest */
2278 renamefile(vol, -1, p, upath, path->m_name, addp);
2279 of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
2282 /* rename temp back to source */
2283 renamefile(vol, -1, temp, p, spath, adsp);
2284 of_rename(vol, s_of, curdir, temp, sdir, spath);
2287 if ( !s_of && adsp && ad_meta_fileno(adsp) != -1 ) { /* META */
2288 ad_close(adsp, ADFLAGS_HF);
2290 if ( !d_of && addp && ad_meta_fileno(addp) != -1 ) {/* META */
2291 ad_close(addp, ADFLAGS_HF);