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>
42 #include "directory.h"
51 /* the format for the finderinfo fields (from IM: Toolbox Essentials):
52 * field bytes subfield bytes
55 * ioFlFndrInfo 16 -> type 4 type field
56 * creator 4 creator field
57 * flags 2 finder flags:
59 * location 4 location in window
60 * folder 2 window that contains file
62 * ioFlXFndrInfo 16 -> iconID 2 icon id
64 * script 1 script system
66 * commentID 2 comment id
67 * putawayID 4 home directory id
70 const u_char ufinderi[ADEDLEN_FINDERI] = {
71 0, 0, 0, 0, 0, 0, 0, 0,
72 1, 0, 0, 0, 0, 0, 0, 0,
73 0, 0, 0, 0, 0, 0, 0, 0,
74 0, 0, 0, 0, 0, 0, 0, 0
77 static const u_char old_ufinderi[] = {
78 'T', 'E', 'X', 'T', 'U', 'N', 'I', 'X'
81 /* ----------------------
83 static int default_type(void *finder)
85 if (!memcmp(finder, ufinderi, 8) || !memcmp(finder, old_ufinderi, 8))
90 /* FIXME path : unix or mac name ? (for now it's unix name ) */
91 void *get_finderinfo(const struct vol *vol, const char *upath, struct adouble *adp, void *data, int islink)
94 void *ad_finder = NULL;
98 ad_finder = ad_entry(adp, ADEID_FINDERI);
101 memcpy(data, ad_finder, ADEDLEN_FINDERI);
103 if (default_type(ad_finder))
107 memcpy(data, ufinderi, ADEDLEN_FINDERI);
109 if (vol_inv_dots(vol) && *upath == '.') { /* make it invisible */
112 ashort = htons(FINDERINFO_INVISIBLE);
113 memcpy((char *)data + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
119 memcpy(&linkflag, (char *)data + FINDERINFO_FRFLAGOFF, 2);
120 linkflag |= htons(FINDERINFO_ISALIAS);
121 memcpy((char *)data + FINDERINFO_FRFLAGOFF, &linkflag, 2);
122 memcpy((char *)data + FINDERINFO_FRTYPEOFF,"slnk",4);
123 memcpy((char *)data + FINDERINFO_FRCREATOFF,"rhap",4);
127 /** Only enter if no appledouble information and no finder information found. */
128 if (chk_ext && (em = getextmap( upath ))) {
129 memcpy(data, em->em_type, sizeof( em->em_type ));
130 memcpy((char *)data + 4, em->em_creator, sizeof(em->em_creator));
135 /* ---------------------
137 char *set_name(const struct vol *vol, char *data, cnid_t pid, char *name, cnid_t id, u_int32_t utf8)
142 aint = strlen( name );
146 if (utf8_encoding()) {
147 /* but name is an utf8 mac name */
150 /* global static variable... */
152 if (!(u = mtoupath(vol, name, pid, 1)) || !(m = utompath(vol, u, id, 0))) {
161 if (aint > MACFILELEN)
168 if (aint > UTF8FILELEN_EARLY) /* FIXME safeguard, anyway if no ascii char it's game over*/
169 aint = UTF8FILELEN_EARLY;
171 utf8 = vol->v_kTextEncoding;
172 memcpy(data, &utf8, sizeof(utf8));
173 data += sizeof(utf8);
176 memcpy(data, &temp, sizeof(temp));
177 data += sizeof(temp);
180 memcpy( data, src, aint );
190 * FIXME: PDINFO is UTF8 and doesn't need adp
192 #define PARAM_NEED_ADP(b) ((b) & ((1 << FILPBIT_ATTR) |\
193 (1 << FILPBIT_CDATE) |\
194 (1 << FILPBIT_MDATE) |\
195 (1 << FILPBIT_BDATE) |\
196 (1 << FILPBIT_FINFO) |\
197 (1 << FILPBIT_RFLEN) |\
198 (1 << FILPBIT_EXTRFLEN) |\
199 (1 << FILPBIT_PDINFO) |\
200 (1 << FILPBIT_FNUM) |\
201 (1 << FILPBIT_UNIXPR)))
204 * @brief Get CNID for did/upath args both from database and adouble file
206 * 1. Get the objects CNID as stored in its adouble file
207 * 2. Get the objects CNID from the database
208 * 3. If there's a problem with a "dbd" database, fallback to "tdb" in memory
209 * 4. In case 2 and 3 differ, store 3 in the adouble file
211 * @param vol (rw) volume
212 * @param adp (rw) adouble struct of object upath, might be NULL
213 * @param st (r) stat of upath, must NOT be NULL
214 * @param did (r) parent CNID of upath
215 * @param upath (r) name of object
216 * @param len (r) strlen of upath
218 uint32_t get_id(struct vol *vol,
220 const struct stat *st,
225 static int first = 1; /* mark if this func is called the first time */
227 u_int32_t dbcnid = CNID_INVALID;
230 if (vol->v_cdb != NULL) {
231 /* prime aint with what we think is the cnid, set did to zero for
232 catching moved files */
233 adcnid = ad_getid(adp, st->st_dev, st->st_ino, 0, vol->v_stamp); /* (1) */
235 dbcnid = cnid_add(vol->v_cdb, st, did, upath, len, adcnid); /* (2) */
236 /* Throw errors if cnid_add fails. */
237 if (dbcnid == CNID_INVALID) {
239 case CNID_ERR_CLOSE: /* the db is closed */
242 LOG(log_error, logtype_afpd, "get_id: Incorrect parameters passed to cnid_add");
243 afp_errno = AFPERR_PARAM;
246 afp_errno = AFPERR_PARAM;
249 /* Close CNID backend if "dbd" and switch to temp in-memory "tdb" */
250 /* we have to do it here for "dbd" because it uses "lazy opening" */
251 /* In order to not end in a loop somehow with goto restart below */
253 if (first && (strcmp(vol->v_cnidscheme, "dbd") == 0)) { /* (3) */
254 cnid_close(vol->v_cdb);
255 free(vol->v_cnidscheme);
256 vol->v_cnidscheme = strdup("tdb");
258 int flags = CNID_FLAG_MEMORY;
259 if ((vol->v_flags & AFPVOL_NODEV)) {
260 flags |= CNID_FLAG_NODEV;
262 LOG(log_error, logtype_afpd, "Reopen volume %s using in memory temporary CNID DB.",
264 vol->v_cdb = cnid_open(vol->v_path, vol->v_umask, "tdb", flags, NULL, NULL);
266 /* deactivate cnid caching/storing in AppleDouble files and set ro mode*/
267 vol->v_flags &= ~AFPVOL_CACHE;
268 vol->v_flags |= AFPVOL_RO;
270 /* kill ourself with SIGUSR2 aka msg pending */
271 setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB instead."
272 "Check server messages for details. Switching to read-only mode.");
273 kill(getpid(), SIGUSR2);
275 goto restart; /* not try again with the temp CNID db */
278 setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB failed too!"
279 "Check server messages for details, can't recover from this state!");
283 afp_errno = AFPERR_MISC;
287 else if (adp && (adcnid != dbcnid)) { /* 4 */
288 /* Update the ressource fork. For a folder adp is always null */
289 LOG(log_debug, logtype_afpd, "get_id(%s/%s): calling ad_setid(old: %u, new: %u)",
290 getcwdpath(), upath, htonl(adcnid), htonl(dbcnid));
291 if (ad_setid(adp, st->st_dev, st->st_ino, dbcnid, did, vol->v_stamp)) {
302 /* -------------------------- */
303 int getmetadata(struct vol *vol,
305 struct path *path, struct dir *dir,
306 char *buf, size_t *buflen, struct adouble *adp)
308 char *data, *l_nameoff = NULL, *upath;
309 char *utf_nameoff = NULL;
314 u_char achar, fdType[4];
319 LOG(log_debug, logtype_afpd, "getmetadata(\"%s\")", path->u_name);
321 upath = path->u_name;
325 if ( ((bitmap & ( (1 << FILPBIT_FINFO)|(1 << FILPBIT_LNAME)|(1 <<FILPBIT_PDINFO) ) ) && !path->m_name)
326 || (bitmap & ( (1 << FILPBIT_LNAME) ) && utf8_encoding()) /* FIXME should be m_name utf8 filename */
327 || (bitmap & (1 << FILPBIT_FNUM))) {
330 struct dir *cachedfile;
331 int len = strlen(upath);
332 if ((cachedfile = dircache_search_by_name(vol, dir, upath, len)) != NULL)
333 id = cachedfile->d_did;
335 id = get_id(vol, adp, st, dir->d_did, upath, len);
337 /* Add it to the cache */
338 LOG(log_debug, logtype_afpd, "getmetadata: caching: did:%u, \"%s\", cnid:%u",
339 ntohl(dir->d_did), upath, ntohl(id));
341 /* Get macname from unixname first */
342 if (path->m_name == NULL) {
343 if ((path->m_name = utompath(vol, upath, id, utf8_encoding())) == NULL) {
344 LOG(log_error, logtype_afpd, "getmetadata: utompath error");
350 if (((fullpath = bstrcpy(dir->d_fullpath)) == NULL)
351 || (bconchar(fullpath, '/') != BSTR_OK)
352 || (bcatcstr(fullpath, upath)) != BSTR_OK) {
353 LOG(log_error, logtype_afpd, "getmetadata: fullpath: %s", strerror(errno));
357 if ((cachedfile = dir_new(path->m_name, upath, vol, dir->d_did, id, fullpath, st)) == NULL) {
358 LOG(log_error, logtype_afpd, "getmetadata: error from dir_new");
362 if ((dircache_add(vol, cachedfile)) != 0) {
363 LOG(log_error, logtype_afpd, "getmetadata: fatal dircache error");
371 if (id == CNID_INVALID)
375 path->m_name = utompath(vol, upath, id, utf8_encoding());
378 while ( bitmap != 0 ) {
379 while (( bitmap & 1 ) == 0 ) {
387 ad_getattr(adp, &ashort);
388 } else if (vol_inv_dots(vol) && *upath == '.') {
389 ashort = htons(ATTRBIT_INVISIBLE);
393 /* FIXME do we want a visual clue if the file is read only
396 accessmode( ".", &ma, dir , NULL);
397 if ((ma.ma_user & AR_UWRITE)) {
398 accessmode( upath, &ma, dir , st);
399 if (!(ma.ma_user & AR_UWRITE)) {
400 ashort |= htons(ATTRBIT_NOWRITE);
404 memcpy(data, &ashort, sizeof( ashort ));
405 data += sizeof( ashort );
406 LOG(log_debug, logtype_afpd, "metadata('%s'): AFP Attributes: %04x",
407 path->u_name, ntohs(ashort));
411 memcpy(data, &dir->d_did, sizeof( u_int32_t ));
412 data += sizeof( u_int32_t );
413 LOG(log_debug, logtype_afpd, "metadata('%s'): Parent DID: %u",
414 path->u_name, ntohl(dir->d_did));
418 if (!adp || (ad_getdate(adp, AD_DATE_CREATE, &aint) < 0))
419 aint = AD_DATE_FROM_UNIX(st->st_mtime);
420 memcpy(data, &aint, sizeof( aint ));
421 data += sizeof( aint );
425 if ( adp && (ad_getdate(adp, AD_DATE_MODIFY, &aint) == 0)) {
426 if ((st->st_mtime > AD_DATE_TO_UNIX(aint))) {
427 aint = AD_DATE_FROM_UNIX(st->st_mtime);
430 aint = AD_DATE_FROM_UNIX(st->st_mtime);
432 memcpy(data, &aint, sizeof( int ));
433 data += sizeof( int );
437 if (!adp || (ad_getdate(adp, AD_DATE_BACKUP, &aint) < 0))
438 aint = AD_DATE_START;
439 memcpy(data, &aint, sizeof( int ));
440 data += sizeof( int );
444 get_finderinfo(vol, upath, adp, (char *)data,S_ISLNK(st->st_mode));
445 data += ADEDLEN_FINDERI;
450 data += sizeof( u_int16_t );
454 memset(data, 0, sizeof(u_int16_t));
455 data += sizeof( u_int16_t );
459 memcpy(data, &id, sizeof( id ));
460 data += sizeof( id );
461 LOG(log_debug, logtype_afpd, "metadata('%s'): CNID: %u",
462 path->u_name, ntohl(id));
466 if (st->st_size > 0xffffffff)
469 aint = htonl( st->st_size );
470 memcpy(data, &aint, sizeof( aint ));
471 data += sizeof( aint );
476 if (adp->ad_rlen > 0xffffffff)
479 aint = htonl( adp->ad_rlen);
483 memcpy(data, &aint, sizeof( aint ));
484 data += sizeof( aint );
487 /* Current client needs ProDOS info block for this file.
488 Use simple heuristic and let the Mac "type" string tell
489 us what the PD file code should be. Everything gets a
490 subtype of 0x0000 unless the original value was hashed
491 to "pXYZ" when we created it. See IA, Ver 2.
492 <shirsch@adelphia.net> */
493 case FILPBIT_PDINFO :
494 if (afp_version >= 30) { /* UTF8 name */
495 utf8 = kTextEncodingUTF8;
497 data += sizeof( u_int16_t );
499 memcpy(data, &aint, sizeof( aint ));
500 data += sizeof( aint );
504 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
506 if ( memcmp( fdType, "TEXT", 4 ) == 0 ) {
510 else if ( memcmp( fdType, "PSYS", 4 ) == 0 ) {
514 else if ( memcmp( fdType, "PS16", 4 ) == 0 ) {
518 else if ( memcmp( fdType, "BINA", 4 ) == 0 ) {
522 else if ( fdType[0] == 'p' ) {
524 ashort = (fdType[2] * 256) + fdType[3];
538 memcpy(data, &ashort, sizeof( ashort ));
539 data += sizeof( ashort );
540 memset(data, 0, sizeof( ashort ));
541 data += sizeof( ashort );
544 case FILPBIT_EXTDFLEN:
545 aint = htonl(st->st_size >> 32);
546 memcpy(data, &aint, sizeof( aint ));
547 data += sizeof( aint );
548 aint = htonl(st->st_size);
549 memcpy(data, &aint, sizeof( aint ));
550 data += sizeof( aint );
552 case FILPBIT_EXTRFLEN:
555 aint = htonl(adp->ad_rlen >> 32);
556 memcpy(data, &aint, sizeof( aint ));
557 data += sizeof( aint );
559 aint = htonl(adp->ad_rlen);
560 memcpy(data, &aint, sizeof( aint ));
561 data += sizeof( aint );
563 case FILPBIT_UNIXPR :
564 /* accessmode may change st_mode with ACLs */
565 accessmode( upath, &ma, dir , st);
567 aint = htonl(st->st_uid);
568 memcpy( data, &aint, sizeof( aint ));
569 data += sizeof( aint );
570 aint = htonl(st->st_gid);
571 memcpy( data, &aint, sizeof( aint ));
572 data += sizeof( aint );
575 type == slnk indicates an OSX style symlink,
576 we have to add S_IFLNK to the mode, otherwise
577 10.3 clients freak out. */
581 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
582 if ( memcmp( fdType, "slnk", 4 ) == 0 ) {
588 memcpy( data, &aint, sizeof( aint ));
589 data += sizeof( aint );
591 *data++ = ma.ma_user;
592 *data++ = ma.ma_world;
593 *data++ = ma.ma_group;
594 *data++ = ma.ma_owner;
598 return( AFPERR_BITMAP );
604 ashort = htons( data - buf );
605 memcpy(l_nameoff, &ashort, sizeof( ashort ));
606 data = set_name(vol, data, dir->d_did, path->m_name, id, 0);
609 ashort = htons( data - buf );
610 memcpy(utf_nameoff, &ashort, sizeof( ashort ));
611 data = set_name(vol, data, dir->d_did, path->m_name, id, utf8);
613 *buflen = data - buf;
617 /* ----------------------- */
618 int getfilparams(struct vol *vol,
620 struct path *path, struct dir *dir,
621 char *buf, size_t *buflen )
623 struct adouble ad, *adp;
627 LOG(log_debug, logtype_afpd, "getfilparams(\"%s\")", path->u_name);
629 opened = PARAM_NEED_ADP(bitmap);
634 int flags = (bitmap & (1 << FILPBIT_ATTR))?ADFLAGS_OPENFORKS:0;
636 adp = of_ad(vol, path, &ad);
637 upath = path->u_name;
639 if ( ad_metadata( upath, flags|ADFLAGS_CREATE, adp) < 0 ) {
642 LOG(log_error, logtype_afpd, "getfilparams(%s): %s: check resource fork permission?",
643 upath, strerror(errno));
644 return AFPERR_ACCESS;
646 LOG(log_error, logtype_afpd, "getfilparams(%s): bad resource fork", upath);
655 rc = getmetadata(vol, bitmap, path, dir, buf, buflen, adp);
657 ad_close_metadata( adp);
663 /* ----------------------------- */
664 int afp_createfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
666 struct adouble ad, *adp;
669 struct ofork *of = NULL;
671 int creatf, did, openf, retvalue = AFP_OK;
677 creatf = (unsigned char) *ibuf++;
679 memcpy(&vid, ibuf, sizeof( vid ));
680 ibuf += sizeof( vid );
682 if (NULL == ( vol = getvolbyvid( vid )) ) {
683 return( AFPERR_PARAM );
686 if (vol->v_flags & AFPVOL_RO)
689 memcpy(&did, ibuf, sizeof( did));
690 ibuf += sizeof( did );
692 if (NULL == ( dir = dirlookup( vol, did )) ) {
696 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
697 return get_afp_errno(AFPERR_PARAM);
700 if ( *s_path->m_name == '\0' ) {
701 return( AFPERR_BADTYPE );
704 upath = s_path->u_name;
706 /* if upath is deleted we already in trouble anyway */
707 if ((of = of_findname(s_path))) {
710 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
714 /* on a hard create, fail if file exists and is open */
717 openf = O_RDWR|O_CREAT|O_TRUNC;
719 /* on a soft create, if the file is open then ad_open won't fail
720 because open syscall is not called
725 openf = O_RDWR|O_CREAT|O_EXCL;
728 if ( ad_open( upath, ADFLAGS_DF|ADFLAGS_HF|ADFLAGS_NOHF|ADFLAGS_CREATE,
729 openf, 0666, adp) < 0 ) {
733 case ENOENT : /* we were already in 'did folder' so chdir() didn't fail */
734 return ( AFPERR_NOOBJ );
736 return( AFPERR_EXIST );
738 return( AFPERR_ACCESS );
741 return( AFPERR_DFULL );
743 return( AFPERR_PARAM );
746 if ( ad_reso_fileno( adp ) == -1 ) { /* Hard META / HF */
747 /* on noadouble volumes, just creating the data fork is ok */
748 if (vol_noadouble(vol)) {
749 ad_close( adp, ADFLAGS_DF );
750 goto createfile_done;
752 /* FIXME with hard create on an existing file, we already
753 * corrupted the data file.
755 netatalk_unlink( upath );
756 ad_close( adp, ADFLAGS_DF );
757 return AFPERR_ACCESS;
760 path = s_path->m_name;
761 ad_setname(adp, path);
764 if (lstat(upath, &st) != 0) {
765 LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): stat: %s",
766 upath, strerror(errno));
767 ad_close( adp, ADFLAGS_DF|ADFLAGS_HF);
771 (void)get_id(vol, adp, &st, dir->d_did, upath, strlen(upath));
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 err = d_vol->vfs->vfs_copyfile(d_vol, sfd, src, dst);
1562 if (!ret_err && newname && (adflags & ADFLAGS_HF)) {
1563 /* set the new name in the resource fork */
1564 ad_copy_header(&add, adp);
1565 ad_setname(&add, newname);
1568 ad_close( adp, adflags );
1570 if (ad_close( &add, adflags ) <0) {
1575 deletefile(d_vol, -1, dst, 0);
1577 else if (stat_result == 0) {
1578 /* set dest modification date to src date */
1581 ut.actime = ut.modtime = st.st_mtime;
1583 /* FIXME netatalk doesn't use resource fork file date
1584 * but maybe we should set its modtime too.
1589 switch ( ret_err ) {
1595 return AFPERR_DFULL;
1597 return AFPERR_NOOBJ;
1599 return AFPERR_ACCESS;
1601 return AFPERR_VLOCK;
1603 return AFPERR_PARAM;
1607 /* -----------------------------------
1608 vol: not NULL delete cnid entry. then we are in curdir and file is a only filename
1609 checkAttrib: 1 check kFPDeleteInhibitBit (deletfile called by afp_delete)
1611 when deletefile is called we don't have lock on it, file is closed (for us)
1612 untrue if called by renamefile
1614 ad_open always try to open file RDWR first and ad_lock takes care of
1615 WRITE lock on read only file.
1618 static int check_attrib(struct adouble *adp)
1620 u_int16_t bshort = 0;
1622 ad_getattr(adp, &bshort);
1624 * Does kFPDeleteInhibitBit (bit 8) set?
1626 if ((bshort & htons(ATTRBIT_NODELETE))) {
1627 return AFPERR_OLOCK;
1629 if ((bshort & htons(ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
1635 * dirfd can be used for unlinkat semantics
1637 int deletefile(const struct vol *vol, int dirfd, char *file, int checkAttrib)
1640 struct adouble *adp = NULL;
1641 int adflags, err = AFP_OK;
1644 LOG(log_debug, logtype_afpd, "deletefile('%s')", file);
1646 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
1648 /* was EACCESS error try to get only metadata */
1649 /* we never want to create a resource fork here, we are going to delete it
1650 * moreover sometimes deletefile is called with a no existent file and
1651 * ad_open would create a 0 byte resource fork
1653 if ( ad_metadataat(dirfd, file, ADFLAGS_OPENFORKS, &ad) == 0 ) {
1654 if ((err = check_attrib(&ad))) {
1655 ad_close_metadata(&ad);
1662 /* try to open both forks at once */
1663 adflags = ADFLAGS_DF;
1664 if ( ad_openat(dirfd, file, adflags |ADFLAGS_HF|ADFLAGS_NOHF, O_RDONLY, 0, &ad ) < 0 ) {
1669 case EACCES: /* maybe it's a file with no write mode for us */
1670 break; /* was return AFPERR_ACCESS;*/
1683 if ( adp && ad_reso_fileno( adp ) != -1 ) { /* there's a resource fork */
1684 adflags |= ADFLAGS_HF;
1685 /* FIXME we have a pb here because we want to know if a file is open
1686 * there's a 'priority inversion' if you can't open the ressource fork RW
1687 * you can delete it if it's open because you can't get a write lock.
1689 * ADLOCK_FILELOCK means the whole ressource fork, not only after the
1692 * FIXME it doesn't work for RFORK open read only and fork open without deny mode
1694 if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
1700 if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
1702 } else if (!(err = vol->vfs->vfs_deletefile(vol, dirfd, file)) && !(err = netatalk_unlinkat(dirfd, file )) ) {
1704 if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file)))) {
1705 cnid_delete(vol->v_cdb, id);
1711 ad_close_metadata(&ad);
1714 ad_close( &ad, adflags ); /* ad_close removes locks if any */
1719 /* ------------------------------------ */
1720 /* return a file id */
1721 int afp_createid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1730 struct path *s_path;
1736 memcpy(&vid, ibuf, sizeof(vid));
1737 ibuf += sizeof(vid);
1739 if (NULL == ( vol = getvolbyvid( vid )) ) {
1740 return( AFPERR_PARAM);
1743 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1747 if (vol->v_flags & AFPVOL_RO)
1748 return AFPERR_VLOCK;
1750 memcpy(&did, ibuf, sizeof( did ));
1751 ibuf += sizeof(did);
1753 if (NULL == ( dir = dirlookup( vol, did )) ) {
1754 return afp_errno; /* was AFPERR_PARAM */
1757 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
1758 return get_afp_errno(AFPERR_NOOBJ); /* was AFPERR_PARAM */
1761 if ( path_isadir(s_path) ) {
1762 return( AFPERR_BADTYPE );
1765 upath = s_path->u_name;
1766 switch (s_path->st_errno) {
1768 break; /* success */
1771 return AFPERR_ACCESS;
1773 return AFPERR_NOOBJ;
1775 return AFPERR_PARAM;
1778 if ((id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath)))) {
1779 memcpy(rbuf, &id, sizeof(id));
1780 *rbuflen = sizeof(id);
1781 return AFPERR_EXISTID;
1784 if ((id = get_id(vol, NULL, st, did, upath, len)) != CNID_INVALID) {
1785 memcpy(rbuf, &id, sizeof(id));
1786 *rbuflen = sizeof(id);
1793 /* ------------------------------- */
1799 static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
1802 struct reenum *param = data;
1803 struct vol *vol = param->vol;
1804 cnid_t did = param->did;
1807 if ( lstat(de->d_name, &path.st)<0 )
1810 /* update or add to cnid */
1811 aint = cnid_add(vol->v_cdb, &path.st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */
1813 #if AD_VERSION > AD_VERSION1
1814 if (aint != CNID_INVALID && !S_ISDIR(path.st.st_mode)) {
1815 struct adouble ad, *adp;
1819 path.u_name = de->d_name;
1821 adp = of_ad(vol, &path, &ad);
1823 if ( ad_open_metadata( de->d_name, 0, 0, adp ) < 0 ) {
1826 if (ad_setid(adp, path.st.st_dev, path.st.st_ino, aint, did, vol->v_stamp)) {
1829 ad_close_metadata(adp);
1831 #endif /* AD_VERSION > AD_VERSION1 */
1836 /* --------------------
1837 * Ok the db is out of synch with the dir.
1838 * but if it's a deleted file we don't want to do it again and again.
1841 reenumerate_id(struct vol *vol, char *name, struct dir *dir)
1847 if (vol->v_cdb == NULL) {
1851 /* FIXME use of_statdir ? */
1852 if (lstat(name, &st)) {
1856 if (dirreenumerate(dir, &st)) {
1857 /* we already did it once and the dir haven't been modified */
1858 return dir->d_offcnt;
1862 data.did = dir->d_did;
1863 if ((ret = for_each_dirent(vol, name, reenumerate_loop, (void *)&data)) >= 0) {
1864 setdiroffcnt(curdir, &st, ret);
1865 dir->d_flags |= DIRF_CNID;
1871 /* ------------------------------
1872 resolve a file id */
1873 int afp_resolveid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1882 u_int16_t vid, bitmap;
1884 static char buffer[12 + MAXPATHLEN + 1];
1885 int len = 12 + MAXPATHLEN + 1;
1890 memcpy(&vid, ibuf, sizeof(vid));
1891 ibuf += sizeof(vid);
1893 if (NULL == ( vol = getvolbyvid( vid )) ) {
1894 return( AFPERR_PARAM);
1897 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1901 memcpy(&id, ibuf, sizeof( id ));
1906 /* some MacOS versions after a catsearch do a *lot* of afp_resolveid with 0 */
1910 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1911 return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
1914 if (NULL == ( dir = dirlookup( vol, id )) ) {
1915 return AFPERR_NOID; /* idem AFPERR_PARAM */
1917 if (movecwd(vol, dir) < 0) {
1921 return AFPERR_ACCESS;
1925 return AFPERR_PARAM;
1929 memset(&path, 0, sizeof(path));
1930 path.u_name = upath;
1931 if ( of_stat(&path) < 0 ) {
1933 /* with nfs and our working directory is deleted */
1934 if (errno == ESTALE) {
1938 if ( errno == ENOENT && !retry) {
1939 /* cnid db is out of sync, reenumerate the directory and update ids */
1940 reenumerate_id(vol, ".", dir);
1948 return AFPERR_ACCESS;
1952 return AFPERR_PARAM;
1956 /* directories are bad */
1957 if (S_ISDIR(path.st.st_mode)) {
1958 /* OS9 and OSX don't return the same error code */
1959 return (afp_version >=30)?AFPERR_NOID:AFPERR_BADTYPE;
1962 memcpy(&bitmap, ibuf, sizeof(bitmap));
1963 bitmap = ntohs( bitmap );
1964 if (NULL == (path.m_name = utompath(vol, upath, cnid, utf8_encoding()))) {
1968 if (AFP_OK != (err = getfilparams(vol, bitmap, &path , curdir,
1969 rbuf + sizeof(bitmap), &buflen))) {
1972 *rbuflen = buflen + sizeof(bitmap);
1973 memcpy(rbuf, ibuf, sizeof(bitmap));
1978 /* ------------------------------ */
1979 int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1989 static char buffer[12 + MAXPATHLEN + 1];
1990 int len = 12 + MAXPATHLEN + 1;
1995 memcpy(&vid, ibuf, sizeof(vid));
1996 ibuf += sizeof(vid);
1998 if (NULL == ( vol = getvolbyvid( vid )) ) {
1999 return( AFPERR_PARAM);
2002 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
2006 if (vol->v_flags & AFPVOL_RO)
2007 return AFPERR_VLOCK;
2009 memcpy(&id, ibuf, sizeof( id ));
2013 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
2017 if (NULL == ( dir = dirlookup( vol, id )) ) {
2018 if (afp_errno == AFPERR_NOOBJ) {
2022 return( AFPERR_PARAM );
2026 if ((movecwd(vol, dir) < 0) || (lstat(upath, &st) < 0)) {
2030 return AFPERR_ACCESS;
2035 /* still try to delete the id */
2039 return AFPERR_PARAM;
2042 else if (S_ISDIR(st.st_mode)) /* directories are bad */
2043 return AFPERR_BADTYPE;
2046 if (cnid_delete(vol->v_cdb, fileid)) {
2049 return AFPERR_VLOCK;
2052 return AFPERR_ACCESS;
2054 return AFPERR_PARAM;
2061 /* ------------------------------ */
2062 static struct adouble *find_adouble(struct path *path, struct ofork **of, struct adouble *adp)
2066 if (path->st_errno) {
2067 switch (path->st_errno) {
2069 afp_errno = AFPERR_NOID;
2073 afp_errno = AFPERR_ACCESS;
2076 afp_errno = AFPERR_PARAM;
2081 /* we use file_access both for legacy Mac perm and
2082 * for unix privilege, rename will take care of folder perms
2084 if (file_access(path, OPENACC_WR ) < 0) {
2085 afp_errno = AFPERR_ACCESS;
2089 if ((*of = of_findname(path))) {
2090 /* reuse struct adouble so it won't break locks */
2094 ret = ad_open( path->u_name, ADFLAGS_HF, O_RDONLY, 0, adp);
2096 if ( !ret && ad_reso_fileno(adp) != -1 && !(adp->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
2098 * The user must have the Read & Write privilege for both files in order to use this command.
2100 ad_close(adp, ADFLAGS_HF);
2101 afp_errno = AFPERR_ACCESS;
2108 #define APPLETEMP ".AppleTempXXXXXX"
2110 int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
2112 struct stat srcst, destst;
2114 struct dir *dir, *sdir;
2115 char *spath, temp[17], *p;
2116 char *supath, *upath;
2121 struct adouble *adsp = NULL;
2122 struct adouble *addp = NULL;
2123 struct ofork *s_of = NULL;
2124 struct ofork *d_of = NULL;
2137 memcpy(&vid, ibuf, sizeof(vid));
2138 ibuf += sizeof(vid);
2140 if (NULL == ( vol = getvolbyvid( vid )) ) {
2141 return( AFPERR_PARAM);
2144 if ((vol->v_flags & AFPVOL_RO))
2145 return AFPERR_VLOCK;
2147 /* source and destination dids */
2148 memcpy(&sid, ibuf, sizeof(sid));
2149 ibuf += sizeof(sid);
2150 memcpy(&did, ibuf, sizeof(did));
2151 ibuf += sizeof(did);
2154 if (NULL == (dir = dirlookup( vol, sid )) ) {
2155 return afp_errno; /* was AFPERR_PARAM */
2158 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2159 return get_afp_errno(AFPERR_NOOBJ);
2162 if ( path_isadir(path) ) {
2163 return AFPERR_BADTYPE; /* it's a dir */
2166 /* save some stuff */
2169 spath = obj->oldtmp;
2170 supath = obj->newtmp;
2171 strcpy(spath, path->m_name);
2172 strcpy(supath, path->u_name); /* this is for the cnid changing */
2173 p = absupath( vol, sdir, supath);
2175 /* pathname too long */
2176 return AFPERR_PARAM ;
2179 ad_init(&ads, vol->v_adouble, vol->v_ad_options);
2180 if (!(adsp = find_adouble( path, &s_of, &ads))) {
2184 /* ***** from here we may have resource fork open **** */
2186 /* look for the source cnid. if it doesn't exist, don't worry about
2188 sid = cnid_lookup(vol->v_cdb, &srcst, sdir->d_did, supath,slen = strlen(supath));
2190 if (NULL == ( dir = dirlookup( vol, did )) ) {
2191 err = afp_errno; /* was AFPERR_PARAM */
2192 goto err_exchangefile;
2195 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2196 err = get_afp_errno(AFPERR_NOOBJ);
2197 goto err_exchangefile;
2200 if ( path_isadir(path) ) {
2201 err = AFPERR_BADTYPE;
2202 goto err_exchangefile;
2205 /* FPExchangeFiles is the only call that can return the SameObj
2207 if ((curdir == sdir) && strcmp(spath, path->m_name) == 0) {
2208 err = AFPERR_SAMEOBJ;
2209 goto err_exchangefile;
2212 ad_init(&add, vol->v_adouble, vol->v_ad_options);
2213 if (!(addp = find_adouble( path, &d_of, &add))) {
2215 goto err_exchangefile;
2219 /* they are not on the same device and at least one is open
2220 * FIXME broken for for crossdev and adouble v2
2223 crossdev = (srcst.st_dev != destst.st_dev);
2224 if (/* (d_of || s_of) && */ crossdev) {
2226 goto err_exchangefile;
2229 /* look for destination id. */
2230 upath = path->u_name;
2231 did = cnid_lookup(vol->v_cdb, &destst, curdir->d_did, upath, dlen = strlen(upath));
2233 /* construct a temp name.
2234 * NOTE: the temp file will be in the dest file's directory. it
2235 * will also be inaccessible from AFP. */
2236 memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
2237 if (!mktemp(temp)) {
2239 goto err_exchangefile;
2243 /* FIXME we need to close fork for copy, both s_of and d_of are null */
2244 ad_close(adsp, ADFLAGS_HF);
2245 ad_close(addp, ADFLAGS_HF);
2248 /* now, quickly rename the file. we error if we can't. */
2249 if ((err = renamefile(vol, -1, p, temp, temp, adsp)) != AFP_OK)
2250 goto err_exchangefile;
2251 of_rename(vol, s_of, sdir, spath, curdir, temp);
2253 /* rename destination to source */
2254 if ((err = renamefile(vol, -1, upath, p, spath, addp)) != AFP_OK)
2255 goto err_src_to_tmp;
2256 of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
2258 /* rename temp to destination */
2259 if ((err = renamefile(vol, -1, temp, upath, path->m_name, adsp)) != AFP_OK)
2260 goto err_dest_to_src;
2261 of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
2263 /* id's need switching. src -> dest and dest -> src.
2264 * we need to re-stat() if it was a cross device copy.
2267 cnid_delete(vol->v_cdb, sid);
2269 cnid_delete(vol->v_cdb, did);
2271 if ((did && ( (crossdev && lstat( upath, &srcst) < 0) ||
2272 cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
2274 (sid && ( (crossdev && lstat(p, &destst) < 0) ||
2275 cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
2280 err = AFPERR_ACCESS;
2285 goto err_temp_to_dest;
2288 /* here we need to reopen if crossdev */
2289 if (sid && ad_setid(addp, destst.st_dev, destst.st_ino, sid, sdir->d_did, vol->v_stamp))
2294 if (did && ad_setid(adsp, srcst.st_dev, srcst.st_ino, did, curdir->d_did, vol->v_stamp))
2299 /* change perms, src gets dest perm and vice versa */
2304 LOG(log_error, logtype_afpd, "seteuid failed %s", strerror(errno));
2305 err = AFP_OK; /* ignore error */
2306 goto err_temp_to_dest;
2310 * we need to exchange ACL entries as well
2312 /* exchange_acls(vol, p, upath); */
2317 path->m_name = NULL;
2318 path->u_name = upath;
2320 setfilunixmode(vol, path, destst.st_mode);
2321 setfilowner(vol, destst.st_uid, destst.st_gid, path);
2328 setfilunixmode(vol, path, srcst.st_mode);
2329 setfilowner(vol, srcst.st_uid, srcst.st_gid, path);
2331 if ( setegid(gid) < 0 || seteuid(uid) < 0) {
2332 LOG(log_error, logtype_afpd, "can't seteuid back %s", strerror(errno));
2337 goto err_exchangefile;
2339 /* all this stuff is so that we can unwind a failed operation
2342 /* rename dest to temp */
2343 renamefile(vol, -1, upath, temp, temp, adsp);
2344 of_rename(vol, s_of, curdir, upath, curdir, temp);
2347 /* rename source back to dest */
2348 renamefile(vol, -1, p, upath, path->m_name, addp);
2349 of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
2352 /* rename temp back to source */
2353 renamefile(vol, -1, temp, p, spath, adsp);
2354 of_rename(vol, s_of, curdir, temp, sdir, spath);
2357 if ( !s_of && adsp && ad_meta_fileno(adsp) != -1 ) { /* META */
2358 ad_close(adsp, ADFLAGS_HF);
2360 if ( !d_of && addp && ad_meta_fileno(addp) != -1 ) {/* META */
2361 ad_close(addp, ADFLAGS_HF);
2365 if ((cached = dircache_search_by_did(vol, sid)) != NULL)
2366 (void)dir_remove(vol, cached);
2367 if ((cached = dircache_search_by_did(vol, did)) != NULL)
2368 (void)dir_remove(vol, cached);