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>
41 #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))) {
329 struct dir *cachedfile;
330 int len = strlen(upath);
331 if ((cachedfile = dircache_search_by_name(vol, dir, upath, len, st->st_ctime)) != NULL)
332 id = cachedfile->d_did;
334 id = get_id(vol, adp, st, dir->d_did, upath, len);
336 /* Add it to the cache */
337 LOG(log_debug, logtype_afpd, "getmetadata: caching: did:%u, \"%s\", cnid:%u",
338 ntohl(dir->d_did), upath, ntohl(id));
340 /* Get macname from unixname first */
341 if (path->m_name == NULL) {
342 if ((path->m_name = utompath(vol, upath, id, utf8_encoding())) == NULL) {
343 LOG(log_error, logtype_afpd, "getmetadata: utompath error");
348 if ((cachedfile = dir_new(path->m_name, upath, vol, dir->d_did, id, NULL, st->st_ctime)) == NULL) {
349 LOG(log_error, logtype_afpd, "getmetadata: error from dir_new");
353 if ((dircache_add(vol, cachedfile)) != 0) {
354 LOG(log_error, logtype_afpd, "getmetadata: fatal dircache error");
362 if (id == CNID_INVALID)
366 path->m_name = utompath(vol, upath, id, utf8_encoding());
369 while ( bitmap != 0 ) {
370 while (( bitmap & 1 ) == 0 ) {
378 ad_getattr(adp, &ashort);
379 } else if (vol_inv_dots(vol) && *upath == '.') {
380 ashort = htons(ATTRBIT_INVISIBLE);
384 /* FIXME do we want a visual clue if the file is read only
387 accessmode( ".", &ma, dir , NULL);
388 if ((ma.ma_user & AR_UWRITE)) {
389 accessmode( upath, &ma, dir , st);
390 if (!(ma.ma_user & AR_UWRITE)) {
391 ashort |= htons(ATTRBIT_NOWRITE);
395 memcpy(data, &ashort, sizeof( ashort ));
396 data += sizeof( ashort );
397 LOG(log_debug, logtype_afpd, "metadata('%s'): AFP Attributes: %04x",
398 path->u_name, ntohs(ashort));
402 memcpy(data, &dir->d_did, sizeof( u_int32_t ));
403 data += sizeof( u_int32_t );
404 LOG(log_debug, logtype_afpd, "metadata('%s'): Parent DID: %u",
405 path->u_name, ntohl(dir->d_did));
409 if (!adp || (ad_getdate(adp, AD_DATE_CREATE, &aint) < 0))
410 aint = AD_DATE_FROM_UNIX(st->st_mtime);
411 memcpy(data, &aint, sizeof( aint ));
412 data += sizeof( aint );
416 if ( adp && (ad_getdate(adp, AD_DATE_MODIFY, &aint) == 0)) {
417 if ((st->st_mtime > AD_DATE_TO_UNIX(aint))) {
418 aint = AD_DATE_FROM_UNIX(st->st_mtime);
421 aint = AD_DATE_FROM_UNIX(st->st_mtime);
423 memcpy(data, &aint, sizeof( int ));
424 data += sizeof( int );
428 if (!adp || (ad_getdate(adp, AD_DATE_BACKUP, &aint) < 0))
429 aint = AD_DATE_START;
430 memcpy(data, &aint, sizeof( int ));
431 data += sizeof( int );
435 get_finderinfo(vol, upath, adp, (char *)data,S_ISLNK(st->st_mode));
436 data += ADEDLEN_FINDERI;
441 data += sizeof( u_int16_t );
445 memset(data, 0, sizeof(u_int16_t));
446 data += sizeof( u_int16_t );
450 memcpy(data, &id, sizeof( id ));
451 data += sizeof( id );
452 LOG(log_debug, logtype_afpd, "metadata('%s'): CNID: %u",
453 path->u_name, ntohl(id));
457 if (st->st_size > 0xffffffff)
460 aint = htonl( st->st_size );
461 memcpy(data, &aint, sizeof( aint ));
462 data += sizeof( aint );
467 if (adp->ad_rlen > 0xffffffff)
470 aint = htonl( adp->ad_rlen);
474 memcpy(data, &aint, sizeof( aint ));
475 data += sizeof( aint );
478 /* Current client needs ProDOS info block for this file.
479 Use simple heuristic and let the Mac "type" string tell
480 us what the PD file code should be. Everything gets a
481 subtype of 0x0000 unless the original value was hashed
482 to "pXYZ" when we created it. See IA, Ver 2.
483 <shirsch@adelphia.net> */
484 case FILPBIT_PDINFO :
485 if (afp_version >= 30) { /* UTF8 name */
486 utf8 = kTextEncodingUTF8;
488 data += sizeof( u_int16_t );
490 memcpy(data, &aint, sizeof( aint ));
491 data += sizeof( aint );
495 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
497 if ( memcmp( fdType, "TEXT", 4 ) == 0 ) {
501 else if ( memcmp( fdType, "PSYS", 4 ) == 0 ) {
505 else if ( memcmp( fdType, "PS16", 4 ) == 0 ) {
509 else if ( memcmp( fdType, "BINA", 4 ) == 0 ) {
513 else if ( fdType[0] == 'p' ) {
515 ashort = (fdType[2] * 256) + fdType[3];
529 memcpy(data, &ashort, sizeof( ashort ));
530 data += sizeof( ashort );
531 memset(data, 0, sizeof( ashort ));
532 data += sizeof( ashort );
535 case FILPBIT_EXTDFLEN:
536 aint = htonl(st->st_size >> 32);
537 memcpy(data, &aint, sizeof( aint ));
538 data += sizeof( aint );
539 aint = htonl(st->st_size);
540 memcpy(data, &aint, sizeof( aint ));
541 data += sizeof( aint );
543 case FILPBIT_EXTRFLEN:
546 aint = htonl(adp->ad_rlen >> 32);
547 memcpy(data, &aint, sizeof( aint ));
548 data += sizeof( aint );
550 aint = htonl(adp->ad_rlen);
551 memcpy(data, &aint, sizeof( aint ));
552 data += sizeof( aint );
554 case FILPBIT_UNIXPR :
555 /* accessmode may change st_mode with ACLs */
556 accessmode( upath, &ma, dir , st);
558 aint = htonl(st->st_uid);
559 memcpy( data, &aint, sizeof( aint ));
560 data += sizeof( aint );
561 aint = htonl(st->st_gid);
562 memcpy( data, &aint, sizeof( aint ));
563 data += sizeof( aint );
566 type == slnk indicates an OSX style symlink,
567 we have to add S_IFLNK to the mode, otherwise
568 10.3 clients freak out. */
572 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
573 if ( memcmp( fdType, "slnk", 4 ) == 0 ) {
579 memcpy( data, &aint, sizeof( aint ));
580 data += sizeof( aint );
582 *data++ = ma.ma_user;
583 *data++ = ma.ma_world;
584 *data++ = ma.ma_group;
585 *data++ = ma.ma_owner;
589 return( AFPERR_BITMAP );
595 ashort = htons( data - buf );
596 memcpy(l_nameoff, &ashort, sizeof( ashort ));
597 data = set_name(vol, data, dir->d_did, path->m_name, id, 0);
600 ashort = htons( data - buf );
601 memcpy(utf_nameoff, &ashort, sizeof( ashort ));
602 data = set_name(vol, data, dir->d_did, path->m_name, id, utf8);
604 *buflen = data - buf;
608 /* ----------------------- */
609 int getfilparams(struct vol *vol,
611 struct path *path, struct dir *dir,
612 char *buf, size_t *buflen )
614 struct adouble ad, *adp;
618 LOG(log_debug, logtype_afpd, "getfilparams(\"%s\")", path->u_name);
620 opened = PARAM_NEED_ADP(bitmap);
625 int flags = (bitmap & (1 << FILPBIT_ATTR))?ADFLAGS_OPENFORKS:0;
627 adp = of_ad(vol, path, &ad);
628 upath = path->u_name;
630 if ( ad_metadata( upath, flags|ADFLAGS_CREATE, adp) < 0 ) {
633 LOG(log_error, logtype_afpd, "getfilparams(%s): %s: check resource fork permission?",
634 upath, strerror(errno));
635 return AFPERR_ACCESS;
637 LOG(log_error, logtype_afpd, "getfilparams(%s): bad resource fork", upath);
646 rc = getmetadata(vol, bitmap, path, dir, buf, buflen, adp);
648 ad_close_metadata( adp);
654 /* ----------------------------- */
655 int afp_createfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
657 struct adouble ad, *adp;
660 struct ofork *of = NULL;
662 int creatf, did, openf, retvalue = AFP_OK;
668 creatf = (unsigned char) *ibuf++;
670 memcpy(&vid, ibuf, sizeof( vid ));
671 ibuf += sizeof( vid );
673 if (NULL == ( vol = getvolbyvid( vid )) ) {
674 return( AFPERR_PARAM );
677 if (vol->v_flags & AFPVOL_RO)
680 memcpy(&did, ibuf, sizeof( did));
681 ibuf += sizeof( did );
683 if (NULL == ( dir = dirlookup( vol, did )) ) {
687 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
688 return get_afp_errno(AFPERR_PARAM);
691 if ( *s_path->m_name == '\0' ) {
692 return( AFPERR_BADTYPE );
695 upath = s_path->u_name;
697 /* if upath is deleted we already in trouble anyway */
698 if ((of = of_findname(s_path))) {
701 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
705 /* on a hard create, fail if file exists and is open */
708 openf = O_RDWR|O_CREAT|O_TRUNC;
710 /* on a soft create, if the file is open then ad_open won't fail
711 because open syscall is not called
716 openf = O_RDWR|O_CREAT|O_EXCL;
719 if ( ad_open( upath, ADFLAGS_DF|ADFLAGS_HF|ADFLAGS_NOHF|ADFLAGS_CREATE,
720 openf, 0666, adp) < 0 ) {
724 case ENOENT : /* we were already in 'did folder' so chdir() didn't fail */
725 return ( AFPERR_NOOBJ );
727 return( AFPERR_EXIST );
729 return( AFPERR_ACCESS );
732 return( AFPERR_DFULL );
734 return( AFPERR_PARAM );
737 if ( ad_reso_fileno( adp ) == -1 ) { /* Hard META / HF */
738 /* on noadouble volumes, just creating the data fork is ok */
739 if (vol_noadouble(vol)) {
740 ad_close( adp, ADFLAGS_DF );
741 goto createfile_done;
743 /* FIXME with hard create on an existing file, we already
744 * corrupted the data file.
746 netatalk_unlink( upath );
747 ad_close( adp, ADFLAGS_DF );
748 return AFPERR_ACCESS;
751 path = s_path->m_name;
752 ad_setname(adp, path);
755 if (lstat(upath, &st) != 0) {
756 LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): stat: %s",
757 upath, strerror(errno));
758 ad_close( adp, ADFLAGS_DF|ADFLAGS_HF);
762 (void)get_id(vol, adp, &st, dir->d_did, upath, strlen(upath));
765 ad_close( adp, ADFLAGS_DF|ADFLAGS_HF );
771 if (vol->v_flags & AFPVOL_DROPBOX) {
772 retvalue = matchfile2dirperms(upath, vol, did);
774 #endif /* DROPKLUDGE */
776 setvoltime(obj, vol );
781 int afp_setfilparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
787 u_int16_t vid, bitmap;
792 memcpy(&vid, ibuf, sizeof( vid ));
793 ibuf += sizeof( vid );
794 if (NULL == ( vol = getvolbyvid( vid )) ) {
795 return( AFPERR_PARAM );
798 if (vol->v_flags & AFPVOL_RO)
801 memcpy(&did, ibuf, sizeof( did ));
802 ibuf += sizeof( did );
803 if (NULL == ( dir = dirlookup( vol, did )) ) {
804 return afp_errno; /* was AFPERR_NOOBJ */
807 memcpy(&bitmap, ibuf, sizeof( bitmap ));
808 bitmap = ntohs( bitmap );
809 ibuf += sizeof( bitmap );
811 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
812 return get_afp_errno(AFPERR_PARAM);
815 if (path_isadir(s_path)) {
816 return( AFPERR_BADTYPE ); /* it's a directory */
819 if ( s_path->st_errno != 0 ) {
820 return( AFPERR_NOOBJ );
823 if ((u_long)ibuf & 1 ) {
827 if (AFP_OK == ( rc = setfilparams(vol, s_path, bitmap, ibuf )) ) {
828 setvoltime(obj, vol );
835 * cf AFP3.0.pdf page 252 for change_mdate and change_parent_mdate logic
838 extern struct path Cur_Path;
840 int setfilparams(struct vol *vol,
841 struct path *path, u_int16_t f_bitmap, char *buf )
843 struct adouble ad, *adp;
845 int bit, isad = 1, err = AFP_OK;
847 u_char achar, *fdType, xyy[4]; /* uninitialized, OK 310105 */
848 u_int16_t ashort, bshort, oshort;
851 u_int16_t upriv_bit = 0;
855 int change_mdate = 0;
856 int change_parent_mdate = 0;
861 u_int16_t bitmap = f_bitmap;
862 u_int32_t cdate,bdate;
863 u_char finder_buf[32];
866 LOG(log_debug9, logtype_afpd, "begin setfilparams:");
869 adp = of_ad(vol, path, &ad);
870 upath = path->u_name;
872 if (!vol_unix_priv(vol) && check_access(upath, OPENACC_WR ) < 0) {
873 return AFPERR_ACCESS;
876 /* with unix priv maybe we have to change adouble file priv first */
878 while ( bitmap != 0 ) {
879 while (( bitmap & 1 ) == 0 ) {
886 memcpy(&ashort, buf, sizeof( ashort ));
887 buf += sizeof( ashort );
891 memcpy(&cdate, buf, sizeof(cdate));
892 buf += sizeof( cdate );
895 memcpy(&newdate, buf, sizeof( newdate ));
896 buf += sizeof( newdate );
900 memcpy(&bdate, buf, sizeof( bdate));
901 buf += sizeof( bdate );
905 memcpy(finder_buf, buf, 32 );
906 if (memcmp(buf,"slnkrhap",8)==0 && !S_ISLNK(path->st.st_mode)){
911 char buf[PATH_MAX+1];
912 if ((fp=open(path->u_name,O_RDONLY))>=0){
913 if ((len=read(fp,buf,PATH_MAX+1))){
914 if (unlink(path->u_name)==0){
916 erc = symlink(buf, path->u_name);
925 goto setfilparam_done;
930 case FILPBIT_UNIXPR :
931 if (!vol_unix_priv(vol)) {
932 /* this volume doesn't use unix priv */
938 change_parent_mdate = 1;
940 memcpy( &aint, buf, sizeof( aint ));
941 f_uid = ntohl (aint);
942 buf += sizeof( aint );
943 memcpy( &aint, buf, sizeof( aint ));
944 f_gid = ntohl (aint);
945 buf += sizeof( aint );
946 setfilowner(vol, f_uid, f_gid, path);
948 memcpy( &upriv, buf, sizeof( upriv ));
949 buf += sizeof( upriv );
950 upriv = ntohl (upriv);
951 if ((upriv & S_IWUSR)) {
952 setfilunixmode(vol, path, upriv);
959 case FILPBIT_PDINFO :
960 if (afp_version < 30) { /* else it's UTF8 name */
963 /* Keep special case to support crlf translations */
964 if ((unsigned int) achar == 0x04) {
965 fdType = (u_char *)"TEXT";
968 xyy[0] = ( u_char ) 'p';
979 /* break while loop */
988 /* second try with adouble open
990 if ( ad_open_metadata( upath, 0, O_CREAT, adp) < 0) {
991 LOG(log_debug, logtype_afpd, "setfilparams: ad_open_metadata error");
993 * For some things, we don't need an adouble header:
994 * - change of modification date
995 * - UNIX privs (Bug-ID #2863424)
997 if (!vol_noadouble(vol) && (f_bitmap & ~(1<<FILPBIT_MDATE | 1<<FILPBIT_UNIXPR))) {
998 LOG(log_debug, logtype_afpd, "setfilparams: need adouble access");
999 return AFPERR_ACCESS;
1001 LOG(log_debug, logtype_afpd, "setfilparams: no adouble perms, but only FILPBIT_MDATE and/or FILPBIT_UNIXPR");
1003 } else if ((ad_get_HF_flags( adp ) & O_CREAT) ) {
1004 ad_setname(adp, path->m_name);
1009 while ( bitmap != 0 ) {
1010 while (( bitmap & 1 ) == 0 ) {
1017 ad_getattr(adp, &bshort);
1019 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
1020 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
1024 if ((bshort & htons(ATTRBIT_INVISIBLE)) != (oshort & htons(ATTRBIT_INVISIBLE)))
1025 change_parent_mdate = 1;
1026 ad_setattr(adp, bshort);
1028 case FILPBIT_CDATE :
1029 ad_setdate(adp, AD_DATE_CREATE, cdate);
1031 case FILPBIT_MDATE :
1033 case FILPBIT_BDATE :
1034 ad_setdate(adp, AD_DATE_BACKUP, bdate);
1036 case FILPBIT_FINFO :
1037 if (default_type( ad_entry( adp, ADEID_FINDERI ))
1039 ((em = getextmap( path->m_name )) &&
1040 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1041 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1042 || ((em = getdefextmap()) &&
1043 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1044 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1046 memcpy(finder_buf, ufinderi, 8 );
1048 memcpy(ad_entry( adp, ADEID_FINDERI ), finder_buf, 32 );
1050 case FILPBIT_UNIXPR :
1052 setfilunixmode(vol, path, upriv);
1055 case FILPBIT_PDINFO :
1056 if (afp_version < 30) { /* else it's UTF8 name */
1057 memcpy(ad_entry( adp, ADEID_FINDERI ), fdType, 4 );
1058 memcpy(ad_entry( adp, ADEID_FINDERI ) + 4, "pdos", 4 );
1063 err = AFPERR_BITMAP;
1064 goto setfilparam_done;
1071 if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
1072 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1076 ad_setdate(adp, AD_DATE_MODIFY, newdate);
1077 ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
1083 ad_close_metadata( adp);
1087 if (change_parent_mdate && gettimeofday(&tv, NULL) == 0) {
1088 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1089 bitmap = 1<<FILPBIT_MDATE;
1090 setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
1094 LOG(log_debug9, logtype_afpd, "end setfilparams:");
1100 * renamefile and copyfile take the old and new unix pathnames
1101 * and the new mac name.
1103 * sdir_fd source dir fd to which src path is relative (for openat et al semantics)
1104 * passing -1 means this is not used, src path is a full path
1105 * src the source path
1106 * dst the dest filename in current dir
1107 * newname the dest mac name
1108 * adp adouble struct of src file, if open, or & zeroed one
1111 int renamefile(const struct vol *vol, int sdir_fd, char *src, char *dst, char *newname, struct adouble *adp)
1115 LOG(log_debug, logtype_afpd,
1116 "renamefile: src[%d, \"%s\"] -> dst[\"%s\"]", sdir_fd, src, dst);
1118 if ( unix_rename( sdir_fd, src, -1, dst ) < 0 ) {
1121 return( AFPERR_NOOBJ );
1124 return( AFPERR_ACCESS );
1126 return AFPERR_VLOCK;
1127 case EXDEV : /* Cross device move -- try copy */
1128 /* NOTE: with open file it's an error because after the copy we will
1129 * get two files, it's fixable for our process (eg reopen the new file, get the
1130 * locks, and so on. But it doesn't solve the case with a second process
1132 if (adp->ad_open_forks) {
1133 /* FIXME warning in syslog so admin'd know there's a conflict ?*/
1134 return AFPERR_OLOCK; /* little lie */
1136 if (AFP_OK != ( rc = copyfile(vol, vol, sdir_fd, src, dst, newname, NULL )) ) {
1137 /* on error copyfile delete dest */
1140 return deletefile(vol, sdir_fd, src, 0);
1142 return( AFPERR_PARAM );
1146 if (vol->vfs->vfs_renamefile(vol, sdir_fd, src, dst) < 0 ) {
1150 /* try to undo the data fork rename,
1151 * we know we are on the same device
1154 unix_rename(-1, dst, sdir_fd, src );
1155 /* return the first error */
1158 return AFPERR_NOOBJ;
1161 return AFPERR_ACCESS ;
1163 return AFPERR_VLOCK;
1165 return AFPERR_PARAM ;
1170 /* don't care if we can't open the newly renamed ressource fork
1172 if (!ad_open( dst, ADFLAGS_HF, O_RDWR, 0666, adp)) {
1173 ad_setname(adp, newname);
1175 ad_close( adp, ADFLAGS_HF );
1182 convert a Mac long name to an utf8 name,
1184 size_t mtoUTF8(const struct vol *vol, const char *src, size_t srclen, char *dest, size_t destlen)
1188 if ((size_t)-1 == (outlen = convert_string ( vol->v_maccharset, CH_UTF8_MAC, src, srclen, dest, destlen)) ) {
1194 /* ---------------- */
1195 int copy_path_name(const struct vol *vol, char *newname, char *ibuf)
1202 if ( type != 2 && !(afp_version >= 30 && type == 3) ) {
1208 if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
1209 if (afp_version >= 30) {
1210 /* convert it to UTF8
1212 if ((plen = mtoUTF8(vol, ibuf, plen, newname, AFPOBJ_TMPSIZ)) == (size_t)-1)
1216 strncpy( newname, ibuf, plen );
1217 newname[ plen ] = '\0';
1219 if (strlen(newname) != plen) {
1220 /* there's \0 in newname, e.g. it's a pathname not
1228 memcpy(&hint, ibuf, sizeof(hint));
1229 ibuf += sizeof(hint);
1231 memcpy(&len16, ibuf, sizeof(len16));
1232 ibuf += sizeof(len16);
1233 plen = ntohs(len16);
1236 if (plen > AFPOBJ_TMPSIZ) {
1239 strncpy( newname, ibuf, plen );
1240 newname[ plen ] = '\0';
1241 if (strlen(newname) != plen) {
1250 /* -----------------------------------
1252 int afp_copyfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1254 struct vol *s_vol, *d_vol;
1256 char *newname, *p, *upath;
1257 struct path *s_path;
1258 u_int32_t sdid, ddid;
1259 int err, retvalue = AFP_OK;
1260 u_int16_t svid, dvid;
1262 struct adouble ad, *adp;
1268 memcpy(&svid, ibuf, sizeof( svid ));
1269 ibuf += sizeof( svid );
1270 if (NULL == ( s_vol = getvolbyvid( svid )) ) {
1271 return( AFPERR_PARAM );
1274 memcpy(&sdid, ibuf, sizeof( sdid ));
1275 ibuf += sizeof( sdid );
1276 if (NULL == ( dir = dirlookup( s_vol, sdid )) ) {
1280 memcpy(&dvid, ibuf, sizeof( dvid ));
1281 ibuf += sizeof( dvid );
1282 memcpy(&ddid, ibuf, sizeof( ddid ));
1283 ibuf += sizeof( ddid );
1285 if (NULL == ( s_path = cname( s_vol, dir, &ibuf )) ) {
1286 return get_afp_errno(AFPERR_PARAM);
1288 if ( path_isadir(s_path) ) {
1289 return( AFPERR_BADTYPE );
1292 /* don't allow copies when the file is open.
1293 * XXX: the spec only calls for read/deny write access.
1294 * however, copyfile doesn't have any of that info,
1295 * and locks need to stay coherent. as a result,
1296 * we just balk if the file is opened already. */
1298 adp = of_ad(s_vol, s_path, &ad);
1300 if (ad_open(s_path->u_name , ADFLAGS_DF |ADFLAGS_HF | ADFLAGS_NOHF, O_RDONLY, 0, adp) < 0) {
1301 return AFPERR_DENYCONF;
1303 denyreadset = (getforkmode(adp, ADEID_DFORK, AD_FILELOCK_DENY_RD) != 0 ||
1304 getforkmode(adp, ADEID_RFORK, AD_FILELOCK_DENY_RD) != 0 );
1307 retvalue = AFPERR_DENYCONF;
1311 newname = obj->newtmp;
1312 strcpy( newname, s_path->m_name );
1314 p = ctoupath( s_vol, curdir, newname );
1316 retvalue = AFPERR_PARAM;
1321 /* FIXME svid != dvid && dvid's user can't read svid */
1323 if (NULL == ( d_vol = getvolbyvid( dvid )) ) {
1324 retvalue = AFPERR_PARAM;
1328 if (d_vol->v_flags & AFPVOL_RO) {
1329 retvalue = AFPERR_VLOCK;
1333 if (NULL == ( dir = dirlookup( d_vol, ddid )) ) {
1334 retvalue = afp_errno;
1338 if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) {
1339 retvalue = get_afp_errno(AFPERR_NOOBJ);
1343 if ( *s_path->m_name != '\0' ) {
1344 retvalue =path_error(s_path, AFPERR_NOOBJ);
1348 /* one of the handful of places that knows about the path type */
1349 if (copy_path_name(d_vol, newname, ibuf) < 0) {
1350 retvalue = AFPERR_PARAM;
1353 /* newname is always only a filename so curdir *is* its
1356 if (NULL == (upath = mtoupath(d_vol, newname, curdir->d_did, utf8_encoding()))) {
1357 retvalue =AFPERR_PARAM;
1361 if ( (err = copyfile(s_vol, d_vol, -1, p, upath , newname, adp)) < 0 ) {
1368 if (vol->v_flags & AFPVOL_DROPBOX) {
1369 retvalue=matchfile2dirperms(upath, vol, ddid); /* FIXME sdir or ddid */
1371 #endif /* DROPKLUDGE */
1373 setvoltime(obj, d_vol );
1376 ad_close( adp, ADFLAGS_DF |ADFLAGS_HF );
1380 /* ----------------------- */
1381 static int copy_all(const int dfd, const void *buf,
1387 LOG(log_debug9, logtype_afpd, "begin copy_all:");
1390 while (buflen > 0) {
1391 if ((cc = write(dfd, buf, buflen)) < 0) {
1403 LOG(log_debug9, logtype_afpd, "end copy_all:");
1409 /* --------------------------
1410 * copy only the fork data stream
1412 static int copy_fork(int eid, struct adouble *add, struct adouble *ads)
1419 if (eid == ADEID_DFORK) {
1420 sfd = ad_data_fileno(ads);
1421 dfd = ad_data_fileno(add);
1424 sfd = ad_reso_fileno(ads);
1425 dfd = ad_reso_fileno(add);
1428 if ((off_t)-1 == lseek(sfd, ad_getentryoff(ads, eid), SEEK_SET))
1431 if ((off_t)-1 == lseek(dfd, ad_getentryoff(add, eid), SEEK_SET))
1434 #if 0 /* ifdef SENDFILE_FLAVOR_LINUX */
1435 /* doesn't work With 2.6 FIXME, only check for EBADFD ? */
1439 #define BUF 128*1024*1024
1441 if (fstat(sfd, &st) == 0) {
1444 if ( offset >= st.st_size) {
1447 size = (st.st_size -offset > BUF)?BUF:st.st_size -offset;
1448 if ((cc = sys_sendfile(dfd, sfd, &offset, size)) < 0) {
1451 case EINVAL: /* there's no guarantee that all fs support sendfile */
1460 lseek(sfd, offset, SEEK_SET);
1464 if ((cc = read(sfd, filebuf, sizeof(filebuf))) < 0) {
1471 if (!cc || ((err = copy_all(dfd, filebuf, cc)) < 0)) {
1478 /* ----------------------------------
1479 * if newname is NULL (from directory.c) we don't want to copy the resource fork.
1480 * because we are doing it elsewhere.
1481 * currently if newname is NULL then adp is NULL.
1483 int copyfile(const struct vol *s_vol,
1484 const struct vol *d_vol,
1489 struct adouble *adp)
1491 struct adouble ads, add;
1498 LOG(log_debug, logtype_afpd, "copyfile(sfd:%d,s:'%s',d:'%s',n:'%s')",
1499 sfd, src, dst, newname);
1502 ad_init(&ads, s_vol->v_adouble, s_vol->v_ad_options);
1506 adflags = ADFLAGS_DF;
1508 adflags |= ADFLAGS_HF;
1511 if (ad_openat(sfd, src, adflags | ADFLAGS_NOHF, O_RDONLY, 0, adp) < 0) {
1516 if (ad_meta_fileno(adp) == -1 && ad_reso_fileno(adp) == -1) { /* META / HF */
1517 /* no resource fork, don't create one for dst file */
1518 adflags &= ~ADFLAGS_HF;
1521 stat_result = fstat(ad_data_fileno(adp), &st); /* saving stat exit code, thus saving us on one more stat later on */
1523 if (stat_result < 0) {
1524 /* unlikely but if fstat fails, the default file mode will be 0666. */
1525 st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
1528 ad_init(&add, d_vol->v_adouble, d_vol->v_ad_options);
1529 if (ad_open(dst , adflags, O_RDWR|O_CREAT|O_EXCL, st.st_mode, &add) < 0) {
1531 ad_close( adp, adflags );
1532 if (EEXIST != ret_err) {
1533 deletefile(d_vol, -1, dst, 0);
1536 return AFPERR_EXIST;
1540 * XXX if the source and the dest don't use the same resource type it's broken
1542 if (ad_reso_fileno(adp) == -1 || 0 == (err = copy_fork(ADEID_RFORK, &add, adp))){
1543 /* copy the data fork */
1544 if ((err = copy_fork(ADEID_DFORK, &add, adp)) == 0) {
1545 err = d_vol->vfs->vfs_copyfile(d_vol, sfd, src, dst);
1553 if (!ret_err && newname && (adflags & ADFLAGS_HF)) {
1554 /* set the new name in the resource fork */
1555 ad_copy_header(&add, adp);
1556 ad_setname(&add, newname);
1559 ad_close( adp, adflags );
1561 if (ad_close( &add, adflags ) <0) {
1566 deletefile(d_vol, -1, dst, 0);
1568 else if (stat_result == 0) {
1569 /* set dest modification date to src date */
1572 ut.actime = ut.modtime = st.st_mtime;
1574 /* FIXME netatalk doesn't use resource fork file date
1575 * but maybe we should set its modtime too.
1580 switch ( ret_err ) {
1586 return AFPERR_DFULL;
1588 return AFPERR_NOOBJ;
1590 return AFPERR_ACCESS;
1592 return AFPERR_VLOCK;
1594 return AFPERR_PARAM;
1598 /* -----------------------------------
1599 vol: not NULL delete cnid entry. then we are in curdir and file is a only filename
1600 checkAttrib: 1 check kFPDeleteInhibitBit (deletfile called by afp_delete)
1602 when deletefile is called we don't have lock on it, file is closed (for us)
1603 untrue if called by renamefile
1605 ad_open always try to open file RDWR first and ad_lock takes care of
1606 WRITE lock on read only file.
1609 static int check_attrib(struct adouble *adp)
1611 u_int16_t bshort = 0;
1613 ad_getattr(adp, &bshort);
1615 * Does kFPDeleteInhibitBit (bit 8) set?
1617 if ((bshort & htons(ATTRBIT_NODELETE))) {
1618 return AFPERR_OLOCK;
1620 if ((bshort & htons(ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
1626 * dirfd can be used for unlinkat semantics
1628 int deletefile(const struct vol *vol, int dirfd, char *file, int checkAttrib)
1631 struct adouble *adp = NULL;
1632 int adflags, err = AFP_OK;
1635 LOG(log_debug, logtype_afpd, "deletefile('%s')", file);
1637 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
1639 /* was EACCESS error try to get only metadata */
1640 /* we never want to create a resource fork here, we are going to delete it
1641 * moreover sometimes deletefile is called with a no existent file and
1642 * ad_open would create a 0 byte resource fork
1644 if ( ad_metadataat(dirfd, file, ADFLAGS_OPENFORKS, &ad) == 0 ) {
1645 if ((err = check_attrib(&ad))) {
1646 ad_close_metadata(&ad);
1653 /* try to open both forks at once */
1654 adflags = ADFLAGS_DF;
1655 if ( ad_openat(dirfd, file, adflags |ADFLAGS_HF|ADFLAGS_NOHF, O_RDONLY, 0, &ad ) < 0 ) {
1660 case EACCES: /* maybe it's a file with no write mode for us */
1661 break; /* was return AFPERR_ACCESS;*/
1674 if ( adp && ad_reso_fileno( adp ) != -1 ) { /* there's a resource fork */
1675 adflags |= ADFLAGS_HF;
1676 /* FIXME we have a pb here because we want to know if a file is open
1677 * there's a 'priority inversion' if you can't open the ressource fork RW
1678 * you can delete it if it's open because you can't get a write lock.
1680 * ADLOCK_FILELOCK means the whole ressource fork, not only after the
1683 * FIXME it doesn't work for RFORK open read only and fork open without deny mode
1685 if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
1691 if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
1693 } else if (!(err = vol->vfs->vfs_deletefile(vol, dirfd, file)) && !(err = netatalk_unlinkat(dirfd, file )) ) {
1695 if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file)))) {
1696 cnid_delete(vol->v_cdb, id);
1702 ad_close_metadata(&ad);
1705 ad_close( &ad, adflags ); /* ad_close removes locks if any */
1710 /* ------------------------------------ */
1711 /* return a file id */
1712 int afp_createid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1721 struct path *s_path;
1727 memcpy(&vid, ibuf, sizeof(vid));
1728 ibuf += sizeof(vid);
1730 if (NULL == ( vol = getvolbyvid( vid )) ) {
1731 return( AFPERR_PARAM);
1734 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1738 if (vol->v_flags & AFPVOL_RO)
1739 return AFPERR_VLOCK;
1741 memcpy(&did, ibuf, sizeof( did ));
1742 ibuf += sizeof(did);
1744 if (NULL == ( dir = dirlookup( vol, did )) ) {
1745 return afp_errno; /* was AFPERR_PARAM */
1748 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
1749 return get_afp_errno(AFPERR_NOOBJ); /* was AFPERR_PARAM */
1752 if ( path_isadir(s_path) ) {
1753 return( AFPERR_BADTYPE );
1756 upath = s_path->u_name;
1757 switch (s_path->st_errno) {
1759 break; /* success */
1762 return AFPERR_ACCESS;
1764 return AFPERR_NOOBJ;
1766 return AFPERR_PARAM;
1769 if ((id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath)))) {
1770 memcpy(rbuf, &id, sizeof(id));
1771 *rbuflen = sizeof(id);
1772 return AFPERR_EXISTID;
1775 if ((id = get_id(vol, NULL, st, did, upath, len)) != CNID_INVALID) {
1776 memcpy(rbuf, &id, sizeof(id));
1777 *rbuflen = sizeof(id);
1784 /* ------------------------------- */
1790 static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
1793 struct reenum *param = data;
1794 struct vol *vol = param->vol;
1795 cnid_t did = param->did;
1798 if ( lstat(de->d_name, &path.st)<0 )
1801 /* update or add to cnid */
1802 aint = cnid_add(vol->v_cdb, &path.st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */
1804 #if AD_VERSION > AD_VERSION1
1805 if (aint != CNID_INVALID && !S_ISDIR(path.st.st_mode)) {
1806 struct adouble ad, *adp;
1810 path.u_name = de->d_name;
1812 adp = of_ad(vol, &path, &ad);
1814 if ( ad_open_metadata( de->d_name, 0, 0, adp ) < 0 ) {
1817 if (ad_setid(adp, path.st.st_dev, path.st.st_ino, aint, did, vol->v_stamp)) {
1820 ad_close_metadata(adp);
1822 #endif /* AD_VERSION > AD_VERSION1 */
1827 /* --------------------
1828 * Ok the db is out of synch with the dir.
1829 * but if it's a deleted file we don't want to do it again and again.
1832 reenumerate_id(struct vol *vol, char *name, struct dir *dir)
1838 if (vol->v_cdb == NULL) {
1842 /* FIXME use of_statdir ? */
1843 if (lstat(name, &st)) {
1847 if (dirreenumerate(dir, &st)) {
1848 /* we already did it once and the dir haven't been modified */
1853 data.did = dir->d_did;
1854 if ((ret = for_each_dirent(vol, name, reenumerate_loop, (void *)&data)) >= 0) {
1855 setdiroffcnt(curdir, &st, ret);
1856 dir->d_flags |= DIRF_CNID;
1862 /* ------------------------------
1863 resolve a file id */
1864 int afp_resolveid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1873 u_int16_t vid, bitmap;
1875 static char buffer[12 + MAXPATHLEN + 1];
1876 int len = 12 + MAXPATHLEN + 1;
1881 memcpy(&vid, ibuf, sizeof(vid));
1882 ibuf += sizeof(vid);
1884 if (NULL == ( vol = getvolbyvid( vid )) ) {
1885 return( AFPERR_PARAM);
1888 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1892 memcpy(&id, ibuf, sizeof( id ));
1897 /* some MacOS versions after a catsearch do a *lot* of afp_resolveid with 0 */
1901 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1902 return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
1905 if (NULL == ( dir = dirlookup( vol, id )) ) {
1906 return AFPERR_NOID; /* idem AFPERR_PARAM */
1908 if (movecwd(vol, dir) < 0) {
1912 return AFPERR_ACCESS;
1916 return AFPERR_PARAM;
1920 memset(&path, 0, sizeof(path));
1921 path.u_name = upath;
1922 if ( of_stat(&path) < 0 ) {
1924 /* with nfs and our working directory is deleted */
1925 if (errno == ESTALE) {
1929 if ( errno == ENOENT && !retry) {
1930 /* cnid db is out of sync, reenumerate the directory and update ids */
1931 reenumerate_id(vol, ".", dir);
1939 return AFPERR_ACCESS;
1943 return AFPERR_PARAM;
1947 /* directories are bad */
1948 if (S_ISDIR(path.st.st_mode)) {
1949 /* OS9 and OSX don't return the same error code */
1950 return (afp_version >=30)?AFPERR_NOID:AFPERR_BADTYPE;
1953 memcpy(&bitmap, ibuf, sizeof(bitmap));
1954 bitmap = ntohs( bitmap );
1955 if (NULL == (path.m_name = utompath(vol, upath, cnid, utf8_encoding()))) {
1959 if (AFP_OK != (err = getfilparams(vol, bitmap, &path , curdir,
1960 rbuf + sizeof(bitmap), &buflen))) {
1963 *rbuflen = buflen + sizeof(bitmap);
1964 memcpy(rbuf, ibuf, sizeof(bitmap));
1969 /* ------------------------------ */
1970 int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1980 static char buffer[12 + MAXPATHLEN + 1];
1981 int len = 12 + MAXPATHLEN + 1;
1986 memcpy(&vid, ibuf, sizeof(vid));
1987 ibuf += sizeof(vid);
1989 if (NULL == ( vol = getvolbyvid( vid )) ) {
1990 return( AFPERR_PARAM);
1993 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1997 if (vol->v_flags & AFPVOL_RO)
1998 return AFPERR_VLOCK;
2000 memcpy(&id, ibuf, sizeof( id ));
2004 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
2008 if (NULL == ( dir = dirlookup( vol, id )) ) {
2009 if (afp_errno == AFPERR_NOOBJ) {
2013 return( AFPERR_PARAM );
2017 if ((movecwd(vol, dir) < 0) || (lstat(upath, &st) < 0)) {
2021 return AFPERR_ACCESS;
2026 /* still try to delete the id */
2030 return AFPERR_PARAM;
2033 else if (S_ISDIR(st.st_mode)) /* directories are bad */
2034 return AFPERR_BADTYPE;
2037 if (cnid_delete(vol->v_cdb, fileid)) {
2040 return AFPERR_VLOCK;
2043 return AFPERR_ACCESS;
2045 return AFPERR_PARAM;
2052 /* ------------------------------ */
2053 static struct adouble *find_adouble(struct path *path, struct ofork **of, struct adouble *adp)
2057 if (path->st_errno) {
2058 switch (path->st_errno) {
2060 afp_errno = AFPERR_NOID;
2064 afp_errno = AFPERR_ACCESS;
2067 afp_errno = AFPERR_PARAM;
2072 /* we use file_access both for legacy Mac perm and
2073 * for unix privilege, rename will take care of folder perms
2075 if (file_access(path, OPENACC_WR ) < 0) {
2076 afp_errno = AFPERR_ACCESS;
2080 if ((*of = of_findname(path))) {
2081 /* reuse struct adouble so it won't break locks */
2085 ret = ad_open( path->u_name, ADFLAGS_HF, O_RDONLY, 0, adp);
2087 if ( !ret && ad_reso_fileno(adp) != -1 && !(adp->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
2089 * The user must have the Read & Write privilege for both files in order to use this command.
2091 ad_close(adp, ADFLAGS_HF);
2092 afp_errno = AFPERR_ACCESS;
2099 #define APPLETEMP ".AppleTempXXXXXX"
2101 int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
2103 struct stat srcst, destst;
2105 struct dir *dir, *sdir;
2106 char *spath, temp[17], *p;
2107 char *supath, *upath;
2112 struct adouble *adsp = NULL;
2113 struct adouble *addp = NULL;
2114 struct ofork *s_of = NULL;
2115 struct ofork *d_of = NULL;
2128 memcpy(&vid, ibuf, sizeof(vid));
2129 ibuf += sizeof(vid);
2131 if (NULL == ( vol = getvolbyvid( vid )) ) {
2132 return( AFPERR_PARAM);
2135 if ((vol->v_flags & AFPVOL_RO))
2136 return AFPERR_VLOCK;
2138 /* source and destination dids */
2139 memcpy(&sid, ibuf, sizeof(sid));
2140 ibuf += sizeof(sid);
2141 memcpy(&did, ibuf, sizeof(did));
2142 ibuf += sizeof(did);
2145 if (NULL == (dir = dirlookup( vol, sid )) ) {
2146 return afp_errno; /* was AFPERR_PARAM */
2149 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2150 return get_afp_errno(AFPERR_NOOBJ);
2153 if ( path_isadir(path) ) {
2154 return AFPERR_BADTYPE; /* it's a dir */
2157 /* save some stuff */
2160 spath = obj->oldtmp;
2161 supath = obj->newtmp;
2162 strcpy(spath, path->m_name);
2163 strcpy(supath, path->u_name); /* this is for the cnid changing */
2164 p = absupath( vol, sdir, supath);
2166 /* pathname too long */
2167 return AFPERR_PARAM ;
2170 ad_init(&ads, vol->v_adouble, vol->v_ad_options);
2171 if (!(adsp = find_adouble( path, &s_of, &ads))) {
2175 /* ***** from here we may have resource fork open **** */
2177 /* look for the source cnid. if it doesn't exist, don't worry about
2179 sid = cnid_lookup(vol->v_cdb, &srcst, sdir->d_did, supath,slen = strlen(supath));
2181 if (NULL == ( dir = dirlookup( vol, did )) ) {
2182 err = afp_errno; /* was AFPERR_PARAM */
2183 goto err_exchangefile;
2186 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2187 err = get_afp_errno(AFPERR_NOOBJ);
2188 goto err_exchangefile;
2191 if ( path_isadir(path) ) {
2192 err = AFPERR_BADTYPE;
2193 goto err_exchangefile;
2196 /* FPExchangeFiles is the only call that can return the SameObj
2198 if ((curdir == sdir) && strcmp(spath, path->m_name) == 0) {
2199 err = AFPERR_SAMEOBJ;
2200 goto err_exchangefile;
2203 ad_init(&add, vol->v_adouble, vol->v_ad_options);
2204 if (!(addp = find_adouble( path, &d_of, &add))) {
2206 goto err_exchangefile;
2210 /* they are not on the same device and at least one is open
2211 * FIXME broken for for crossdev and adouble v2
2214 crossdev = (srcst.st_dev != destst.st_dev);
2215 if (/* (d_of || s_of) && */ crossdev) {
2217 goto err_exchangefile;
2220 /* look for destination id. */
2221 upath = path->u_name;
2222 did = cnid_lookup(vol->v_cdb, &destst, curdir->d_did, upath, dlen = strlen(upath));
2224 /* construct a temp name.
2225 * NOTE: the temp file will be in the dest file's directory. it
2226 * will also be inaccessible from AFP. */
2227 memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
2228 if (!mktemp(temp)) {
2230 goto err_exchangefile;
2234 /* FIXME we need to close fork for copy, both s_of and d_of are null */
2235 ad_close(adsp, ADFLAGS_HF);
2236 ad_close(addp, ADFLAGS_HF);
2239 /* now, quickly rename the file. we error if we can't. */
2240 if ((err = renamefile(vol, -1, p, temp, temp, adsp)) != AFP_OK)
2241 goto err_exchangefile;
2242 of_rename(vol, s_of, sdir, spath, curdir, temp);
2244 /* rename destination to source */
2245 if ((err = renamefile(vol, -1, upath, p, spath, addp)) != AFP_OK)
2246 goto err_src_to_tmp;
2247 of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
2249 /* rename temp to destination */
2250 if ((err = renamefile(vol, -1, temp, upath, path->m_name, adsp)) != AFP_OK)
2251 goto err_dest_to_src;
2252 of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
2254 /* id's need switching. src -> dest and dest -> src.
2255 * we need to re-stat() if it was a cross device copy.
2258 cnid_delete(vol->v_cdb, sid);
2260 cnid_delete(vol->v_cdb, did);
2262 if ((did && ( (crossdev && lstat( upath, &srcst) < 0) ||
2263 cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
2265 (sid && ( (crossdev && lstat(p, &destst) < 0) ||
2266 cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
2271 err = AFPERR_ACCESS;
2276 goto err_temp_to_dest;
2279 /* here we need to reopen if crossdev */
2280 if (sid && ad_setid(addp, destst.st_dev, destst.st_ino, sid, sdir->d_did, vol->v_stamp))
2285 if (did && ad_setid(adsp, srcst.st_dev, srcst.st_ino, did, curdir->d_did, vol->v_stamp))
2290 /* change perms, src gets dest perm and vice versa */
2295 LOG(log_error, logtype_afpd, "seteuid failed %s", strerror(errno));
2296 err = AFP_OK; /* ignore error */
2297 goto err_temp_to_dest;
2301 * we need to exchange ACL entries as well
2303 /* exchange_acls(vol, p, upath); */
2308 path->m_name = NULL;
2309 path->u_name = upath;
2311 setfilunixmode(vol, path, destst.st_mode);
2312 setfilowner(vol, destst.st_uid, destst.st_gid, path);
2319 setfilunixmode(vol, path, srcst.st_mode);
2320 setfilowner(vol, srcst.st_uid, srcst.st_gid, path);
2322 if ( setegid(gid) < 0 || seteuid(uid) < 0) {
2323 LOG(log_error, logtype_afpd, "can't seteuid back %s", strerror(errno));
2328 goto err_exchangefile;
2330 /* all this stuff is so that we can unwind a failed operation
2333 /* rename dest to temp */
2334 renamefile(vol, -1, upath, temp, temp, adsp);
2335 of_rename(vol, s_of, curdir, upath, curdir, temp);
2338 /* rename source back to dest */
2339 renamefile(vol, -1, p, upath, path->m_name, addp);
2340 of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
2343 /* rename temp back to source */
2344 renamefile(vol, -1, temp, p, spath, adsp);
2345 of_rename(vol, s_of, curdir, temp, sdir, spath);
2348 if ( !s_of && adsp && ad_meta_fileno(adsp) != -1 ) { /* META */
2349 ad_close(adsp, ADFLAGS_HF);
2351 if ( !d_of && addp && ad_meta_fileno(addp) != -1 ) {/* META */
2352 ad_close(addp, ADFLAGS_HF);
2356 if ((cached = dircache_search_by_did(vol, sid)) != NULL)
2357 (void)dir_remove(vol, cached);
2358 if ((cached = dircache_search_by_did(vol, did)) != NULL)
2359 (void)dir_remove(vol, cached);