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, struct vol *vol, uint16_t bitmap, struct path *path,
596 struct dir *dir, char *buf, size_t *buflen, int in_enumerate)
598 struct adouble ad, *adp;
603 LOG(log_debug, logtype_afpd, "getfilparams(\"%s\")", path->u_name);
605 opened = PARAM_NEED_ADP(bitmap);
611 * Dont check for and resturn open fork attributes when enumerating
612 * This saves a lot of syscalls, the client will hopefully only use the result
613 * in FPGetFileParms where we return the correct value
615 flags = (!in_enumerate &&(bitmap & (1 << FILPBIT_ATTR))) ? ADFLAGS_CHECK_OF : 0;
617 adp = of_ad(vol, path, &ad);
618 upath = path->u_name;
620 if ( ad_metadata( upath, flags, adp) < 0 ) {
623 LOG(log_error, logtype_afpd, "getfilparams(%s): %s: check resource fork permission?",
624 upath, strerror(errno));
625 return AFPERR_ACCESS;
627 LOG(log_error, logtype_afpd, "getfilparams(%s): bad resource fork", upath);
636 rc = getmetadata(obj, vol, bitmap, path, dir, buf, buflen, adp);
637 ad_close(adp, ADFLAGS_HF | flags);
642 /* ----------------------------- */
643 int afp_createfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
648 struct ofork *of = NULL;
650 int creatf, did, openf, retvalue = AFP_OK;
656 creatf = (unsigned char) *ibuf++;
658 memcpy(&vid, ibuf, sizeof( vid ));
659 ibuf += sizeof( vid );
661 if (NULL == ( vol = getvolbyvid( vid )) )
662 return( AFPERR_PARAM );
664 if (vol->v_flags & AFPVOL_RO)
667 memcpy(&did, ibuf, sizeof( did));
668 ibuf += sizeof( did );
670 if (NULL == ( dir = dirlookup( vol, did )) )
673 if (NULL == ( s_path = cname( vol, dir, &ibuf )) )
674 return get_afp_errno(AFPERR_PARAM);
675 if ( *s_path->m_name == '\0' )
676 return( AFPERR_BADTYPE );
678 upath = s_path->u_name;
681 /* if upath is deleted we already in trouble anyway */
682 if ((of = of_findname(s_path))) {
690 openf = ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_TRUNC;
692 /* on a soft create, if the file is open then ad_open won't fail
693 because open syscall is not called */
694 openf = ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_EXCL;
696 if (ad_open(&ad, upath, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | openf, 0666) < 0) {
700 case ENOENT : /* we were already in 'did folder' so chdir() didn't fail */
701 return ( AFPERR_NOOBJ );
703 return( AFPERR_EXIST );
705 return( AFPERR_ACCESS );
708 return( AFPERR_DFULL );
710 return( AFPERR_PARAM );
713 if ( ad_meta_fileno( &ad ) == -1 ) { /* Hard META / HF */
714 /* FIXME with hard create on an existing file, we already
715 * corrupted the data file.
717 netatalk_unlink( upath );
718 ad_close( &ad, ADFLAGS_DF );
719 return AFPERR_ACCESS;
722 path = s_path->m_name;
723 ad_setname(&ad, path);
726 if (lstat(upath, &st) != 0) {
727 LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): stat: %s",
728 upath, strerror(errno));
729 ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF);
733 (void)get_id(vol, &ad, &st, dir->d_did, upath, strlen(upath));
736 ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF );
737 fce_register_new_file(s_path);
742 setvoltime(obj, vol );
747 int afp_setfilparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
753 uint16_t vid, bitmap;
758 memcpy(&vid, ibuf, sizeof( vid ));
759 ibuf += sizeof( vid );
760 if (NULL == ( vol = getvolbyvid( vid )) ) {
761 return( AFPERR_PARAM );
764 if (vol->v_flags & AFPVOL_RO)
767 memcpy(&did, ibuf, sizeof( did ));
768 ibuf += sizeof( did );
769 if (NULL == ( dir = dirlookup( vol, did )) ) {
770 return afp_errno; /* was AFPERR_NOOBJ */
773 memcpy(&bitmap, ibuf, sizeof( bitmap ));
774 bitmap = ntohs( bitmap );
775 ibuf += sizeof( bitmap );
777 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
778 return get_afp_errno(AFPERR_PARAM);
781 if (path_isadir(s_path)) {
782 return( AFPERR_BADTYPE ); /* it's a directory */
785 if ( s_path->st_errno != 0 ) {
786 return( AFPERR_NOOBJ );
789 if ((u_long)ibuf & 1 ) {
793 if (AFP_OK == ( rc = setfilparams(obj, vol, s_path, bitmap, ibuf )) ) {
794 setvoltime(obj, vol );
801 * cf AFP3.0.pdf page 252 for change_mdate and change_parent_mdate logic
804 extern struct path Cur_Path;
806 int setfilparams(const AFPObj *obj, struct vol *vol,
807 struct path *path, uint16_t f_bitmap, char *buf )
809 struct adouble ad, *adp;
811 int bit, isad = 1, err = AFP_OK;
813 u_char achar, *fdType, xyy[4]; /* uninitialized, OK 310105 */
814 uint16_t ashort, bshort, oshort;
817 uint16_t upriv_bit = 0;
819 int change_mdate = 0;
820 int change_parent_mdate = 0;
825 uint16_t bitmap = f_bitmap;
826 uint32_t cdate,bdate;
827 u_char finder_buf[32];
831 LOG(log_debug9, logtype_afpd, "begin setfilparams:");
834 adp = of_ad(vol, path, &ad);
835 upath = path->u_name;
837 if (!vol_unix_priv(vol) && check_access(obj, vol, upath, OPENACC_WR ) < 0) {
838 return AFPERR_ACCESS;
841 /* with unix priv maybe we have to change adouble file priv first */
843 while ( bitmap != 0 ) {
844 while (( bitmap & 1 ) == 0 ) {
851 memcpy(&ashort, buf, sizeof( ashort ));
852 buf += sizeof( ashort );
856 memcpy(&cdate, buf, sizeof(cdate));
857 buf += sizeof( cdate );
860 memcpy(&newdate, buf, sizeof( newdate ));
861 buf += sizeof( newdate );
865 memcpy(&bdate, buf, sizeof( bdate));
866 buf += sizeof( bdate );
870 memcpy(finder_buf, buf, 32 );
871 if (memcmp(buf, "slnkrhap", 8) == 0 && !S_ISLNK(path->st.st_mode)) {
875 char buf[PATH_MAX+1];
876 if ((fp = open(path->u_name, O_RDONLY)) >= 0) {
877 if ((len = read(fp, buf, PATH_MAX+1))) {
878 if (unlink(path->u_name) == 0) {
880 erc = symlink(buf, path->u_name);
889 goto setfilparam_done;
895 case FILPBIT_UNIXPR :
896 if (!vol_unix_priv(vol)) {
897 /* this volume doesn't use unix priv */
903 change_parent_mdate = 1;
905 memcpy( &aint, buf, sizeof( aint ));
906 f_uid = ntohl (aint);
907 buf += sizeof( aint );
908 memcpy( &aint, buf, sizeof( aint ));
909 f_gid = ntohl (aint);
910 buf += sizeof( aint );
911 setfilowner(vol, f_uid, f_gid, path);
913 memcpy( &upriv, buf, sizeof( upriv ));
914 buf += sizeof( upriv );
915 upriv = ntohl (upriv);
916 if ((upriv & S_IWUSR)) {
917 setfilunixmode(vol, path, upriv);
924 case FILPBIT_PDINFO :
925 if (obj->afp_version < 30) { /* else it's UTF8 name */
928 /* Keep special case to support crlf translations */
929 if ((unsigned int) achar == 0x04) {
930 fdType = (u_char *)"TEXT";
933 xyy[0] = ( u_char ) 'p';
944 /* break while loop */
953 /* second try with adouble open
955 if (ad_open(adp, upath, ADFLAGS_HF | ADFLAGS_RDWR | ADFLAGS_CREATE, 0666) < 0) {
956 LOG(log_debug, logtype_afpd, "setfilparams: ad_open_metadata error");
958 * For some things, we don't need an adouble header:
959 * - change of modification date
960 * - UNIX privs (Bug-ID #2863424)
962 if (!symlinked && f_bitmap & ~(1<<FILPBIT_MDATE | 1<<FILPBIT_UNIXPR)) {
963 LOG(log_debug, logtype_afpd, "setfilparams: need adouble access");
964 return AFPERR_ACCESS;
966 LOG(log_debug, logtype_afpd, "setfilparams: no adouble perms, but only FILPBIT_MDATE and/or FILPBIT_UNIXPR");
968 } else if ((ad_get_MD_flags( adp ) & O_CREAT) ) {
969 ad_setname(adp, path->m_name);
974 while ( bitmap != 0 ) {
975 while (( bitmap & 1 ) == 0 ) {
982 ad_getattr(adp, &bshort);
984 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
985 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
989 if ((bshort & htons(ATTRBIT_INVISIBLE)) != (oshort & htons(ATTRBIT_INVISIBLE)))
990 change_parent_mdate = 1;
991 ad_setattr(adp, bshort);
994 ad_setdate(adp, AD_DATE_CREATE, cdate);
999 ad_setdate(adp, AD_DATE_BACKUP, bdate);
1001 case FILPBIT_FINFO :
1002 memcpy(ad_entry( adp, ADEID_FINDERI ), finder_buf, 32 );
1004 case FILPBIT_UNIXPR :
1006 setfilunixmode(vol, path, upriv);
1009 case FILPBIT_PDINFO :
1010 if (obj->afp_version < 30) { /* else it's UTF8 name */
1011 memcpy(ad_entry( adp, ADEID_FINDERI ), fdType, 4 );
1012 memcpy(ad_entry( adp, ADEID_FINDERI ) + 4, "pdos", 4 );
1017 err = AFPERR_BITMAP;
1018 goto setfilparam_done;
1025 if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
1026 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1030 ad_setdate(adp, AD_DATE_MODIFY, newdate);
1031 ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
1037 ad_close(adp, ADFLAGS_HF);
1040 if (change_parent_mdate && gettimeofday(&tv, NULL) == 0) {
1041 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1042 bitmap = 1<<FILPBIT_MDATE;
1043 setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
1047 LOG(log_debug9, logtype_afpd, "end setfilparams:");
1053 * renamefile and copyfile take the old and new unix pathnames
1054 * and the new mac name.
1056 * sdir_fd source dir fd to which src path is relative (for openat et al semantics)
1057 * passing -1 means this is not used, src path is a full path
1058 * src the source path
1059 * dst the dest filename in current dir
1060 * newname the dest mac name
1061 * adp adouble struct of src file, if open, or & zeroed one
1064 int renamefile(const struct vol *vol, int sdir_fd, char *src, char *dst, char *newname, struct adouble *adp)
1068 LOG(log_debug, logtype_afpd,
1069 "renamefile: src[%d, \"%s\"] -> dst[\"%s\"]", sdir_fd, src, dst);
1071 if ( unix_rename( sdir_fd, src, -1, dst ) < 0 ) {
1074 return( AFPERR_NOOBJ );
1077 return( AFPERR_ACCESS );
1079 return AFPERR_VLOCK;
1080 case EXDEV : /* Cross device move -- try copy */
1081 /* NOTE: with open file it's an error because after the copy we will
1082 * get two files, it's fixable for our process (eg reopen the new file, get the
1083 * locks, and so on. But it doesn't solve the case with a second process
1085 if (adp->ad_open_forks) {
1086 /* FIXME warning in syslog so admin'd know there's a conflict ?*/
1087 return AFPERR_OLOCK; /* little lie */
1089 if (AFP_OK != ( rc = copyfile(vol, vol, sdir_fd, src, dst, newname, NULL )) ) {
1090 /* on error copyfile delete dest */
1093 return deletefile(vol, sdir_fd, src, 0);
1095 return( AFPERR_PARAM );
1099 if (vol->vfs->vfs_renamefile(vol, sdir_fd, src, dst) < 0 ) {
1103 /* try to undo the data fork rename,
1104 * we know we are on the same device
1107 unix_rename(-1, dst, sdir_fd, src );
1108 /* return the first error */
1111 return AFPERR_NOOBJ;
1114 return AFPERR_ACCESS ;
1116 return AFPERR_VLOCK;
1118 return AFPERR_PARAM ;
1123 /* don't care if we can't open the newly renamed ressource fork */
1124 if (ad_open(adp, dst, ADFLAGS_HF | ADFLAGS_RDWR) == 0) {
1125 ad_setname(adp, newname);
1127 ad_close( adp, ADFLAGS_HF );
1134 convert a Mac long name to an utf8 name,
1136 size_t mtoUTF8(const struct vol *vol, const char *src, size_t srclen, char *dest, size_t destlen)
1140 if ((size_t)-1 == (outlen = convert_string ( vol->v_maccharset, CH_UTF8_MAC, src, srclen, dest, destlen)) ) {
1146 /* ---------------- */
1147 int copy_path_name(const struct vol *vol, char *newname, char *ibuf)
1154 if ( type != 2 && !(vol->v_obj->afp_version >= 30 && type == 3) ) {
1160 if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
1161 if (vol->v_obj->afp_version >= 30) {
1162 /* convert it to UTF8
1164 if ((plen = mtoUTF8(vol, ibuf, plen, newname, AFPOBJ_TMPSIZ)) == (size_t)-1)
1168 strncpy( newname, ibuf, plen );
1169 newname[ plen ] = '\0';
1171 if (strlen(newname) != plen) {
1172 /* there's \0 in newname, e.g. it's a pathname not
1180 memcpy(&hint, ibuf, sizeof(hint));
1181 ibuf += sizeof(hint);
1183 memcpy(&len16, ibuf, sizeof(len16));
1184 ibuf += sizeof(len16);
1185 plen = ntohs(len16);
1188 if (plen > AFPOBJ_TMPSIZ) {
1191 strncpy( newname, ibuf, plen );
1192 newname[ plen ] = '\0';
1193 if (strlen(newname) != plen) {
1202 /* -----------------------------------
1204 int afp_copyfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1206 struct vol *s_vol, *d_vol;
1208 char *newname, *p, *upath;
1209 struct path *s_path;
1210 uint32_t sdid, ddid;
1211 int err, retvalue = AFP_OK;
1212 uint16_t svid, dvid;
1214 struct adouble ad, *adp;
1220 memcpy(&svid, ibuf, sizeof( svid ));
1221 ibuf += sizeof( svid );
1222 if (NULL == ( s_vol = getvolbyvid( svid )) ) {
1223 return( AFPERR_PARAM );
1226 memcpy(&sdid, ibuf, sizeof( sdid ));
1227 ibuf += sizeof( sdid );
1228 if (NULL == ( dir = dirlookup( s_vol, sdid )) ) {
1232 memcpy(&dvid, ibuf, sizeof( dvid ));
1233 ibuf += sizeof( dvid );
1234 memcpy(&ddid, ibuf, sizeof( ddid ));
1235 ibuf += sizeof( ddid );
1237 if (NULL == ( s_path = cname( s_vol, dir, &ibuf )) ) {
1238 return get_afp_errno(AFPERR_PARAM);
1240 if ( path_isadir(s_path) ) {
1241 return( AFPERR_BADTYPE );
1244 /* don't allow copies when the file is open.
1245 * XXX: the spec only calls for read/deny write access.
1246 * however, copyfile doesn't have any of that info,
1247 * and locks need to stay coherent. as a result,
1248 * we just balk if the file is opened already. */
1250 adp = of_ad(s_vol, s_path, &ad);
1252 if (ad_open(adp, s_path->u_name, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | ADFLAGS_RDONLY | ADFLAGS_SETSHRMD) < 0) {
1253 return AFPERR_DENYCONF;
1255 denyreadset = (ad_testlock(adp, ADEID_DFORK, AD_FILELOCK_DENY_RD) != 0 ||
1256 ad_testlock(adp, ADEID_RFORK, AD_FILELOCK_DENY_RD) != 0 );
1259 retvalue = AFPERR_DENYCONF;
1263 newname = obj->newtmp;
1264 strcpy( newname, s_path->m_name );
1266 p = ctoupath( s_vol, curdir, newname );
1268 retvalue = AFPERR_PARAM;
1272 if (NULL == ( d_vol = getvolbyvid( dvid )) ) {
1273 retvalue = AFPERR_PARAM;
1277 if (d_vol->v_flags & AFPVOL_RO) {
1278 retvalue = AFPERR_VLOCK;
1282 if (NULL == ( dir = dirlookup( d_vol, ddid )) ) {
1283 retvalue = afp_errno;
1287 if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) {
1288 retvalue = get_afp_errno(AFPERR_NOOBJ);
1292 if ( *s_path->m_name != '\0' ) {
1293 retvalue =path_error(s_path, AFPERR_NOOBJ);
1297 /* one of the handful of places that knows about the path type */
1298 if (copy_path_name(d_vol, newname, ibuf) < 0) {
1299 retvalue = AFPERR_PARAM;
1302 /* newname is always only a filename so curdir *is* its
1305 if (NULL == (upath = mtoupath(d_vol, newname, curdir->d_did, utf8_encoding(d_vol->v_obj)))) {
1306 retvalue =AFPERR_PARAM;
1310 if ( (err = copyfile(s_vol, d_vol, -1, p, upath , newname, adp)) < 0 ) {
1316 setvoltime(obj, d_vol );
1319 ad_close( adp, ADFLAGS_DF |ADFLAGS_HF | ADFLAGS_SETSHRMD);
1323 /* ----------------------------------
1324 * if newname is NULL (from directory.c) we don't want to copy the resource fork.
1325 * because we are doing it elsewhere.
1326 * currently if newname is NULL then adp is NULL.
1328 int copyfile(const struct vol *s_vol,
1329 const struct vol *d_vol,
1334 struct adouble *adp)
1336 struct adouble ads, add;
1343 LOG(log_debug, logtype_afpd, "copyfile(sfd:%d,s:'%s',d:'%s',n:'%s')",
1344 sfd, src, dst, newname);
1347 ad_init(&ads, s_vol);
1351 adflags = ADFLAGS_DF | ADFLAGS_RF | ADFLAGS_NORF;
1353 if (ad_openat(adp, sfd, src, adflags | ADFLAGS_RDONLY) < 0) {
1358 if (!AD_RSRC_OPEN(adp))
1359 /* no resource fork, don't create one for dst file */
1360 adflags &= ~ADFLAGS_RF;
1362 stat_result = fstat(ad_data_fileno(adp), &st); /* saving stat exit code, thus saving us on one more stat later on */
1364 if (stat_result < 0) {
1365 /* unlikely but if fstat fails, the default file mode will be 0666. */
1366 st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
1369 ad_init(&add, d_vol);
1370 if (ad_open(&add, dst, adflags | ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_EXCL, st.st_mode | S_IRUSR | S_IWUSR) < 0) {
1372 ad_close( adp, adflags );
1373 if (EEXIST != ret_err) {
1374 deletefile(d_vol, -1, dst, 0);
1377 return AFPERR_EXIST;
1380 if ((err = copy_fork(ADEID_DFORK, &add, adp)) != 0)
1381 LOG(log_error, logtype_afpd, "copyfile('%s'): %s", src, strerror(errno));
1384 if ((err = d_vol->vfs->vfs_copyfile(d_vol, sfd, src, dst)) != 0)
1385 LOG(log_error, logtype_afpd, "copyfile('%s'): %s", src, strerror(errno));
1390 if (!ret_err && newname && (adflags & ADFLAGS_HF)) {
1391 /* set the new name in the resource fork */
1392 ad_copy_header(&add, adp);
1393 ad_setname(&add, newname);
1396 ad_close( adp, adflags );
1398 if (ad_close( &add, adflags ) <0) {
1403 deletefile(d_vol, -1, dst, 0);
1405 else if (stat_result == 0) {
1406 /* set dest modification date to src date */
1409 ut.actime = ut.modtime = st.st_mtime;
1411 /* FIXME netatalk doesn't use resource fork file date
1412 * but maybe we should set its modtime too.
1417 switch ( ret_err ) {
1423 return AFPERR_DFULL;
1425 return AFPERR_NOOBJ;
1427 return AFPERR_ACCESS;
1429 return AFPERR_VLOCK;
1431 return AFPERR_PARAM;
1435 /* -----------------------------------
1436 vol: not NULL delete cnid entry. then we are in curdir and file is a only filename
1437 checkAttrib: 1 check kFPDeleteInhibitBit (deletfile called by afp_delete)
1439 when deletefile is called we don't have lock on it, file is closed (for us)
1440 untrue if called by renamefile
1442 ad_open always try to open file RDWR first and ad_lock takes care of
1443 WRITE lock on read only file.
1446 static int check_attrib(struct adouble *adp)
1448 uint16_t bshort = 0;
1450 ad_getattr(adp, &bshort);
1452 * Does kFPDeleteInhibitBit (bit 8) set?
1454 if ((bshort & htons(ATTRBIT_NODELETE))) {
1455 return AFPERR_OLOCK;
1457 if ((bshort & htons(ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
1463 * dirfd can be used for unlinkat semantics
1465 int deletefile(const struct vol *vol, int dirfd, char *file, int checkAttrib)
1468 struct adouble *adp = NULL;
1469 int adflags, err = AFP_OK;
1472 LOG(log_debug, logtype_afpd, "deletefile('%s')", file);
1476 /* was EACCESS error try to get only metadata */
1477 /* we never want to create a resource fork here, we are going to delete it
1478 * moreover sometimes deletefile is called with a no existent file and
1479 * ad_open would create a 0 byte resource fork
1481 if ( ad_metadataat(dirfd, file, ADFLAGS_CHECK_OF, &ad) == 0 ) {
1482 if ((err = check_attrib(&ad))) {
1483 ad_close(&ad, ADFLAGS_HF | ADFLAGS_CHECK_OF);
1490 /* try to open both forks at once */
1491 adflags = ADFLAGS_DF;
1492 if (ad_openat(&ad, dirfd, file, adflags | ADFLAGS_RF | ADFLAGS_NORF | ADFLAGS_RDONLY) < 0 ) {
1497 case EACCES: /* maybe it's a file with no write mode for us */
1498 break; /* was return AFPERR_ACCESS;*/
1511 if ( adp && AD_RSRC_OPEN(adp) != -1 ) { /* there's a resource fork */
1512 adflags |= ADFLAGS_RF;
1513 /* FIXME we have a pb here because we want to know if a file is open
1514 * there's a 'priority inversion' if you can't open the ressource fork RW
1515 * you can delete it if it's open because you can't get a write lock.
1517 * ADLOCK_FILELOCK means the whole ressource fork, not only after the
1520 * FIXME it doesn't work for RFORK open read only and fork open without deny mode
1522 if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
1528 if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
1529 LOG(log_error, logtype_afpd, "deletefile('%s'): ad_tmplock error: %s", file, strerror(errno));
1531 } else if (!(err = vol->vfs->vfs_deletefile(vol, dirfd, file)) && !(err = netatalk_unlinkat(dirfd, file )) ) {
1533 if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file)))) {
1534 cnid_delete(vol->v_cdb, id);
1540 ad_close(&ad, ADFLAGS_HF | ADFLAGS_CHECK_OF);
1543 ad_close( &ad, adflags ); /* ad_close removes locks if any */
1548 /* ------------------------------------ */
1549 /* return a file id */
1550 int afp_createid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1559 struct path *s_path;
1565 memcpy(&vid, ibuf, sizeof(vid));
1566 ibuf += sizeof(vid);
1568 if (NULL == ( vol = getvolbyvid( vid )) ) {
1569 return( AFPERR_PARAM);
1572 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1576 if (vol->v_flags & AFPVOL_RO)
1577 return AFPERR_VLOCK;
1579 memcpy(&did, ibuf, sizeof( did ));
1580 ibuf += sizeof(did);
1582 if (NULL == ( dir = dirlookup( vol, did )) ) {
1583 return afp_errno; /* was AFPERR_PARAM */
1586 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
1587 return get_afp_errno(AFPERR_NOOBJ); /* was AFPERR_PARAM */
1590 if ( path_isadir(s_path) ) {
1591 return( AFPERR_BADTYPE );
1594 upath = s_path->u_name;
1595 switch (s_path->st_errno) {
1597 break; /* success */
1600 return AFPERR_ACCESS;
1602 return AFPERR_NOOBJ;
1604 return AFPERR_PARAM;
1607 if ((id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath)))) {
1608 memcpy(rbuf, &id, sizeof(id));
1609 *rbuflen = sizeof(id);
1610 return AFPERR_EXISTID;
1613 if ((id = get_id(vol, NULL, st, did, upath, len)) != CNID_INVALID) {
1614 memcpy(rbuf, &id, sizeof(id));
1615 *rbuflen = sizeof(id);
1622 /* ------------------------------- */
1628 static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
1631 struct reenum *param = data;
1632 struct vol *vol = param->vol;
1633 cnid_t did = param->did;
1636 if ( lstat(de->d_name, &path.st) < 0 )
1639 /* update or add to cnid */
1640 aint = cnid_add(vol->v_cdb, &path.st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */
1645 /* --------------------
1646 * Ok the db is out of synch with the dir.
1647 * but if it's a deleted file we don't want to do it again and again.
1650 reenumerate_id(struct vol *vol, char *name, struct dir *dir)
1656 if (vol->v_cdb == NULL) {
1660 /* FIXME use of_statdir ? */
1661 if (lstat(name, &st)) {
1665 if (dirreenumerate(dir, &st)) {
1666 /* we already did it once and the dir haven't been modified */
1667 return dir->d_offcnt;
1671 data.did = dir->d_did;
1672 if ((ret = for_each_dirent(vol, name, reenumerate_loop, (void *)&data)) >= 0) {
1673 setdiroffcnt(curdir, &st, ret);
1674 dir->d_flags |= DIRF_CNID;
1680 /* ------------------------------
1681 resolve a file id */
1682 int afp_resolveid(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1691 uint16_t vid, bitmap;
1693 static char buffer[12 + MAXPATHLEN + 1];
1694 int len = 12 + MAXPATHLEN + 1;
1699 memcpy(&vid, ibuf, sizeof(vid));
1700 ibuf += sizeof(vid);
1702 if (NULL == ( vol = getvolbyvid( vid )) ) {
1703 return( AFPERR_PARAM);
1706 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1710 memcpy(&id, ibuf, sizeof( id ));
1715 /* some MacOS versions after a catsearch do a *lot* of afp_resolveid with 0 */
1719 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1720 return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
1723 if (NULL == ( dir = dirlookup( vol, id )) ) {
1724 return AFPERR_NOID; /* idem AFPERR_PARAM */
1726 if (movecwd(vol, dir) < 0) {
1730 return AFPERR_ACCESS;
1734 return AFPERR_PARAM;
1738 memset(&path, 0, sizeof(path));
1739 path.u_name = upath;
1740 if ( of_stat(&path) < 0 ) {
1742 /* with nfs and our working directory is deleted */
1743 if (errno == ESTALE) {
1747 if ( errno == ENOENT && !retry) {
1748 /* cnid db is out of sync, reenumerate the directory and update ids */
1749 reenumerate_id(vol, ".", dir);
1757 return AFPERR_ACCESS;
1761 return AFPERR_PARAM;
1765 /* directories are bad */
1766 if (S_ISDIR(path.st.st_mode)) {
1767 /* OS9 and OSX don't return the same error code */
1768 return (obj->afp_version >=30)?AFPERR_NOID:AFPERR_BADTYPE;
1771 memcpy(&bitmap, ibuf, sizeof(bitmap));
1772 bitmap = ntohs( bitmap );
1773 if (NULL == (path.m_name = utompath(vol, upath, cnid, utf8_encoding(obj)))) {
1777 if (AFP_OK != (err = getfilparams(obj, vol, bitmap, &path , curdir,
1778 rbuf + sizeof(bitmap), &buflen, 0))) {
1781 *rbuflen = buflen + sizeof(bitmap);
1782 memcpy(rbuf, ibuf, sizeof(bitmap));
1787 /* ------------------------------ */
1788 int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1798 static char buffer[12 + MAXPATHLEN + 1];
1799 int len = 12 + MAXPATHLEN + 1;
1804 memcpy(&vid, ibuf, sizeof(vid));
1805 ibuf += sizeof(vid);
1807 if (NULL == ( vol = getvolbyvid( vid )) ) {
1808 return( AFPERR_PARAM);
1811 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1815 if (vol->v_flags & AFPVOL_RO)
1816 return AFPERR_VLOCK;
1818 memcpy(&id, ibuf, sizeof( id ));
1822 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1826 if (NULL == ( dir = dirlookup( vol, id )) ) {
1827 if (afp_errno == AFPERR_NOOBJ) {
1831 return( AFPERR_PARAM );
1835 if ((movecwd(vol, dir) < 0) || (lstat(upath, &st) < 0)) {
1839 return AFPERR_ACCESS;
1844 /* still try to delete the id */
1848 return AFPERR_PARAM;
1851 else if (S_ISDIR(st.st_mode)) /* directories are bad */
1852 return AFPERR_BADTYPE;
1855 if (cnid_delete(vol->v_cdb, fileid)) {
1858 return AFPERR_VLOCK;
1861 return AFPERR_ACCESS;
1863 return AFPERR_PARAM;
1870 /* ------------------------------ */
1871 static struct adouble *find_adouble(const AFPObj *obj, struct vol *vol, struct path *path, struct ofork **of, struct adouble *adp)
1875 if (path->st_errno) {
1876 switch (path->st_errno) {
1878 afp_errno = AFPERR_NOID;
1882 afp_errno = AFPERR_ACCESS;
1885 afp_errno = AFPERR_PARAM;
1890 /* we use file_access both for legacy Mac perm and
1891 * for unix privilege, rename will take care of folder perms
1893 if (file_access(obj, vol, path, OPENACC_WR ) < 0) {
1894 afp_errno = AFPERR_ACCESS;
1898 if ((*of = of_findname(path))) {
1899 /* reuse struct adouble so it won't break locks */
1903 ret = ad_open(adp, path->u_name, ADFLAGS_HF | ADFLAGS_RDWR);
1905 if ( !ret && ad_reso_fileno(adp) != -1 && !(adp->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
1907 * The user must have the Read & Write privilege for both files in order to use this command.
1909 ad_close(adp, ADFLAGS_HF);
1910 afp_errno = AFPERR_ACCESS;
1917 #define APPLETEMP ".AppleTempXXXXXX"
1919 int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1921 struct stat srcst, destst;
1923 struct dir *dir, *sdir;
1924 char *spath, temp[17], *p;
1925 char *supath, *upath;
1930 struct adouble *adsp = NULL;
1931 struct adouble *addp = NULL;
1932 struct ofork *s_of = NULL;
1933 struct ofork *d_of = NULL;
1946 memcpy(&vid, ibuf, sizeof(vid));
1947 ibuf += sizeof(vid);
1949 if (NULL == ( vol = getvolbyvid( vid )) ) {
1950 return( AFPERR_PARAM);
1953 if ((vol->v_flags & AFPVOL_RO))
1954 return AFPERR_VLOCK;
1956 /* source and destination dids */
1957 memcpy(&sid, ibuf, sizeof(sid));
1958 ibuf += sizeof(sid);
1959 memcpy(&did, ibuf, sizeof(did));
1960 ibuf += sizeof(did);
1963 if (NULL == (dir = dirlookup( vol, sid )) ) {
1964 return afp_errno; /* was AFPERR_PARAM */
1967 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
1968 return get_afp_errno(AFPERR_NOOBJ);
1971 if ( path_isadir(path) ) {
1972 return AFPERR_BADTYPE; /* it's a dir */
1975 /* save some stuff */
1978 spath = obj->oldtmp;
1979 supath = obj->newtmp;
1980 strcpy(spath, path->m_name);
1981 strcpy(supath, path->u_name); /* this is for the cnid changing */
1982 p = absupath( vol, sdir, supath);
1984 /* pathname too long */
1985 return AFPERR_PARAM ;
1989 if (!(adsp = find_adouble(obj, vol, path, &s_of, &ads))) {
1993 /* ***** from here we may have resource fork open **** */
1995 /* look for the source cnid. if it doesn't exist, don't worry about
1997 sid = cnid_lookup(vol->v_cdb, &srcst, sdir->d_did, supath,slen = strlen(supath));
1999 if (NULL == ( dir = dirlookup( vol, did )) ) {
2000 err = afp_errno; /* was AFPERR_PARAM */
2001 goto err_exchangefile;
2004 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2005 err = get_afp_errno(AFPERR_NOOBJ);
2006 goto err_exchangefile;
2009 if ( path_isadir(path) ) {
2010 err = AFPERR_BADTYPE;
2011 goto err_exchangefile;
2014 /* FPExchangeFiles is the only call that can return the SameObj
2016 if ((curdir == sdir) && strcmp(spath, path->m_name) == 0) {
2017 err = AFPERR_SAMEOBJ;
2018 goto err_exchangefile;
2022 if (!(addp = find_adouble(obj, vol, path, &d_of, &add))) {
2024 goto err_exchangefile;
2028 /* they are not on the same device and at least one is open
2029 * FIXME broken for for crossdev and adouble v2
2032 crossdev = (srcst.st_dev != destst.st_dev);
2033 if (/* (d_of || s_of) && */ crossdev) {
2035 goto err_exchangefile;
2038 /* look for destination id. */
2039 upath = path->u_name;
2040 did = cnid_lookup(vol->v_cdb, &destst, curdir->d_did, upath, dlen = strlen(upath));
2042 /* construct a temp name.
2043 * NOTE: the temp file will be in the dest file's directory. it
2044 * will also be inaccessible from AFP. */
2045 memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
2046 if (!mktemp(temp)) {
2048 goto err_exchangefile;
2052 /* FIXME we need to close fork for copy, both s_of and d_of are null */
2053 ad_close(adsp, ADFLAGS_HF);
2054 ad_close(addp, ADFLAGS_HF);
2057 /* now, quickly rename the file. we error if we can't. */
2058 if ((err = renamefile(vol, -1, p, temp, temp, adsp)) != AFP_OK)
2059 goto err_exchangefile;
2060 of_rename(vol, s_of, sdir, spath, curdir, temp);
2062 /* rename destination to source */
2063 if ((err = renamefile(vol, -1, upath, p, spath, addp)) != AFP_OK)
2064 goto err_src_to_tmp;
2065 of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
2067 /* rename temp to destination */
2068 if ((err = renamefile(vol, -1, temp, upath, path->m_name, adsp)) != AFP_OK)
2069 goto err_dest_to_src;
2070 of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
2072 /* id's need switching. src -> dest and dest -> src.
2073 * we need to re-stat() if it was a cross device copy.
2076 cnid_delete(vol->v_cdb, sid);
2078 cnid_delete(vol->v_cdb, did);
2080 if ((did && ( (crossdev && lstat( upath, &srcst) < 0) ||
2081 cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
2083 (sid && ( (crossdev && lstat(p, &destst) < 0) ||
2084 cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
2089 err = AFPERR_ACCESS;
2094 goto err_temp_to_dest;
2097 /* here we need to reopen if crossdev */
2098 if (sid && ad_setid(addp, destst.st_dev, destst.st_ino, sid, sdir->d_did, vol->v_stamp))
2103 if (did && ad_setid(adsp, srcst.st_dev, srcst.st_ino, did, curdir->d_did, vol->v_stamp))
2108 /* change perms, src gets dest perm and vice versa */
2113 LOG(log_error, logtype_afpd, "seteuid failed %s", strerror(errno));
2114 err = AFP_OK; /* ignore error */
2115 goto err_temp_to_dest;
2119 * we need to exchange ACL entries as well
2121 /* exchange_acls(vol, p, upath); */
2126 path->m_name = NULL;
2127 path->u_name = upath;
2129 setfilunixmode(vol, path, destst.st_mode);
2130 setfilowner(vol, destst.st_uid, destst.st_gid, path);
2137 setfilunixmode(vol, path, srcst.st_mode);
2138 setfilowner(vol, srcst.st_uid, srcst.st_gid, path);
2140 if ( setegid(gid) < 0 || seteuid(uid) < 0) {
2141 LOG(log_error, logtype_afpd, "can't seteuid back %s", strerror(errno));
2146 goto err_exchangefile;
2148 /* all this stuff is so that we can unwind a failed operation
2151 /* rename dest to temp */
2152 renamefile(vol, -1, upath, temp, temp, adsp);
2153 of_rename(vol, s_of, curdir, upath, curdir, temp);
2156 /* rename source back to dest */
2157 renamefile(vol, -1, p, upath, path->m_name, addp);
2158 of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
2161 /* rename temp back to source */
2162 renamefile(vol, -1, temp, p, spath, adsp);
2163 of_rename(vol, s_of, curdir, temp, sdir, spath);
2166 if ( !s_of && adsp && ad_meta_fileno(adsp) != -1 ) { /* META */
2167 ad_close(adsp, ADFLAGS_HF);
2169 if ( !d_of && addp && ad_meta_fileno(addp) != -1 ) {/* META */
2170 ad_close(addp, ADFLAGS_HF);
2174 if ((cached = dircache_search_by_did(vol, sid)) != NULL)
2175 (void)dir_remove(vol, cached);
2176 if ((cached = dircache_search_by_did(vol, did)) != NULL)
2177 (void)dir_remove(vol, cached);