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 && (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)) {
267 if (ad_flush(adp) != 0)
268 LOG(log_error, logtype_afpd, "get_id(\"%s\"): can't flush", fullpathname(upath));
278 /* -------------------------- */
279 int getmetadata(const AFPObj *obj,
282 struct path *path, struct dir *dir,
283 char *buf, size_t *buflen, struct adouble *adp)
285 char *data, *l_nameoff = NULL, *upath;
286 char *utf_nameoff = NULL;
291 u_char achar, fdType[4];
296 LOG(log_debug, logtype_afpd, "getmetadata(\"%s\")", path->u_name);
298 upath = path->u_name;
302 if ( ((bitmap & ( (1 << FILPBIT_FINFO)|(1 << FILPBIT_LNAME)|(1 <<FILPBIT_PDINFO) ) ) && !path->m_name)
303 || (bitmap & ( (1 << FILPBIT_LNAME) ) && utf8_encoding(obj)) /* FIXME should be m_name utf8 filename */
304 || (bitmap & (1 << FILPBIT_FNUM))) {
307 struct dir *cachedfile;
308 int len = strlen(upath);
309 if ((cachedfile = dircache_search_by_name(vol, dir, upath, len)) != NULL)
310 id = cachedfile->d_did;
312 id = get_id(vol, adp, st, dir->d_did, upath, len);
314 /* Add it to the cache */
315 LOG(log_debug, logtype_afpd, "getmetadata: caching: did:%u, \"%s\", cnid:%u",
316 ntohl(dir->d_did), upath, ntohl(id));
318 /* Get macname from unixname first */
319 if (path->m_name == NULL) {
320 if ((path->m_name = utompath(vol, upath, id, utf8_encoding(obj))) == NULL) {
321 LOG(log_error, logtype_afpd, "getmetadata: utompath error");
327 if (((fullpath = bstrcpy(dir->d_fullpath)) == NULL)
328 || (bconchar(fullpath, '/') != BSTR_OK)
329 || (bcatcstr(fullpath, upath)) != BSTR_OK) {
330 LOG(log_error, logtype_afpd, "getmetadata: fullpath: %s", strerror(errno));
334 if ((cachedfile = dir_new(path->m_name, upath, vol, dir->d_did, id, fullpath, st)) == NULL) {
335 LOG(log_error, logtype_afpd, "getmetadata: error from dir_new");
339 if ((dircache_add(vol, cachedfile)) != 0) {
340 LOG(log_error, logtype_afpd, "getmetadata: fatal dircache error");
348 if (id == CNID_INVALID)
352 path->m_name = utompath(vol, upath, id, utf8_encoding(vol->v_obj));
355 while ( bitmap != 0 ) {
356 while (( bitmap & 1 ) == 0 ) {
364 ad_getattr(adp, &ashort);
365 } else if (vol_inv_dots(vol) && *upath == '.') {
366 ashort = htons(ATTRBIT_INVISIBLE);
370 /* FIXME do we want a visual clue if the file is read only
373 accessmode(vol, ".", &ma, dir , NULL);
374 if ((ma.ma_user & AR_UWRITE)) {
375 accessmode(vol, upath, &ma, dir , st);
376 if (!(ma.ma_user & AR_UWRITE)) {
377 ashort |= htons(ATTRBIT_NOWRITE);
381 memcpy(data, &ashort, sizeof( ashort ));
382 data += sizeof( ashort );
383 LOG(log_debug, logtype_afpd, "metadata('%s'): AFP Attributes: %04x",
384 path->u_name, ntohs(ashort));
388 memcpy(data, &dir->d_did, sizeof( uint32_t ));
389 data += sizeof( uint32_t );
390 LOG(log_debug, logtype_afpd, "metadata('%s'): Parent DID: %u",
391 path->u_name, ntohl(dir->d_did));
395 if (!adp || (ad_getdate(adp, AD_DATE_CREATE, &aint) < 0))
396 aint = AD_DATE_FROM_UNIX(st->st_mtime);
397 memcpy(data, &aint, sizeof( aint ));
398 data += sizeof( aint );
402 if ( adp && (ad_getdate(adp, AD_DATE_MODIFY, &aint) == 0)) {
403 if ((st->st_mtime > AD_DATE_TO_UNIX(aint))) {
404 aint = AD_DATE_FROM_UNIX(st->st_mtime);
407 aint = AD_DATE_FROM_UNIX(st->st_mtime);
409 memcpy(data, &aint, sizeof( int ));
410 data += sizeof( int );
414 if (!adp || (ad_getdate(adp, AD_DATE_BACKUP, &aint) < 0))
415 aint = AD_DATE_START;
416 memcpy(data, &aint, sizeof( int ));
417 data += sizeof( int );
421 get_finderinfo(vol, upath, adp, (char *)data,S_ISLNK(st->st_mode));
422 data += ADEDLEN_FINDERI;
427 data += sizeof( uint16_t );
431 memset(data, 0, sizeof(uint16_t));
432 data += sizeof( uint16_t );
436 memcpy(data, &id, sizeof( id ));
437 data += sizeof( id );
438 LOG(log_debug, logtype_afpd, "metadata('%s'): CNID: %u",
439 path->u_name, ntohl(id));
443 if (st->st_size > 0xffffffff)
446 aint = htonl( st->st_size );
447 memcpy(data, &aint, sizeof( aint ));
448 data += sizeof( aint );
453 if (adp->ad_rlen > 0xffffffff)
456 aint = htonl( adp->ad_rlen);
460 memcpy(data, &aint, sizeof( aint ));
461 data += sizeof( aint );
464 /* Current client needs ProDOS info block for this file.
465 Use simple heuristic and let the Mac "type" string tell
466 us what the PD file code should be. Everything gets a
467 subtype of 0x0000 unless the original value was hashed
468 to "pXYZ" when we created it. See IA, Ver 2.
469 <shirsch@adelphia.net> */
470 case FILPBIT_PDINFO :
471 if (obj->afp_version >= 30) { /* UTF8 name */
472 utf8 = kTextEncodingUTF8;
474 data += sizeof( uint16_t );
476 memcpy(data, &aint, sizeof( aint ));
477 data += sizeof( aint );
481 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
483 if ( memcmp( fdType, "TEXT", 4 ) == 0 ) {
487 else if ( memcmp( fdType, "PSYS", 4 ) == 0 ) {
491 else if ( memcmp( fdType, "PS16", 4 ) == 0 ) {
495 else if ( memcmp( fdType, "BINA", 4 ) == 0 ) {
499 else if ( fdType[0] == 'p' ) {
501 ashort = (fdType[2] * 256) + fdType[3];
515 memcpy(data, &ashort, sizeof( ashort ));
516 data += sizeof( ashort );
517 memset(data, 0, sizeof( ashort ));
518 data += sizeof( ashort );
521 case FILPBIT_EXTDFLEN:
522 aint = htonl(st->st_size >> 32);
523 memcpy(data, &aint, sizeof( aint ));
524 data += sizeof( aint );
525 aint = htonl(st->st_size);
526 memcpy(data, &aint, sizeof( aint ));
527 data += sizeof( aint );
529 case FILPBIT_EXTRFLEN:
532 aint = htonl(adp->ad_rlen >> 32);
533 memcpy(data, &aint, sizeof( aint ));
534 data += sizeof( aint );
536 aint = htonl(adp->ad_rlen);
537 memcpy(data, &aint, sizeof( aint ));
538 data += sizeof( aint );
540 case FILPBIT_UNIXPR :
541 /* accessmode may change st_mode with ACLs */
542 accessmode(obj, vol, upath, &ma, dir , st);
544 aint = htonl(st->st_uid);
545 memcpy( data, &aint, sizeof( aint ));
546 data += sizeof( aint );
547 aint = htonl(st->st_gid);
548 memcpy( data, &aint, sizeof( aint ));
549 data += sizeof( aint );
552 type == slnk indicates an OSX style symlink,
553 we have to add S_IFLNK to the mode, otherwise
554 10.3 clients freak out. */
558 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
559 if ( memcmp( fdType, "slnk", 4 ) == 0 ) {
565 memcpy( data, &aint, sizeof( aint ));
566 data += sizeof( aint );
568 *data++ = ma.ma_user;
569 *data++ = ma.ma_world;
570 *data++ = ma.ma_group;
571 *data++ = ma.ma_owner;
575 return( AFPERR_BITMAP );
581 ashort = htons( data - buf );
582 memcpy(l_nameoff, &ashort, sizeof( ashort ));
583 data = set_name(vol, data, dir->d_did, path->m_name, id, 0);
586 ashort = htons( data - buf );
587 memcpy(utf_nameoff, &ashort, sizeof( ashort ));
588 data = set_name(vol, data, dir->d_did, path->m_name, id, utf8);
590 *buflen = data - buf;
594 /* ----------------------- */
595 int getfilparams(const AFPObj *obj,
598 struct path *path, struct dir *dir,
599 char *buf, size_t *buflen )
601 struct adouble ad, *adp;
606 LOG(log_debug, logtype_afpd, "getfilparams(\"%s\")", path->u_name);
608 opened = PARAM_NEED_ADP(bitmap);
613 flags = (bitmap & (1 << FILPBIT_ATTR)) ? ADFLAGS_CHECK_OF : 0;
615 adp = of_ad(vol, path, &ad);
616 upath = path->u_name;
618 if ( ad_metadata( upath, flags, adp) < 0 ) {
621 LOG(log_error, logtype_afpd, "getfilparams(%s): %s: check resource fork permission?",
622 upath, strerror(errno));
623 return AFPERR_ACCESS;
625 LOG(log_error, logtype_afpd, "getfilparams(%s): bad resource fork", upath);
634 rc = getmetadata(obj, vol, bitmap, path, dir, buf, buflen, adp);
635 ad_close(adp, ADFLAGS_HF | flags);
640 /* ----------------------------- */
641 int afp_createfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
646 struct ofork *of = NULL;
648 int creatf, did, openf, retvalue = AFP_OK;
654 creatf = (unsigned char) *ibuf++;
656 memcpy(&vid, ibuf, sizeof( vid ));
657 ibuf += sizeof( vid );
659 if (NULL == ( vol = getvolbyvid( vid )) )
660 return( AFPERR_PARAM );
662 if (vol->v_flags & AFPVOL_RO)
665 memcpy(&did, ibuf, sizeof( did));
666 ibuf += sizeof( did );
668 if (NULL == ( dir = dirlookup( vol, did )) )
671 if (NULL == ( s_path = cname( vol, dir, &ibuf )) )
672 return get_afp_errno(AFPERR_PARAM);
673 if ( *s_path->m_name == '\0' )
674 return( AFPERR_BADTYPE );
676 upath = s_path->u_name;
679 /* if upath is deleted we already in trouble anyway */
680 if ((of = of_findname(s_path))) {
688 openf = ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_TRUNC;
690 /* on a soft create, if the file is open then ad_open won't fail
691 because open syscall is not called */
692 openf = ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_EXCL;
694 if (ad_open(&ad, upath, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | openf, 0666) < 0) {
698 case ENOENT : /* we were already in 'did folder' so chdir() didn't fail */
699 return ( AFPERR_NOOBJ );
701 return( AFPERR_EXIST );
703 return( AFPERR_ACCESS );
706 return( AFPERR_DFULL );
708 return( AFPERR_PARAM );
711 if ( ad_meta_fileno( &ad ) == -1 ) { /* Hard META / HF */
712 /* FIXME with hard create on an existing file, we already
713 * corrupted the data file.
715 netatalk_unlink( upath );
716 ad_close( &ad, ADFLAGS_DF );
717 return AFPERR_ACCESS;
720 path = s_path->m_name;
721 ad_setname(&ad, path);
724 if (lstat(upath, &st) != 0) {
725 LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): stat: %s",
726 upath, strerror(errno));
727 ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF);
731 (void)get_id(vol, &ad, &st, dir->d_did, upath, strlen(upath));
734 ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF );
735 fce_register_new_file(s_path);
740 setvoltime(obj, vol );
745 int afp_setfilparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
751 uint16_t vid, bitmap;
756 memcpy(&vid, ibuf, sizeof( vid ));
757 ibuf += sizeof( vid );
758 if (NULL == ( vol = getvolbyvid( vid )) ) {
759 return( AFPERR_PARAM );
762 if (vol->v_flags & AFPVOL_RO)
765 memcpy(&did, ibuf, sizeof( did ));
766 ibuf += sizeof( did );
767 if (NULL == ( dir = dirlookup( vol, did )) ) {
768 return afp_errno; /* was AFPERR_NOOBJ */
771 memcpy(&bitmap, ibuf, sizeof( bitmap ));
772 bitmap = ntohs( bitmap );
773 ibuf += sizeof( bitmap );
775 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
776 return get_afp_errno(AFPERR_PARAM);
779 if (path_isadir(s_path)) {
780 return( AFPERR_BADTYPE ); /* it's a directory */
783 if ( s_path->st_errno != 0 ) {
784 return( AFPERR_NOOBJ );
787 if ((u_long)ibuf & 1 ) {
791 if (AFP_OK == ( rc = setfilparams(obj, vol, s_path, bitmap, ibuf )) ) {
792 setvoltime(obj, vol );
799 * cf AFP3.0.pdf page 252 for change_mdate and change_parent_mdate logic
802 extern struct path Cur_Path;
804 int setfilparams(const AFPObj *obj, struct vol *vol,
805 struct path *path, uint16_t f_bitmap, char *buf )
807 struct adouble ad, *adp;
809 int bit, isad = 1, err = AFP_OK;
811 u_char achar, *fdType, xyy[4]; /* uninitialized, OK 310105 */
812 uint16_t ashort, bshort, oshort;
815 uint16_t upriv_bit = 0;
817 int change_mdate = 0;
818 int change_parent_mdate = 0;
823 uint16_t bitmap = f_bitmap;
824 uint32_t cdate,bdate;
825 u_char finder_buf[32];
829 LOG(log_debug9, logtype_afpd, "begin setfilparams:");
832 adp = of_ad(vol, path, &ad);
833 upath = path->u_name;
835 if (!vol_unix_priv(vol) && check_access(obj, vol, upath, OPENACC_WR ) < 0) {
836 return AFPERR_ACCESS;
839 /* with unix priv maybe we have to change adouble file priv first */
841 while ( bitmap != 0 ) {
842 while (( bitmap & 1 ) == 0 ) {
849 memcpy(&ashort, buf, sizeof( ashort ));
850 buf += sizeof( ashort );
854 memcpy(&cdate, buf, sizeof(cdate));
855 buf += sizeof( cdate );
858 memcpy(&newdate, buf, sizeof( newdate ));
859 buf += sizeof( newdate );
863 memcpy(&bdate, buf, sizeof( bdate));
864 buf += sizeof( bdate );
868 memcpy(finder_buf, buf, 32 );
869 if (memcmp(buf, "slnkrhap", 8) == 0 && !S_ISLNK(path->st.st_mode)) {
873 char buf[PATH_MAX+1];
874 if ((fp = open(path->u_name, O_RDONLY)) >= 0) {
875 if ((len = read(fp, buf, PATH_MAX+1))) {
876 if (unlink(path->u_name) == 0) {
878 erc = symlink(buf, path->u_name);
887 goto setfilparam_done;
893 case FILPBIT_UNIXPR :
894 if (!vol_unix_priv(vol)) {
895 /* this volume doesn't use unix priv */
901 change_parent_mdate = 1;
903 memcpy( &aint, buf, sizeof( aint ));
904 f_uid = ntohl (aint);
905 buf += sizeof( aint );
906 memcpy( &aint, buf, sizeof( aint ));
907 f_gid = ntohl (aint);
908 buf += sizeof( aint );
909 setfilowner(vol, f_uid, f_gid, path);
911 memcpy( &upriv, buf, sizeof( upriv ));
912 buf += sizeof( upriv );
913 upriv = ntohl (upriv);
914 if ((upriv & S_IWUSR)) {
915 setfilunixmode(vol, path, upriv);
922 case FILPBIT_PDINFO :
923 if (obj->afp_version < 30) { /* else it's UTF8 name */
926 /* Keep special case to support crlf translations */
927 if ((unsigned int) achar == 0x04) {
928 fdType = (u_char *)"TEXT";
931 xyy[0] = ( u_char ) 'p';
942 /* break while loop */
951 /* second try with adouble open
953 if (ad_open(adp, upath, ADFLAGS_HF | ADFLAGS_RDWR | ADFLAGS_CREATE, 0666) < 0) {
954 LOG(log_debug, logtype_afpd, "setfilparams: ad_open_metadata error");
956 * For some things, we don't need an adouble header:
957 * - change of modification date
958 * - UNIX privs (Bug-ID #2863424)
960 if (!symlinked && f_bitmap & ~(1<<FILPBIT_MDATE | 1<<FILPBIT_UNIXPR)) {
961 LOG(log_debug, logtype_afpd, "setfilparams: need adouble access");
962 return AFPERR_ACCESS;
964 LOG(log_debug, logtype_afpd, "setfilparams: no adouble perms, but only FILPBIT_MDATE and/or FILPBIT_UNIXPR");
966 } else if ((ad_get_MD_flags( adp ) & O_CREAT) ) {
967 ad_setname(adp, path->m_name);
972 while ( bitmap != 0 ) {
973 while (( bitmap & 1 ) == 0 ) {
980 ad_getattr(adp, &bshort);
982 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
983 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
987 if ((bshort & htons(ATTRBIT_INVISIBLE)) != (oshort & htons(ATTRBIT_INVISIBLE)))
988 change_parent_mdate = 1;
989 ad_setattr(adp, bshort);
992 ad_setdate(adp, AD_DATE_CREATE, cdate);
997 ad_setdate(adp, AD_DATE_BACKUP, bdate);
1000 memcpy(ad_entry( adp, ADEID_FINDERI ), finder_buf, 32 );
1002 case FILPBIT_UNIXPR :
1004 setfilunixmode(vol, path, upriv);
1007 case FILPBIT_PDINFO :
1008 if (obj->afp_version < 30) { /* else it's UTF8 name */
1009 memcpy(ad_entry( adp, ADEID_FINDERI ), fdType, 4 );
1010 memcpy(ad_entry( adp, ADEID_FINDERI ) + 4, "pdos", 4 );
1015 err = AFPERR_BITMAP;
1016 goto setfilparam_done;
1023 if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
1024 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1028 ad_setdate(adp, AD_DATE_MODIFY, newdate);
1029 ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
1035 ad_close(adp, ADFLAGS_HF);
1038 if (change_parent_mdate && gettimeofday(&tv, NULL) == 0) {
1039 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1040 bitmap = 1<<FILPBIT_MDATE;
1041 setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
1045 LOG(log_debug9, logtype_afpd, "end setfilparams:");
1051 * renamefile and copyfile take the old and new unix pathnames
1052 * and the new mac name.
1054 * sdir_fd source dir fd to which src path is relative (for openat et al semantics)
1055 * passing -1 means this is not used, src path is a full path
1056 * src the source path
1057 * dst the dest filename in current dir
1058 * newname the dest mac name
1059 * adp adouble struct of src file, if open, or & zeroed one
1062 int renamefile(const struct vol *vol, int sdir_fd, char *src, char *dst, char *newname, struct adouble *adp)
1066 LOG(log_debug, logtype_afpd,
1067 "renamefile: src[%d, \"%s\"] -> dst[\"%s\"]", sdir_fd, src, dst);
1069 if ( unix_rename( sdir_fd, src, -1, dst ) < 0 ) {
1072 return( AFPERR_NOOBJ );
1075 return( AFPERR_ACCESS );
1077 return AFPERR_VLOCK;
1078 case EXDEV : /* Cross device move -- try copy */
1079 /* NOTE: with open file it's an error because after the copy we will
1080 * get two files, it's fixable for our process (eg reopen the new file, get the
1081 * locks, and so on. But it doesn't solve the case with a second process
1083 if (adp->ad_open_forks) {
1084 /* FIXME warning in syslog so admin'd know there's a conflict ?*/
1085 return AFPERR_OLOCK; /* little lie */
1087 if (AFP_OK != ( rc = copyfile(vol, vol, sdir_fd, src, dst, newname, NULL )) ) {
1088 /* on error copyfile delete dest */
1091 return deletefile(vol, sdir_fd, src, 0);
1093 return( AFPERR_PARAM );
1097 if (vol->vfs->vfs_renamefile(vol, sdir_fd, src, dst) < 0 ) {
1101 /* try to undo the data fork rename,
1102 * we know we are on the same device
1105 unix_rename(-1, dst, sdir_fd, src );
1106 /* return the first error */
1109 return AFPERR_NOOBJ;
1112 return AFPERR_ACCESS ;
1114 return AFPERR_VLOCK;
1116 return AFPERR_PARAM ;
1121 /* don't care if we can't open the newly renamed ressource fork */
1122 if (ad_open(adp, dst, ADFLAGS_HF | ADFLAGS_RDWR) == 0) {
1123 ad_setname(adp, newname);
1125 ad_close( adp, ADFLAGS_HF );
1132 convert a Mac long name to an utf8 name,
1134 size_t mtoUTF8(const struct vol *vol, const char *src, size_t srclen, char *dest, size_t destlen)
1138 if ((size_t)-1 == (outlen = convert_string ( vol->v_maccharset, CH_UTF8_MAC, src, srclen, dest, destlen)) ) {
1144 /* ---------------- */
1145 int copy_path_name(const struct vol *vol, char *newname, char *ibuf)
1152 if ( type != 2 && !(vol->v_obj->afp_version >= 30 && type == 3) ) {
1158 if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
1159 if (vol->v_obj->afp_version >= 30) {
1160 /* convert it to UTF8
1162 if ((plen = mtoUTF8(vol, ibuf, plen, newname, AFPOBJ_TMPSIZ)) == (size_t)-1)
1166 strncpy( newname, ibuf, plen );
1167 newname[ plen ] = '\0';
1169 if (strlen(newname) != plen) {
1170 /* there's \0 in newname, e.g. it's a pathname not
1178 memcpy(&hint, ibuf, sizeof(hint));
1179 ibuf += sizeof(hint);
1181 memcpy(&len16, ibuf, sizeof(len16));
1182 ibuf += sizeof(len16);
1183 plen = ntohs(len16);
1186 if (plen > AFPOBJ_TMPSIZ) {
1189 strncpy( newname, ibuf, plen );
1190 newname[ plen ] = '\0';
1191 if (strlen(newname) != plen) {
1200 /* -----------------------------------
1202 int afp_copyfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1204 struct vol *s_vol, *d_vol;
1206 char *newname, *p, *upath;
1207 struct path *s_path;
1208 uint32_t sdid, ddid;
1209 int err, retvalue = AFP_OK;
1210 uint16_t svid, dvid;
1212 struct adouble ad, *adp;
1218 memcpy(&svid, ibuf, sizeof( svid ));
1219 ibuf += sizeof( svid );
1220 if (NULL == ( s_vol = getvolbyvid( svid )) ) {
1221 return( AFPERR_PARAM );
1224 memcpy(&sdid, ibuf, sizeof( sdid ));
1225 ibuf += sizeof( sdid );
1226 if (NULL == ( dir = dirlookup( s_vol, sdid )) ) {
1230 memcpy(&dvid, ibuf, sizeof( dvid ));
1231 ibuf += sizeof( dvid );
1232 memcpy(&ddid, ibuf, sizeof( ddid ));
1233 ibuf += sizeof( ddid );
1235 if (NULL == ( s_path = cname( s_vol, dir, &ibuf )) ) {
1236 return get_afp_errno(AFPERR_PARAM);
1238 if ( path_isadir(s_path) ) {
1239 return( AFPERR_BADTYPE );
1242 /* don't allow copies when the file is open.
1243 * XXX: the spec only calls for read/deny write access.
1244 * however, copyfile doesn't have any of that info,
1245 * and locks need to stay coherent. as a result,
1246 * we just balk if the file is opened already. */
1248 adp = of_ad(s_vol, s_path, &ad);
1250 if (ad_open(adp, s_path->u_name, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | ADFLAGS_RDONLY | ADFLAGS_SETSHRMD) < 0) {
1251 return AFPERR_DENYCONF;
1253 denyreadset = (ad_testlock(adp, ADEID_DFORK, AD_FILELOCK_DENY_RD) != 0 ||
1254 ad_testlock(adp, ADEID_RFORK, AD_FILELOCK_DENY_RD) != 0 );
1257 retvalue = AFPERR_DENYCONF;
1261 newname = obj->newtmp;
1262 strcpy( newname, s_path->m_name );
1264 p = ctoupath( s_vol, curdir, newname );
1266 retvalue = AFPERR_PARAM;
1270 if (NULL == ( d_vol = getvolbyvid( dvid )) ) {
1271 retvalue = AFPERR_PARAM;
1275 if (d_vol->v_flags & AFPVOL_RO) {
1276 retvalue = AFPERR_VLOCK;
1280 if (NULL == ( dir = dirlookup( d_vol, ddid )) ) {
1281 retvalue = afp_errno;
1285 if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) {
1286 retvalue = get_afp_errno(AFPERR_NOOBJ);
1290 if ( *s_path->m_name != '\0' ) {
1291 retvalue =path_error(s_path, AFPERR_NOOBJ);
1295 /* one of the handful of places that knows about the path type */
1296 if (copy_path_name(d_vol, newname, ibuf) < 0) {
1297 retvalue = AFPERR_PARAM;
1300 /* newname is always only a filename so curdir *is* its
1303 if (NULL == (upath = mtoupath(d_vol, newname, curdir->d_did, utf8_encoding(d_vol->v_obj)))) {
1304 retvalue =AFPERR_PARAM;
1308 if ( (err = copyfile(s_vol, d_vol, -1, p, upath , newname, adp)) < 0 ) {
1314 setvoltime(obj, d_vol );
1317 ad_close( adp, ADFLAGS_DF |ADFLAGS_HF | ADFLAGS_SETSHRMD);
1321 /* ----------------------------------
1322 * if newname is NULL (from directory.c) we don't want to copy the resource fork.
1323 * because we are doing it elsewhere.
1324 * currently if newname is NULL then adp is NULL.
1326 int copyfile(const struct vol *s_vol,
1327 const struct vol *d_vol,
1332 struct adouble *adp)
1334 struct adouble ads, add;
1341 LOG(log_debug, logtype_afpd, "copyfile(sfd:%d,s:'%s',d:'%s',n:'%s')",
1342 sfd, src, dst, newname);
1345 ad_init(&ads, s_vol);
1349 adflags = ADFLAGS_DF | ADFLAGS_RF | ADFLAGS_NORF;
1351 if (ad_openat(adp, sfd, src, adflags | ADFLAGS_RDONLY) < 0) {
1356 if (!AD_RSRC_OPEN(adp))
1357 /* no resource fork, don't create one for dst file */
1358 adflags &= ~ADFLAGS_RF;
1360 stat_result = fstat(ad_data_fileno(adp), &st); /* saving stat exit code, thus saving us on one more stat later on */
1362 if (stat_result < 0) {
1363 /* unlikely but if fstat fails, the default file mode will be 0666. */
1364 st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
1367 ad_init(&add, d_vol);
1368 if (ad_open(&add, dst, adflags | ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_EXCL, st.st_mode | S_IRUSR | S_IWUSR) < 0) {
1370 ad_close( adp, adflags );
1371 if (EEXIST != ret_err) {
1372 deletefile(d_vol, -1, dst, 0);
1375 return AFPERR_EXIST;
1378 if ((err = copy_fork(ADEID_DFORK, &add, adp)) != 0)
1379 LOG(log_error, logtype_afpd, "copyfile('%s'): %s", src, strerror(errno));
1382 if ((err = d_vol->vfs->vfs_copyfile(d_vol, sfd, src, dst)) != 0)
1383 LOG(log_error, logtype_afpd, "copyfile('%s'): %s", src, strerror(errno));
1388 if (!ret_err && newname && (adflags & ADFLAGS_HF)) {
1389 /* set the new name in the resource fork */
1390 ad_copy_header(&add, adp);
1391 ad_setname(&add, newname);
1394 ad_close( adp, adflags );
1396 if (ad_close( &add, adflags ) <0) {
1401 deletefile(d_vol, -1, dst, 0);
1403 else if (stat_result == 0) {
1404 /* set dest modification date to src date */
1407 ut.actime = ut.modtime = st.st_mtime;
1409 /* FIXME netatalk doesn't use resource fork file date
1410 * but maybe we should set its modtime too.
1415 switch ( ret_err ) {
1421 return AFPERR_DFULL;
1423 return AFPERR_NOOBJ;
1425 return AFPERR_ACCESS;
1427 return AFPERR_VLOCK;
1429 return AFPERR_PARAM;
1433 /* -----------------------------------
1434 vol: not NULL delete cnid entry. then we are in curdir and file is a only filename
1435 checkAttrib: 1 check kFPDeleteInhibitBit (deletfile called by afp_delete)
1437 when deletefile is called we don't have lock on it, file is closed (for us)
1438 untrue if called by renamefile
1440 ad_open always try to open file RDWR first and ad_lock takes care of
1441 WRITE lock on read only file.
1444 static int check_attrib(struct adouble *adp)
1446 uint16_t bshort = 0;
1448 ad_getattr(adp, &bshort);
1450 * Does kFPDeleteInhibitBit (bit 8) set?
1452 if ((bshort & htons(ATTRBIT_NODELETE))) {
1453 return AFPERR_OLOCK;
1455 if ((bshort & htons(ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
1461 * dirfd can be used for unlinkat semantics
1463 int deletefile(const struct vol *vol, int dirfd, char *file, int checkAttrib)
1466 struct adouble *adp = NULL;
1467 int adflags, err = AFP_OK;
1470 LOG(log_debug, logtype_afpd, "deletefile('%s')", file);
1474 /* was EACCESS error try to get only metadata */
1475 /* we never want to create a resource fork here, we are going to delete it
1476 * moreover sometimes deletefile is called with a no existent file and
1477 * ad_open would create a 0 byte resource fork
1479 if ( ad_metadataat(dirfd, file, ADFLAGS_CHECK_OF, &ad) == 0 ) {
1480 if ((err = check_attrib(&ad))) {
1481 ad_close(&ad, ADFLAGS_HF | ADFLAGS_CHECK_OF);
1488 /* try to open both forks at once */
1489 adflags = ADFLAGS_DF;
1490 if (ad_openat(&ad, dirfd, file, adflags | ADFLAGS_RF | ADFLAGS_NORF | ADFLAGS_RDONLY) < 0 ) {
1495 case EACCES: /* maybe it's a file with no write mode for us */
1496 break; /* was return AFPERR_ACCESS;*/
1509 if ( adp && AD_RSRC_OPEN(adp) != -1 ) { /* there's a resource fork */
1510 adflags |= ADFLAGS_RF;
1511 /* FIXME we have a pb here because we want to know if a file is open
1512 * there's a 'priority inversion' if you can't open the ressource fork RW
1513 * you can delete it if it's open because you can't get a write lock.
1515 * ADLOCK_FILELOCK means the whole ressource fork, not only after the
1518 * FIXME it doesn't work for RFORK open read only and fork open without deny mode
1520 if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
1526 if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
1527 LOG(log_error, logtype_afpd, "deletefile('%s'): ad_tmplock error: %s", file, strerror(errno));
1529 } else if (!(err = vol->vfs->vfs_deletefile(vol, dirfd, file)) && !(err = netatalk_unlinkat(dirfd, file )) ) {
1531 if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file)))) {
1532 cnid_delete(vol->v_cdb, id);
1538 ad_close(&ad, ADFLAGS_HF | ADFLAGS_CHECK_OF);
1541 ad_close( &ad, adflags ); /* ad_close removes locks if any */
1546 /* ------------------------------------ */
1547 /* return a file id */
1548 int afp_createid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1557 struct path *s_path;
1563 memcpy(&vid, ibuf, sizeof(vid));
1564 ibuf += sizeof(vid);
1566 if (NULL == ( vol = getvolbyvid( vid )) ) {
1567 return( AFPERR_PARAM);
1570 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1574 if (vol->v_flags & AFPVOL_RO)
1575 return AFPERR_VLOCK;
1577 memcpy(&did, ibuf, sizeof( did ));
1578 ibuf += sizeof(did);
1580 if (NULL == ( dir = dirlookup( vol, did )) ) {
1581 return afp_errno; /* was AFPERR_PARAM */
1584 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
1585 return get_afp_errno(AFPERR_NOOBJ); /* was AFPERR_PARAM */
1588 if ( path_isadir(s_path) ) {
1589 return( AFPERR_BADTYPE );
1592 upath = s_path->u_name;
1593 switch (s_path->st_errno) {
1595 break; /* success */
1598 return AFPERR_ACCESS;
1600 return AFPERR_NOOBJ;
1602 return AFPERR_PARAM;
1605 if ((id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath)))) {
1606 memcpy(rbuf, &id, sizeof(id));
1607 *rbuflen = sizeof(id);
1608 return AFPERR_EXISTID;
1611 if ((id = get_id(vol, NULL, st, did, upath, len)) != CNID_INVALID) {
1612 memcpy(rbuf, &id, sizeof(id));
1613 *rbuflen = sizeof(id);
1620 /* ------------------------------- */
1626 static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
1629 struct reenum *param = data;
1630 struct vol *vol = param->vol;
1631 cnid_t did = param->did;
1634 if ( lstat(de->d_name, &path.st) < 0 )
1637 /* update or add to cnid */
1638 aint = cnid_add(vol->v_cdb, &path.st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */
1643 /* --------------------
1644 * Ok the db is out of synch with the dir.
1645 * but if it's a deleted file we don't want to do it again and again.
1648 reenumerate_id(struct vol *vol, char *name, struct dir *dir)
1654 if (vol->v_cdb == NULL) {
1658 /* FIXME use of_statdir ? */
1659 if (lstat(name, &st)) {
1663 if (dirreenumerate(dir, &st)) {
1664 /* we already did it once and the dir haven't been modified */
1665 return dir->d_offcnt;
1669 data.did = dir->d_did;
1670 if ((ret = for_each_dirent(vol, name, reenumerate_loop, (void *)&data)) >= 0) {
1671 setdiroffcnt(curdir, &st, ret);
1672 dir->d_flags |= DIRF_CNID;
1678 /* ------------------------------
1679 resolve a file id */
1680 int afp_resolveid(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1689 uint16_t vid, bitmap;
1691 static char buffer[12 + MAXPATHLEN + 1];
1692 int len = 12 + MAXPATHLEN + 1;
1697 memcpy(&vid, ibuf, sizeof(vid));
1698 ibuf += sizeof(vid);
1700 if (NULL == ( vol = getvolbyvid( vid )) ) {
1701 return( AFPERR_PARAM);
1704 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1708 memcpy(&id, ibuf, sizeof( id ));
1713 /* some MacOS versions after a catsearch do a *lot* of afp_resolveid with 0 */
1717 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1718 return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
1721 if (NULL == ( dir = dirlookup( vol, id )) ) {
1722 return AFPERR_NOID; /* idem AFPERR_PARAM */
1724 if (movecwd(vol, dir) < 0) {
1728 return AFPERR_ACCESS;
1732 return AFPERR_PARAM;
1736 memset(&path, 0, sizeof(path));
1737 path.u_name = upath;
1738 if ( of_stat(&path) < 0 ) {
1740 /* with nfs and our working directory is deleted */
1741 if (errno == ESTALE) {
1745 if ( errno == ENOENT && !retry) {
1746 /* cnid db is out of sync, reenumerate the directory and update ids */
1747 reenumerate_id(vol, ".", dir);
1755 return AFPERR_ACCESS;
1759 return AFPERR_PARAM;
1763 /* directories are bad */
1764 if (S_ISDIR(path.st.st_mode)) {
1765 /* OS9 and OSX don't return the same error code */
1766 return (obj->afp_version >=30)?AFPERR_NOID:AFPERR_BADTYPE;
1769 memcpy(&bitmap, ibuf, sizeof(bitmap));
1770 bitmap = ntohs( bitmap );
1771 if (NULL == (path.m_name = utompath(vol, upath, cnid, utf8_encoding(obj)))) {
1775 if (AFP_OK != (err = getfilparams(obj, vol, bitmap, &path , curdir,
1776 rbuf + sizeof(bitmap), &buflen))) {
1779 *rbuflen = buflen + sizeof(bitmap);
1780 memcpy(rbuf, ibuf, sizeof(bitmap));
1785 /* ------------------------------ */
1786 int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1796 static char buffer[12 + MAXPATHLEN + 1];
1797 int len = 12 + MAXPATHLEN + 1;
1802 memcpy(&vid, ibuf, sizeof(vid));
1803 ibuf += sizeof(vid);
1805 if (NULL == ( vol = getvolbyvid( vid )) ) {
1806 return( AFPERR_PARAM);
1809 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1813 if (vol->v_flags & AFPVOL_RO)
1814 return AFPERR_VLOCK;
1816 memcpy(&id, ibuf, sizeof( id ));
1820 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1824 if (NULL == ( dir = dirlookup( vol, id )) ) {
1825 if (afp_errno == AFPERR_NOOBJ) {
1829 return( AFPERR_PARAM );
1833 if ((movecwd(vol, dir) < 0) || (lstat(upath, &st) < 0)) {
1837 return AFPERR_ACCESS;
1842 /* still try to delete the id */
1846 return AFPERR_PARAM;
1849 else if (S_ISDIR(st.st_mode)) /* directories are bad */
1850 return AFPERR_BADTYPE;
1853 if (cnid_delete(vol->v_cdb, fileid)) {
1856 return AFPERR_VLOCK;
1859 return AFPERR_ACCESS;
1861 return AFPERR_PARAM;
1868 /* ------------------------------ */
1869 static struct adouble *find_adouble(const AFPObj *obj, struct vol *vol, struct path *path, struct ofork **of, struct adouble *adp)
1873 if (path->st_errno) {
1874 switch (path->st_errno) {
1876 afp_errno = AFPERR_NOID;
1880 afp_errno = AFPERR_ACCESS;
1883 afp_errno = AFPERR_PARAM;
1888 /* we use file_access both for legacy Mac perm and
1889 * for unix privilege, rename will take care of folder perms
1891 if (file_access(obj, vol, path, OPENACC_WR ) < 0) {
1892 afp_errno = AFPERR_ACCESS;
1896 if ((*of = of_findname(path))) {
1897 /* reuse struct adouble so it won't break locks */
1901 ret = ad_open(adp, path->u_name, ADFLAGS_HF | ADFLAGS_RDWR);
1903 if ( !ret && ad_reso_fileno(adp) != -1 && !(adp->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
1905 * The user must have the Read & Write privilege for both files in order to use this command.
1907 ad_close(adp, ADFLAGS_HF);
1908 afp_errno = AFPERR_ACCESS;
1915 #define APPLETEMP ".AppleTempXXXXXX"
1917 int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1919 struct stat srcst, destst;
1921 struct dir *dir, *sdir;
1922 char *spath, temp[17], *p;
1923 char *supath, *upath;
1928 struct adouble *adsp = NULL;
1929 struct adouble *addp = NULL;
1930 struct ofork *s_of = NULL;
1931 struct ofork *d_of = NULL;
1944 memcpy(&vid, ibuf, sizeof(vid));
1945 ibuf += sizeof(vid);
1947 if (NULL == ( vol = getvolbyvid( vid )) ) {
1948 return( AFPERR_PARAM);
1951 if ((vol->v_flags & AFPVOL_RO))
1952 return AFPERR_VLOCK;
1954 /* source and destination dids */
1955 memcpy(&sid, ibuf, sizeof(sid));
1956 ibuf += sizeof(sid);
1957 memcpy(&did, ibuf, sizeof(did));
1958 ibuf += sizeof(did);
1961 if (NULL == (dir = dirlookup( vol, sid )) ) {
1962 return afp_errno; /* was AFPERR_PARAM */
1965 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
1966 return get_afp_errno(AFPERR_NOOBJ);
1969 if ( path_isadir(path) ) {
1970 return AFPERR_BADTYPE; /* it's a dir */
1973 /* save some stuff */
1976 spath = obj->oldtmp;
1977 supath = obj->newtmp;
1978 strcpy(spath, path->m_name);
1979 strcpy(supath, path->u_name); /* this is for the cnid changing */
1980 p = absupath( vol, sdir, supath);
1982 /* pathname too long */
1983 return AFPERR_PARAM ;
1987 if (!(adsp = find_adouble(obj, vol, path, &s_of, &ads))) {
1991 /* ***** from here we may have resource fork open **** */
1993 /* look for the source cnid. if it doesn't exist, don't worry about
1995 sid = cnid_lookup(vol->v_cdb, &srcst, sdir->d_did, supath,slen = strlen(supath));
1997 if (NULL == ( dir = dirlookup( vol, did )) ) {
1998 err = afp_errno; /* was AFPERR_PARAM */
1999 goto err_exchangefile;
2002 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2003 err = get_afp_errno(AFPERR_NOOBJ);
2004 goto err_exchangefile;
2007 if ( path_isadir(path) ) {
2008 err = AFPERR_BADTYPE;
2009 goto err_exchangefile;
2012 /* FPExchangeFiles is the only call that can return the SameObj
2014 if ((curdir == sdir) && strcmp(spath, path->m_name) == 0) {
2015 err = AFPERR_SAMEOBJ;
2016 goto err_exchangefile;
2020 if (!(addp = find_adouble(obj, vol, path, &d_of, &add))) {
2022 goto err_exchangefile;
2026 /* they are not on the same device and at least one is open
2027 * FIXME broken for for crossdev and adouble v2
2030 crossdev = (srcst.st_dev != destst.st_dev);
2031 if (/* (d_of || s_of) && */ crossdev) {
2033 goto err_exchangefile;
2036 /* look for destination id. */
2037 upath = path->u_name;
2038 did = cnid_lookup(vol->v_cdb, &destst, curdir->d_did, upath, dlen = strlen(upath));
2040 /* construct a temp name.
2041 * NOTE: the temp file will be in the dest file's directory. it
2042 * will also be inaccessible from AFP. */
2043 memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
2044 if (!mktemp(temp)) {
2046 goto err_exchangefile;
2050 /* FIXME we need to close fork for copy, both s_of and d_of are null */
2051 ad_close(adsp, ADFLAGS_HF);
2052 ad_close(addp, ADFLAGS_HF);
2055 /* now, quickly rename the file. we error if we can't. */
2056 if ((err = renamefile(vol, -1, p, temp, temp, adsp)) != AFP_OK)
2057 goto err_exchangefile;
2058 of_rename(vol, s_of, sdir, spath, curdir, temp);
2060 /* rename destination to source */
2061 if ((err = renamefile(vol, -1, upath, p, spath, addp)) != AFP_OK)
2062 goto err_src_to_tmp;
2063 of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
2065 /* rename temp to destination */
2066 if ((err = renamefile(vol, -1, temp, upath, path->m_name, adsp)) != AFP_OK)
2067 goto err_dest_to_src;
2068 of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
2070 /* id's need switching. src -> dest and dest -> src.
2071 * we need to re-stat() if it was a cross device copy.
2074 cnid_delete(vol->v_cdb, sid);
2076 cnid_delete(vol->v_cdb, did);
2078 if ((did && ( (crossdev && lstat( upath, &srcst) < 0) ||
2079 cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
2081 (sid && ( (crossdev && lstat(p, &destst) < 0) ||
2082 cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
2087 err = AFPERR_ACCESS;
2092 goto err_temp_to_dest;
2095 /* here we need to reopen if crossdev */
2096 if (sid && ad_setid(addp, destst.st_dev, destst.st_ino, sid, sdir->d_did, vol->v_stamp))
2101 if (did && ad_setid(adsp, srcst.st_dev, srcst.st_ino, did, curdir->d_did, vol->v_stamp))
2106 /* change perms, src gets dest perm and vice versa */
2111 LOG(log_error, logtype_afpd, "seteuid failed %s", strerror(errno));
2112 err = AFP_OK; /* ignore error */
2113 goto err_temp_to_dest;
2117 * we need to exchange ACL entries as well
2119 /* exchange_acls(vol, p, upath); */
2124 path->m_name = NULL;
2125 path->u_name = upath;
2127 setfilunixmode(vol, path, destst.st_mode);
2128 setfilowner(vol, destst.st_uid, destst.st_gid, path);
2135 setfilunixmode(vol, path, srcst.st_mode);
2136 setfilowner(vol, srcst.st_uid, srcst.st_gid, path);
2138 if ( setegid(gid) < 0 || seteuid(uid) < 0) {
2139 LOG(log_error, logtype_afpd, "can't seteuid back %s", strerror(errno));
2144 goto err_exchangefile;
2146 /* all this stuff is so that we can unwind a failed operation
2149 /* rename dest to temp */
2150 renamefile(vol, -1, upath, temp, temp, adsp);
2151 of_rename(vol, s_of, curdir, upath, curdir, temp);
2154 /* rename source back to dest */
2155 renamefile(vol, -1, p, upath, path->m_name, addp);
2156 of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
2159 /* rename temp back to source */
2160 renamefile(vol, -1, temp, p, spath, adsp);
2161 of_rename(vol, s_of, curdir, temp, sdir, spath);
2164 if ( !s_of && adsp && ad_meta_fileno(adsp) != -1 ) { /* META */
2165 ad_close(adsp, ADFLAGS_HF);
2167 if ( !d_of && addp && ad_meta_fileno(addp) != -1 ) {/* META */
2168 ad_close(addp, ADFLAGS_HF);
2172 if ((cached = dircache_search_by_did(vol, sid)) != NULL)
2173 (void)dir_remove(vol, cached);
2174 if ((cached = dircache_search_by_did(vol, did)) != NULL)
2175 (void)dir_remove(vol, cached);