2 * $Id: file.c,v 1.141 2010/03/12 15:16:49 franklahm Exp $
4 * Copyright (c) 1990,1993 Regents of The University of Michigan.
5 * All Rights Reserved. See COPYRIGHT.
10 #endif /* HAVE_CONFIG_H */
18 #else /* STDC_HEADERS */
22 #endif /* HAVE_STRCHR */
23 char *strchr (), *strrchr ();
26 #define memcpy(d,s,n) bcopy ((s), (d), (n))
27 #define memmove(d,s,n) bcopy ((s), (d), (n))
28 #endif /* ! HAVE_MEMCPY */
29 #endif /* STDC_HEADERS */
33 #include <sys/param.h>
35 #include <atalk/adouble.h>
36 #include <atalk/vfs.h>
37 #include <atalk/logger.h>
38 #include <atalk/afp.h>
39 #include <atalk/util.h>
40 #include <atalk/cnid.h>
41 #include <atalk/unix.h>
43 #include "directory.h"
52 /* the format for the finderinfo fields (from IM: Toolbox Essentials):
53 * field bytes subfield bytes
56 * ioFlFndrInfo 16 -> type 4 type field
57 * creator 4 creator field
58 * flags 2 finder flags:
60 * location 4 location in window
61 * folder 2 window that contains file
63 * ioFlXFndrInfo 16 -> iconID 2 icon id
65 * script 1 script system
67 * commentID 2 comment id
68 * putawayID 4 home directory id
71 const u_char ufinderi[ADEDLEN_FINDERI] = {
72 0, 0, 0, 0, 0, 0, 0, 0,
73 1, 0, 0, 0, 0, 0, 0, 0,
74 0, 0, 0, 0, 0, 0, 0, 0,
75 0, 0, 0, 0, 0, 0, 0, 0
78 static const u_char old_ufinderi[] = {
79 'T', 'E', 'X', 'T', 'U', 'N', 'I', 'X'
82 /* ----------------------
84 static int default_type(void *finder)
86 if (!memcmp(finder, ufinderi, 8) || !memcmp(finder, old_ufinderi, 8))
91 /* FIXME path : unix or mac name ? (for now it's unix name ) */
92 void *get_finderinfo(const struct vol *vol, const char *upath, struct adouble *adp, void *data, int islink)
95 void *ad_finder = NULL;
99 ad_finder = ad_entry(adp, ADEID_FINDERI);
102 memcpy(data, ad_finder, ADEDLEN_FINDERI);
104 if (default_type(ad_finder))
108 memcpy(data, ufinderi, ADEDLEN_FINDERI);
110 if (vol_inv_dots(vol) && *upath == '.') { /* make it invisible */
113 ashort = htons(FINDERINFO_INVISIBLE);
114 memcpy((char *)data + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
120 memcpy(&linkflag, (char *)data + FINDERINFO_FRFLAGOFF, 2);
121 linkflag |= htons(FINDERINFO_ISALIAS);
122 memcpy((char *)data + FINDERINFO_FRFLAGOFF, &linkflag, 2);
123 memcpy((char *)data + FINDERINFO_FRTYPEOFF,"slnk",4);
124 memcpy((char *)data + FINDERINFO_FRCREATOFF,"rhap",4);
128 /** Only enter if no appledouble information and no finder information found. */
129 if (chk_ext && (em = getextmap( upath ))) {
130 memcpy(data, em->em_type, sizeof( em->em_type ));
131 memcpy((char *)data + 4, em->em_creator, sizeof(em->em_creator));
136 /* ---------------------
138 char *set_name(const struct vol *vol, char *data, cnid_t pid, char *name, cnid_t id, u_int32_t utf8)
143 aint = strlen( name );
147 if (utf8_encoding()) {
148 /* but name is an utf8 mac name */
151 /* global static variable... */
153 if (!(u = mtoupath(vol, name, pid, 1)) || !(m = utompath(vol, u, id, 0))) {
162 if (aint > MACFILELEN)
169 if (aint > 255) /* FIXME safeguard, anyway if no ascii char it's game over*/
172 utf8 = vol->v_kTextEncoding;
173 memcpy(data, &utf8, sizeof(utf8));
174 data += sizeof(utf8);
177 memcpy(data, &temp, sizeof(temp));
178 data += sizeof(temp);
181 memcpy( data, src, aint );
191 * FIXME: PDINFO is UTF8 and doesn't need adp
193 #define PARAM_NEED_ADP(b) ((b) & ((1 << FILPBIT_ATTR) |\
194 (1 << FILPBIT_CDATE) |\
195 (1 << FILPBIT_MDATE) |\
196 (1 << FILPBIT_BDATE) |\
197 (1 << FILPBIT_FINFO) |\
198 (1 << FILPBIT_RFLEN) |\
199 (1 << FILPBIT_EXTRFLEN) |\
200 (1 << FILPBIT_PDINFO) |\
201 (1 << FILPBIT_FNUM) |\
202 (1 << FILPBIT_UNIXPR)))
204 /* -------------------------- */
205 uint32_t get_id(struct vol *vol, struct adouble *adp, const struct stat *st,
206 const cnid_t did, char *upath, const int len)
208 static int first = 1; /* mark if this func is called the first time */
210 u_int32_t dbcnid = CNID_INVALID;
213 if (vol->v_cdb != NULL) {
214 /* prime aint with what we think is the cnid, set did to zero for
215 catching moved files */
216 adcnid = ad_getid(adp, st->st_dev, st->st_ino, 0, vol->v_stamp);
218 dbcnid = cnid_add(vol->v_cdb, st, did, upath, len, adcnid);
219 /* Throw errors if cnid_add fails. */
220 if (dbcnid == CNID_INVALID) {
222 case CNID_ERR_CLOSE: /* the db is closed */
225 LOG(log_error, logtype_afpd, "get_id: Incorrect parameters passed to cnid_add");
226 afp_errno = AFPERR_PARAM;
229 afp_errno = AFPERR_PARAM;
232 /* Close CNID backend if "dbd" and switch to temp in-memory "tdb" */
233 /* we have to do it here for "dbd" because it uses "lazy opening" */
234 /* In order to not end in a loop somehow with goto restart below */
236 if (first && (strcmp(vol->v_cnidscheme, "dbd") == 0)) {
237 cnid_close(vol->v_cdb);
238 free(vol->v_cnidscheme);
239 vol->v_cnidscheme = strdup("tdb");
241 int flags = CNID_FLAG_MEMORY;
242 if ((vol->v_flags & AFPVOL_NODEV)) {
243 flags |= CNID_FLAG_NODEV;
245 LOG(log_error, logtype_afpd, "Reopen volume %s using in memory temporary CNID DB.",
247 vol->v_cdb = cnid_open(vol->v_path, vol->v_umask, "tdb", flags, NULL, NULL);
249 /* deactivate cnid caching/storing in AppleDouble files and set ro mode*/
250 vol->v_flags &= ~AFPVOL_CACHE;
251 vol->v_flags |= AFPVOL_RO;
253 /* kill ourself with SIGUSR2 aka msg pending */
254 setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB instead."
255 "Check server messages for details. Switching to read-only mode.");
256 kill(getpid(), SIGUSR2);
258 goto restart; /* not try again with the temp CNID db */
261 setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB failed too!"
262 "Check server messages for details, can't recover from this state!");
266 afp_errno = AFPERR_MISC;
270 else if (adp && (adcnid != dbcnid)) {
271 /* Update the ressource fork. For a folder adp is always null */
272 LOG(log_debug, logtype_afpd, "get_id: calling ad_setid. adcnid: %u, dbcnid: %u", htonl(adcnid), htonl(dbcnid));
273 if (ad_setid(adp, st->st_dev, st->st_ino, dbcnid, did, vol->v_stamp)) {
284 /* -------------------------- */
285 int getmetadata(struct vol *vol,
287 struct path *path, struct dir *dir,
288 char *buf, size_t *buflen, struct adouble *adp)
290 char *data, *l_nameoff = NULL, *upath;
291 char *utf_nameoff = NULL;
296 u_char achar, fdType[4];
302 upath = path->u_name;
307 if ( ((bitmap & ( (1 << FILPBIT_FINFO)|(1 << FILPBIT_LNAME)|(1 <<FILPBIT_PDINFO) ) ) && !path->m_name)
308 || (bitmap & ( (1 << FILPBIT_LNAME) ) && utf8_encoding()) /* FIXME should be m_name utf8 filename */
309 || (bitmap & (1 << FILPBIT_FNUM))) {
311 id = get_id(vol, adp, st, dir->d_did, upath, strlen(upath));
314 if (id == CNID_INVALID)
317 path->m_name = utompath(vol, upath, id, utf8_encoding());
320 while ( bitmap != 0 ) {
321 while (( bitmap & 1 ) == 0 ) {
329 ad_getattr(adp, &ashort);
330 } else if (vol_inv_dots(vol) && *upath == '.') {
331 ashort = htons(ATTRBIT_INVISIBLE);
335 /* FIXME do we want a visual clue if the file is read only
338 accessmode( ".", &ma, dir , NULL);
339 if ((ma.ma_user & AR_UWRITE)) {
340 accessmode( upath, &ma, dir , st);
341 if (!(ma.ma_user & AR_UWRITE)) {
342 ashort |= htons(ATTRBIT_NOWRITE);
346 memcpy(data, &ashort, sizeof( ashort ));
347 data += sizeof( ashort );
348 LOG(log_debug, logtype_afpd, "metadata('%s'): AFP Attributes: %04x",
349 path->u_name, ntohs(ashort));
353 memcpy(data, &dir->d_did, sizeof( u_int32_t ));
354 data += sizeof( u_int32_t );
355 LOG(log_debug, logtype_afpd, "metadata('%s'): Parent DID: %u",
356 path->u_name, ntohl(dir->d_did));
360 if (!adp || (ad_getdate(adp, AD_DATE_CREATE, &aint) < 0))
361 aint = AD_DATE_FROM_UNIX(st->st_mtime);
362 memcpy(data, &aint, sizeof( aint ));
363 data += sizeof( aint );
367 if ( adp && (ad_getdate(adp, AD_DATE_MODIFY, &aint) == 0)) {
368 if ((st->st_mtime > AD_DATE_TO_UNIX(aint))) {
369 aint = AD_DATE_FROM_UNIX(st->st_mtime);
372 aint = AD_DATE_FROM_UNIX(st->st_mtime);
374 memcpy(data, &aint, sizeof( int ));
375 data += sizeof( int );
379 if (!adp || (ad_getdate(adp, AD_DATE_BACKUP, &aint) < 0))
380 aint = AD_DATE_START;
381 memcpy(data, &aint, sizeof( int ));
382 data += sizeof( int );
386 get_finderinfo(vol, upath, adp, (char *)data,S_ISLNK(st->st_mode));
387 data += ADEDLEN_FINDERI;
392 data += sizeof( u_int16_t );
396 memset(data, 0, sizeof(u_int16_t));
397 data += sizeof( u_int16_t );
401 memcpy(data, &id, sizeof( id ));
402 data += sizeof( id );
403 LOG(log_debug, logtype_afpd, "metadata('%s'): CNID: %u",
404 path->u_name, ntohl(id));
408 if (st->st_size > 0xffffffff)
411 aint = htonl( st->st_size );
412 memcpy(data, &aint, sizeof( aint ));
413 data += sizeof( aint );
418 if (adp->ad_rlen > 0xffffffff)
421 aint = htonl( adp->ad_rlen);
425 memcpy(data, &aint, sizeof( aint ));
426 data += sizeof( aint );
429 /* Current client needs ProDOS info block for this file.
430 Use simple heuristic and let the Mac "type" string tell
431 us what the PD file code should be. Everything gets a
432 subtype of 0x0000 unless the original value was hashed
433 to "pXYZ" when we created it. See IA, Ver 2.
434 <shirsch@adelphia.net> */
435 case FILPBIT_PDINFO :
436 if (afp_version >= 30) { /* UTF8 name */
437 utf8 = kTextEncodingUTF8;
439 data += sizeof( u_int16_t );
441 memcpy(data, &aint, sizeof( aint ));
442 data += sizeof( aint );
446 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
448 if ( memcmp( fdType, "TEXT", 4 ) == 0 ) {
452 else if ( memcmp( fdType, "PSYS", 4 ) == 0 ) {
456 else if ( memcmp( fdType, "PS16", 4 ) == 0 ) {
460 else if ( memcmp( fdType, "BINA", 4 ) == 0 ) {
464 else if ( fdType[0] == 'p' ) {
466 ashort = (fdType[2] * 256) + fdType[3];
480 memcpy(data, &ashort, sizeof( ashort ));
481 data += sizeof( ashort );
482 memset(data, 0, sizeof( ashort ));
483 data += sizeof( ashort );
486 case FILPBIT_EXTDFLEN:
487 aint = htonl(st->st_size >> 32);
488 memcpy(data, &aint, sizeof( aint ));
489 data += sizeof( aint );
490 aint = htonl(st->st_size);
491 memcpy(data, &aint, sizeof( aint ));
492 data += sizeof( aint );
494 case FILPBIT_EXTRFLEN:
497 aint = htonl(adp->ad_rlen >> 32);
498 memcpy(data, &aint, sizeof( aint ));
499 data += sizeof( aint );
501 aint = htonl(adp->ad_rlen);
502 memcpy(data, &aint, sizeof( aint ));
503 data += sizeof( aint );
505 case FILPBIT_UNIXPR :
506 /* accessmode may change st_mode with ACLs */
507 accessmode( upath, &ma, dir , st);
509 aint = htonl(st->st_uid);
510 memcpy( data, &aint, sizeof( aint ));
511 data += sizeof( aint );
512 aint = htonl(st->st_gid);
513 memcpy( data, &aint, sizeof( aint ));
514 data += sizeof( aint );
517 type == slnk indicates an OSX style symlink,
518 we have to add S_IFLNK to the mode, otherwise
519 10.3 clients freak out. */
523 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
524 if ( memcmp( fdType, "slnk", 4 ) == 0 ) {
530 memcpy( data, &aint, sizeof( aint ));
531 data += sizeof( aint );
533 *data++ = ma.ma_user;
534 *data++ = ma.ma_world;
535 *data++ = ma.ma_group;
536 *data++ = ma.ma_owner;
540 return( AFPERR_BITMAP );
546 ashort = htons( data - buf );
547 memcpy(l_nameoff, &ashort, sizeof( ashort ));
548 data = set_name(vol, data, dir->d_did, path->m_name, id, 0);
551 ashort = htons( data - buf );
552 memcpy(utf_nameoff, &ashort, sizeof( ashort ));
553 data = set_name(vol, data, dir->d_did, path->m_name, id, utf8);
555 *buflen = data - buf;
559 /* ----------------------- */
560 int getfilparams(struct vol *vol,
562 struct path *path, struct dir *dir,
563 char *buf, size_t *buflen )
565 struct adouble ad, *adp;
569 opened = PARAM_NEED_ADP(bitmap);
574 int flags = (bitmap & (1 << FILPBIT_ATTR))?ADFLAGS_OPENFORKS:0;
576 adp = of_ad(vol, path, &ad);
577 upath = path->u_name;
579 if ( ad_metadata( upath, flags|ADFLAGS_CREATE, adp) < 0 ) {
582 LOG(log_error, logtype_afpd, "getfilparams(%s): %s: check resource fork permission?",
583 upath, strerror(errno));
584 return AFPERR_ACCESS;
586 LOG(log_error, logtype_afpd, "getfilparams(%s): bad resource fork", upath);
595 rc = getmetadata(vol, bitmap, path, dir, buf, buflen, adp);
597 ad_close_metadata( adp);
603 /* ----------------------------- */
604 int afp_createfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
606 struct adouble ad, *adp;
609 struct ofork *of = NULL;
611 int creatf, did, openf, retvalue = AFP_OK;
617 creatf = (unsigned char) *ibuf++;
619 memcpy(&vid, ibuf, sizeof( vid ));
620 ibuf += sizeof( vid );
622 if (NULL == ( vol = getvolbyvid( vid )) ) {
623 return( AFPERR_PARAM );
626 if (vol->v_flags & AFPVOL_RO)
629 memcpy(&did, ibuf, sizeof( did));
630 ibuf += sizeof( did );
632 if (NULL == ( dir = dirlookup( vol, did )) ) {
636 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
637 return get_afp_errno(AFPERR_PARAM);
640 if ( *s_path->m_name == '\0' ) {
641 return( AFPERR_BADTYPE );
644 upath = s_path->u_name;
646 /* if upath is deleted we already in trouble anyway */
647 if ((of = of_findname(s_path))) {
650 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
654 /* on a hard create, fail if file exists and is open */
657 openf = O_RDWR|O_CREAT|O_TRUNC;
659 /* on a soft create, if the file is open then ad_open won't fail
660 because open syscall is not called
665 openf = O_RDWR|O_CREAT|O_EXCL;
668 if ( ad_open( upath, ADFLAGS_DF|ADFLAGS_HF|ADFLAGS_NOHF|ADFLAGS_CREATE,
669 openf, 0666, adp) < 0 ) {
673 case ENOENT : /* we were already in 'did folder' so chdir() didn't fail */
674 return ( AFPERR_NOOBJ );
676 return( AFPERR_EXIST );
678 return( AFPERR_ACCESS );
681 return( AFPERR_DFULL );
683 return( AFPERR_PARAM );
686 if ( ad_reso_fileno( adp ) == -1 ) { /* Hard META / HF */
687 /* on noadouble volumes, just creating the data fork is ok */
688 if (vol_noadouble(vol)) {
689 ad_close( adp, ADFLAGS_DF );
690 goto createfile_done;
692 /* FIXME with hard create on an existing file, we already
693 * corrupted the data file.
695 netatalk_unlink( upath );
696 ad_close( adp, ADFLAGS_DF );
697 return AFPERR_ACCESS;
700 path = s_path->m_name;
701 ad_setname(adp, path);
703 ad_close( adp, ADFLAGS_DF|ADFLAGS_HF );
709 if (vol->v_flags & AFPVOL_DROPBOX) {
710 retvalue = matchfile2dirperms(upath, vol, did);
712 #endif /* DROPKLUDGE */
714 setvoltime(obj, vol );
719 int afp_setfilparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
725 u_int16_t vid, bitmap;
730 memcpy(&vid, ibuf, sizeof( vid ));
731 ibuf += sizeof( vid );
732 if (NULL == ( vol = getvolbyvid( vid )) ) {
733 return( AFPERR_PARAM );
736 if (vol->v_flags & AFPVOL_RO)
739 memcpy(&did, ibuf, sizeof( did ));
740 ibuf += sizeof( did );
741 if (NULL == ( dir = dirlookup( vol, did )) ) {
742 return afp_errno; /* was AFPERR_NOOBJ */
745 memcpy(&bitmap, ibuf, sizeof( bitmap ));
746 bitmap = ntohs( bitmap );
747 ibuf += sizeof( bitmap );
749 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
750 return get_afp_errno(AFPERR_PARAM);
753 if (path_isadir(s_path)) {
754 return( AFPERR_BADTYPE ); /* it's a directory */
757 if ( s_path->st_errno != 0 ) {
758 return( AFPERR_NOOBJ );
761 if ((u_long)ibuf & 1 ) {
765 if (AFP_OK == ( rc = setfilparams(vol, s_path, bitmap, ibuf )) ) {
766 setvoltime(obj, vol );
773 * cf AFP3.0.pdf page 252 for change_mdate and change_parent_mdate logic
776 extern struct path Cur_Path;
778 int setfilparams(struct vol *vol,
779 struct path *path, u_int16_t f_bitmap, char *buf )
781 struct adouble ad, *adp;
783 int bit, isad = 1, err = AFP_OK;
785 u_char achar, *fdType, xyy[4]; /* uninitialized, OK 310105 */
786 u_int16_t ashort, bshort, oshort;
789 u_int16_t upriv_bit = 0;
793 int change_mdate = 0;
794 int change_parent_mdate = 0;
799 u_int16_t bitmap = f_bitmap;
800 u_int32_t cdate,bdate;
801 u_char finder_buf[32];
804 LOG(log_debug9, logtype_afpd, "begin setfilparams:");
807 adp = of_ad(vol, path, &ad);
808 upath = path->u_name;
810 if (!vol_unix_priv(vol) && check_access(upath, OPENACC_WR ) < 0) {
811 return AFPERR_ACCESS;
814 /* with unix priv maybe we have to change adouble file priv first */
816 while ( bitmap != 0 ) {
817 while (( bitmap & 1 ) == 0 ) {
824 memcpy(&ashort, buf, sizeof( ashort ));
825 buf += sizeof( ashort );
829 memcpy(&cdate, buf, sizeof(cdate));
830 buf += sizeof( cdate );
833 memcpy(&newdate, buf, sizeof( newdate ));
834 buf += sizeof( newdate );
838 memcpy(&bdate, buf, sizeof( bdate));
839 buf += sizeof( bdate );
843 memcpy(finder_buf, buf, 32 );
844 if (memcmp(buf,"slnkrhap",8)==0 && !S_ISLNK(path->st.st_mode)){
849 char buf[PATH_MAX+1];
850 if ((fp=open(path->u_name,O_RDONLY))>=0){
851 if ((len=read(fp,buf,PATH_MAX+1))){
852 if (unlink(path->u_name)==0){
854 erc = symlink(buf, path->u_name);
863 goto setfilparam_done;
868 case FILPBIT_UNIXPR :
869 if (!vol_unix_priv(vol)) {
870 /* this volume doesn't use unix priv */
876 change_parent_mdate = 1;
878 memcpy( &aint, buf, sizeof( aint ));
879 f_uid = ntohl (aint);
880 buf += sizeof( aint );
881 memcpy( &aint, buf, sizeof( aint ));
882 f_gid = ntohl (aint);
883 buf += sizeof( aint );
884 setfilowner(vol, f_uid, f_gid, path);
886 memcpy( &upriv, buf, sizeof( upriv ));
887 buf += sizeof( upriv );
888 upriv = ntohl (upriv);
889 if ((upriv & S_IWUSR)) {
890 setfilunixmode(vol, path, upriv);
897 case FILPBIT_PDINFO :
898 if (afp_version < 30) { /* else it's UTF8 name */
901 /* Keep special case to support crlf translations */
902 if ((unsigned int) achar == 0x04) {
903 fdType = (u_char *)"TEXT";
906 xyy[0] = ( u_char ) 'p';
917 /* break while loop */
926 /* second try with adouble open
928 if ( ad_open_metadata( upath, 0, O_CREAT, adp) < 0) {
929 LOG(log_debug, logtype_afpd, "setfilparams: ad_open_metadata error");
931 * For some things, we don't need an adouble header:
932 * - change of modification date
933 * - UNIX privs (Bug-ID #2863424)
935 if (!vol_noadouble(vol) && (f_bitmap & ~(1<<FILPBIT_MDATE | 1<<FILPBIT_UNIXPR))) {
936 LOG(log_debug, logtype_afpd, "setfilparams: need adouble access");
937 return AFPERR_ACCESS;
939 LOG(log_debug, logtype_afpd, "setfilparams: no adouble perms, but only FILPBIT_MDATE and/or FILPBIT_UNIXPR");
941 } else if ((ad_get_HF_flags( adp ) & O_CREAT) ) {
942 ad_setname(adp, path->m_name);
947 while ( bitmap != 0 ) {
948 while (( bitmap & 1 ) == 0 ) {
955 ad_getattr(adp, &bshort);
957 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
958 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
962 if ((bshort & htons(ATTRBIT_INVISIBLE)) != (oshort & htons(ATTRBIT_INVISIBLE)))
963 change_parent_mdate = 1;
964 ad_setattr(adp, bshort);
967 ad_setdate(adp, AD_DATE_CREATE, cdate);
972 ad_setdate(adp, AD_DATE_BACKUP, bdate);
975 if (default_type( ad_entry( adp, ADEID_FINDERI ))
977 ((em = getextmap( path->m_name )) &&
978 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
979 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
980 || ((em = getdefextmap()) &&
981 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
982 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
984 memcpy(finder_buf, ufinderi, 8 );
986 memcpy(ad_entry( adp, ADEID_FINDERI ), finder_buf, 32 );
988 case FILPBIT_UNIXPR :
990 setfilunixmode(vol, path, upriv);
993 case FILPBIT_PDINFO :
994 if (afp_version < 30) { /* else it's UTF8 name */
995 memcpy(ad_entry( adp, ADEID_FINDERI ), fdType, 4 );
996 memcpy(ad_entry( adp, ADEID_FINDERI ) + 4, "pdos", 4 );
1001 err = AFPERR_BITMAP;
1002 goto setfilparam_done;
1009 if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
1010 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1014 ad_setdate(adp, AD_DATE_MODIFY, newdate);
1015 ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
1021 ad_close_metadata( adp);
1025 if (change_parent_mdate && gettimeofday(&tv, NULL) == 0) {
1026 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1027 bitmap = 1<<FILPBIT_MDATE;
1028 setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
1032 LOG(log_debug9, logtype_afpd, "end setfilparams:");
1038 * renamefile and copyfile take the old and new unix pathnames
1039 * and the new mac name.
1041 * sdir_fd source dir fd to which src path is relative (for openat et al semantics)
1042 * passing -1 means this is not used, src path is a full path
1043 * src the source path
1044 * dst the dest filename in current dir
1045 * newname the dest mac name
1046 * adp adouble struct of src file, if open, or & zeroed one
1049 int renamefile(const struct vol *vol, int sdir_fd, char *src, char *dst, char *newname, struct adouble *adp)
1053 if ( unix_rename( sdir_fd, src, -1, dst ) < 0 ) {
1056 return( AFPERR_NOOBJ );
1059 return( AFPERR_ACCESS );
1061 return AFPERR_VLOCK;
1062 case EXDEV : /* Cross device move -- try copy */
1063 /* NOTE: with open file it's an error because after the copy we will
1064 * get two files, it's fixable for our process (eg reopen the new file, get the
1065 * locks, and so on. But it doesn't solve the case with a second process
1067 if (adp->ad_open_forks) {
1068 /* FIXME warning in syslog so admin'd know there's a conflict ?*/
1069 return AFPERR_OLOCK; /* little lie */
1071 if (AFP_OK != ( rc = copyfile(vol, vol, sdir_fd, src, dst, newname, NULL )) ) {
1072 /* on error copyfile delete dest */
1075 return deletefile(vol, sdir_fd, src, 0);
1077 return( AFPERR_PARAM );
1081 if (vol->vfs->vfs_renamefile(vol, sdir_fd, src, dst) < 0 ) {
1085 /* try to undo the data fork rename,
1086 * we know we are on the same device
1089 unix_rename(-1, dst, sdir_fd, src );
1090 /* return the first error */
1093 return AFPERR_NOOBJ;
1096 return AFPERR_ACCESS ;
1098 return AFPERR_VLOCK;
1100 return AFPERR_PARAM ;
1105 /* don't care if we can't open the newly renamed ressource fork
1107 if (!ad_open( dst, ADFLAGS_HF, O_RDWR, 0666, adp)) {
1108 ad_setname(adp, newname);
1110 ad_close( adp, ADFLAGS_HF );
1117 convert a Mac long name to an utf8 name,
1119 size_t mtoUTF8(const struct vol *vol, const char *src, size_t srclen, char *dest, size_t destlen)
1123 if ((size_t)-1 == (outlen = convert_string ( vol->v_maccharset, CH_UTF8_MAC, src, srclen, dest, destlen)) ) {
1129 /* ---------------- */
1130 int copy_path_name(const struct vol *vol, char *newname, char *ibuf)
1137 if ( type != 2 && !(afp_version >= 30 && type == 3) ) {
1143 if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
1144 if (afp_version >= 30) {
1145 /* convert it to UTF8
1147 if ((plen = mtoUTF8(vol, ibuf, plen, newname, AFPOBJ_TMPSIZ)) == (size_t)-1)
1151 strncpy( newname, ibuf, plen );
1152 newname[ plen ] = '\0';
1154 if (strlen(newname) != plen) {
1155 /* there's \0 in newname, e.g. it's a pathname not
1163 memcpy(&hint, ibuf, sizeof(hint));
1164 ibuf += sizeof(hint);
1166 memcpy(&len16, ibuf, sizeof(len16));
1167 ibuf += sizeof(len16);
1168 plen = ntohs(len16);
1171 if (plen > AFPOBJ_TMPSIZ) {
1174 strncpy( newname, ibuf, plen );
1175 newname[ plen ] = '\0';
1176 if (strlen(newname) != plen) {
1185 /* -----------------------------------
1187 int afp_copyfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1189 struct vol *s_vol, *d_vol;
1191 char *newname, *p, *upath;
1192 struct path *s_path;
1193 u_int32_t sdid, ddid;
1194 int err, retvalue = AFP_OK;
1195 u_int16_t svid, dvid;
1197 struct adouble ad, *adp;
1203 memcpy(&svid, ibuf, sizeof( svid ));
1204 ibuf += sizeof( svid );
1205 if (NULL == ( s_vol = getvolbyvid( svid )) ) {
1206 return( AFPERR_PARAM );
1209 memcpy(&sdid, ibuf, sizeof( sdid ));
1210 ibuf += sizeof( sdid );
1211 if (NULL == ( dir = dirlookup( s_vol, sdid )) ) {
1215 memcpy(&dvid, ibuf, sizeof( dvid ));
1216 ibuf += sizeof( dvid );
1217 memcpy(&ddid, ibuf, sizeof( ddid ));
1218 ibuf += sizeof( ddid );
1220 if (NULL == ( s_path = cname( s_vol, dir, &ibuf )) ) {
1221 return get_afp_errno(AFPERR_PARAM);
1223 if ( path_isadir(s_path) ) {
1224 return( AFPERR_BADTYPE );
1227 /* don't allow copies when the file is open.
1228 * XXX: the spec only calls for read/deny write access.
1229 * however, copyfile doesn't have any of that info,
1230 * and locks need to stay coherent. as a result,
1231 * we just balk if the file is opened already. */
1233 adp = of_ad(s_vol, s_path, &ad);
1235 if (ad_open(s_path->u_name , ADFLAGS_DF |ADFLAGS_HF | ADFLAGS_NOHF, O_RDONLY, 0, adp) < 0) {
1236 return AFPERR_DENYCONF;
1238 denyreadset = (getforkmode(adp, ADEID_DFORK, AD_FILELOCK_DENY_RD) != 0 ||
1239 getforkmode(adp, ADEID_RFORK, AD_FILELOCK_DENY_RD) != 0 );
1242 retvalue = AFPERR_DENYCONF;
1246 newname = obj->newtmp;
1247 strcpy( newname, s_path->m_name );
1249 p = ctoupath( s_vol, curdir, newname );
1251 retvalue = AFPERR_PARAM;
1256 /* FIXME svid != dvid && dvid's user can't read svid */
1258 if (NULL == ( d_vol = getvolbyvid( dvid )) ) {
1259 retvalue = AFPERR_PARAM;
1263 if (d_vol->v_flags & AFPVOL_RO) {
1264 retvalue = AFPERR_VLOCK;
1268 if (NULL == ( dir = dirlookup( d_vol, ddid )) ) {
1269 retvalue = afp_errno;
1273 if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) {
1274 retvalue = get_afp_errno(AFPERR_NOOBJ);
1278 if ( *s_path->m_name != '\0' ) {
1279 retvalue =path_error(s_path, AFPERR_NOOBJ);
1283 /* one of the handful of places that knows about the path type */
1284 if (copy_path_name(d_vol, newname, ibuf) < 0) {
1285 retvalue = AFPERR_PARAM;
1288 /* newname is always only a filename so curdir *is* its
1291 if (NULL == (upath = mtoupath(d_vol, newname, curdir->d_did, utf8_encoding()))) {
1292 retvalue =AFPERR_PARAM;
1296 if ( (err = copyfile(s_vol, d_vol, -1, p, upath , newname, adp)) < 0 ) {
1303 if (vol->v_flags & AFPVOL_DROPBOX) {
1304 retvalue=matchfile2dirperms(upath, vol, ddid); /* FIXME sdir or ddid */
1306 #endif /* DROPKLUDGE */
1308 setvoltime(obj, d_vol );
1311 ad_close( adp, ADFLAGS_DF |ADFLAGS_HF );
1315 /* ----------------------- */
1316 static int copy_all(const int dfd, const void *buf,
1322 LOG(log_debug9, logtype_afpd, "begin copy_all:");
1325 while (buflen > 0) {
1326 if ((cc = write(dfd, buf, buflen)) < 0) {
1338 LOG(log_debug9, logtype_afpd, "end copy_all:");
1344 /* --------------------------
1345 * copy only the fork data stream
1347 static int copy_fork(int eid, struct adouble *add, struct adouble *ads)
1354 if (eid == ADEID_DFORK) {
1355 sfd = ad_data_fileno(ads);
1356 dfd = ad_data_fileno(add);
1359 sfd = ad_reso_fileno(ads);
1360 dfd = ad_reso_fileno(add);
1363 if ((off_t)-1 == lseek(sfd, ad_getentryoff(ads, eid), SEEK_SET))
1366 if ((off_t)-1 == lseek(dfd, ad_getentryoff(add, eid), SEEK_SET))
1369 #if 0 /* ifdef SENDFILE_FLAVOR_LINUX */
1370 /* doesn't work With 2.6 FIXME, only check for EBADFD ? */
1374 #define BUF 128*1024*1024
1376 if (fstat(sfd, &st) == 0) {
1379 if ( offset >= st.st_size) {
1382 size = (st.st_size -offset > BUF)?BUF:st.st_size -offset;
1383 if ((cc = sys_sendfile(dfd, sfd, &offset, size)) < 0) {
1386 case EINVAL: /* there's no guarantee that all fs support sendfile */
1395 lseek(sfd, offset, SEEK_SET);
1399 if ((cc = read(sfd, filebuf, sizeof(filebuf))) < 0) {
1406 if (!cc || ((err = copy_all(dfd, filebuf, cc)) < 0)) {
1413 /* ----------------------------------
1414 * if newname is NULL (from directory.c) we don't want to copy the resource fork.
1415 * because we are doing it elsewhere.
1416 * currently if newname is NULL then adp is NULL.
1418 int copyfile(const struct vol *s_vol,
1419 const struct vol *d_vol,
1424 struct adouble *adp)
1426 struct adouble ads, add;
1433 LOG(log_debug, logtype_afpd, "copyfile(sfd:%d,s:'%s',d:'%s',n:'%s')",
1434 sfd, src, dst, newname);
1437 ad_init(&ads, s_vol->v_adouble, s_vol->v_ad_options);
1441 adflags = ADFLAGS_DF;
1443 adflags |= ADFLAGS_HF;
1446 if (ad_openat(sfd, src, adflags | ADFLAGS_NOHF, O_RDONLY, 0, adp) < 0) {
1451 if (ad_meta_fileno(adp) == -1 && ad_reso_fileno(adp) == -1) { /* META / HF */
1452 /* no resource fork, don't create one for dst file */
1453 adflags &= ~ADFLAGS_HF;
1456 stat_result = fstat(ad_data_fileno(adp), &st); /* saving stat exit code, thus saving us on one more stat later on */
1458 if (stat_result < 0) {
1459 /* unlikely but if fstat fails, the default file mode will be 0666. */
1460 st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
1463 ad_init(&add, d_vol->v_adouble, d_vol->v_ad_options);
1464 if (ad_open(dst , adflags, O_RDWR|O_CREAT|O_EXCL, st.st_mode, &add) < 0) {
1466 ad_close( adp, adflags );
1467 if (EEXIST != ret_err) {
1468 deletefile(d_vol, -1, dst, 0);
1471 return AFPERR_EXIST;
1475 * XXX if the source and the dest don't use the same resource type it's broken
1477 if (ad_reso_fileno(adp) == -1 || 0 == (err = copy_fork(ADEID_RFORK, &add, adp))){
1478 /* copy the data fork */
1479 if ((err = copy_fork(ADEID_DFORK, &add, adp)) == 0) {
1480 err = d_vol->vfs->vfs_copyfile(d_vol, sfd, src, dst);
1488 if (!ret_err && newname && (adflags & ADFLAGS_HF)) {
1489 /* set the new name in the resource fork */
1490 ad_copy_header(&add, adp);
1491 ad_setname(&add, newname);
1494 ad_close( adp, adflags );
1496 if (ad_close( &add, adflags ) <0) {
1501 deletefile(d_vol, -1, dst, 0);
1503 else if (stat_result == 0) {
1504 /* set dest modification date to src date */
1507 ut.actime = ut.modtime = st.st_mtime;
1509 /* FIXME netatalk doesn't use resource fork file date
1510 * but maybe we should set its modtime too.
1515 switch ( ret_err ) {
1521 return AFPERR_DFULL;
1523 return AFPERR_NOOBJ;
1525 return AFPERR_ACCESS;
1527 return AFPERR_VLOCK;
1529 return AFPERR_PARAM;
1533 /* -----------------------------------
1534 vol: not NULL delete cnid entry. then we are in curdir and file is a only filename
1535 checkAttrib: 1 check kFPDeleteInhibitBit (deletfile called by afp_delete)
1537 when deletefile is called we don't have lock on it, file is closed (for us)
1538 untrue if called by renamefile
1540 ad_open always try to open file RDWR first and ad_lock takes care of
1541 WRITE lock on read only file.
1544 static int check_attrib(struct adouble *adp)
1546 u_int16_t bshort = 0;
1548 ad_getattr(adp, &bshort);
1550 * Does kFPDeleteInhibitBit (bit 8) set?
1552 if ((bshort & htons(ATTRBIT_NODELETE))) {
1553 return AFPERR_OLOCK;
1555 if ((bshort & htons(ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
1561 * dirfd can be used for unlinkat semantics
1563 int deletefile(const struct vol *vol, int dirfd, char *file, int checkAttrib)
1566 struct adouble *adp = NULL;
1567 int adflags, err = AFP_OK;
1570 LOG(log_debug, logtype_afpd, "deletefile('%s')", file);
1572 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
1574 /* was EACCESS error try to get only metadata */
1575 /* we never want to create a resource fork here, we are going to delete it
1576 * moreover sometimes deletefile is called with a no existent file and
1577 * ad_open would create a 0 byte resource fork
1579 if ( ad_metadataat(dirfd, file, ADFLAGS_OPENFORKS, &ad) == 0 ) {
1580 if ((err = check_attrib(&ad))) {
1581 ad_close_metadata(&ad);
1588 /* try to open both forks at once */
1589 adflags = ADFLAGS_DF;
1590 if ( ad_openat(dirfd, file, adflags |ADFLAGS_HF|ADFLAGS_NOHF, O_RDONLY, 0, &ad ) < 0 ) {
1595 case EACCES: /* maybe it's a file with no write mode for us */
1596 break; /* was return AFPERR_ACCESS;*/
1609 if ( adp && ad_reso_fileno( adp ) != -1 ) { /* there's a resource fork */
1610 adflags |= ADFLAGS_HF;
1611 /* FIXME we have a pb here because we want to know if a file is open
1612 * there's a 'priority inversion' if you can't open the ressource fork RW
1613 * you can delete it if it's open because you can't get a write lock.
1615 * ADLOCK_FILELOCK means the whole ressource fork, not only after the
1618 * FIXME it doesn't work for RFORK open read only and fork open without deny mode
1620 if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
1626 if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
1628 } else if (!(err = vol->vfs->vfs_deletefile(vol, dirfd, file)) && !(err = netatalk_unlinkat(dirfd, file )) ) {
1630 if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file)))) {
1631 cnid_delete(vol->v_cdb, id);
1637 ad_close_metadata(&ad);
1640 ad_close( &ad, adflags ); /* ad_close removes locks if any */
1645 /* ------------------------------------ */
1646 /* return a file id */
1647 int afp_createid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1656 struct path *s_path;
1662 memcpy(&vid, ibuf, sizeof(vid));
1663 ibuf += sizeof(vid);
1665 if (NULL == ( vol = getvolbyvid( vid )) ) {
1666 return( AFPERR_PARAM);
1669 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1673 if (vol->v_flags & AFPVOL_RO)
1674 return AFPERR_VLOCK;
1676 memcpy(&did, ibuf, sizeof( did ));
1677 ibuf += sizeof(did);
1679 if (NULL == ( dir = dirlookup( vol, did )) ) {
1680 return afp_errno; /* was AFPERR_PARAM */
1683 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
1684 return get_afp_errno(AFPERR_NOOBJ); /* was AFPERR_PARAM */
1687 if ( path_isadir(s_path) ) {
1688 return( AFPERR_BADTYPE );
1691 upath = s_path->u_name;
1692 switch (s_path->st_errno) {
1694 break; /* success */
1697 return AFPERR_ACCESS;
1699 return AFPERR_NOOBJ;
1701 return AFPERR_PARAM;
1704 if ((id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath)))) {
1705 memcpy(rbuf, &id, sizeof(id));
1706 *rbuflen = sizeof(id);
1707 return AFPERR_EXISTID;
1710 if ((id = get_id(vol, NULL, st, did, upath, len)) != CNID_INVALID) {
1711 memcpy(rbuf, &id, sizeof(id));
1712 *rbuflen = sizeof(id);
1719 /* ------------------------------- */
1725 static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
1728 struct reenum *param = data;
1729 struct vol *vol = param->vol;
1730 cnid_t did = param->did;
1733 if ( lstat(de->d_name, &path.st)<0 )
1736 /* update or add to cnid */
1737 aint = cnid_add(vol->v_cdb, &path.st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */
1739 #if AD_VERSION > AD_VERSION1
1740 if (aint != CNID_INVALID && !S_ISDIR(path.st.st_mode)) {
1741 struct adouble ad, *adp;
1745 path.u_name = de->d_name;
1747 adp = of_ad(vol, &path, &ad);
1749 if ( ad_open_metadata( de->d_name, 0, 0, adp ) < 0 ) {
1752 if (ad_setid(adp, path.st.st_dev, path.st.st_ino, aint, did, vol->v_stamp)) {
1755 ad_close_metadata(adp);
1757 #endif /* AD_VERSION > AD_VERSION1 */
1762 /* --------------------
1763 * Ok the db is out of synch with the dir.
1764 * but if it's a deleted file we don't want to do it again and again.
1767 reenumerate_id(struct vol *vol, char *name, struct dir *dir)
1773 if (vol->v_cdb == NULL) {
1777 /* FIXME use of_statdir ? */
1778 if (lstat(name, &st)) {
1782 if (dirreenumerate(dir, &st)) {
1783 /* we already did it once and the dir haven't been modified */
1788 data.did = dir->d_did;
1789 if ((ret = for_each_dirent(vol, name, reenumerate_loop, (void *)&data)) >= 0) {
1790 setdiroffcnt(curdir, &st, ret);
1791 dir->d_flags |= DIRF_CNID;
1797 /* ------------------------------
1798 resolve a file id */
1799 int afp_resolveid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1808 u_int16_t vid, bitmap;
1810 static char buffer[12 + MAXPATHLEN + 1];
1811 int len = 12 + MAXPATHLEN + 1;
1816 memcpy(&vid, ibuf, sizeof(vid));
1817 ibuf += sizeof(vid);
1819 if (NULL == ( vol = getvolbyvid( vid )) ) {
1820 return( AFPERR_PARAM);
1823 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1827 memcpy(&id, ibuf, sizeof( id ));
1832 /* some MacOS versions after a catsearch do a *lot* of afp_resolveid with 0 */
1836 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1837 return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
1840 if (NULL == ( dir = dirlookup( vol, id )) ) {
1841 return AFPERR_NOID; /* idem AFPERR_PARAM */
1843 if (movecwd(vol, dir) < 0) {
1847 return AFPERR_ACCESS;
1851 return AFPERR_PARAM;
1855 memset(&path, 0, sizeof(path));
1856 path.u_name = upath;
1857 if ( of_stat(&path) < 0 ) {
1859 /* with nfs and our working directory is deleted */
1860 if (errno == ESTALE) {
1864 if ( errno == ENOENT && !retry) {
1865 /* cnid db is out of sync, reenumerate the directory and update ids */
1866 reenumerate_id(vol, ".", dir);
1874 return AFPERR_ACCESS;
1878 return AFPERR_PARAM;
1882 /* directories are bad */
1883 if (S_ISDIR(path.st.st_mode)) {
1884 /* OS9 and OSX don't return the same error code */
1885 return (afp_version >=30)?AFPERR_NOID:AFPERR_BADTYPE;
1888 memcpy(&bitmap, ibuf, sizeof(bitmap));
1889 bitmap = ntohs( bitmap );
1890 if (NULL == (path.m_name = utompath(vol, upath, cnid, utf8_encoding()))) {
1894 if (AFP_OK != (err = getfilparams(vol, bitmap, &path , curdir,
1895 rbuf + sizeof(bitmap), &buflen))) {
1898 *rbuflen = buflen + sizeof(bitmap);
1899 memcpy(rbuf, ibuf, sizeof(bitmap));
1904 /* ------------------------------ */
1905 int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1915 static char buffer[12 + MAXPATHLEN + 1];
1916 int len = 12 + MAXPATHLEN + 1;
1921 memcpy(&vid, ibuf, sizeof(vid));
1922 ibuf += sizeof(vid);
1924 if (NULL == ( vol = getvolbyvid( vid )) ) {
1925 return( AFPERR_PARAM);
1928 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1932 if (vol->v_flags & AFPVOL_RO)
1933 return AFPERR_VLOCK;
1935 memcpy(&id, ibuf, sizeof( id ));
1939 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1943 if (NULL == ( dir = dirlookup( vol, id )) ) {
1944 if (afp_errno == AFPERR_NOOBJ) {
1948 return( AFPERR_PARAM );
1952 if ((movecwd(vol, dir) < 0) || (lstat(upath, &st) < 0)) {
1956 return AFPERR_ACCESS;
1961 /* still try to delete the id */
1965 return AFPERR_PARAM;
1968 else if (S_ISDIR(st.st_mode)) /* directories are bad */
1969 return AFPERR_BADTYPE;
1972 if (cnid_delete(vol->v_cdb, fileid)) {
1975 return AFPERR_VLOCK;
1978 return AFPERR_ACCESS;
1980 return AFPERR_PARAM;
1987 /* ------------------------------ */
1988 static struct adouble *find_adouble(struct path *path, struct ofork **of, struct adouble *adp)
1992 if (path->st_errno) {
1993 switch (path->st_errno) {
1995 afp_errno = AFPERR_NOID;
1999 afp_errno = AFPERR_ACCESS;
2002 afp_errno = AFPERR_PARAM;
2007 /* we use file_access both for legacy Mac perm and
2008 * for unix privilege, rename will take care of folder perms
2010 if (file_access(path, OPENACC_WR ) < 0) {
2011 afp_errno = AFPERR_ACCESS;
2015 if ((*of = of_findname(path))) {
2016 /* reuse struct adouble so it won't break locks */
2020 ret = ad_open( path->u_name, ADFLAGS_HF, O_RDONLY, 0, adp);
2022 if ( !ret && ad_reso_fileno(adp) != -1 && !(adp->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
2024 * The user must have the Read & Write privilege for both files in order to use this command.
2026 ad_close(adp, ADFLAGS_HF);
2027 afp_errno = AFPERR_ACCESS;
2034 #define APPLETEMP ".AppleTempXXXXXX"
2036 int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
2038 struct stat srcst, destst;
2040 struct dir *dir, *sdir;
2041 char *spath, temp[17], *p;
2042 char *supath, *upath;
2047 struct adouble *adsp = NULL;
2048 struct adouble *addp = NULL;
2049 struct ofork *s_of = NULL;
2050 struct ofork *d_of = NULL;
2063 memcpy(&vid, ibuf, sizeof(vid));
2064 ibuf += sizeof(vid);
2066 if (NULL == ( vol = getvolbyvid( vid )) ) {
2067 return( AFPERR_PARAM);
2070 if ((vol->v_flags & AFPVOL_RO))
2071 return AFPERR_VLOCK;
2073 /* source and destination dids */
2074 memcpy(&sid, ibuf, sizeof(sid));
2075 ibuf += sizeof(sid);
2076 memcpy(&did, ibuf, sizeof(did));
2077 ibuf += sizeof(did);
2080 if (NULL == (dir = dirlookup( vol, sid )) ) {
2081 return afp_errno; /* was AFPERR_PARAM */
2084 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2085 return get_afp_errno(AFPERR_NOOBJ);
2088 if ( path_isadir(path) ) {
2089 return AFPERR_BADTYPE; /* it's a dir */
2092 /* save some stuff */
2095 spath = obj->oldtmp;
2096 supath = obj->newtmp;
2097 strcpy(spath, path->m_name);
2098 strcpy(supath, path->u_name); /* this is for the cnid changing */
2099 p = absupath( vol, sdir, supath);
2101 /* pathname too long */
2102 return AFPERR_PARAM ;
2105 ad_init(&ads, vol->v_adouble, vol->v_ad_options);
2106 if (!(adsp = find_adouble( path, &s_of, &ads))) {
2110 /* ***** from here we may have resource fork open **** */
2112 /* look for the source cnid. if it doesn't exist, don't worry about
2114 sid = cnid_lookup(vol->v_cdb, &srcst, sdir->d_did, supath,slen = strlen(supath));
2116 if (NULL == ( dir = dirlookup( vol, did )) ) {
2117 err = afp_errno; /* was AFPERR_PARAM */
2118 goto err_exchangefile;
2121 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2122 err = get_afp_errno(AFPERR_NOOBJ);
2123 goto err_exchangefile;
2126 if ( path_isadir(path) ) {
2127 err = AFPERR_BADTYPE;
2128 goto err_exchangefile;
2131 /* FPExchangeFiles is the only call that can return the SameObj
2133 if ((curdir == sdir) && strcmp(spath, path->m_name) == 0) {
2134 err = AFPERR_SAMEOBJ;
2135 goto err_exchangefile;
2138 ad_init(&add, vol->v_adouble, vol->v_ad_options);
2139 if (!(addp = find_adouble( path, &d_of, &add))) {
2141 goto err_exchangefile;
2145 /* they are not on the same device and at least one is open
2146 * FIXME broken for for crossdev and adouble v2
2149 crossdev = (srcst.st_dev != destst.st_dev);
2150 if (/* (d_of || s_of) && */ crossdev) {
2152 goto err_exchangefile;
2155 /* look for destination id. */
2156 upath = path->u_name;
2157 did = cnid_lookup(vol->v_cdb, &destst, curdir->d_did, upath, dlen = strlen(upath));
2159 /* construct a temp name.
2160 * NOTE: the temp file will be in the dest file's directory. it
2161 * will also be inaccessible from AFP. */
2162 memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
2163 if (!mktemp(temp)) {
2165 goto err_exchangefile;
2169 /* FIXME we need to close fork for copy, both s_of and d_of are null */
2170 ad_close(adsp, ADFLAGS_HF);
2171 ad_close(addp, ADFLAGS_HF);
2174 /* now, quickly rename the file. we error if we can't. */
2175 if ((err = renamefile(vol, -1, p, temp, temp, adsp)) != AFP_OK)
2176 goto err_exchangefile;
2177 of_rename(vol, s_of, sdir, spath, curdir, temp);
2179 /* rename destination to source */
2180 if ((err = renamefile(vol, -1, upath, p, spath, addp)) != AFP_OK)
2181 goto err_src_to_tmp;
2182 of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
2184 /* rename temp to destination */
2185 if ((err = renamefile(vol, -1, temp, upath, path->m_name, adsp)) != AFP_OK)
2186 goto err_dest_to_src;
2187 of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
2189 /* id's need switching. src -> dest and dest -> src.
2190 * we need to re-stat() if it was a cross device copy.
2193 cnid_delete(vol->v_cdb, sid);
2196 cnid_delete(vol->v_cdb, did);
2198 if ((did && ( (crossdev && lstat( upath, &srcst) < 0) ||
2199 cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
2201 (sid && ( (crossdev && lstat(p, &destst) < 0) ||
2202 cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
2207 err = AFPERR_ACCESS;
2212 goto err_temp_to_dest;
2215 /* here we need to reopen if crossdev */
2216 if (sid && ad_setid(addp, destst.st_dev, destst.st_ino, sid, sdir->d_did, vol->v_stamp))
2221 if (did && ad_setid(adsp, srcst.st_dev, srcst.st_ino, did, curdir->d_did, vol->v_stamp))
2226 /* change perms, src gets dest perm and vice versa */
2231 LOG(log_error, logtype_afpd, "seteuid failed %s", strerror(errno));
2232 err = AFP_OK; /* ignore error */
2233 goto err_temp_to_dest;
2237 * we need to exchange ACL entries as well
2239 /* exchange_acls(vol, p, upath); */
2244 path->m_name = NULL;
2245 path->u_name = upath;
2247 setfilunixmode(vol, path, destst.st_mode);
2248 setfilowner(vol, destst.st_uid, destst.st_gid, path);
2255 setfilunixmode(vol, path, srcst.st_mode);
2256 setfilowner(vol, srcst.st_uid, srcst.st_gid, path);
2258 if ( setegid(gid) < 0 || seteuid(uid) < 0) {
2259 LOG(log_error, logtype_afpd, "can't seteuid back %s", strerror(errno));
2264 goto err_exchangefile;
2266 /* all this stuff is so that we can unwind a failed operation
2269 /* rename dest to temp */
2270 renamefile(vol, -1, upath, temp, temp, adsp);
2271 of_rename(vol, s_of, curdir, upath, curdir, temp);
2274 /* rename source back to dest */
2275 renamefile(vol, -1, p, upath, path->m_name, addp);
2276 of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
2279 /* rename temp back to source */
2280 renamefile(vol, -1, temp, p, spath, adsp);
2281 of_rename(vol, s_of, curdir, temp, sdir, spath);
2284 if ( !s_of && adsp && ad_meta_fileno(adsp) != -1 ) { /* META */
2285 ad_close(adsp, ADFLAGS_HF);
2287 if ( !d_of && addp && ad_meta_fileno(addp) != -1 ) {/* META */
2288 ad_close(addp, ADFLAGS_HF);