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);
381 ashort &= ~htons(vol->v_ignattr);
383 /* FIXME do we want a visual clue if the file is read only
386 accessmode(vol, ".", &ma, dir , NULL);
387 if ((ma.ma_user & AR_UWRITE)) {
388 accessmode(vol, upath, &ma, dir , st);
389 if (!(ma.ma_user & AR_UWRITE)) {
390 ashort |= htons(ATTRBIT_NOWRITE);
394 memcpy(data, &ashort, sizeof( ashort ));
395 data += sizeof( ashort );
396 LOG(log_debug, logtype_afpd, "metadata('%s'): AFP Attributes: %04x",
397 path->u_name, ntohs(ashort));
401 memcpy(data, &dir->d_did, sizeof( uint32_t ));
402 data += sizeof( uint32_t );
403 LOG(log_debug, logtype_afpd, "metadata('%s'): Parent DID: %u",
404 path->u_name, ntohl(dir->d_did));
408 if (!adp || (ad_getdate(adp, AD_DATE_CREATE, &aint) < 0))
409 aint = AD_DATE_FROM_UNIX(st->st_mtime);
410 memcpy(data, &aint, sizeof( aint ));
411 data += sizeof( aint );
415 if ( adp && (ad_getdate(adp, AD_DATE_MODIFY, &aint) == 0)) {
416 if ((st->st_mtime > AD_DATE_TO_UNIX(aint))) {
417 aint = AD_DATE_FROM_UNIX(st->st_mtime);
420 aint = AD_DATE_FROM_UNIX(st->st_mtime);
422 memcpy(data, &aint, sizeof( int ));
423 data += sizeof( int );
427 if (!adp || (ad_getdate(adp, AD_DATE_BACKUP, &aint) < 0))
428 aint = AD_DATE_START;
429 memcpy(data, &aint, sizeof( int ));
430 data += sizeof( int );
434 get_finderinfo(vol, upath, adp, (char *)data,S_ISLNK(st->st_mode));
435 data += ADEDLEN_FINDERI;
440 data += sizeof( uint16_t );
444 memset(data, 0, sizeof(uint16_t));
445 data += sizeof( uint16_t );
449 memcpy(data, &id, sizeof( id ));
450 data += sizeof( id );
451 LOG(log_debug, logtype_afpd, "metadata('%s'): CNID: %u",
452 path->u_name, ntohl(id));
456 if (st->st_size > 0xffffffff)
459 aint = htonl( st->st_size );
460 memcpy(data, &aint, sizeof( aint ));
461 data += sizeof( aint );
464 case FILPBIT_RFLEN: {
467 if (adp->ad_rlen > 0xffffffff)
470 aint = htonl( adp->ad_rlen);
472 rlen = ad_reso_size(path->u_name, 0, NULL);
473 if (rlen > 0xffffffff)
477 memcpy(data, &aint, sizeof( aint ));
478 data += sizeof( aint );
482 /* Current client needs ProDOS info block for this file.
483 Use simple heuristic and let the Mac "type" string tell
484 us what the PD file code should be. Everything gets a
485 subtype of 0x0000 unless the original value was hashed
486 to "pXYZ" when we created it. See IA, Ver 2.
487 <shirsch@adelphia.net> */
488 case FILPBIT_PDINFO :
489 if (obj->afp_version >= 30) { /* UTF8 name */
490 utf8 = kTextEncodingUTF8;
492 data += sizeof( uint16_t );
494 memcpy(data, &aint, sizeof( aint ));
495 data += sizeof( aint );
499 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
501 if ( memcmp( fdType, "TEXT", 4 ) == 0 ) {
505 else if ( memcmp( fdType, "PSYS", 4 ) == 0 ) {
509 else if ( memcmp( fdType, "PS16", 4 ) == 0 ) {
513 else if ( memcmp( fdType, "BINA", 4 ) == 0 ) {
517 else if ( fdType[0] == 'p' ) {
519 ashort = (fdType[2] * 256) + fdType[3];
533 memcpy(data, &ashort, sizeof( ashort ));
534 data += sizeof( ashort );
535 memset(data, 0, sizeof( ashort ));
536 data += sizeof( ashort );
539 case FILPBIT_EXTDFLEN:
540 aint = htonl(st->st_size >> 32);
541 memcpy(data, &aint, sizeof( aint ));
542 data += sizeof( aint );
543 aint = htonl(st->st_size);
544 memcpy(data, &aint, sizeof( aint ));
545 data += sizeof( aint );
547 case FILPBIT_EXTRFLEN:
549 aint = htonl(adp->ad_rlen >> 32);
550 memcpy(data, &aint, sizeof( aint ));
551 data += sizeof( aint );
552 aint = htonl(adp->ad_rlen);
553 memcpy(data, &aint, sizeof( aint ));
554 data += sizeof( aint );
556 int64_t rlen = hton64(ad_reso_size(path->u_name, 0, NULL));
557 memcpy(data, &rlen, sizeof(rlen));
558 data += sizeof(rlen);
561 case FILPBIT_UNIXPR :
562 /* accessmode may change st_mode with ACLs */
563 accessmode(obj, vol, upath, &ma, dir , st);
565 aint = htonl(st->st_uid);
566 memcpy( data, &aint, sizeof( aint ));
567 data += sizeof( aint );
568 aint = htonl(st->st_gid);
569 memcpy( data, &aint, sizeof( aint ));
570 data += sizeof( aint );
573 type == slnk indicates an OSX style symlink,
574 we have to add S_IFLNK to the mode, otherwise
575 10.3 clients freak out. */
579 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
580 if ( memcmp( fdType, "slnk", 4 ) == 0 ) {
586 memcpy( data, &aint, sizeof( aint ));
587 data += sizeof( aint );
589 *data++ = ma.ma_user;
590 *data++ = ma.ma_world;
591 *data++ = ma.ma_group;
592 *data++ = ma.ma_owner;
596 return( AFPERR_BITMAP );
602 ashort = htons( data - buf );
603 memcpy(l_nameoff, &ashort, sizeof( ashort ));
604 data = set_name(vol, data, dir->d_did, path->m_name, id, 0);
607 ashort = htons( data - buf );
608 memcpy(utf_nameoff, &ashort, sizeof( ashort ));
609 data = set_name(vol, data, dir->d_did, path->m_name, id, utf8);
611 *buflen = data - buf;
615 /* ----------------------- */
616 int getfilparams(const AFPObj *obj, struct vol *vol, uint16_t bitmap, struct path *path,
617 struct dir *dir, char *buf, size_t *buflen, int in_enumerate)
619 struct adouble ad, *adp;
622 int flags; /* uninitialized ok */
624 LOG(log_debug, logtype_afpd, "getfilparams(\"%s\")", path->u_name);
626 opened = PARAM_NEED_ADP(bitmap);
632 * Dont check for and resturn open fork attributes when enumerating
633 * This saves a lot of syscalls, the client will hopefully only use the result
634 * in FPGetFileParms where we return the correct value
636 flags = (!in_enumerate &&(bitmap & (1 << FILPBIT_ATTR))) ? ADFLAGS_CHECK_OF : 0;
638 adp = of_ad(vol, path, &ad);
639 upath = path->u_name;
641 if ( ad_metadata( upath, flags, adp) < 0 ) {
644 LOG(log_error, logtype_afpd, "getfilparams(%s): %s: check resource fork permission?",
645 upath, strerror(errno));
646 return AFPERR_ACCESS;
648 LOG(log_error, logtype_afpd, "getfilparams(%s): bad resource fork", upath);
657 rc = getmetadata(obj, vol, bitmap, path, dir, buf, buflen, adp);
660 ad_close(adp, ADFLAGS_HF | flags);
665 /* ----------------------------- */
666 int afp_createfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
671 struct ofork *of = NULL;
673 int creatf, did, openf, retvalue = AFP_OK;
679 creatf = (unsigned char) *ibuf++;
681 memcpy(&vid, ibuf, sizeof( vid ));
682 ibuf += sizeof( vid );
684 if (NULL == ( vol = getvolbyvid( vid )) )
685 return( AFPERR_PARAM );
687 if (vol->v_flags & AFPVOL_RO)
690 memcpy(&did, ibuf, sizeof( did));
691 ibuf += sizeof( did );
693 if (NULL == ( dir = dirlookup( vol, did )) )
696 if (NULL == ( s_path = cname( vol, dir, &ibuf )) )
697 return get_afp_errno(AFPERR_PARAM);
698 if ( *s_path->m_name == '\0' )
699 return( AFPERR_BADTYPE );
701 upath = s_path->u_name;
704 /* if upath is deleted we already in trouble anyway */
705 if ((of = of_findname(vol, s_path))) {
713 openf = ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_TRUNC;
715 /* on a soft create, if the file is open then ad_open won't fail
716 because open syscall is not called */
717 openf = ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_EXCL;
719 if (ad_open(&ad, upath, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | openf, 0666) < 0) {
723 case ENOENT : /* we were already in 'did folder' so chdir() didn't fail */
724 return ( AFPERR_NOOBJ );
726 return( AFPERR_EXIST );
728 return( AFPERR_ACCESS );
731 LOG(log_info, logtype_afpd, "afp_createfile: DISK FULL");
732 return( AFPERR_DFULL );
734 return( AFPERR_PARAM );
737 if ( ad_meta_fileno( &ad ) == -1 ) { /* Hard META / HF */
738 /* FIXME with hard create on an existing file, we already
739 * corrupted the data file.
741 netatalk_unlink( upath );
742 ad_close( &ad, ADFLAGS_DF );
743 return AFPERR_ACCESS;
746 path = s_path->m_name;
747 ad_setname(&ad, path);
750 if (lstat(upath, &st) != 0) {
751 LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): stat: %s",
752 upath, strerror(errno));
753 ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF);
758 if ((id = get_id(vol, &ad, &st, dir->d_did, upath, strlen(upath))) == CNID_INVALID) {
759 LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): CNID error", upath);
760 goto createfile_iderr;
762 (void)ad_setid(&ad, st.st_dev, st.st_ino, id, dir->d_did, vol->v_stamp);
766 ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF );
767 fce_register(FCE_FILE_CREATE, fullpathname(upath), NULL, fce_file);
771 setvoltime(obj, vol );
776 int afp_setfilparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
782 uint16_t vid, bitmap;
787 memcpy(&vid, ibuf, sizeof( vid ));
788 ibuf += sizeof( vid );
789 if (NULL == ( vol = getvolbyvid( vid )) ) {
790 return( AFPERR_PARAM );
793 if (vol->v_flags & AFPVOL_RO)
796 memcpy(&did, ibuf, sizeof( did ));
797 ibuf += sizeof( did );
798 if (NULL == ( dir = dirlookup( vol, did )) ) {
799 return afp_errno; /* was AFPERR_NOOBJ */
802 memcpy(&bitmap, ibuf, sizeof( bitmap ));
803 bitmap = ntohs( bitmap );
804 ibuf += sizeof( bitmap );
806 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
807 return get_afp_errno(AFPERR_PARAM);
810 if (path_isadir(s_path)) {
811 return( AFPERR_BADTYPE ); /* it's a directory */
814 if ( s_path->st_errno != 0 ) {
815 return( AFPERR_NOOBJ );
818 if ((u_long)ibuf & 1 ) {
822 if (AFP_OK == ( rc = setfilparams(obj, vol, s_path, bitmap, ibuf )) ) {
823 setvoltime(obj, vol );
830 * cf AFP3.0.pdf page 252 for change_mdate and change_parent_mdate logic
833 extern struct path Cur_Path;
835 int setfilparams(const AFPObj *obj, struct vol *vol,
836 struct path *path, uint16_t f_bitmap, char *buf )
838 struct adouble ad, *adp;
840 int bit, isad = 1, err = AFP_OK;
842 u_char achar, *fdType, xyy[4]; /* uninitialized, OK 310105 */
843 uint16_t ashort, bshort, oshort;
846 uint16_t upriv_bit = 0;
848 int change_mdate = 0;
849 int change_parent_mdate = 0;
854 uint16_t bitmap = f_bitmap;
855 uint32_t cdate,bdate;
856 u_char finder_buf[32];
857 int symlinked = S_ISLNK(path->st.st_mode);
860 char symbuf[MAXPATHLEN+1];
863 LOG(log_debug9, logtype_afpd, "begin setfilparams:");
866 adp = of_ad(vol, path, &ad);
867 upath = path->u_name;
869 if (!vol_unix_priv(vol) && check_access(obj, vol, upath, OPENACC_WR ) < 0) {
870 return AFPERR_ACCESS;
873 /* with unix priv maybe we have to change adouble file priv first */
875 while ( bitmap != 0 ) {
876 while (( bitmap & 1 ) == 0 ) {
883 memcpy(&ashort, buf, sizeof( ashort ));
884 buf += sizeof( ashort );
888 memcpy(&cdate, buf, sizeof(cdate));
889 buf += sizeof( cdate );
892 memcpy(&newdate, buf, sizeof( newdate ));
893 buf += sizeof( newdate );
897 memcpy(&bdate, buf, sizeof( bdate));
898 buf += sizeof( bdate );
902 if (memcmp(buf,"slnkrhap",8) == 0
903 && !(S_ISLNK(path->st.st_mode))
904 && !(vol->v_flags & AFPVOL_FOLLOWSYM)) {
905 /* request to turn this into a symlink */
906 if ((fp = open(path->u_name, O_RDONLY)) == -1) {
908 goto setfilparam_done;
910 len = read(fp, symbuf, MAXPATHLEN);
914 goto setfilparam_done;
916 if (unlink(path->u_name) != 0) {
918 goto setfilparam_done;
921 if (symlink(symbuf, path->u_name) != 0) {
923 goto setfilparam_done;
928 memcpy(finder_buf, buf, 32 );
931 case FILPBIT_UNIXPR :
932 if (!vol_unix_priv(vol)) {
933 /* this volume doesn't use unix priv */
939 change_parent_mdate = 1;
941 memcpy( &aint, buf, sizeof( aint ));
942 f_uid = ntohl (aint);
943 buf += sizeof( aint );
944 memcpy( &aint, buf, sizeof( aint ));
945 f_gid = ntohl (aint);
946 buf += sizeof( aint );
947 setfilowner(vol, f_uid, f_gid, path);
949 memcpy( &upriv, buf, sizeof( upriv ));
950 buf += sizeof( upriv );
951 upriv = ntohl (upriv);
952 if ((upriv & S_IWUSR)) {
953 setfilunixmode(vol, path, upriv);
960 case FILPBIT_PDINFO :
961 if (obj->afp_version < 30) { /* else it's UTF8 name */
964 /* Keep special case to support crlf translations */
965 if ((unsigned int) achar == 0x04) {
966 fdType = (u_char *)"TEXT";
969 xyy[0] = ( u_char ) 'p';
980 /* break while loop */
989 /* second try with adouble open
991 if (ad_open(adp, upath, ADFLAGS_HF | ADFLAGS_RDWR | ADFLAGS_CREATE, 0666) < 0) {
992 LOG(log_debug, logtype_afpd, "setfilparams: ad_open_metadata error");
994 * For some things, we don't need an adouble header:
995 * - change of modification date
996 * - UNIX privs (Bug-ID #2863424)
998 if (!symlinked && f_bitmap & ~(1<<FILPBIT_MDATE | 1<<FILPBIT_UNIXPR)) {
999 LOG(log_debug, logtype_afpd, "setfilparams: need adouble access");
1000 return AFPERR_ACCESS;
1002 LOG(log_debug, logtype_afpd, "setfilparams: no adouble perms, but only FILPBIT_MDATE and/or FILPBIT_UNIXPR");
1004 } else if ((ad_get_MD_flags( adp ) & O_CREAT) ) {
1005 ad_setname(adp, path->m_name);
1007 if ((id = get_id(vol, adp, &path->st, curdir->d_did, upath, strlen(upath))) == CNID_INVALID) {
1008 LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): CNID error", upath);
1011 (void)ad_setid(adp, path->st.st_dev, path->st.st_ino, id, curdir->d_did, vol->v_stamp);
1016 while ( bitmap != 0 ) {
1017 while (( bitmap & 1 ) == 0 ) {
1024 ad_getattr(adp, &bshort);
1026 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
1027 ashort &= ~htons(vol->v_ignattr);
1028 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
1032 if ((bshort & htons(ATTRBIT_INVISIBLE)) != (oshort & htons(ATTRBIT_INVISIBLE)))
1033 change_parent_mdate = 1;
1034 ad_setattr(adp, bshort);
1036 case FILPBIT_CDATE :
1037 ad_setdate(adp, AD_DATE_CREATE, cdate);
1039 case FILPBIT_MDATE :
1041 case FILPBIT_BDATE :
1042 ad_setdate(adp, AD_DATE_BACKUP, bdate);
1044 case FILPBIT_FINFO :
1045 if (default_type( ad_entry( adp, ADEID_FINDERI ))
1047 ((em = getextmap( path->m_name )) &&
1048 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1049 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1050 || ((em = getdefextmap()) &&
1051 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1052 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1054 memcpy(finder_buf, ufinderi, 8 );
1056 memcpy(ad_entry( adp, ADEID_FINDERI ), finder_buf, 32 );
1058 case FILPBIT_UNIXPR :
1060 setfilunixmode(vol, path, upriv);
1063 case FILPBIT_PDINFO :
1064 if (obj->afp_version < 30) { /* else it's UTF8 name */
1065 memcpy(ad_entry( adp, ADEID_FINDERI ), fdType, 4 );
1066 memcpy(ad_entry( adp, ADEID_FINDERI ) + 4, "pdos", 4 );
1071 err = AFPERR_BITMAP;
1072 goto setfilparam_done;
1079 if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
1080 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1084 ad_setdate(adp, AD_DATE_MODIFY, newdate);
1085 ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
1091 ad_close(adp, ADFLAGS_HF);
1094 if (change_parent_mdate && gettimeofday(&tv, NULL) == 0) {
1095 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1096 bitmap = 1<<FILPBIT_MDATE;
1097 setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
1101 LOG(log_debug9, logtype_afpd, "end setfilparams:");
1107 * renamefile and copyfile take the old and new unix pathnames
1108 * and the new mac name.
1110 * sdir_fd source dir fd to which src path is relative (for openat et al semantics)
1111 * passing -1 means this is not used, src path is a full path
1112 * src the source path
1113 * dst the dest filename in current dir
1114 * newname the dest mac name
1115 * adp adouble struct of src file, if open, or & zeroed one
1118 int renamefile(struct vol *vol, struct dir *ddir, int sdir_fd, char *src, char *dst, char *newname, struct adouble *adp)
1122 LOG(log_debug, logtype_afpd,
1123 "renamefile: src[%d, \"%s\"] -> dst[\"%s\"]", sdir_fd, src, dst);
1125 if ( unix_rename( sdir_fd, src, -1, dst ) < 0 ) {
1128 return( AFPERR_NOOBJ );
1131 return( AFPERR_ACCESS );
1133 return AFPERR_VLOCK;
1134 case EXDEV : /* Cross device move -- try copy */
1135 /* NOTE: with open file it's an error because after the copy we will
1136 * get two files, it's fixable for our process (eg reopen the new file, get the
1137 * locks, and so on. But it doesn't solve the case with a second process
1139 if (adp->ad_open_forks) {
1140 /* FIXME warning in syslog so admin'd know there's a conflict ?*/
1141 return AFPERR_OLOCK; /* little lie */
1143 if (AFP_OK != ( rc = copyfile(vol, vol, ddir, sdir_fd, src, dst, newname, NULL )) ) {
1144 /* on error copyfile delete dest */
1147 return deletefile(vol, sdir_fd, src, 0);
1149 return( AFPERR_PARAM );
1153 if (vol->vfs->vfs_renamefile(vol, sdir_fd, src, dst) < 0 ) {
1157 /* try to undo the data fork rename,
1158 * we know we are on the same device
1161 unix_rename(-1, dst, sdir_fd, src );
1162 /* return the first error */
1165 return AFPERR_NOOBJ;
1168 return AFPERR_ACCESS ;
1170 return AFPERR_VLOCK;
1172 return AFPERR_PARAM ;
1177 /* don't care if we can't open the newly renamed ressource fork */
1178 if (ad_open(adp, dst, ADFLAGS_HF | ADFLAGS_RDWR) == 0) {
1179 ad_setname(adp, newname);
1181 ad_close( adp, ADFLAGS_HF );
1188 convert a Mac long name to an utf8 name,
1190 size_t mtoUTF8(const struct vol *vol, const char *src, size_t srclen, char *dest, size_t destlen)
1194 if ((size_t)-1 == (outlen = convert_string ( vol->v_maccharset, CH_UTF8_MAC, src, srclen, dest, destlen)) ) {
1200 /* ---------------- */
1201 int copy_path_name(const struct vol *vol, char *newname, char *ibuf)
1208 if ( type != 2 && !(vol->v_obj->afp_version >= 30 && type == 3) ) {
1214 if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
1215 if (vol->v_obj->afp_version >= 30) {
1216 /* convert it to UTF8
1218 if ((plen = mtoUTF8(vol, ibuf, plen, newname, AFPOBJ_TMPSIZ)) == (size_t)-1)
1222 strncpy( newname, ibuf, plen );
1223 newname[ plen ] = '\0';
1225 if (strlen(newname) != plen) {
1226 /* there's \0 in newname, e.g. it's a pathname not
1234 memcpy(&hint, ibuf, sizeof(hint));
1235 ibuf += sizeof(hint);
1237 memcpy(&len16, ibuf, sizeof(len16));
1238 ibuf += sizeof(len16);
1239 plen = ntohs(len16);
1242 if (plen > AFPOBJ_TMPSIZ) {
1245 strncpy( newname, ibuf, plen );
1246 newname[ plen ] = '\0';
1247 if (strlen(newname) != plen) {
1256 /* -----------------------------------
1258 int afp_copyfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1260 struct vol *s_vol, *d_vol;
1262 char *newname, *p, *upath;
1263 struct path *s_path;
1264 uint32_t sdid, ddid;
1265 int err, retvalue = AFP_OK;
1266 uint16_t svid, dvid;
1268 struct adouble ad, *adp;
1274 memcpy(&svid, ibuf, sizeof( svid ));
1275 ibuf += sizeof( svid );
1276 if (NULL == ( s_vol = getvolbyvid( svid )) ) {
1277 return( AFPERR_PARAM );
1280 memcpy(&sdid, ibuf, sizeof( sdid ));
1281 ibuf += sizeof( sdid );
1282 if (NULL == ( dir = dirlookup( s_vol, sdid )) ) {
1286 memcpy(&dvid, ibuf, sizeof( dvid ));
1287 ibuf += sizeof( dvid );
1288 memcpy(&ddid, ibuf, sizeof( ddid ));
1289 ibuf += sizeof( ddid );
1291 if (NULL == ( s_path = cname( s_vol, dir, &ibuf )) ) {
1292 return get_afp_errno(AFPERR_PARAM);
1294 if ( path_isadir(s_path) ) {
1295 return( AFPERR_BADTYPE );
1298 /* don't allow copies when the file is open.
1299 * XXX: the spec only calls for read/deny write access.
1300 * however, copyfile doesn't have any of that info,
1301 * and locks need to stay coherent. as a result,
1302 * we just balk if the file is opened already. */
1304 adp = of_ad(s_vol, s_path, &ad);
1306 if (ad_open(adp, s_path->u_name, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | ADFLAGS_RDONLY | ADFLAGS_SETSHRMD) < 0) {
1307 return AFPERR_DENYCONF;
1309 #ifdef HAVE_FSHARE_T
1311 shmd.f_access = F_RDACC;
1312 shmd.f_deny = F_NODNY;
1313 if (fcntl(ad_data_fileno(adp), F_SHARE, &shmd) != 0) {
1314 retvalue = AFPERR_DENYCONF;
1317 if (AD_RSRC_OPEN(adp) && fcntl(ad_reso_fileno(adp), F_SHARE, &shmd) != 0) {
1318 retvalue = AFPERR_DENYCONF;
1322 denyreadset = (ad_testlock(adp, ADEID_DFORK, AD_FILELOCK_DENY_RD) != 0 ||
1323 ad_testlock(adp, ADEID_RFORK, AD_FILELOCK_DENY_RD) != 0 );
1326 retvalue = AFPERR_DENYCONF;
1330 newname = obj->newtmp;
1331 strcpy( newname, s_path->m_name );
1333 p = ctoupath( s_vol, curdir, newname );
1335 retvalue = AFPERR_PARAM;
1339 if (NULL == ( d_vol = getvolbyvid( dvid )) ) {
1340 retvalue = AFPERR_PARAM;
1344 if (d_vol->v_flags & AFPVOL_RO) {
1345 retvalue = AFPERR_VLOCK;
1349 if (NULL == ( dir = dirlookup( d_vol, ddid )) ) {
1350 retvalue = afp_errno;
1354 if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) {
1355 retvalue = get_afp_errno(AFPERR_NOOBJ);
1359 if ( *s_path->m_name != '\0' ) {
1360 retvalue =path_error(s_path, AFPERR_NOOBJ);
1364 /* one of the handful of places that knows about the path type */
1365 if (copy_path_name(d_vol, newname, ibuf) < 0) {
1366 retvalue = AFPERR_PARAM;
1369 /* newname is always only a filename so curdir *is* its
1372 if (NULL == (upath = mtoupath(d_vol, newname, curdir->d_did, utf8_encoding(d_vol->v_obj)))) {
1373 retvalue =AFPERR_PARAM;
1377 if ( (err = copyfile(s_vol, d_vol, curdir, -1, p, upath , newname, adp)) < 0 ) {
1383 setvoltime(obj, d_vol );
1386 ad_close( adp, ADFLAGS_DF |ADFLAGS_HF | ADFLAGS_SETSHRMD);
1390 /* ----------------------------------
1391 * if newname is NULL (from directory.c) we don't want to copy the resource fork.
1392 * because we are doing it elsewhere.
1393 * currently if newname is NULL then adp is NULL.
1395 int copyfile(struct vol *s_vol,
1402 struct adouble *adp)
1404 struct adouble ads, add;
1411 LOG(log_debug, logtype_afpd, "copyfile(sfd:%d,s:'%s',d:'%s',n:'%s')",
1412 sfd, src, dst, newname);
1415 ad_init(&ads, s_vol);
1419 adflags = ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | ADFLAGS_RF | ADFLAGS_NORF;
1421 if (ad_openat(adp, sfd, src, adflags | ADFLAGS_RDONLY) < 0) {
1426 if (!AD_META_OPEN(adp))
1427 /* no resource fork, don't create one for dst file */
1428 adflags &= ~ADFLAGS_HF;
1430 if (!AD_RSRC_OPEN(adp))
1431 /* no resource fork, don't create one for dst file */
1432 adflags &= ~ADFLAGS_RF;
1434 stat_result = fstat(ad_data_fileno(adp), &st); /* saving stat exit code, thus saving us on one more stat later on */
1436 if (stat_result < 0) {
1437 /* unlikely but if fstat fails, the default file mode will be 0666. */
1438 st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
1441 ad_init(&add, d_vol);
1442 if (ad_open(&add, dst, adflags | ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_EXCL, st.st_mode | S_IRUSR | S_IWUSR) < 0) {
1444 ad_close( adp, adflags );
1445 if (EEXIST != ret_err) {
1446 deletefile(d_vol, -1, dst, 0);
1449 return AFPERR_EXIST;
1452 if ((err = copy_fork(ADEID_DFORK, &add, adp)) != 0)
1453 LOG(log_error, logtype_afpd, "copyfile('%s'): %s", src, strerror(errno));
1456 if ((err = d_vol->vfs->vfs_copyfile(d_vol, sfd, src, dst)) != 0)
1457 LOG(log_error, logtype_afpd, "copyfile('%s'): %s", src, strerror(errno));
1462 if (AD_META_OPEN(&add)) {
1463 if (AD_META_OPEN(adp))
1464 ad_copy_header(&add, adp);
1465 ad_setname(&add, dst);
1468 if (fstat(ad_meta_fileno(&add), &stdest) != 0) {
1472 if ((id = get_id(d_vol, &add, &stdest, d_dir->d_did, dst, strlen(dst))) == CNID_INVALID) {
1476 (void)ad_setid(&add, stdest.st_dev, stdest.st_ino, id, d_dir->d_did, d_vol->v_stamp);
1481 ad_close( adp, adflags );
1483 if (ad_close( &add, adflags ) <0) {
1488 deletefile(d_vol, -1, dst, 0);
1490 else if (stat_result == 0) {
1491 /* set dest modification date to src date */
1494 ut.actime = ut.modtime = st.st_mtime;
1496 /* FIXME netatalk doesn't use resource fork file date
1497 * but maybe we should set its modtime too.
1502 switch ( ret_err ) {
1508 LOG(log_info, logtype_afpd, "copyfile: DISK FULL");
1509 return AFPERR_DFULL;
1511 return AFPERR_NOOBJ;
1513 return AFPERR_ACCESS;
1515 return AFPERR_VLOCK;
1517 return AFPERR_PARAM;
1521 /* -----------------------------------
1522 vol: not NULL delete cnid entry. then we are in curdir and file is a only filename
1523 checkAttrib: 1 check kFPDeleteInhibitBit (deletfile called by afp_delete)
1525 when deletefile is called we don't have lock on it, file is closed (for us)
1526 untrue if called by renamefile
1528 ad_open always try to open file RDWR first and ad_lock takes care of
1529 WRITE lock on read only file.
1532 static int check_attrib(const struct vol *vol, struct adouble *adp)
1534 uint16_t bshort = 0;
1536 ad_getattr(adp, &bshort);
1538 * Does kFPDeleteInhibitBit (bit 8) set?
1540 if (!(vol->v_ignattr & ATTRBIT_NODELETE) && (bshort & htons(ATTRBIT_NODELETE))) {
1541 return AFPERR_OLOCK;
1543 if ((bshort & htons(ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
1549 * dirfd can be used for unlinkat semantics
1551 int deletefile(const struct vol *vol, int dirfd, char *file, int checkAttrib)
1554 struct adouble *adp = NULL;
1555 int adflags, err = AFP_OK;
1558 LOG(log_debug, logtype_afpd, "deletefile('%s')", file);
1562 /* was EACCESS error try to get only metadata */
1563 /* we never want to create a resource fork here, we are going to delete it
1564 * moreover sometimes deletefile is called with a no existent file and
1565 * ad_open would create a 0 byte resource fork
1567 if ( ad_metadataat(dirfd, file, ADFLAGS_CHECK_OF, &ad) == 0 ) {
1568 if ((err = check_attrib(vol, &ad))) {
1569 ad_close(&ad, ADFLAGS_HF | ADFLAGS_CHECK_OF);
1576 /* try to open both forks at once */
1577 adflags = ADFLAGS_DF;
1578 if (ad_openat(&ad, dirfd, file, adflags | ADFLAGS_RF | ADFLAGS_NORF | ADFLAGS_RDONLY) < 0 ) {
1583 case EACCES: /* maybe it's a file with no write mode for us */
1584 break; /* was return AFPERR_ACCESS;*/
1597 if ( adp && AD_RSRC_OPEN(adp) ) { /* there's a resource fork */
1598 adflags |= ADFLAGS_RF;
1599 /* FIXME we have a pb here because we want to know if a file is open
1600 * there's a 'priority inversion' if you can't open the ressource fork RW
1601 * you can delete it if it's open because you can't get a write lock.
1603 * ADLOCK_FILELOCK means the whole ressource fork, not only after the
1606 * FIXME it doesn't work for RFORK open read only and fork open without deny mode
1608 if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
1614 if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
1615 LOG(log_error, logtype_afpd, "deletefile('%s'): ad_tmplock error: %s", file, strerror(errno));
1617 } else if (!(err = vol->vfs->vfs_deletefile(vol, dirfd, file)) && !(err = netatalk_unlinkat(dirfd, file )) ) {
1620 AFP_CNID_START("cnid_get");
1621 id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file));
1624 AFP_CNID_START("cnid_delete");
1625 cnid_delete(vol->v_cdb, id);
1633 ad_close(&ad, ADFLAGS_HF | ADFLAGS_CHECK_OF);
1636 ad_close( &ad, adflags ); /* ad_close removes locks if any */
1641 /* ------------------------------------ */
1642 /* return a file id */
1643 int afp_createid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1652 struct path *s_path;
1658 memcpy(&vid, ibuf, sizeof(vid));
1659 ibuf += sizeof(vid);
1661 if (NULL == ( vol = getvolbyvid( vid )) ) {
1662 return( AFPERR_PARAM);
1665 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1669 if (vol->v_flags & AFPVOL_RO)
1670 return AFPERR_VLOCK;
1672 memcpy(&did, ibuf, sizeof( did ));
1673 ibuf += sizeof(did);
1675 if (NULL == ( dir = dirlookup( vol, did )) ) {
1676 return afp_errno; /* was AFPERR_PARAM */
1679 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
1680 return get_afp_errno(AFPERR_NOOBJ); /* was AFPERR_PARAM */
1683 if ( path_isadir(s_path) ) {
1684 return( AFPERR_BADTYPE );
1687 upath = s_path->u_name;
1688 switch (s_path->st_errno) {
1690 break; /* success */
1693 return AFPERR_ACCESS;
1695 return AFPERR_NOOBJ;
1697 return AFPERR_PARAM;
1700 AFP_CNID_START("cnid_lookup");
1701 id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath));
1704 memcpy(rbuf, &id, sizeof(id));
1705 *rbuflen = sizeof(id);
1706 return AFPERR_EXISTID;
1709 if ((id = get_id(vol, NULL, st, did, upath, len)) != CNID_INVALID) {
1710 memcpy(rbuf, &id, sizeof(id));
1711 *rbuflen = sizeof(id);
1718 /* ------------------------------- */
1724 static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
1727 struct reenum *param = data;
1728 struct vol *vol = param->vol;
1729 cnid_t did = param->did;
1732 if (ostat(de->d_name, &path.st, vol_syml_opt(vol)) < 0)
1735 /* update or add to cnid */
1736 AFP_CNID_START("cnid_add");
1737 aint = cnid_add(vol->v_cdb, &path.st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */
1743 /* --------------------
1744 * Ok the db is out of synch with the dir.
1745 * but if it's a deleted file we don't want to do it again and again.
1748 reenumerate_id(struct vol *vol, char *name, struct dir *dir)
1754 if (vol->v_cdb == NULL) {
1758 /* FIXME use of_statdir ? */
1759 if (ostat(name, &st, vol_syml_opt(vol))) {
1763 if (dirreenumerate(dir, &st)) {
1764 /* we already did it once and the dir haven't been modified */
1765 return dir->d_offcnt;
1769 data.did = dir->d_did;
1770 if ((ret = for_each_dirent(vol, name, reenumerate_loop, (void *)&data)) >= 0) {
1771 setdiroffcnt(curdir, &st, ret);
1772 dir->d_flags |= DIRF_CNID;
1778 /* ------------------------------
1779 resolve a file id */
1780 int afp_resolveid(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1789 uint16_t vid, bitmap;
1791 static char buffer[12 + MAXPATHLEN + 1];
1792 int len = 12 + MAXPATHLEN + 1;
1797 memcpy(&vid, ibuf, sizeof(vid));
1798 ibuf += sizeof(vid);
1800 if (NULL == ( vol = getvolbyvid( vid )) ) {
1801 return( AFPERR_PARAM);
1804 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1808 memcpy(&id, ibuf, sizeof( id ));
1813 /* some MacOS versions after a catsearch do a *lot* of afp_resolveid with 0 */
1817 AFP_CNID_START("cnid_resolve");
1818 upath = cnid_resolve(vol->v_cdb, &id, buffer, len);
1820 if (upath == NULL) {
1821 return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
1824 if (NULL == ( dir = dirlookup( vol, id )) ) {
1825 return AFPERR_NOID; /* idem AFPERR_PARAM */
1827 if (movecwd(vol, dir) < 0) {
1831 return AFPERR_ACCESS;
1835 return AFPERR_PARAM;
1839 memset(&path, 0, sizeof(path));
1840 path.u_name = upath;
1841 if (of_stat(vol, &path) < 0 ) {
1843 /* with nfs and our working directory is deleted */
1844 if (errno == ESTALE) {
1848 if ( errno == ENOENT && !retry) {
1849 /* cnid db is out of sync, reenumerate the directory and update ids */
1850 reenumerate_id(vol, ".", dir);
1858 return AFPERR_ACCESS;
1862 return AFPERR_PARAM;
1866 /* directories are bad */
1867 if (S_ISDIR(path.st.st_mode)) {
1868 /* OS9 and OSX don't return the same error code */
1869 return (obj->afp_version >=30)?AFPERR_NOID:AFPERR_BADTYPE;
1872 memcpy(&bitmap, ibuf, sizeof(bitmap));
1873 bitmap = ntohs( bitmap );
1874 if (NULL == (path.m_name = utompath(vol, upath, cnid, utf8_encoding(obj)))) {
1878 if (AFP_OK != (err = getfilparams(obj, vol, bitmap, &path , curdir,
1879 rbuf + sizeof(bitmap), &buflen, 0))) {
1882 *rbuflen = buflen + sizeof(bitmap);
1883 memcpy(rbuf, ibuf, sizeof(bitmap));
1888 /* ------------------------------ */
1889 int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1899 static char buffer[12 + MAXPATHLEN + 1];
1900 int len = 12 + MAXPATHLEN + 1;
1905 memcpy(&vid, ibuf, sizeof(vid));
1906 ibuf += sizeof(vid);
1908 if (NULL == ( vol = getvolbyvid( vid )) ) {
1909 return( AFPERR_PARAM);
1912 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1916 if (vol->v_flags & AFPVOL_RO)
1917 return AFPERR_VLOCK;
1919 memcpy(&id, ibuf, sizeof( id ));
1923 AFP_CNID_START("cnid_resolve");
1924 upath = cnid_resolve(vol->v_cdb, &id, buffer, len);
1926 if (upath == NULL) {
1930 if (NULL == ( dir = dirlookup( vol, id )) ) {
1931 if (afp_errno == AFPERR_NOOBJ) {
1935 return( AFPERR_PARAM );
1939 if ((movecwd(vol, dir) < 0) || (ostat(upath, &st, vol_syml_opt(vol)) < 0)) {
1943 return AFPERR_ACCESS;
1948 /* still try to delete the id */
1952 return AFPERR_PARAM;
1955 else if (S_ISDIR(st.st_mode)) /* directories are bad */
1956 return AFPERR_BADTYPE;
1959 AFP_CNID_START("cnid_delete");
1960 if (cnid_delete(vol->v_cdb, fileid)) {
1964 return AFPERR_VLOCK;
1967 return AFPERR_ACCESS;
1969 return AFPERR_PARAM;
1976 /* ------------------------------ */
1977 static struct adouble *find_adouble(const AFPObj *obj, struct vol *vol, struct path *path, struct ofork **of, struct adouble *adp)
1981 if (path->st_errno) {
1982 switch (path->st_errno) {
1984 afp_errno = AFPERR_NOID;
1988 afp_errno = AFPERR_ACCESS;
1991 afp_errno = AFPERR_PARAM;
1996 /* we use file_access both for legacy Mac perm and
1997 * for unix privilege, rename will take care of folder perms
1999 if (file_access(obj, vol, path, OPENACC_WR ) < 0) {
2000 afp_errno = AFPERR_ACCESS;
2004 if ((*of = of_findname(vol, path))) {
2005 /* reuse struct adouble so it won't break locks */
2009 ret = ad_open(adp, path->u_name, ADFLAGS_HF | ADFLAGS_RDWR);
2011 if ( !ret && ad_reso_fileno(adp) != -1 && !(adp->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
2013 * The user must have the Read & Write privilege for both files in order to use this command.
2015 ad_close(adp, ADFLAGS_HF);
2016 afp_errno = AFPERR_ACCESS;
2023 #define APPLETEMP ".AppleTempXXXXXX"
2025 int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
2027 struct stat srcst, destst;
2029 struct dir *dir, *sdir;
2030 char *spath, temp[17], *p;
2031 char *supath, *upath;
2036 struct adouble *adsp = NULL;
2037 struct adouble *addp = NULL;
2038 struct ofork *s_of = NULL;
2039 struct ofork *d_of = NULL;
2049 memcpy(&vid, ibuf, sizeof(vid));
2050 ibuf += sizeof(vid);
2052 if (NULL == ( vol = getvolbyvid( vid )) ) {
2053 return( AFPERR_PARAM);
2056 if ((vol->v_flags & AFPVOL_RO))
2057 return AFPERR_VLOCK;
2059 /* source and destination dids */
2060 memcpy(&sid, ibuf, sizeof(sid));
2061 ibuf += sizeof(sid);
2062 memcpy(&did, ibuf, sizeof(did));
2063 ibuf += sizeof(did);
2066 if (NULL == (dir = dirlookup( vol, sid )) ) {
2067 return afp_errno; /* was AFPERR_PARAM */
2070 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2071 return get_afp_errno(AFPERR_NOOBJ);
2074 if ( path_isadir(path) ) {
2075 return AFPERR_BADTYPE; /* it's a dir */
2078 /* save some stuff */
2081 spath = obj->oldtmp;
2082 supath = obj->newtmp;
2083 strcpy(spath, path->m_name);
2084 strcpy(supath, path->u_name); /* this is for the cnid changing */
2085 p = absupath( vol, sdir, supath);
2087 /* pathname too long */
2088 return AFPERR_PARAM ;
2092 if (!(adsp = find_adouble(obj, vol, path, &s_of, &ads))) {
2096 /* ***** from here we may have resource fork open **** */
2098 /* look for the source cnid. if it doesn't exist, don't worry about
2100 AFP_CNID_START("cnid_lookup");
2101 sid = cnid_lookup(vol->v_cdb, &srcst, sdir->d_did, supath,slen = strlen(supath));
2104 if (NULL == ( dir = dirlookup( vol, did )) ) {
2105 err = afp_errno; /* was AFPERR_PARAM */
2106 goto err_exchangefile;
2109 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2110 err = get_afp_errno(AFPERR_NOOBJ);
2111 goto err_exchangefile;
2114 if ( path_isadir(path) ) {
2115 err = AFPERR_BADTYPE;
2116 goto err_exchangefile;
2119 /* FPExchangeFiles is the only call that can return the SameObj
2121 if ((curdir == sdir) && strcmp(spath, path->m_name) == 0) {
2122 err = AFPERR_SAMEOBJ;
2123 goto err_exchangefile;
2127 if (!(addp = find_adouble(obj, vol, path, &d_of, &add))) {
2129 goto err_exchangefile;
2133 /* they are not on the same device and at least one is open
2134 * FIXME broken for for crossdev and adouble v2
2137 crossdev = (srcst.st_dev != destst.st_dev);
2138 if (/* (d_of || s_of) && */ crossdev) {
2140 goto err_exchangefile;
2143 /* look for destination id. */
2144 upath = path->u_name;
2145 AFP_CNID_START("cnid_lookup");
2146 did = cnid_lookup(vol->v_cdb, &destst, curdir->d_did, upath, dlen = strlen(upath));
2149 /* construct a temp name.
2150 * NOTE: the temp file will be in the dest file's directory. it
2151 * will also be inaccessible from AFP. */
2152 memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
2154 if ((fd = mkstemp(temp)) == -1) {
2156 goto err_exchangefile;
2161 /* FIXME we need to close fork for copy, both s_of and d_of are null */
2162 ad_close(adsp, ADFLAGS_HF);
2163 ad_close(addp, ADFLAGS_HF);
2166 /* now, quickly rename the file. we error if we can't. */
2167 if ((err = renamefile(vol, curdir, -1, p, temp, temp, adsp)) != AFP_OK)
2168 goto err_exchangefile;
2169 of_rename(vol, s_of, sdir, spath, curdir, temp);
2171 /* rename destination to source */
2172 if ((err = renamefile(vol, curdir, -1, upath, p, spath, addp)) != AFP_OK)
2173 goto err_src_to_tmp;
2174 of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
2176 /* rename temp to destination */
2177 if ((err = renamefile(vol, curdir, -1, temp, upath, path->m_name, adsp)) != AFP_OK)
2178 goto err_dest_to_src;
2179 of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
2182 * id's need switching. src -> dest and dest -> src.
2183 * we need to re-stat() if it was a cross device copy.
2186 AFP_CNID_START("cnid_delete");
2187 cnid_delete(vol->v_cdb, sid);
2191 AFP_CNID_START("cnid_delete");
2192 cnid_delete(vol->v_cdb, did);
2196 if ((did && ( (crossdev && ostat(upath, &srcst, vol_syml_opt(vol)) < 0) ||
2197 cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
2199 (sid && ( (crossdev && ostat(p, &destst, vol_syml_opt(vol)) < 0) ||
2200 cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
2205 err = AFPERR_ACCESS;
2210 goto err_temp_to_dest;
2213 if (AD_META_OPEN(adsp) || AD_META_OPEN(addp)) {
2214 struct adouble adtmp;
2215 bool opened_ads, opened_add;
2217 ad_init(&adtmp, vol);
2218 ad_init_offsets(&adtmp);
2220 if (!AD_META_OPEN(adsp)) {
2221 if (ad_open(adsp, p, ADFLAGS_HF) != 0)
2226 if (!AD_META_OPEN(addp)) {
2227 if (ad_open(addp, upath, ADFLAGS_HF) != 0)
2232 if (ad_copy_header(&adtmp, adsp) != 0)
2233 goto err_temp_to_dest;
2234 if (ad_copy_header(adsp, addp) != 0)
2235 goto err_temp_to_dest;
2236 if (ad_copy_header(addp, &adtmp) != 0)
2237 goto err_temp_to_dest;
2242 ad_close(adsp, ADFLAGS_HF);
2244 ad_close(addp, ADFLAGS_HF);
2247 /* FIXME: we should switch ressource fork too */
2249 /* here we need to reopen if crossdev */
2250 if (sid && ad_setid(addp, destst.st_dev, destst.st_ino, sid, sdir->d_did, vol->v_stamp))
2255 if (did && ad_setid(adsp, srcst.st_dev, srcst.st_ino, did, curdir->d_did, vol->v_stamp))
2260 /* change perms, src gets dest perm and vice versa */
2265 * we need to exchange ACL entries as well
2267 /* exchange_acls(vol, p, upath); */
2272 path->m_name = NULL;
2273 path->u_name = upath;
2275 setfilunixmode(vol, path, destst.st_mode);
2276 setfilowner(vol, destst.st_uid, destst.st_gid, path);
2283 setfilunixmode(vol, path, srcst.st_mode);
2284 setfilowner(vol, srcst.st_uid, srcst.st_gid, path);
2289 goto err_exchangefile;
2291 /* all this stuff is so that we can unwind a failed operation
2294 /* rename dest to temp */
2295 renamefile(vol, curdir, -1, upath, temp, temp, adsp);
2296 of_rename(vol, s_of, curdir, upath, curdir, temp);
2299 /* rename source back to dest */
2300 renamefile(vol, curdir, -1, p, upath, path->m_name, addp);
2301 of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
2304 /* rename temp back to source */
2305 renamefile(vol, curdir, -1, temp, p, spath, adsp);
2306 of_rename(vol, s_of, curdir, temp, sdir, spath);
2309 if ( !s_of && adsp && ad_meta_fileno(adsp) != -1 ) { /* META */
2310 ad_close(adsp, ADFLAGS_HF);
2312 if ( !d_of && addp && ad_meta_fileno(addp) != -1 ) {/* META */
2313 ad_close(addp, ADFLAGS_HF);
2317 if ((cached = dircache_search_by_did(vol, sid)) != NULL)
2318 (void)dir_remove(vol, cached);
2319 if ((cached = dircache_search_by_did(vol, did)) != NULL)
2320 (void)dir_remove(vol, cached);