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);
734 if ((id = get_id(vol, &ad, &st, dir->d_did, upath, strlen(upath))) == CNID_INVALID) {
735 LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): CNID error", upath);
736 goto createfile_iderr;
738 (void)ad_setid(&ad, st.st_dev, st.st_ino, id, dir->d_did, vol->v_stamp);
742 ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF );
743 fce_register_new_file(s_path);
748 setvoltime(obj, vol );
753 int afp_setfilparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
759 uint16_t vid, bitmap;
764 memcpy(&vid, ibuf, sizeof( vid ));
765 ibuf += sizeof( vid );
766 if (NULL == ( vol = getvolbyvid( vid )) ) {
767 return( AFPERR_PARAM );
770 if (vol->v_flags & AFPVOL_RO)
773 memcpy(&did, ibuf, sizeof( did ));
774 ibuf += sizeof( did );
775 if (NULL == ( dir = dirlookup( vol, did )) ) {
776 return afp_errno; /* was AFPERR_NOOBJ */
779 memcpy(&bitmap, ibuf, sizeof( bitmap ));
780 bitmap = ntohs( bitmap );
781 ibuf += sizeof( bitmap );
783 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
784 return get_afp_errno(AFPERR_PARAM);
787 if (path_isadir(s_path)) {
788 return( AFPERR_BADTYPE ); /* it's a directory */
791 if ( s_path->st_errno != 0 ) {
792 return( AFPERR_NOOBJ );
795 if ((u_long)ibuf & 1 ) {
799 if (AFP_OK == ( rc = setfilparams(obj, vol, s_path, bitmap, ibuf )) ) {
800 setvoltime(obj, vol );
807 * cf AFP3.0.pdf page 252 for change_mdate and change_parent_mdate logic
810 extern struct path Cur_Path;
812 int setfilparams(const AFPObj *obj, struct vol *vol,
813 struct path *path, uint16_t f_bitmap, char *buf )
815 struct adouble ad, *adp;
817 int bit, isad = 1, err = AFP_OK;
819 u_char achar, *fdType, xyy[4]; /* uninitialized, OK 310105 */
820 uint16_t ashort, bshort, oshort;
823 uint16_t upriv_bit = 0;
825 int change_mdate = 0;
826 int change_parent_mdate = 0;
831 uint16_t bitmap = f_bitmap;
832 uint32_t cdate,bdate;
833 u_char finder_buf[32];
837 LOG(log_debug9, logtype_afpd, "begin setfilparams:");
840 adp = of_ad(vol, path, &ad);
841 upath = path->u_name;
843 if (!vol_unix_priv(vol) && check_access(obj, vol, upath, OPENACC_WR ) < 0) {
844 return AFPERR_ACCESS;
847 /* with unix priv maybe we have to change adouble file priv first */
849 while ( bitmap != 0 ) {
850 while (( bitmap & 1 ) == 0 ) {
857 memcpy(&ashort, buf, sizeof( ashort ));
858 buf += sizeof( ashort );
862 memcpy(&cdate, buf, sizeof(cdate));
863 buf += sizeof( cdate );
866 memcpy(&newdate, buf, sizeof( newdate ));
867 buf += sizeof( newdate );
871 memcpy(&bdate, buf, sizeof( bdate));
872 buf += sizeof( bdate );
876 memcpy(finder_buf, buf, 32 );
877 if (memcmp(buf, "slnkrhap", 8) == 0 && !S_ISLNK(path->st.st_mode)) {
881 char buf[PATH_MAX+1];
882 if ((fp = open(path->u_name, O_RDONLY)) >= 0) {
883 if ((len = read(fp, buf, PATH_MAX+1))) {
884 if (unlink(path->u_name) == 0) {
886 erc = symlink(buf, path->u_name);
895 goto setfilparam_done;
901 case FILPBIT_UNIXPR :
902 if (!vol_unix_priv(vol)) {
903 /* this volume doesn't use unix priv */
909 change_parent_mdate = 1;
911 memcpy( &aint, buf, sizeof( aint ));
912 f_uid = ntohl (aint);
913 buf += sizeof( aint );
914 memcpy( &aint, buf, sizeof( aint ));
915 f_gid = ntohl (aint);
916 buf += sizeof( aint );
917 setfilowner(vol, f_uid, f_gid, path);
919 memcpy( &upriv, buf, sizeof( upriv ));
920 buf += sizeof( upriv );
921 upriv = ntohl (upriv);
922 if ((upriv & S_IWUSR)) {
923 setfilunixmode(vol, path, upriv);
930 case FILPBIT_PDINFO :
931 if (obj->afp_version < 30) { /* else it's UTF8 name */
934 /* Keep special case to support crlf translations */
935 if ((unsigned int) achar == 0x04) {
936 fdType = (u_char *)"TEXT";
939 xyy[0] = ( u_char ) 'p';
950 /* break while loop */
959 /* second try with adouble open
961 if (ad_open(adp, upath, ADFLAGS_HF | ADFLAGS_RDWR | ADFLAGS_CREATE, 0666) < 0) {
962 LOG(log_debug, logtype_afpd, "setfilparams: ad_open_metadata error");
964 * For some things, we don't need an adouble header:
965 * - change of modification date
966 * - UNIX privs (Bug-ID #2863424)
968 if (!symlinked && f_bitmap & ~(1<<FILPBIT_MDATE | 1<<FILPBIT_UNIXPR)) {
969 LOG(log_debug, logtype_afpd, "setfilparams: need adouble access");
970 return AFPERR_ACCESS;
972 LOG(log_debug, logtype_afpd, "setfilparams: no adouble perms, but only FILPBIT_MDATE and/or FILPBIT_UNIXPR");
974 } else if ((ad_get_MD_flags( adp ) & O_CREAT) ) {
975 ad_setname(adp, path->m_name);
980 while ( bitmap != 0 ) {
981 while (( bitmap & 1 ) == 0 ) {
988 ad_getattr(adp, &bshort);
990 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
991 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
995 if ((bshort & htons(ATTRBIT_INVISIBLE)) != (oshort & htons(ATTRBIT_INVISIBLE)))
996 change_parent_mdate = 1;
997 ad_setattr(adp, bshort);
1000 ad_setdate(adp, AD_DATE_CREATE, cdate);
1002 case FILPBIT_MDATE :
1004 case FILPBIT_BDATE :
1005 ad_setdate(adp, AD_DATE_BACKUP, bdate);
1007 case FILPBIT_FINFO :
1008 memcpy(ad_entry( adp, ADEID_FINDERI ), finder_buf, 32 );
1010 case FILPBIT_UNIXPR :
1012 setfilunixmode(vol, path, upriv);
1015 case FILPBIT_PDINFO :
1016 if (obj->afp_version < 30) { /* else it's UTF8 name */
1017 memcpy(ad_entry( adp, ADEID_FINDERI ), fdType, 4 );
1018 memcpy(ad_entry( adp, ADEID_FINDERI ) + 4, "pdos", 4 );
1023 err = AFPERR_BITMAP;
1024 goto setfilparam_done;
1031 if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
1032 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1036 ad_setdate(adp, AD_DATE_MODIFY, newdate);
1037 ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
1043 ad_close(adp, ADFLAGS_HF);
1046 if (change_parent_mdate && gettimeofday(&tv, NULL) == 0) {
1047 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1048 bitmap = 1<<FILPBIT_MDATE;
1049 setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
1053 LOG(log_debug9, logtype_afpd, "end setfilparams:");
1059 * renamefile and copyfile take the old and new unix pathnames
1060 * and the new mac name.
1062 * sdir_fd source dir fd to which src path is relative (for openat et al semantics)
1063 * passing -1 means this is not used, src path is a full path
1064 * src the source path
1065 * dst the dest filename in current dir
1066 * newname the dest mac name
1067 * adp adouble struct of src file, if open, or & zeroed one
1070 int renamefile(const struct vol *vol, int sdir_fd, char *src, char *dst, char *newname, struct adouble *adp)
1074 LOG(log_debug, logtype_afpd,
1075 "renamefile: src[%d, \"%s\"] -> dst[\"%s\"]", sdir_fd, src, dst);
1077 if ( unix_rename( sdir_fd, src, -1, dst ) < 0 ) {
1080 return( AFPERR_NOOBJ );
1083 return( AFPERR_ACCESS );
1085 return AFPERR_VLOCK;
1086 case EXDEV : /* Cross device move -- try copy */
1087 /* NOTE: with open file it's an error because after the copy we will
1088 * get two files, it's fixable for our process (eg reopen the new file, get the
1089 * locks, and so on. But it doesn't solve the case with a second process
1091 if (adp->ad_open_forks) {
1092 /* FIXME warning in syslog so admin'd know there's a conflict ?*/
1093 return AFPERR_OLOCK; /* little lie */
1095 if (AFP_OK != ( rc = copyfile(vol, vol, sdir_fd, src, dst, newname, NULL )) ) {
1096 /* on error copyfile delete dest */
1099 return deletefile(vol, sdir_fd, src, 0);
1101 return( AFPERR_PARAM );
1105 if (vol->vfs->vfs_renamefile(vol, sdir_fd, src, dst) < 0 ) {
1109 /* try to undo the data fork rename,
1110 * we know we are on the same device
1113 unix_rename(-1, dst, sdir_fd, src );
1114 /* return the first error */
1117 return AFPERR_NOOBJ;
1120 return AFPERR_ACCESS ;
1122 return AFPERR_VLOCK;
1124 return AFPERR_PARAM ;
1129 /* don't care if we can't open the newly renamed ressource fork */
1130 if (ad_open(adp, dst, ADFLAGS_HF | ADFLAGS_RDWR) == 0) {
1131 ad_setname(adp, newname);
1133 ad_close( adp, ADFLAGS_HF );
1140 convert a Mac long name to an utf8 name,
1142 size_t mtoUTF8(const struct vol *vol, const char *src, size_t srclen, char *dest, size_t destlen)
1146 if ((size_t)-1 == (outlen = convert_string ( vol->v_maccharset, CH_UTF8_MAC, src, srclen, dest, destlen)) ) {
1152 /* ---------------- */
1153 int copy_path_name(const struct vol *vol, char *newname, char *ibuf)
1160 if ( type != 2 && !(vol->v_obj->afp_version >= 30 && type == 3) ) {
1166 if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
1167 if (vol->v_obj->afp_version >= 30) {
1168 /* convert it to UTF8
1170 if ((plen = mtoUTF8(vol, ibuf, plen, newname, AFPOBJ_TMPSIZ)) == (size_t)-1)
1174 strncpy( newname, ibuf, plen );
1175 newname[ plen ] = '\0';
1177 if (strlen(newname) != plen) {
1178 /* there's \0 in newname, e.g. it's a pathname not
1186 memcpy(&hint, ibuf, sizeof(hint));
1187 ibuf += sizeof(hint);
1189 memcpy(&len16, ibuf, sizeof(len16));
1190 ibuf += sizeof(len16);
1191 plen = ntohs(len16);
1194 if (plen > AFPOBJ_TMPSIZ) {
1197 strncpy( newname, ibuf, plen );
1198 newname[ plen ] = '\0';
1199 if (strlen(newname) != plen) {
1208 /* -----------------------------------
1210 int afp_copyfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1212 struct vol *s_vol, *d_vol;
1214 char *newname, *p, *upath;
1215 struct path *s_path;
1216 uint32_t sdid, ddid;
1217 int err, retvalue = AFP_OK;
1218 uint16_t svid, dvid;
1220 struct adouble ad, *adp;
1226 memcpy(&svid, ibuf, sizeof( svid ));
1227 ibuf += sizeof( svid );
1228 if (NULL == ( s_vol = getvolbyvid( svid )) ) {
1229 return( AFPERR_PARAM );
1232 memcpy(&sdid, ibuf, sizeof( sdid ));
1233 ibuf += sizeof( sdid );
1234 if (NULL == ( dir = dirlookup( s_vol, sdid )) ) {
1238 memcpy(&dvid, ibuf, sizeof( dvid ));
1239 ibuf += sizeof( dvid );
1240 memcpy(&ddid, ibuf, sizeof( ddid ));
1241 ibuf += sizeof( ddid );
1243 if (NULL == ( s_path = cname( s_vol, dir, &ibuf )) ) {
1244 return get_afp_errno(AFPERR_PARAM);
1246 if ( path_isadir(s_path) ) {
1247 return( AFPERR_BADTYPE );
1250 /* don't allow copies when the file is open.
1251 * XXX: the spec only calls for read/deny write access.
1252 * however, copyfile doesn't have any of that info,
1253 * and locks need to stay coherent. as a result,
1254 * we just balk if the file is opened already. */
1256 adp = of_ad(s_vol, s_path, &ad);
1258 if (ad_open(adp, s_path->u_name, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | ADFLAGS_RDONLY | ADFLAGS_SETSHRMD) < 0) {
1259 return AFPERR_DENYCONF;
1261 #ifdef HAVE_FSHARE_T
1263 shmd.f_access = F_RDACC;
1264 shmd.f_deny = F_NODNY;
1265 if (fcntl(ad_data_fileno(adp), F_SHARE, &shmd) != 0) {
1266 retvalue = AFPERR_DENYCONF;
1269 if (AD_RSRC_OPEN(adp) && fcntl(ad_reso_fileno(adp), F_SHARE, &shmd) != 0) {
1270 retvalue = AFPERR_DENYCONF;
1274 denyreadset = (ad_testlock(adp, ADEID_DFORK, AD_FILELOCK_DENY_RD) != 0 ||
1275 ad_testlock(adp, ADEID_RFORK, AD_FILELOCK_DENY_RD) != 0 );
1278 retvalue = AFPERR_DENYCONF;
1282 newname = obj->newtmp;
1283 strcpy( newname, s_path->m_name );
1285 p = ctoupath( s_vol, curdir, newname );
1287 retvalue = AFPERR_PARAM;
1291 if (NULL == ( d_vol = getvolbyvid( dvid )) ) {
1292 retvalue = AFPERR_PARAM;
1296 if (d_vol->v_flags & AFPVOL_RO) {
1297 retvalue = AFPERR_VLOCK;
1301 if (NULL == ( dir = dirlookup( d_vol, ddid )) ) {
1302 retvalue = afp_errno;
1306 if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) {
1307 retvalue = get_afp_errno(AFPERR_NOOBJ);
1311 if ( *s_path->m_name != '\0' ) {
1312 retvalue =path_error(s_path, AFPERR_NOOBJ);
1316 /* one of the handful of places that knows about the path type */
1317 if (copy_path_name(d_vol, newname, ibuf) < 0) {
1318 retvalue = AFPERR_PARAM;
1321 /* newname is always only a filename so curdir *is* its
1324 if (NULL == (upath = mtoupath(d_vol, newname, curdir->d_did, utf8_encoding(d_vol->v_obj)))) {
1325 retvalue =AFPERR_PARAM;
1329 if ( (err = copyfile(s_vol, d_vol, -1, p, upath , newname, adp)) < 0 ) {
1335 setvoltime(obj, d_vol );
1338 ad_close( adp, ADFLAGS_DF |ADFLAGS_HF | ADFLAGS_SETSHRMD);
1342 /* ----------------------------------
1343 * if newname is NULL (from directory.c) we don't want to copy the resource fork.
1344 * because we are doing it elsewhere.
1345 * currently if newname is NULL then adp is NULL.
1347 int copyfile(const struct vol *s_vol,
1348 const struct vol *d_vol,
1353 struct adouble *adp)
1355 struct adouble ads, add;
1362 LOG(log_debug, logtype_afpd, "copyfile(sfd:%d,s:'%s',d:'%s',n:'%s')",
1363 sfd, src, dst, newname);
1366 ad_init(&ads, s_vol);
1370 adflags = ADFLAGS_DF | ADFLAGS_RF | ADFLAGS_NORF;
1372 if (ad_openat(adp, sfd, src, adflags | ADFLAGS_RDONLY) < 0) {
1377 if (!AD_RSRC_OPEN(adp))
1378 /* no resource fork, don't create one for dst file */
1379 adflags &= ~ADFLAGS_RF;
1381 stat_result = fstat(ad_data_fileno(adp), &st); /* saving stat exit code, thus saving us on one more stat later on */
1383 if (stat_result < 0) {
1384 /* unlikely but if fstat fails, the default file mode will be 0666. */
1385 st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
1388 ad_init(&add, d_vol);
1389 if (ad_open(&add, dst, adflags | ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_EXCL, st.st_mode | S_IRUSR | S_IWUSR) < 0) {
1391 ad_close( adp, adflags );
1392 if (EEXIST != ret_err) {
1393 deletefile(d_vol, -1, dst, 0);
1396 return AFPERR_EXIST;
1399 if ((err = copy_fork(ADEID_DFORK, &add, adp)) != 0)
1400 LOG(log_error, logtype_afpd, "copyfile('%s'): %s", src, strerror(errno));
1403 if ((err = d_vol->vfs->vfs_copyfile(d_vol, sfd, src, dst)) != 0)
1404 LOG(log_error, logtype_afpd, "copyfile('%s'): %s", src, strerror(errno));
1409 if (!ret_err && newname && (adflags & ADFLAGS_HF)) {
1410 /* set the new name in the resource fork */
1411 ad_copy_header(&add, adp);
1412 ad_setname(&add, newname);
1415 ad_close( adp, adflags );
1417 if (ad_close( &add, adflags ) <0) {
1422 deletefile(d_vol, -1, dst, 0);
1424 else if (stat_result == 0) {
1425 /* set dest modification date to src date */
1428 ut.actime = ut.modtime = st.st_mtime;
1430 /* FIXME netatalk doesn't use resource fork file date
1431 * but maybe we should set its modtime too.
1436 switch ( ret_err ) {
1442 return AFPERR_DFULL;
1444 return AFPERR_NOOBJ;
1446 return AFPERR_ACCESS;
1448 return AFPERR_VLOCK;
1450 return AFPERR_PARAM;
1454 /* -----------------------------------
1455 vol: not NULL delete cnid entry. then we are in curdir and file is a only filename
1456 checkAttrib: 1 check kFPDeleteInhibitBit (deletfile called by afp_delete)
1458 when deletefile is called we don't have lock on it, file is closed (for us)
1459 untrue if called by renamefile
1461 ad_open always try to open file RDWR first and ad_lock takes care of
1462 WRITE lock on read only file.
1465 static int check_attrib(struct adouble *adp)
1467 uint16_t bshort = 0;
1469 ad_getattr(adp, &bshort);
1471 * Does kFPDeleteInhibitBit (bit 8) set?
1473 if ((bshort & htons(ATTRBIT_NODELETE))) {
1474 return AFPERR_OLOCK;
1476 if ((bshort & htons(ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
1482 * dirfd can be used for unlinkat semantics
1484 int deletefile(const struct vol *vol, int dirfd, char *file, int checkAttrib)
1487 struct adouble *adp = NULL;
1488 int adflags, err = AFP_OK;
1491 LOG(log_debug, logtype_afpd, "deletefile('%s')", file);
1495 /* was EACCESS error try to get only metadata */
1496 /* we never want to create a resource fork here, we are going to delete it
1497 * moreover sometimes deletefile is called with a no existent file and
1498 * ad_open would create a 0 byte resource fork
1500 if ( ad_metadataat(dirfd, file, ADFLAGS_CHECK_OF, &ad) == 0 ) {
1501 if ((err = check_attrib(&ad))) {
1502 ad_close(&ad, ADFLAGS_HF | ADFLAGS_CHECK_OF);
1509 /* try to open both forks at once */
1510 adflags = ADFLAGS_DF;
1511 if (ad_openat(&ad, dirfd, file, adflags | ADFLAGS_RF | ADFLAGS_NORF | ADFLAGS_RDONLY) < 0 ) {
1516 case EACCES: /* maybe it's a file with no write mode for us */
1517 break; /* was return AFPERR_ACCESS;*/
1530 if ( adp && AD_RSRC_OPEN(adp) != -1 ) { /* there's a resource fork */
1531 adflags |= ADFLAGS_RF;
1532 /* FIXME we have a pb here because we want to know if a file is open
1533 * there's a 'priority inversion' if you can't open the ressource fork RW
1534 * you can delete it if it's open because you can't get a write lock.
1536 * ADLOCK_FILELOCK means the whole ressource fork, not only after the
1539 * FIXME it doesn't work for RFORK open read only and fork open without deny mode
1541 if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
1547 if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
1548 LOG(log_error, logtype_afpd, "deletefile('%s'): ad_tmplock error: %s", file, strerror(errno));
1550 } else if (!(err = vol->vfs->vfs_deletefile(vol, dirfd, file)) && !(err = netatalk_unlinkat(dirfd, file )) ) {
1552 if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file)))) {
1553 cnid_delete(vol->v_cdb, id);
1559 ad_close(&ad, ADFLAGS_HF | ADFLAGS_CHECK_OF);
1562 ad_close( &ad, adflags ); /* ad_close removes locks if any */
1567 /* ------------------------------------ */
1568 /* return a file id */
1569 int afp_createid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1578 struct path *s_path;
1584 memcpy(&vid, ibuf, sizeof(vid));
1585 ibuf += sizeof(vid);
1587 if (NULL == ( vol = getvolbyvid( vid )) ) {
1588 return( AFPERR_PARAM);
1591 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1595 if (vol->v_flags & AFPVOL_RO)
1596 return AFPERR_VLOCK;
1598 memcpy(&did, ibuf, sizeof( did ));
1599 ibuf += sizeof(did);
1601 if (NULL == ( dir = dirlookup( vol, did )) ) {
1602 return afp_errno; /* was AFPERR_PARAM */
1605 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
1606 return get_afp_errno(AFPERR_NOOBJ); /* was AFPERR_PARAM */
1609 if ( path_isadir(s_path) ) {
1610 return( AFPERR_BADTYPE );
1613 upath = s_path->u_name;
1614 switch (s_path->st_errno) {
1616 break; /* success */
1619 return AFPERR_ACCESS;
1621 return AFPERR_NOOBJ;
1623 return AFPERR_PARAM;
1626 if ((id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath)))) {
1627 memcpy(rbuf, &id, sizeof(id));
1628 *rbuflen = sizeof(id);
1629 return AFPERR_EXISTID;
1632 if ((id = get_id(vol, NULL, st, did, upath, len)) != CNID_INVALID) {
1633 memcpy(rbuf, &id, sizeof(id));
1634 *rbuflen = sizeof(id);
1641 /* ------------------------------- */
1647 static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
1650 struct reenum *param = data;
1651 struct vol *vol = param->vol;
1652 cnid_t did = param->did;
1655 if ( lstat(de->d_name, &path.st) < 0 )
1658 /* update or add to cnid */
1659 aint = cnid_add(vol->v_cdb, &path.st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */
1664 /* --------------------
1665 * Ok the db is out of synch with the dir.
1666 * but if it's a deleted file we don't want to do it again and again.
1669 reenumerate_id(struct vol *vol, char *name, struct dir *dir)
1675 if (vol->v_cdb == NULL) {
1679 /* FIXME use of_statdir ? */
1680 if (lstat(name, &st)) {
1684 if (dirreenumerate(dir, &st)) {
1685 /* we already did it once and the dir haven't been modified */
1686 return dir->d_offcnt;
1690 data.did = dir->d_did;
1691 if ((ret = for_each_dirent(vol, name, reenumerate_loop, (void *)&data)) >= 0) {
1692 setdiroffcnt(curdir, &st, ret);
1693 dir->d_flags |= DIRF_CNID;
1699 /* ------------------------------
1700 resolve a file id */
1701 int afp_resolveid(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1710 uint16_t vid, bitmap;
1712 static char buffer[12 + MAXPATHLEN + 1];
1713 int len = 12 + MAXPATHLEN + 1;
1718 memcpy(&vid, ibuf, sizeof(vid));
1719 ibuf += sizeof(vid);
1721 if (NULL == ( vol = getvolbyvid( vid )) ) {
1722 return( AFPERR_PARAM);
1725 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1729 memcpy(&id, ibuf, sizeof( id ));
1734 /* some MacOS versions after a catsearch do a *lot* of afp_resolveid with 0 */
1738 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1739 return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
1742 if (NULL == ( dir = dirlookup( vol, id )) ) {
1743 return AFPERR_NOID; /* idem AFPERR_PARAM */
1745 if (movecwd(vol, dir) < 0) {
1749 return AFPERR_ACCESS;
1753 return AFPERR_PARAM;
1757 memset(&path, 0, sizeof(path));
1758 path.u_name = upath;
1759 if ( of_stat(&path) < 0 ) {
1761 /* with nfs and our working directory is deleted */
1762 if (errno == ESTALE) {
1766 if ( errno == ENOENT && !retry) {
1767 /* cnid db is out of sync, reenumerate the directory and update ids */
1768 reenumerate_id(vol, ".", dir);
1776 return AFPERR_ACCESS;
1780 return AFPERR_PARAM;
1784 /* directories are bad */
1785 if (S_ISDIR(path.st.st_mode)) {
1786 /* OS9 and OSX don't return the same error code */
1787 return (obj->afp_version >=30)?AFPERR_NOID:AFPERR_BADTYPE;
1790 memcpy(&bitmap, ibuf, sizeof(bitmap));
1791 bitmap = ntohs( bitmap );
1792 if (NULL == (path.m_name = utompath(vol, upath, cnid, utf8_encoding(obj)))) {
1796 if (AFP_OK != (err = getfilparams(obj, vol, bitmap, &path , curdir,
1797 rbuf + sizeof(bitmap), &buflen, 0))) {
1800 *rbuflen = buflen + sizeof(bitmap);
1801 memcpy(rbuf, ibuf, sizeof(bitmap));
1806 /* ------------------------------ */
1807 int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1817 static char buffer[12 + MAXPATHLEN + 1];
1818 int len = 12 + MAXPATHLEN + 1;
1823 memcpy(&vid, ibuf, sizeof(vid));
1824 ibuf += sizeof(vid);
1826 if (NULL == ( vol = getvolbyvid( vid )) ) {
1827 return( AFPERR_PARAM);
1830 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1834 if (vol->v_flags & AFPVOL_RO)
1835 return AFPERR_VLOCK;
1837 memcpy(&id, ibuf, sizeof( id ));
1841 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1845 if (NULL == ( dir = dirlookup( vol, id )) ) {
1846 if (afp_errno == AFPERR_NOOBJ) {
1850 return( AFPERR_PARAM );
1854 if ((movecwd(vol, dir) < 0) || (lstat(upath, &st) < 0)) {
1858 return AFPERR_ACCESS;
1863 /* still try to delete the id */
1867 return AFPERR_PARAM;
1870 else if (S_ISDIR(st.st_mode)) /* directories are bad */
1871 return AFPERR_BADTYPE;
1874 if (cnid_delete(vol->v_cdb, fileid)) {
1877 return AFPERR_VLOCK;
1880 return AFPERR_ACCESS;
1882 return AFPERR_PARAM;
1889 /* ------------------------------ */
1890 static struct adouble *find_adouble(const AFPObj *obj, struct vol *vol, struct path *path, struct ofork **of, struct adouble *adp)
1894 if (path->st_errno) {
1895 switch (path->st_errno) {
1897 afp_errno = AFPERR_NOID;
1901 afp_errno = AFPERR_ACCESS;
1904 afp_errno = AFPERR_PARAM;
1909 /* we use file_access both for legacy Mac perm and
1910 * for unix privilege, rename will take care of folder perms
1912 if (file_access(obj, vol, path, OPENACC_WR ) < 0) {
1913 afp_errno = AFPERR_ACCESS;
1917 if ((*of = of_findname(path))) {
1918 /* reuse struct adouble so it won't break locks */
1922 ret = ad_open(adp, path->u_name, ADFLAGS_HF | ADFLAGS_RDWR);
1924 if ( !ret && ad_reso_fileno(adp) != -1 && !(adp->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
1926 * The user must have the Read & Write privilege for both files in order to use this command.
1928 ad_close(adp, ADFLAGS_HF);
1929 afp_errno = AFPERR_ACCESS;
1936 #define APPLETEMP ".AppleTempXXXXXX"
1938 int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1940 struct stat srcst, destst;
1942 struct dir *dir, *sdir;
1943 char *spath, temp[17], *p;
1944 char *supath, *upath;
1949 struct adouble *adsp = NULL;
1950 struct adouble *addp = NULL;
1951 struct ofork *s_of = NULL;
1952 struct ofork *d_of = NULL;
1965 memcpy(&vid, ibuf, sizeof(vid));
1966 ibuf += sizeof(vid);
1968 if (NULL == ( vol = getvolbyvid( vid )) ) {
1969 return( AFPERR_PARAM);
1972 if ((vol->v_flags & AFPVOL_RO))
1973 return AFPERR_VLOCK;
1975 /* source and destination dids */
1976 memcpy(&sid, ibuf, sizeof(sid));
1977 ibuf += sizeof(sid);
1978 memcpy(&did, ibuf, sizeof(did));
1979 ibuf += sizeof(did);
1982 if (NULL == (dir = dirlookup( vol, sid )) ) {
1983 return afp_errno; /* was AFPERR_PARAM */
1986 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
1987 return get_afp_errno(AFPERR_NOOBJ);
1990 if ( path_isadir(path) ) {
1991 return AFPERR_BADTYPE; /* it's a dir */
1994 /* save some stuff */
1997 spath = obj->oldtmp;
1998 supath = obj->newtmp;
1999 strcpy(spath, path->m_name);
2000 strcpy(supath, path->u_name); /* this is for the cnid changing */
2001 p = absupath( vol, sdir, supath);
2003 /* pathname too long */
2004 return AFPERR_PARAM ;
2008 if (!(adsp = find_adouble(obj, vol, path, &s_of, &ads))) {
2012 /* ***** from here we may have resource fork open **** */
2014 /* look for the source cnid. if it doesn't exist, don't worry about
2016 sid = cnid_lookup(vol->v_cdb, &srcst, sdir->d_did, supath,slen = strlen(supath));
2018 if (NULL == ( dir = dirlookup( vol, did )) ) {
2019 err = afp_errno; /* was AFPERR_PARAM */
2020 goto err_exchangefile;
2023 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2024 err = get_afp_errno(AFPERR_NOOBJ);
2025 goto err_exchangefile;
2028 if ( path_isadir(path) ) {
2029 err = AFPERR_BADTYPE;
2030 goto err_exchangefile;
2033 /* FPExchangeFiles is the only call that can return the SameObj
2035 if ((curdir == sdir) && strcmp(spath, path->m_name) == 0) {
2036 err = AFPERR_SAMEOBJ;
2037 goto err_exchangefile;
2041 if (!(addp = find_adouble(obj, vol, path, &d_of, &add))) {
2043 goto err_exchangefile;
2047 /* they are not on the same device and at least one is open
2048 * FIXME broken for for crossdev and adouble v2
2051 crossdev = (srcst.st_dev != destst.st_dev);
2052 if (/* (d_of || s_of) && */ crossdev) {
2054 goto err_exchangefile;
2057 /* look for destination id. */
2058 upath = path->u_name;
2059 did = cnid_lookup(vol->v_cdb, &destst, curdir->d_did, upath, dlen = strlen(upath));
2061 /* construct a temp name.
2062 * NOTE: the temp file will be in the dest file's directory. it
2063 * will also be inaccessible from AFP. */
2064 memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
2065 if (!mktemp(temp)) {
2067 goto err_exchangefile;
2071 /* FIXME we need to close fork for copy, both s_of and d_of are null */
2072 ad_close(adsp, ADFLAGS_HF);
2073 ad_close(addp, ADFLAGS_HF);
2076 /* now, quickly rename the file. we error if we can't. */
2077 if ((err = renamefile(vol, -1, p, temp, temp, adsp)) != AFP_OK)
2078 goto err_exchangefile;
2079 of_rename(vol, s_of, sdir, spath, curdir, temp);
2081 /* rename destination to source */
2082 if ((err = renamefile(vol, -1, upath, p, spath, addp)) != AFP_OK)
2083 goto err_src_to_tmp;
2084 of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
2086 /* rename temp to destination */
2087 if ((err = renamefile(vol, -1, temp, upath, path->m_name, adsp)) != AFP_OK)
2088 goto err_dest_to_src;
2089 of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
2091 /* id's need switching. src -> dest and dest -> src.
2092 * we need to re-stat() if it was a cross device copy.
2095 cnid_delete(vol->v_cdb, sid);
2097 cnid_delete(vol->v_cdb, did);
2099 if ((did && ( (crossdev && lstat( upath, &srcst) < 0) ||
2100 cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
2102 (sid && ( (crossdev && lstat(p, &destst) < 0) ||
2103 cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
2108 err = AFPERR_ACCESS;
2113 goto err_temp_to_dest;
2116 /* here we need to reopen if crossdev */
2117 if (sid && ad_setid(addp, destst.st_dev, destst.st_ino, sid, sdir->d_did, vol->v_stamp))
2122 if (did && ad_setid(adsp, srcst.st_dev, srcst.st_ino, did, curdir->d_did, vol->v_stamp))
2127 /* change perms, src gets dest perm and vice versa */
2132 LOG(log_error, logtype_afpd, "seteuid failed %s", strerror(errno));
2133 err = AFP_OK; /* ignore error */
2134 goto err_temp_to_dest;
2138 * we need to exchange ACL entries as well
2140 /* exchange_acls(vol, p, upath); */
2145 path->m_name = NULL;
2146 path->u_name = upath;
2148 setfilunixmode(vol, path, destst.st_mode);
2149 setfilowner(vol, destst.st_uid, destst.st_gid, path);
2156 setfilunixmode(vol, path, srcst.st_mode);
2157 setfilowner(vol, srcst.st_uid, srcst.st_gid, path);
2159 if ( setegid(gid) < 0 || seteuid(uid) < 0) {
2160 LOG(log_error, logtype_afpd, "can't seteuid back %s", strerror(errno));
2165 goto err_exchangefile;
2167 /* all this stuff is so that we can unwind a failed operation
2170 /* rename dest to temp */
2171 renamefile(vol, -1, upath, temp, temp, adsp);
2172 of_rename(vol, s_of, curdir, upath, curdir, temp);
2175 /* rename source back to dest */
2176 renamefile(vol, -1, p, upath, path->m_name, addp);
2177 of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
2180 /* rename temp back to source */
2181 renamefile(vol, -1, temp, p, spath, adsp);
2182 of_rename(vol, s_of, curdir, temp, sdir, spath);
2185 if ( !s_of && adsp && ad_meta_fileno(adsp) != -1 ) { /* META */
2186 ad_close(adsp, ADFLAGS_HF);
2188 if ( !d_of && addp && ad_meta_fileno(addp) != -1 ) {/* META */
2189 ad_close(addp, ADFLAGS_HF);
2193 if ((cached = dircache_search_by_did(vol, sid)) != NULL)
2194 (void)dir_remove(vol, cached);
2195 if ((cached = dircache_search_by_did(vol, did)) != NULL)
2196 (void)dir_remove(vol, cached);