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)
79 void *ad_finder = NULL;
82 ad_finder = ad_entry(adp, ADEID_FINDERI);
85 memcpy(data, ad_finder, ADEDLEN_FINDERI);
88 memcpy(data, ufinderi, ADEDLEN_FINDERI);
89 if (vol_inv_dots(vol) && *upath == '.') { /* make it invisible */
92 ashort = htons(FINDERINFO_INVISIBLE);
93 memcpy((char *)data + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
99 memcpy(&linkflag, (char *)data + FINDERINFO_FRFLAGOFF, 2);
100 linkflag |= htons(FINDERINFO_ISALIAS);
101 memcpy((char *)data + FINDERINFO_FRFLAGOFF, &linkflag, 2);
102 memcpy((char *)data + FINDERINFO_FRTYPEOFF,"slnk",4);
103 memcpy((char *)data + FINDERINFO_FRCREATOFF,"rhap",4);
109 /* ---------------------
111 char *set_name(const struct vol *vol, char *data, cnid_t pid, char *name, cnid_t id, uint32_t utf8)
116 aint = strlen( name );
120 if (utf8_encoding()) {
121 /* but name is an utf8 mac name */
124 /* global static variable... */
126 if (!(u = mtoupath(vol, name, pid, 1)) || !(m = utompath(vol, u, id, 0))) {
135 if (aint > MACFILELEN)
142 if (aint > UTF8FILELEN_EARLY) /* FIXME safeguard, anyway if no ascii char it's game over*/
143 aint = UTF8FILELEN_EARLY;
145 utf8 = vol->v_kTextEncoding;
146 memcpy(data, &utf8, sizeof(utf8));
147 data += sizeof(utf8);
150 memcpy(data, &temp, sizeof(temp));
151 data += sizeof(temp);
154 memcpy( data, src, aint );
164 * FIXME: PDINFO is UTF8 and doesn't need adp
166 #define PARAM_NEED_ADP(b) ((b) & ((1 << FILPBIT_ATTR) |\
167 (1 << FILPBIT_CDATE) |\
168 (1 << FILPBIT_MDATE) |\
169 (1 << FILPBIT_BDATE) |\
170 (1 << FILPBIT_FINFO) |\
171 (1 << FILPBIT_RFLEN) |\
172 (1 << FILPBIT_EXTRFLEN) |\
173 (1 << FILPBIT_PDINFO) |\
174 (1 << FILPBIT_FNUM) |\
175 (1 << FILPBIT_UNIXPR)))
178 * @brief Get CNID for did/upath args both from database and adouble file
180 * 1. Get the objects CNID as stored in its adouble file
181 * 2. Get the objects CNID from the database
182 * 3. If there's a problem with a "dbd" database, fallback to "tdb" in memory
183 * 4. In case 2 and 3 differ, store 3 in the adouble file
185 * @param vol (rw) volume
186 * @param adp (rw) adouble struct of object upath, might be NULL
187 * @param st (r) stat of upath, must NOT be NULL
188 * @param did (r) parent CNID of upath
189 * @param upath (r) name of object
190 * @param len (r) strlen of upath
192 uint32_t get_id(struct vol *vol,
194 const struct stat *st,
199 static int first = 1; /* mark if this func is called the first time */
201 uint32_t dbcnid = CNID_INVALID;
204 if (vol->v_cdb != NULL) {
205 /* prime aint with what we think is the cnid, set did to zero for
206 catching moved files */
207 adcnid = ad_getid(adp, st->st_dev, st->st_ino, 0, vol->v_stamp); /* (1) */
209 dbcnid = cnid_add(vol->v_cdb, st, did, upath, len, adcnid); /* (2) */
210 /* Throw errors if cnid_add fails. */
211 if (dbcnid == CNID_INVALID) {
213 case CNID_ERR_CLOSE: /* the db is closed */
216 LOG(log_error, logtype_afpd, "get_id: Incorrect parameters passed to cnid_add");
217 afp_errno = AFPERR_PARAM;
220 afp_errno = AFPERR_PARAM;
223 /* Close CNID backend if "dbd" and switch to temp in-memory "tdb" */
224 /* we have to do it here for "dbd" because it uses "lazy opening" */
225 /* In order to not end in a loop somehow with goto restart below */
227 if (first && (strcmp(vol->v_cnidscheme, "dbd") == 0)) { /* (3) */
228 cnid_close(vol->v_cdb);
229 free(vol->v_cnidscheme);
230 vol->v_cnidscheme = strdup("tdb");
232 int flags = CNID_FLAG_MEMORY;
233 if ((vol->v_flags & AFPVOL_NODEV)) {
234 flags |= CNID_FLAG_NODEV;
236 LOG(log_error, logtype_afpd, "Reopen volume %s using in memory temporary CNID DB.",
238 vol->v_cdb = cnid_open(vol->v_path, vol->v_umask, "tdb", flags, NULL, NULL);
241 vol->v_flags |= AFPVOL_RO;
243 /* kill ourself with SIGUSR2 aka msg pending */
244 setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB instead."
245 "Check server messages for details. Switching to read-only mode.");
246 kill(getpid(), SIGUSR2);
248 goto restart; /* not try again with the temp CNID db */
251 setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB failed too!"
252 "Check server messages for details, can't recover from this state!");
256 afp_errno = AFPERR_MISC;
260 else if (adp && (adcnid != dbcnid)) { /* 4 */
261 /* Update the ressource fork. For a folder adp is always null */
262 LOG(log_debug, logtype_afpd, "get_id(%s/%s): calling ad_setid(old: %u, new: %u)",
263 getcwdpath(), upath, htonl(adcnid), htonl(dbcnid));
264 if (ad_setid(adp, st->st_dev, st->st_ino, dbcnid, did, vol->v_stamp)) {
275 /* -------------------------- */
276 int getmetadata(struct vol *vol,
278 struct path *path, struct dir *dir,
279 char *buf, size_t *buflen, struct adouble *adp)
281 char *data, *l_nameoff = NULL, *upath;
282 char *utf_nameoff = NULL;
287 u_char achar, fdType[4];
292 LOG(log_debug, logtype_afpd, "getmetadata(\"%s\")", path->u_name);
294 upath = path->u_name;
298 if ( ((bitmap & ( (1 << FILPBIT_FINFO)|(1 << FILPBIT_LNAME)|(1 <<FILPBIT_PDINFO) ) ) && !path->m_name)
299 || (bitmap & ( (1 << FILPBIT_LNAME) ) && utf8_encoding()) /* FIXME should be m_name utf8 filename */
300 || (bitmap & (1 << FILPBIT_FNUM))) {
303 struct dir *cachedfile;
304 int len = strlen(upath);
305 if ((cachedfile = dircache_search_by_name(vol, dir, upath, len)) != NULL)
306 id = cachedfile->d_did;
308 id = get_id(vol, adp, st, dir->d_did, upath, len);
310 /* Add it to the cache */
311 LOG(log_debug, logtype_afpd, "getmetadata: caching: did:%u, \"%s\", cnid:%u",
312 ntohl(dir->d_did), upath, ntohl(id));
314 /* Get macname from unixname first */
315 if (path->m_name == NULL) {
316 if ((path->m_name = utompath(vol, upath, id, utf8_encoding())) == NULL) {
317 LOG(log_error, logtype_afpd, "getmetadata: utompath error");
323 if (((fullpath = bstrcpy(dir->d_fullpath)) == NULL)
324 || (bconchar(fullpath, '/') != BSTR_OK)
325 || (bcatcstr(fullpath, upath)) != BSTR_OK) {
326 LOG(log_error, logtype_afpd, "getmetadata: fullpath: %s", strerror(errno));
330 if ((cachedfile = dir_new(path->m_name, upath, vol, dir->d_did, id, fullpath, st)) == NULL) {
331 LOG(log_error, logtype_afpd, "getmetadata: error from dir_new");
335 if ((dircache_add(vol, cachedfile)) != 0) {
336 LOG(log_error, logtype_afpd, "getmetadata: fatal dircache error");
344 if (id == CNID_INVALID)
348 path->m_name = utompath(vol, upath, id, utf8_encoding());
351 while ( bitmap != 0 ) {
352 while (( bitmap & 1 ) == 0 ) {
360 ad_getattr(adp, &ashort);
361 } else if (vol_inv_dots(vol) && *upath == '.') {
362 ashort = htons(ATTRBIT_INVISIBLE);
366 /* FIXME do we want a visual clue if the file is read only
369 accessmode(vol, ".", &ma, dir , NULL);
370 if ((ma.ma_user & AR_UWRITE)) {
371 accessmode(vol, upath, &ma, dir , st);
372 if (!(ma.ma_user & AR_UWRITE)) {
373 ashort |= htons(ATTRBIT_NOWRITE);
377 memcpy(data, &ashort, sizeof( ashort ));
378 data += sizeof( ashort );
379 LOG(log_debug, logtype_afpd, "metadata('%s'): AFP Attributes: %04x",
380 path->u_name, ntohs(ashort));
384 memcpy(data, &dir->d_did, sizeof( uint32_t ));
385 data += sizeof( uint32_t );
386 LOG(log_debug, logtype_afpd, "metadata('%s'): Parent DID: %u",
387 path->u_name, ntohl(dir->d_did));
391 if (!adp || (ad_getdate(adp, AD_DATE_CREATE, &aint) < 0))
392 aint = AD_DATE_FROM_UNIX(st->st_mtime);
393 memcpy(data, &aint, sizeof( aint ));
394 data += sizeof( aint );
398 if ( adp && (ad_getdate(adp, AD_DATE_MODIFY, &aint) == 0)) {
399 if ((st->st_mtime > AD_DATE_TO_UNIX(aint))) {
400 aint = AD_DATE_FROM_UNIX(st->st_mtime);
403 aint = AD_DATE_FROM_UNIX(st->st_mtime);
405 memcpy(data, &aint, sizeof( int ));
406 data += sizeof( int );
410 if (!adp || (ad_getdate(adp, AD_DATE_BACKUP, &aint) < 0))
411 aint = AD_DATE_START;
412 memcpy(data, &aint, sizeof( int ));
413 data += sizeof( int );
417 get_finderinfo(vol, upath, adp, (char *)data,S_ISLNK(st->st_mode));
418 data += ADEDLEN_FINDERI;
423 data += sizeof( uint16_t );
427 memset(data, 0, sizeof(uint16_t));
428 data += sizeof( uint16_t );
432 memcpy(data, &id, sizeof( id ));
433 data += sizeof( id );
434 LOG(log_debug, logtype_afpd, "metadata('%s'): CNID: %u",
435 path->u_name, ntohl(id));
439 if (st->st_size > 0xffffffff)
442 aint = htonl( st->st_size );
443 memcpy(data, &aint, sizeof( aint ));
444 data += sizeof( aint );
449 if (adp->ad_rlen > 0xffffffff)
452 aint = htonl( adp->ad_rlen);
456 memcpy(data, &aint, sizeof( aint ));
457 data += sizeof( aint );
460 /* Current client needs ProDOS info block for this file.
461 Use simple heuristic and let the Mac "type" string tell
462 us what the PD file code should be. Everything gets a
463 subtype of 0x0000 unless the original value was hashed
464 to "pXYZ" when we created it. See IA, Ver 2.
465 <shirsch@adelphia.net> */
466 case FILPBIT_PDINFO :
467 if (afp_version >= 30) { /* UTF8 name */
468 utf8 = kTextEncodingUTF8;
470 data += sizeof( uint16_t );
472 memcpy(data, &aint, sizeof( aint ));
473 data += sizeof( aint );
477 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
479 if ( memcmp( fdType, "TEXT", 4 ) == 0 ) {
483 else if ( memcmp( fdType, "PSYS", 4 ) == 0 ) {
487 else if ( memcmp( fdType, "PS16", 4 ) == 0 ) {
491 else if ( memcmp( fdType, "BINA", 4 ) == 0 ) {
495 else if ( fdType[0] == 'p' ) {
497 ashort = (fdType[2] * 256) + fdType[3];
511 memcpy(data, &ashort, sizeof( ashort ));
512 data += sizeof( ashort );
513 memset(data, 0, sizeof( ashort ));
514 data += sizeof( ashort );
517 case FILPBIT_EXTDFLEN:
518 aint = htonl(st->st_size >> 32);
519 memcpy(data, &aint, sizeof( aint ));
520 data += sizeof( aint );
521 aint = htonl(st->st_size);
522 memcpy(data, &aint, sizeof( aint ));
523 data += sizeof( aint );
525 case FILPBIT_EXTRFLEN:
528 aint = htonl(adp->ad_rlen >> 32);
529 memcpy(data, &aint, sizeof( aint ));
530 data += sizeof( aint );
532 aint = htonl(adp->ad_rlen);
533 memcpy(data, &aint, sizeof( aint ));
534 data += sizeof( aint );
536 case FILPBIT_UNIXPR :
537 /* accessmode may change st_mode with ACLs */
538 accessmode(vol, upath, &ma, dir , st);
540 aint = htonl(st->st_uid);
541 memcpy( data, &aint, sizeof( aint ));
542 data += sizeof( aint );
543 aint = htonl(st->st_gid);
544 memcpy( data, &aint, sizeof( aint ));
545 data += sizeof( aint );
548 type == slnk indicates an OSX style symlink,
549 we have to add S_IFLNK to the mode, otherwise
550 10.3 clients freak out. */
554 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
555 if ( memcmp( fdType, "slnk", 4 ) == 0 ) {
561 memcpy( data, &aint, sizeof( aint ));
562 data += sizeof( aint );
564 *data++ = ma.ma_user;
565 *data++ = ma.ma_world;
566 *data++ = ma.ma_group;
567 *data++ = ma.ma_owner;
571 return( AFPERR_BITMAP );
577 ashort = htons( data - buf );
578 memcpy(l_nameoff, &ashort, sizeof( ashort ));
579 data = set_name(vol, data, dir->d_did, path->m_name, id, 0);
582 ashort = htons( data - buf );
583 memcpy(utf_nameoff, &ashort, sizeof( ashort ));
584 data = set_name(vol, data, dir->d_did, path->m_name, id, utf8);
586 *buflen = data - buf;
590 /* ----------------------- */
591 int getfilparams(struct vol *vol,
593 struct path *path, struct dir *dir,
594 char *buf, size_t *buflen )
596 struct adouble ad, *adp;
601 LOG(log_debug, logtype_afpd, "getfilparams(\"%s\")", path->u_name);
603 opened = PARAM_NEED_ADP(bitmap);
608 flags = (bitmap & (1 << FILPBIT_ATTR)) ? ADFLAGS_CHECK_OF : 0;
610 adp = of_ad(vol, path, &ad);
611 upath = path->u_name;
613 if ( ad_metadata( upath, flags, adp) < 0 ) {
616 LOG(log_error, logtype_afpd, "getfilparams(%s): %s: check resource fork permission?",
617 upath, strerror(errno));
618 return AFPERR_ACCESS;
620 LOG(log_error, logtype_afpd, "getfilparams(%s): bad resource fork", upath);
629 rc = getmetadata(vol, bitmap, path, dir, buf, buflen, adp);
630 ad_close(adp, ADFLAGS_HF | flags);
635 /* ----------------------------- */
636 int afp_createfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
641 struct ofork *of = NULL;
643 int creatf, did, openf, retvalue = AFP_OK;
649 creatf = (unsigned char) *ibuf++;
651 memcpy(&vid, ibuf, sizeof( vid ));
652 ibuf += sizeof( vid );
654 if (NULL == ( vol = getvolbyvid( vid )) )
655 return( AFPERR_PARAM );
657 if (vol->v_flags & AFPVOL_RO)
660 memcpy(&did, ibuf, sizeof( did));
661 ibuf += sizeof( did );
663 if (NULL == ( dir = dirlookup( vol, did )) )
666 if (NULL == ( s_path = cname( vol, dir, &ibuf )) )
667 return get_afp_errno(AFPERR_PARAM);
668 if ( *s_path->m_name == '\0' )
669 return( AFPERR_BADTYPE );
671 upath = s_path->u_name;
674 /* if upath is deleted we already in trouble anyway */
675 if ((of = of_findname(s_path))) {
683 openf = ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_TRUNC;
685 /* on a soft create, if the file is open then ad_open won't fail
686 because open syscall is not called */
687 openf = ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_EXCL;
689 if (ad_open(&ad, upath, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | openf, 0666) < 0) {
693 case ENOENT : /* we were already in 'did folder' so chdir() didn't fail */
694 return ( AFPERR_NOOBJ );
696 return( AFPERR_EXIST );
698 return( AFPERR_ACCESS );
701 return( AFPERR_DFULL );
703 return( AFPERR_PARAM );
706 if ( ad_meta_fileno( &ad ) == -1 ) { /* Hard META / HF */
707 /* FIXME with hard create on an existing file, we already
708 * corrupted the data file.
710 netatalk_unlink( upath );
711 ad_close( &ad, ADFLAGS_DF );
712 return AFPERR_ACCESS;
715 path = s_path->m_name;
716 ad_setname(&ad, path);
719 if (lstat(upath, &st) != 0) {
720 LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): stat: %s",
721 upath, strerror(errno));
722 ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF);
726 (void)get_id(vol, &ad, &st, dir->d_did, upath, strlen(upath));
729 ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF );
730 fce_register_new_file(s_path);
735 setvoltime(obj, vol );
740 int afp_setfilparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
746 uint16_t vid, bitmap;
751 memcpy(&vid, ibuf, sizeof( vid ));
752 ibuf += sizeof( vid );
753 if (NULL == ( vol = getvolbyvid( vid )) ) {
754 return( AFPERR_PARAM );
757 if (vol->v_flags & AFPVOL_RO)
760 memcpy(&did, ibuf, sizeof( did ));
761 ibuf += sizeof( did );
762 if (NULL == ( dir = dirlookup( vol, did )) ) {
763 return afp_errno; /* was AFPERR_NOOBJ */
766 memcpy(&bitmap, ibuf, sizeof( bitmap ));
767 bitmap = ntohs( bitmap );
768 ibuf += sizeof( bitmap );
770 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
771 return get_afp_errno(AFPERR_PARAM);
774 if (path_isadir(s_path)) {
775 return( AFPERR_BADTYPE ); /* it's a directory */
778 if ( s_path->st_errno != 0 ) {
779 return( AFPERR_NOOBJ );
782 if ((u_long)ibuf & 1 ) {
786 if (AFP_OK == ( rc = setfilparams(vol, s_path, bitmap, ibuf )) ) {
787 setvoltime(obj, vol );
794 * cf AFP3.0.pdf page 252 for change_mdate and change_parent_mdate logic
797 extern struct path Cur_Path;
799 int setfilparams(struct vol *vol,
800 struct path *path, uint16_t f_bitmap, char *buf )
802 struct adouble ad, *adp;
804 int bit, isad = 1, err = AFP_OK;
806 u_char achar, *fdType, xyy[4]; /* uninitialized, OK 310105 */
807 uint16_t ashort, bshort, oshort;
810 uint16_t upriv_bit = 0;
812 int change_mdate = 0;
813 int change_parent_mdate = 0;
818 uint16_t bitmap = f_bitmap;
819 uint32_t cdate,bdate;
820 u_char finder_buf[32];
824 LOG(log_debug9, logtype_afpd, "begin setfilparams:");
827 adp = of_ad(vol, path, &ad);
828 upath = path->u_name;
830 if (!vol_unix_priv(vol) && check_access(upath, OPENACC_WR ) < 0) {
831 return AFPERR_ACCESS;
834 /* with unix priv maybe we have to change adouble file priv first */
836 while ( bitmap != 0 ) {
837 while (( bitmap & 1 ) == 0 ) {
844 memcpy(&ashort, buf, sizeof( ashort ));
845 buf += sizeof( ashort );
849 memcpy(&cdate, buf, sizeof(cdate));
850 buf += sizeof( cdate );
853 memcpy(&newdate, buf, sizeof( newdate ));
854 buf += sizeof( newdate );
858 memcpy(&bdate, buf, sizeof( bdate));
859 buf += sizeof( bdate );
863 memcpy(finder_buf, buf, 32 );
864 if (memcmp(buf, "slnkrhap", 8) == 0 && !S_ISLNK(path->st.st_mode)) {
868 char buf[PATH_MAX+1];
869 if ((fp = open(path->u_name, O_RDONLY)) >= 0) {
870 if ((len = read(fp, buf, PATH_MAX+1))) {
871 if (unlink(path->u_name) == 0) {
873 erc = symlink(buf, path->u_name);
882 goto setfilparam_done;
888 case FILPBIT_UNIXPR :
889 if (!vol_unix_priv(vol)) {
890 /* this volume doesn't use unix priv */
896 change_parent_mdate = 1;
898 memcpy( &aint, buf, sizeof( aint ));
899 f_uid = ntohl (aint);
900 buf += sizeof( aint );
901 memcpy( &aint, buf, sizeof( aint ));
902 f_gid = ntohl (aint);
903 buf += sizeof( aint );
904 setfilowner(vol, f_uid, f_gid, path);
906 memcpy( &upriv, buf, sizeof( upriv ));
907 buf += sizeof( upriv );
908 upriv = ntohl (upriv);
909 if ((upriv & S_IWUSR)) {
910 setfilunixmode(vol, path, upriv);
917 case FILPBIT_PDINFO :
918 if (afp_version < 30) { /* else it's UTF8 name */
921 /* Keep special case to support crlf translations */
922 if ((unsigned int) achar == 0x04) {
923 fdType = (u_char *)"TEXT";
926 xyy[0] = ( u_char ) 'p';
937 /* break while loop */
946 /* second try with adouble open
948 if (ad_open(adp, upath, ADFLAGS_HF | ADFLAGS_RDWR | ADFLAGS_CREATE, 0666) < 0) {
949 LOG(log_debug, logtype_afpd, "setfilparams: ad_open_metadata error");
951 * For some things, we don't need an adouble header:
952 * - change of modification date
953 * - UNIX privs (Bug-ID #2863424)
955 if (!symlinked && f_bitmap & ~(1<<FILPBIT_MDATE | 1<<FILPBIT_UNIXPR)) {
956 LOG(log_debug, logtype_afpd, "setfilparams: need adouble access");
957 return AFPERR_ACCESS;
959 LOG(log_debug, logtype_afpd, "setfilparams: no adouble perms, but only FILPBIT_MDATE and/or FILPBIT_UNIXPR");
961 } else if ((ad_get_MD_flags( adp ) & O_CREAT) ) {
962 ad_setname(adp, path->m_name);
967 while ( bitmap != 0 ) {
968 while (( bitmap & 1 ) == 0 ) {
975 ad_getattr(adp, &bshort);
977 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
978 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
982 if ((bshort & htons(ATTRBIT_INVISIBLE)) != (oshort & htons(ATTRBIT_INVISIBLE)))
983 change_parent_mdate = 1;
984 ad_setattr(adp, bshort);
987 ad_setdate(adp, AD_DATE_CREATE, cdate);
992 ad_setdate(adp, AD_DATE_BACKUP, bdate);
995 memcpy(ad_entry( adp, ADEID_FINDERI ), finder_buf, 32 );
997 case FILPBIT_UNIXPR :
999 setfilunixmode(vol, path, upriv);
1002 case FILPBIT_PDINFO :
1003 if (afp_version < 30) { /* else it's UTF8 name */
1004 memcpy(ad_entry( adp, ADEID_FINDERI ), fdType, 4 );
1005 memcpy(ad_entry( adp, ADEID_FINDERI ) + 4, "pdos", 4 );
1010 err = AFPERR_BITMAP;
1011 goto setfilparam_done;
1018 if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
1019 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1023 ad_setdate(adp, AD_DATE_MODIFY, newdate);
1024 ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
1030 ad_close(adp, ADFLAGS_HF);
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 LOG(log_debug, logtype_afpd,
1062 "renamefile: src[%d, \"%s\"] -> dst[\"%s\"]", sdir_fd, src, dst);
1064 if ( unix_rename( sdir_fd, src, -1, dst ) < 0 ) {
1067 return( AFPERR_NOOBJ );
1070 return( AFPERR_ACCESS );
1072 return AFPERR_VLOCK;
1073 case EXDEV : /* Cross device move -- try copy */
1074 /* NOTE: with open file it's an error because after the copy we will
1075 * get two files, it's fixable for our process (eg reopen the new file, get the
1076 * locks, and so on. But it doesn't solve the case with a second process
1078 if (adp->ad_open_forks) {
1079 /* FIXME warning in syslog so admin'd know there's a conflict ?*/
1080 return AFPERR_OLOCK; /* little lie */
1082 if (AFP_OK != ( rc = copyfile(vol, vol, sdir_fd, src, dst, newname, NULL )) ) {
1083 /* on error copyfile delete dest */
1086 return deletefile(vol, sdir_fd, src, 0);
1088 return( AFPERR_PARAM );
1092 if (vol->vfs->vfs_renamefile(vol, sdir_fd, src, dst) < 0 ) {
1096 /* try to undo the data fork rename,
1097 * we know we are on the same device
1100 unix_rename(-1, dst, sdir_fd, src );
1101 /* return the first error */
1104 return AFPERR_NOOBJ;
1107 return AFPERR_ACCESS ;
1109 return AFPERR_VLOCK;
1111 return AFPERR_PARAM ;
1116 /* don't care if we can't open the newly renamed ressource fork */
1117 if (ad_open(adp, dst, ADFLAGS_HF | ADFLAGS_RDWR) == 0) {
1118 ad_setname(adp, newname);
1120 ad_close( adp, ADFLAGS_HF );
1127 convert a Mac long name to an utf8 name,
1129 size_t mtoUTF8(const struct vol *vol, const char *src, size_t srclen, char *dest, size_t destlen)
1133 if ((size_t)-1 == (outlen = convert_string ( vol->v_maccharset, CH_UTF8_MAC, src, srclen, dest, destlen)) ) {
1139 /* ---------------- */
1140 int copy_path_name(const struct vol *vol, char *newname, char *ibuf)
1147 if ( type != 2 && !(afp_version >= 30 && type == 3) ) {
1153 if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
1154 if (afp_version >= 30) {
1155 /* convert it to UTF8
1157 if ((plen = mtoUTF8(vol, ibuf, plen, newname, AFPOBJ_TMPSIZ)) == (size_t)-1)
1161 strncpy( newname, ibuf, plen );
1162 newname[ plen ] = '\0';
1164 if (strlen(newname) != plen) {
1165 /* there's \0 in newname, e.g. it's a pathname not
1173 memcpy(&hint, ibuf, sizeof(hint));
1174 ibuf += sizeof(hint);
1176 memcpy(&len16, ibuf, sizeof(len16));
1177 ibuf += sizeof(len16);
1178 plen = ntohs(len16);
1181 if (plen > AFPOBJ_TMPSIZ) {
1184 strncpy( newname, ibuf, plen );
1185 newname[ plen ] = '\0';
1186 if (strlen(newname) != plen) {
1195 /* -----------------------------------
1197 int afp_copyfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1199 struct vol *s_vol, *d_vol;
1201 char *newname, *p, *upath;
1202 struct path *s_path;
1203 uint32_t sdid, ddid;
1204 int err, retvalue = AFP_OK;
1205 uint16_t svid, dvid;
1207 struct adouble ad, *adp;
1213 memcpy(&svid, ibuf, sizeof( svid ));
1214 ibuf += sizeof( svid );
1215 if (NULL == ( s_vol = getvolbyvid( svid )) ) {
1216 return( AFPERR_PARAM );
1219 memcpy(&sdid, ibuf, sizeof( sdid ));
1220 ibuf += sizeof( sdid );
1221 if (NULL == ( dir = dirlookup( s_vol, sdid )) ) {
1225 memcpy(&dvid, ibuf, sizeof( dvid ));
1226 ibuf += sizeof( dvid );
1227 memcpy(&ddid, ibuf, sizeof( ddid ));
1228 ibuf += sizeof( ddid );
1230 if (NULL == ( s_path = cname( s_vol, dir, &ibuf )) ) {
1231 return get_afp_errno(AFPERR_PARAM);
1233 if ( path_isadir(s_path) ) {
1234 return( AFPERR_BADTYPE );
1237 /* don't allow copies when the file is open.
1238 * XXX: the spec only calls for read/deny write access.
1239 * however, copyfile doesn't have any of that info,
1240 * and locks need to stay coherent. as a result,
1241 * we just balk if the file is opened already. */
1243 adp = of_ad(s_vol, s_path, &ad);
1245 if (ad_open(adp, s_path->u_name, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | ADFLAGS_RDONLY | ADFLAGS_SETSHRMD) < 0) {
1246 return AFPERR_DENYCONF;
1248 denyreadset = (ad_testlock(adp, ADEID_DFORK, AD_FILELOCK_DENY_RD) != 0 ||
1249 ad_testlock(adp, ADEID_RFORK, AD_FILELOCK_DENY_RD) != 0 );
1252 retvalue = AFPERR_DENYCONF;
1256 newname = obj->newtmp;
1257 strcpy( newname, s_path->m_name );
1259 p = ctoupath( s_vol, curdir, newname );
1261 retvalue = AFPERR_PARAM;
1265 if (NULL == ( d_vol = getvolbyvid( dvid )) ) {
1266 retvalue = AFPERR_PARAM;
1270 if (d_vol->v_flags & AFPVOL_RO) {
1271 retvalue = AFPERR_VLOCK;
1275 if (NULL == ( dir = dirlookup( d_vol, ddid )) ) {
1276 retvalue = afp_errno;
1280 if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) {
1281 retvalue = get_afp_errno(AFPERR_NOOBJ);
1285 if ( *s_path->m_name != '\0' ) {
1286 retvalue =path_error(s_path, AFPERR_NOOBJ);
1290 /* one of the handful of places that knows about the path type */
1291 if (copy_path_name(d_vol, newname, ibuf) < 0) {
1292 retvalue = AFPERR_PARAM;
1295 /* newname is always only a filename so curdir *is* its
1298 if (NULL == (upath = mtoupath(d_vol, newname, curdir->d_did, utf8_encoding()))) {
1299 retvalue =AFPERR_PARAM;
1303 if ( (err = copyfile(s_vol, d_vol, -1, p, upath , newname, adp)) < 0 ) {
1309 setvoltime(obj, d_vol );
1312 ad_close( adp, ADFLAGS_DF |ADFLAGS_HF | ADFLAGS_SETSHRMD);
1316 /* ----------------------------------
1317 * if newname is NULL (from directory.c) we don't want to copy the resource fork.
1318 * because we are doing it elsewhere.
1319 * currently if newname is NULL then adp is NULL.
1321 int copyfile(const struct vol *s_vol,
1322 const struct vol *d_vol,
1327 struct adouble *adp)
1329 struct adouble ads, add;
1336 LOG(log_debug, logtype_afpd, "copyfile(sfd:%d,s:'%s',d:'%s',n:'%s')",
1337 sfd, src, dst, newname);
1340 ad_init(&ads, s_vol);
1344 adflags = ADFLAGS_DF | ADFLAGS_RF | ADFLAGS_NORF;
1346 if (ad_openat(adp, sfd, src, adflags | ADFLAGS_RDONLY) < 0) {
1351 if (!AD_RSRC_OPEN(adp))
1352 /* no resource fork, don't create one for dst file */
1353 adflags &= ~ADFLAGS_RF;
1355 stat_result = fstat(ad_data_fileno(adp), &st); /* saving stat exit code, thus saving us on one more stat later on */
1357 if (stat_result < 0) {
1358 /* unlikely but if fstat fails, the default file mode will be 0666. */
1359 st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
1362 ad_init(&add, d_vol);
1363 if (ad_open(&add, dst, adflags | ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_EXCL, st.st_mode | S_IRUSR | S_IWUSR) < 0) {
1365 ad_close( adp, adflags );
1366 if (EEXIST != ret_err) {
1367 deletefile(d_vol, -1, dst, 0);
1370 return AFPERR_EXIST;
1373 if ((err = copy_fork(ADEID_DFORK, &add, adp)) != 0)
1374 LOG(log_error, logtype_afpd, "copyfile('%s'): %s", src, strerror(errno));
1377 if ((err = d_vol->vfs->vfs_copyfile(d_vol, sfd, src, dst)) != 0)
1378 LOG(log_error, logtype_afpd, "copyfile('%s'): %s", src, strerror(errno));
1383 if (!ret_err && newname && (adflags & ADFLAGS_HF)) {
1384 /* set the new name in the resource fork */
1385 ad_copy_header(&add, adp);
1386 ad_setname(&add, newname);
1389 ad_close( adp, adflags );
1391 if (ad_close( &add, adflags ) <0) {
1396 deletefile(d_vol, -1, dst, 0);
1398 else if (stat_result == 0) {
1399 /* set dest modification date to src date */
1402 ut.actime = ut.modtime = st.st_mtime;
1404 /* FIXME netatalk doesn't use resource fork file date
1405 * but maybe we should set its modtime too.
1410 switch ( ret_err ) {
1416 return AFPERR_DFULL;
1418 return AFPERR_NOOBJ;
1420 return AFPERR_ACCESS;
1422 return AFPERR_VLOCK;
1424 return AFPERR_PARAM;
1428 /* -----------------------------------
1429 vol: not NULL delete cnid entry. then we are in curdir and file is a only filename
1430 checkAttrib: 1 check kFPDeleteInhibitBit (deletfile called by afp_delete)
1432 when deletefile is called we don't have lock on it, file is closed (for us)
1433 untrue if called by renamefile
1435 ad_open always try to open file RDWR first and ad_lock takes care of
1436 WRITE lock on read only file.
1439 static int check_attrib(struct adouble *adp)
1441 uint16_t bshort = 0;
1443 ad_getattr(adp, &bshort);
1445 * Does kFPDeleteInhibitBit (bit 8) set?
1447 if ((bshort & htons(ATTRBIT_NODELETE))) {
1448 return AFPERR_OLOCK;
1450 if ((bshort & htons(ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
1456 * dirfd can be used for unlinkat semantics
1458 int deletefile(const struct vol *vol, int dirfd, char *file, int checkAttrib)
1461 struct adouble *adp = NULL;
1462 int adflags, err = AFP_OK;
1465 LOG(log_debug, logtype_afpd, "deletefile('%s')", file);
1469 /* was EACCESS error try to get only metadata */
1470 /* we never want to create a resource fork here, we are going to delete it
1471 * moreover sometimes deletefile is called with a no existent file and
1472 * ad_open would create a 0 byte resource fork
1474 if ( ad_metadataat(dirfd, file, ADFLAGS_CHECK_OF, &ad) == 0 ) {
1475 if ((err = check_attrib(&ad))) {
1476 ad_close(&ad, ADFLAGS_HF | ADFLAGS_CHECK_OF);
1483 /* try to open both forks at once */
1484 adflags = ADFLAGS_DF;
1485 if (ad_openat(&ad, dirfd, file, adflags | ADFLAGS_RF | ADFLAGS_NORF | ADFLAGS_RDONLY) < 0 ) {
1490 case EACCES: /* maybe it's a file with no write mode for us */
1491 break; /* was return AFPERR_ACCESS;*/
1504 if ( adp && AD_RSRC_OPEN(adp) != -1 ) { /* there's a resource fork */
1505 adflags |= ADFLAGS_RF;
1506 /* FIXME we have a pb here because we want to know if a file is open
1507 * there's a 'priority inversion' if you can't open the ressource fork RW
1508 * you can delete it if it's open because you can't get a write lock.
1510 * ADLOCK_FILELOCK means the whole ressource fork, not only after the
1513 * FIXME it doesn't work for RFORK open read only and fork open without deny mode
1515 if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
1521 if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
1522 LOG(log_error, logtype_afpd, "deletefile('%s'): ad_tmplock error: %s", file, strerror(errno));
1524 } else if (!(err = vol->vfs->vfs_deletefile(vol, dirfd, file)) && !(err = netatalk_unlinkat(dirfd, file )) ) {
1526 if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file)))) {
1527 cnid_delete(vol->v_cdb, id);
1533 ad_close(&ad, ADFLAGS_HF | ADFLAGS_CHECK_OF);
1536 ad_close( &ad, adflags ); /* ad_close removes locks if any */
1541 /* ------------------------------------ */
1542 /* return a file id */
1543 int afp_createid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1552 struct path *s_path;
1558 memcpy(&vid, ibuf, sizeof(vid));
1559 ibuf += sizeof(vid);
1561 if (NULL == ( vol = getvolbyvid( vid )) ) {
1562 return( AFPERR_PARAM);
1565 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1569 if (vol->v_flags & AFPVOL_RO)
1570 return AFPERR_VLOCK;
1572 memcpy(&did, ibuf, sizeof( did ));
1573 ibuf += sizeof(did);
1575 if (NULL == ( dir = dirlookup( vol, did )) ) {
1576 return afp_errno; /* was AFPERR_PARAM */
1579 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
1580 return get_afp_errno(AFPERR_NOOBJ); /* was AFPERR_PARAM */
1583 if ( path_isadir(s_path) ) {
1584 return( AFPERR_BADTYPE );
1587 upath = s_path->u_name;
1588 switch (s_path->st_errno) {
1590 break; /* success */
1593 return AFPERR_ACCESS;
1595 return AFPERR_NOOBJ;
1597 return AFPERR_PARAM;
1600 if ((id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath)))) {
1601 memcpy(rbuf, &id, sizeof(id));
1602 *rbuflen = sizeof(id);
1603 return AFPERR_EXISTID;
1606 if ((id = get_id(vol, NULL, st, did, upath, len)) != CNID_INVALID) {
1607 memcpy(rbuf, &id, sizeof(id));
1608 *rbuflen = sizeof(id);
1615 /* ------------------------------- */
1621 static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
1624 struct reenum *param = data;
1625 struct vol *vol = param->vol;
1626 cnid_t did = param->did;
1629 if ( lstat(de->d_name, &path.st) < 0 )
1632 /* update or add to cnid */
1633 aint = cnid_add(vol->v_cdb, &path.st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */
1638 /* --------------------
1639 * Ok the db is out of synch with the dir.
1640 * but if it's a deleted file we don't want to do it again and again.
1643 reenumerate_id(struct vol *vol, char *name, struct dir *dir)
1649 if (vol->v_cdb == NULL) {
1653 /* FIXME use of_statdir ? */
1654 if (lstat(name, &st)) {
1658 if (dirreenumerate(dir, &st)) {
1659 /* we already did it once and the dir haven't been modified */
1660 return dir->d_offcnt;
1664 data.did = dir->d_did;
1665 if ((ret = for_each_dirent(vol, name, reenumerate_loop, (void *)&data)) >= 0) {
1666 setdiroffcnt(curdir, &st, ret);
1667 dir->d_flags |= DIRF_CNID;
1673 /* ------------------------------
1674 resolve a file id */
1675 int afp_resolveid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1684 uint16_t vid, bitmap;
1686 static char buffer[12 + MAXPATHLEN + 1];
1687 int len = 12 + MAXPATHLEN + 1;
1692 memcpy(&vid, ibuf, sizeof(vid));
1693 ibuf += sizeof(vid);
1695 if (NULL == ( vol = getvolbyvid( vid )) ) {
1696 return( AFPERR_PARAM);
1699 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1703 memcpy(&id, ibuf, sizeof( id ));
1708 /* some MacOS versions after a catsearch do a *lot* of afp_resolveid with 0 */
1712 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1713 return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
1716 if (NULL == ( dir = dirlookup( vol, id )) ) {
1717 return AFPERR_NOID; /* idem AFPERR_PARAM */
1719 if (movecwd(vol, dir) < 0) {
1723 return AFPERR_ACCESS;
1727 return AFPERR_PARAM;
1731 memset(&path, 0, sizeof(path));
1732 path.u_name = upath;
1733 if ( of_stat(&path) < 0 ) {
1735 /* with nfs and our working directory is deleted */
1736 if (errno == ESTALE) {
1740 if ( errno == ENOENT && !retry) {
1741 /* cnid db is out of sync, reenumerate the directory and update ids */
1742 reenumerate_id(vol, ".", dir);
1750 return AFPERR_ACCESS;
1754 return AFPERR_PARAM;
1758 /* directories are bad */
1759 if (S_ISDIR(path.st.st_mode)) {
1760 /* OS9 and OSX don't return the same error code */
1761 return (afp_version >=30)?AFPERR_NOID:AFPERR_BADTYPE;
1764 memcpy(&bitmap, ibuf, sizeof(bitmap));
1765 bitmap = ntohs( bitmap );
1766 if (NULL == (path.m_name = utompath(vol, upath, cnid, utf8_encoding()))) {
1770 if (AFP_OK != (err = getfilparams(vol, bitmap, &path , curdir,
1771 rbuf + sizeof(bitmap), &buflen))) {
1774 *rbuflen = buflen + sizeof(bitmap);
1775 memcpy(rbuf, ibuf, sizeof(bitmap));
1780 /* ------------------------------ */
1781 int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1791 static char buffer[12 + MAXPATHLEN + 1];
1792 int len = 12 + MAXPATHLEN + 1;
1797 memcpy(&vid, ibuf, sizeof(vid));
1798 ibuf += sizeof(vid);
1800 if (NULL == ( vol = getvolbyvid( vid )) ) {
1801 return( AFPERR_PARAM);
1804 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1808 if (vol->v_flags & AFPVOL_RO)
1809 return AFPERR_VLOCK;
1811 memcpy(&id, ibuf, sizeof( id ));
1815 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1819 if (NULL == ( dir = dirlookup( vol, id )) ) {
1820 if (afp_errno == AFPERR_NOOBJ) {
1824 return( AFPERR_PARAM );
1828 if ((movecwd(vol, dir) < 0) || (lstat(upath, &st) < 0)) {
1832 return AFPERR_ACCESS;
1837 /* still try to delete the id */
1841 return AFPERR_PARAM;
1844 else if (S_ISDIR(st.st_mode)) /* directories are bad */
1845 return AFPERR_BADTYPE;
1848 if (cnid_delete(vol->v_cdb, fileid)) {
1851 return AFPERR_VLOCK;
1854 return AFPERR_ACCESS;
1856 return AFPERR_PARAM;
1863 /* ------------------------------ */
1864 static struct adouble *find_adouble(struct path *path, struct ofork **of, struct adouble *adp)
1868 if (path->st_errno) {
1869 switch (path->st_errno) {
1871 afp_errno = AFPERR_NOID;
1875 afp_errno = AFPERR_ACCESS;
1878 afp_errno = AFPERR_PARAM;
1883 /* we use file_access both for legacy Mac perm and
1884 * for unix privilege, rename will take care of folder perms
1886 if (file_access(path, OPENACC_WR ) < 0) {
1887 afp_errno = AFPERR_ACCESS;
1891 if ((*of = of_findname(path))) {
1892 /* reuse struct adouble so it won't break locks */
1896 ret = ad_open(adp, path->u_name, ADFLAGS_HF | ADFLAGS_RDWR);
1898 if ( !ret && ad_reso_fileno(adp) != -1 && !(adp->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
1900 * The user must have the Read & Write privilege for both files in order to use this command.
1902 ad_close(adp, ADFLAGS_HF);
1903 afp_errno = AFPERR_ACCESS;
1910 #define APPLETEMP ".AppleTempXXXXXX"
1912 int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1914 struct stat srcst, destst;
1916 struct dir *dir, *sdir;
1917 char *spath, temp[17], *p;
1918 char *supath, *upath;
1923 struct adouble *adsp = NULL;
1924 struct adouble *addp = NULL;
1925 struct ofork *s_of = NULL;
1926 struct ofork *d_of = NULL;
1939 memcpy(&vid, ibuf, sizeof(vid));
1940 ibuf += sizeof(vid);
1942 if (NULL == ( vol = getvolbyvid( vid )) ) {
1943 return( AFPERR_PARAM);
1946 if ((vol->v_flags & AFPVOL_RO))
1947 return AFPERR_VLOCK;
1949 /* source and destination dids */
1950 memcpy(&sid, ibuf, sizeof(sid));
1951 ibuf += sizeof(sid);
1952 memcpy(&did, ibuf, sizeof(did));
1953 ibuf += sizeof(did);
1956 if (NULL == (dir = dirlookup( vol, sid )) ) {
1957 return afp_errno; /* was AFPERR_PARAM */
1960 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
1961 return get_afp_errno(AFPERR_NOOBJ);
1964 if ( path_isadir(path) ) {
1965 return AFPERR_BADTYPE; /* it's a dir */
1968 /* save some stuff */
1971 spath = obj->oldtmp;
1972 supath = obj->newtmp;
1973 strcpy(spath, path->m_name);
1974 strcpy(supath, path->u_name); /* this is for the cnid changing */
1975 p = absupath( vol, sdir, supath);
1977 /* pathname too long */
1978 return AFPERR_PARAM ;
1982 if (!(adsp = find_adouble( path, &s_of, &ads))) {
1986 /* ***** from here we may have resource fork open **** */
1988 /* look for the source cnid. if it doesn't exist, don't worry about
1990 sid = cnid_lookup(vol->v_cdb, &srcst, sdir->d_did, supath,slen = strlen(supath));
1992 if (NULL == ( dir = dirlookup( vol, did )) ) {
1993 err = afp_errno; /* was AFPERR_PARAM */
1994 goto err_exchangefile;
1997 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
1998 err = get_afp_errno(AFPERR_NOOBJ);
1999 goto err_exchangefile;
2002 if ( path_isadir(path) ) {
2003 err = AFPERR_BADTYPE;
2004 goto err_exchangefile;
2007 /* FPExchangeFiles is the only call that can return the SameObj
2009 if ((curdir == sdir) && strcmp(spath, path->m_name) == 0) {
2010 err = AFPERR_SAMEOBJ;
2011 goto err_exchangefile;
2015 if (!(addp = find_adouble( path, &d_of, &add))) {
2017 goto err_exchangefile;
2021 /* they are not on the same device and at least one is open
2022 * FIXME broken for for crossdev and adouble v2
2025 crossdev = (srcst.st_dev != destst.st_dev);
2026 if (/* (d_of || s_of) && */ crossdev) {
2028 goto err_exchangefile;
2031 /* look for destination id. */
2032 upath = path->u_name;
2033 did = cnid_lookup(vol->v_cdb, &destst, curdir->d_did, upath, dlen = strlen(upath));
2035 /* construct a temp name.
2036 * NOTE: the temp file will be in the dest file's directory. it
2037 * will also be inaccessible from AFP. */
2038 memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
2039 if (!mktemp(temp)) {
2041 goto err_exchangefile;
2045 /* FIXME we need to close fork for copy, both s_of and d_of are null */
2046 ad_close(adsp, ADFLAGS_HF);
2047 ad_close(addp, ADFLAGS_HF);
2050 /* now, quickly rename the file. we error if we can't. */
2051 if ((err = renamefile(vol, -1, p, temp, temp, adsp)) != AFP_OK)
2052 goto err_exchangefile;
2053 of_rename(vol, s_of, sdir, spath, curdir, temp);
2055 /* rename destination to source */
2056 if ((err = renamefile(vol, -1, upath, p, spath, addp)) != AFP_OK)
2057 goto err_src_to_tmp;
2058 of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
2060 /* rename temp to destination */
2061 if ((err = renamefile(vol, -1, temp, upath, path->m_name, adsp)) != AFP_OK)
2062 goto err_dest_to_src;
2063 of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
2065 /* id's need switching. src -> dest and dest -> src.
2066 * we need to re-stat() if it was a cross device copy.
2069 cnid_delete(vol->v_cdb, sid);
2071 cnid_delete(vol->v_cdb, did);
2073 if ((did && ( (crossdev && lstat( upath, &srcst) < 0) ||
2074 cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
2076 (sid && ( (crossdev && lstat(p, &destst) < 0) ||
2077 cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
2082 err = AFPERR_ACCESS;
2087 goto err_temp_to_dest;
2090 /* here we need to reopen if crossdev */
2091 if (sid && ad_setid(addp, destst.st_dev, destst.st_ino, sid, sdir->d_did, vol->v_stamp))
2096 if (did && ad_setid(adsp, srcst.st_dev, srcst.st_ino, did, curdir->d_did, vol->v_stamp))
2101 /* change perms, src gets dest perm and vice versa */
2106 LOG(log_error, logtype_afpd, "seteuid failed %s", strerror(errno));
2107 err = AFP_OK; /* ignore error */
2108 goto err_temp_to_dest;
2112 * we need to exchange ACL entries as well
2114 /* exchange_acls(vol, p, upath); */
2119 path->m_name = NULL;
2120 path->u_name = upath;
2122 setfilunixmode(vol, path, destst.st_mode);
2123 setfilowner(vol, destst.st_uid, destst.st_gid, path);
2130 setfilunixmode(vol, path, srcst.st_mode);
2131 setfilowner(vol, srcst.st_uid, srcst.st_gid, path);
2133 if ( setegid(gid) < 0 || seteuid(uid) < 0) {
2134 LOG(log_error, logtype_afpd, "can't seteuid back %s", strerror(errno));
2139 goto err_exchangefile;
2141 /* all this stuff is so that we can unwind a failed operation
2144 /* rename dest to temp */
2145 renamefile(vol, -1, upath, temp, temp, adsp);
2146 of_rename(vol, s_of, curdir, upath, curdir, temp);
2149 /* rename source back to dest */
2150 renamefile(vol, -1, p, upath, path->m_name, addp);
2151 of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
2154 /* rename temp back to source */
2155 renamefile(vol, -1, temp, p, spath, adsp);
2156 of_rename(vol, s_of, curdir, temp, sdir, spath);
2159 if ( !s_of && adsp && ad_meta_fileno(adsp) != -1 ) { /* META */
2160 ad_close(adsp, ADFLAGS_HF);
2162 if ( !d_of && addp && ad_meta_fileno(addp) != -1 ) {/* META */
2163 ad_close(addp, ADFLAGS_HF);
2167 if ((cached = dircache_search_by_did(vol, sid)) != NULL)
2168 (void)dir_remove(vol, cached);
2169 if ((cached = dircache_search_by_did(vol, did)) != NULL)
2170 (void)dir_remove(vol, cached);