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 > 255) /* FIXME safeguard, anyway if no ascii char it's game over*/
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 upath = path->u_name;
323 if ( ((bitmap & ( (1 << FILPBIT_FINFO)|(1 << FILPBIT_LNAME)|(1 <<FILPBIT_PDINFO) ) ) && !path->m_name)
324 || (bitmap & ( (1 << FILPBIT_LNAME) ) && utf8_encoding()) /* FIXME should be m_name utf8 filename */
325 || (bitmap & (1 << FILPBIT_FNUM))) {
327 struct dir *cachedfile;
328 int len = strlen(upath);
329 if ((cachedfile = dircache_search_by_name(vol, dir, upath, len, st->st_ctime)) != NULL)
330 id = cachedfile->d_did;
332 id = get_id(vol, adp, st, dir->d_did, upath, len);
334 /* Add it to the cache */
335 LOG(log_debug, logtype_afpd, "getmetadata: caching: did:%u, \"%s\", cnid:%u",
336 ntohl(dir->d_did), upath, ntohl(id));
338 /* Get macname from unixname first */
339 if (path->m_name == NULL) {
340 if ((path->m_name = utompath(vol, upath, id, utf8_encoding())) == NULL) {
341 LOG(log_error, logtype_afpd, "getmetadata: utompath error");
346 if ((cachedfile = dir_new(path->m_name, upath, vol, dir->d_did, id, NULL, st->st_ctime)) == NULL) {
347 LOG(log_error, logtype_afpd, "getmetadata: error from dir_new");
351 if ((dircache_add(cachedfile)) != 0) {
352 LOG(log_error, logtype_afpd, "getmetadata: fatal dircache error");
360 if (id == CNID_INVALID)
364 path->m_name = utompath(vol, upath, id, utf8_encoding());
367 while ( bitmap != 0 ) {
368 while (( bitmap & 1 ) == 0 ) {
376 ad_getattr(adp, &ashort);
377 } else if (vol_inv_dots(vol) && *upath == '.') {
378 ashort = htons(ATTRBIT_INVISIBLE);
382 /* FIXME do we want a visual clue if the file is read only
385 accessmode( ".", &ma, dir , NULL);
386 if ((ma.ma_user & AR_UWRITE)) {
387 accessmode( upath, &ma, dir , st);
388 if (!(ma.ma_user & AR_UWRITE)) {
389 ashort |= htons(ATTRBIT_NOWRITE);
393 memcpy(data, &ashort, sizeof( ashort ));
394 data += sizeof( ashort );
395 LOG(log_debug, logtype_afpd, "metadata('%s'): AFP Attributes: %04x",
396 path->u_name, ntohs(ashort));
400 memcpy(data, &dir->d_did, sizeof( u_int32_t ));
401 data += sizeof( u_int32_t );
402 LOG(log_debug, logtype_afpd, "metadata('%s'): Parent DID: %u",
403 path->u_name, ntohl(dir->d_did));
407 if (!adp || (ad_getdate(adp, AD_DATE_CREATE, &aint) < 0))
408 aint = AD_DATE_FROM_UNIX(st->st_mtime);
409 memcpy(data, &aint, sizeof( aint ));
410 data += sizeof( aint );
414 if ( adp && (ad_getdate(adp, AD_DATE_MODIFY, &aint) == 0)) {
415 if ((st->st_mtime > AD_DATE_TO_UNIX(aint))) {
416 aint = AD_DATE_FROM_UNIX(st->st_mtime);
419 aint = AD_DATE_FROM_UNIX(st->st_mtime);
421 memcpy(data, &aint, sizeof( int ));
422 data += sizeof( int );
426 if (!adp || (ad_getdate(adp, AD_DATE_BACKUP, &aint) < 0))
427 aint = AD_DATE_START;
428 memcpy(data, &aint, sizeof( int ));
429 data += sizeof( int );
433 get_finderinfo(vol, upath, adp, (char *)data,S_ISLNK(st->st_mode));
434 data += ADEDLEN_FINDERI;
439 data += sizeof( u_int16_t );
443 memset(data, 0, sizeof(u_int16_t));
444 data += sizeof( u_int16_t );
448 memcpy(data, &id, sizeof( id ));
449 data += sizeof( id );
450 LOG(log_debug, logtype_afpd, "metadata('%s'): CNID: %u",
451 path->u_name, ntohl(id));
455 if (st->st_size > 0xffffffff)
458 aint = htonl( st->st_size );
459 memcpy(data, &aint, sizeof( aint ));
460 data += sizeof( aint );
465 if (adp->ad_rlen > 0xffffffff)
468 aint = htonl( adp->ad_rlen);
472 memcpy(data, &aint, sizeof( aint ));
473 data += sizeof( aint );
476 /* Current client needs ProDOS info block for this file.
477 Use simple heuristic and let the Mac "type" string tell
478 us what the PD file code should be. Everything gets a
479 subtype of 0x0000 unless the original value was hashed
480 to "pXYZ" when we created it. See IA, Ver 2.
481 <shirsch@adelphia.net> */
482 case FILPBIT_PDINFO :
483 if (afp_version >= 30) { /* UTF8 name */
484 utf8 = kTextEncodingUTF8;
486 data += sizeof( u_int16_t );
488 memcpy(data, &aint, sizeof( aint ));
489 data += sizeof( aint );
493 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
495 if ( memcmp( fdType, "TEXT", 4 ) == 0 ) {
499 else if ( memcmp( fdType, "PSYS", 4 ) == 0 ) {
503 else if ( memcmp( fdType, "PS16", 4 ) == 0 ) {
507 else if ( memcmp( fdType, "BINA", 4 ) == 0 ) {
511 else if ( fdType[0] == 'p' ) {
513 ashort = (fdType[2] * 256) + fdType[3];
527 memcpy(data, &ashort, sizeof( ashort ));
528 data += sizeof( ashort );
529 memset(data, 0, sizeof( ashort ));
530 data += sizeof( ashort );
533 case FILPBIT_EXTDFLEN:
534 aint = htonl(st->st_size >> 32);
535 memcpy(data, &aint, sizeof( aint ));
536 data += sizeof( aint );
537 aint = htonl(st->st_size);
538 memcpy(data, &aint, sizeof( aint ));
539 data += sizeof( aint );
541 case FILPBIT_EXTRFLEN:
544 aint = htonl(adp->ad_rlen >> 32);
545 memcpy(data, &aint, sizeof( aint ));
546 data += sizeof( aint );
548 aint = htonl(adp->ad_rlen);
549 memcpy(data, &aint, sizeof( aint ));
550 data += sizeof( aint );
552 case FILPBIT_UNIXPR :
553 /* accessmode may change st_mode with ACLs */
554 accessmode( upath, &ma, dir , st);
556 aint = htonl(st->st_uid);
557 memcpy( data, &aint, sizeof( aint ));
558 data += sizeof( aint );
559 aint = htonl(st->st_gid);
560 memcpy( data, &aint, sizeof( aint ));
561 data += sizeof( aint );
564 type == slnk indicates an OSX style symlink,
565 we have to add S_IFLNK to the mode, otherwise
566 10.3 clients freak out. */
570 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
571 if ( memcmp( fdType, "slnk", 4 ) == 0 ) {
577 memcpy( data, &aint, sizeof( aint ));
578 data += sizeof( aint );
580 *data++ = ma.ma_user;
581 *data++ = ma.ma_world;
582 *data++ = ma.ma_group;
583 *data++ = ma.ma_owner;
587 return( AFPERR_BITMAP );
593 ashort = htons( data - buf );
594 memcpy(l_nameoff, &ashort, sizeof( ashort ));
595 data = set_name(vol, data, dir->d_did, path->m_name, id, 0);
598 ashort = htons( data - buf );
599 memcpy(utf_nameoff, &ashort, sizeof( ashort ));
600 data = set_name(vol, data, dir->d_did, path->m_name, id, utf8);
602 *buflen = data - buf;
606 /* ----------------------- */
607 int getfilparams(struct vol *vol,
609 struct path *path, struct dir *dir,
610 char *buf, size_t *buflen )
612 struct adouble ad, *adp;
616 opened = PARAM_NEED_ADP(bitmap);
621 int flags = (bitmap & (1 << FILPBIT_ATTR))?ADFLAGS_OPENFORKS:0;
623 adp = of_ad(vol, path, &ad);
624 upath = path->u_name;
626 if ( ad_metadata( upath, flags, adp) < 0 ) {
629 LOG(log_error, logtype_afpd, "getfilparams(%s): %s: check resource fork permission?",
630 upath, strerror(errno));
631 return AFPERR_ACCESS;
633 LOG(log_error, logtype_afpd, "getfilparams(%s): bad resource fork", upath);
642 rc = getmetadata(vol, bitmap, path, dir, buf, buflen, adp);
644 ad_close_metadata( adp);
650 /* ----------------------------- */
651 int afp_createfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
656 struct ofork *of = NULL;
658 int creatf, did, openf, retvalue = AFP_OK;
664 creatf = (unsigned char) *ibuf++;
666 memcpy(&vid, ibuf, sizeof( vid ));
667 ibuf += sizeof( vid );
669 if (NULL == ( vol = getvolbyvid( vid )) )
670 return( AFPERR_PARAM );
672 if (vol->v_flags & AFPVOL_RO)
675 memcpy(&did, ibuf, sizeof( did));
676 ibuf += sizeof( did );
678 if (NULL == ( dir = dirlookup( vol, did )) )
681 if (NULL == ( s_path = cname( vol, dir, &ibuf )) )
682 return get_afp_errno(AFPERR_PARAM);
684 if ( *s_path->m_name == '\0' )
685 return( AFPERR_BADTYPE );
687 upath = s_path->u_name;
688 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
690 /* if upath is deleted we already in trouble anyway */
691 if ((of = of_findname(s_path))) {
699 openf = O_RDWR|O_CREAT|O_TRUNC;
701 /* on a soft create, if the file is open then ad_open won't fail
702 because open syscall is not called */
703 openf = O_RDWR|O_CREAT|O_EXCL;
705 if ( ad_open(&ad, upath, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF,
706 openf, 0666, openf, 0666) < 0 ) {
710 case ENOENT : /* we were already in 'did folder' so chdir() didn't fail */
711 return ( AFPERR_NOOBJ );
713 return( AFPERR_EXIST );
715 return( AFPERR_ACCESS );
718 return( AFPERR_DFULL );
720 return( AFPERR_PARAM );
723 if ( ad_meta_fileno( &ad ) == -1 ) { /* Hard META / HF */
724 /* on noadouble volumes, just creating the data fork is ok */
725 if (vol_noadouble(vol)) {
726 ad_close( &ad, ADFLAGS_DF );
727 goto createfile_done;
729 /* FIXME with hard create on an existing file, we already
730 * corrupted the data file.
732 netatalk_unlink( upath );
733 ad_close( &ad, ADFLAGS_DF );
734 return AFPERR_ACCESS;
737 path = s_path->m_name;
738 ad_setname(&ad, path);
740 ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF );
746 if (vol->v_flags & AFPVOL_DROPBOX) {
747 retvalue = matchfile2dirperms(upath, vol, did);
749 #endif /* DROPKLUDGE */
751 setvoltime(obj, vol );
756 int afp_setfilparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
762 u_int16_t vid, bitmap;
767 memcpy(&vid, ibuf, sizeof( vid ));
768 ibuf += sizeof( vid );
769 if (NULL == ( vol = getvolbyvid( vid )) ) {
770 return( AFPERR_PARAM );
773 if (vol->v_flags & AFPVOL_RO)
776 memcpy(&did, ibuf, sizeof( did ));
777 ibuf += sizeof( did );
778 if (NULL == ( dir = dirlookup( vol, did )) ) {
779 return afp_errno; /* was AFPERR_NOOBJ */
782 memcpy(&bitmap, ibuf, sizeof( bitmap ));
783 bitmap = ntohs( bitmap );
784 ibuf += sizeof( bitmap );
786 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
787 return get_afp_errno(AFPERR_PARAM);
790 if (path_isadir(s_path)) {
791 return( AFPERR_BADTYPE ); /* it's a directory */
794 if ( s_path->st_errno != 0 ) {
795 return( AFPERR_NOOBJ );
798 if ((u_long)ibuf & 1 ) {
802 if (AFP_OK == ( rc = setfilparams(vol, s_path, bitmap, ibuf )) ) {
803 setvoltime(obj, vol );
810 * cf AFP3.0.pdf page 252 for change_mdate and change_parent_mdate logic
813 extern struct path Cur_Path;
815 int setfilparams(struct vol *vol,
816 struct path *path, u_int16_t f_bitmap, char *buf )
818 struct adouble ad, *adp;
820 int bit, isad = 1, err = AFP_OK;
822 u_char achar, *fdType, xyy[4]; /* uninitialized, OK 310105 */
823 u_int16_t ashort, bshort, oshort;
826 u_int16_t upriv_bit = 0;
830 int change_mdate = 0;
831 int change_parent_mdate = 0;
836 u_int16_t bitmap = f_bitmap;
837 u_int32_t cdate,bdate;
838 u_char finder_buf[32];
841 LOG(log_debug9, logtype_afpd, "begin setfilparams:");
844 adp = of_ad(vol, path, &ad);
845 upath = path->u_name;
847 if (!vol_unix_priv(vol) && check_access(upath, OPENACC_WR ) < 0) {
848 return AFPERR_ACCESS;
851 /* with unix priv maybe we have to change adouble file priv first */
853 while ( bitmap != 0 ) {
854 while (( bitmap & 1 ) == 0 ) {
861 memcpy(&ashort, buf, sizeof( ashort ));
862 buf += sizeof( ashort );
866 memcpy(&cdate, buf, sizeof(cdate));
867 buf += sizeof( cdate );
870 memcpy(&newdate, buf, sizeof( newdate ));
871 buf += sizeof( newdate );
875 memcpy(&bdate, buf, sizeof( bdate));
876 buf += sizeof( bdate );
880 memcpy(finder_buf, buf, 32 );
881 if (memcmp(buf,"slnkrhap",8)==0 && !S_ISLNK(path->st.st_mode)){
886 char buf[PATH_MAX+1];
887 if ((fp=open(path->u_name,O_RDONLY))>=0){
888 if ((len=read(fp,buf,PATH_MAX+1))){
889 if (unlink(path->u_name)==0){
891 erc = symlink(buf, path->u_name);
900 goto setfilparam_done;
905 case FILPBIT_UNIXPR :
906 if (!vol_unix_priv(vol)) {
907 /* this volume doesn't use unix priv */
913 change_parent_mdate = 1;
915 memcpy( &aint, buf, sizeof( aint ));
916 f_uid = ntohl (aint);
917 buf += sizeof( aint );
918 memcpy( &aint, buf, sizeof( aint ));
919 f_gid = ntohl (aint);
920 buf += sizeof( aint );
921 setfilowner(vol, f_uid, f_gid, path);
923 memcpy( &upriv, buf, sizeof( upriv ));
924 buf += sizeof( upriv );
925 upriv = ntohl (upriv);
926 if ((upriv & S_IWUSR)) {
927 setfilunixmode(vol, path, upriv);
934 case FILPBIT_PDINFO :
935 if (afp_version < 30) { /* else it's UTF8 name */
938 /* Keep special case to support crlf translations */
939 if ((unsigned int) achar == 0x04) {
940 fdType = (u_char *)"TEXT";
943 xyy[0] = ( u_char ) 'p';
954 /* break while loop */
963 /* second try with adouble open
965 if ( ad_open(adp, upath, ADFLAGS_HF, O_RDWR | O_CREAT, 0666) < 0) {
966 LOG(log_debug, logtype_afpd, "setfilparams: ad_open_metadata error");
968 * For some things, we don't need an adouble header:
969 * - change of modification date
970 * - UNIX privs (Bug-ID #2863424)
972 if (!vol_noadouble(vol) && (f_bitmap & ~(1<<FILPBIT_MDATE | 1<<FILPBIT_UNIXPR))) {
973 LOG(log_debug, logtype_afpd, "setfilparams: need adouble access");
974 return AFPERR_ACCESS;
976 LOG(log_debug, logtype_afpd, "setfilparams: no adouble perms, but only FILPBIT_MDATE and/or FILPBIT_UNIXPR");
978 } else if ((ad_get_HF_flags( adp ) & O_CREAT) ) {
979 ad_setname(adp, path->m_name);
984 while ( bitmap != 0 ) {
985 while (( bitmap & 1 ) == 0 ) {
992 ad_getattr(adp, &bshort);
994 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
995 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
999 if ((bshort & htons(ATTRBIT_INVISIBLE)) != (oshort & htons(ATTRBIT_INVISIBLE)))
1000 change_parent_mdate = 1;
1001 ad_setattr(adp, bshort);
1003 case FILPBIT_CDATE :
1004 ad_setdate(adp, AD_DATE_CREATE, cdate);
1006 case FILPBIT_MDATE :
1008 case FILPBIT_BDATE :
1009 ad_setdate(adp, AD_DATE_BACKUP, bdate);
1011 case FILPBIT_FINFO :
1012 if (default_type( ad_entry( adp, ADEID_FINDERI ))
1014 ((em = getextmap( path->m_name )) &&
1015 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1016 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1017 || ((em = getdefextmap()) &&
1018 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1019 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1021 memcpy(finder_buf, ufinderi, 8 );
1023 memcpy(ad_entry( adp, ADEID_FINDERI ), finder_buf, 32 );
1025 case FILPBIT_UNIXPR :
1027 setfilunixmode(vol, path, upriv);
1030 case FILPBIT_PDINFO :
1031 if (afp_version < 30) { /* else it's UTF8 name */
1032 memcpy(ad_entry( adp, ADEID_FINDERI ), fdType, 4 );
1033 memcpy(ad_entry( adp, ADEID_FINDERI ) + 4, "pdos", 4 );
1038 err = AFPERR_BITMAP;
1039 goto setfilparam_done;
1046 if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
1047 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1051 ad_setdate(adp, AD_DATE_MODIFY, newdate);
1052 ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
1059 ad_close_metadata( adp);
1062 if (change_parent_mdate && gettimeofday(&tv, NULL) == 0) {
1063 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1064 bitmap = 1<<FILPBIT_MDATE;
1065 setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
1069 LOG(log_debug9, logtype_afpd, "end setfilparams:");
1075 * renamefile and copyfile take the old and new unix pathnames
1076 * and the new mac name.
1078 * sdir_fd source dir fd to which src path is relative (for openat et al semantics)
1079 * passing -1 means this is not used, src path is a full path
1080 * src the source path
1081 * dst the dest filename in current dir
1082 * newname the dest mac name
1083 * adp adouble struct of src file, if open, or & zeroed one
1086 int renamefile(const struct vol *vol, int sdir_fd, char *src, char *dst, char *newname, struct adouble *adp)
1090 if ( unix_rename( sdir_fd, src, -1, dst ) < 0 ) {
1093 return( AFPERR_NOOBJ );
1096 return( AFPERR_ACCESS );
1098 return AFPERR_VLOCK;
1099 case EXDEV : /* Cross device move -- try copy */
1100 /* NOTE: with open file it's an error because after the copy we will
1101 * get two files, it's fixable for our process (eg reopen the new file, get the
1102 * locks, and so on. But it doesn't solve the case with a second process
1104 if (adp->ad_open_forks) {
1105 /* FIXME warning in syslog so admin'd know there's a conflict ?*/
1106 return AFPERR_OLOCK; /* little lie */
1108 if (AFP_OK != ( rc = copyfile(vol, vol, sdir_fd, src, dst, newname, NULL )) ) {
1109 /* on error copyfile delete dest */
1112 return deletefile(vol, sdir_fd, src, 0);
1114 return( AFPERR_PARAM );
1118 if (vol->vfs->vfs_renamefile(vol, sdir_fd, src, dst) < 0 ) {
1122 /* try to undo the data fork rename,
1123 * we know we are on the same device
1126 unix_rename(-1, dst, sdir_fd, src );
1127 /* return the first error */
1130 return AFPERR_NOOBJ;
1133 return AFPERR_ACCESS ;
1135 return AFPERR_VLOCK;
1137 return AFPERR_PARAM ;
1142 /* don't care if we can't open the newly renamed ressource fork
1144 if (ad_open(adp, dst, ADFLAGS_HF, O_RDWR) == 0) {
1145 ad_setname(adp, newname);
1147 ad_close( adp, ADFLAGS_HF );
1154 convert a Mac long name to an utf8 name,
1156 size_t mtoUTF8(const struct vol *vol, const char *src, size_t srclen, char *dest, size_t destlen)
1160 if ((size_t)-1 == (outlen = convert_string ( vol->v_maccharset, CH_UTF8_MAC, src, srclen, dest, destlen)) ) {
1166 /* ---------------- */
1167 int copy_path_name(const struct vol *vol, char *newname, char *ibuf)
1174 if ( type != 2 && !(afp_version >= 30 && type == 3) ) {
1180 if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
1181 if (afp_version >= 30) {
1182 /* convert it to UTF8
1184 if ((plen = mtoUTF8(vol, ibuf, plen, newname, AFPOBJ_TMPSIZ)) == (size_t)-1)
1188 strncpy( newname, ibuf, plen );
1189 newname[ plen ] = '\0';
1191 if (strlen(newname) != plen) {
1192 /* there's \0 in newname, e.g. it's a pathname not
1200 memcpy(&hint, ibuf, sizeof(hint));
1201 ibuf += sizeof(hint);
1203 memcpy(&len16, ibuf, sizeof(len16));
1204 ibuf += sizeof(len16);
1205 plen = ntohs(len16);
1208 if (plen > AFPOBJ_TMPSIZ) {
1211 strncpy( newname, ibuf, plen );
1212 newname[ plen ] = '\0';
1213 if (strlen(newname) != plen) {
1222 /* -----------------------------------
1224 int afp_copyfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1226 struct vol *s_vol, *d_vol;
1228 char *newname, *p, *upath;
1229 struct path *s_path;
1230 u_int32_t sdid, ddid;
1231 int err, retvalue = AFP_OK;
1232 u_int16_t svid, dvid;
1234 struct adouble ad, *adp;
1240 memcpy(&svid, ibuf, sizeof( svid ));
1241 ibuf += sizeof( svid );
1242 if (NULL == ( s_vol = getvolbyvid( svid )) ) {
1243 return( AFPERR_PARAM );
1246 memcpy(&sdid, ibuf, sizeof( sdid ));
1247 ibuf += sizeof( sdid );
1248 if (NULL == ( dir = dirlookup( s_vol, sdid )) ) {
1252 memcpy(&dvid, ibuf, sizeof( dvid ));
1253 ibuf += sizeof( dvid );
1254 memcpy(&ddid, ibuf, sizeof( ddid ));
1255 ibuf += sizeof( ddid );
1257 if (NULL == ( s_path = cname( s_vol, dir, &ibuf )) ) {
1258 return get_afp_errno(AFPERR_PARAM);
1260 if ( path_isadir(s_path) ) {
1261 return( AFPERR_BADTYPE );
1264 /* don't allow copies when the file is open.
1265 * XXX: the spec only calls for read/deny write access.
1266 * however, copyfile doesn't have any of that info,
1267 * and locks need to stay coherent. as a result,
1268 * we just balk if the file is opened already. */
1270 adp = of_ad(s_vol, s_path, &ad);
1272 if (ad_open(adp, s_path->u_name, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF, O_RDONLY, O_RDONLY) < 0) {
1273 return AFPERR_DENYCONF;
1275 denyreadset = (getforkmode(adp, ADEID_DFORK, AD_FILELOCK_DENY_RD) != 0 ||
1276 getforkmode(adp, ADEID_RFORK, AD_FILELOCK_DENY_RD) != 0 );
1279 retvalue = AFPERR_DENYCONF;
1283 newname = obj->newtmp;
1284 strcpy( newname, s_path->m_name );
1286 p = ctoupath( s_vol, curdir, newname );
1288 retvalue = AFPERR_PARAM;
1293 /* FIXME svid != dvid && dvid's user can't read svid */
1295 if (NULL == ( d_vol = getvolbyvid( dvid )) ) {
1296 retvalue = AFPERR_PARAM;
1300 if (d_vol->v_flags & AFPVOL_RO) {
1301 retvalue = AFPERR_VLOCK;
1305 if (NULL == ( dir = dirlookup( d_vol, ddid )) ) {
1306 retvalue = afp_errno;
1310 if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) {
1311 retvalue = get_afp_errno(AFPERR_NOOBJ);
1315 if ( *s_path->m_name != '\0' ) {
1316 retvalue =path_error(s_path, AFPERR_NOOBJ);
1320 /* one of the handful of places that knows about the path type */
1321 if (copy_path_name(d_vol, newname, ibuf) < 0) {
1322 retvalue = AFPERR_PARAM;
1325 /* newname is always only a filename so curdir *is* its
1328 if (NULL == (upath = mtoupath(d_vol, newname, curdir->d_did, utf8_encoding()))) {
1329 retvalue =AFPERR_PARAM;
1333 if ( (err = copyfile(s_vol, d_vol, -1, p, upath , newname, adp)) < 0 ) {
1340 if (vol->v_flags & AFPVOL_DROPBOX) {
1341 retvalue=matchfile2dirperms(upath, vol, ddid); /* FIXME sdir or ddid */
1343 #endif /* DROPKLUDGE */
1345 setvoltime(obj, d_vol );
1349 ad_close( adp, ADFLAGS_DF |ADFLAGS_HF );
1353 /* ----------------------- */
1354 static int copy_all(const int dfd, const void *buf,
1360 LOG(log_debug9, logtype_afpd, "begin copy_all:");
1363 while (buflen > 0) {
1364 if ((cc = write(dfd, buf, buflen)) < 0) {
1376 LOG(log_debug9, logtype_afpd, "end copy_all:");
1382 /* --------------------------
1383 * copy only the fork data stream
1385 static int copy_fork(int eid, struct adouble *add, struct adouble *ads)
1392 if (eid == ADEID_DFORK) {
1393 sfd = ad_data_fileno(ads);
1394 dfd = ad_data_fileno(add);
1397 sfd = ad_reso_fileno(ads);
1398 dfd = ad_reso_fileno(add);
1401 if ((off_t)-1 == lseek(sfd, ad_getentryoff(ads, eid), SEEK_SET))
1404 if ((off_t)-1 == lseek(dfd, ad_getentryoff(add, eid), SEEK_SET))
1407 #if 0 /* ifdef SENDFILE_FLAVOR_LINUX */
1408 /* doesn't work With 2.6 FIXME, only check for EBADFD ? */
1412 #define BUF 128*1024*1024
1414 if (fstat(sfd, &st) == 0) {
1417 if ( offset >= st.st_size) {
1420 size = (st.st_size -offset > BUF)?BUF:st.st_size -offset;
1421 if ((cc = sys_sendfile(dfd, sfd, &offset, size)) < 0) {
1424 case EINVAL: /* there's no guarantee that all fs support sendfile */
1433 lseek(sfd, offset, SEEK_SET);
1437 if ((cc = read(sfd, filebuf, sizeof(filebuf))) < 0) {
1444 if (!cc || ((err = copy_all(dfd, filebuf, cc)) < 0)) {
1451 /* ----------------------------------
1452 * if newname is NULL (from directory.c) we don't want to copy the resource fork.
1453 * because we are doing it elsewhere.
1454 * currently if newname is NULL then adp is NULL.
1456 int copyfile(const struct vol *s_vol,
1457 const struct vol *d_vol,
1462 struct adouble *adp)
1464 struct adouble ads, add;
1471 LOG(log_debug, logtype_afpd, "copyfile(sfd:%d,s:'%s',d:'%s',n:'%s')",
1472 sfd, src, dst, newname);
1475 ad_init(&ads, s_vol->v_adouble, s_vol->v_ad_options);
1479 adflags = ADFLAGS_DF;
1481 adflags |= ADFLAGS_HF;
1484 if (ad_openat(adp, sfd, src, adflags | ADFLAGS_NOHF, O_RDONLY) < 0) {
1489 if (ad_meta_fileno(adp) == -1 && ad_reso_fileno(adp) == -1) { /* META / HF */
1490 /* no resource fork, don't create one for dst file */
1491 adflags &= ~ADFLAGS_HF;
1494 stat_result = fstat(ad_data_fileno(adp), &st); /* saving stat exit code, thus saving us on one more stat later on */
1496 if (stat_result < 0) {
1497 /* unlikely but if fstat fails, the default file mode will be 0666. */
1498 st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
1501 ad_init(&add, d_vol->v_adouble, d_vol->v_ad_options);
1502 if (ad_open(&add, dst, adflags, O_RDWR|O_CREAT|O_EXCL, st.st_mode, O_RDWR|O_CREAT|O_EXCL, st.st_mode) < 0) {
1504 ad_close( adp, adflags );
1505 if (EEXIST != ret_err) {
1506 deletefile(d_vol, -1, dst, 0);
1509 return AFPERR_EXIST;
1513 * XXX if the source and the dest don't use the same resource type it's broken
1515 if (ad_reso_fileno(adp) == -1 || 0 == (err = copy_fork(ADEID_RFORK, &add, adp))){
1516 /* copy the data fork */
1517 if ((err = copy_fork(ADEID_DFORK, &add, adp)) == 0) {
1518 err = d_vol->vfs->vfs_copyfile(d_vol, sfd, src, dst);
1526 if (!ret_err && newname && (adflags & ADFLAGS_HF)) {
1527 /* set the new name in the resource fork */
1528 ad_copy_header(&add, adp);
1529 ad_setname(&add, newname);
1532 ad_close( adp, adflags );
1534 if (ad_close( &add, adflags ) <0) {
1539 deletefile(d_vol, -1, dst, 0);
1541 else if (stat_result == 0) {
1542 /* set dest modification date to src date */
1545 ut.actime = ut.modtime = st.st_mtime;
1547 /* FIXME netatalk doesn't use resource fork file date
1548 * but maybe we should set its modtime too.
1553 switch ( ret_err ) {
1559 return AFPERR_DFULL;
1561 return AFPERR_NOOBJ;
1563 return AFPERR_ACCESS;
1565 return AFPERR_VLOCK;
1567 return AFPERR_PARAM;
1571 /* -----------------------------------
1572 vol: not NULL delete cnid entry. then we are in curdir and file is a only filename
1573 checkAttrib: 1 check kFPDeleteInhibitBit (deletfile called by afp_delete)
1575 when deletefile is called we don't have lock on it, file is closed (for us)
1576 untrue if called by renamefile
1578 ad_open always try to open file RDWR first and ad_lock takes care of
1579 WRITE lock on read only file.
1582 static int check_attrib(struct adouble *adp)
1584 u_int16_t bshort = 0;
1586 ad_getattr(adp, &bshort);
1588 * Does kFPDeleteInhibitBit (bit 8) set?
1590 if ((bshort & htons(ATTRBIT_NODELETE))) {
1591 return AFPERR_OLOCK;
1593 if ((bshort & htons(ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
1599 * dirfd can be used for unlinkat semantics
1601 int deletefile(const struct vol *vol, int dirfd, char *file, int checkAttrib)
1604 struct adouble *adp = NULL;
1605 int adflags, err = AFP_OK;
1608 LOG(log_debug, logtype_afpd, "deletefile('%s')", file);
1610 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
1612 /* was EACCESS error try to get only metadata */
1613 /* we never want to create a resource fork here, we are going to delete it
1614 * moreover sometimes deletefile is called with a no existent file and
1615 * ad_open would create a 0 byte resource fork
1617 if ( ad_metadataat(dirfd, file, ADFLAGS_OPENFORKS, &ad) == 0 ) {
1618 if ((err = check_attrib(&ad))) {
1619 ad_close_metadata(&ad);
1626 /* try to open both forks at once */
1627 adflags = ADFLAGS_DF;
1628 if ( ad_openat(&ad, dirfd, file, adflags |ADFLAGS_HF|ADFLAGS_NOHF, O_RDONLY) < 0 ) {
1633 case EACCES: /* maybe it's a file with no write mode for us */
1634 break; /* was return AFPERR_ACCESS;*/
1647 if ( adp && ad_reso_fileno( adp ) != -1 ) { /* there's a resource fork */
1648 adflags |= ADFLAGS_HF;
1649 /* FIXME we have a pb here because we want to know if a file is open
1650 * there's a 'priority inversion' if you can't open the ressource fork RW
1651 * you can delete it if it's open because you can't get a write lock.
1653 * ADLOCK_FILELOCK means the whole ressource fork, not only after the
1656 * FIXME it doesn't work for RFORK open read only and fork open without deny mode
1658 if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
1664 if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
1666 } else if (!(err = vol->vfs->vfs_deletefile(vol, dirfd, file)) && !(err = netatalk_unlinkat(dirfd, file )) ) {
1668 if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file)))) {
1669 cnid_delete(vol->v_cdb, id);
1675 ad_close_metadata(&ad);
1678 ad_close( &ad, adflags ); /* ad_close removes locks if any */
1683 /* ------------------------------------ */
1684 /* return a file id */
1685 int afp_createid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1694 struct path *s_path;
1700 memcpy(&vid, ibuf, sizeof(vid));
1701 ibuf += sizeof(vid);
1703 if (NULL == ( vol = getvolbyvid( vid )) ) {
1704 return( AFPERR_PARAM);
1707 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1711 if (vol->v_flags & AFPVOL_RO)
1712 return AFPERR_VLOCK;
1714 memcpy(&did, ibuf, sizeof( did ));
1715 ibuf += sizeof(did);
1717 if (NULL == ( dir = dirlookup( vol, did )) ) {
1718 return afp_errno; /* was AFPERR_PARAM */
1721 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
1722 return get_afp_errno(AFPERR_NOOBJ); /* was AFPERR_PARAM */
1725 if ( path_isadir(s_path) ) {
1726 return( AFPERR_BADTYPE );
1729 upath = s_path->u_name;
1730 switch (s_path->st_errno) {
1732 break; /* success */
1735 return AFPERR_ACCESS;
1737 return AFPERR_NOOBJ;
1739 return AFPERR_PARAM;
1742 if ((id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath)))) {
1743 memcpy(rbuf, &id, sizeof(id));
1744 *rbuflen = sizeof(id);
1745 return AFPERR_EXISTID;
1748 if ((id = get_id(vol, NULL, st, did, upath, len)) != CNID_INVALID) {
1749 memcpy(rbuf, &id, sizeof(id));
1750 *rbuflen = sizeof(id);
1757 /* ------------------------------- */
1763 static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
1766 struct reenum *param = data;
1767 struct vol *vol = param->vol;
1768 cnid_t did = param->did;
1771 if ( lstat(de->d_name, &path.st) < 0 )
1774 /* update or add to cnid */
1775 aint = cnid_add(vol->v_cdb, &path.st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */
1780 /* --------------------
1781 * Ok the db is out of synch with the dir.
1782 * but if it's a deleted file we don't want to do it again and again.
1785 reenumerate_id(struct vol *vol, char *name, struct dir *dir)
1791 if (vol->v_cdb == NULL) {
1795 /* FIXME use of_statdir ? */
1796 if (lstat(name, &st)) {
1800 if (dirreenumerate(dir, &st)) {
1801 /* we already did it once and the dir haven't been modified */
1806 data.did = dir->d_did;
1807 if ((ret = for_each_dirent(vol, name, reenumerate_loop, (void *)&data)) >= 0) {
1808 setdiroffcnt(curdir, &st, ret);
1809 dir->d_flags |= DIRF_CNID;
1815 /* ------------------------------
1816 resolve a file id */
1817 int afp_resolveid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1826 u_int16_t vid, bitmap;
1828 static char buffer[12 + MAXPATHLEN + 1];
1829 int len = 12 + MAXPATHLEN + 1;
1834 memcpy(&vid, ibuf, sizeof(vid));
1835 ibuf += sizeof(vid);
1837 if (NULL == ( vol = getvolbyvid( vid )) ) {
1838 return( AFPERR_PARAM);
1841 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1845 memcpy(&id, ibuf, sizeof( id ));
1850 /* some MacOS versions after a catsearch do a *lot* of afp_resolveid with 0 */
1854 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1855 return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
1858 if (NULL == ( dir = dirlookup( vol, id )) ) {
1859 return AFPERR_NOID; /* idem AFPERR_PARAM */
1861 if (movecwd(vol, dir) < 0) {
1865 return AFPERR_ACCESS;
1869 return AFPERR_PARAM;
1873 memset(&path, 0, sizeof(path));
1874 path.u_name = upath;
1875 if ( of_stat(&path) < 0 ) {
1877 /* with nfs and our working directory is deleted */
1878 if (errno == ESTALE) {
1882 if ( errno == ENOENT && !retry) {
1883 /* cnid db is out of sync, reenumerate the directory and update ids */
1884 reenumerate_id(vol, ".", dir);
1892 return AFPERR_ACCESS;
1896 return AFPERR_PARAM;
1900 /* directories are bad */
1901 if (S_ISDIR(path.st.st_mode)) {
1902 /* OS9 and OSX don't return the same error code */
1903 return (afp_version >=30)?AFPERR_NOID:AFPERR_BADTYPE;
1906 memcpy(&bitmap, ibuf, sizeof(bitmap));
1907 bitmap = ntohs( bitmap );
1908 if (NULL == (path.m_name = utompath(vol, upath, cnid, utf8_encoding()))) {
1912 if (AFP_OK != (err = getfilparams(vol, bitmap, &path , curdir,
1913 rbuf + sizeof(bitmap), &buflen))) {
1916 *rbuflen = buflen + sizeof(bitmap);
1917 memcpy(rbuf, ibuf, sizeof(bitmap));
1922 /* ------------------------------ */
1923 int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1933 static char buffer[12 + MAXPATHLEN + 1];
1934 int len = 12 + MAXPATHLEN + 1;
1939 memcpy(&vid, ibuf, sizeof(vid));
1940 ibuf += sizeof(vid);
1942 if (NULL == ( vol = getvolbyvid( vid )) ) {
1943 return( AFPERR_PARAM);
1946 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1950 if (vol->v_flags & AFPVOL_RO)
1951 return AFPERR_VLOCK;
1953 memcpy(&id, ibuf, sizeof( id ));
1957 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1961 if (NULL == ( dir = dirlookup( vol, id )) ) {
1962 if (afp_errno == AFPERR_NOOBJ) {
1966 return( AFPERR_PARAM );
1970 if ((movecwd(vol, dir) < 0) || (lstat(upath, &st) < 0)) {
1974 return AFPERR_ACCESS;
1979 /* still try to delete the id */
1983 return AFPERR_PARAM;
1986 else if (S_ISDIR(st.st_mode)) /* directories are bad */
1987 return AFPERR_BADTYPE;
1990 if (cnid_delete(vol->v_cdb, fileid)) {
1993 return AFPERR_VLOCK;
1996 return AFPERR_ACCESS;
1998 return AFPERR_PARAM;
2005 /* ------------------------------ */
2006 static struct adouble *find_adouble(struct path *path, struct ofork **of, struct adouble *adp)
2010 if (path->st_errno) {
2011 switch (path->st_errno) {
2013 afp_errno = AFPERR_NOID;
2017 afp_errno = AFPERR_ACCESS;
2020 afp_errno = AFPERR_PARAM;
2025 /* we use file_access both for legacy Mac perm and
2026 * for unix privilege, rename will take care of folder perms
2028 if (file_access(path, OPENACC_WR ) < 0) {
2029 afp_errno = AFPERR_ACCESS;
2033 if ((*of = of_findname(path))) {
2034 /* reuse struct adouble so it won't break locks */
2038 ret = ad_open(adp, path->u_name, ADFLAGS_HF, O_RDONLY);
2040 if ( !ret && ad_reso_fileno(adp) != -1 && !(adp->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
2042 * The user must have the Read & Write privilege for both files in order to use this command.
2044 ad_close(adp, ADFLAGS_HF);
2045 afp_errno = AFPERR_ACCESS;
2052 #define APPLETEMP ".AppleTempXXXXXX"
2054 int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
2056 struct stat srcst, destst;
2058 struct dir *dir, *sdir;
2059 char *spath, temp[17], *p;
2060 char *supath, *upath;
2065 struct adouble *adsp = NULL;
2066 struct adouble *addp = NULL;
2067 struct ofork *s_of = NULL;
2068 struct ofork *d_of = NULL;
2081 memcpy(&vid, ibuf, sizeof(vid));
2082 ibuf += sizeof(vid);
2084 if (NULL == ( vol = getvolbyvid( vid )) ) {
2085 return( AFPERR_PARAM);
2088 if ((vol->v_flags & AFPVOL_RO))
2089 return AFPERR_VLOCK;
2091 /* source and destination dids */
2092 memcpy(&sid, ibuf, sizeof(sid));
2093 ibuf += sizeof(sid);
2094 memcpy(&did, ibuf, sizeof(did));
2095 ibuf += sizeof(did);
2098 if (NULL == (dir = dirlookup( vol, sid )) ) {
2099 return afp_errno; /* was AFPERR_PARAM */
2102 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2103 return get_afp_errno(AFPERR_NOOBJ);
2106 if ( path_isadir(path) ) {
2107 return AFPERR_BADTYPE; /* it's a dir */
2110 /* save some stuff */
2113 spath = obj->oldtmp;
2114 supath = obj->newtmp;
2115 strcpy(spath, path->m_name);
2116 strcpy(supath, path->u_name); /* this is for the cnid changing */
2117 p = absupath( vol, sdir, supath);
2119 /* pathname too long */
2120 return AFPERR_PARAM ;
2123 ad_init(&ads, vol->v_adouble, vol->v_ad_options);
2124 if (!(adsp = find_adouble( path, &s_of, &ads))) {
2128 /* ***** from here we may have resource fork open **** */
2130 /* look for the source cnid. if it doesn't exist, don't worry about
2132 sid = cnid_lookup(vol->v_cdb, &srcst, sdir->d_did, supath,slen = strlen(supath));
2134 if (NULL == ( dir = dirlookup( vol, did )) ) {
2135 err = afp_errno; /* was AFPERR_PARAM */
2136 goto err_exchangefile;
2139 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2140 err = get_afp_errno(AFPERR_NOOBJ);
2141 goto err_exchangefile;
2144 if ( path_isadir(path) ) {
2145 err = AFPERR_BADTYPE;
2146 goto err_exchangefile;
2149 /* FPExchangeFiles is the only call that can return the SameObj
2151 if ((curdir == sdir) && strcmp(spath, path->m_name) == 0) {
2152 err = AFPERR_SAMEOBJ;
2153 goto err_exchangefile;
2156 ad_init(&add, vol->v_adouble, vol->v_ad_options);
2157 if (!(addp = find_adouble( path, &d_of, &add))) {
2159 goto err_exchangefile;
2163 /* they are not on the same device and at least one is open
2164 * FIXME broken for for crossdev and adouble v2
2167 crossdev = (srcst.st_dev != destst.st_dev);
2168 if (/* (d_of || s_of) && */ crossdev) {
2170 goto err_exchangefile;
2173 /* look for destination id. */
2174 upath = path->u_name;
2175 did = cnid_lookup(vol->v_cdb, &destst, curdir->d_did, upath, dlen = strlen(upath));
2177 /* construct a temp name.
2178 * NOTE: the temp file will be in the dest file's directory. it
2179 * will also be inaccessible from AFP. */
2180 memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
2181 if (!mktemp(temp)) {
2183 goto err_exchangefile;
2187 /* FIXME we need to close fork for copy, both s_of and d_of are null */
2188 ad_close(adsp, ADFLAGS_HF);
2189 ad_close(addp, ADFLAGS_HF);
2192 /* now, quickly rename the file. we error if we can't. */
2193 if ((err = renamefile(vol, -1, p, temp, temp, adsp)) != AFP_OK)
2194 goto err_exchangefile;
2195 of_rename(vol, s_of, sdir, spath, curdir, temp);
2197 /* rename destination to source */
2198 if ((err = renamefile(vol, -1, upath, p, spath, addp)) != AFP_OK)
2199 goto err_src_to_tmp;
2200 of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
2202 /* rename temp to destination */
2203 if ((err = renamefile(vol, -1, temp, upath, path->m_name, adsp)) != AFP_OK)
2204 goto err_dest_to_src;
2205 of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
2207 /* id's need switching. src -> dest and dest -> src.
2208 * we need to re-stat() if it was a cross device copy.
2211 cnid_delete(vol->v_cdb, sid);
2213 cnid_delete(vol->v_cdb, did);
2215 if ((did && ( (crossdev && lstat( upath, &srcst) < 0) ||
2216 cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
2218 (sid && ( (crossdev && lstat(p, &destst) < 0) ||
2219 cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
2224 err = AFPERR_ACCESS;
2229 goto err_temp_to_dest;
2232 /* here we need to reopen if crossdev */
2233 if (sid && ad_setid(addp, destst.st_dev, destst.st_ino, sid, sdir->d_did, vol->v_stamp))
2238 if (did && ad_setid(adsp, srcst.st_dev, srcst.st_ino, did, curdir->d_did, vol->v_stamp))
2243 /* change perms, src gets dest perm and vice versa */
2248 LOG(log_error, logtype_afpd, "seteuid failed %s", strerror(errno));
2249 err = AFP_OK; /* ignore error */
2250 goto err_temp_to_dest;
2254 * we need to exchange ACL entries as well
2256 /* exchange_acls(vol, p, upath); */
2261 path->m_name = NULL;
2262 path->u_name = upath;
2264 setfilunixmode(vol, path, destst.st_mode);
2265 setfilowner(vol, destst.st_uid, destst.st_gid, path);
2272 setfilunixmode(vol, path, srcst.st_mode);
2273 setfilowner(vol, srcst.st_uid, srcst.st_gid, path);
2275 if ( setegid(gid) < 0 || seteuid(uid) < 0) {
2276 LOG(log_error, logtype_afpd, "can't seteuid back %s", strerror(errno));
2281 goto err_exchangefile;
2283 /* all this stuff is so that we can unwind a failed operation
2286 /* rename dest to temp */
2287 renamefile(vol, -1, upath, temp, temp, adsp);
2288 of_rename(vol, s_of, curdir, upath, curdir, temp);
2291 /* rename source back to dest */
2292 renamefile(vol, -1, p, upath, path->m_name, addp);
2293 of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
2296 /* rename temp back to source */
2297 renamefile(vol, -1, temp, p, spath, adsp);
2298 of_rename(vol, s_of, curdir, temp, sdir, spath);
2301 if ( !s_of && adsp && ad_meta_fileno(adsp) != -1 ) { /* META */
2302 ad_close(adsp, ADFLAGS_HF);
2304 if ( !d_of && addp && ad_meta_fileno(addp) != -1 ) {/* META */
2305 ad_close(addp, ADFLAGS_HF);
2309 if ((cached = dircache_search_by_did(vol, sid)) != NULL)
2310 (void)dir_remove(vol, cached);
2311 if ((cached = dircache_search_by_did(vol, did)) != NULL)
2312 (void)dir_remove(vol, cached);