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>
27 #include <atalk/netatalk_conf.h>
29 #include "directory.h"
38 /* the format for the finderinfo fields (from IM: Toolbox Essentials):
39 * field bytes subfield bytes
42 * ioFlFndrInfo 16 -> type 4 type field
43 * creator 4 creator field
44 * flags 2 finder flags:
46 * location 4 location in window
47 * folder 2 window that contains file
49 * ioFlXFndrInfo 16 -> iconID 2 icon id
51 * script 1 script system
53 * commentID 2 comment id
54 * putawayID 4 home directory id
57 const u_char ufinderi[ADEDLEN_FINDERI] = {
58 0, 0, 0, 0, 0, 0, 0, 0,
59 1, 0, 0, 0, 0, 0, 0, 0,
60 0, 0, 0, 0, 0, 0, 0, 0,
61 0, 0, 0, 0, 0, 0, 0, 0
64 static const u_char old_ufinderi[] = {
65 'T', 'E', 'X', 'T', 'U', 'N', 'I', 'X'
68 /* ----------------------
70 static int default_type(void *finder)
72 if (!memcmp(finder, ufinderi, 8) || !memcmp(finder, old_ufinderi, 8))
77 /* FIXME path : unix or mac name ? (for now it's unix name ) */
78 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);
90 memcpy(data, ufinderi, ADEDLEN_FINDERI);
91 if (vol_inv_dots(vol) && *upath == '.') { /* make it invisible */
94 ashort = htons(FINDERINFO_INVISIBLE);
95 memcpy((char *)data + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
101 memcpy(&linkflag, (char *)data + FINDERINFO_FRFLAGOFF, 2);
102 linkflag |= htons(FINDERINFO_ISALIAS);
103 memcpy((char *)data + FINDERINFO_FRFLAGOFF, &linkflag, 2);
104 memcpy((char *)data + FINDERINFO_FRTYPEOFF,"slnk",4);
105 memcpy((char *)data + FINDERINFO_FRCREATOFF,"rhap",4);
111 /* ---------------------
113 char *set_name(const struct vol *vol, char *data, cnid_t pid, char *name, cnid_t id, uint32_t utf8)
118 aint = strlen( name );
122 if (utf8_encoding(vol->v_obj)) {
123 /* but name is an utf8 mac name */
126 /* global static variable... */
128 if (!(u = mtoupath(vol, name, pid, 1)) || !(m = utompath(vol, u, id, 0))) {
137 if (aint > MACFILELEN)
144 if (aint > UTF8FILELEN_EARLY) /* FIXME safeguard, anyway if no ascii char it's game over*/
145 aint = UTF8FILELEN_EARLY;
147 utf8 = vol->v_kTextEncoding;
148 memcpy(data, &utf8, sizeof(utf8));
149 data += sizeof(utf8);
152 memcpy(data, &temp, sizeof(temp));
153 data += sizeof(temp);
156 memcpy( data, src, aint );
166 * FIXME: PDINFO is UTF8 and doesn't need adp
168 #define PARAM_NEED_ADP(b) ((b) & ((1 << FILPBIT_ATTR) |\
169 (1 << FILPBIT_CDATE) |\
170 (1 << FILPBIT_MDATE) |\
171 (1 << FILPBIT_BDATE) |\
172 (1 << FILPBIT_FINFO) |\
173 (1 << FILPBIT_RFLEN) |\
174 (1 << FILPBIT_EXTRFLEN) |\
175 (1 << FILPBIT_PDINFO) |\
176 (1 << FILPBIT_FNUM) |\
177 (1 << FILPBIT_UNIXPR)))
180 * @brief Get CNID for did/upath args both from database and adouble file
182 * 1. Get the objects CNID as stored in its adouble file
183 * 2. Get the objects CNID from the database
184 * 3. If there's a problem with a "dbd" database, fallback to "tdb" in memory
185 * 4. In case 2 and 3 differ, store 3 in the adouble file
187 * @param vol (rw) volume
188 * @param adp (rw) adouble struct of object upath, might be NULL
189 * @param st (r) stat of upath, must NOT be NULL
190 * @param did (r) parent CNID of upath
191 * @param upath (r) name of object
192 * @param len (r) strlen of upath
194 uint32_t get_id(struct vol *vol,
196 const struct stat *st,
201 static int first = 1; /* mark if this func is called the first time */
203 uint32_t dbcnid = CNID_INVALID;
206 if (vol->v_cdb != NULL) {
207 /* prime aint with what we think is the cnid, set did to zero for
208 catching moved files */
209 adcnid = ad_getid(adp, st->st_dev, st->st_ino, 0, vol->v_stamp); /* (1) */
211 dbcnid = cnid_add(vol->v_cdb, st, did, upath, len, adcnid); /* (2) */
212 /* Throw errors if cnid_add fails. */
213 if (dbcnid == CNID_INVALID) {
215 case CNID_ERR_CLOSE: /* the db is closed */
218 LOG(log_error, logtype_afpd, "get_id: Incorrect parameters passed to cnid_add");
219 afp_errno = AFPERR_PARAM;
222 afp_errno = AFPERR_PARAM;
225 /* Close CNID backend if "dbd" and switch to temp in-memory "tdb" */
226 /* we have to do it here for "dbd" because it uses "lazy opening" */
227 /* In order to not end in a loop somehow with goto restart below */
229 if (first && (strcmp(vol->v_cnidscheme, "dbd") == 0)) { /* (3) */
230 cnid_close(vol->v_cdb);
231 free(vol->v_cnidscheme);
232 vol->v_cnidscheme = strdup("tdb");
234 int flags = CNID_FLAG_MEMORY;
235 if ((vol->v_flags & AFPVOL_NODEV)) {
236 flags |= CNID_FLAG_NODEV;
238 LOG(log_error, logtype_afpd, "Reopen volume %s using in memory temporary CNID DB.",
240 vol->v_cdb = cnid_open(vol->v_path, vol->v_umask, "tdb", flags, NULL, NULL);
243 vol->v_flags |= AFPVOL_RO;
245 /* kill ourself with SIGUSR2 aka msg pending */
246 setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB instead."
247 "Check server messages for details. Switching to read-only mode.");
248 kill(getpid(), SIGUSR2);
250 goto restart; /* not try again with the temp CNID db */
253 setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB failed too!"
254 "Check server messages for details, can't recover from this state!");
258 afp_errno = AFPERR_MISC;
262 else if (adp && (adcnid != dbcnid)) { /* 4 */
263 /* Update the ressource fork. For a folder adp is always null */
264 LOG(log_debug, logtype_afpd, "get_id(%s/%s): calling ad_setid(old: %u, new: %u)",
265 getcwdpath(), upath, htonl(adcnid), htonl(dbcnid));
266 if (ad_setid(adp, st->st_dev, st->st_ino, dbcnid, did, vol->v_stamp)) {
277 /* -------------------------- */
278 int getmetadata(const AFPObj *obj,
281 struct path *path, struct dir *dir,
282 char *buf, size_t *buflen, struct adouble *adp)
284 char *data, *l_nameoff = NULL, *upath;
285 char *utf_nameoff = NULL;
290 u_char achar, fdType[4];
295 LOG(log_debug, logtype_afpd, "getmetadata(\"%s\")", path->u_name);
297 upath = path->u_name;
301 if ( ((bitmap & ( (1 << FILPBIT_FINFO)|(1 << FILPBIT_LNAME)|(1 <<FILPBIT_PDINFO) ) ) && !path->m_name)
302 || (bitmap & ( (1 << FILPBIT_LNAME) ) && utf8_encoding(obj)) /* FIXME should be m_name utf8 filename */
303 || (bitmap & (1 << FILPBIT_FNUM))) {
306 struct dir *cachedfile;
307 int len = strlen(upath);
308 if ((cachedfile = dircache_search_by_name(vol, dir, upath, len)) != NULL)
309 id = cachedfile->d_did;
311 id = get_id(vol, adp, st, dir->d_did, upath, len);
313 /* Add it to the cache */
314 LOG(log_debug, logtype_afpd, "getmetadata: caching: did:%u, \"%s\", cnid:%u",
315 ntohl(dir->d_did), upath, ntohl(id));
317 /* Get macname from unixname first */
318 if (path->m_name == NULL) {
319 if ((path->m_name = utompath(vol, upath, id, utf8_encoding(obj))) == NULL) {
320 LOG(log_error, logtype_afpd, "getmetadata: utompath error");
326 if (((fullpath = bstrcpy(dir->d_fullpath)) == NULL)
327 || (bconchar(fullpath, '/') != BSTR_OK)
328 || (bcatcstr(fullpath, upath)) != BSTR_OK) {
329 LOG(log_error, logtype_afpd, "getmetadata: fullpath: %s", strerror(errno));
333 if ((cachedfile = dir_new(path->m_name, upath, vol, dir->d_did, id, fullpath, st)) == NULL) {
334 LOG(log_error, logtype_afpd, "getmetadata: error from dir_new");
338 if ((dircache_add(vol, cachedfile)) != 0) {
339 LOG(log_error, logtype_afpd, "getmetadata: fatal dircache error");
347 if (id == CNID_INVALID)
351 path->m_name = utompath(vol, upath, id, utf8_encoding(vol->v_obj));
354 while ( bitmap != 0 ) {
355 while (( bitmap & 1 ) == 0 ) {
363 ad_getattr(adp, &ashort);
364 } else if (vol_inv_dots(vol) && *upath == '.') {
365 ashort = htons(ATTRBIT_INVISIBLE);
369 /* FIXME do we want a visual clue if the file is read only
372 accessmode(vol, ".", &ma, dir , NULL);
373 if ((ma.ma_user & AR_UWRITE)) {
374 accessmode(vol, upath, &ma, dir , st);
375 if (!(ma.ma_user & AR_UWRITE)) {
376 ashort |= htons(ATTRBIT_NOWRITE);
380 memcpy(data, &ashort, sizeof( ashort ));
381 data += sizeof( ashort );
382 LOG(log_debug, logtype_afpd, "metadata('%s'): AFP Attributes: %04x",
383 path->u_name, ntohs(ashort));
387 memcpy(data, &dir->d_did, sizeof( uint32_t ));
388 data += sizeof( uint32_t );
389 LOG(log_debug, logtype_afpd, "metadata('%s'): Parent DID: %u",
390 path->u_name, ntohl(dir->d_did));
394 if (!adp || (ad_getdate(adp, AD_DATE_CREATE, &aint) < 0))
395 aint = AD_DATE_FROM_UNIX(st->st_mtime);
396 memcpy(data, &aint, sizeof( aint ));
397 data += sizeof( aint );
401 if ( adp && (ad_getdate(adp, AD_DATE_MODIFY, &aint) == 0)) {
402 if ((st->st_mtime > AD_DATE_TO_UNIX(aint))) {
403 aint = AD_DATE_FROM_UNIX(st->st_mtime);
406 aint = AD_DATE_FROM_UNIX(st->st_mtime);
408 memcpy(data, &aint, sizeof( int ));
409 data += sizeof( int );
413 if (!adp || (ad_getdate(adp, AD_DATE_BACKUP, &aint) < 0))
414 aint = AD_DATE_START;
415 memcpy(data, &aint, sizeof( int ));
416 data += sizeof( int );
420 get_finderinfo(vol, upath, adp, (char *)data,S_ISLNK(st->st_mode));
421 data += ADEDLEN_FINDERI;
426 data += sizeof( uint16_t );
430 memset(data, 0, sizeof(uint16_t));
431 data += sizeof( uint16_t );
435 memcpy(data, &id, sizeof( id ));
436 data += sizeof( id );
437 LOG(log_debug, logtype_afpd, "metadata('%s'): CNID: %u",
438 path->u_name, ntohl(id));
442 if (st->st_size > 0xffffffff)
445 aint = htonl( st->st_size );
446 memcpy(data, &aint, sizeof( aint ));
447 data += sizeof( aint );
452 if (adp->ad_rlen > 0xffffffff)
455 aint = htonl( adp->ad_rlen);
459 memcpy(data, &aint, sizeof( aint ));
460 data += sizeof( aint );
463 /* Current client needs ProDOS info block for this file.
464 Use simple heuristic and let the Mac "type" string tell
465 us what the PD file code should be. Everything gets a
466 subtype of 0x0000 unless the original value was hashed
467 to "pXYZ" when we created it. See IA, Ver 2.
468 <shirsch@adelphia.net> */
469 case FILPBIT_PDINFO :
470 if (obj->afp_version >= 30) { /* UTF8 name */
471 utf8 = kTextEncodingUTF8;
473 data += sizeof( uint16_t );
475 memcpy(data, &aint, sizeof( aint ));
476 data += sizeof( aint );
480 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
482 if ( memcmp( fdType, "TEXT", 4 ) == 0 ) {
486 else if ( memcmp( fdType, "PSYS", 4 ) == 0 ) {
490 else if ( memcmp( fdType, "PS16", 4 ) == 0 ) {
494 else if ( memcmp( fdType, "BINA", 4 ) == 0 ) {
498 else if ( fdType[0] == 'p' ) {
500 ashort = (fdType[2] * 256) + fdType[3];
514 memcpy(data, &ashort, sizeof( ashort ));
515 data += sizeof( ashort );
516 memset(data, 0, sizeof( ashort ));
517 data += sizeof( ashort );
520 case FILPBIT_EXTDFLEN:
521 aint = htonl(st->st_size >> 32);
522 memcpy(data, &aint, sizeof( aint ));
523 data += sizeof( aint );
524 aint = htonl(st->st_size);
525 memcpy(data, &aint, sizeof( aint ));
526 data += sizeof( aint );
528 case FILPBIT_EXTRFLEN:
531 aint = htonl(adp->ad_rlen >> 32);
532 memcpy(data, &aint, sizeof( aint ));
533 data += sizeof( aint );
535 aint = htonl(adp->ad_rlen);
536 memcpy(data, &aint, sizeof( aint ));
537 data += sizeof( aint );
539 case FILPBIT_UNIXPR :
540 /* accessmode may change st_mode with ACLs */
541 accessmode(obj, vol, upath, &ma, dir , st);
543 aint = htonl(st->st_uid);
544 memcpy( data, &aint, sizeof( aint ));
545 data += sizeof( aint );
546 aint = htonl(st->st_gid);
547 memcpy( data, &aint, sizeof( aint ));
548 data += sizeof( aint );
551 type == slnk indicates an OSX style symlink,
552 we have to add S_IFLNK to the mode, otherwise
553 10.3 clients freak out. */
557 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
558 if ( memcmp( fdType, "slnk", 4 ) == 0 ) {
564 memcpy( data, &aint, sizeof( aint ));
565 data += sizeof( aint );
567 *data++ = ma.ma_user;
568 *data++ = ma.ma_world;
569 *data++ = ma.ma_group;
570 *data++ = ma.ma_owner;
574 return( AFPERR_BITMAP );
580 ashort = htons( data - buf );
581 memcpy(l_nameoff, &ashort, sizeof( ashort ));
582 data = set_name(vol, data, dir->d_did, path->m_name, id, 0);
585 ashort = htons( data - buf );
586 memcpy(utf_nameoff, &ashort, sizeof( ashort ));
587 data = set_name(vol, data, dir->d_did, path->m_name, id, utf8);
589 *buflen = data - buf;
593 /* ----------------------- */
594 int getfilparams(const AFPObj *obj,
597 struct path *path, struct dir *dir,
598 char *buf, size_t *buflen )
600 struct adouble ad, *adp;
605 LOG(log_debug, logtype_afpd, "getfilparams(\"%s\")", path->u_name);
607 opened = PARAM_NEED_ADP(bitmap);
612 flags = (bitmap & (1 << FILPBIT_ATTR)) ? ADFLAGS_CHECK_OF : 0;
614 adp = of_ad(vol, path, &ad);
615 upath = path->u_name;
617 if ( ad_metadata( upath, flags, adp) < 0 ) {
620 LOG(log_error, logtype_afpd, "getfilparams(%s): %s: check resource fork permission?",
621 upath, strerror(errno));
622 return AFPERR_ACCESS;
624 LOG(log_error, logtype_afpd, "getfilparams(%s): bad resource fork", upath);
633 rc = getmetadata(obj, vol, bitmap, path, dir, buf, buflen, adp);
634 ad_close(adp, ADFLAGS_HF | flags);
639 /* ----------------------------- */
640 int afp_createfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
645 struct ofork *of = NULL;
647 int creatf, did, openf, retvalue = AFP_OK;
653 creatf = (unsigned char) *ibuf++;
655 memcpy(&vid, ibuf, sizeof( vid ));
656 ibuf += sizeof( vid );
658 if (NULL == ( vol = getvolbyvid( vid )) )
659 return( AFPERR_PARAM );
661 if (vol->v_flags & AFPVOL_RO)
664 memcpy(&did, ibuf, sizeof( did));
665 ibuf += sizeof( did );
667 if (NULL == ( dir = dirlookup( vol, did )) )
670 if (NULL == ( s_path = cname( vol, dir, &ibuf )) )
671 return get_afp_errno(AFPERR_PARAM);
672 if ( *s_path->m_name == '\0' )
673 return( AFPERR_BADTYPE );
675 upath = s_path->u_name;
678 /* if upath is deleted we already in trouble anyway */
679 if ((of = of_findname(s_path))) {
687 openf = ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_TRUNC;
689 /* on a soft create, if the file is open then ad_open won't fail
690 because open syscall is not called */
691 openf = ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_EXCL;
693 if (ad_open(&ad, upath, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | openf, 0666) < 0) {
697 case ENOENT : /* we were already in 'did folder' so chdir() didn't fail */
698 return ( AFPERR_NOOBJ );
700 return( AFPERR_EXIST );
702 return( AFPERR_ACCESS );
705 return( AFPERR_DFULL );
707 return( AFPERR_PARAM );
710 if ( ad_meta_fileno( &ad ) == -1 ) { /* Hard META / HF */
711 /* FIXME with hard create on an existing file, we already
712 * corrupted the data file.
714 netatalk_unlink( upath );
715 ad_close( &ad, ADFLAGS_DF );
716 return AFPERR_ACCESS;
719 path = s_path->m_name;
720 ad_setname(&ad, path);
723 if (lstat(upath, &st) != 0) {
724 LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): stat: %s",
725 upath, strerror(errno));
726 ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF);
730 (void)get_id(vol, &ad, &st, dir->d_did, upath, strlen(upath));
733 ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF );
734 fce_register_new_file(s_path);
739 setvoltime(obj, vol );
744 int afp_setfilparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
750 uint16_t vid, bitmap;
755 memcpy(&vid, ibuf, sizeof( vid ));
756 ibuf += sizeof( vid );
757 if (NULL == ( vol = getvolbyvid( vid )) ) {
758 return( AFPERR_PARAM );
761 if (vol->v_flags & AFPVOL_RO)
764 memcpy(&did, ibuf, sizeof( did ));
765 ibuf += sizeof( did );
766 if (NULL == ( dir = dirlookup( vol, did )) ) {
767 return afp_errno; /* was AFPERR_NOOBJ */
770 memcpy(&bitmap, ibuf, sizeof( bitmap ));
771 bitmap = ntohs( bitmap );
772 ibuf += sizeof( bitmap );
774 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
775 return get_afp_errno(AFPERR_PARAM);
778 if (path_isadir(s_path)) {
779 return( AFPERR_BADTYPE ); /* it's a directory */
782 if ( s_path->st_errno != 0 ) {
783 return( AFPERR_NOOBJ );
786 if ((u_long)ibuf & 1 ) {
790 if (AFP_OK == ( rc = setfilparams(obj, vol, s_path, bitmap, ibuf )) ) {
791 setvoltime(obj, vol );
798 * cf AFP3.0.pdf page 252 for change_mdate and change_parent_mdate logic
801 extern struct path Cur_Path;
803 int setfilparams(const AFPObj *obj, struct vol *vol,
804 struct path *path, uint16_t f_bitmap, char *buf )
806 struct adouble ad, *adp;
808 int bit, isad = 1, err = AFP_OK;
810 u_char achar, *fdType, xyy[4]; /* uninitialized, OK 310105 */
811 uint16_t ashort, bshort, oshort;
814 uint16_t upriv_bit = 0;
816 int change_mdate = 0;
817 int change_parent_mdate = 0;
822 uint16_t bitmap = f_bitmap;
823 uint32_t cdate,bdate;
824 u_char finder_buf[32];
828 LOG(log_debug9, logtype_afpd, "begin setfilparams:");
831 adp = of_ad(vol, path, &ad);
832 upath = path->u_name;
834 if (!vol_unix_priv(vol) && check_access(obj, vol, upath, OPENACC_WR ) < 0) {
835 return AFPERR_ACCESS;
838 /* with unix priv maybe we have to change adouble file priv first */
840 while ( bitmap != 0 ) {
841 while (( bitmap & 1 ) == 0 ) {
848 memcpy(&ashort, buf, sizeof( ashort ));
849 buf += sizeof( ashort );
853 memcpy(&cdate, buf, sizeof(cdate));
854 buf += sizeof( cdate );
857 memcpy(&newdate, buf, sizeof( newdate ));
858 buf += sizeof( newdate );
862 memcpy(&bdate, buf, sizeof( bdate));
863 buf += sizeof( bdate );
867 memcpy(finder_buf, buf, 32 );
868 if (memcmp(buf, "slnkrhap", 8) == 0 && !S_ISLNK(path->st.st_mode)) {
872 char buf[PATH_MAX+1];
873 if ((fp = open(path->u_name, O_RDONLY)) >= 0) {
874 if ((len = read(fp, buf, PATH_MAX+1))) {
875 if (unlink(path->u_name) == 0) {
877 erc = symlink(buf, path->u_name);
886 goto setfilparam_done;
892 case FILPBIT_UNIXPR :
893 if (!vol_unix_priv(vol)) {
894 /* this volume doesn't use unix priv */
900 change_parent_mdate = 1;
902 memcpy( &aint, buf, sizeof( aint ));
903 f_uid = ntohl (aint);
904 buf += sizeof( aint );
905 memcpy( &aint, buf, sizeof( aint ));
906 f_gid = ntohl (aint);
907 buf += sizeof( aint );
908 setfilowner(vol, f_uid, f_gid, path);
910 memcpy( &upriv, buf, sizeof( upriv ));
911 buf += sizeof( upriv );
912 upriv = ntohl (upriv);
913 if ((upriv & S_IWUSR)) {
914 setfilunixmode(vol, path, upriv);
921 case FILPBIT_PDINFO :
922 if (obj->afp_version < 30) { /* else it's UTF8 name */
925 /* Keep special case to support crlf translations */
926 if ((unsigned int) achar == 0x04) {
927 fdType = (u_char *)"TEXT";
930 xyy[0] = ( u_char ) 'p';
941 /* break while loop */
950 /* second try with adouble open
952 if (ad_open(adp, upath, ADFLAGS_HF | ADFLAGS_RDWR | ADFLAGS_CREATE, 0666) < 0) {
953 LOG(log_debug, logtype_afpd, "setfilparams: ad_open_metadata error");
955 * For some things, we don't need an adouble header:
956 * - change of modification date
957 * - UNIX privs (Bug-ID #2863424)
959 if (!symlinked && f_bitmap & ~(1<<FILPBIT_MDATE | 1<<FILPBIT_UNIXPR)) {
960 LOG(log_debug, logtype_afpd, "setfilparams: need adouble access");
961 return AFPERR_ACCESS;
963 LOG(log_debug, logtype_afpd, "setfilparams: no adouble perms, but only FILPBIT_MDATE and/or FILPBIT_UNIXPR");
965 } else if ((ad_get_MD_flags( adp ) & O_CREAT) ) {
966 ad_setname(adp, path->m_name);
971 while ( bitmap != 0 ) {
972 while (( bitmap & 1 ) == 0 ) {
979 ad_getattr(adp, &bshort);
981 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
982 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
986 if ((bshort & htons(ATTRBIT_INVISIBLE)) != (oshort & htons(ATTRBIT_INVISIBLE)))
987 change_parent_mdate = 1;
988 ad_setattr(adp, bshort);
991 ad_setdate(adp, AD_DATE_CREATE, cdate);
996 ad_setdate(adp, AD_DATE_BACKUP, bdate);
999 memcpy(ad_entry( adp, ADEID_FINDERI ), finder_buf, 32 );
1001 case FILPBIT_UNIXPR :
1003 setfilunixmode(vol, path, upriv);
1006 case FILPBIT_PDINFO :
1007 if (obj->afp_version < 30) { /* else it's UTF8 name */
1008 memcpy(ad_entry( adp, ADEID_FINDERI ), fdType, 4 );
1009 memcpy(ad_entry( adp, ADEID_FINDERI ) + 4, "pdos", 4 );
1014 err = AFPERR_BITMAP;
1015 goto setfilparam_done;
1022 if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
1023 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1027 ad_setdate(adp, AD_DATE_MODIFY, newdate);
1028 ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
1034 ad_close(adp, ADFLAGS_HF);
1037 if (change_parent_mdate && gettimeofday(&tv, NULL) == 0) {
1038 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1039 bitmap = 1<<FILPBIT_MDATE;
1040 setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
1044 LOG(log_debug9, logtype_afpd, "end setfilparams:");
1050 * renamefile and copyfile take the old and new unix pathnames
1051 * and the new mac name.
1053 * sdir_fd source dir fd to which src path is relative (for openat et al semantics)
1054 * passing -1 means this is not used, src path is a full path
1055 * src the source path
1056 * dst the dest filename in current dir
1057 * newname the dest mac name
1058 * adp adouble struct of src file, if open, or & zeroed one
1061 int renamefile(const struct vol *vol, int sdir_fd, char *src, char *dst, char *newname, struct adouble *adp)
1065 LOG(log_debug, logtype_afpd,
1066 "renamefile: src[%d, \"%s\"] -> dst[\"%s\"]", sdir_fd, src, dst);
1068 if ( unix_rename( sdir_fd, src, -1, dst ) < 0 ) {
1071 return( AFPERR_NOOBJ );
1074 return( AFPERR_ACCESS );
1076 return AFPERR_VLOCK;
1077 case EXDEV : /* Cross device move -- try copy */
1078 /* NOTE: with open file it's an error because after the copy we will
1079 * get two files, it's fixable for our process (eg reopen the new file, get the
1080 * locks, and so on. But it doesn't solve the case with a second process
1082 if (adp->ad_open_forks) {
1083 /* FIXME warning in syslog so admin'd know there's a conflict ?*/
1084 return AFPERR_OLOCK; /* little lie */
1086 if (AFP_OK != ( rc = copyfile(vol, vol, sdir_fd, src, dst, newname, NULL )) ) {
1087 /* on error copyfile delete dest */
1090 return deletefile(vol, sdir_fd, src, 0);
1092 return( AFPERR_PARAM );
1096 if (vol->vfs->vfs_renamefile(vol, sdir_fd, src, dst) < 0 ) {
1100 /* try to undo the data fork rename,
1101 * we know we are on the same device
1104 unix_rename(-1, dst, sdir_fd, src );
1105 /* return the first error */
1108 return AFPERR_NOOBJ;
1111 return AFPERR_ACCESS ;
1113 return AFPERR_VLOCK;
1115 return AFPERR_PARAM ;
1120 /* don't care if we can't open the newly renamed ressource fork */
1121 if (ad_open(adp, dst, ADFLAGS_HF | ADFLAGS_RDWR) == 0) {
1122 ad_setname(adp, newname);
1124 ad_close( adp, ADFLAGS_HF );
1131 convert a Mac long name to an utf8 name,
1133 size_t mtoUTF8(const struct vol *vol, const char *src, size_t srclen, char *dest, size_t destlen)
1137 if ((size_t)-1 == (outlen = convert_string ( vol->v_maccharset, CH_UTF8_MAC, src, srclen, dest, destlen)) ) {
1143 /* ---------------- */
1144 int copy_path_name(const struct vol *vol, char *newname, char *ibuf)
1151 if ( type != 2 && !(vol->v_obj->afp_version >= 30 && type == 3) ) {
1157 if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
1158 if (vol->v_obj->afp_version >= 30) {
1159 /* convert it to UTF8
1161 if ((plen = mtoUTF8(vol, ibuf, plen, newname, AFPOBJ_TMPSIZ)) == (size_t)-1)
1165 strncpy( newname, ibuf, plen );
1166 newname[ plen ] = '\0';
1168 if (strlen(newname) != plen) {
1169 /* there's \0 in newname, e.g. it's a pathname not
1177 memcpy(&hint, ibuf, sizeof(hint));
1178 ibuf += sizeof(hint);
1180 memcpy(&len16, ibuf, sizeof(len16));
1181 ibuf += sizeof(len16);
1182 plen = ntohs(len16);
1185 if (plen > AFPOBJ_TMPSIZ) {
1188 strncpy( newname, ibuf, plen );
1189 newname[ plen ] = '\0';
1190 if (strlen(newname) != plen) {
1199 /* -----------------------------------
1201 int afp_copyfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1203 struct vol *s_vol, *d_vol;
1205 char *newname, *p, *upath;
1206 struct path *s_path;
1207 uint32_t sdid, ddid;
1208 int err, retvalue = AFP_OK;
1209 uint16_t svid, dvid;
1211 struct adouble ad, *adp;
1217 memcpy(&svid, ibuf, sizeof( svid ));
1218 ibuf += sizeof( svid );
1219 if (NULL == ( s_vol = getvolbyvid( svid )) ) {
1220 return( AFPERR_PARAM );
1223 memcpy(&sdid, ibuf, sizeof( sdid ));
1224 ibuf += sizeof( sdid );
1225 if (NULL == ( dir = dirlookup( s_vol, sdid )) ) {
1229 memcpy(&dvid, ibuf, sizeof( dvid ));
1230 ibuf += sizeof( dvid );
1231 memcpy(&ddid, ibuf, sizeof( ddid ));
1232 ibuf += sizeof( ddid );
1234 if (NULL == ( s_path = cname( s_vol, dir, &ibuf )) ) {
1235 return get_afp_errno(AFPERR_PARAM);
1237 if ( path_isadir(s_path) ) {
1238 return( AFPERR_BADTYPE );
1241 /* don't allow copies when the file is open.
1242 * XXX: the spec only calls for read/deny write access.
1243 * however, copyfile doesn't have any of that info,
1244 * and locks need to stay coherent. as a result,
1245 * we just balk if the file is opened already. */
1247 adp = of_ad(s_vol, s_path, &ad);
1249 if (ad_open(adp, s_path->u_name, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | ADFLAGS_RDONLY | ADFLAGS_SETSHRMD) < 0) {
1250 return AFPERR_DENYCONF;
1252 denyreadset = (ad_testlock(adp, ADEID_DFORK, AD_FILELOCK_DENY_RD) != 0 ||
1253 ad_testlock(adp, ADEID_RFORK, AD_FILELOCK_DENY_RD) != 0 );
1256 retvalue = AFPERR_DENYCONF;
1260 newname = obj->newtmp;
1261 strcpy( newname, s_path->m_name );
1263 p = ctoupath( s_vol, curdir, newname );
1265 retvalue = AFPERR_PARAM;
1269 if (NULL == ( d_vol = getvolbyvid( dvid )) ) {
1270 retvalue = AFPERR_PARAM;
1274 if (d_vol->v_flags & AFPVOL_RO) {
1275 retvalue = AFPERR_VLOCK;
1279 if (NULL == ( dir = dirlookup( d_vol, ddid )) ) {
1280 retvalue = afp_errno;
1284 if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) {
1285 retvalue = get_afp_errno(AFPERR_NOOBJ);
1289 if ( *s_path->m_name != '\0' ) {
1290 retvalue =path_error(s_path, AFPERR_NOOBJ);
1294 /* one of the handful of places that knows about the path type */
1295 if (copy_path_name(d_vol, newname, ibuf) < 0) {
1296 retvalue = AFPERR_PARAM;
1299 /* newname is always only a filename so curdir *is* its
1302 if (NULL == (upath = mtoupath(d_vol, newname, curdir->d_did, utf8_encoding(d_vol->v_obj)))) {
1303 retvalue =AFPERR_PARAM;
1307 if ( (err = copyfile(s_vol, d_vol, -1, p, upath , newname, adp)) < 0 ) {
1313 setvoltime(obj, d_vol );
1316 ad_close( adp, ADFLAGS_DF |ADFLAGS_HF | ADFLAGS_SETSHRMD);
1320 /* ----------------------------------
1321 * if newname is NULL (from directory.c) we don't want to copy the resource fork.
1322 * because we are doing it elsewhere.
1323 * currently if newname is NULL then adp is NULL.
1325 int copyfile(const struct vol *s_vol,
1326 const struct vol *d_vol,
1331 struct adouble *adp)
1333 struct adouble ads, add;
1340 LOG(log_debug, logtype_afpd, "copyfile(sfd:%d,s:'%s',d:'%s',n:'%s')",
1341 sfd, src, dst, newname);
1344 ad_init(&ads, s_vol);
1348 adflags = ADFLAGS_DF | ADFLAGS_RF | ADFLAGS_NORF;
1350 if (ad_openat(adp, sfd, src, adflags | ADFLAGS_RDONLY) < 0) {
1355 if (!AD_RSRC_OPEN(adp))
1356 /* no resource fork, don't create one for dst file */
1357 adflags &= ~ADFLAGS_RF;
1359 stat_result = fstat(ad_data_fileno(adp), &st); /* saving stat exit code, thus saving us on one more stat later on */
1361 if (stat_result < 0) {
1362 /* unlikely but if fstat fails, the default file mode will be 0666. */
1363 st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
1366 ad_init(&add, d_vol);
1367 if (ad_open(&add, dst, adflags | ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_EXCL, st.st_mode | S_IRUSR | S_IWUSR) < 0) {
1369 ad_close( adp, adflags );
1370 if (EEXIST != ret_err) {
1371 deletefile(d_vol, -1, dst, 0);
1374 return AFPERR_EXIST;
1377 if ((err = copy_fork(ADEID_DFORK, &add, adp)) != 0)
1378 LOG(log_error, logtype_afpd, "copyfile('%s'): %s", src, strerror(errno));
1381 if ((err = d_vol->vfs->vfs_copyfile(d_vol, sfd, src, dst)) != 0)
1382 LOG(log_error, logtype_afpd, "copyfile('%s'): %s", src, strerror(errno));
1387 if (!ret_err && newname && (adflags & ADFLAGS_HF)) {
1388 /* set the new name in the resource fork */
1389 ad_copy_header(&add, adp);
1390 ad_setname(&add, newname);
1393 ad_close( adp, adflags );
1395 if (ad_close( &add, adflags ) <0) {
1400 deletefile(d_vol, -1, dst, 0);
1402 else if (stat_result == 0) {
1403 /* set dest modification date to src date */
1406 ut.actime = ut.modtime = st.st_mtime;
1408 /* FIXME netatalk doesn't use resource fork file date
1409 * but maybe we should set its modtime too.
1414 switch ( ret_err ) {
1420 return AFPERR_DFULL;
1422 return AFPERR_NOOBJ;
1424 return AFPERR_ACCESS;
1426 return AFPERR_VLOCK;
1428 return AFPERR_PARAM;
1432 /* -----------------------------------
1433 vol: not NULL delete cnid entry. then we are in curdir and file is a only filename
1434 checkAttrib: 1 check kFPDeleteInhibitBit (deletfile called by afp_delete)
1436 when deletefile is called we don't have lock on it, file is closed (for us)
1437 untrue if called by renamefile
1439 ad_open always try to open file RDWR first and ad_lock takes care of
1440 WRITE lock on read only file.
1443 static int check_attrib(struct adouble *adp)
1445 uint16_t bshort = 0;
1447 ad_getattr(adp, &bshort);
1449 * Does kFPDeleteInhibitBit (bit 8) set?
1451 if ((bshort & htons(ATTRBIT_NODELETE))) {
1452 return AFPERR_OLOCK;
1454 if ((bshort & htons(ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
1460 * dirfd can be used for unlinkat semantics
1462 int deletefile(const struct vol *vol, int dirfd, char *file, int checkAttrib)
1465 struct adouble *adp = NULL;
1466 int adflags, err = AFP_OK;
1469 LOG(log_debug, logtype_afpd, "deletefile('%s')", file);
1473 /* was EACCESS error try to get only metadata */
1474 /* we never want to create a resource fork here, we are going to delete it
1475 * moreover sometimes deletefile is called with a no existent file and
1476 * ad_open would create a 0 byte resource fork
1478 if ( ad_metadataat(dirfd, file, ADFLAGS_CHECK_OF, &ad) == 0 ) {
1479 if ((err = check_attrib(&ad))) {
1480 ad_close(&ad, ADFLAGS_HF | ADFLAGS_CHECK_OF);
1487 /* try to open both forks at once */
1488 adflags = ADFLAGS_DF;
1489 if (ad_openat(&ad, dirfd, file, adflags | ADFLAGS_RF | ADFLAGS_NORF | ADFLAGS_RDONLY) < 0 ) {
1494 case EACCES: /* maybe it's a file with no write mode for us */
1495 break; /* was return AFPERR_ACCESS;*/
1508 if ( adp && AD_RSRC_OPEN(adp) != -1 ) { /* there's a resource fork */
1509 adflags |= ADFLAGS_RF;
1510 /* FIXME we have a pb here because we want to know if a file is open
1511 * there's a 'priority inversion' if you can't open the ressource fork RW
1512 * you can delete it if it's open because you can't get a write lock.
1514 * ADLOCK_FILELOCK means the whole ressource fork, not only after the
1517 * FIXME it doesn't work for RFORK open read only and fork open without deny mode
1519 if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
1525 if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
1526 LOG(log_error, logtype_afpd, "deletefile('%s'): ad_tmplock error: %s", file, strerror(errno));
1528 } else if (!(err = vol->vfs->vfs_deletefile(vol, dirfd, file)) && !(err = netatalk_unlinkat(dirfd, file )) ) {
1530 if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file)))) {
1531 cnid_delete(vol->v_cdb, id);
1537 ad_close(&ad, ADFLAGS_HF | ADFLAGS_CHECK_OF);
1540 ad_close( &ad, adflags ); /* ad_close removes locks if any */
1545 /* ------------------------------------ */
1546 /* return a file id */
1547 int afp_createid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1556 struct path *s_path;
1562 memcpy(&vid, ibuf, sizeof(vid));
1563 ibuf += sizeof(vid);
1565 if (NULL == ( vol = getvolbyvid( vid )) ) {
1566 return( AFPERR_PARAM);
1569 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1573 if (vol->v_flags & AFPVOL_RO)
1574 return AFPERR_VLOCK;
1576 memcpy(&did, ibuf, sizeof( did ));
1577 ibuf += sizeof(did);
1579 if (NULL == ( dir = dirlookup( vol, did )) ) {
1580 return afp_errno; /* was AFPERR_PARAM */
1583 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
1584 return get_afp_errno(AFPERR_NOOBJ); /* was AFPERR_PARAM */
1587 if ( path_isadir(s_path) ) {
1588 return( AFPERR_BADTYPE );
1591 upath = s_path->u_name;
1592 switch (s_path->st_errno) {
1594 break; /* success */
1597 return AFPERR_ACCESS;
1599 return AFPERR_NOOBJ;
1601 return AFPERR_PARAM;
1604 if ((id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath)))) {
1605 memcpy(rbuf, &id, sizeof(id));
1606 *rbuflen = sizeof(id);
1607 return AFPERR_EXISTID;
1610 if ((id = get_id(vol, NULL, st, did, upath, len)) != CNID_INVALID) {
1611 memcpy(rbuf, &id, sizeof(id));
1612 *rbuflen = sizeof(id);
1619 /* ------------------------------- */
1625 static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
1628 struct reenum *param = data;
1629 struct vol *vol = param->vol;
1630 cnid_t did = param->did;
1633 if ( lstat(de->d_name, &path.st) < 0 )
1636 /* update or add to cnid */
1637 aint = cnid_add(vol->v_cdb, &path.st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */
1642 /* --------------------
1643 * Ok the db is out of synch with the dir.
1644 * but if it's a deleted file we don't want to do it again and again.
1647 reenumerate_id(struct vol *vol, char *name, struct dir *dir)
1653 if (vol->v_cdb == NULL) {
1657 /* FIXME use of_statdir ? */
1658 if (lstat(name, &st)) {
1662 if (dirreenumerate(dir, &st)) {
1663 /* we already did it once and the dir haven't been modified */
1664 return dir->d_offcnt;
1668 data.did = dir->d_did;
1669 if ((ret = for_each_dirent(vol, name, reenumerate_loop, (void *)&data)) >= 0) {
1670 setdiroffcnt(curdir, &st, ret);
1671 dir->d_flags |= DIRF_CNID;
1677 /* ------------------------------
1678 resolve a file id */
1679 int afp_resolveid(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1688 uint16_t vid, bitmap;
1690 static char buffer[12 + MAXPATHLEN + 1];
1691 int len = 12 + MAXPATHLEN + 1;
1696 memcpy(&vid, ibuf, sizeof(vid));
1697 ibuf += sizeof(vid);
1699 if (NULL == ( vol = getvolbyvid( vid )) ) {
1700 return( AFPERR_PARAM);
1703 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1707 memcpy(&id, ibuf, sizeof( id ));
1712 /* some MacOS versions after a catsearch do a *lot* of afp_resolveid with 0 */
1716 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1717 return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
1720 if (NULL == ( dir = dirlookup( vol, id )) ) {
1721 return AFPERR_NOID; /* idem AFPERR_PARAM */
1723 if (movecwd(vol, dir) < 0) {
1727 return AFPERR_ACCESS;
1731 return AFPERR_PARAM;
1735 memset(&path, 0, sizeof(path));
1736 path.u_name = upath;
1737 if ( of_stat(&path) < 0 ) {
1739 /* with nfs and our working directory is deleted */
1740 if (errno == ESTALE) {
1744 if ( errno == ENOENT && !retry) {
1745 /* cnid db is out of sync, reenumerate the directory and update ids */
1746 reenumerate_id(vol, ".", dir);
1754 return AFPERR_ACCESS;
1758 return AFPERR_PARAM;
1762 /* directories are bad */
1763 if (S_ISDIR(path.st.st_mode)) {
1764 /* OS9 and OSX don't return the same error code */
1765 return (obj->afp_version >=30)?AFPERR_NOID:AFPERR_BADTYPE;
1768 memcpy(&bitmap, ibuf, sizeof(bitmap));
1769 bitmap = ntohs( bitmap );
1770 if (NULL == (path.m_name = utompath(vol, upath, cnid, utf8_encoding(obj)))) {
1774 if (AFP_OK != (err = getfilparams(obj, vol, bitmap, &path , curdir,
1775 rbuf + sizeof(bitmap), &buflen))) {
1778 *rbuflen = buflen + sizeof(bitmap);
1779 memcpy(rbuf, ibuf, sizeof(bitmap));
1784 /* ------------------------------ */
1785 int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1795 static char buffer[12 + MAXPATHLEN + 1];
1796 int len = 12 + MAXPATHLEN + 1;
1801 memcpy(&vid, ibuf, sizeof(vid));
1802 ibuf += sizeof(vid);
1804 if (NULL == ( vol = getvolbyvid( vid )) ) {
1805 return( AFPERR_PARAM);
1808 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1812 if (vol->v_flags & AFPVOL_RO)
1813 return AFPERR_VLOCK;
1815 memcpy(&id, ibuf, sizeof( id ));
1819 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1823 if (NULL == ( dir = dirlookup( vol, id )) ) {
1824 if (afp_errno == AFPERR_NOOBJ) {
1828 return( AFPERR_PARAM );
1832 if ((movecwd(vol, dir) < 0) || (lstat(upath, &st) < 0)) {
1836 return AFPERR_ACCESS;
1841 /* still try to delete the id */
1845 return AFPERR_PARAM;
1848 else if (S_ISDIR(st.st_mode)) /* directories are bad */
1849 return AFPERR_BADTYPE;
1852 if (cnid_delete(vol->v_cdb, fileid)) {
1855 return AFPERR_VLOCK;
1858 return AFPERR_ACCESS;
1860 return AFPERR_PARAM;
1867 /* ------------------------------ */
1868 static struct adouble *find_adouble(const AFPObj *obj, struct vol *vol, struct path *path, struct ofork **of, struct adouble *adp)
1872 if (path->st_errno) {
1873 switch (path->st_errno) {
1875 afp_errno = AFPERR_NOID;
1879 afp_errno = AFPERR_ACCESS;
1882 afp_errno = AFPERR_PARAM;
1887 /* we use file_access both for legacy Mac perm and
1888 * for unix privilege, rename will take care of folder perms
1890 if (file_access(obj, vol, path, OPENACC_WR ) < 0) {
1891 afp_errno = AFPERR_ACCESS;
1895 if ((*of = of_findname(path))) {
1896 /* reuse struct adouble so it won't break locks */
1900 ret = ad_open(adp, path->u_name, ADFLAGS_HF | ADFLAGS_RDWR);
1902 if ( !ret && ad_reso_fileno(adp) != -1 && !(adp->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
1904 * The user must have the Read & Write privilege for both files in order to use this command.
1906 ad_close(adp, ADFLAGS_HF);
1907 afp_errno = AFPERR_ACCESS;
1914 #define APPLETEMP ".AppleTempXXXXXX"
1916 int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1918 struct stat srcst, destst;
1920 struct dir *dir, *sdir;
1921 char *spath, temp[17], *p;
1922 char *supath, *upath;
1927 struct adouble *adsp = NULL;
1928 struct adouble *addp = NULL;
1929 struct ofork *s_of = NULL;
1930 struct ofork *d_of = NULL;
1943 memcpy(&vid, ibuf, sizeof(vid));
1944 ibuf += sizeof(vid);
1946 if (NULL == ( vol = getvolbyvid( vid )) ) {
1947 return( AFPERR_PARAM);
1950 if ((vol->v_flags & AFPVOL_RO))
1951 return AFPERR_VLOCK;
1953 /* source and destination dids */
1954 memcpy(&sid, ibuf, sizeof(sid));
1955 ibuf += sizeof(sid);
1956 memcpy(&did, ibuf, sizeof(did));
1957 ibuf += sizeof(did);
1960 if (NULL == (dir = dirlookup( vol, sid )) ) {
1961 return afp_errno; /* was AFPERR_PARAM */
1964 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
1965 return get_afp_errno(AFPERR_NOOBJ);
1968 if ( path_isadir(path) ) {
1969 return AFPERR_BADTYPE; /* it's a dir */
1972 /* save some stuff */
1975 spath = obj->oldtmp;
1976 supath = obj->newtmp;
1977 strcpy(spath, path->m_name);
1978 strcpy(supath, path->u_name); /* this is for the cnid changing */
1979 p = absupath( vol, sdir, supath);
1981 /* pathname too long */
1982 return AFPERR_PARAM ;
1986 if (!(adsp = find_adouble(obj, vol, path, &s_of, &ads))) {
1990 /* ***** from here we may have resource fork open **** */
1992 /* look for the source cnid. if it doesn't exist, don't worry about
1994 sid = cnid_lookup(vol->v_cdb, &srcst, sdir->d_did, supath,slen = strlen(supath));
1996 if (NULL == ( dir = dirlookup( vol, did )) ) {
1997 err = afp_errno; /* was AFPERR_PARAM */
1998 goto err_exchangefile;
2001 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2002 err = get_afp_errno(AFPERR_NOOBJ);
2003 goto err_exchangefile;
2006 if ( path_isadir(path) ) {
2007 err = AFPERR_BADTYPE;
2008 goto err_exchangefile;
2011 /* FPExchangeFiles is the only call that can return the SameObj
2013 if ((curdir == sdir) && strcmp(spath, path->m_name) == 0) {
2014 err = AFPERR_SAMEOBJ;
2015 goto err_exchangefile;
2019 if (!(addp = find_adouble(obj, vol, path, &d_of, &add))) {
2021 goto err_exchangefile;
2025 /* they are not on the same device and at least one is open
2026 * FIXME broken for for crossdev and adouble v2
2029 crossdev = (srcst.st_dev != destst.st_dev);
2030 if (/* (d_of || s_of) && */ crossdev) {
2032 goto err_exchangefile;
2035 /* look for destination id. */
2036 upath = path->u_name;
2037 did = cnid_lookup(vol->v_cdb, &destst, curdir->d_did, upath, dlen = strlen(upath));
2039 /* construct a temp name.
2040 * NOTE: the temp file will be in the dest file's directory. it
2041 * will also be inaccessible from AFP. */
2042 memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
2043 if (!mktemp(temp)) {
2045 goto err_exchangefile;
2049 /* FIXME we need to close fork for copy, both s_of and d_of are null */
2050 ad_close(adsp, ADFLAGS_HF);
2051 ad_close(addp, ADFLAGS_HF);
2054 /* now, quickly rename the file. we error if we can't. */
2055 if ((err = renamefile(vol, -1, p, temp, temp, adsp)) != AFP_OK)
2056 goto err_exchangefile;
2057 of_rename(vol, s_of, sdir, spath, curdir, temp);
2059 /* rename destination to source */
2060 if ((err = renamefile(vol, -1, upath, p, spath, addp)) != AFP_OK)
2061 goto err_src_to_tmp;
2062 of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
2064 /* rename temp to destination */
2065 if ((err = renamefile(vol, -1, temp, upath, path->m_name, adsp)) != AFP_OK)
2066 goto err_dest_to_src;
2067 of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
2069 /* id's need switching. src -> dest and dest -> src.
2070 * we need to re-stat() if it was a cross device copy.
2073 cnid_delete(vol->v_cdb, sid);
2075 cnid_delete(vol->v_cdb, did);
2077 if ((did && ( (crossdev && lstat( upath, &srcst) < 0) ||
2078 cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
2080 (sid && ( (crossdev && lstat(p, &destst) < 0) ||
2081 cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
2086 err = AFPERR_ACCESS;
2091 goto err_temp_to_dest;
2094 /* here we need to reopen if crossdev */
2095 if (sid && ad_setid(addp, destst.st_dev, destst.st_ino, sid, sdir->d_did, vol->v_stamp))
2100 if (did && ad_setid(adsp, srcst.st_dev, srcst.st_ino, did, curdir->d_did, vol->v_stamp))
2105 /* change perms, src gets dest perm and vice versa */
2110 LOG(log_error, logtype_afpd, "seteuid failed %s", strerror(errno));
2111 err = AFP_OK; /* ignore error */
2112 goto err_temp_to_dest;
2116 * we need to exchange ACL entries as well
2118 /* exchange_acls(vol, p, upath); */
2123 path->m_name = NULL;
2124 path->u_name = upath;
2126 setfilunixmode(vol, path, destst.st_mode);
2127 setfilowner(vol, destst.st_uid, destst.st_gid, path);
2134 setfilunixmode(vol, path, srcst.st_mode);
2135 setfilowner(vol, srcst.st_uid, srcst.st_gid, path);
2137 if ( setegid(gid) < 0 || seteuid(uid) < 0) {
2138 LOG(log_error, logtype_afpd, "can't seteuid back %s", strerror(errno));
2143 goto err_exchangefile;
2145 /* all this stuff is so that we can unwind a failed operation
2148 /* rename dest to temp */
2149 renamefile(vol, -1, upath, temp, temp, adsp);
2150 of_rename(vol, s_of, curdir, upath, curdir, temp);
2153 /* rename source back to dest */
2154 renamefile(vol, -1, p, upath, path->m_name, addp);
2155 of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
2158 /* rename temp back to source */
2159 renamefile(vol, -1, temp, p, spath, adsp);
2160 of_rename(vol, s_of, curdir, temp, sdir, spath);
2163 if ( !s_of && adsp && ad_meta_fileno(adsp) != -1 ) { /* META */
2164 ad_close(adsp, ADFLAGS_HF);
2166 if ( !d_of && addp && ad_meta_fileno(addp) != -1 ) {/* META */
2167 ad_close(addp, ADFLAGS_HF);
2171 if ((cached = dircache_search_by_did(vol, sid)) != NULL)
2172 (void)dir_remove(vol, cached);
2173 if ((cached = dircache_search_by_did(vol, did)) != NULL)
2174 (void)dir_remove(vol, cached);