2 * Copyright (c) 1990,1993 Regents of The University of Michigan.
3 * All Rights Reserved. See COPYRIGHT.
8 #endif /* HAVE_CONFIG_H */
16 #else /* STDC_HEADERS */
20 #endif /* HAVE_STRCHR */
21 char *strchr (), *strrchr ();
24 #define memcpy(d,s,n) bcopy ((s), (d), (n))
25 #define memmove(d,s,n) bcopy ((s), (d), (n))
26 #endif /* ! HAVE_MEMCPY */
27 #endif /* STDC_HEADERS */
31 #include <sys/param.h>
33 #include <atalk/adouble.h>
34 #include <atalk/vfs.h>
35 #include <atalk/logger.h>
36 #include <atalk/afp.h>
37 #include <atalk/util.h>
38 #include <atalk/cnid.h>
39 #include <atalk/unix.h>
40 #include <atalk/globals.h>
41 #include <atalk/fce_api.h>
43 #include "directory.h"
52 /* the format for the finderinfo fields (from IM: Toolbox Essentials):
53 * field bytes subfield bytes
56 * ioFlFndrInfo 16 -> type 4 type field
57 * creator 4 creator field
58 * flags 2 finder flags:
60 * location 4 location in window
61 * folder 2 window that contains file
63 * ioFlXFndrInfo 16 -> iconID 2 icon id
65 * script 1 script system
67 * commentID 2 comment id
68 * putawayID 4 home directory id
71 const u_char ufinderi[ADEDLEN_FINDERI] = {
72 0, 0, 0, 0, 0, 0, 0, 0,
73 1, 0, 0, 0, 0, 0, 0, 0,
74 0, 0, 0, 0, 0, 0, 0, 0,
75 0, 0, 0, 0, 0, 0, 0, 0
78 static const u_char old_ufinderi[] = {
79 'T', 'E', 'X', 'T', 'U', 'N', 'I', 'X'
82 /* ----------------------
84 static int default_type(void *finder)
86 if (!memcmp(finder, ufinderi, 8) || !memcmp(finder, old_ufinderi, 8))
91 /* FIXME path : unix or mac name ? (for now it's unix name ) */
92 void *get_finderinfo(const struct vol *vol, const char *upath, struct adouble *adp, void *data, int islink)
95 void *ad_finder = NULL;
99 ad_finder = ad_entry(adp, ADEID_FINDERI);
102 memcpy(data, ad_finder, ADEDLEN_FINDERI);
104 if (default_type(ad_finder))
108 memcpy(data, ufinderi, ADEDLEN_FINDERI);
110 if (vol_inv_dots(vol) && *upath == '.') { /* make it invisible */
113 ashort = htons(FINDERINFO_INVISIBLE);
114 memcpy((char *)data + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
120 memcpy(&linkflag, (char *)data + FINDERINFO_FRFLAGOFF, 2);
121 linkflag |= htons(FINDERINFO_ISALIAS);
122 memcpy((char *)data + FINDERINFO_FRFLAGOFF, &linkflag, 2);
123 memcpy((char *)data + FINDERINFO_FRTYPEOFF,"slnk",4);
124 memcpy((char *)data + FINDERINFO_FRCREATOFF,"rhap",4);
128 /** Only enter if no appledouble information and no finder information found. */
129 if (chk_ext && (em = getextmap( upath ))) {
130 memcpy(data, em->em_type, sizeof( em->em_type ));
131 memcpy((char *)data + 4, em->em_creator, sizeof(em->em_creator));
136 /* ---------------------
138 char *set_name(const struct vol *vol, char *data, cnid_t pid, char *name, cnid_t id, u_int32_t utf8)
143 aint = strlen( name );
147 if (utf8_encoding()) {
148 /* but name is an utf8 mac name */
151 /* global static variable... */
153 if (!(u = mtoupath(vol, name, pid, 1)) || !(m = utompath(vol, u, id, 0))) {
162 if (aint > MACFILELEN)
169 if (aint > UTF8FILELEN_EARLY) /* FIXME safeguard, anyway if no ascii char it's game over*/
170 aint = UTF8FILELEN_EARLY;
172 utf8 = vol->v_kTextEncoding;
173 memcpy(data, &utf8, sizeof(utf8));
174 data += sizeof(utf8);
177 memcpy(data, &temp, sizeof(temp));
178 data += sizeof(temp);
181 memcpy( data, src, aint );
191 * FIXME: PDINFO is UTF8 and doesn't need adp
193 #define PARAM_NEED_ADP(b) ((b) & ((1 << FILPBIT_ATTR) |\
194 (1 << FILPBIT_CDATE) |\
195 (1 << FILPBIT_MDATE) |\
196 (1 << FILPBIT_BDATE) |\
197 (1 << FILPBIT_FINFO) |\
198 (1 << FILPBIT_RFLEN) |\
199 (1 << FILPBIT_EXTRFLEN) |\
200 (1 << FILPBIT_PDINFO) |\
201 (1 << FILPBIT_FNUM) |\
202 (1 << FILPBIT_UNIXPR)))
205 * @brief Get CNID for did/upath args both from database and adouble file
207 * 1. Get the objects CNID as stored in its adouble file
208 * 2. Get the objects CNID from the database
209 * 3. If there's a problem with a "dbd" database, fallback to "tdb" in memory
210 * 4. In case 2 and 3 differ, store 3 in the adouble file
212 * @param vol (rw) volume
213 * @param adp (rw) adouble struct of object upath, might be NULL
214 * @param st (r) stat of upath, must NOT be NULL
215 * @param did (r) parent CNID of upath
216 * @param upath (r) name of object
217 * @param len (r) strlen of upath
219 uint32_t get_id(struct vol *vol,
221 const struct stat *st,
226 static int first = 1; /* mark if this func is called the first time */
228 u_int32_t dbcnid = CNID_INVALID;
231 if (vol->v_cdb != NULL) {
232 /* prime aint with what we think is the cnid, set did to zero for
233 catching moved files */
234 adcnid = ad_getid(adp, st->st_dev, st->st_ino, 0, vol->v_stamp); /* (1) */
236 dbcnid = cnid_add(vol->v_cdb, st, did, upath, len, adcnid); /* (2) */
237 /* Throw errors if cnid_add fails. */
238 if (dbcnid == CNID_INVALID) {
240 case CNID_ERR_CLOSE: /* the db is closed */
243 LOG(log_error, logtype_afpd, "get_id: Incorrect parameters passed to cnid_add");
244 afp_errno = AFPERR_PARAM;
247 afp_errno = AFPERR_PARAM;
250 /* Close CNID backend if "dbd" and switch to temp in-memory "tdb" */
251 /* we have to do it here for "dbd" because it uses "lazy opening" */
252 /* In order to not end in a loop somehow with goto restart below */
254 if (first && (strcmp(vol->v_cnidscheme, "dbd") == 0)) { /* (3) */
255 cnid_close(vol->v_cdb);
256 free(vol->v_cnidscheme);
257 vol->v_cnidscheme = strdup("tdb");
259 int flags = CNID_FLAG_MEMORY;
260 if ((vol->v_flags & AFPVOL_NODEV)) {
261 flags |= CNID_FLAG_NODEV;
263 LOG(log_error, logtype_afpd, "Reopen volume %s using in memory temporary CNID DB.",
265 vol->v_cdb = cnid_open(vol->v_path, vol->v_umask, "tdb", flags, NULL, NULL);
267 vol->v_flags &= ~AFPVOL_CACHE;
268 if (!(vol->v_flags & AFPVOL_TM)) {
269 vol->v_flags |= AFPVOL_RO;
270 setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB instead."
271 "Check server messages for details. Switching to read-only mode.");
272 kill(getpid(), SIGUSR2);
274 goto restart; /* now try again with the temp CNID db */
276 setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB failed too!"
277 "Check server messages for details, can't recover from this state!");
280 afp_errno = AFPERR_MISC;
284 else if (adp && (adcnid != dbcnid)) { /* 4 */
285 /* Update the ressource fork. For a folder adp is always null */
286 LOG(log_debug, logtype_afpd, "get_id(%s/%s): calling ad_setid(old: %u, new: %u)",
287 getcwdpath(), upath, htonl(adcnid), htonl(dbcnid));
288 if (ad_setid(adp, st->st_dev, st->st_ino, dbcnid, did, vol->v_stamp)) {
299 /* -------------------------- */
300 int getmetadata(struct vol *vol,
302 struct path *path, struct dir *dir,
303 char *buf, size_t *buflen, struct adouble *adp)
305 char *data, *l_nameoff = NULL, *upath;
306 char *utf_nameoff = NULL;
311 u_char achar, fdType[4];
316 LOG(log_debug, logtype_afpd, "getmetadata(\"%s\")", path->u_name);
318 upath = path->u_name;
322 if ( ((bitmap & ( (1 << FILPBIT_FINFO)|(1 << FILPBIT_LNAME)|(1 <<FILPBIT_PDINFO) ) ) && !path->m_name)
323 || (bitmap & ( (1 << FILPBIT_LNAME) ) && utf8_encoding()) /* FIXME should be m_name utf8 filename */
324 || (bitmap & (1 << FILPBIT_FNUM))) {
327 struct dir *cachedfile;
328 int len = strlen(upath);
329 if ((cachedfile = dircache_search_by_name(vol, dir, upath, len)) != NULL)
330 id = cachedfile->d_did;
332 id = get_id(vol, adp, st, dir->d_did, upath, len);
334 /* Add it to the cache */
335 LOG(log_debug, logtype_afpd, "getmetadata: caching: did:%u, \"%s\", cnid:%u",
336 ntohl(dir->d_did), upath, ntohl(id));
338 /* Get macname from unixname first */
339 if (path->m_name == NULL) {
340 if ((path->m_name = utompath(vol, upath, id, utf8_encoding())) == NULL) {
341 LOG(log_error, logtype_afpd, "getmetadata: utompath error");
347 if (((fullpath = bstrcpy(dir->d_fullpath)) == NULL)
348 || (bconchar(fullpath, '/') != BSTR_OK)
349 || (bcatcstr(fullpath, upath)) != BSTR_OK) {
350 LOG(log_error, logtype_afpd, "getmetadata: fullpath: %s", strerror(errno));
354 if ((cachedfile = dir_new(path->m_name, upath, vol, dir->d_did, id, fullpath, st)) == NULL) {
355 LOG(log_error, logtype_afpd, "getmetadata: error from dir_new");
359 if ((dircache_add(vol, cachedfile)) != 0) {
360 LOG(log_error, logtype_afpd, "getmetadata: fatal dircache error");
368 if (id == CNID_INVALID)
372 path->m_name = utompath(vol, upath, id, utf8_encoding());
375 while ( bitmap != 0 ) {
376 while (( bitmap & 1 ) == 0 ) {
384 ad_getattr(adp, &ashort);
385 } else if (vol_inv_dots(vol) && *upath == '.') {
386 ashort = htons(ATTRBIT_INVISIBLE);
390 /* FIXME do we want a visual clue if the file is read only
393 accessmode(vol, ".", &ma, dir , NULL);
394 if ((ma.ma_user & AR_UWRITE)) {
395 accessmode(vol, upath, &ma, dir , st);
396 if (!(ma.ma_user & AR_UWRITE)) {
397 ashort |= htons(ATTRBIT_NOWRITE);
401 memcpy(data, &ashort, sizeof( ashort ));
402 data += sizeof( ashort );
403 LOG(log_debug, logtype_afpd, "metadata('%s'): AFP Attributes: %04x",
404 path->u_name, ntohs(ashort));
408 memcpy(data, &dir->d_did, sizeof( u_int32_t ));
409 data += sizeof( u_int32_t );
410 LOG(log_debug, logtype_afpd, "metadata('%s'): Parent DID: %u",
411 path->u_name, ntohl(dir->d_did));
415 if (!adp || (ad_getdate(adp, AD_DATE_CREATE, &aint) < 0))
416 aint = AD_DATE_FROM_UNIX(st->st_mtime);
417 memcpy(data, &aint, sizeof( aint ));
418 data += sizeof( aint );
422 if ( adp && (ad_getdate(adp, AD_DATE_MODIFY, &aint) == 0)) {
423 if ((st->st_mtime > AD_DATE_TO_UNIX(aint))) {
424 aint = AD_DATE_FROM_UNIX(st->st_mtime);
427 aint = AD_DATE_FROM_UNIX(st->st_mtime);
429 memcpy(data, &aint, sizeof( int ));
430 data += sizeof( int );
434 if (!adp || (ad_getdate(adp, AD_DATE_BACKUP, &aint) < 0))
435 aint = AD_DATE_START;
436 memcpy(data, &aint, sizeof( int ));
437 data += sizeof( int );
441 get_finderinfo(vol, upath, adp, (char *)data,S_ISLNK(st->st_mode));
442 data += ADEDLEN_FINDERI;
447 data += sizeof( u_int16_t );
451 memset(data, 0, sizeof(u_int16_t));
452 data += sizeof( u_int16_t );
456 memcpy(data, &id, sizeof( id ));
457 data += sizeof( id );
458 LOG(log_debug, logtype_afpd, "metadata('%s'): CNID: %u",
459 path->u_name, ntohl(id));
463 if (st->st_size > 0xffffffff)
466 aint = htonl( st->st_size );
467 memcpy(data, &aint, sizeof( aint ));
468 data += sizeof( aint );
473 if (adp->ad_rlen > 0xffffffff)
476 aint = htonl( adp->ad_rlen);
480 memcpy(data, &aint, sizeof( aint ));
481 data += sizeof( aint );
484 /* Current client needs ProDOS info block for this file.
485 Use simple heuristic and let the Mac "type" string tell
486 us what the PD file code should be. Everything gets a
487 subtype of 0x0000 unless the original value was hashed
488 to "pXYZ" when we created it. See IA, Ver 2.
489 <shirsch@adelphia.net> */
490 case FILPBIT_PDINFO :
491 if (afp_version >= 30) { /* UTF8 name */
492 utf8 = kTextEncodingUTF8;
494 data += sizeof( u_int16_t );
496 memcpy(data, &aint, sizeof( aint ));
497 data += sizeof( aint );
501 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
503 if ( memcmp( fdType, "TEXT", 4 ) == 0 ) {
507 else if ( memcmp( fdType, "PSYS", 4 ) == 0 ) {
511 else if ( memcmp( fdType, "PS16", 4 ) == 0 ) {
515 else if ( memcmp( fdType, "BINA", 4 ) == 0 ) {
519 else if ( fdType[0] == 'p' ) {
521 ashort = (fdType[2] * 256) + fdType[3];
535 memcpy(data, &ashort, sizeof( ashort ));
536 data += sizeof( ashort );
537 memset(data, 0, sizeof( ashort ));
538 data += sizeof( ashort );
541 case FILPBIT_EXTDFLEN:
542 aint = htonl(st->st_size >> 32);
543 memcpy(data, &aint, sizeof( aint ));
544 data += sizeof( aint );
545 aint = htonl(st->st_size);
546 memcpy(data, &aint, sizeof( aint ));
547 data += sizeof( aint );
549 case FILPBIT_EXTRFLEN:
552 aint = htonl(adp->ad_rlen >> 32);
553 memcpy(data, &aint, sizeof( aint ));
554 data += sizeof( aint );
556 aint = htonl(adp->ad_rlen);
557 memcpy(data, &aint, sizeof( aint ));
558 data += sizeof( aint );
560 case FILPBIT_UNIXPR :
561 /* accessmode may change st_mode with ACLs */
562 accessmode(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(struct vol *vol,
617 struct path *path, struct dir *dir,
618 char *buf, size_t *buflen )
620 struct adouble ad, *adp;
624 LOG(log_debug, logtype_afpd, "getfilparams(\"%s\")", path->u_name);
626 opened = PARAM_NEED_ADP(bitmap);
631 int flags = (bitmap & (1 << FILPBIT_ATTR))?ADFLAGS_OPENFORKS:0;
633 adp = of_ad(vol, path, &ad);
634 upath = path->u_name;
636 if ( ad_metadata( upath, flags|ADFLAGS_CREATE, adp) < 0 ) {
639 LOG(log_error, logtype_afpd, "getfilparams(%s): %s: check resource fork permission?",
640 upath, strerror(errno));
641 return AFPERR_ACCESS;
643 LOG(log_error, logtype_afpd, "getfilparams(%s): bad resource fork", upath);
652 rc = getmetadata(vol, bitmap, path, dir, buf, buflen, adp);
654 ad_close_metadata( adp);
660 /* ----------------------------- */
661 int afp_createfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
663 struct adouble ad, *adp;
666 struct ofork *of = NULL;
668 int creatf, did, openf, retvalue = AFP_OK;
674 creatf = (unsigned char) *ibuf++;
676 memcpy(&vid, ibuf, sizeof( vid ));
677 ibuf += sizeof( vid );
679 if (NULL == ( vol = getvolbyvid( vid )) ) {
680 return( AFPERR_PARAM );
683 if (vol->v_flags & AFPVOL_RO)
686 memcpy(&did, ibuf, sizeof( did));
687 ibuf += sizeof( did );
689 if (NULL == ( dir = dirlookup( vol, did )) ) {
693 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
694 return get_afp_errno(AFPERR_PARAM);
697 if ( *s_path->m_name == '\0' ) {
698 return( AFPERR_BADTYPE );
701 upath = s_path->u_name;
703 /* if upath is deleted we already in trouble anyway */
704 if ((of = of_findname(s_path))) {
707 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
711 /* on a hard create, fail if file exists and is open */
714 openf = O_RDWR|O_CREAT|O_TRUNC;
716 /* on a soft create, if the file is open then ad_open won't fail
717 because open syscall is not called
722 openf = O_RDWR|O_CREAT|O_EXCL;
725 if ( ad_open( upath, ADFLAGS_DF|ADFLAGS_HF|ADFLAGS_NOHF|ADFLAGS_CREATE,
726 openf, 0666, adp) < 0 ) {
730 case ENOENT : /* we were already in 'did folder' so chdir() didn't fail */
731 return ( AFPERR_NOOBJ );
733 return( AFPERR_EXIST );
735 return( AFPERR_ACCESS );
738 return( AFPERR_DFULL );
740 return( AFPERR_PARAM );
743 if ( ad_reso_fileno( adp ) == -1 ) { /* Hard META / HF */
744 /* on noadouble volumes, just creating the data fork is ok */
745 if (vol_noadouble(vol)) {
746 ad_close( adp, ADFLAGS_DF );
747 goto createfile_done;
749 /* FIXME with hard create on an existing file, we already
750 * corrupted the data file.
752 netatalk_unlink( upath );
753 ad_close( adp, ADFLAGS_DF );
754 return AFPERR_ACCESS;
757 path = s_path->m_name;
758 ad_setname(adp, path);
761 if (lstat(upath, &st) != 0) {
762 LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): stat: %s",
763 upath, strerror(errno));
764 ad_close( adp, ADFLAGS_DF|ADFLAGS_HF);
768 (void)get_id(vol, adp, &st, dir->d_did, upath, strlen(upath));
772 fce_register_new_file(s_path);
774 ad_close( adp, ADFLAGS_DF|ADFLAGS_HF );
780 if (vol->v_flags & AFPVOL_DROPBOX) {
781 retvalue = matchfile2dirperms(upath, vol, did);
783 #endif /* DROPKLUDGE */
785 setvoltime(obj, vol );
790 int afp_setfilparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
796 u_int16_t vid, bitmap;
801 memcpy(&vid, ibuf, sizeof( vid ));
802 ibuf += sizeof( vid );
803 if (NULL == ( vol = getvolbyvid( vid )) ) {
804 return( AFPERR_PARAM );
807 if (vol->v_flags & AFPVOL_RO)
810 memcpy(&did, ibuf, sizeof( did ));
811 ibuf += sizeof( did );
812 if (NULL == ( dir = dirlookup( vol, did )) ) {
813 return afp_errno; /* was AFPERR_NOOBJ */
816 memcpy(&bitmap, ibuf, sizeof( bitmap ));
817 bitmap = ntohs( bitmap );
818 ibuf += sizeof( bitmap );
820 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
821 return get_afp_errno(AFPERR_PARAM);
824 if (path_isadir(s_path)) {
825 return( AFPERR_BADTYPE ); /* it's a directory */
828 if ( s_path->st_errno != 0 ) {
829 return( AFPERR_NOOBJ );
832 if ((u_long)ibuf & 1 ) {
836 if (AFP_OK == ( rc = setfilparams(vol, s_path, bitmap, ibuf )) ) {
837 setvoltime(obj, vol );
844 * cf AFP3.0.pdf page 252 for change_mdate and change_parent_mdate logic
847 extern struct path Cur_Path;
849 int setfilparams(struct vol *vol,
850 struct path *path, u_int16_t f_bitmap, char *buf )
852 struct adouble ad, *adp;
854 int bit, isad = 1, err = AFP_OK;
856 u_char achar, *fdType, xyy[4]; /* uninitialized, OK 310105 */
857 u_int16_t ashort, bshort, oshort;
860 u_int16_t upriv_bit = 0;
864 int change_mdate = 0;
865 int change_parent_mdate = 0;
870 u_int16_t bitmap = f_bitmap;
871 u_int32_t cdate,bdate;
872 u_char finder_buf[32];
875 LOG(log_debug9, logtype_afpd, "begin setfilparams:");
878 adp = of_ad(vol, path, &ad);
879 upath = path->u_name;
881 if (!vol_unix_priv(vol) && check_access(upath, OPENACC_WR ) < 0) {
882 return AFPERR_ACCESS;
885 /* with unix priv maybe we have to change adouble file priv first */
887 while ( bitmap != 0 ) {
888 while (( bitmap & 1 ) == 0 ) {
895 memcpy(&ashort, buf, sizeof( ashort ));
896 buf += sizeof( ashort );
900 memcpy(&cdate, buf, sizeof(cdate));
901 buf += sizeof( cdate );
904 memcpy(&newdate, buf, sizeof( newdate ));
905 buf += sizeof( newdate );
909 memcpy(&bdate, buf, sizeof( bdate));
910 buf += sizeof( bdate );
914 memcpy(finder_buf, buf, 32 );
915 if (memcmp(buf,"slnkrhap",8)==0 && !S_ISLNK(path->st.st_mode)){
920 char buf[PATH_MAX+1];
921 if ((fp=open(path->u_name,O_RDONLY))>=0){
922 if ((len=read(fp,buf,PATH_MAX+1))){
923 if (unlink(path->u_name)==0){
925 erc = symlink(buf, path->u_name);
934 goto setfilparam_done;
939 case FILPBIT_UNIXPR :
940 if (!vol_unix_priv(vol)) {
941 /* this volume doesn't use unix priv */
947 change_parent_mdate = 1;
949 memcpy( &aint, buf, sizeof( aint ));
950 f_uid = ntohl (aint);
951 buf += sizeof( aint );
952 memcpy( &aint, buf, sizeof( aint ));
953 f_gid = ntohl (aint);
954 buf += sizeof( aint );
955 setfilowner(vol, f_uid, f_gid, path);
957 memcpy( &upriv, buf, sizeof( upriv ));
958 buf += sizeof( upriv );
959 upriv = ntohl (upriv);
960 if ((upriv & S_IWUSR)) {
961 setfilunixmode(vol, path, upriv);
968 case FILPBIT_PDINFO :
969 if (afp_version < 30) { /* else it's UTF8 name */
972 /* Keep special case to support crlf translations */
973 if ((unsigned int) achar == 0x04) {
974 fdType = (u_char *)"TEXT";
977 xyy[0] = ( u_char ) 'p';
988 /* break while loop */
997 /* second try with adouble open
999 if ( ad_open_metadata( upath, 0, O_CREAT, adp) < 0) {
1000 LOG(log_debug, logtype_afpd, "setfilparams: ad_open_metadata error");
1002 * For some things, we don't need an adouble header:
1003 * - change of modification date
1004 * - UNIX privs (Bug-ID #2863424)
1006 if (!vol_noadouble(vol) && (f_bitmap & ~(1<<FILPBIT_MDATE | 1<<FILPBIT_UNIXPR))) {
1007 LOG(log_debug, logtype_afpd, "setfilparams: need adouble access");
1008 return AFPERR_ACCESS;
1010 LOG(log_debug, logtype_afpd, "setfilparams: no adouble perms, but only FILPBIT_MDATE and/or FILPBIT_UNIXPR");
1012 } else if ((ad_get_HF_flags( adp ) & O_CREAT) ) {
1013 ad_setname(adp, path->m_name);
1018 while ( bitmap != 0 ) {
1019 while (( bitmap & 1 ) == 0 ) {
1026 ad_getattr(adp, &bshort);
1028 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
1029 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
1033 if ((bshort & htons(ATTRBIT_INVISIBLE)) != (oshort & htons(ATTRBIT_INVISIBLE)))
1034 change_parent_mdate = 1;
1035 ad_setattr(adp, bshort);
1037 case FILPBIT_CDATE :
1038 ad_setdate(adp, AD_DATE_CREATE, cdate);
1040 case FILPBIT_MDATE :
1042 case FILPBIT_BDATE :
1043 ad_setdate(adp, AD_DATE_BACKUP, bdate);
1045 case FILPBIT_FINFO :
1046 if (default_type( ad_entry( adp, ADEID_FINDERI ))
1048 ((em = getextmap( path->m_name )) &&
1049 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1050 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1051 || ((em = getdefextmap()) &&
1052 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1053 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1055 memcpy(finder_buf, ufinderi, 8 );
1057 memcpy(ad_entry( adp, ADEID_FINDERI ), finder_buf, 32 );
1059 case FILPBIT_UNIXPR :
1061 setfilunixmode(vol, path, upriv);
1064 case FILPBIT_PDINFO :
1065 if (afp_version < 30) { /* else it's UTF8 name */
1066 memcpy(ad_entry( adp, ADEID_FINDERI ), fdType, 4 );
1067 memcpy(ad_entry( adp, ADEID_FINDERI ) + 4, "pdos", 4 );
1072 err = AFPERR_BITMAP;
1073 goto setfilparam_done;
1080 if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
1081 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1085 ad_setdate(adp, AD_DATE_MODIFY, newdate);
1086 ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
1092 ad_close_metadata( adp);
1096 if (change_parent_mdate && gettimeofday(&tv, NULL) == 0) {
1097 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1098 bitmap = 1<<FILPBIT_MDATE;
1099 setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
1103 LOG(log_debug9, logtype_afpd, "end setfilparams:");
1109 * renamefile and copyfile take the old and new unix pathnames
1110 * and the new mac name.
1112 * sdir_fd source dir fd to which src path is relative (for openat et al semantics)
1113 * passing -1 means this is not used, src path is a full path
1114 * src the source path
1115 * dst the dest filename in current dir
1116 * newname the dest mac name
1117 * adp adouble struct of src file, if open, or & zeroed one
1120 int renamefile(const struct vol *vol, int sdir_fd, char *src, char *dst, char *newname, struct adouble *adp)
1124 LOG(log_debug, logtype_afpd,
1125 "renamefile: src[%d, \"%s\"] -> dst[\"%s\"]", sdir_fd, src, dst);
1127 if ( unix_rename( sdir_fd, src, -1, dst ) < 0 ) {
1130 return( AFPERR_NOOBJ );
1133 return( AFPERR_ACCESS );
1135 return AFPERR_VLOCK;
1136 case EXDEV : /* Cross device move -- try copy */
1137 /* NOTE: with open file it's an error because after the copy we will
1138 * get two files, it's fixable for our process (eg reopen the new file, get the
1139 * locks, and so on. But it doesn't solve the case with a second process
1141 if (adp->ad_open_forks) {
1142 /* FIXME warning in syslog so admin'd know there's a conflict ?*/
1143 return AFPERR_OLOCK; /* little lie */
1145 if (AFP_OK != ( rc = copyfile(vol, vol, sdir_fd, src, dst, newname, NULL )) ) {
1146 /* on error copyfile delete dest */
1149 return deletefile(vol, sdir_fd, src, 0);
1151 return( AFPERR_PARAM );
1155 if (vol->vfs->vfs_renamefile(vol, sdir_fd, src, dst) < 0 ) {
1159 /* try to undo the data fork rename,
1160 * we know we are on the same device
1163 unix_rename(-1, dst, sdir_fd, src );
1164 /* return the first error */
1167 return AFPERR_NOOBJ;
1170 return AFPERR_ACCESS ;
1172 return AFPERR_VLOCK;
1174 return AFPERR_PARAM ;
1179 /* don't care if we can't open the newly renamed ressource fork
1181 if (!ad_open( dst, ADFLAGS_HF, O_RDWR, 0666, adp)) {
1182 ad_setname(adp, newname);
1184 ad_close( adp, ADFLAGS_HF );
1191 convert a Mac long name to an utf8 name,
1193 size_t mtoUTF8(const struct vol *vol, const char *src, size_t srclen, char *dest, size_t destlen)
1197 if ((size_t)-1 == (outlen = convert_string ( vol->v_maccharset, CH_UTF8_MAC, src, srclen, dest, destlen)) ) {
1203 /* ---------------- */
1204 int copy_path_name(const struct vol *vol, char *newname, char *ibuf)
1211 if ( type != 2 && !(afp_version >= 30 && type == 3) ) {
1217 if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
1218 if (afp_version >= 30) {
1219 /* convert it to UTF8
1221 if ((plen = mtoUTF8(vol, ibuf, plen, newname, AFPOBJ_TMPSIZ)) == (size_t)-1)
1225 strncpy( newname, ibuf, plen );
1226 newname[ plen ] = '\0';
1228 if (strlen(newname) != plen) {
1229 /* there's \0 in newname, e.g. it's a pathname not
1237 memcpy(&hint, ibuf, sizeof(hint));
1238 ibuf += sizeof(hint);
1240 memcpy(&len16, ibuf, sizeof(len16));
1241 ibuf += sizeof(len16);
1242 plen = ntohs(len16);
1245 if (plen > AFPOBJ_TMPSIZ) {
1248 strncpy( newname, ibuf, plen );
1249 newname[ plen ] = '\0';
1250 if (strlen(newname) != plen) {
1259 /* -----------------------------------
1261 int afp_copyfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1263 struct vol *s_vol, *d_vol;
1265 char *newname, *p, *upath;
1266 struct path *s_path;
1267 u_int32_t sdid, ddid;
1268 int err, retvalue = AFP_OK;
1269 u_int16_t svid, dvid;
1271 struct adouble ad, *adp;
1277 memcpy(&svid, ibuf, sizeof( svid ));
1278 ibuf += sizeof( svid );
1279 if (NULL == ( s_vol = getvolbyvid( svid )) ) {
1280 return( AFPERR_PARAM );
1283 memcpy(&sdid, ibuf, sizeof( sdid ));
1284 ibuf += sizeof( sdid );
1285 if (NULL == ( dir = dirlookup( s_vol, sdid )) ) {
1289 memcpy(&dvid, ibuf, sizeof( dvid ));
1290 ibuf += sizeof( dvid );
1291 memcpy(&ddid, ibuf, sizeof( ddid ));
1292 ibuf += sizeof( ddid );
1294 if (NULL == ( s_path = cname( s_vol, dir, &ibuf )) ) {
1295 return get_afp_errno(AFPERR_PARAM);
1297 if ( path_isadir(s_path) ) {
1298 return( AFPERR_BADTYPE );
1301 /* don't allow copies when the file is open.
1302 * XXX: the spec only calls for read/deny write access.
1303 * however, copyfile doesn't have any of that info,
1304 * and locks need to stay coherent. as a result,
1305 * we just balk if the file is opened already. */
1307 adp = of_ad(s_vol, s_path, &ad);
1309 if (ad_open(s_path->u_name , ADFLAGS_DF |ADFLAGS_HF | ADFLAGS_NOHF, O_RDONLY, 0, adp) < 0) {
1310 return AFPERR_DENYCONF;
1312 denyreadset = (getforkmode(adp, ADEID_DFORK, AD_FILELOCK_DENY_RD) != 0 ||
1313 getforkmode(adp, ADEID_RFORK, AD_FILELOCK_DENY_RD) != 0 );
1316 retvalue = AFPERR_DENYCONF;
1320 newname = obj->newtmp;
1321 strcpy( newname, s_path->m_name );
1323 p = ctoupath( s_vol, curdir, newname );
1325 retvalue = AFPERR_PARAM;
1330 /* FIXME svid != dvid && dvid's user can't read svid */
1332 if (NULL == ( d_vol = getvolbyvid( dvid )) ) {
1333 retvalue = AFPERR_PARAM;
1337 if (d_vol->v_flags & AFPVOL_RO) {
1338 retvalue = AFPERR_VLOCK;
1342 if (NULL == ( dir = dirlookup( d_vol, ddid )) ) {
1343 retvalue = afp_errno;
1347 if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) {
1348 retvalue = get_afp_errno(AFPERR_NOOBJ);
1352 if ( *s_path->m_name != '\0' ) {
1353 retvalue =path_error(s_path, AFPERR_NOOBJ);
1357 /* one of the handful of places that knows about the path type */
1358 if (copy_path_name(d_vol, newname, ibuf) < 0) {
1359 retvalue = AFPERR_PARAM;
1362 /* newname is always only a filename so curdir *is* its
1365 if (NULL == (upath = mtoupath(d_vol, newname, curdir->d_did, utf8_encoding()))) {
1366 retvalue =AFPERR_PARAM;
1370 if ( (err = copyfile(s_vol, d_vol, -1, p, upath , newname, adp)) < 0 ) {
1377 if (vol->v_flags & AFPVOL_DROPBOX) {
1378 retvalue=matchfile2dirperms(upath, vol, ddid); /* FIXME sdir or ddid */
1380 #endif /* DROPKLUDGE */
1382 setvoltime(obj, d_vol );
1385 ad_close( adp, ADFLAGS_DF |ADFLAGS_HF );
1389 /* ----------------------- */
1390 static int copy_all(const int dfd, const void *buf,
1396 LOG(log_debug9, logtype_afpd, "begin copy_all:");
1399 while (buflen > 0) {
1400 if ((cc = write(dfd, buf, buflen)) < 0) {
1412 LOG(log_debug9, logtype_afpd, "end copy_all:");
1418 /* --------------------------
1419 * copy only the fork data stream
1421 static int copy_fork(int eid, struct adouble *add, struct adouble *ads)
1428 if (eid == ADEID_DFORK) {
1429 sfd = ad_data_fileno(ads);
1430 dfd = ad_data_fileno(add);
1433 sfd = ad_reso_fileno(ads);
1434 dfd = ad_reso_fileno(add);
1437 if ((off_t)-1 == lseek(sfd, ad_getentryoff(ads, eid), SEEK_SET))
1440 if ((off_t)-1 == lseek(dfd, ad_getentryoff(add, eid), SEEK_SET))
1443 #if 0 /* ifdef SENDFILE_FLAVOR_LINUX */
1444 /* doesn't work With 2.6 FIXME, only check for EBADFD ? */
1448 #define BUF 128*1024*1024
1450 if (fstat(sfd, &st) == 0) {
1453 if ( offset >= st.st_size) {
1456 size = (st.st_size -offset > BUF)?BUF:st.st_size -offset;
1457 if ((cc = sys_sendfile(dfd, sfd, &offset, size)) < 0) {
1460 case EINVAL: /* there's no guarantee that all fs support sendfile */
1469 lseek(sfd, offset, SEEK_SET);
1473 if ((cc = read(sfd, filebuf, sizeof(filebuf))) < 0) {
1480 if (!cc || ((err = copy_all(dfd, filebuf, cc)) < 0)) {
1487 /* ----------------------------------
1488 * if newname is NULL (from directory.c) we don't want to copy the resource fork.
1489 * because we are doing it elsewhere.
1490 * currently if newname is NULL then adp is NULL.
1492 int copyfile(const struct vol *s_vol,
1493 const struct vol *d_vol,
1498 struct adouble *adp)
1500 struct adouble ads, add;
1507 LOG(log_debug, logtype_afpd, "copyfile(sfd:%d,s:'%s',d:'%s',n:'%s')",
1508 sfd, src, dst, newname);
1511 ad_init(&ads, s_vol->v_adouble, s_vol->v_ad_options);
1515 adflags = ADFLAGS_DF;
1517 adflags |= ADFLAGS_HF;
1520 if (ad_openat(sfd, src, adflags | ADFLAGS_NOHF, O_RDONLY, 0, adp) < 0) {
1525 if (ad_meta_fileno(adp) == -1 && ad_reso_fileno(adp) == -1) { /* META / HF */
1526 /* no resource fork, don't create one for dst file */
1527 adflags &= ~ADFLAGS_HF;
1530 stat_result = fstat(ad_data_fileno(adp), &st); /* saving stat exit code, thus saving us on one more stat later on */
1532 if (stat_result < 0) {
1533 /* unlikely but if fstat fails, the default file mode will be 0666. */
1534 st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
1537 ad_init(&add, d_vol->v_adouble, d_vol->v_ad_options);
1538 if (ad_open(dst , adflags, O_RDWR|O_CREAT|O_EXCL, st.st_mode, &add) < 0) {
1540 ad_close( adp, adflags );
1541 if (EEXIST != ret_err) {
1542 deletefile(d_vol, -1, dst, 0);
1545 return AFPERR_EXIST;
1549 * XXX if the source and the dest don't use the same resource type it's broken
1551 if (ad_reso_fileno(adp) == -1 || 0 == (err = copy_fork(ADEID_RFORK, &add, adp))){
1552 /* copy the data fork */
1553 if ((err = copy_fork(ADEID_DFORK, &add, adp)) == 0) {
1554 if (ad_meta_fileno(adp) != -1)
1555 err = d_vol->vfs->vfs_copyfile(d_vol, sfd, src, dst);
1563 if (!ret_err && newname && (adflags & ADFLAGS_HF)) {
1564 /* set the new name in the resource fork */
1565 ad_copy_header(&add, adp);
1566 ad_setname(&add, newname);
1569 ad_close( adp, adflags );
1571 if (ad_close( &add, adflags ) <0) {
1576 deletefile(d_vol, -1, dst, 0);
1578 else if (stat_result == 0) {
1579 /* set dest modification date to src date */
1582 ut.actime = ut.modtime = st.st_mtime;
1584 /* FIXME netatalk doesn't use resource fork file date
1585 * but maybe we should set its modtime too.
1590 switch ( ret_err ) {
1596 return AFPERR_DFULL;
1598 return AFPERR_NOOBJ;
1600 return AFPERR_ACCESS;
1602 return AFPERR_VLOCK;
1604 return AFPERR_PARAM;
1608 /* -----------------------------------
1609 vol: not NULL delete cnid entry. then we are in curdir and file is a only filename
1610 checkAttrib: 1 check kFPDeleteInhibitBit (deletfile called by afp_delete)
1612 when deletefile is called we don't have lock on it, file is closed (for us)
1613 untrue if called by renamefile
1615 ad_open always try to open file RDWR first and ad_lock takes care of
1616 WRITE lock on read only file.
1619 static int check_attrib(struct adouble *adp)
1621 u_int16_t bshort = 0;
1623 ad_getattr(adp, &bshort);
1625 * Does kFPDeleteInhibitBit (bit 8) set?
1627 if ((bshort & htons(ATTRBIT_NODELETE))) {
1628 return AFPERR_OLOCK;
1630 if ((bshort & htons(ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
1636 * dirfd can be used for unlinkat semantics
1638 int deletefile(const struct vol *vol, int dirfd, char *file, int checkAttrib)
1641 struct adouble *adp = NULL;
1642 int adflags, err = AFP_OK;
1645 LOG(log_debug, logtype_afpd, "deletefile('%s')", file);
1647 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
1649 /* was EACCESS error try to get only metadata */
1650 /* we never want to create a resource fork here, we are going to delete it
1651 * moreover sometimes deletefile is called with a no existent file and
1652 * ad_open would create a 0 byte resource fork
1654 if ( ad_metadataat(dirfd, file, ADFLAGS_OPENFORKS, &ad) == 0 ) {
1655 if ((err = check_attrib(&ad))) {
1656 ad_close_metadata(&ad);
1663 /* try to open both forks at once */
1664 adflags = ADFLAGS_DF;
1665 if ( ad_openat(dirfd, file, adflags |ADFLAGS_HF|ADFLAGS_NOHF, O_RDONLY, 0, &ad ) < 0 ) {
1670 case EACCES: /* maybe it's a file with no write mode for us */
1671 break; /* was return AFPERR_ACCESS;*/
1684 if ( adp && ad_reso_fileno( adp ) != -1 ) { /* there's a resource fork */
1685 adflags |= ADFLAGS_HF;
1686 /* FIXME we have a pb here because we want to know if a file is open
1687 * there's a 'priority inversion' if you can't open the ressource fork RW
1688 * you can delete it if it's open because you can't get a write lock.
1690 * ADLOCK_FILELOCK means the whole ressource fork, not only after the
1693 * FIXME it doesn't work for RFORK open read only and fork open without deny mode
1695 if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
1701 if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
1703 } else if (!(err = vol->vfs->vfs_deletefile(vol, dirfd, file)) && !(err = netatalk_unlinkat(dirfd, file )) ) {
1705 if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file)))) {
1706 cnid_delete(vol->v_cdb, id);
1712 ad_close_metadata(&ad);
1715 ad_close( &ad, adflags ); /* ad_close removes locks if any */
1720 /* ------------------------------------ */
1721 /* return a file id */
1722 int afp_createid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1731 struct path *s_path;
1737 memcpy(&vid, ibuf, sizeof(vid));
1738 ibuf += sizeof(vid);
1740 if (NULL == ( vol = getvolbyvid( vid )) ) {
1741 return( AFPERR_PARAM);
1744 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1748 if (vol->v_flags & AFPVOL_RO)
1749 return AFPERR_VLOCK;
1751 memcpy(&did, ibuf, sizeof( did ));
1752 ibuf += sizeof(did);
1754 if (NULL == ( dir = dirlookup( vol, did )) ) {
1755 return afp_errno; /* was AFPERR_PARAM */
1758 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
1759 return get_afp_errno(AFPERR_NOOBJ); /* was AFPERR_PARAM */
1762 if ( path_isadir(s_path) ) {
1763 return( AFPERR_BADTYPE );
1766 upath = s_path->u_name;
1767 switch (s_path->st_errno) {
1769 break; /* success */
1772 return AFPERR_ACCESS;
1774 return AFPERR_NOOBJ;
1776 return AFPERR_PARAM;
1779 if ((id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath)))) {
1780 memcpy(rbuf, &id, sizeof(id));
1781 *rbuflen = sizeof(id);
1782 return AFPERR_EXISTID;
1785 if ((id = get_id(vol, NULL, st, did, upath, len)) != CNID_INVALID) {
1786 memcpy(rbuf, &id, sizeof(id));
1787 *rbuflen = sizeof(id);
1794 /* ------------------------------- */
1800 static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
1803 struct reenum *param = data;
1804 struct vol *vol = param->vol;
1805 cnid_t did = param->did;
1808 if ( lstat(de->d_name, &path.st)<0 )
1811 /* update or add to cnid */
1812 aint = cnid_add(vol->v_cdb, &path.st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */
1814 #if AD_VERSION > AD_VERSION1
1815 if (aint != CNID_INVALID && !S_ISDIR(path.st.st_mode)) {
1816 struct adouble ad, *adp;
1820 path.u_name = de->d_name;
1822 adp = of_ad(vol, &path, &ad);
1824 if ( ad_open_metadata( de->d_name, 0, 0, adp ) < 0 ) {
1827 if (ad_setid(adp, path.st.st_dev, path.st.st_ino, aint, did, vol->v_stamp)) {
1830 ad_close_metadata(adp);
1832 #endif /* AD_VERSION > AD_VERSION1 */
1837 /* --------------------
1838 * Ok the db is out of synch with the dir.
1839 * but if it's a deleted file we don't want to do it again and again.
1842 reenumerate_id(struct vol *vol, char *name, struct dir *dir)
1848 if (vol->v_cdb == NULL) {
1852 /* FIXME use of_statdir ? */
1853 if (lstat(name, &st)) {
1857 if (dirreenumerate(dir, &st)) {
1858 /* we already did it once and the dir haven't been modified */
1859 return dir->d_offcnt;
1863 data.did = dir->d_did;
1864 if ((ret = for_each_dirent(vol, name, reenumerate_loop, (void *)&data)) >= 0) {
1865 setdiroffcnt(curdir, &st, ret);
1866 dir->d_flags |= DIRF_CNID;
1872 /* ------------------------------
1873 resolve a file id */
1874 int afp_resolveid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1883 u_int16_t vid, bitmap;
1885 static char buffer[12 + MAXPATHLEN + 1];
1886 int len = 12 + MAXPATHLEN + 1;
1891 memcpy(&vid, ibuf, sizeof(vid));
1892 ibuf += sizeof(vid);
1894 if (NULL == ( vol = getvolbyvid( vid )) ) {
1895 return( AFPERR_PARAM);
1898 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1902 memcpy(&id, ibuf, sizeof( id ));
1907 /* some MacOS versions after a catsearch do a *lot* of afp_resolveid with 0 */
1911 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1912 return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
1915 if (NULL == ( dir = dirlookup( vol, id )) ) {
1916 return AFPERR_NOID; /* idem AFPERR_PARAM */
1918 if (movecwd(vol, dir) < 0) {
1922 return AFPERR_ACCESS;
1926 return AFPERR_PARAM;
1930 memset(&path, 0, sizeof(path));
1931 path.u_name = upath;
1932 if ( of_stat(&path) < 0 ) {
1934 /* with nfs and our working directory is deleted */
1935 if (errno == ESTALE) {
1939 if ( errno == ENOENT && !retry) {
1940 /* cnid db is out of sync, reenumerate the directory and update ids */
1941 reenumerate_id(vol, ".", dir);
1949 return AFPERR_ACCESS;
1953 return AFPERR_PARAM;
1957 /* directories are bad */
1958 if (S_ISDIR(path.st.st_mode)) {
1959 /* OS9 and OSX don't return the same error code */
1960 return (afp_version >=30)?AFPERR_NOID:AFPERR_BADTYPE;
1963 memcpy(&bitmap, ibuf, sizeof(bitmap));
1964 bitmap = ntohs( bitmap );
1965 if (NULL == (path.m_name = utompath(vol, upath, cnid, utf8_encoding()))) {
1969 if (AFP_OK != (err = getfilparams(vol, bitmap, &path , curdir,
1970 rbuf + sizeof(bitmap), &buflen))) {
1973 *rbuflen = buflen + sizeof(bitmap);
1974 memcpy(rbuf, ibuf, sizeof(bitmap));
1979 /* ------------------------------ */
1980 int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1990 static char buffer[12 + MAXPATHLEN + 1];
1991 int len = 12 + MAXPATHLEN + 1;
1996 memcpy(&vid, ibuf, sizeof(vid));
1997 ibuf += sizeof(vid);
1999 if (NULL == ( vol = getvolbyvid( vid )) ) {
2000 return( AFPERR_PARAM);
2003 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
2007 if (vol->v_flags & AFPVOL_RO)
2008 return AFPERR_VLOCK;
2010 memcpy(&id, ibuf, sizeof( id ));
2014 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
2018 if (NULL == ( dir = dirlookup( vol, id )) ) {
2019 if (afp_errno == AFPERR_NOOBJ) {
2023 return( AFPERR_PARAM );
2027 if ((movecwd(vol, dir) < 0) || (lstat(upath, &st) < 0)) {
2031 return AFPERR_ACCESS;
2036 /* still try to delete the id */
2040 return AFPERR_PARAM;
2043 else if (S_ISDIR(st.st_mode)) /* directories are bad */
2044 return AFPERR_BADTYPE;
2047 if (cnid_delete(vol->v_cdb, fileid)) {
2050 return AFPERR_VLOCK;
2053 return AFPERR_ACCESS;
2055 return AFPERR_PARAM;
2062 /* ------------------------------ */
2063 static struct adouble *find_adouble(struct path *path, struct ofork **of, struct adouble *adp)
2067 if (path->st_errno) {
2068 switch (path->st_errno) {
2070 afp_errno = AFPERR_NOID;
2074 afp_errno = AFPERR_ACCESS;
2077 afp_errno = AFPERR_PARAM;
2082 /* we use file_access both for legacy Mac perm and
2083 * for unix privilege, rename will take care of folder perms
2085 if (file_access(path, OPENACC_WR ) < 0) {
2086 afp_errno = AFPERR_ACCESS;
2090 if ((*of = of_findname(path))) {
2091 /* reuse struct adouble so it won't break locks */
2095 ret = ad_open( path->u_name, ADFLAGS_HF, O_RDONLY, 0, adp);
2097 if ( !ret && ad_reso_fileno(adp) != -1 && !(adp->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
2099 * The user must have the Read & Write privilege for both files in order to use this command.
2101 ad_close(adp, ADFLAGS_HF);
2102 afp_errno = AFPERR_ACCESS;
2109 #define APPLETEMP ".AppleTempXXXXXX"
2111 int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
2113 struct stat srcst, destst;
2115 struct dir *dir, *sdir;
2116 char *spath, temp[17], *p;
2117 char *supath, *upath;
2122 struct adouble *adsp = NULL;
2123 struct adouble *addp = NULL;
2124 struct ofork *s_of = NULL;
2125 struct ofork *d_of = NULL;
2138 memcpy(&vid, ibuf, sizeof(vid));
2139 ibuf += sizeof(vid);
2141 if (NULL == ( vol = getvolbyvid( vid )) ) {
2142 return( AFPERR_PARAM);
2145 if ((vol->v_flags & AFPVOL_RO))
2146 return AFPERR_VLOCK;
2148 /* source and destination dids */
2149 memcpy(&sid, ibuf, sizeof(sid));
2150 ibuf += sizeof(sid);
2151 memcpy(&did, ibuf, sizeof(did));
2152 ibuf += sizeof(did);
2155 if (NULL == (dir = dirlookup( vol, sid )) ) {
2156 return afp_errno; /* was AFPERR_PARAM */
2159 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2160 return get_afp_errno(AFPERR_NOOBJ);
2163 if ( path_isadir(path) ) {
2164 return AFPERR_BADTYPE; /* it's a dir */
2167 /* save some stuff */
2170 spath = obj->oldtmp;
2171 supath = obj->newtmp;
2172 strcpy(spath, path->m_name);
2173 strcpy(supath, path->u_name); /* this is for the cnid changing */
2174 p = absupath( vol, sdir, supath);
2176 /* pathname too long */
2177 return AFPERR_PARAM ;
2180 ad_init(&ads, vol->v_adouble, vol->v_ad_options);
2181 if (!(adsp = find_adouble( path, &s_of, &ads))) {
2185 /* ***** from here we may have resource fork open **** */
2187 /* look for the source cnid. if it doesn't exist, don't worry about
2189 sid = cnid_lookup(vol->v_cdb, &srcst, sdir->d_did, supath,slen = strlen(supath));
2191 if (NULL == ( dir = dirlookup( vol, did )) ) {
2192 err = afp_errno; /* was AFPERR_PARAM */
2193 goto err_exchangefile;
2196 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2197 err = get_afp_errno(AFPERR_NOOBJ);
2198 goto err_exchangefile;
2201 if ( path_isadir(path) ) {
2202 err = AFPERR_BADTYPE;
2203 goto err_exchangefile;
2206 /* FPExchangeFiles is the only call that can return the SameObj
2208 if ((curdir == sdir) && strcmp(spath, path->m_name) == 0) {
2209 err = AFPERR_SAMEOBJ;
2210 goto err_exchangefile;
2213 ad_init(&add, vol->v_adouble, vol->v_ad_options);
2214 if (!(addp = find_adouble( path, &d_of, &add))) {
2216 goto err_exchangefile;
2220 /* they are not on the same device and at least one is open
2221 * FIXME broken for for crossdev and adouble v2
2224 crossdev = (srcst.st_dev != destst.st_dev);
2225 if (/* (d_of || s_of) && */ crossdev) {
2227 goto err_exchangefile;
2230 /* look for destination id. */
2231 upath = path->u_name;
2232 did = cnid_lookup(vol->v_cdb, &destst, curdir->d_did, upath, dlen = strlen(upath));
2234 /* construct a temp name.
2235 * NOTE: the temp file will be in the dest file's directory. it
2236 * will also be inaccessible from AFP. */
2237 memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
2238 if (!mktemp(temp)) {
2240 goto err_exchangefile;
2244 /* FIXME we need to close fork for copy, both s_of and d_of are null */
2245 ad_close(adsp, ADFLAGS_HF);
2246 ad_close(addp, ADFLAGS_HF);
2249 /* now, quickly rename the file. we error if we can't. */
2250 if ((err = renamefile(vol, -1, p, temp, temp, adsp)) != AFP_OK)
2251 goto err_exchangefile;
2252 of_rename(vol, s_of, sdir, spath, curdir, temp);
2254 /* rename destination to source */
2255 if ((err = renamefile(vol, -1, upath, p, spath, addp)) != AFP_OK)
2256 goto err_src_to_tmp;
2257 of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
2259 /* rename temp to destination */
2260 if ((err = renamefile(vol, -1, temp, upath, path->m_name, adsp)) != AFP_OK)
2261 goto err_dest_to_src;
2262 of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
2264 /* id's need switching. src -> dest and dest -> src.
2265 * we need to re-stat() if it was a cross device copy.
2268 cnid_delete(vol->v_cdb, sid);
2270 cnid_delete(vol->v_cdb, did);
2272 if ((did && ( (crossdev && lstat( upath, &srcst) < 0) ||
2273 cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
2275 (sid && ( (crossdev && lstat(p, &destst) < 0) ||
2276 cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
2281 err = AFPERR_ACCESS;
2286 goto err_temp_to_dest;
2289 /* here we need to reopen if crossdev */
2290 if (sid && ad_setid(addp, destst.st_dev, destst.st_ino, sid, sdir->d_did, vol->v_stamp))
2295 if (did && ad_setid(adsp, srcst.st_dev, srcst.st_ino, did, curdir->d_did, vol->v_stamp))
2300 /* change perms, src gets dest perm and vice versa */
2305 LOG(log_error, logtype_afpd, "seteuid failed %s", strerror(errno));
2306 err = AFP_OK; /* ignore error */
2307 goto err_temp_to_dest;
2311 * we need to exchange ACL entries as well
2313 /* exchange_acls(vol, p, upath); */
2318 path->m_name = NULL;
2319 path->u_name = upath;
2321 setfilunixmode(vol, path, destst.st_mode);
2322 setfilowner(vol, destst.st_uid, destst.st_gid, path);
2329 setfilunixmode(vol, path, srcst.st_mode);
2330 setfilowner(vol, srcst.st_uid, srcst.st_gid, path);
2332 if ( setegid(gid) < 0 || seteuid(uid) < 0) {
2333 LOG(log_error, logtype_afpd, "can't seteuid back %s", strerror(errno));
2338 goto err_exchangefile;
2340 /* all this stuff is so that we can unwind a failed operation
2343 /* rename dest to temp */
2344 renamefile(vol, -1, upath, temp, temp, adsp);
2345 of_rename(vol, s_of, curdir, upath, curdir, temp);
2348 /* rename source back to dest */
2349 renamefile(vol, -1, p, upath, path->m_name, addp);
2350 of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
2353 /* rename temp back to source */
2354 renamefile(vol, -1, temp, p, spath, adsp);
2355 of_rename(vol, s_of, curdir, temp, sdir, spath);
2358 if ( !s_of && adsp && ad_meta_fileno(adsp) != -1 ) { /* META */
2359 ad_close(adsp, ADFLAGS_HF);
2361 if ( !d_of && addp && ad_meta_fileno(addp) != -1 ) {/* META */
2362 ad_close(addp, ADFLAGS_HF);
2366 if ((cached = dircache_search_by_did(vol, sid)) != NULL)
2367 (void)dir_remove(vol, cached);
2368 if ((cached = dircache_search_by_did(vol, did)) != NULL)
2369 (void)dir_remove(vol, cached);