2 * Copyright (c) 1990,1993 Regents of The University of Michigan.
3 * All Rights Reserved. See COPYRIGHT.
8 #endif /* HAVE_CONFIG_H */
16 #include <sys/param.h>
18 #include <atalk/adouble.h>
19 #include <atalk/vfs.h>
20 #include <atalk/logger.h>
21 #include <atalk/afp.h>
22 #include <atalk/util.h>
23 #include <atalk/cnid.h>
24 #include <atalk/unix.h>
25 #include <atalk/globals.h>
26 #include <atalk/fce_api.h>
28 #include "directory.h"
37 /* the format for the finderinfo fields (from IM: Toolbox Essentials):
38 * field bytes subfield bytes
41 * ioFlFndrInfo 16 -> type 4 type field
42 * creator 4 creator field
43 * flags 2 finder flags:
45 * location 4 location in window
46 * folder 2 window that contains file
48 * ioFlXFndrInfo 16 -> iconID 2 icon id
50 * script 1 script system
52 * commentID 2 comment id
53 * putawayID 4 home directory id
56 const u_char ufinderi[ADEDLEN_FINDERI] = {
57 0, 0, 0, 0, 0, 0, 0, 0,
58 1, 0, 0, 0, 0, 0, 0, 0,
59 0, 0, 0, 0, 0, 0, 0, 0,
60 0, 0, 0, 0, 0, 0, 0, 0
63 static const u_char old_ufinderi[] = {
64 'T', 'E', 'X', 'T', 'U', 'N', 'I', 'X'
67 /* ----------------------
69 static int default_type(void *finder)
71 if (!memcmp(finder, ufinderi, 8) || !memcmp(finder, old_ufinderi, 8))
76 /* FIXME path : unix or mac name ? (for now it's unix name ) */
77 void *get_finderinfo(const struct vol *vol, const char *upath, struct adouble *adp, void *data, int islink)
80 void *ad_finder = NULL;
84 ad_finder = ad_entry(adp, ADEID_FINDERI);
87 memcpy(data, ad_finder, ADEDLEN_FINDERI);
89 if (default_type(ad_finder))
93 memcpy(data, ufinderi, ADEDLEN_FINDERI);
95 if (vol_inv_dots(vol) && *upath == '.') { /* make it invisible */
98 ashort = htons(FINDERINFO_INVISIBLE);
99 memcpy((char *)data + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
105 memcpy(&linkflag, (char *)data + FINDERINFO_FRFLAGOFF, 2);
106 linkflag |= htons(FINDERINFO_ISALIAS);
107 memcpy((char *)data + FINDERINFO_FRFLAGOFF, &linkflag, 2);
108 memcpy((char *)data + FINDERINFO_FRTYPEOFF,"slnk",4);
109 memcpy((char *)data + FINDERINFO_FRCREATOFF,"rhap",4);
113 /** Only enter if no appledouble information and no finder information found. */
114 if (chk_ext && (em = getextmap( upath ))) {
115 memcpy(data, em->em_type, sizeof( em->em_type ));
116 memcpy((char *)data + 4, em->em_creator, sizeof(em->em_creator));
121 /* ---------------------
123 char *set_name(const struct vol *vol, char *data, cnid_t pid, char *name, cnid_t id, uint32_t utf8)
128 aint = strlen( name );
132 if (utf8_encoding()) {
133 /* but name is an utf8 mac name */
136 /* global static variable... */
138 if (!(u = mtoupath(vol, name, pid, 1)) || !(m = utompath(vol, u, id, 0))) {
147 if (aint > MACFILELEN)
154 if (aint > UTF8FILELEN_EARLY) /* FIXME safeguard, anyway if no ascii char it's game over*/
155 aint = UTF8FILELEN_EARLY;
157 utf8 = vol->v_kTextEncoding;
158 memcpy(data, &utf8, sizeof(utf8));
159 data += sizeof(utf8);
162 memcpy(data, &temp, sizeof(temp));
163 data += sizeof(temp);
166 memcpy( data, src, aint );
176 * FIXME: PDINFO is UTF8 and doesn't need adp
178 #define PARAM_NEED_ADP(b) ((b) & ((1 << FILPBIT_ATTR) |\
179 (1 << FILPBIT_CDATE) |\
180 (1 << FILPBIT_MDATE) |\
181 (1 << FILPBIT_BDATE) |\
182 (1 << FILPBIT_FINFO) |\
183 (1 << FILPBIT_RFLEN) |\
184 (1 << FILPBIT_EXTRFLEN) |\
185 (1 << FILPBIT_PDINFO) |\
186 (1 << FILPBIT_FNUM) |\
187 (1 << FILPBIT_UNIXPR)))
190 * @brief Get CNID for did/upath args both from database and adouble file
192 * 1. Get the objects CNID as stored in its adouble file
193 * 2. Get the objects CNID from the database
194 * 3. If there's a problem with a "dbd" database, fallback to "tdb" in memory
195 * 4. In case 2 and 3 differ, store 3 in the adouble file
197 * @param vol (rw) volume
198 * @param adp (rw) adouble struct of object upath, might be NULL
199 * @param st (r) stat of upath, must NOT be NULL
200 * @param did (r) parent CNID of upath
201 * @param upath (r) name of object
202 * @param len (r) strlen of upath
204 uint32_t get_id(struct vol *vol,
206 const struct stat *st,
211 static int first = 1; /* mark if this func is called the first time */
213 uint32_t dbcnid = CNID_INVALID;
216 if (vol->v_cdb != NULL) {
217 /* prime aint with what we think is the cnid, set did to zero for
218 catching moved files */
219 adcnid = ad_getid(adp, st->st_dev, st->st_ino, 0, vol->v_stamp); /* (1) */
221 dbcnid = cnid_add(vol->v_cdb, st, did, upath, len, adcnid); /* (2) */
222 /* Throw errors if cnid_add fails. */
223 if (dbcnid == CNID_INVALID) {
225 case CNID_ERR_CLOSE: /* the db is closed */
228 LOG(log_error, logtype_afpd, "get_id: Incorrect parameters passed to cnid_add");
229 afp_errno = AFPERR_PARAM;
232 afp_errno = AFPERR_PARAM;
235 /* Close CNID backend if "dbd" and switch to temp in-memory "tdb" */
236 /* we have to do it here for "dbd" because it uses "lazy opening" */
237 /* In order to not end in a loop somehow with goto restart below */
239 if (first && (strcmp(vol->v_cnidscheme, "dbd") == 0)) { /* (3) */
240 cnid_close(vol->v_cdb);
241 free(vol->v_cnidscheme);
242 vol->v_cnidscheme = strdup("tdb");
244 int flags = CNID_FLAG_MEMORY;
245 if ((vol->v_flags & AFPVOL_NODEV)) {
246 flags |= CNID_FLAG_NODEV;
248 LOG(log_error, logtype_afpd, "Reopen volume %s using in memory temporary CNID DB.",
250 vol->v_cdb = cnid_open(vol->v_path, vol->v_umask, "tdb", flags, NULL, NULL);
253 vol->v_flags |= AFPVOL_RO;
255 /* kill ourself with SIGUSR2 aka msg pending */
256 setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB instead."
257 "Check server messages for details. Switching to read-only mode.");
258 kill(getpid(), SIGUSR2);
260 goto restart; /* not try again with the temp CNID db */
263 setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB failed too!"
264 "Check server messages for details, can't recover from this state!");
268 afp_errno = AFPERR_MISC;
272 else if (adp && (adcnid != dbcnid)) { /* 4 */
273 /* Update the ressource fork. For a folder adp is always null */
274 LOG(log_debug, logtype_afpd, "get_id(%s/%s): calling ad_setid(old: %u, new: %u)",
275 getcwdpath(), upath, htonl(adcnid), htonl(dbcnid));
276 if (ad_setid(adp, st->st_dev, st->st_ino, dbcnid, did, vol->v_stamp)) {
287 /* -------------------------- */
288 int getmetadata(struct vol *vol,
290 struct path *path, struct dir *dir,
291 char *buf, size_t *buflen, struct adouble *adp)
293 char *data, *l_nameoff = NULL, *upath;
294 char *utf_nameoff = NULL;
299 u_char achar, fdType[4];
304 LOG(log_debug, logtype_afpd, "getmetadata(\"%s\")", path->u_name);
306 upath = path->u_name;
310 if ( ((bitmap & ( (1 << FILPBIT_FINFO)|(1 << FILPBIT_LNAME)|(1 <<FILPBIT_PDINFO) ) ) && !path->m_name)
311 || (bitmap & ( (1 << FILPBIT_LNAME) ) && utf8_encoding()) /* FIXME should be m_name utf8 filename */
312 || (bitmap & (1 << FILPBIT_FNUM))) {
315 struct dir *cachedfile;
316 int len = strlen(upath);
317 if ((cachedfile = dircache_search_by_name(vol, dir, upath, len)) != NULL)
318 id = cachedfile->d_did;
320 id = get_id(vol, adp, st, dir->d_did, upath, len);
322 /* Add it to the cache */
323 LOG(log_debug, logtype_afpd, "getmetadata: caching: did:%u, \"%s\", cnid:%u",
324 ntohl(dir->d_did), upath, ntohl(id));
326 /* Get macname from unixname first */
327 if (path->m_name == NULL) {
328 if ((path->m_name = utompath(vol, upath, id, utf8_encoding())) == NULL) {
329 LOG(log_error, logtype_afpd, "getmetadata: utompath error");
335 if (((fullpath = bstrcpy(dir->d_fullpath)) == NULL)
336 || (bconchar(fullpath, '/') != BSTR_OK)
337 || (bcatcstr(fullpath, upath)) != BSTR_OK) {
338 LOG(log_error, logtype_afpd, "getmetadata: fullpath: %s", strerror(errno));
342 if ((cachedfile = dir_new(path->m_name, upath, vol, dir->d_did, id, fullpath, st)) == NULL) {
343 LOG(log_error, logtype_afpd, "getmetadata: error from dir_new");
347 if ((dircache_add(vol, cachedfile)) != 0) {
348 LOG(log_error, logtype_afpd, "getmetadata: fatal dircache error");
356 if (id == CNID_INVALID)
360 path->m_name = utompath(vol, upath, id, utf8_encoding());
363 while ( bitmap != 0 ) {
364 while (( bitmap & 1 ) == 0 ) {
372 ad_getattr(adp, &ashort);
373 } else if (vol_inv_dots(vol) && *upath == '.') {
374 ashort = htons(ATTRBIT_INVISIBLE);
378 /* FIXME do we want a visual clue if the file is read only
381 accessmode(vol, ".", &ma, dir , NULL);
382 if ((ma.ma_user & AR_UWRITE)) {
383 accessmode(vol, upath, &ma, dir , st);
384 if (!(ma.ma_user & AR_UWRITE)) {
385 ashort |= htons(ATTRBIT_NOWRITE);
389 memcpy(data, &ashort, sizeof( ashort ));
390 data += sizeof( ashort );
391 LOG(log_debug, logtype_afpd, "metadata('%s'): AFP Attributes: %04x",
392 path->u_name, ntohs(ashort));
396 memcpy(data, &dir->d_did, sizeof( uint32_t ));
397 data += sizeof( uint32_t );
398 LOG(log_debug, logtype_afpd, "metadata('%s'): Parent DID: %u",
399 path->u_name, ntohl(dir->d_did));
403 if (!adp || (ad_getdate(adp, AD_DATE_CREATE, &aint) < 0))
404 aint = AD_DATE_FROM_UNIX(st->st_mtime);
405 memcpy(data, &aint, sizeof( aint ));
406 data += sizeof( aint );
410 if ( adp && (ad_getdate(adp, AD_DATE_MODIFY, &aint) == 0)) {
411 if ((st->st_mtime > AD_DATE_TO_UNIX(aint))) {
412 aint = AD_DATE_FROM_UNIX(st->st_mtime);
415 aint = AD_DATE_FROM_UNIX(st->st_mtime);
417 memcpy(data, &aint, sizeof( int ));
418 data += sizeof( int );
422 if (!adp || (ad_getdate(adp, AD_DATE_BACKUP, &aint) < 0))
423 aint = AD_DATE_START;
424 memcpy(data, &aint, sizeof( int ));
425 data += sizeof( int );
429 get_finderinfo(vol, upath, adp, (char *)data,S_ISLNK(st->st_mode));
430 data += ADEDLEN_FINDERI;
435 data += sizeof( uint16_t );
439 memset(data, 0, sizeof(uint16_t));
440 data += sizeof( uint16_t );
444 memcpy(data, &id, sizeof( id ));
445 data += sizeof( id );
446 LOG(log_debug, logtype_afpd, "metadata('%s'): CNID: %u",
447 path->u_name, ntohl(id));
451 if (st->st_size > 0xffffffff)
454 aint = htonl( st->st_size );
455 memcpy(data, &aint, sizeof( aint ));
456 data += sizeof( aint );
461 if (adp->ad_rlen > 0xffffffff)
464 aint = htonl( adp->ad_rlen);
468 memcpy(data, &aint, sizeof( aint ));
469 data += sizeof( aint );
472 /* Current client needs ProDOS info block for this file.
473 Use simple heuristic and let the Mac "type" string tell
474 us what the PD file code should be. Everything gets a
475 subtype of 0x0000 unless the original value was hashed
476 to "pXYZ" when we created it. See IA, Ver 2.
477 <shirsch@adelphia.net> */
478 case FILPBIT_PDINFO :
479 if (afp_version >= 30) { /* UTF8 name */
480 utf8 = kTextEncodingUTF8;
482 data += sizeof( uint16_t );
484 memcpy(data, &aint, sizeof( aint ));
485 data += sizeof( aint );
489 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
491 if ( memcmp( fdType, "TEXT", 4 ) == 0 ) {
495 else if ( memcmp( fdType, "PSYS", 4 ) == 0 ) {
499 else if ( memcmp( fdType, "PS16", 4 ) == 0 ) {
503 else if ( memcmp( fdType, "BINA", 4 ) == 0 ) {
507 else if ( fdType[0] == 'p' ) {
509 ashort = (fdType[2] * 256) + fdType[3];
523 memcpy(data, &ashort, sizeof( ashort ));
524 data += sizeof( ashort );
525 memset(data, 0, sizeof( ashort ));
526 data += sizeof( ashort );
529 case FILPBIT_EXTDFLEN:
530 aint = htonl(st->st_size >> 32);
531 memcpy(data, &aint, sizeof( aint ));
532 data += sizeof( aint );
533 aint = htonl(st->st_size);
534 memcpy(data, &aint, sizeof( aint ));
535 data += sizeof( aint );
537 case FILPBIT_EXTRFLEN:
540 aint = htonl(adp->ad_rlen >> 32);
541 memcpy(data, &aint, sizeof( aint ));
542 data += sizeof( aint );
544 aint = htonl(adp->ad_rlen);
545 memcpy(data, &aint, sizeof( aint ));
546 data += sizeof( aint );
548 case FILPBIT_UNIXPR :
549 /* accessmode may change st_mode with ACLs */
550 accessmode(vol, upath, &ma, dir , st);
552 aint = htonl(st->st_uid);
553 memcpy( data, &aint, sizeof( aint ));
554 data += sizeof( aint );
555 aint = htonl(st->st_gid);
556 memcpy( data, &aint, sizeof( aint ));
557 data += sizeof( aint );
560 type == slnk indicates an OSX style symlink,
561 we have to add S_IFLNK to the mode, otherwise
562 10.3 clients freak out. */
566 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
567 if ( memcmp( fdType, "slnk", 4 ) == 0 ) {
573 memcpy( data, &aint, sizeof( aint ));
574 data += sizeof( aint );
576 *data++ = ma.ma_user;
577 *data++ = ma.ma_world;
578 *data++ = ma.ma_group;
579 *data++ = ma.ma_owner;
583 return( AFPERR_BITMAP );
589 ashort = htons( data - buf );
590 memcpy(l_nameoff, &ashort, sizeof( ashort ));
591 data = set_name(vol, data, dir->d_did, path->m_name, id, 0);
594 ashort = htons( data - buf );
595 memcpy(utf_nameoff, &ashort, sizeof( ashort ));
596 data = set_name(vol, data, dir->d_did, path->m_name, id, utf8);
598 *buflen = data - buf;
602 /* ----------------------- */
603 int getfilparams(struct vol *vol,
605 struct path *path, struct dir *dir,
606 char *buf, size_t *buflen )
608 struct adouble ad, *adp;
613 LOG(log_debug, logtype_afpd, "getfilparams(\"%s\")", path->u_name);
615 opened = PARAM_NEED_ADP(bitmap);
620 flags = (bitmap & (1 << FILPBIT_ATTR)) ? ADFLAGS_CHECK_OF : 0;
622 adp = of_ad(vol, path, &ad);
623 upath = path->u_name;
625 if ( ad_metadata( upath, flags, adp) < 0 ) {
628 LOG(log_error, logtype_afpd, "getfilparams(%s): %s: check resource fork permission?",
629 upath, strerror(errno));
630 return AFPERR_ACCESS;
632 LOG(log_error, logtype_afpd, "getfilparams(%s): bad resource fork", upath);
641 rc = getmetadata(vol, bitmap, path, dir, buf, buflen, adp);
642 ad_close(adp, ADFLAGS_HF | flags);
647 /* ----------------------------- */
648 int afp_createfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
653 struct ofork *of = NULL;
655 int creatf, did, openf, retvalue = AFP_OK;
661 creatf = (unsigned char) *ibuf++;
663 memcpy(&vid, ibuf, sizeof( vid ));
664 ibuf += sizeof( vid );
666 if (NULL == ( vol = getvolbyvid( vid )) )
667 return( AFPERR_PARAM );
669 if (vol->v_flags & AFPVOL_RO)
672 memcpy(&did, ibuf, sizeof( did));
673 ibuf += sizeof( did );
675 if (NULL == ( dir = dirlookup( vol, did )) )
678 if (NULL == ( s_path = cname( vol, dir, &ibuf )) )
679 return get_afp_errno(AFPERR_PARAM);
680 if ( *s_path->m_name == '\0' )
681 return( AFPERR_BADTYPE );
683 upath = s_path->u_name;
686 /* if upath is deleted we already in trouble anyway */
687 if ((of = of_findname(s_path))) {
695 openf = ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_TRUNC;
697 /* on a soft create, if the file is open then ad_open won't fail
698 because open syscall is not called */
699 openf = ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_EXCL;
701 if (ad_open(&ad, upath, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | openf, 0666) < 0) {
705 case ENOENT : /* we were already in 'did folder' so chdir() didn't fail */
706 return ( AFPERR_NOOBJ );
708 return( AFPERR_EXIST );
710 return( AFPERR_ACCESS );
713 return( AFPERR_DFULL );
715 return( AFPERR_PARAM );
718 if ( ad_meta_fileno( &ad ) == -1 ) { /* Hard META / HF */
719 /* FIXME with hard create on an existing file, we already
720 * corrupted the data file.
722 netatalk_unlink( upath );
723 ad_close( &ad, ADFLAGS_DF );
724 return AFPERR_ACCESS;
727 path = s_path->m_name;
728 ad_setname(&ad, path);
731 if (lstat(upath, &st) != 0) {
732 LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): stat: %s",
733 upath, strerror(errno));
734 ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF);
738 (void)get_id(vol, &ad, &st, dir->d_did, upath, strlen(upath));
741 ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF );
742 fce_register_new_file(s_path);
747 setvoltime(obj, vol );
752 int afp_setfilparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
758 uint16_t vid, bitmap;
763 memcpy(&vid, ibuf, sizeof( vid ));
764 ibuf += sizeof( vid );
765 if (NULL == ( vol = getvolbyvid( vid )) ) {
766 return( AFPERR_PARAM );
769 if (vol->v_flags & AFPVOL_RO)
772 memcpy(&did, ibuf, sizeof( did ));
773 ibuf += sizeof( did );
774 if (NULL == ( dir = dirlookup( vol, did )) ) {
775 return afp_errno; /* was AFPERR_NOOBJ */
778 memcpy(&bitmap, ibuf, sizeof( bitmap ));
779 bitmap = ntohs( bitmap );
780 ibuf += sizeof( bitmap );
782 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
783 return get_afp_errno(AFPERR_PARAM);
786 if (path_isadir(s_path)) {
787 return( AFPERR_BADTYPE ); /* it's a directory */
790 if ( s_path->st_errno != 0 ) {
791 return( AFPERR_NOOBJ );
794 if ((u_long)ibuf & 1 ) {
798 if (AFP_OK == ( rc = setfilparams(vol, s_path, bitmap, ibuf )) ) {
799 setvoltime(obj, vol );
806 * cf AFP3.0.pdf page 252 for change_mdate and change_parent_mdate logic
809 extern struct path Cur_Path;
811 int setfilparams(struct vol *vol,
812 struct path *path, uint16_t f_bitmap, char *buf )
814 struct adouble ad, *adp;
816 int bit, isad = 1, err = AFP_OK;
818 u_char achar, *fdType, xyy[4]; /* uninitialized, OK 310105 */
819 uint16_t ashort, bshort, oshort;
822 uint16_t upriv_bit = 0;
824 int change_mdate = 0;
825 int change_parent_mdate = 0;
830 uint16_t bitmap = f_bitmap;
831 uint32_t cdate,bdate;
832 u_char finder_buf[32];
836 LOG(log_debug9, logtype_afpd, "begin setfilparams:");
839 adp = of_ad(vol, path, &ad);
840 upath = path->u_name;
842 if (!vol_unix_priv(vol) && check_access(upath, OPENACC_WR ) < 0) {
843 return AFPERR_ACCESS;
846 /* with unix priv maybe we have to change adouble file priv first */
848 while ( bitmap != 0 ) {
849 while (( bitmap & 1 ) == 0 ) {
856 memcpy(&ashort, buf, sizeof( ashort ));
857 buf += sizeof( ashort );
861 memcpy(&cdate, buf, sizeof(cdate));
862 buf += sizeof( cdate );
865 memcpy(&newdate, buf, sizeof( newdate ));
866 buf += sizeof( newdate );
870 memcpy(&bdate, buf, sizeof( bdate));
871 buf += sizeof( bdate );
875 memcpy(finder_buf, buf, 32 );
876 if (memcmp(buf, "slnkrhap", 8) == 0 && !S_ISLNK(path->st.st_mode)) {
880 char buf[PATH_MAX+1];
881 if ((fp = open(path->u_name, O_RDONLY)) >= 0) {
882 if ((len = read(fp, buf, PATH_MAX+1))) {
883 if (unlink(path->u_name) == 0) {
885 erc = symlink(buf, path->u_name);
894 goto setfilparam_done;
900 case FILPBIT_UNIXPR :
901 if (!vol_unix_priv(vol)) {
902 /* this volume doesn't use unix priv */
908 change_parent_mdate = 1;
910 memcpy( &aint, buf, sizeof( aint ));
911 f_uid = ntohl (aint);
912 buf += sizeof( aint );
913 memcpy( &aint, buf, sizeof( aint ));
914 f_gid = ntohl (aint);
915 buf += sizeof( aint );
916 setfilowner(vol, f_uid, f_gid, path);
918 memcpy( &upriv, buf, sizeof( upriv ));
919 buf += sizeof( upriv );
920 upriv = ntohl (upriv);
921 if ((upriv & S_IWUSR)) {
922 setfilunixmode(vol, path, upriv);
929 case FILPBIT_PDINFO :
930 if (afp_version < 30) { /* else it's UTF8 name */
933 /* Keep special case to support crlf translations */
934 if ((unsigned int) achar == 0x04) {
935 fdType = (u_char *)"TEXT";
938 xyy[0] = ( u_char ) 'p';
949 /* break while loop */
958 /* second try with adouble open
960 if (ad_open(adp, upath, ADFLAGS_HF | ADFLAGS_RDWR | ADFLAGS_CREATE, 0666) < 0) {
961 LOG(log_debug, logtype_afpd, "setfilparams: ad_open_metadata error");
963 * For some things, we don't need an adouble header:
964 * - change of modification date
965 * - UNIX privs (Bug-ID #2863424)
967 if (!symlinked && f_bitmap & ~(1<<FILPBIT_MDATE | 1<<FILPBIT_UNIXPR)) {
968 LOG(log_debug, logtype_afpd, "setfilparams: need adouble access");
969 return AFPERR_ACCESS;
971 LOG(log_debug, logtype_afpd, "setfilparams: no adouble perms, but only FILPBIT_MDATE and/or FILPBIT_UNIXPR");
973 } else if ((ad_get_MD_flags( adp ) & O_CREAT) ) {
974 ad_setname(adp, path->m_name);
979 while ( bitmap != 0 ) {
980 while (( bitmap & 1 ) == 0 ) {
987 ad_getattr(adp, &bshort);
989 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
990 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
994 if ((bshort & htons(ATTRBIT_INVISIBLE)) != (oshort & htons(ATTRBIT_INVISIBLE)))
995 change_parent_mdate = 1;
996 ad_setattr(adp, bshort);
999 ad_setdate(adp, AD_DATE_CREATE, cdate);
1001 case FILPBIT_MDATE :
1003 case FILPBIT_BDATE :
1004 ad_setdate(adp, AD_DATE_BACKUP, bdate);
1006 case FILPBIT_FINFO :
1007 if (default_type( ad_entry( adp, ADEID_FINDERI ))
1009 ((em = getextmap( path->m_name )) &&
1010 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1011 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1012 || ((em = getdefextmap()) &&
1013 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1014 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1016 memcpy(finder_buf, ufinderi, 8 );
1018 memcpy(ad_entry( adp, ADEID_FINDERI ), finder_buf, 32 );
1020 case FILPBIT_UNIXPR :
1022 setfilunixmode(vol, path, upriv);
1025 case FILPBIT_PDINFO :
1026 if (afp_version < 30) { /* else it's UTF8 name */
1027 memcpy(ad_entry( adp, ADEID_FINDERI ), fdType, 4 );
1028 memcpy(ad_entry( adp, ADEID_FINDERI ) + 4, "pdos", 4 );
1033 err = AFPERR_BITMAP;
1034 goto setfilparam_done;
1041 if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
1042 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1046 ad_setdate(adp, AD_DATE_MODIFY, newdate);
1047 ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
1053 ad_close(adp, ADFLAGS_HF);
1056 if (change_parent_mdate && gettimeofday(&tv, NULL) == 0) {
1057 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1058 bitmap = 1<<FILPBIT_MDATE;
1059 setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
1063 LOG(log_debug9, logtype_afpd, "end setfilparams:");
1069 * renamefile and copyfile take the old and new unix pathnames
1070 * and the new mac name.
1072 * sdir_fd source dir fd to which src path is relative (for openat et al semantics)
1073 * passing -1 means this is not used, src path is a full path
1074 * src the source path
1075 * dst the dest filename in current dir
1076 * newname the dest mac name
1077 * adp adouble struct of src file, if open, or & zeroed one
1080 int renamefile(const struct vol *vol, int sdir_fd, char *src, char *dst, char *newname, struct adouble *adp)
1084 LOG(log_debug, logtype_afpd,
1085 "renamefile: src[%d, \"%s\"] -> dst[\"%s\"]", sdir_fd, src, dst);
1087 if ( unix_rename( sdir_fd, src, -1, dst ) < 0 ) {
1090 return( AFPERR_NOOBJ );
1093 return( AFPERR_ACCESS );
1095 return AFPERR_VLOCK;
1096 case EXDEV : /* Cross device move -- try copy */
1097 /* NOTE: with open file it's an error because after the copy we will
1098 * get two files, it's fixable for our process (eg reopen the new file, get the
1099 * locks, and so on. But it doesn't solve the case with a second process
1101 if (adp->ad_open_forks) {
1102 /* FIXME warning in syslog so admin'd know there's a conflict ?*/
1103 return AFPERR_OLOCK; /* little lie */
1105 if (AFP_OK != ( rc = copyfile(vol, vol, sdir_fd, src, dst, newname, NULL )) ) {
1106 /* on error copyfile delete dest */
1109 return deletefile(vol, sdir_fd, src, 0);
1111 return( AFPERR_PARAM );
1115 if (vol->vfs->vfs_renamefile(vol, sdir_fd, src, dst) < 0 ) {
1119 /* try to undo the data fork rename,
1120 * we know we are on the same device
1123 unix_rename(-1, dst, sdir_fd, src );
1124 /* return the first error */
1127 return AFPERR_NOOBJ;
1130 return AFPERR_ACCESS ;
1132 return AFPERR_VLOCK;
1134 return AFPERR_PARAM ;
1139 /* don't care if we can't open the newly renamed ressource fork */
1140 if (ad_open(adp, dst, ADFLAGS_HF | ADFLAGS_RDWR) == 0) {
1141 ad_setname(adp, newname);
1143 ad_close( adp, ADFLAGS_HF );
1150 convert a Mac long name to an utf8 name,
1152 size_t mtoUTF8(const struct vol *vol, const char *src, size_t srclen, char *dest, size_t destlen)
1156 if ((size_t)-1 == (outlen = convert_string ( vol->v_maccharset, CH_UTF8_MAC, src, srclen, dest, destlen)) ) {
1162 /* ---------------- */
1163 int copy_path_name(const struct vol *vol, char *newname, char *ibuf)
1170 if ( type != 2 && !(afp_version >= 30 && type == 3) ) {
1176 if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
1177 if (afp_version >= 30) {
1178 /* convert it to UTF8
1180 if ((plen = mtoUTF8(vol, ibuf, plen, newname, AFPOBJ_TMPSIZ)) == (size_t)-1)
1184 strncpy( newname, ibuf, plen );
1185 newname[ plen ] = '\0';
1187 if (strlen(newname) != plen) {
1188 /* there's \0 in newname, e.g. it's a pathname not
1196 memcpy(&hint, ibuf, sizeof(hint));
1197 ibuf += sizeof(hint);
1199 memcpy(&len16, ibuf, sizeof(len16));
1200 ibuf += sizeof(len16);
1201 plen = ntohs(len16);
1204 if (plen > AFPOBJ_TMPSIZ) {
1207 strncpy( newname, ibuf, plen );
1208 newname[ plen ] = '\0';
1209 if (strlen(newname) != plen) {
1218 /* -----------------------------------
1220 int afp_copyfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1222 struct vol *s_vol, *d_vol;
1224 char *newname, *p, *upath;
1225 struct path *s_path;
1226 uint32_t sdid, ddid;
1227 int err, retvalue = AFP_OK;
1228 uint16_t svid, dvid;
1230 struct adouble ad, *adp;
1236 memcpy(&svid, ibuf, sizeof( svid ));
1237 ibuf += sizeof( svid );
1238 if (NULL == ( s_vol = getvolbyvid( svid )) ) {
1239 return( AFPERR_PARAM );
1242 memcpy(&sdid, ibuf, sizeof( sdid ));
1243 ibuf += sizeof( sdid );
1244 if (NULL == ( dir = dirlookup( s_vol, sdid )) ) {
1248 memcpy(&dvid, ibuf, sizeof( dvid ));
1249 ibuf += sizeof( dvid );
1250 memcpy(&ddid, ibuf, sizeof( ddid ));
1251 ibuf += sizeof( ddid );
1253 if (NULL == ( s_path = cname( s_vol, dir, &ibuf )) ) {
1254 return get_afp_errno(AFPERR_PARAM);
1256 if ( path_isadir(s_path) ) {
1257 return( AFPERR_BADTYPE );
1260 /* don't allow copies when the file is open.
1261 * XXX: the spec only calls for read/deny write access.
1262 * however, copyfile doesn't have any of that info,
1263 * and locks need to stay coherent. as a result,
1264 * we just balk if the file is opened already. */
1266 adp = of_ad(s_vol, s_path, &ad);
1268 if (ad_open(adp, s_path->u_name, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | ADFLAGS_RDONLY | ADFLAGS_SETSHRMD) < 0) {
1269 return AFPERR_DENYCONF;
1271 denyreadset = (ad_testlock(adp, ADEID_DFORK, AD_FILELOCK_DENY_RD) != 0 ||
1272 ad_testlock(adp, ADEID_RFORK, AD_FILELOCK_DENY_RD) != 0 );
1275 retvalue = AFPERR_DENYCONF;
1279 newname = obj->newtmp;
1280 strcpy( newname, s_path->m_name );
1282 p = ctoupath( s_vol, curdir, newname );
1284 retvalue = AFPERR_PARAM;
1288 if (NULL == ( d_vol = getvolbyvid( dvid )) ) {
1289 retvalue = AFPERR_PARAM;
1293 if (d_vol->v_flags & AFPVOL_RO) {
1294 retvalue = AFPERR_VLOCK;
1298 if (NULL == ( dir = dirlookup( d_vol, ddid )) ) {
1299 retvalue = afp_errno;
1303 if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) {
1304 retvalue = get_afp_errno(AFPERR_NOOBJ);
1308 if ( *s_path->m_name != '\0' ) {
1309 retvalue =path_error(s_path, AFPERR_NOOBJ);
1313 /* one of the handful of places that knows about the path type */
1314 if (copy_path_name(d_vol, newname, ibuf) < 0) {
1315 retvalue = AFPERR_PARAM;
1318 /* newname is always only a filename so curdir *is* its
1321 if (NULL == (upath = mtoupath(d_vol, newname, curdir->d_did, utf8_encoding()))) {
1322 retvalue =AFPERR_PARAM;
1326 if ( (err = copyfile(s_vol, d_vol, -1, p, upath , newname, adp)) < 0 ) {
1332 setvoltime(obj, d_vol );
1335 ad_close( adp, ADFLAGS_DF |ADFLAGS_HF | ADFLAGS_SETSHRMD);
1339 /* ----------------------------------
1340 * if newname is NULL (from directory.c) we don't want to copy the resource fork.
1341 * because we are doing it elsewhere.
1342 * currently if newname is NULL then adp is NULL.
1344 int copyfile(const struct vol *s_vol,
1345 const struct vol *d_vol,
1350 struct adouble *adp)
1352 struct adouble ads, add;
1359 LOG(log_debug, logtype_afpd, "copyfile(sfd:%d,s:'%s',d:'%s',n:'%s')",
1360 sfd, src, dst, newname);
1363 ad_init(&ads, s_vol);
1367 adflags = ADFLAGS_DF | ADFLAGS_RF | ADFLAGS_NORF;
1369 if (ad_openat(adp, sfd, src, adflags | ADFLAGS_RDONLY) < 0) {
1374 if (!AD_RSRC_OPEN(adp))
1375 /* no resource fork, don't create one for dst file */
1376 adflags &= ~ADFLAGS_RF;
1378 stat_result = fstat(ad_data_fileno(adp), &st); /* saving stat exit code, thus saving us on one more stat later on */
1380 if (stat_result < 0) {
1381 /* unlikely but if fstat fails, the default file mode will be 0666. */
1382 st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
1385 ad_init(&add, d_vol);
1386 if (ad_open(&add, dst, adflags | ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_EXCL, st.st_mode | S_IRUSR | S_IWUSR) < 0) {
1388 ad_close( adp, adflags );
1389 if (EEXIST != ret_err) {
1390 deletefile(d_vol, -1, dst, 0);
1393 return AFPERR_EXIST;
1396 if ((err = copy_fork(ADEID_DFORK, &add, adp)) != 0)
1397 LOG(log_error, logtype_afpd, "copyfile('%s'): %s", src, strerror(errno));
1400 if ((err = d_vol->vfs->vfs_copyfile(d_vol, sfd, src, dst)) != 0)
1401 LOG(log_error, logtype_afpd, "copyfile('%s'): %s", src, strerror(errno));
1406 if (!ret_err && newname && (adflags & ADFLAGS_HF)) {
1407 /* set the new name in the resource fork */
1408 ad_copy_header(&add, adp);
1409 ad_setname(&add, newname);
1412 ad_close( adp, adflags );
1414 if (ad_close( &add, adflags ) <0) {
1419 deletefile(d_vol, -1, dst, 0);
1421 else if (stat_result == 0) {
1422 /* set dest modification date to src date */
1425 ut.actime = ut.modtime = st.st_mtime;
1427 /* FIXME netatalk doesn't use resource fork file date
1428 * but maybe we should set its modtime too.
1433 switch ( ret_err ) {
1439 return AFPERR_DFULL;
1441 return AFPERR_NOOBJ;
1443 return AFPERR_ACCESS;
1445 return AFPERR_VLOCK;
1447 return AFPERR_PARAM;
1451 /* -----------------------------------
1452 vol: not NULL delete cnid entry. then we are in curdir and file is a only filename
1453 checkAttrib: 1 check kFPDeleteInhibitBit (deletfile called by afp_delete)
1455 when deletefile is called we don't have lock on it, file is closed (for us)
1456 untrue if called by renamefile
1458 ad_open always try to open file RDWR first and ad_lock takes care of
1459 WRITE lock on read only file.
1462 static int check_attrib(struct adouble *adp)
1464 uint16_t bshort = 0;
1466 ad_getattr(adp, &bshort);
1468 * Does kFPDeleteInhibitBit (bit 8) set?
1470 if ((bshort & htons(ATTRBIT_NODELETE))) {
1471 return AFPERR_OLOCK;
1473 if ((bshort & htons(ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
1479 * dirfd can be used for unlinkat semantics
1481 int deletefile(const struct vol *vol, int dirfd, char *file, int checkAttrib)
1484 struct adouble *adp = NULL;
1485 int adflags, err = AFP_OK;
1488 LOG(log_debug, logtype_afpd, "deletefile('%s')", file);
1492 /* was EACCESS error try to get only metadata */
1493 /* we never want to create a resource fork here, we are going to delete it
1494 * moreover sometimes deletefile is called with a no existent file and
1495 * ad_open would create a 0 byte resource fork
1497 if ( ad_metadataat(dirfd, file, ADFLAGS_CHECK_OF, &ad) == 0 ) {
1498 if ((err = check_attrib(&ad))) {
1499 ad_close(&ad, ADFLAGS_HF | ADFLAGS_CHECK_OF);
1506 /* try to open both forks at once */
1507 adflags = ADFLAGS_DF;
1508 if (ad_openat(&ad, dirfd, file, adflags | ADFLAGS_RF | ADFLAGS_NORF | ADFLAGS_RDONLY) < 0 ) {
1513 case EACCES: /* maybe it's a file with no write mode for us */
1514 break; /* was return AFPERR_ACCESS;*/
1527 if ( adp && AD_RSRC_OPEN(adp) != -1 ) { /* there's a resource fork */
1528 adflags |= ADFLAGS_RF;
1529 /* FIXME we have a pb here because we want to know if a file is open
1530 * there's a 'priority inversion' if you can't open the ressource fork RW
1531 * you can delete it if it's open because you can't get a write lock.
1533 * ADLOCK_FILELOCK means the whole ressource fork, not only after the
1536 * FIXME it doesn't work for RFORK open read only and fork open without deny mode
1538 if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
1544 if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
1545 LOG(log_error, logtype_afpd, "deletefile('%s'): ad_tmplock error: %s", file, strerror(errno));
1547 } else if (!(err = vol->vfs->vfs_deletefile(vol, dirfd, file)) && !(err = netatalk_unlinkat(dirfd, file )) ) {
1549 if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file)))) {
1550 cnid_delete(vol->v_cdb, id);
1556 ad_close(&ad, ADFLAGS_HF | ADFLAGS_CHECK_OF);
1559 ad_close( &ad, adflags ); /* ad_close removes locks if any */
1564 /* ------------------------------------ */
1565 /* return a file id */
1566 int afp_createid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1575 struct path *s_path;
1581 memcpy(&vid, ibuf, sizeof(vid));
1582 ibuf += sizeof(vid);
1584 if (NULL == ( vol = getvolbyvid( vid )) ) {
1585 return( AFPERR_PARAM);
1588 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1592 if (vol->v_flags & AFPVOL_RO)
1593 return AFPERR_VLOCK;
1595 memcpy(&did, ibuf, sizeof( did ));
1596 ibuf += sizeof(did);
1598 if (NULL == ( dir = dirlookup( vol, did )) ) {
1599 return afp_errno; /* was AFPERR_PARAM */
1602 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
1603 return get_afp_errno(AFPERR_NOOBJ); /* was AFPERR_PARAM */
1606 if ( path_isadir(s_path) ) {
1607 return( AFPERR_BADTYPE );
1610 upath = s_path->u_name;
1611 switch (s_path->st_errno) {
1613 break; /* success */
1616 return AFPERR_ACCESS;
1618 return AFPERR_NOOBJ;
1620 return AFPERR_PARAM;
1623 if ((id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath)))) {
1624 memcpy(rbuf, &id, sizeof(id));
1625 *rbuflen = sizeof(id);
1626 return AFPERR_EXISTID;
1629 if ((id = get_id(vol, NULL, st, did, upath, len)) != CNID_INVALID) {
1630 memcpy(rbuf, &id, sizeof(id));
1631 *rbuflen = sizeof(id);
1638 /* ------------------------------- */
1644 static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
1647 struct reenum *param = data;
1648 struct vol *vol = param->vol;
1649 cnid_t did = param->did;
1652 if ( lstat(de->d_name, &path.st) < 0 )
1655 /* update or add to cnid */
1656 aint = cnid_add(vol->v_cdb, &path.st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */
1661 /* --------------------
1662 * Ok the db is out of synch with the dir.
1663 * but if it's a deleted file we don't want to do it again and again.
1666 reenumerate_id(struct vol *vol, char *name, struct dir *dir)
1672 if (vol->v_cdb == NULL) {
1676 /* FIXME use of_statdir ? */
1677 if (lstat(name, &st)) {
1681 if (dirreenumerate(dir, &st)) {
1682 /* we already did it once and the dir haven't been modified */
1683 return dir->d_offcnt;
1687 data.did = dir->d_did;
1688 if ((ret = for_each_dirent(vol, name, reenumerate_loop, (void *)&data)) >= 0) {
1689 setdiroffcnt(curdir, &st, ret);
1690 dir->d_flags |= DIRF_CNID;
1696 /* ------------------------------
1697 resolve a file id */
1698 int afp_resolveid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1707 uint16_t vid, bitmap;
1709 static char buffer[12 + MAXPATHLEN + 1];
1710 int len = 12 + MAXPATHLEN + 1;
1715 memcpy(&vid, ibuf, sizeof(vid));
1716 ibuf += sizeof(vid);
1718 if (NULL == ( vol = getvolbyvid( vid )) ) {
1719 return( AFPERR_PARAM);
1722 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1726 memcpy(&id, ibuf, sizeof( id ));
1731 /* some MacOS versions after a catsearch do a *lot* of afp_resolveid with 0 */
1735 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1736 return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
1739 if (NULL == ( dir = dirlookup( vol, id )) ) {
1740 return AFPERR_NOID; /* idem AFPERR_PARAM */
1742 if (movecwd(vol, dir) < 0) {
1746 return AFPERR_ACCESS;
1750 return AFPERR_PARAM;
1754 memset(&path, 0, sizeof(path));
1755 path.u_name = upath;
1756 if ( of_stat(&path) < 0 ) {
1758 /* with nfs and our working directory is deleted */
1759 if (errno == ESTALE) {
1763 if ( errno == ENOENT && !retry) {
1764 /* cnid db is out of sync, reenumerate the directory and update ids */
1765 reenumerate_id(vol, ".", dir);
1773 return AFPERR_ACCESS;
1777 return AFPERR_PARAM;
1781 /* directories are bad */
1782 if (S_ISDIR(path.st.st_mode)) {
1783 /* OS9 and OSX don't return the same error code */
1784 return (afp_version >=30)?AFPERR_NOID:AFPERR_BADTYPE;
1787 memcpy(&bitmap, ibuf, sizeof(bitmap));
1788 bitmap = ntohs( bitmap );
1789 if (NULL == (path.m_name = utompath(vol, upath, cnid, utf8_encoding()))) {
1793 if (AFP_OK != (err = getfilparams(vol, bitmap, &path , curdir,
1794 rbuf + sizeof(bitmap), &buflen))) {
1797 *rbuflen = buflen + sizeof(bitmap);
1798 memcpy(rbuf, ibuf, sizeof(bitmap));
1803 /* ------------------------------ */
1804 int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1814 static char buffer[12 + MAXPATHLEN + 1];
1815 int len = 12 + MAXPATHLEN + 1;
1820 memcpy(&vid, ibuf, sizeof(vid));
1821 ibuf += sizeof(vid);
1823 if (NULL == ( vol = getvolbyvid( vid )) ) {
1824 return( AFPERR_PARAM);
1827 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1831 if (vol->v_flags & AFPVOL_RO)
1832 return AFPERR_VLOCK;
1834 memcpy(&id, ibuf, sizeof( id ));
1838 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1842 if (NULL == ( dir = dirlookup( vol, id )) ) {
1843 if (afp_errno == AFPERR_NOOBJ) {
1847 return( AFPERR_PARAM );
1851 if ((movecwd(vol, dir) < 0) || (lstat(upath, &st) < 0)) {
1855 return AFPERR_ACCESS;
1860 /* still try to delete the id */
1864 return AFPERR_PARAM;
1867 else if (S_ISDIR(st.st_mode)) /* directories are bad */
1868 return AFPERR_BADTYPE;
1871 if (cnid_delete(vol->v_cdb, fileid)) {
1874 return AFPERR_VLOCK;
1877 return AFPERR_ACCESS;
1879 return AFPERR_PARAM;
1886 /* ------------------------------ */
1887 static struct adouble *find_adouble(struct path *path, struct ofork **of, struct adouble *adp)
1891 if (path->st_errno) {
1892 switch (path->st_errno) {
1894 afp_errno = AFPERR_NOID;
1898 afp_errno = AFPERR_ACCESS;
1901 afp_errno = AFPERR_PARAM;
1906 /* we use file_access both for legacy Mac perm and
1907 * for unix privilege, rename will take care of folder perms
1909 if (file_access(path, OPENACC_WR ) < 0) {
1910 afp_errno = AFPERR_ACCESS;
1914 if ((*of = of_findname(path))) {
1915 /* reuse struct adouble so it won't break locks */
1919 ret = ad_open(adp, path->u_name, ADFLAGS_HF | ADFLAGS_RDWR);
1921 if ( !ret && ad_reso_fileno(adp) != -1 && !(adp->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
1923 * The user must have the Read & Write privilege for both files in order to use this command.
1925 ad_close(adp, ADFLAGS_HF);
1926 afp_errno = AFPERR_ACCESS;
1933 #define APPLETEMP ".AppleTempXXXXXX"
1935 int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1937 struct stat srcst, destst;
1939 struct dir *dir, *sdir;
1940 char *spath, temp[17], *p;
1941 char *supath, *upath;
1946 struct adouble *adsp = NULL;
1947 struct adouble *addp = NULL;
1948 struct ofork *s_of = NULL;
1949 struct ofork *d_of = NULL;
1962 memcpy(&vid, ibuf, sizeof(vid));
1963 ibuf += sizeof(vid);
1965 if (NULL == ( vol = getvolbyvid( vid )) ) {
1966 return( AFPERR_PARAM);
1969 if ((vol->v_flags & AFPVOL_RO))
1970 return AFPERR_VLOCK;
1972 /* source and destination dids */
1973 memcpy(&sid, ibuf, sizeof(sid));
1974 ibuf += sizeof(sid);
1975 memcpy(&did, ibuf, sizeof(did));
1976 ibuf += sizeof(did);
1979 if (NULL == (dir = dirlookup( vol, sid )) ) {
1980 return afp_errno; /* was AFPERR_PARAM */
1983 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
1984 return get_afp_errno(AFPERR_NOOBJ);
1987 if ( path_isadir(path) ) {
1988 return AFPERR_BADTYPE; /* it's a dir */
1991 /* save some stuff */
1994 spath = obj->oldtmp;
1995 supath = obj->newtmp;
1996 strcpy(spath, path->m_name);
1997 strcpy(supath, path->u_name); /* this is for the cnid changing */
1998 p = absupath( vol, sdir, supath);
2000 /* pathname too long */
2001 return AFPERR_PARAM ;
2005 if (!(adsp = find_adouble( path, &s_of, &ads))) {
2009 /* ***** from here we may have resource fork open **** */
2011 /* look for the source cnid. if it doesn't exist, don't worry about
2013 sid = cnid_lookup(vol->v_cdb, &srcst, sdir->d_did, supath,slen = strlen(supath));
2015 if (NULL == ( dir = dirlookup( vol, did )) ) {
2016 err = afp_errno; /* was AFPERR_PARAM */
2017 goto err_exchangefile;
2020 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2021 err = get_afp_errno(AFPERR_NOOBJ);
2022 goto err_exchangefile;
2025 if ( path_isadir(path) ) {
2026 err = AFPERR_BADTYPE;
2027 goto err_exchangefile;
2030 /* FPExchangeFiles is the only call that can return the SameObj
2032 if ((curdir == sdir) && strcmp(spath, path->m_name) == 0) {
2033 err = AFPERR_SAMEOBJ;
2034 goto err_exchangefile;
2038 if (!(addp = find_adouble( path, &d_of, &add))) {
2040 goto err_exchangefile;
2044 /* they are not on the same device and at least one is open
2045 * FIXME broken for for crossdev and adouble v2
2048 crossdev = (srcst.st_dev != destst.st_dev);
2049 if (/* (d_of || s_of) && */ crossdev) {
2051 goto err_exchangefile;
2054 /* look for destination id. */
2055 upath = path->u_name;
2056 did = cnid_lookup(vol->v_cdb, &destst, curdir->d_did, upath, dlen = strlen(upath));
2058 /* construct a temp name.
2059 * NOTE: the temp file will be in the dest file's directory. it
2060 * will also be inaccessible from AFP. */
2061 memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
2062 if (!mktemp(temp)) {
2064 goto err_exchangefile;
2068 /* FIXME we need to close fork for copy, both s_of and d_of are null */
2069 ad_close(adsp, ADFLAGS_HF);
2070 ad_close(addp, ADFLAGS_HF);
2073 /* now, quickly rename the file. we error if we can't. */
2074 if ((err = renamefile(vol, -1, p, temp, temp, adsp)) != AFP_OK)
2075 goto err_exchangefile;
2076 of_rename(vol, s_of, sdir, spath, curdir, temp);
2078 /* rename destination to source */
2079 if ((err = renamefile(vol, -1, upath, p, spath, addp)) != AFP_OK)
2080 goto err_src_to_tmp;
2081 of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
2083 /* rename temp to destination */
2084 if ((err = renamefile(vol, -1, temp, upath, path->m_name, adsp)) != AFP_OK)
2085 goto err_dest_to_src;
2086 of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
2088 /* id's need switching. src -> dest and dest -> src.
2089 * we need to re-stat() if it was a cross device copy.
2092 cnid_delete(vol->v_cdb, sid);
2094 cnid_delete(vol->v_cdb, did);
2096 if ((did && ( (crossdev && lstat( upath, &srcst) < 0) ||
2097 cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
2099 (sid && ( (crossdev && lstat(p, &destst) < 0) ||
2100 cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
2105 err = AFPERR_ACCESS;
2110 goto err_temp_to_dest;
2113 /* here we need to reopen if crossdev */
2114 if (sid && ad_setid(addp, destst.st_dev, destst.st_ino, sid, sdir->d_did, vol->v_stamp))
2119 if (did && ad_setid(adsp, srcst.st_dev, srcst.st_ino, did, curdir->d_did, vol->v_stamp))
2124 /* change perms, src gets dest perm and vice versa */
2129 LOG(log_error, logtype_afpd, "seteuid failed %s", strerror(errno));
2130 err = AFP_OK; /* ignore error */
2131 goto err_temp_to_dest;
2135 * we need to exchange ACL entries as well
2137 /* exchange_acls(vol, p, upath); */
2142 path->m_name = NULL;
2143 path->u_name = upath;
2145 setfilunixmode(vol, path, destst.st_mode);
2146 setfilowner(vol, destst.st_uid, destst.st_gid, path);
2153 setfilunixmode(vol, path, srcst.st_mode);
2154 setfilowner(vol, srcst.st_uid, srcst.st_gid, path);
2156 if ( setegid(gid) < 0 || seteuid(uid) < 0) {
2157 LOG(log_error, logtype_afpd, "can't seteuid back %s", strerror(errno));
2162 goto err_exchangefile;
2164 /* all this stuff is so that we can unwind a failed operation
2167 /* rename dest to temp */
2168 renamefile(vol, -1, upath, temp, temp, adsp);
2169 of_rename(vol, s_of, curdir, upath, curdir, temp);
2172 /* rename source back to dest */
2173 renamefile(vol, -1, p, upath, path->m_name, addp);
2174 of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
2177 /* rename temp back to source */
2178 renamefile(vol, -1, temp, p, spath, adsp);
2179 of_rename(vol, s_of, curdir, temp, sdir, spath);
2182 if ( !s_of && adsp && ad_meta_fileno(adsp) != -1 ) { /* META */
2183 ad_close(adsp, ADFLAGS_HF);
2185 if ( !d_of && addp && ad_meta_fileno(addp) != -1 ) {/* META */
2186 ad_close(addp, ADFLAGS_HF);
2190 if ((cached = dircache_search_by_did(vol, sid)) != NULL)
2191 (void)dir_remove(vol, cached);
2192 if ((cached = dircache_search_by_did(vol, did)) != NULL)
2193 (void)dir_remove(vol, cached);