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 /* deactivate cnid caching/storing in AppleDouble files and set ro mode*/
268 vol->v_flags &= ~AFPVOL_CACHE;
269 vol->v_flags |= AFPVOL_RO;
271 /* kill ourself with SIGUSR2 aka msg pending */
272 setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB instead."
273 "Check server messages for details. Switching to read-only mode.");
274 kill(getpid(), SIGUSR2);
276 goto restart; /* not try again with the temp CNID db */
279 setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB failed too!"
280 "Check server messages for details, can't recover from this state!");
284 afp_errno = AFPERR_MISC;
288 else if (adp && (adcnid != dbcnid)) { /* 4 */
289 /* Update the ressource fork. For a folder adp is always null */
290 LOG(log_debug, logtype_afpd, "get_id(%s/%s): calling ad_setid(old: %u, new: %u)",
291 getcwdpath(), upath, htonl(adcnid), htonl(dbcnid));
292 if (ad_setid(adp, st->st_dev, st->st_ino, dbcnid, did, vol->v_stamp)) {
303 /* -------------------------- */
304 int getmetadata(struct vol *vol,
306 struct path *path, struct dir *dir,
307 char *buf, size_t *buflen, struct adouble *adp)
309 char *data, *l_nameoff = NULL, *upath;
310 char *utf_nameoff = NULL;
315 u_char achar, fdType[4];
320 LOG(log_debug, logtype_afpd, "getmetadata(\"%s\")", path->u_name);
322 upath = path->u_name;
326 if ( ((bitmap & ( (1 << FILPBIT_FINFO)|(1 << FILPBIT_LNAME)|(1 <<FILPBIT_PDINFO) ) ) && !path->m_name)
327 || (bitmap & ( (1 << FILPBIT_LNAME) ) && utf8_encoding()) /* FIXME should be m_name utf8 filename */
328 || (bitmap & (1 << FILPBIT_FNUM))) {
331 struct dir *cachedfile;
332 int len = strlen(upath);
333 if ((cachedfile = dircache_search_by_name(vol, dir, upath, len)) != NULL)
334 id = cachedfile->d_did;
336 id = get_id(vol, adp, st, dir->d_did, upath, len);
338 /* Add it to the cache */
339 LOG(log_debug, logtype_afpd, "getmetadata: caching: did:%u, \"%s\", cnid:%u",
340 ntohl(dir->d_did), upath, ntohl(id));
342 /* Get macname from unixname first */
343 if (path->m_name == NULL) {
344 if ((path->m_name = utompath(vol, upath, id, utf8_encoding())) == NULL) {
345 LOG(log_error, logtype_afpd, "getmetadata: utompath error");
351 if (((fullpath = bstrcpy(dir->d_fullpath)) == NULL)
352 || (bconchar(fullpath, '/') != BSTR_OK)
353 || (bcatcstr(fullpath, upath)) != BSTR_OK) {
354 LOG(log_error, logtype_afpd, "getmetadata: fullpath: %s", strerror(errno));
358 if ((cachedfile = dir_new(path->m_name, upath, vol, dir->d_did, id, fullpath, st)) == NULL) {
359 LOG(log_error, logtype_afpd, "getmetadata: error from dir_new");
363 if ((dircache_add(vol, cachedfile)) != 0) {
364 LOG(log_error, logtype_afpd, "getmetadata: fatal dircache error");
372 if (id == CNID_INVALID)
376 path->m_name = utompath(vol, upath, id, utf8_encoding());
379 while ( bitmap != 0 ) {
380 while (( bitmap & 1 ) == 0 ) {
388 ad_getattr(adp, &ashort);
389 } else if (vol_inv_dots(vol) && *upath == '.') {
390 ashort = htons(ATTRBIT_INVISIBLE);
394 /* FIXME do we want a visual clue if the file is read only
397 accessmode(vol, ".", &ma, dir , NULL);
398 if ((ma.ma_user & AR_UWRITE)) {
399 accessmode(vol, upath, &ma, dir , st);
400 if (!(ma.ma_user & AR_UWRITE)) {
401 ashort |= htons(ATTRBIT_NOWRITE);
405 memcpy(data, &ashort, sizeof( ashort ));
406 data += sizeof( ashort );
407 LOG(log_debug, logtype_afpd, "metadata('%s'): AFP Attributes: %04x",
408 path->u_name, ntohs(ashort));
412 memcpy(data, &dir->d_did, sizeof( u_int32_t ));
413 data += sizeof( u_int32_t );
414 LOG(log_debug, logtype_afpd, "metadata('%s'): Parent DID: %u",
415 path->u_name, ntohl(dir->d_did));
419 if (!adp || (ad_getdate(adp, AD_DATE_CREATE, &aint) < 0))
420 aint = AD_DATE_FROM_UNIX(st->st_mtime);
421 memcpy(data, &aint, sizeof( aint ));
422 data += sizeof( aint );
426 if ( adp && (ad_getdate(adp, AD_DATE_MODIFY, &aint) == 0)) {
427 if ((st->st_mtime > AD_DATE_TO_UNIX(aint))) {
428 aint = AD_DATE_FROM_UNIX(st->st_mtime);
431 aint = AD_DATE_FROM_UNIX(st->st_mtime);
433 memcpy(data, &aint, sizeof( int ));
434 data += sizeof( int );
438 if (!adp || (ad_getdate(adp, AD_DATE_BACKUP, &aint) < 0))
439 aint = AD_DATE_START;
440 memcpy(data, &aint, sizeof( int ));
441 data += sizeof( int );
445 get_finderinfo(vol, upath, adp, (char *)data,S_ISLNK(st->st_mode));
446 data += ADEDLEN_FINDERI;
451 data += sizeof( u_int16_t );
455 memset(data, 0, sizeof(u_int16_t));
456 data += sizeof( u_int16_t );
460 memcpy(data, &id, sizeof( id ));
461 data += sizeof( id );
462 LOG(log_debug, logtype_afpd, "metadata('%s'): CNID: %u",
463 path->u_name, ntohl(id));
467 if (st->st_size > 0xffffffff)
470 aint = htonl( st->st_size );
471 memcpy(data, &aint, sizeof( aint ));
472 data += sizeof( aint );
477 if (adp->ad_rlen > 0xffffffff)
480 aint = htonl( adp->ad_rlen);
484 memcpy(data, &aint, sizeof( aint ));
485 data += sizeof( aint );
488 /* Current client needs ProDOS info block for this file.
489 Use simple heuristic and let the Mac "type" string tell
490 us what the PD file code should be. Everything gets a
491 subtype of 0x0000 unless the original value was hashed
492 to "pXYZ" when we created it. See IA, Ver 2.
493 <shirsch@adelphia.net> */
494 case FILPBIT_PDINFO :
495 if (afp_version >= 30) { /* UTF8 name */
496 utf8 = kTextEncodingUTF8;
498 data += sizeof( u_int16_t );
500 memcpy(data, &aint, sizeof( aint ));
501 data += sizeof( aint );
505 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
507 if ( memcmp( fdType, "TEXT", 4 ) == 0 ) {
511 else if ( memcmp( fdType, "PSYS", 4 ) == 0 ) {
515 else if ( memcmp( fdType, "PS16", 4 ) == 0 ) {
519 else if ( memcmp( fdType, "BINA", 4 ) == 0 ) {
523 else if ( fdType[0] == 'p' ) {
525 ashort = (fdType[2] * 256) + fdType[3];
539 memcpy(data, &ashort, sizeof( ashort ));
540 data += sizeof( ashort );
541 memset(data, 0, sizeof( ashort ));
542 data += sizeof( ashort );
545 case FILPBIT_EXTDFLEN:
546 aint = htonl(st->st_size >> 32);
547 memcpy(data, &aint, sizeof( aint ));
548 data += sizeof( aint );
549 aint = htonl(st->st_size);
550 memcpy(data, &aint, sizeof( aint ));
551 data += sizeof( aint );
553 case FILPBIT_EXTRFLEN:
556 aint = htonl(adp->ad_rlen >> 32);
557 memcpy(data, &aint, sizeof( aint ));
558 data += sizeof( aint );
560 aint = htonl(adp->ad_rlen);
561 memcpy(data, &aint, sizeof( aint ));
562 data += sizeof( aint );
564 case FILPBIT_UNIXPR :
565 /* accessmode may change st_mode with ACLs */
566 accessmode(vol, upath, &ma, dir , st);
568 aint = htonl(st->st_uid);
569 memcpy( data, &aint, sizeof( aint ));
570 data += sizeof( aint );
571 aint = htonl(st->st_gid);
572 memcpy( data, &aint, sizeof( aint ));
573 data += sizeof( aint );
576 type == slnk indicates an OSX style symlink,
577 we have to add S_IFLNK to the mode, otherwise
578 10.3 clients freak out. */
582 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
583 if ( memcmp( fdType, "slnk", 4 ) == 0 ) {
589 memcpy( data, &aint, sizeof( aint ));
590 data += sizeof( aint );
592 *data++ = ma.ma_user;
593 *data++ = ma.ma_world;
594 *data++ = ma.ma_group;
595 *data++ = ma.ma_owner;
599 return( AFPERR_BITMAP );
605 ashort = htons( data - buf );
606 memcpy(l_nameoff, &ashort, sizeof( ashort ));
607 data = set_name(vol, data, dir->d_did, path->m_name, id, 0);
610 ashort = htons( data - buf );
611 memcpy(utf_nameoff, &ashort, sizeof( ashort ));
612 data = set_name(vol, data, dir->d_did, path->m_name, id, utf8);
614 *buflen = data - buf;
618 /* ----------------------- */
619 int getfilparams(struct vol *vol,
621 struct path *path, struct dir *dir,
622 char *buf, size_t *buflen )
624 struct adouble ad, *adp;
628 LOG(log_debug, logtype_afpd, "getfilparams(\"%s\")", path->u_name);
630 opened = PARAM_NEED_ADP(bitmap);
635 int flags = (bitmap & (1 << FILPBIT_ATTR))?ADFLAGS_OPENFORKS:0;
637 adp = of_ad(vol, path, &ad);
638 upath = path->u_name;
640 if ( ad_metadata( upath, flags|ADFLAGS_CREATE, adp) < 0 ) {
643 LOG(log_error, logtype_afpd, "getfilparams(%s): %s: check resource fork permission?",
644 upath, strerror(errno));
645 return AFPERR_ACCESS;
647 LOG(log_error, logtype_afpd, "getfilparams(%s): bad resource fork", upath);
656 rc = getmetadata(vol, bitmap, path, dir, buf, buflen, adp);
658 ad_close_metadata( adp);
664 /* ----------------------------- */
665 int afp_createfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
667 struct adouble ad, *adp;
670 struct ofork *of = NULL;
672 int creatf, did, openf, retvalue = AFP_OK;
678 creatf = (unsigned char) *ibuf++;
680 memcpy(&vid, ibuf, sizeof( vid ));
681 ibuf += sizeof( vid );
683 if (NULL == ( vol = getvolbyvid( vid )) ) {
684 return( AFPERR_PARAM );
687 if (vol->v_flags & AFPVOL_RO)
690 memcpy(&did, ibuf, sizeof( did));
691 ibuf += sizeof( did );
693 if (NULL == ( dir = dirlookup( vol, did )) ) {
697 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
698 return get_afp_errno(AFPERR_PARAM);
701 if ( *s_path->m_name == '\0' ) {
702 return( AFPERR_BADTYPE );
705 upath = s_path->u_name;
707 /* if upath is deleted we already in trouble anyway */
708 if ((of = of_findname(s_path))) {
711 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
715 /* on a hard create, fail if file exists and is open */
718 openf = O_RDWR|O_CREAT|O_TRUNC;
720 /* on a soft create, if the file is open then ad_open won't fail
721 because open syscall is not called
726 openf = O_RDWR|O_CREAT|O_EXCL;
729 if ( ad_open( upath, ADFLAGS_DF|ADFLAGS_HF|ADFLAGS_NOHF|ADFLAGS_CREATE,
730 openf, 0666, adp) < 0 ) {
734 case ENOENT : /* we were already in 'did folder' so chdir() didn't fail */
735 return ( AFPERR_NOOBJ );
737 return( AFPERR_EXIST );
739 return( AFPERR_ACCESS );
742 return( AFPERR_DFULL );
744 return( AFPERR_PARAM );
747 if ( ad_reso_fileno( adp ) == -1 ) { /* Hard META / HF */
748 /* on noadouble volumes, just creating the data fork is ok */
749 if (vol_noadouble(vol)) {
750 ad_close( adp, ADFLAGS_DF );
751 goto createfile_done;
753 /* FIXME with hard create on an existing file, we already
754 * corrupted the data file.
756 netatalk_unlink( upath );
757 ad_close( adp, ADFLAGS_DF );
758 return AFPERR_ACCESS;
761 path = s_path->m_name;
762 ad_setname(adp, path);
765 if (lstat(upath, &st) != 0) {
766 LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): stat: %s",
767 upath, strerror(errno));
768 ad_close( adp, ADFLAGS_DF|ADFLAGS_HF);
772 (void)get_id(vol, adp, &st, dir->d_did, upath, strlen(upath));
776 fce_register_new_file(s_path);
778 ad_close( adp, ADFLAGS_DF|ADFLAGS_HF );
784 if (vol->v_flags & AFPVOL_DROPBOX) {
785 retvalue = matchfile2dirperms(upath, vol, did);
787 #endif /* DROPKLUDGE */
789 setvoltime(obj, vol );
794 int afp_setfilparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
800 u_int16_t vid, bitmap;
805 memcpy(&vid, ibuf, sizeof( vid ));
806 ibuf += sizeof( vid );
807 if (NULL == ( vol = getvolbyvid( vid )) ) {
808 return( AFPERR_PARAM );
811 if (vol->v_flags & AFPVOL_RO)
814 memcpy(&did, ibuf, sizeof( did ));
815 ibuf += sizeof( did );
816 if (NULL == ( dir = dirlookup( vol, did )) ) {
817 return afp_errno; /* was AFPERR_NOOBJ */
820 memcpy(&bitmap, ibuf, sizeof( bitmap ));
821 bitmap = ntohs( bitmap );
822 ibuf += sizeof( bitmap );
824 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
825 return get_afp_errno(AFPERR_PARAM);
828 if (path_isadir(s_path)) {
829 return( AFPERR_BADTYPE ); /* it's a directory */
832 if ( s_path->st_errno != 0 ) {
833 return( AFPERR_NOOBJ );
836 if ((u_long)ibuf & 1 ) {
840 if (AFP_OK == ( rc = setfilparams(vol, s_path, bitmap, ibuf )) ) {
841 setvoltime(obj, vol );
848 * cf AFP3.0.pdf page 252 for change_mdate and change_parent_mdate logic
851 extern struct path Cur_Path;
853 int setfilparams(struct vol *vol,
854 struct path *path, u_int16_t f_bitmap, char *buf )
856 struct adouble ad, *adp;
858 int bit, isad = 1, err = AFP_OK;
860 u_char achar, *fdType, xyy[4]; /* uninitialized, OK 310105 */
861 u_int16_t ashort, bshort, oshort;
864 u_int16_t upriv_bit = 0;
868 int change_mdate = 0;
869 int change_parent_mdate = 0;
874 u_int16_t bitmap = f_bitmap;
875 u_int32_t cdate,bdate;
876 u_char finder_buf[32];
879 LOG(log_debug9, logtype_afpd, "begin setfilparams:");
882 adp = of_ad(vol, path, &ad);
883 upath = path->u_name;
885 if (!vol_unix_priv(vol) && check_access(upath, OPENACC_WR ) < 0) {
886 return AFPERR_ACCESS;
889 /* with unix priv maybe we have to change adouble file priv first */
891 while ( bitmap != 0 ) {
892 while (( bitmap & 1 ) == 0 ) {
899 memcpy(&ashort, buf, sizeof( ashort ));
900 buf += sizeof( ashort );
904 memcpy(&cdate, buf, sizeof(cdate));
905 buf += sizeof( cdate );
908 memcpy(&newdate, buf, sizeof( newdate ));
909 buf += sizeof( newdate );
913 memcpy(&bdate, buf, sizeof( bdate));
914 buf += sizeof( bdate );
918 memcpy(finder_buf, buf, 32 );
919 if (memcmp(buf,"slnkrhap",8)==0 && !S_ISLNK(path->st.st_mode)){
924 char buf[PATH_MAX+1];
925 if ((fp=open(path->u_name,O_RDONLY))>=0){
926 if ((len=read(fp,buf,PATH_MAX+1))){
927 if (unlink(path->u_name)==0){
929 erc = symlink(buf, path->u_name);
938 goto setfilparam_done;
943 case FILPBIT_UNIXPR :
944 if (!vol_unix_priv(vol)) {
945 /* this volume doesn't use unix priv */
951 change_parent_mdate = 1;
953 memcpy( &aint, buf, sizeof( aint ));
954 f_uid = ntohl (aint);
955 buf += sizeof( aint );
956 memcpy( &aint, buf, sizeof( aint ));
957 f_gid = ntohl (aint);
958 buf += sizeof( aint );
959 setfilowner(vol, f_uid, f_gid, path);
961 memcpy( &upriv, buf, sizeof( upriv ));
962 buf += sizeof( upriv );
963 upriv = ntohl (upriv);
964 if ((upriv & S_IWUSR)) {
965 setfilunixmode(vol, path, upriv);
972 case FILPBIT_PDINFO :
973 if (afp_version < 30) { /* else it's UTF8 name */
976 /* Keep special case to support crlf translations */
977 if ((unsigned int) achar == 0x04) {
978 fdType = (u_char *)"TEXT";
981 xyy[0] = ( u_char ) 'p';
992 /* break while loop */
1001 /* second try with adouble open
1003 if ( ad_open_metadata( upath, 0, O_CREAT, adp) < 0) {
1004 LOG(log_debug, logtype_afpd, "setfilparams: ad_open_metadata error");
1006 * For some things, we don't need an adouble header:
1007 * - change of modification date
1008 * - UNIX privs (Bug-ID #2863424)
1010 if (!vol_noadouble(vol) && (f_bitmap & ~(1<<FILPBIT_MDATE | 1<<FILPBIT_UNIXPR))) {
1011 LOG(log_debug, logtype_afpd, "setfilparams: need adouble access");
1012 return AFPERR_ACCESS;
1014 LOG(log_debug, logtype_afpd, "setfilparams: no adouble perms, but only FILPBIT_MDATE and/or FILPBIT_UNIXPR");
1016 } else if ((ad_get_HF_flags( adp ) & O_CREAT) ) {
1017 ad_setname(adp, path->m_name);
1022 while ( bitmap != 0 ) {
1023 while (( bitmap & 1 ) == 0 ) {
1030 ad_getattr(adp, &bshort);
1032 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
1033 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
1037 if ((bshort & htons(ATTRBIT_INVISIBLE)) != (oshort & htons(ATTRBIT_INVISIBLE)))
1038 change_parent_mdate = 1;
1039 ad_setattr(adp, bshort);
1041 case FILPBIT_CDATE :
1042 ad_setdate(adp, AD_DATE_CREATE, cdate);
1044 case FILPBIT_MDATE :
1046 case FILPBIT_BDATE :
1047 ad_setdate(adp, AD_DATE_BACKUP, bdate);
1049 case FILPBIT_FINFO :
1050 if (default_type( ad_entry( adp, ADEID_FINDERI ))
1052 ((em = getextmap( path->m_name )) &&
1053 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1054 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1055 || ((em = getdefextmap()) &&
1056 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1057 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1059 memcpy(finder_buf, ufinderi, 8 );
1061 memcpy(ad_entry( adp, ADEID_FINDERI ), finder_buf, 32 );
1063 case FILPBIT_UNIXPR :
1065 setfilunixmode(vol, path, upriv);
1068 case FILPBIT_PDINFO :
1069 if (afp_version < 30) { /* else it's UTF8 name */
1070 memcpy(ad_entry( adp, ADEID_FINDERI ), fdType, 4 );
1071 memcpy(ad_entry( adp, ADEID_FINDERI ) + 4, "pdos", 4 );
1076 err = AFPERR_BITMAP;
1077 goto setfilparam_done;
1084 if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
1085 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1089 ad_setdate(adp, AD_DATE_MODIFY, newdate);
1090 ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
1096 ad_close_metadata( adp);
1100 if (change_parent_mdate && gettimeofday(&tv, NULL) == 0) {
1101 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1102 bitmap = 1<<FILPBIT_MDATE;
1103 setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
1107 LOG(log_debug9, logtype_afpd, "end setfilparams:");
1113 * renamefile and copyfile take the old and new unix pathnames
1114 * and the new mac name.
1116 * sdir_fd source dir fd to which src path is relative (for openat et al semantics)
1117 * passing -1 means this is not used, src path is a full path
1118 * src the source path
1119 * dst the dest filename in current dir
1120 * newname the dest mac name
1121 * adp adouble struct of src file, if open, or & zeroed one
1124 int renamefile(const struct vol *vol, int sdir_fd, char *src, char *dst, char *newname, struct adouble *adp)
1128 LOG(log_debug, logtype_afpd,
1129 "renamefile: src[%d, \"%s\"] -> dst[\"%s\"]", sdir_fd, src, dst);
1131 if ( unix_rename( sdir_fd, src, -1, dst ) < 0 ) {
1134 return( AFPERR_NOOBJ );
1137 return( AFPERR_ACCESS );
1139 return AFPERR_VLOCK;
1140 case EXDEV : /* Cross device move -- try copy */
1141 /* NOTE: with open file it's an error because after the copy we will
1142 * get two files, it's fixable for our process (eg reopen the new file, get the
1143 * locks, and so on. But it doesn't solve the case with a second process
1145 if (adp->ad_open_forks) {
1146 /* FIXME warning in syslog so admin'd know there's a conflict ?*/
1147 return AFPERR_OLOCK; /* little lie */
1149 if (AFP_OK != ( rc = copyfile(vol, vol, sdir_fd, src, dst, newname, NULL )) ) {
1150 /* on error copyfile delete dest */
1153 return deletefile(vol, sdir_fd, src, 0);
1155 return( AFPERR_PARAM );
1159 if (vol->vfs->vfs_renamefile(vol, sdir_fd, src, dst) < 0 ) {
1163 /* try to undo the data fork rename,
1164 * we know we are on the same device
1167 unix_rename(-1, dst, sdir_fd, src );
1168 /* return the first error */
1171 return AFPERR_NOOBJ;
1174 return AFPERR_ACCESS ;
1176 return AFPERR_VLOCK;
1178 return AFPERR_PARAM ;
1183 /* don't care if we can't open the newly renamed ressource fork
1185 if (!ad_open( dst, ADFLAGS_HF, O_RDWR, 0666, adp)) {
1186 ad_setname(adp, newname);
1188 ad_close( adp, ADFLAGS_HF );
1195 convert a Mac long name to an utf8 name,
1197 size_t mtoUTF8(const struct vol *vol, const char *src, size_t srclen, char *dest, size_t destlen)
1201 if ((size_t)-1 == (outlen = convert_string ( vol->v_maccharset, CH_UTF8_MAC, src, srclen, dest, destlen)) ) {
1207 /* ---------------- */
1208 int copy_path_name(const struct vol *vol, char *newname, char *ibuf)
1215 if ( type != 2 && !(afp_version >= 30 && type == 3) ) {
1221 if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
1222 if (afp_version >= 30) {
1223 /* convert it to UTF8
1225 if ((plen = mtoUTF8(vol, ibuf, plen, newname, AFPOBJ_TMPSIZ)) == (size_t)-1)
1229 strncpy( newname, ibuf, plen );
1230 newname[ plen ] = '\0';
1232 if (strlen(newname) != plen) {
1233 /* there's \0 in newname, e.g. it's a pathname not
1241 memcpy(&hint, ibuf, sizeof(hint));
1242 ibuf += sizeof(hint);
1244 memcpy(&len16, ibuf, sizeof(len16));
1245 ibuf += sizeof(len16);
1246 plen = ntohs(len16);
1249 if (plen > AFPOBJ_TMPSIZ) {
1252 strncpy( newname, ibuf, plen );
1253 newname[ plen ] = '\0';
1254 if (strlen(newname) != plen) {
1263 /* -----------------------------------
1265 int afp_copyfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1267 struct vol *s_vol, *d_vol;
1269 char *newname, *p, *upath;
1270 struct path *s_path;
1271 u_int32_t sdid, ddid;
1272 int err, retvalue = AFP_OK;
1273 u_int16_t svid, dvid;
1275 struct adouble ad, *adp;
1281 memcpy(&svid, ibuf, sizeof( svid ));
1282 ibuf += sizeof( svid );
1283 if (NULL == ( s_vol = getvolbyvid( svid )) ) {
1284 return( AFPERR_PARAM );
1287 memcpy(&sdid, ibuf, sizeof( sdid ));
1288 ibuf += sizeof( sdid );
1289 if (NULL == ( dir = dirlookup( s_vol, sdid )) ) {
1293 memcpy(&dvid, ibuf, sizeof( dvid ));
1294 ibuf += sizeof( dvid );
1295 memcpy(&ddid, ibuf, sizeof( ddid ));
1296 ibuf += sizeof( ddid );
1298 if (NULL == ( s_path = cname( s_vol, dir, &ibuf )) ) {
1299 return get_afp_errno(AFPERR_PARAM);
1301 if ( path_isadir(s_path) ) {
1302 return( AFPERR_BADTYPE );
1305 /* don't allow copies when the file is open.
1306 * XXX: the spec only calls for read/deny write access.
1307 * however, copyfile doesn't have any of that info,
1308 * and locks need to stay coherent. as a result,
1309 * we just balk if the file is opened already. */
1311 adp = of_ad(s_vol, s_path, &ad);
1313 if (ad_open(s_path->u_name , ADFLAGS_DF |ADFLAGS_HF | ADFLAGS_NOHF, O_RDONLY, 0, adp) < 0) {
1314 return AFPERR_DENYCONF;
1316 denyreadset = (getforkmode(adp, ADEID_DFORK, AD_FILELOCK_DENY_RD) != 0 ||
1317 getforkmode(adp, ADEID_RFORK, AD_FILELOCK_DENY_RD) != 0 );
1320 retvalue = AFPERR_DENYCONF;
1324 newname = obj->newtmp;
1325 strcpy( newname, s_path->m_name );
1327 p = ctoupath( s_vol, curdir, newname );
1329 retvalue = AFPERR_PARAM;
1334 /* FIXME svid != dvid && dvid's user can't read svid */
1336 if (NULL == ( d_vol = getvolbyvid( dvid )) ) {
1337 retvalue = AFPERR_PARAM;
1341 if (d_vol->v_flags & AFPVOL_RO) {
1342 retvalue = AFPERR_VLOCK;
1346 if (NULL == ( dir = dirlookup( d_vol, ddid )) ) {
1347 retvalue = afp_errno;
1351 if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) {
1352 retvalue = get_afp_errno(AFPERR_NOOBJ);
1356 if ( *s_path->m_name != '\0' ) {
1357 retvalue =path_error(s_path, AFPERR_NOOBJ);
1361 /* one of the handful of places that knows about the path type */
1362 if (copy_path_name(d_vol, newname, ibuf) < 0) {
1363 retvalue = AFPERR_PARAM;
1366 /* newname is always only a filename so curdir *is* its
1369 if (NULL == (upath = mtoupath(d_vol, newname, curdir->d_did, utf8_encoding()))) {
1370 retvalue =AFPERR_PARAM;
1374 if ( (err = copyfile(s_vol, d_vol, -1, p, upath , newname, adp)) < 0 ) {
1381 if (vol->v_flags & AFPVOL_DROPBOX) {
1382 retvalue=matchfile2dirperms(upath, vol, ddid); /* FIXME sdir or ddid */
1384 #endif /* DROPKLUDGE */
1386 setvoltime(obj, d_vol );
1389 ad_close( adp, ADFLAGS_DF |ADFLAGS_HF );
1393 /* ----------------------- */
1394 static int copy_all(const int dfd, const void *buf,
1400 LOG(log_debug9, logtype_afpd, "begin copy_all:");
1403 while (buflen > 0) {
1404 if ((cc = write(dfd, buf, buflen)) < 0) {
1416 LOG(log_debug9, logtype_afpd, "end copy_all:");
1422 /* --------------------------
1423 * copy only the fork data stream
1425 static int copy_fork(int eid, struct adouble *add, struct adouble *ads)
1432 if (eid == ADEID_DFORK) {
1433 sfd = ad_data_fileno(ads);
1434 dfd = ad_data_fileno(add);
1437 sfd = ad_reso_fileno(ads);
1438 dfd = ad_reso_fileno(add);
1441 if ((off_t)-1 == lseek(sfd, ad_getentryoff(ads, eid), SEEK_SET))
1444 if ((off_t)-1 == lseek(dfd, ad_getentryoff(add, eid), SEEK_SET))
1447 #if 0 /* ifdef SENDFILE_FLAVOR_LINUX */
1448 /* doesn't work With 2.6 FIXME, only check for EBADFD ? */
1452 #define BUF 128*1024*1024
1454 if (fstat(sfd, &st) == 0) {
1457 if ( offset >= st.st_size) {
1460 size = (st.st_size -offset > BUF)?BUF:st.st_size -offset;
1461 if ((cc = sys_sendfile(dfd, sfd, &offset, size)) < 0) {
1464 case EINVAL: /* there's no guarantee that all fs support sendfile */
1473 lseek(sfd, offset, SEEK_SET);
1477 if ((cc = read(sfd, filebuf, sizeof(filebuf))) < 0) {
1484 if (!cc || ((err = copy_all(dfd, filebuf, cc)) < 0)) {
1491 /* ----------------------------------
1492 * if newname is NULL (from directory.c) we don't want to copy the resource fork.
1493 * because we are doing it elsewhere.
1494 * currently if newname is NULL then adp is NULL.
1496 int copyfile(const struct vol *s_vol,
1497 const struct vol *d_vol,
1502 struct adouble *adp)
1504 struct adouble ads, add;
1511 LOG(log_debug, logtype_afpd, "copyfile(sfd:%d,s:'%s',d:'%s',n:'%s')",
1512 sfd, src, dst, newname);
1515 ad_init(&ads, s_vol->v_adouble, s_vol->v_ad_options);
1519 adflags = ADFLAGS_DF;
1521 adflags |= ADFLAGS_HF;
1524 if (ad_openat(sfd, src, adflags | ADFLAGS_NOHF, O_RDONLY, 0, adp) < 0) {
1529 if (ad_meta_fileno(adp) == -1 && ad_reso_fileno(adp) == -1) { /* META / HF */
1530 /* no resource fork, don't create one for dst file */
1531 adflags &= ~ADFLAGS_HF;
1534 stat_result = fstat(ad_data_fileno(adp), &st); /* saving stat exit code, thus saving us on one more stat later on */
1536 if (stat_result < 0) {
1537 /* unlikely but if fstat fails, the default file mode will be 0666. */
1538 st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
1541 ad_init(&add, d_vol->v_adouble, d_vol->v_ad_options);
1542 if (ad_open(dst , adflags, O_RDWR|O_CREAT|O_EXCL, st.st_mode, &add) < 0) {
1544 ad_close( adp, adflags );
1545 if (EEXIST != ret_err) {
1546 deletefile(d_vol, -1, dst, 0);
1549 return AFPERR_EXIST;
1553 * XXX if the source and the dest don't use the same resource type it's broken
1555 if (ad_reso_fileno(adp) == -1 || 0 == (err = copy_fork(ADEID_RFORK, &add, adp))){
1556 /* copy the data fork */
1557 if ((err = copy_fork(ADEID_DFORK, &add, adp)) == 0) {
1558 if (ad_meta_fileno(adp) != -1)
1559 err = d_vol->vfs->vfs_copyfile(d_vol, sfd, src, dst);
1567 if (!ret_err && newname && (adflags & ADFLAGS_HF)) {
1568 /* set the new name in the resource fork */
1569 ad_copy_header(&add, adp);
1570 ad_setname(&add, newname);
1573 ad_close( adp, adflags );
1575 if (ad_close( &add, adflags ) <0) {
1580 deletefile(d_vol, -1, dst, 0);
1582 else if (stat_result == 0) {
1583 /* set dest modification date to src date */
1586 ut.actime = ut.modtime = st.st_mtime;
1588 /* FIXME netatalk doesn't use resource fork file date
1589 * but maybe we should set its modtime too.
1594 switch ( ret_err ) {
1600 return AFPERR_DFULL;
1602 return AFPERR_NOOBJ;
1604 return AFPERR_ACCESS;
1606 return AFPERR_VLOCK;
1608 return AFPERR_PARAM;
1612 /* -----------------------------------
1613 vol: not NULL delete cnid entry. then we are in curdir and file is a only filename
1614 checkAttrib: 1 check kFPDeleteInhibitBit (deletfile called by afp_delete)
1616 when deletefile is called we don't have lock on it, file is closed (for us)
1617 untrue if called by renamefile
1619 ad_open always try to open file RDWR first and ad_lock takes care of
1620 WRITE lock on read only file.
1623 static int check_attrib(struct adouble *adp)
1625 u_int16_t bshort = 0;
1627 ad_getattr(adp, &bshort);
1629 * Does kFPDeleteInhibitBit (bit 8) set?
1631 if ((bshort & htons(ATTRBIT_NODELETE))) {
1632 return AFPERR_OLOCK;
1634 if ((bshort & htons(ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
1640 * dirfd can be used for unlinkat semantics
1642 int deletefile(const struct vol *vol, int dirfd, char *file, int checkAttrib)
1645 struct adouble *adp = NULL;
1646 int adflags, err = AFP_OK;
1649 LOG(log_debug, logtype_afpd, "deletefile('%s')", file);
1651 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
1653 /* was EACCESS error try to get only metadata */
1654 /* we never want to create a resource fork here, we are going to delete it
1655 * moreover sometimes deletefile is called with a no existent file and
1656 * ad_open would create a 0 byte resource fork
1658 if ( ad_metadataat(dirfd, file, ADFLAGS_OPENFORKS, &ad) == 0 ) {
1659 if ((err = check_attrib(&ad))) {
1660 ad_close_metadata(&ad);
1667 /* try to open both forks at once */
1668 adflags = ADFLAGS_DF;
1669 if ( ad_openat(dirfd, file, adflags |ADFLAGS_HF|ADFLAGS_NOHF, O_RDONLY, 0, &ad ) < 0 ) {
1674 case EACCES: /* maybe it's a file with no write mode for us */
1675 break; /* was return AFPERR_ACCESS;*/
1688 if ( adp && ad_reso_fileno( adp ) != -1 ) { /* there's a resource fork */
1689 adflags |= ADFLAGS_HF;
1690 /* FIXME we have a pb here because we want to know if a file is open
1691 * there's a 'priority inversion' if you can't open the ressource fork RW
1692 * you can delete it if it's open because you can't get a write lock.
1694 * ADLOCK_FILELOCK means the whole ressource fork, not only after the
1697 * FIXME it doesn't work for RFORK open read only and fork open without deny mode
1699 if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
1705 if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
1707 } else if (!(err = vol->vfs->vfs_deletefile(vol, dirfd, file)) && !(err = netatalk_unlinkat(dirfd, file )) ) {
1709 if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file)))) {
1710 cnid_delete(vol->v_cdb, id);
1716 ad_close_metadata(&ad);
1719 ad_close( &ad, adflags ); /* ad_close removes locks if any */
1724 /* ------------------------------------ */
1725 /* return a file id */
1726 int afp_createid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1735 struct path *s_path;
1741 memcpy(&vid, ibuf, sizeof(vid));
1742 ibuf += sizeof(vid);
1744 if (NULL == ( vol = getvolbyvid( vid )) ) {
1745 return( AFPERR_PARAM);
1748 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1752 if (vol->v_flags & AFPVOL_RO)
1753 return AFPERR_VLOCK;
1755 memcpy(&did, ibuf, sizeof( did ));
1756 ibuf += sizeof(did);
1758 if (NULL == ( dir = dirlookup( vol, did )) ) {
1759 return afp_errno; /* was AFPERR_PARAM */
1762 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
1763 return get_afp_errno(AFPERR_NOOBJ); /* was AFPERR_PARAM */
1766 if ( path_isadir(s_path) ) {
1767 return( AFPERR_BADTYPE );
1770 upath = s_path->u_name;
1771 switch (s_path->st_errno) {
1773 break; /* success */
1776 return AFPERR_ACCESS;
1778 return AFPERR_NOOBJ;
1780 return AFPERR_PARAM;
1783 if ((id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath)))) {
1784 memcpy(rbuf, &id, sizeof(id));
1785 *rbuflen = sizeof(id);
1786 return AFPERR_EXISTID;
1789 if ((id = get_id(vol, NULL, st, did, upath, len)) != CNID_INVALID) {
1790 memcpy(rbuf, &id, sizeof(id));
1791 *rbuflen = sizeof(id);
1798 /* ------------------------------- */
1804 static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
1807 struct reenum *param = data;
1808 struct vol *vol = param->vol;
1809 cnid_t did = param->did;
1812 if ( lstat(de->d_name, &path.st)<0 )
1815 /* update or add to cnid */
1816 aint = cnid_add(vol->v_cdb, &path.st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */
1818 #if AD_VERSION > AD_VERSION1
1819 if (aint != CNID_INVALID && !S_ISDIR(path.st.st_mode)) {
1820 struct adouble ad, *adp;
1824 path.u_name = de->d_name;
1826 adp = of_ad(vol, &path, &ad);
1828 if ( ad_open_metadata( de->d_name, 0, 0, adp ) < 0 ) {
1831 if (ad_setid(adp, path.st.st_dev, path.st.st_ino, aint, did, vol->v_stamp)) {
1834 ad_close_metadata(adp);
1836 #endif /* AD_VERSION > AD_VERSION1 */
1841 /* --------------------
1842 * Ok the db is out of synch with the dir.
1843 * but if it's a deleted file we don't want to do it again and again.
1846 reenumerate_id(struct vol *vol, char *name, struct dir *dir)
1852 if (vol->v_cdb == NULL) {
1856 /* FIXME use of_statdir ? */
1857 if (lstat(name, &st)) {
1861 if (dirreenumerate(dir, &st)) {
1862 /* we already did it once and the dir haven't been modified */
1863 return dir->d_offcnt;
1867 data.did = dir->d_did;
1868 if ((ret = for_each_dirent(vol, name, reenumerate_loop, (void *)&data)) >= 0) {
1869 setdiroffcnt(curdir, &st, ret);
1870 dir->d_flags |= DIRF_CNID;
1876 /* ------------------------------
1877 resolve a file id */
1878 int afp_resolveid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1887 u_int16_t vid, bitmap;
1889 static char buffer[12 + MAXPATHLEN + 1];
1890 int len = 12 + MAXPATHLEN + 1;
1895 memcpy(&vid, ibuf, sizeof(vid));
1896 ibuf += sizeof(vid);
1898 if (NULL == ( vol = getvolbyvid( vid )) ) {
1899 return( AFPERR_PARAM);
1902 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1906 memcpy(&id, ibuf, sizeof( id ));
1911 /* some MacOS versions after a catsearch do a *lot* of afp_resolveid with 0 */
1915 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1916 return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
1919 if (NULL == ( dir = dirlookup( vol, id )) ) {
1920 return AFPERR_NOID; /* idem AFPERR_PARAM */
1922 if (movecwd(vol, dir) < 0) {
1926 return AFPERR_ACCESS;
1930 return AFPERR_PARAM;
1934 memset(&path, 0, sizeof(path));
1935 path.u_name = upath;
1936 if ( of_stat(&path) < 0 ) {
1938 /* with nfs and our working directory is deleted */
1939 if (errno == ESTALE) {
1943 if ( errno == ENOENT && !retry) {
1944 /* cnid db is out of sync, reenumerate the directory and update ids */
1945 reenumerate_id(vol, ".", dir);
1953 return AFPERR_ACCESS;
1957 return AFPERR_PARAM;
1961 /* directories are bad */
1962 if (S_ISDIR(path.st.st_mode)) {
1963 /* OS9 and OSX don't return the same error code */
1964 return (afp_version >=30)?AFPERR_NOID:AFPERR_BADTYPE;
1967 memcpy(&bitmap, ibuf, sizeof(bitmap));
1968 bitmap = ntohs( bitmap );
1969 if (NULL == (path.m_name = utompath(vol, upath, cnid, utf8_encoding()))) {
1973 if (AFP_OK != (err = getfilparams(vol, bitmap, &path , curdir,
1974 rbuf + sizeof(bitmap), &buflen))) {
1977 *rbuflen = buflen + sizeof(bitmap);
1978 memcpy(rbuf, ibuf, sizeof(bitmap));
1983 /* ------------------------------ */
1984 int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1994 static char buffer[12 + MAXPATHLEN + 1];
1995 int len = 12 + MAXPATHLEN + 1;
2000 memcpy(&vid, ibuf, sizeof(vid));
2001 ibuf += sizeof(vid);
2003 if (NULL == ( vol = getvolbyvid( vid )) ) {
2004 return( AFPERR_PARAM);
2007 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
2011 if (vol->v_flags & AFPVOL_RO)
2012 return AFPERR_VLOCK;
2014 memcpy(&id, ibuf, sizeof( id ));
2018 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
2022 if (NULL == ( dir = dirlookup( vol, id )) ) {
2023 if (afp_errno == AFPERR_NOOBJ) {
2027 return( AFPERR_PARAM );
2031 if ((movecwd(vol, dir) < 0) || (lstat(upath, &st) < 0)) {
2035 return AFPERR_ACCESS;
2040 /* still try to delete the id */
2044 return AFPERR_PARAM;
2047 else if (S_ISDIR(st.st_mode)) /* directories are bad */
2048 return AFPERR_BADTYPE;
2051 if (cnid_delete(vol->v_cdb, fileid)) {
2054 return AFPERR_VLOCK;
2057 return AFPERR_ACCESS;
2059 return AFPERR_PARAM;
2066 /* ------------------------------ */
2067 static struct adouble *find_adouble(struct path *path, struct ofork **of, struct adouble *adp)
2071 if (path->st_errno) {
2072 switch (path->st_errno) {
2074 afp_errno = AFPERR_NOID;
2078 afp_errno = AFPERR_ACCESS;
2081 afp_errno = AFPERR_PARAM;
2086 /* we use file_access both for legacy Mac perm and
2087 * for unix privilege, rename will take care of folder perms
2089 if (file_access(path, OPENACC_WR ) < 0) {
2090 afp_errno = AFPERR_ACCESS;
2094 if ((*of = of_findname(path))) {
2095 /* reuse struct adouble so it won't break locks */
2099 ret = ad_open( path->u_name, ADFLAGS_HF, O_RDONLY, 0, adp);
2101 if ( !ret && ad_reso_fileno(adp) != -1 && !(adp->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
2103 * The user must have the Read & Write privilege for both files in order to use this command.
2105 ad_close(adp, ADFLAGS_HF);
2106 afp_errno = AFPERR_ACCESS;
2113 #define APPLETEMP ".AppleTempXXXXXX"
2115 int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
2117 struct stat srcst, destst;
2119 struct dir *dir, *sdir;
2120 char *spath, temp[17], *p;
2121 char *supath, *upath;
2126 struct adouble *adsp = NULL;
2127 struct adouble *addp = NULL;
2128 struct ofork *s_of = NULL;
2129 struct ofork *d_of = NULL;
2142 memcpy(&vid, ibuf, sizeof(vid));
2143 ibuf += sizeof(vid);
2145 if (NULL == ( vol = getvolbyvid( vid )) ) {
2146 return( AFPERR_PARAM);
2149 if ((vol->v_flags & AFPVOL_RO))
2150 return AFPERR_VLOCK;
2152 /* source and destination dids */
2153 memcpy(&sid, ibuf, sizeof(sid));
2154 ibuf += sizeof(sid);
2155 memcpy(&did, ibuf, sizeof(did));
2156 ibuf += sizeof(did);
2159 if (NULL == (dir = dirlookup( vol, sid )) ) {
2160 return afp_errno; /* was AFPERR_PARAM */
2163 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2164 return get_afp_errno(AFPERR_NOOBJ);
2167 if ( path_isadir(path) ) {
2168 return AFPERR_BADTYPE; /* it's a dir */
2171 /* save some stuff */
2174 spath = obj->oldtmp;
2175 supath = obj->newtmp;
2176 strcpy(spath, path->m_name);
2177 strcpy(supath, path->u_name); /* this is for the cnid changing */
2178 p = absupath( vol, sdir, supath);
2180 /* pathname too long */
2181 return AFPERR_PARAM ;
2184 ad_init(&ads, vol->v_adouble, vol->v_ad_options);
2185 if (!(adsp = find_adouble( path, &s_of, &ads))) {
2189 /* ***** from here we may have resource fork open **** */
2191 /* look for the source cnid. if it doesn't exist, don't worry about
2193 sid = cnid_lookup(vol->v_cdb, &srcst, sdir->d_did, supath,slen = strlen(supath));
2195 if (NULL == ( dir = dirlookup( vol, did )) ) {
2196 err = afp_errno; /* was AFPERR_PARAM */
2197 goto err_exchangefile;
2200 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2201 err = get_afp_errno(AFPERR_NOOBJ);
2202 goto err_exchangefile;
2205 if ( path_isadir(path) ) {
2206 err = AFPERR_BADTYPE;
2207 goto err_exchangefile;
2210 /* FPExchangeFiles is the only call that can return the SameObj
2212 if ((curdir == sdir) && strcmp(spath, path->m_name) == 0) {
2213 err = AFPERR_SAMEOBJ;
2214 goto err_exchangefile;
2217 ad_init(&add, vol->v_adouble, vol->v_ad_options);
2218 if (!(addp = find_adouble( path, &d_of, &add))) {
2220 goto err_exchangefile;
2224 /* they are not on the same device and at least one is open
2225 * FIXME broken for for crossdev and adouble v2
2228 crossdev = (srcst.st_dev != destst.st_dev);
2229 if (/* (d_of || s_of) && */ crossdev) {
2231 goto err_exchangefile;
2234 /* look for destination id. */
2235 upath = path->u_name;
2236 did = cnid_lookup(vol->v_cdb, &destst, curdir->d_did, upath, dlen = strlen(upath));
2238 /* construct a temp name.
2239 * NOTE: the temp file will be in the dest file's directory. it
2240 * will also be inaccessible from AFP. */
2241 memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
2242 if (!mktemp(temp)) {
2244 goto err_exchangefile;
2248 /* FIXME we need to close fork for copy, both s_of and d_of are null */
2249 ad_close(adsp, ADFLAGS_HF);
2250 ad_close(addp, ADFLAGS_HF);
2253 /* now, quickly rename the file. we error if we can't. */
2254 if ((err = renamefile(vol, -1, p, temp, temp, adsp)) != AFP_OK)
2255 goto err_exchangefile;
2256 of_rename(vol, s_of, sdir, spath, curdir, temp);
2258 /* rename destination to source */
2259 if ((err = renamefile(vol, -1, upath, p, spath, addp)) != AFP_OK)
2260 goto err_src_to_tmp;
2261 of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
2263 /* rename temp to destination */
2264 if ((err = renamefile(vol, -1, temp, upath, path->m_name, adsp)) != AFP_OK)
2265 goto err_dest_to_src;
2266 of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
2268 /* id's need switching. src -> dest and dest -> src.
2269 * we need to re-stat() if it was a cross device copy.
2272 cnid_delete(vol->v_cdb, sid);
2274 cnid_delete(vol->v_cdb, did);
2276 if ((did && ( (crossdev && lstat( upath, &srcst) < 0) ||
2277 cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
2279 (sid && ( (crossdev && lstat(p, &destst) < 0) ||
2280 cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
2285 err = AFPERR_ACCESS;
2290 goto err_temp_to_dest;
2293 /* here we need to reopen if crossdev */
2294 if (sid && ad_setid(addp, destst.st_dev, destst.st_ino, sid, sdir->d_did, vol->v_stamp))
2299 if (did && ad_setid(adsp, srcst.st_dev, srcst.st_ino, did, curdir->d_did, vol->v_stamp))
2304 /* change perms, src gets dest perm and vice versa */
2309 LOG(log_error, logtype_afpd, "seteuid failed %s", strerror(errno));
2310 err = AFP_OK; /* ignore error */
2311 goto err_temp_to_dest;
2315 * we need to exchange ACL entries as well
2317 /* exchange_acls(vol, p, upath); */
2322 path->m_name = NULL;
2323 path->u_name = upath;
2325 setfilunixmode(vol, path, destst.st_mode);
2326 setfilowner(vol, destst.st_uid, destst.st_gid, path);
2333 setfilunixmode(vol, path, srcst.st_mode);
2334 setfilowner(vol, srcst.st_uid, srcst.st_gid, path);
2336 if ( setegid(gid) < 0 || seteuid(uid) < 0) {
2337 LOG(log_error, logtype_afpd, "can't seteuid back %s", strerror(errno));
2342 goto err_exchangefile;
2344 /* all this stuff is so that we can unwind a failed operation
2347 /* rename dest to temp */
2348 renamefile(vol, -1, upath, temp, temp, adsp);
2349 of_rename(vol, s_of, curdir, upath, curdir, temp);
2352 /* rename source back to dest */
2353 renamefile(vol, -1, p, upath, path->m_name, addp);
2354 of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
2357 /* rename temp back to source */
2358 renamefile(vol, -1, temp, p, spath, adsp);
2359 of_rename(vol, s_of, curdir, temp, sdir, spath);
2362 if ( !s_of && adsp && ad_meta_fileno(adsp) != -1 ) { /* META */
2363 ad_close(adsp, ADFLAGS_HF);
2365 if ( !d_of && addp && ad_meta_fileno(addp) != -1 ) {/* META */
2366 ad_close(addp, ADFLAGS_HF);
2370 if ((cached = dircache_search_by_did(vol, sid)) != NULL)
2371 (void)dir_remove(vol, cached);
2372 if ((cached = dircache_search_by_did(vol, did)) != NULL)
2373 (void)dir_remove(vol, cached);