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>
28 #include <atalk/spotlight.h>
30 #include "directory.h"
39 /* the format for the finderinfo fields (from IM: Toolbox Essentials):
40 * field bytes subfield bytes
43 * ioFlFndrInfo 16 -> type 4 type field
44 * creator 4 creator field
45 * flags 2 finder flags:
47 * location 4 location in window
48 * folder 2 window that contains file
50 * ioFlXFndrInfo 16 -> iconID 2 icon id
52 * script 1 script system
54 * commentID 2 comment id
55 * putawayID 4 home directory id
58 const u_char ufinderi[ADEDLEN_FINDERI] = {
59 0, 0, 0, 0, 0, 0, 0, 0,
60 1, 0, 0, 0, 0, 0, 0, 0,
61 0, 0, 0, 0, 0, 0, 0, 0,
62 0, 0, 0, 0, 0, 0, 0, 0
65 static const u_char old_ufinderi[] = {
66 'T', 'E', 'X', 'T', 'U', 'N', 'I', 'X'
69 /* ----------------------
71 static int default_type(void *finder)
73 if (!memcmp(finder, ufinderi, 8) || !memcmp(finder, old_ufinderi, 8))
78 /* FIXME path : unix or mac name ? (for now it's unix name ) */
79 void *get_finderinfo(const struct vol *vol, const char *upath, struct adouble *adp, void *data, int islink)
82 void *ad_finder = NULL;
86 ad_finder = ad_entry(adp, ADEID_FINDERI);
89 memcpy(data, ad_finder, ADEDLEN_FINDERI);
91 if (default_type(ad_finder))
95 memcpy(data, ufinderi, ADEDLEN_FINDERI);
97 if (vol_inv_dots(vol) && *upath == '.') { /* make it invisible */
100 ashort = htons(FINDERINFO_INVISIBLE);
101 memcpy((char *)data + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
105 if (islink && !vol_syml_opt(vol)) {
107 memcpy(&linkflag, (char *)data + FINDERINFO_FRFLAGOFF, 2);
108 linkflag |= htons(FINDERINFO_ISALIAS);
109 memcpy((char *)data + FINDERINFO_FRFLAGOFF, &linkflag, 2);
110 memcpy((char *)data + FINDERINFO_FRTYPEOFF,"slnk",4);
111 memcpy((char *)data + FINDERINFO_FRCREATOFF,"rhap",4);
114 /** Only enter if no appledouble information and no finder information found. */
115 if (chk_ext && (em = getextmap( upath ))) {
116 memcpy(data, em->em_type, sizeof( em->em_type ));
117 memcpy((char *)data + 4, em->em_creator, sizeof(em->em_creator));
123 /* ---------------------
125 char *set_name(const struct vol *vol, char *data, cnid_t pid, char *name, cnid_t id, uint32_t utf8)
130 aint = strlen( name );
134 if (utf8_encoding(vol->v_obj)) {
135 /* but name is an utf8 mac name */
138 /* global static variable... */
140 if (!(u = mtoupath(vol, name, pid, 1)) || !(m = utompath(vol, u, id, 0))) {
149 if (aint > MACFILELEN)
156 if (aint > UTF8FILELEN_EARLY) /* FIXME safeguard, anyway if no ascii char it's game over*/
157 aint = UTF8FILELEN_EARLY;
159 utf8 = vol->v_kTextEncoding;
160 memcpy(data, &utf8, sizeof(utf8));
161 data += sizeof(utf8);
164 memcpy(data, &temp, sizeof(temp));
165 data += sizeof(temp);
168 memcpy( data, src, aint );
178 * FIXME: PDINFO is UTF8 and doesn't need adp
180 #define PARAM_NEED_ADP(b) ((b) & ((1 << FILPBIT_ATTR) |\
181 (1 << FILPBIT_CDATE) |\
182 (1 << FILPBIT_MDATE) |\
183 (1 << FILPBIT_BDATE) |\
184 (1 << FILPBIT_FINFO) |\
185 (1 << FILPBIT_RFLEN) |\
186 (1 << FILPBIT_EXTRFLEN) |\
187 (1 << FILPBIT_PDINFO) |\
188 (1 << FILPBIT_FNUM) |\
189 (1 << FILPBIT_UNIXPR)))
192 * @brief Get CNID for did/upath args both from database and adouble file
194 * 1. Get the objects CNID as stored in its adouble file
195 * 2. Get the objects CNID from the database
196 * 3. If there's a problem with a "dbd" database, fallback to "tdb" in memory
197 * 4. In case 2 and 3 differ, store 3 in the adouble file
199 * @param vol (rw) volume
200 * @param adp (rw) adouble struct of object upath, might be NULL
201 * @param st (r) stat of upath, must NOT be NULL
202 * @param did (r) parent CNID of upath
203 * @param upath (r) name of object
204 * @param len (r) strlen of upath
206 uint32_t get_id(struct vol *vol,
208 const struct stat *st,
213 static int first = 1; /* mark if this func is called the first time */
215 uint32_t dbcnid = CNID_INVALID;
218 if (vol->v_cdb != NULL) {
219 /* prime aint with what we think is the cnid, set did to zero for
220 catching moved files */
221 adcnid = ad_getid(adp, st->st_dev, st->st_ino, 0, vol->v_stamp); /* (1) */
223 AFP_CNID_START("cnid_add");
224 dbcnid = cnid_add(vol->v_cdb, st, did, upath, len, adcnid); /* (2) */
227 /* Throw errors if cnid_add fails. */
228 if (dbcnid == CNID_INVALID) {
230 case CNID_ERR_CLOSE: /* the db is closed */
233 LOG(log_error, logtype_afpd, "get_id: Incorrect parameters passed to cnid_add");
234 afp_errno = AFPERR_PARAM;
237 afp_errno = AFPERR_PARAM;
240 /* Close CNID backend if "dbd" and switch to temp in-memory "tdb" */
241 /* we have to do it here for "dbd" because it uses "lazy opening" */
242 /* In order to not end in a loop somehow with goto restart below */
244 if (first && (strcmp(vol->v_cnidscheme, "dbd") == 0)) { /* (3) */
245 cnid_close(vol->v_cdb);
246 free(vol->v_cnidscheme);
247 vol->v_cnidscheme = strdup("tdb");
249 int flags = CNID_FLAG_MEMORY;
250 if ((vol->v_flags & AFPVOL_NODEV)) {
251 flags |= CNID_FLAG_NODEV;
253 LOG(log_error, logtype_afpd, "Reopen volume %s using in memory temporary CNID DB.",
255 vol->v_cdb = cnid_open(vol->v_path, vol->v_umask, "tdb", flags, NULL, NULL);
257 if (!(vol->v_flags & AFPVOL_TM)) {
258 vol->v_flags |= AFPVOL_RO;
259 setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB instead."
260 "Check server messages for details. Switching to read-only mode.");
261 kill(getpid(), SIGUSR2);
263 goto restart; /* now try again with the temp CNID db */
265 setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB failed too!"
266 "Check server messages for details, can't recover from this state!");
269 afp_errno = AFPERR_MISC;
273 else if (adp && adcnid && (adcnid != dbcnid)) { /* 4 */
274 /* Update the ressource fork. For a folder adp is always null */
275 LOG(log_debug, logtype_afpd, "get_id(%s/%s): calling ad_setid(old: %u, new: %u)",
276 getcwdpath(), upath, htonl(adcnid), htonl(dbcnid));
277 if (ad_setid(adp, st->st_dev, st->st_ino, dbcnid, did, vol->v_stamp)) {
278 if (ad_flush(adp) != 0)
279 LOG(log_error, logtype_afpd, "get_id(\"%s\"): can't flush", fullpathname(upath));
289 /* -------------------------- */
290 int getmetadata(const AFPObj *obj,
293 struct path *path, struct dir *dir,
294 char *buf, size_t *buflen, struct adouble *adp)
296 char *data, *l_nameoff = NULL, *upath;
297 char *utf_nameoff = NULL;
302 u_char achar, fdType[4];
307 LOG(log_debug, logtype_afpd, "getmetadata(\"%s\")", path->u_name);
309 upath = path->u_name;
313 if ( ((bitmap & ( (1 << FILPBIT_FINFO)|(1 << FILPBIT_LNAME)|(1 <<FILPBIT_PDINFO) ) ) && !path->m_name)
314 || (bitmap & ( (1 << FILPBIT_LNAME) ) && utf8_encoding(obj)) /* FIXME should be m_name utf8 filename */
315 || (bitmap & (1 << FILPBIT_FNUM))) {
318 struct dir *cachedfile;
319 int len = strlen(upath);
320 if ((cachedfile = dircache_search_by_name(vol, dir, upath, len)) != NULL)
321 id = cachedfile->d_did;
323 id = get_id(vol, adp, st, dir->d_did, upath, len);
325 /* Add it to the cache */
326 LOG(log_debug, logtype_afpd, "getmetadata: caching: did:%u, \"%s\", cnid:%u",
327 ntohl(dir->d_did), upath, ntohl(id));
329 /* Get macname from unixname first */
330 if (path->m_name == NULL) {
331 if ((path->m_name = utompath(vol, upath, id, utf8_encoding(obj))) == NULL) {
332 LOG(log_error, logtype_afpd, "getmetadata: utompath error");
338 if (((fullpath = bstrcpy(dir->d_fullpath)) == NULL)
339 || (bconchar(fullpath, '/') != BSTR_OK)
340 || (bcatcstr(fullpath, upath)) != BSTR_OK) {
341 LOG(log_error, logtype_afpd, "getmetadata: fullpath: %s", strerror(errno));
346 if ((cachedfile = dir_new(path->m_name, upath, vol, dir->d_did, id, fullpath, st)) == NULL) {
347 LOG(log_error, logtype_afpd, "getmetadata: error from dir_new");
351 if ((dircache_add(vol, cachedfile)) != 0) {
352 LOG(log_error, logtype_afpd, "getmetadata: fatal dircache error");
360 if (id == CNID_INVALID)
364 path->m_name = utompath(vol, upath, id, utf8_encoding(vol->v_obj));
367 while ( bitmap != 0 ) {
368 while (( bitmap & 1 ) == 0 ) {
376 ad_getattr(adp, &ashort);
377 } else if (vol_inv_dots(vol) && *upath == '.') {
378 ashort = htons(ATTRBIT_INVISIBLE);
382 /* FIXME do we want a visual clue if the file is read only
385 accessmode(vol, ".", &ma, dir , NULL);
386 if ((ma.ma_user & AR_UWRITE)) {
387 accessmode(vol, upath, &ma, dir , st);
388 if (!(ma.ma_user & AR_UWRITE)) {
389 ashort |= htons(ATTRBIT_NOWRITE);
393 memcpy(data, &ashort, sizeof( ashort ));
394 data += sizeof( ashort );
395 LOG(log_debug, logtype_afpd, "metadata('%s'): AFP Attributes: %04x",
396 path->u_name, ntohs(ashort));
400 memcpy(data, &dir->d_did, sizeof( uint32_t ));
401 data += sizeof( uint32_t );
402 LOG(log_debug, logtype_afpd, "metadata('%s'): Parent DID: %u",
403 path->u_name, ntohl(dir->d_did));
407 if (!adp || (ad_getdate(adp, AD_DATE_CREATE, &aint) < 0))
408 aint = AD_DATE_FROM_UNIX(st->st_mtime);
409 memcpy(data, &aint, sizeof( aint ));
410 data += sizeof( aint );
414 if ( adp && (ad_getdate(adp, AD_DATE_MODIFY, &aint) == 0)) {
415 if ((st->st_mtime > AD_DATE_TO_UNIX(aint))) {
416 aint = AD_DATE_FROM_UNIX(st->st_mtime);
419 aint = AD_DATE_FROM_UNIX(st->st_mtime);
421 memcpy(data, &aint, sizeof( int ));
422 data += sizeof( int );
426 if (!adp || (ad_getdate(adp, AD_DATE_BACKUP, &aint) < 0))
427 aint = AD_DATE_START;
428 memcpy(data, &aint, sizeof( int ));
429 data += sizeof( int );
433 get_finderinfo(vol, upath, adp, (char *)data,S_ISLNK(st->st_mode));
434 data += ADEDLEN_FINDERI;
439 data += sizeof( uint16_t );
443 memset(data, 0, sizeof(uint16_t));
444 data += sizeof( uint16_t );
448 memcpy(data, &id, sizeof( id ));
449 data += sizeof( id );
450 LOG(log_debug, logtype_afpd, "metadata('%s'): CNID: %u",
451 path->u_name, ntohl(id));
455 if (st->st_size > 0xffffffff)
458 aint = htonl( st->st_size );
459 memcpy(data, &aint, sizeof( aint ));
460 data += sizeof( aint );
463 case FILPBIT_RFLEN: {
466 if (adp->ad_rlen > 0xffffffff)
469 aint = htonl( adp->ad_rlen);
471 rlen = ad_reso_size(path->u_name, 0, NULL);
472 if (rlen > 0xffffffff)
476 memcpy(data, &aint, sizeof( aint ));
477 data += sizeof( aint );
481 /* Current client needs ProDOS info block for this file.
482 Use simple heuristic and let the Mac "type" string tell
483 us what the PD file code should be. Everything gets a
484 subtype of 0x0000 unless the original value was hashed
485 to "pXYZ" when we created it. See IA, Ver 2.
486 <shirsch@adelphia.net> */
487 case FILPBIT_PDINFO :
488 if (obj->afp_version >= 30) { /* UTF8 name */
489 utf8 = kTextEncodingUTF8;
491 data += sizeof( uint16_t );
493 memcpy(data, &aint, sizeof( aint ));
494 data += sizeof( aint );
498 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
500 if ( memcmp( fdType, "TEXT", 4 ) == 0 ) {
504 else if ( memcmp( fdType, "PSYS", 4 ) == 0 ) {
508 else if ( memcmp( fdType, "PS16", 4 ) == 0 ) {
512 else if ( memcmp( fdType, "BINA", 4 ) == 0 ) {
516 else if ( fdType[0] == 'p' ) {
518 ashort = (fdType[2] * 256) + fdType[3];
532 memcpy(data, &ashort, sizeof( ashort ));
533 data += sizeof( ashort );
534 memset(data, 0, sizeof( ashort ));
535 data += sizeof( ashort );
538 case FILPBIT_EXTDFLEN:
539 aint = htonl(st->st_size >> 32);
540 memcpy(data, &aint, sizeof( aint ));
541 data += sizeof( aint );
542 aint = htonl(st->st_size);
543 memcpy(data, &aint, sizeof( aint ));
544 data += sizeof( aint );
546 case FILPBIT_EXTRFLEN:
548 aint = htonl(adp->ad_rlen >> 32);
549 memcpy(data, &aint, sizeof( aint ));
550 data += sizeof( aint );
551 aint = htonl(adp->ad_rlen);
552 memcpy(data, &aint, sizeof( aint ));
553 data += sizeof( aint );
555 int64_t rlen = hton64(ad_reso_size(path->u_name, 0, NULL));
556 memcpy(data, &rlen, sizeof(rlen));
557 data += sizeof(rlen);
560 case FILPBIT_UNIXPR :
561 /* accessmode may change st_mode with ACLs */
562 accessmode(obj, vol, upath, &ma, dir , st);
564 aint = htonl(st->st_uid);
565 memcpy( data, &aint, sizeof( aint ));
566 data += sizeof( aint );
567 aint = htonl(st->st_gid);
568 memcpy( data, &aint, sizeof( aint ));
569 data += sizeof( aint );
572 type == slnk indicates an OSX style symlink,
573 we have to add S_IFLNK to the mode, otherwise
574 10.3 clients freak out. */
578 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
579 if ( memcmp( fdType, "slnk", 4 ) == 0 ) {
585 memcpy( data, &aint, sizeof( aint ));
586 data += sizeof( aint );
588 *data++ = ma.ma_user;
589 *data++ = ma.ma_world;
590 *data++ = ma.ma_group;
591 *data++ = ma.ma_owner;
595 return( AFPERR_BITMAP );
601 ashort = htons( data - buf );
602 memcpy(l_nameoff, &ashort, sizeof( ashort ));
603 data = set_name(vol, data, dir->d_did, path->m_name, id, 0);
606 ashort = htons( data - buf );
607 memcpy(utf_nameoff, &ashort, sizeof( ashort ));
608 data = set_name(vol, data, dir->d_did, path->m_name, id, utf8);
610 *buflen = data - buf;
614 /* ----------------------- */
615 int getfilparams(const AFPObj *obj, struct vol *vol, uint16_t bitmap, struct path *path,
616 struct dir *dir, char *buf, size_t *buflen, int in_enumerate)
618 struct adouble ad, *adp;
621 int flags; /* uninitialized ok */
623 LOG(log_debug, logtype_afpd, "getfilparams(\"%s\")", path->u_name);
625 opened = PARAM_NEED_ADP(bitmap);
631 * Dont check for and resturn open fork attributes when enumerating
632 * This saves a lot of syscalls, the client will hopefully only use the result
633 * in FPGetFileParms where we return the correct value
635 flags = (!in_enumerate &&(bitmap & (1 << FILPBIT_ATTR))) ? ADFLAGS_CHECK_OF : 0;
637 adp = of_ad(vol, path, &ad);
638 upath = path->u_name;
640 if ( ad_metadata( upath, flags, adp) < 0 ) {
643 LOG(log_error, logtype_afpd, "getfilparams(%s): %s: check resource fork permission?",
644 upath, strerror(errno));
645 return AFPERR_ACCESS;
647 LOG(log_error, logtype_afpd, "getfilparams(%s): bad resource fork", upath);
656 rc = getmetadata(obj, vol, bitmap, path, dir, buf, buflen, adp);
659 ad_close(adp, ADFLAGS_HF | flags);
664 /* ----------------------------- */
665 int afp_createfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
670 struct ofork *of = NULL;
672 int creatf, did, openf, retvalue = AFP_OK;
678 creatf = (unsigned char) *ibuf++;
680 memcpy(&vid, ibuf, sizeof( vid ));
681 ibuf += sizeof( vid );
683 if (NULL == ( vol = getvolbyvid( vid )) )
684 return( AFPERR_PARAM );
686 if (vol->v_flags & AFPVOL_RO)
689 memcpy(&did, ibuf, sizeof( did));
690 ibuf += sizeof( did );
692 if (NULL == ( dir = dirlookup( vol, did )) )
695 if (NULL == ( s_path = cname( vol, dir, &ibuf )) )
696 return get_afp_errno(AFPERR_PARAM);
697 if ( *s_path->m_name == '\0' )
698 return( AFPERR_BADTYPE );
700 upath = s_path->u_name;
703 /* if upath is deleted we already in trouble anyway */
704 if ((of = of_findname(vol, s_path))) {
712 openf = ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_TRUNC;
714 /* on a soft create, if the file is open then ad_open won't fail
715 because open syscall is not called */
716 openf = ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_EXCL;
718 if (ad_open(&ad, upath, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | openf, 0666) < 0) {
722 case ENOENT : /* we were already in 'did folder' so chdir() didn't fail */
723 return ( AFPERR_NOOBJ );
725 return( AFPERR_EXIST );
727 return( AFPERR_ACCESS );
730 LOG(log_info, logtype_afpd, "afp_createfile: DISK FULL");
731 return( AFPERR_DFULL );
733 return( AFPERR_PARAM );
736 if ( ad_meta_fileno( &ad ) == -1 ) { /* Hard META / HF */
737 /* FIXME with hard create on an existing file, we already
738 * corrupted the data file.
740 netatalk_unlink( upath );
741 ad_close( &ad, ADFLAGS_DF );
742 return AFPERR_ACCESS;
745 path = s_path->m_name;
746 ad_setname(&ad, path);
749 if (lstat(upath, &st) != 0) {
750 LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): stat: %s",
751 upath, strerror(errno));
752 ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF);
757 if ((id = get_id(vol, &ad, &st, dir->d_did, upath, strlen(upath))) == CNID_INVALID) {
758 LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): CNID error", upath);
759 goto createfile_iderr;
761 (void)ad_setid(&ad, st.st_dev, st.st_ino, id, dir->d_did, vol->v_stamp);
765 ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF );
766 fce_register(FCE_FILE_CREATE, fullpathname(upath), NULL, fce_file);
770 setvoltime(obj, vol );
775 int afp_setfilparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
781 uint16_t vid, bitmap;
786 memcpy(&vid, ibuf, sizeof( vid ));
787 ibuf += sizeof( vid );
788 if (NULL == ( vol = getvolbyvid( vid )) ) {
789 return( AFPERR_PARAM );
792 if (vol->v_flags & AFPVOL_RO)
795 memcpy(&did, ibuf, sizeof( did ));
796 ibuf += sizeof( did );
797 if (NULL == ( dir = dirlookup( vol, did )) ) {
798 return afp_errno; /* was AFPERR_NOOBJ */
801 memcpy(&bitmap, ibuf, sizeof( bitmap ));
802 bitmap = ntohs( bitmap );
803 ibuf += sizeof( bitmap );
805 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
806 return get_afp_errno(AFPERR_PARAM);
809 if (path_isadir(s_path)) {
810 return( AFPERR_BADTYPE ); /* it's a directory */
813 if ( s_path->st_errno != 0 ) {
814 return( AFPERR_NOOBJ );
817 if ((u_long)ibuf & 1 ) {
821 if (AFP_OK == ( rc = setfilparams(obj, vol, s_path, bitmap, ibuf )) ) {
822 setvoltime(obj, vol );
829 * cf AFP3.0.pdf page 252 for change_mdate and change_parent_mdate logic
832 extern struct path Cur_Path;
834 int setfilparams(const AFPObj *obj, struct vol *vol,
835 struct path *path, uint16_t f_bitmap, char *buf )
837 struct adouble ad, *adp;
839 int bit, isad = 1, err = AFP_OK;
841 u_char achar, *fdType, xyy[4]; /* uninitialized, OK 310105 */
842 uint16_t ashort, bshort, oshort;
845 uint16_t upriv_bit = 0;
847 int change_mdate = 0;
848 int change_parent_mdate = 0;
853 uint16_t bitmap = f_bitmap;
854 uint32_t cdate,bdate;
855 u_char finder_buf[32];
856 int symlinked = S_ISLNK(path->st.st_mode);
859 char symbuf[MAXPATHLEN+1];
862 LOG(log_debug9, logtype_afpd, "begin setfilparams:");
865 adp = of_ad(vol, path, &ad);
866 upath = path->u_name;
868 if (!vol_unix_priv(vol) && check_access(obj, vol, upath, OPENACC_WR ) < 0) {
869 return AFPERR_ACCESS;
872 /* with unix priv maybe we have to change adouble file priv first */
874 while ( bitmap != 0 ) {
875 while (( bitmap & 1 ) == 0 ) {
882 memcpy(&ashort, buf, sizeof( ashort ));
883 buf += sizeof( ashort );
887 memcpy(&cdate, buf, sizeof(cdate));
888 buf += sizeof( cdate );
891 memcpy(&newdate, buf, sizeof( newdate ));
892 buf += sizeof( newdate );
896 memcpy(&bdate, buf, sizeof( bdate));
897 buf += sizeof( bdate );
901 if (memcmp(buf,"slnkrhap",8) == 0
902 && !(S_ISLNK(path->st.st_mode))
903 && !(vol->v_flags & AFPVOL_FOLLOWSYM)) {
904 /* request to turn this into a symlink */
905 if ((fp = open(path->u_name, O_RDONLY)) == -1) {
907 goto setfilparam_done;
909 len = read(fp, symbuf, MAXPATHLEN);
913 goto setfilparam_done;
915 if (unlink(path->u_name) != 0) {
917 goto setfilparam_done;
920 if (symlink(symbuf, path->u_name) != 0) {
922 goto setfilparam_done;
927 memcpy(finder_buf, buf, 32 );
930 case FILPBIT_UNIXPR :
931 if (!vol_unix_priv(vol)) {
932 /* this volume doesn't use unix priv */
938 change_parent_mdate = 1;
940 memcpy( &aint, buf, sizeof( aint ));
941 f_uid = ntohl (aint);
942 buf += sizeof( aint );
943 memcpy( &aint, buf, sizeof( aint ));
944 f_gid = ntohl (aint);
945 buf += sizeof( aint );
946 setfilowner(vol, f_uid, f_gid, path);
948 memcpy( &upriv, buf, sizeof( upriv ));
949 buf += sizeof( upriv );
950 upriv = ntohl (upriv);
951 if ((upriv & S_IWUSR)) {
952 setfilunixmode(vol, path, upriv);
959 case FILPBIT_PDINFO :
960 if (obj->afp_version < 30) { /* else it's UTF8 name */
963 /* Keep special case to support crlf translations */
964 if ((unsigned int) achar == 0x04) {
965 fdType = (u_char *)"TEXT";
968 xyy[0] = ( u_char ) 'p';
979 /* break while loop */
988 /* second try with adouble open
990 if (ad_open(adp, upath, ADFLAGS_HF | ADFLAGS_RDWR | ADFLAGS_CREATE, 0666) < 0) {
991 LOG(log_debug, logtype_afpd, "setfilparams: ad_open_metadata error");
993 * For some things, we don't need an adouble header:
994 * - change of modification date
995 * - UNIX privs (Bug-ID #2863424)
997 if (!symlinked && f_bitmap & ~(1<<FILPBIT_MDATE | 1<<FILPBIT_UNIXPR)) {
998 LOG(log_debug, logtype_afpd, "setfilparams: need adouble access");
999 return AFPERR_ACCESS;
1001 LOG(log_debug, logtype_afpd, "setfilparams: no adouble perms, but only FILPBIT_MDATE and/or FILPBIT_UNIXPR");
1003 } else if ((ad_get_MD_flags( adp ) & O_CREAT) ) {
1004 ad_setname(adp, path->m_name);
1006 if ((id = get_id(vol, adp, &path->st, curdir->d_did, upath, strlen(upath))) == CNID_INVALID) {
1007 LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): CNID error", upath);
1010 (void)ad_setid(adp, path->st.st_dev, path->st.st_ino, id, curdir->d_did, vol->v_stamp);
1015 while ( bitmap != 0 ) {
1016 while (( bitmap & 1 ) == 0 ) {
1023 ad_getattr(adp, &bshort);
1025 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
1026 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
1030 if ((bshort & htons(ATTRBIT_INVISIBLE)) != (oshort & htons(ATTRBIT_INVISIBLE)))
1031 change_parent_mdate = 1;
1032 ad_setattr(adp, bshort);
1034 case FILPBIT_CDATE :
1035 ad_setdate(adp, AD_DATE_CREATE, cdate);
1037 case FILPBIT_MDATE :
1039 case FILPBIT_BDATE :
1040 ad_setdate(adp, AD_DATE_BACKUP, bdate);
1042 case FILPBIT_FINFO :
1043 if (default_type( ad_entry( adp, ADEID_FINDERI ))
1045 ((em = getextmap( path->m_name )) &&
1046 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1047 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1048 || ((em = getdefextmap()) &&
1049 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1050 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1052 memcpy(finder_buf, ufinderi, 8 );
1054 memcpy(ad_entry( adp, ADEID_FINDERI ), finder_buf, 32 );
1056 case FILPBIT_UNIXPR :
1058 setfilunixmode(vol, path, upriv);
1061 case FILPBIT_PDINFO :
1062 if (obj->afp_version < 30) { /* else it's UTF8 name */
1063 memcpy(ad_entry( adp, ADEID_FINDERI ), fdType, 4 );
1064 memcpy(ad_entry( adp, ADEID_FINDERI ) + 4, "pdos", 4 );
1069 err = AFPERR_BITMAP;
1070 goto setfilparam_done;
1077 if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
1078 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1082 ad_setdate(adp, AD_DATE_MODIFY, newdate);
1083 ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
1089 ad_close(adp, ADFLAGS_HF);
1092 if (change_parent_mdate && gettimeofday(&tv, NULL) == 0) {
1093 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1094 bitmap = 1<<FILPBIT_MDATE;
1095 setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
1099 LOG(log_debug9, logtype_afpd, "end setfilparams:");
1105 * renamefile and copyfile take the old and new unix pathnames
1106 * and the new mac name.
1108 * sdir_fd source dir fd to which src path is relative (for openat et al semantics)
1109 * passing -1 means this is not used, src path is a full path
1110 * src the source path
1111 * dst the dest filename in current dir
1112 * newname the dest mac name
1113 * adp adouble struct of src file, if open, or & zeroed one
1116 int renamefile(struct vol *vol, struct dir *ddir, int sdir_fd, char *src, char *dst, char *newname, struct adouble *adp)
1120 LOG(log_debug, logtype_afpd,
1121 "renamefile: src[%d, \"%s\"] -> dst[\"%s\"]", sdir_fd, src, dst);
1123 if ( unix_rename( sdir_fd, src, -1, dst ) < 0 ) {
1126 return( AFPERR_NOOBJ );
1129 return( AFPERR_ACCESS );
1131 return AFPERR_VLOCK;
1132 case EXDEV : /* Cross device move -- try copy */
1133 /* NOTE: with open file it's an error because after the copy we will
1134 * get two files, it's fixable for our process (eg reopen the new file, get the
1135 * locks, and so on. But it doesn't solve the case with a second process
1137 if (adp->ad_open_forks) {
1138 /* FIXME warning in syslog so admin'd know there's a conflict ?*/
1139 return AFPERR_OLOCK; /* little lie */
1141 if (AFP_OK != ( rc = copyfile(vol, vol, ddir, sdir_fd, src, dst, newname, NULL )) ) {
1142 /* on error copyfile delete dest */
1145 return deletefile(vol, sdir_fd, src, 0);
1147 return( AFPERR_PARAM );
1151 if (vol->vfs->vfs_renamefile(vol, sdir_fd, src, dst) < 0 ) {
1155 /* try to undo the data fork rename,
1156 * we know we are on the same device
1159 unix_rename(-1, dst, sdir_fd, src );
1160 /* return the first error */
1163 return AFPERR_NOOBJ;
1166 return AFPERR_ACCESS ;
1168 return AFPERR_VLOCK;
1170 return AFPERR_PARAM ;
1175 /* don't care if we can't open the newly renamed ressource fork */
1176 if (ad_open(adp, dst, ADFLAGS_HF | ADFLAGS_RDWR) == 0) {
1177 ad_setname(adp, newname);
1179 ad_close( adp, ADFLAGS_HF );
1186 convert a Mac long name to an utf8 name,
1188 size_t mtoUTF8(const struct vol *vol, const char *src, size_t srclen, char *dest, size_t destlen)
1192 if ((size_t)-1 == (outlen = convert_string ( vol->v_maccharset, CH_UTF8_MAC, src, srclen, dest, destlen)) ) {
1198 /* ---------------- */
1199 int copy_path_name(const struct vol *vol, char *newname, char *ibuf)
1206 if ( type != 2 && !(vol->v_obj->afp_version >= 30 && type == 3) ) {
1212 if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
1213 if (vol->v_obj->afp_version >= 30) {
1214 /* convert it to UTF8
1216 if ((plen = mtoUTF8(vol, ibuf, plen, newname, AFPOBJ_TMPSIZ)) == (size_t)-1)
1220 strncpy( newname, ibuf, plen );
1221 newname[ plen ] = '\0';
1223 if (strlen(newname) != plen) {
1224 /* there's \0 in newname, e.g. it's a pathname not
1232 memcpy(&hint, ibuf, sizeof(hint));
1233 ibuf += sizeof(hint);
1235 memcpy(&len16, ibuf, sizeof(len16));
1236 ibuf += sizeof(len16);
1237 plen = ntohs(len16);
1240 if (plen > AFPOBJ_TMPSIZ) {
1243 strncpy( newname, ibuf, plen );
1244 newname[ plen ] = '\0';
1245 if (strlen(newname) != plen) {
1254 /* -----------------------------------
1256 int afp_copyfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1258 struct vol *s_vol, *d_vol;
1260 char *newname, *p, *upath;
1261 struct path *s_path;
1262 uint32_t sdid, ddid;
1263 int err, retvalue = AFP_OK;
1264 uint16_t svid, dvid;
1266 struct adouble ad, *adp;
1272 memcpy(&svid, ibuf, sizeof( svid ));
1273 ibuf += sizeof( svid );
1274 if (NULL == ( s_vol = getvolbyvid( svid )) ) {
1275 return( AFPERR_PARAM );
1278 memcpy(&sdid, ibuf, sizeof( sdid ));
1279 ibuf += sizeof( sdid );
1280 if (NULL == ( dir = dirlookup( s_vol, sdid )) ) {
1284 memcpy(&dvid, ibuf, sizeof( dvid ));
1285 ibuf += sizeof( dvid );
1286 memcpy(&ddid, ibuf, sizeof( ddid ));
1287 ibuf += sizeof( ddid );
1289 if (NULL == ( s_path = cname( s_vol, dir, &ibuf )) ) {
1290 return get_afp_errno(AFPERR_PARAM);
1292 if ( path_isadir(s_path) ) {
1293 return( AFPERR_BADTYPE );
1296 /* don't allow copies when the file is open.
1297 * XXX: the spec only calls for read/deny write access.
1298 * however, copyfile doesn't have any of that info,
1299 * and locks need to stay coherent. as a result,
1300 * we just balk if the file is opened already. */
1302 adp = of_ad(s_vol, s_path, &ad);
1304 if (ad_open(adp, s_path->u_name, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | ADFLAGS_RDONLY | ADFLAGS_SETSHRMD) < 0) {
1305 return AFPERR_DENYCONF;
1307 #ifdef HAVE_FSHARE_T
1309 shmd.f_access = F_RDACC;
1310 shmd.f_deny = F_NODNY;
1311 if (fcntl(ad_data_fileno(adp), F_SHARE, &shmd) != 0) {
1312 retvalue = AFPERR_DENYCONF;
1315 if (AD_RSRC_OPEN(adp) && fcntl(ad_reso_fileno(adp), F_SHARE, &shmd) != 0) {
1316 retvalue = AFPERR_DENYCONF;
1320 denyreadset = (ad_testlock(adp, ADEID_DFORK, AD_FILELOCK_DENY_RD) != 0 ||
1321 ad_testlock(adp, ADEID_RFORK, AD_FILELOCK_DENY_RD) != 0 );
1324 retvalue = AFPERR_DENYCONF;
1328 newname = obj->newtmp;
1329 strcpy( newname, s_path->m_name );
1331 p = ctoupath( s_vol, curdir, newname );
1333 retvalue = AFPERR_PARAM;
1337 if (NULL == ( d_vol = getvolbyvid( dvid )) ) {
1338 retvalue = AFPERR_PARAM;
1342 if (d_vol->v_flags & AFPVOL_RO) {
1343 retvalue = AFPERR_VLOCK;
1347 if (NULL == ( dir = dirlookup( d_vol, ddid )) ) {
1348 retvalue = afp_errno;
1352 if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) {
1353 retvalue = get_afp_errno(AFPERR_NOOBJ);
1357 if ( *s_path->m_name != '\0' ) {
1358 retvalue =path_error(s_path, AFPERR_NOOBJ);
1362 /* one of the handful of places that knows about the path type */
1363 if (copy_path_name(d_vol, newname, ibuf) < 0) {
1364 retvalue = AFPERR_PARAM;
1367 /* newname is always only a filename so curdir *is* its
1370 if (NULL == (upath = mtoupath(d_vol, newname, curdir->d_did, utf8_encoding(d_vol->v_obj)))) {
1371 retvalue =AFPERR_PARAM;
1375 if ( (err = copyfile(s_vol, d_vol, curdir, -1, p, upath , newname, adp)) < 0 ) {
1381 setvoltime(obj, d_vol );
1384 ad_close( adp, ADFLAGS_DF |ADFLAGS_HF | ADFLAGS_SETSHRMD);
1388 /* ----------------------------------
1389 * if newname is NULL (from directory.c) we don't want to copy the resource fork.
1390 * because we are doing it elsewhere.
1391 * currently if newname is NULL then adp is NULL.
1393 int copyfile(struct vol *s_vol,
1400 struct adouble *adp)
1402 struct adouble ads, add;
1409 LOG(log_debug, logtype_afpd, "copyfile(sfd:%d,s:'%s',d:'%s',n:'%s')",
1410 sfd, src, dst, newname);
1413 ad_init(&ads, s_vol);
1417 adflags = ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | ADFLAGS_RF | ADFLAGS_NORF;
1419 if (ad_openat(adp, sfd, src, adflags | ADFLAGS_RDONLY) < 0) {
1424 if (!AD_META_OPEN(adp))
1425 /* no resource fork, don't create one for dst file */
1426 adflags &= ~ADFLAGS_HF;
1428 if (!AD_RSRC_OPEN(adp))
1429 /* no resource fork, don't create one for dst file */
1430 adflags &= ~ADFLAGS_RF;
1432 stat_result = fstat(ad_data_fileno(adp), &st); /* saving stat exit code, thus saving us on one more stat later on */
1434 if (stat_result < 0) {
1435 /* unlikely but if fstat fails, the default file mode will be 0666. */
1436 st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
1439 ad_init(&add, d_vol);
1440 if (ad_open(&add, dst, adflags | ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_EXCL, st.st_mode | S_IRUSR | S_IWUSR) < 0) {
1442 ad_close( adp, adflags );
1443 if (EEXIST != ret_err) {
1444 deletefile(d_vol, -1, dst, 0);
1447 return AFPERR_EXIST;
1450 if ((err = copy_fork(ADEID_DFORK, &add, adp)) != 0)
1451 LOG(log_error, logtype_afpd, "copyfile('%s'): %s", src, strerror(errno));
1454 if ((err = d_vol->vfs->vfs_copyfile(d_vol, sfd, src, dst)) != 0)
1455 LOG(log_error, logtype_afpd, "copyfile('%s'): %s", src, strerror(errno));
1460 if (AD_META_OPEN(&add)) {
1461 if (AD_META_OPEN(adp))
1462 ad_copy_header(&add, adp);
1463 ad_setname(&add, dst);
1466 if (fstat(ad_meta_fileno(&add), &stdest) != 0) {
1470 if ((id = get_id(d_vol, &add, &stdest, d_dir->d_did, dst, strlen(dst))) == CNID_INVALID) {
1474 (void)ad_setid(&add, stdest.st_dev, stdest.st_ino, id, d_dir->d_did, d_vol->v_stamp);
1479 ad_close( adp, adflags );
1481 if (ad_close( &add, adflags ) <0) {
1486 deletefile(d_vol, -1, dst, 0);
1488 else if (stat_result == 0) {
1489 /* set dest modification date to src date */
1492 ut.actime = ut.modtime = st.st_mtime;
1494 /* FIXME netatalk doesn't use resource fork file date
1495 * but maybe we should set its modtime too.
1500 switch ( ret_err ) {
1506 LOG(log_info, logtype_afpd, "copyfile: DISK FULL");
1507 return AFPERR_DFULL;
1509 return AFPERR_NOOBJ;
1511 return AFPERR_ACCESS;
1513 return AFPERR_VLOCK;
1515 return AFPERR_PARAM;
1519 /* -----------------------------------
1520 vol: not NULL delete cnid entry. then we are in curdir and file is a only filename
1521 checkAttrib: 1 check kFPDeleteInhibitBit (deletfile called by afp_delete)
1523 when deletefile is called we don't have lock on it, file is closed (for us)
1524 untrue if called by renamefile
1526 ad_open always try to open file RDWR first and ad_lock takes care of
1527 WRITE lock on read only file.
1530 static int check_attrib(struct adouble *adp)
1532 uint16_t bshort = 0;
1534 ad_getattr(adp, &bshort);
1536 * Does kFPDeleteInhibitBit (bit 8) set?
1538 if ((bshort & htons(ATTRBIT_NODELETE))) {
1539 return AFPERR_OLOCK;
1541 if ((bshort & htons(ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
1547 * dirfd can be used for unlinkat semantics
1549 int deletefile(const struct vol *vol, int dirfd, char *file, int checkAttrib)
1552 struct adouble *adp = NULL;
1553 int adflags, err = AFP_OK;
1556 LOG(log_debug, logtype_afpd, "deletefile('%s')", file);
1560 /* was EACCESS error try to get only metadata */
1561 /* we never want to create a resource fork here, we are going to delete it
1562 * moreover sometimes deletefile is called with a no existent file and
1563 * ad_open would create a 0 byte resource fork
1565 if ( ad_metadataat(dirfd, file, ADFLAGS_CHECK_OF, &ad) == 0 ) {
1566 if ((err = check_attrib(&ad))) {
1567 ad_close(&ad, ADFLAGS_HF | ADFLAGS_CHECK_OF);
1574 /* try to open both forks at once */
1575 adflags = ADFLAGS_DF;
1576 if (ad_openat(&ad, dirfd, file, adflags | ADFLAGS_RF | ADFLAGS_NORF | ADFLAGS_RDONLY) < 0 ) {
1581 case EACCES: /* maybe it's a file with no write mode for us */
1582 break; /* was return AFPERR_ACCESS;*/
1595 if ( adp && AD_RSRC_OPEN(adp) ) { /* there's a resource fork */
1596 adflags |= ADFLAGS_RF;
1597 /* FIXME we have a pb here because we want to know if a file is open
1598 * there's a 'priority inversion' if you can't open the ressource fork RW
1599 * you can delete it if it's open because you can't get a write lock.
1601 * ADLOCK_FILELOCK means the whole ressource fork, not only after the
1604 * FIXME it doesn't work for RFORK open read only and fork open without deny mode
1606 if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
1612 if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
1613 LOG(log_error, logtype_afpd, "deletefile('%s'): ad_tmplock error: %s", file, strerror(errno));
1615 } else if (!(err = vol->vfs->vfs_deletefile(vol, dirfd, file)) && !(err = netatalk_unlinkat(dirfd, file )) ) {
1618 AFP_CNID_START("cnid_get");
1619 id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file));
1622 AFP_CNID_START("cnid_delete");
1623 cnid_delete(vol->v_cdb, id);
1631 ad_close(&ad, ADFLAGS_HF | ADFLAGS_CHECK_OF);
1634 ad_close( &ad, adflags ); /* ad_close removes locks if any */
1639 /* ------------------------------------ */
1640 /* return a file id */
1641 int afp_createid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1650 struct path *s_path;
1656 memcpy(&vid, ibuf, sizeof(vid));
1657 ibuf += sizeof(vid);
1659 if (NULL == ( vol = getvolbyvid( vid )) ) {
1660 return( AFPERR_PARAM);
1663 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1667 if (vol->v_flags & AFPVOL_RO)
1668 return AFPERR_VLOCK;
1670 memcpy(&did, ibuf, sizeof( did ));
1671 ibuf += sizeof(did);
1673 if (NULL == ( dir = dirlookup( vol, did )) ) {
1674 return afp_errno; /* was AFPERR_PARAM */
1677 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
1678 return get_afp_errno(AFPERR_NOOBJ); /* was AFPERR_PARAM */
1681 if ( path_isadir(s_path) ) {
1682 return( AFPERR_BADTYPE );
1685 upath = s_path->u_name;
1686 switch (s_path->st_errno) {
1688 break; /* success */
1691 return AFPERR_ACCESS;
1693 return AFPERR_NOOBJ;
1695 return AFPERR_PARAM;
1698 AFP_CNID_START("cnid_lookup");
1699 id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath));
1702 memcpy(rbuf, &id, sizeof(id));
1703 *rbuflen = sizeof(id);
1704 return AFPERR_EXISTID;
1707 if ((id = get_id(vol, NULL, st, did, upath, len)) != CNID_INVALID) {
1708 memcpy(rbuf, &id, sizeof(id));
1709 *rbuflen = sizeof(id);
1716 /* ------------------------------- */
1722 static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
1725 struct reenum *param = data;
1726 struct vol *vol = param->vol;
1727 cnid_t did = param->did;
1730 if (ostat(de->d_name, &path.st, vol_syml_opt(vol)) < 0)
1733 /* update or add to cnid */
1734 AFP_CNID_START("cnid_add");
1735 aint = cnid_add(vol->v_cdb, &path.st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */
1741 /* --------------------
1742 * Ok the db is out of synch with the dir.
1743 * but if it's a deleted file we don't want to do it again and again.
1746 reenumerate_id(struct vol *vol, char *name, struct dir *dir)
1752 if (vol->v_cdb == NULL) {
1756 /* FIXME use of_statdir ? */
1757 if (ostat(name, &st, vol_syml_opt(vol))) {
1761 if (dirreenumerate(dir, &st)) {
1762 /* we already did it once and the dir haven't been modified */
1763 return dir->d_offcnt;
1767 data.did = dir->d_did;
1768 if ((ret = for_each_dirent(vol, name, reenumerate_loop, (void *)&data)) >= 0) {
1769 setdiroffcnt(curdir, &st, ret);
1770 dir->d_flags |= DIRF_CNID;
1776 /* ------------------------------
1777 resolve a file id */
1778 int afp_resolveid(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1787 uint16_t vid, bitmap;
1789 static char buffer[12 + MAXPATHLEN + 1];
1790 int len = 12 + MAXPATHLEN + 1;
1795 memcpy(&vid, ibuf, sizeof(vid));
1796 ibuf += sizeof(vid);
1798 if (NULL == ( vol = getvolbyvid( vid )) ) {
1799 return( AFPERR_PARAM);
1802 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1806 memcpy(&id, ibuf, sizeof( id ));
1811 /* some MacOS versions after a catsearch do a *lot* of afp_resolveid with 0 */
1815 AFP_CNID_START("cnid_resolve");
1816 upath = cnid_resolve(vol->v_cdb, &id, buffer, len);
1818 if (upath == NULL) {
1819 return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
1822 if (NULL == ( dir = dirlookup( vol, id )) ) {
1823 return AFPERR_NOID; /* idem AFPERR_PARAM */
1825 if (movecwd(vol, dir) < 0) {
1829 return AFPERR_ACCESS;
1833 return AFPERR_PARAM;
1837 memset(&path, 0, sizeof(path));
1838 path.u_name = upath;
1839 if (of_stat(vol, &path) < 0 ) {
1841 /* with nfs and our working directory is deleted */
1842 if (errno == ESTALE) {
1846 if ( errno == ENOENT && !retry) {
1847 /* cnid db is out of sync, reenumerate the directory and update ids */
1848 reenumerate_id(vol, ".", dir);
1856 return AFPERR_ACCESS;
1860 return AFPERR_PARAM;
1864 /* directories are bad */
1865 if (S_ISDIR(path.st.st_mode)) {
1866 /* OS9 and OSX don't return the same error code */
1867 return (obj->afp_version >=30)?AFPERR_NOID:AFPERR_BADTYPE;
1870 memcpy(&bitmap, ibuf, sizeof(bitmap));
1871 bitmap = ntohs( bitmap );
1872 if (NULL == (path.m_name = utompath(vol, upath, cnid, utf8_encoding(obj)))) {
1876 if (AFP_OK != (err = getfilparams(obj, vol, bitmap, &path , curdir,
1877 rbuf + sizeof(bitmap), &buflen, 0))) {
1880 *rbuflen = buflen + sizeof(bitmap);
1881 memcpy(rbuf, ibuf, sizeof(bitmap));
1886 /* ------------------------------ */
1887 int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1897 static char buffer[12 + MAXPATHLEN + 1];
1898 int len = 12 + MAXPATHLEN + 1;
1903 memcpy(&vid, ibuf, sizeof(vid));
1904 ibuf += sizeof(vid);
1906 if (NULL == ( vol = getvolbyvid( vid )) ) {
1907 return( AFPERR_PARAM);
1910 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1914 if (vol->v_flags & AFPVOL_RO)
1915 return AFPERR_VLOCK;
1917 memcpy(&id, ibuf, sizeof( id ));
1921 AFP_CNID_START("cnid_resolve");
1922 upath = cnid_resolve(vol->v_cdb, &id, buffer, len);
1924 if (upath == NULL) {
1928 if (NULL == ( dir = dirlookup( vol, id )) ) {
1929 if (afp_errno == AFPERR_NOOBJ) {
1933 return( AFPERR_PARAM );
1937 if ((movecwd(vol, dir) < 0) || (ostat(upath, &st, vol_syml_opt(vol)) < 0)) {
1941 return AFPERR_ACCESS;
1946 /* still try to delete the id */
1950 return AFPERR_PARAM;
1953 else if (S_ISDIR(st.st_mode)) /* directories are bad */
1954 return AFPERR_BADTYPE;
1957 AFP_CNID_START("cnid_delete");
1958 if (cnid_delete(vol->v_cdb, fileid)) {
1962 return AFPERR_VLOCK;
1965 return AFPERR_ACCESS;
1967 return AFPERR_PARAM;
1974 /* ------------------------------ */
1975 static struct adouble *find_adouble(const AFPObj *obj, struct vol *vol, struct path *path, struct ofork **of, struct adouble *adp)
1979 if (path->st_errno) {
1980 switch (path->st_errno) {
1982 afp_errno = AFPERR_NOID;
1986 afp_errno = AFPERR_ACCESS;
1989 afp_errno = AFPERR_PARAM;
1994 /* we use file_access both for legacy Mac perm and
1995 * for unix privilege, rename will take care of folder perms
1997 if (file_access(obj, vol, path, OPENACC_WR ) < 0) {
1998 afp_errno = AFPERR_ACCESS;
2002 if ((*of = of_findname(vol, path))) {
2003 /* reuse struct adouble so it won't break locks */
2007 ret = ad_open(adp, path->u_name, ADFLAGS_HF | ADFLAGS_RDWR);
2009 if ( !ret && ad_reso_fileno(adp) != -1 && !(adp->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
2011 * The user must have the Read & Write privilege for both files in order to use this command.
2013 ad_close(adp, ADFLAGS_HF);
2014 afp_errno = AFPERR_ACCESS;
2021 #define APPLETEMP ".AppleTempXXXXXX"
2023 int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
2025 struct stat srcst, destst;
2027 struct dir *dir, *sdir;
2028 char *spath, temp[17], *p;
2029 char *supath, *upath;
2034 struct adouble *adsp = NULL;
2035 struct adouble *addp = NULL;
2036 struct ofork *s_of = NULL;
2037 struct ofork *d_of = NULL;
2047 memcpy(&vid, ibuf, sizeof(vid));
2048 ibuf += sizeof(vid);
2050 if (NULL == ( vol = getvolbyvid( vid )) ) {
2051 return( AFPERR_PARAM);
2054 if ((vol->v_flags & AFPVOL_RO))
2055 return AFPERR_VLOCK;
2057 /* source and destination dids */
2058 memcpy(&sid, ibuf, sizeof(sid));
2059 ibuf += sizeof(sid);
2060 memcpy(&did, ibuf, sizeof(did));
2061 ibuf += sizeof(did);
2064 if (NULL == (dir = dirlookup( vol, sid )) ) {
2065 return afp_errno; /* was AFPERR_PARAM */
2068 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2069 return get_afp_errno(AFPERR_NOOBJ);
2072 if ( path_isadir(path) ) {
2073 return AFPERR_BADTYPE; /* it's a dir */
2076 /* save some stuff */
2079 spath = obj->oldtmp;
2080 supath = obj->newtmp;
2081 strcpy(spath, path->m_name);
2082 strcpy(supath, path->u_name); /* this is for the cnid changing */
2083 p = absupath( vol, sdir, supath);
2085 /* pathname too long */
2086 return AFPERR_PARAM ;
2090 if (!(adsp = find_adouble(obj, vol, path, &s_of, &ads))) {
2094 /* ***** from here we may have resource fork open **** */
2096 /* look for the source cnid. if it doesn't exist, don't worry about
2098 AFP_CNID_START("cnid_lookup");
2099 sid = cnid_lookup(vol->v_cdb, &srcst, sdir->d_did, supath,slen = strlen(supath));
2102 if (NULL == ( dir = dirlookup( vol, did )) ) {
2103 err = afp_errno; /* was AFPERR_PARAM */
2104 goto err_exchangefile;
2107 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2108 err = get_afp_errno(AFPERR_NOOBJ);
2109 goto err_exchangefile;
2112 if ( path_isadir(path) ) {
2113 err = AFPERR_BADTYPE;
2114 goto err_exchangefile;
2117 /* FPExchangeFiles is the only call that can return the SameObj
2119 if ((curdir == sdir) && strcmp(spath, path->m_name) == 0) {
2120 err = AFPERR_SAMEOBJ;
2121 goto err_exchangefile;
2125 if (!(addp = find_adouble(obj, vol, path, &d_of, &add))) {
2127 goto err_exchangefile;
2131 /* they are not on the same device and at least one is open
2132 * FIXME broken for for crossdev and adouble v2
2135 crossdev = (srcst.st_dev != destst.st_dev);
2136 if (/* (d_of || s_of) && */ crossdev) {
2138 goto err_exchangefile;
2141 /* look for destination id. */
2142 upath = path->u_name;
2143 AFP_CNID_START("cnid_lookup");
2144 did = cnid_lookup(vol->v_cdb, &destst, curdir->d_did, upath, dlen = strlen(upath));
2147 /* construct a temp name.
2148 * NOTE: the temp file will be in the dest file's directory. it
2149 * will also be inaccessible from AFP. */
2150 memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
2152 if ((fd = mkstemp(temp)) == -1) {
2154 goto err_exchangefile;
2159 /* FIXME we need to close fork for copy, both s_of and d_of are null */
2160 ad_close(adsp, ADFLAGS_HF);
2161 ad_close(addp, ADFLAGS_HF);
2164 /* now, quickly rename the file. we error if we can't. */
2165 if ((err = renamefile(vol, curdir, -1, p, temp, temp, adsp)) != AFP_OK)
2166 goto err_exchangefile;
2167 of_rename(vol, s_of, sdir, spath, curdir, temp);
2169 /* rename destination to source */
2170 if ((err = renamefile(vol, curdir, -1, upath, p, spath, addp)) != AFP_OK)
2171 goto err_src_to_tmp;
2172 of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
2174 /* rename temp to destination */
2175 if ((err = renamefile(vol, curdir, -1, temp, upath, path->m_name, adsp)) != AFP_OK)
2176 goto err_dest_to_src;
2177 of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
2180 * id's need switching. src -> dest and dest -> src.
2181 * we need to re-stat() if it was a cross device copy.
2184 AFP_CNID_START("cnid_delete");
2185 cnid_delete(vol->v_cdb, sid);
2189 AFP_CNID_START("cnid_delete");
2190 cnid_delete(vol->v_cdb, did);
2194 if ((did && ( (crossdev && ostat(upath, &srcst, vol_syml_opt(vol)) < 0) ||
2195 cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
2197 (sid && ( (crossdev && ostat(p, &destst, vol_syml_opt(vol)) < 0) ||
2198 cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
2203 err = AFPERR_ACCESS;
2208 goto err_temp_to_dest;
2211 /* here we need to reopen if crossdev */
2212 if (sid && ad_setid(addp, destst.st_dev, destst.st_ino, sid, sdir->d_did, vol->v_stamp))
2217 if (did && ad_setid(adsp, srcst.st_dev, srcst.st_ino, did, curdir->d_did, vol->v_stamp))
2222 /* change perms, src gets dest perm and vice versa */
2227 * we need to exchange ACL entries as well
2229 /* exchange_acls(vol, p, upath); */
2234 path->m_name = NULL;
2235 path->u_name = upath;
2237 setfilunixmode(vol, path, destst.st_mode);
2238 setfilowner(vol, destst.st_uid, destst.st_gid, path);
2245 setfilunixmode(vol, path, srcst.st_mode);
2246 setfilowner(vol, srcst.st_uid, srcst.st_gid, path);
2251 goto err_exchangefile;
2253 /* all this stuff is so that we can unwind a failed operation
2256 /* rename dest to temp */
2257 renamefile(vol, curdir, -1, upath, temp, temp, adsp);
2258 of_rename(vol, s_of, curdir, upath, curdir, temp);
2261 /* rename source back to dest */
2262 renamefile(vol, curdir, -1, p, upath, path->m_name, addp);
2263 of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
2266 /* rename temp back to source */
2267 renamefile(vol, curdir, -1, temp, p, spath, adsp);
2268 of_rename(vol, s_of, curdir, temp, sdir, spath);
2271 if ( !s_of && adsp && ad_meta_fileno(adsp) != -1 ) { /* META */
2272 ad_close(adsp, ADFLAGS_HF);
2274 if ( !d_of && addp && ad_meta_fileno(addp) != -1 ) {/* META */
2275 ad_close(addp, ADFLAGS_HF);
2279 if ((cached = dircache_search_by_did(vol, sid)) != NULL)
2280 (void)dir_remove(vol, cached);
2281 if ((cached = dircache_search_by_did(vol, did)) != NULL)
2282 (void)dir_remove(vol, cached);