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];
301 upath = path->u_name;
305 if ( ((bitmap & ( (1 << FILPBIT_FINFO)|(1 << FILPBIT_LNAME)|(1 <<FILPBIT_PDINFO) ) ) && !path->m_name)
306 || (bitmap & ( (1 << FILPBIT_LNAME) ) && utf8_encoding()) /* FIXME should be m_name utf8 filename */
307 || (bitmap & (1 << FILPBIT_FNUM))) {
309 struct dir *cachedfile;
310 int len = strlen(upath);
311 if (cachedfile = dircache_search_by_name(vol, dir, upath, len))
312 id = cachedfile->d_did;
314 id = get_id(vol, adp, st, dir->d_did, upath, len);
316 /* Add it to the cache */
317 LOG(log_debug, logtype_afpd, "getmetadata: caching: did:%u, \"%s\", cnid:%u",
318 ntohl(dir->d_did), upath, ntohl(id));
320 /* Get macname from unixname first */
321 if (path->m_name == NULL) {
322 if ((path->m_name = utompath(vol, upath, id, utf8_encoding())) == NULL) {
323 LOG(log_error, logtype_afpd, "getmetadata: utompath error");
328 if ((cachedfile = dir_new(path->m_name, upath, vol, dir->d_did, id, NULL)) == NULL) {
329 LOG(log_error, logtype_afpd, "getmetadata: error from dir_new");
332 if ((dircache_add(cachedfile)) != 0) {
333 LOG(log_error, logtype_afpd, "getmetadata: fatal dircache error");
341 if (id == CNID_INVALID)
345 path->m_name = utompath(vol, upath, id, utf8_encoding());
348 while ( bitmap != 0 ) {
349 while (( bitmap & 1 ) == 0 ) {
357 ad_getattr(adp, &ashort);
358 } else if (vol_inv_dots(vol) && *upath == '.') {
359 ashort = htons(ATTRBIT_INVISIBLE);
363 /* FIXME do we want a visual clue if the file is read only
366 accessmode( ".", &ma, dir , NULL);
367 if ((ma.ma_user & AR_UWRITE)) {
368 accessmode( upath, &ma, dir , st);
369 if (!(ma.ma_user & AR_UWRITE)) {
370 ashort |= htons(ATTRBIT_NOWRITE);
374 memcpy(data, &ashort, sizeof( ashort ));
375 data += sizeof( ashort );
376 LOG(log_debug, logtype_afpd, "metadata('%s'): AFP Attributes: %04x",
377 path->u_name, ntohs(ashort));
381 memcpy(data, &dir->d_did, sizeof( u_int32_t ));
382 data += sizeof( u_int32_t );
383 LOG(log_debug, logtype_afpd, "metadata('%s'): Parent DID: %u",
384 path->u_name, ntohl(dir->d_did));
388 if (!adp || (ad_getdate(adp, AD_DATE_CREATE, &aint) < 0))
389 aint = AD_DATE_FROM_UNIX(st->st_mtime);
390 memcpy(data, &aint, sizeof( aint ));
391 data += sizeof( aint );
395 if ( adp && (ad_getdate(adp, AD_DATE_MODIFY, &aint) == 0)) {
396 if ((st->st_mtime > AD_DATE_TO_UNIX(aint))) {
397 aint = AD_DATE_FROM_UNIX(st->st_mtime);
400 aint = AD_DATE_FROM_UNIX(st->st_mtime);
402 memcpy(data, &aint, sizeof( int ));
403 data += sizeof( int );
407 if (!adp || (ad_getdate(adp, AD_DATE_BACKUP, &aint) < 0))
408 aint = AD_DATE_START;
409 memcpy(data, &aint, sizeof( int ));
410 data += sizeof( int );
414 get_finderinfo(vol, upath, adp, (char *)data,S_ISLNK(st->st_mode));
415 data += ADEDLEN_FINDERI;
420 data += sizeof( u_int16_t );
424 memset(data, 0, sizeof(u_int16_t));
425 data += sizeof( u_int16_t );
429 memcpy(data, &id, sizeof( id ));
430 data += sizeof( id );
431 LOG(log_debug, logtype_afpd, "metadata('%s'): CNID: %u",
432 path->u_name, ntohl(id));
436 if (st->st_size > 0xffffffff)
439 aint = htonl( st->st_size );
440 memcpy(data, &aint, sizeof( aint ));
441 data += sizeof( aint );
446 if (adp->ad_rlen > 0xffffffff)
449 aint = htonl( adp->ad_rlen);
453 memcpy(data, &aint, sizeof( aint ));
454 data += sizeof( aint );
457 /* Current client needs ProDOS info block for this file.
458 Use simple heuristic and let the Mac "type" string tell
459 us what the PD file code should be. Everything gets a
460 subtype of 0x0000 unless the original value was hashed
461 to "pXYZ" when we created it. See IA, Ver 2.
462 <shirsch@adelphia.net> */
463 case FILPBIT_PDINFO :
464 if (afp_version >= 30) { /* UTF8 name */
465 utf8 = kTextEncodingUTF8;
467 data += sizeof( u_int16_t );
469 memcpy(data, &aint, sizeof( aint ));
470 data += sizeof( aint );
474 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
476 if ( memcmp( fdType, "TEXT", 4 ) == 0 ) {
480 else if ( memcmp( fdType, "PSYS", 4 ) == 0 ) {
484 else if ( memcmp( fdType, "PS16", 4 ) == 0 ) {
488 else if ( memcmp( fdType, "BINA", 4 ) == 0 ) {
492 else if ( fdType[0] == 'p' ) {
494 ashort = (fdType[2] * 256) + fdType[3];
508 memcpy(data, &ashort, sizeof( ashort ));
509 data += sizeof( ashort );
510 memset(data, 0, sizeof( ashort ));
511 data += sizeof( ashort );
514 case FILPBIT_EXTDFLEN:
515 aint = htonl(st->st_size >> 32);
516 memcpy(data, &aint, sizeof( aint ));
517 data += sizeof( aint );
518 aint = htonl(st->st_size);
519 memcpy(data, &aint, sizeof( aint ));
520 data += sizeof( aint );
522 case FILPBIT_EXTRFLEN:
525 aint = htonl(adp->ad_rlen >> 32);
526 memcpy(data, &aint, sizeof( aint ));
527 data += sizeof( aint );
529 aint = htonl(adp->ad_rlen);
530 memcpy(data, &aint, sizeof( aint ));
531 data += sizeof( aint );
533 case FILPBIT_UNIXPR :
534 /* accessmode may change st_mode with ACLs */
535 accessmode( upath, &ma, dir , st);
537 aint = htonl(st->st_uid);
538 memcpy( data, &aint, sizeof( aint ));
539 data += sizeof( aint );
540 aint = htonl(st->st_gid);
541 memcpy( data, &aint, sizeof( aint ));
542 data += sizeof( aint );
545 type == slnk indicates an OSX style symlink,
546 we have to add S_IFLNK to the mode, otherwise
547 10.3 clients freak out. */
551 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
552 if ( memcmp( fdType, "slnk", 4 ) == 0 ) {
558 memcpy( data, &aint, sizeof( aint ));
559 data += sizeof( aint );
561 *data++ = ma.ma_user;
562 *data++ = ma.ma_world;
563 *data++ = ma.ma_group;
564 *data++ = ma.ma_owner;
568 return( AFPERR_BITMAP );
574 ashort = htons( data - buf );
575 memcpy(l_nameoff, &ashort, sizeof( ashort ));
576 data = set_name(vol, data, dir->d_did, path->m_name, id, 0);
579 ashort = htons( data - buf );
580 memcpy(utf_nameoff, &ashort, sizeof( ashort ));
581 data = set_name(vol, data, dir->d_did, path->m_name, id, utf8);
583 *buflen = data - buf;
587 /* ----------------------- */
588 int getfilparams(struct vol *vol,
590 struct path *path, struct dir *dir,
591 char *buf, size_t *buflen )
593 struct adouble ad, *adp;
597 opened = PARAM_NEED_ADP(bitmap);
602 int flags = (bitmap & (1 << FILPBIT_ATTR))?ADFLAGS_OPENFORKS:0;
604 adp = of_ad(vol, path, &ad);
605 upath = path->u_name;
607 if ( ad_metadata( upath, flags|ADFLAGS_CREATE, adp) < 0 ) {
610 LOG(log_error, logtype_afpd, "getfilparams(%s): %s: check resource fork permission?",
611 upath, strerror(errno));
612 return AFPERR_ACCESS;
614 LOG(log_error, logtype_afpd, "getfilparams(%s): bad resource fork", upath);
623 rc = getmetadata(vol, bitmap, path, dir, buf, buflen, adp);
625 ad_close_metadata( adp);
631 /* ----------------------------- */
632 int afp_createfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
634 struct adouble ad, *adp;
637 struct ofork *of = NULL;
639 int creatf, did, openf, retvalue = AFP_OK;
645 creatf = (unsigned char) *ibuf++;
647 memcpy(&vid, ibuf, sizeof( vid ));
648 ibuf += sizeof( vid );
650 if (NULL == ( vol = getvolbyvid( vid )) ) {
651 return( AFPERR_PARAM );
654 if (vol->v_flags & AFPVOL_RO)
657 memcpy(&did, ibuf, sizeof( did));
658 ibuf += sizeof( did );
660 if (NULL == ( dir = dirlookup( vol, did )) ) {
664 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
665 return get_afp_errno(AFPERR_PARAM);
668 if ( *s_path->m_name == '\0' ) {
669 return( AFPERR_BADTYPE );
672 upath = s_path->u_name;
674 /* if upath is deleted we already in trouble anyway */
675 if ((of = of_findname(s_path))) {
678 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
682 /* on a hard create, fail if file exists and is open */
685 openf = O_RDWR|O_CREAT|O_TRUNC;
687 /* on a soft create, if the file is open then ad_open won't fail
688 because open syscall is not called
693 openf = O_RDWR|O_CREAT|O_EXCL;
696 if ( ad_open( upath, ADFLAGS_DF|ADFLAGS_HF|ADFLAGS_NOHF|ADFLAGS_CREATE,
697 openf, 0666, adp) < 0 ) {
701 case ENOENT : /* we were already in 'did folder' so chdir() didn't fail */
702 return ( AFPERR_NOOBJ );
704 return( AFPERR_EXIST );
706 return( AFPERR_ACCESS );
709 return( AFPERR_DFULL );
711 return( AFPERR_PARAM );
714 if ( ad_reso_fileno( adp ) == -1 ) { /* Hard META / HF */
715 /* on noadouble volumes, just creating the data fork is ok */
716 if (vol_noadouble(vol)) {
717 ad_close( adp, ADFLAGS_DF );
718 goto createfile_done;
720 /* FIXME with hard create on an existing file, we already
721 * corrupted the data file.
723 netatalk_unlink( upath );
724 ad_close( adp, ADFLAGS_DF );
725 return AFPERR_ACCESS;
728 path = s_path->m_name;
729 ad_setname(adp, path);
731 ad_close( adp, ADFLAGS_DF|ADFLAGS_HF );
737 if (vol->v_flags & AFPVOL_DROPBOX) {
738 retvalue = matchfile2dirperms(upath, vol, did);
740 #endif /* DROPKLUDGE */
742 setvoltime(obj, vol );
747 int afp_setfilparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
753 u_int16_t vid, bitmap;
758 memcpy(&vid, ibuf, sizeof( vid ));
759 ibuf += sizeof( vid );
760 if (NULL == ( vol = getvolbyvid( vid )) ) {
761 return( AFPERR_PARAM );
764 if (vol->v_flags & AFPVOL_RO)
767 memcpy(&did, ibuf, sizeof( did ));
768 ibuf += sizeof( did );
769 if (NULL == ( dir = dirlookup( vol, did )) ) {
770 return afp_errno; /* was AFPERR_NOOBJ */
773 memcpy(&bitmap, ibuf, sizeof( bitmap ));
774 bitmap = ntohs( bitmap );
775 ibuf += sizeof( bitmap );
777 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
778 return get_afp_errno(AFPERR_PARAM);
781 if (path_isadir(s_path)) {
782 return( AFPERR_BADTYPE ); /* it's a directory */
785 if ( s_path->st_errno != 0 ) {
786 return( AFPERR_NOOBJ );
789 if ((u_long)ibuf & 1 ) {
793 if (AFP_OK == ( rc = setfilparams(vol, s_path, bitmap, ibuf )) ) {
794 setvoltime(obj, vol );
801 * cf AFP3.0.pdf page 252 for change_mdate and change_parent_mdate logic
804 extern struct path Cur_Path;
806 int setfilparams(struct vol *vol,
807 struct path *path, u_int16_t f_bitmap, char *buf )
809 struct adouble ad, *adp;
811 int bit, isad = 1, err = AFP_OK;
813 u_char achar, *fdType, xyy[4]; /* uninitialized, OK 310105 */
814 u_int16_t ashort, bshort, oshort;
817 u_int16_t upriv_bit = 0;
821 int change_mdate = 0;
822 int change_parent_mdate = 0;
827 u_int16_t bitmap = f_bitmap;
828 u_int32_t cdate,bdate;
829 u_char finder_buf[32];
832 LOG(log_debug9, logtype_afpd, "begin setfilparams:");
835 adp = of_ad(vol, path, &ad);
836 upath = path->u_name;
838 if (!vol_unix_priv(vol) && check_access(upath, OPENACC_WR ) < 0) {
839 return AFPERR_ACCESS;
842 /* with unix priv maybe we have to change adouble file priv first */
844 while ( bitmap != 0 ) {
845 while (( bitmap & 1 ) == 0 ) {
852 memcpy(&ashort, buf, sizeof( ashort ));
853 buf += sizeof( ashort );
857 memcpy(&cdate, buf, sizeof(cdate));
858 buf += sizeof( cdate );
861 memcpy(&newdate, buf, sizeof( newdate ));
862 buf += sizeof( newdate );
866 memcpy(&bdate, buf, sizeof( bdate));
867 buf += sizeof( bdate );
871 memcpy(finder_buf, buf, 32 );
872 if (memcmp(buf,"slnkrhap",8)==0 && !S_ISLNK(path->st.st_mode)){
877 char buf[PATH_MAX+1];
878 if ((fp=open(path->u_name,O_RDONLY))>=0){
879 if ((len=read(fp,buf,PATH_MAX+1))){
880 if (unlink(path->u_name)==0){
882 erc = symlink(buf, path->u_name);
891 goto setfilparam_done;
896 case FILPBIT_UNIXPR :
897 if (!vol_unix_priv(vol)) {
898 /* this volume doesn't use unix priv */
904 change_parent_mdate = 1;
906 memcpy( &aint, buf, sizeof( aint ));
907 f_uid = ntohl (aint);
908 buf += sizeof( aint );
909 memcpy( &aint, buf, sizeof( aint ));
910 f_gid = ntohl (aint);
911 buf += sizeof( aint );
912 setfilowner(vol, f_uid, f_gid, path);
914 memcpy( &upriv, buf, sizeof( upriv ));
915 buf += sizeof( upriv );
916 upriv = ntohl (upriv);
917 if ((upriv & S_IWUSR)) {
918 setfilunixmode(vol, path, upriv);
925 case FILPBIT_PDINFO :
926 if (afp_version < 30) { /* else it's UTF8 name */
929 /* Keep special case to support crlf translations */
930 if ((unsigned int) achar == 0x04) {
931 fdType = (u_char *)"TEXT";
934 xyy[0] = ( u_char ) 'p';
945 /* break while loop */
954 /* second try with adouble open
956 if ( ad_open_metadata( upath, 0, O_CREAT, adp) < 0) {
957 LOG(log_debug, logtype_afpd, "setfilparams: ad_open_metadata error");
959 * For some things, we don't need an adouble header:
960 * - change of modification date
961 * - UNIX privs (Bug-ID #2863424)
963 if (!vol_noadouble(vol) && (f_bitmap & ~(1<<FILPBIT_MDATE | 1<<FILPBIT_UNIXPR))) {
964 LOG(log_debug, logtype_afpd, "setfilparams: need adouble access");
965 return AFPERR_ACCESS;
967 LOG(log_debug, logtype_afpd, "setfilparams: no adouble perms, but only FILPBIT_MDATE and/or FILPBIT_UNIXPR");
969 } else if ((ad_get_HF_flags( adp ) & O_CREAT) ) {
970 ad_setname(adp, path->m_name);
975 while ( bitmap != 0 ) {
976 while (( bitmap & 1 ) == 0 ) {
983 ad_getattr(adp, &bshort);
985 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
986 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
990 if ((bshort & htons(ATTRBIT_INVISIBLE)) != (oshort & htons(ATTRBIT_INVISIBLE)))
991 change_parent_mdate = 1;
992 ad_setattr(adp, bshort);
995 ad_setdate(adp, AD_DATE_CREATE, cdate);
1000 ad_setdate(adp, AD_DATE_BACKUP, bdate);
1002 case FILPBIT_FINFO :
1003 if (default_type( ad_entry( adp, ADEID_FINDERI ))
1005 ((em = getextmap( path->m_name )) &&
1006 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1007 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1008 || ((em = getdefextmap()) &&
1009 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1010 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1012 memcpy(finder_buf, ufinderi, 8 );
1014 memcpy(ad_entry( adp, ADEID_FINDERI ), finder_buf, 32 );
1016 case FILPBIT_UNIXPR :
1018 setfilunixmode(vol, path, upriv);
1021 case FILPBIT_PDINFO :
1022 if (afp_version < 30) { /* else it's UTF8 name */
1023 memcpy(ad_entry( adp, ADEID_FINDERI ), fdType, 4 );
1024 memcpy(ad_entry( adp, ADEID_FINDERI ) + 4, "pdos", 4 );
1029 err = AFPERR_BITMAP;
1030 goto setfilparam_done;
1037 if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
1038 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1042 ad_setdate(adp, AD_DATE_MODIFY, newdate);
1043 ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
1049 ad_close_metadata( adp);
1053 if (change_parent_mdate && gettimeofday(&tv, NULL) == 0) {
1054 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1055 bitmap = 1<<FILPBIT_MDATE;
1056 setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
1060 LOG(log_debug9, logtype_afpd, "end setfilparams:");
1066 * renamefile and copyfile take the old and new unix pathnames
1067 * and the new mac name.
1069 * sdir_fd source dir fd to which src path is relative (for openat et al semantics)
1070 * passing -1 means this is not used, src path is a full path
1071 * src the source path
1072 * dst the dest filename in current dir
1073 * newname the dest mac name
1074 * adp adouble struct of src file, if open, or & zeroed one
1077 int renamefile(const struct vol *vol, int sdir_fd, char *src, char *dst, char *newname, struct adouble *adp)
1081 if ( unix_rename( sdir_fd, src, -1, dst ) < 0 ) {
1084 return( AFPERR_NOOBJ );
1087 return( AFPERR_ACCESS );
1089 return AFPERR_VLOCK;
1090 case EXDEV : /* Cross device move -- try copy */
1091 /* NOTE: with open file it's an error because after the copy we will
1092 * get two files, it's fixable for our process (eg reopen the new file, get the
1093 * locks, and so on. But it doesn't solve the case with a second process
1095 if (adp->ad_open_forks) {
1096 /* FIXME warning in syslog so admin'd know there's a conflict ?*/
1097 return AFPERR_OLOCK; /* little lie */
1099 if (AFP_OK != ( rc = copyfile(vol, vol, sdir_fd, src, dst, newname, NULL )) ) {
1100 /* on error copyfile delete dest */
1103 return deletefile(vol, sdir_fd, src, 0);
1105 return( AFPERR_PARAM );
1109 if (vol->vfs->vfs_renamefile(vol, sdir_fd, src, dst) < 0 ) {
1113 /* try to undo the data fork rename,
1114 * we know we are on the same device
1117 unix_rename(-1, dst, sdir_fd, src );
1118 /* return the first error */
1121 return AFPERR_NOOBJ;
1124 return AFPERR_ACCESS ;
1126 return AFPERR_VLOCK;
1128 return AFPERR_PARAM ;
1133 /* don't care if we can't open the newly renamed ressource fork
1135 if (!ad_open( dst, ADFLAGS_HF, O_RDWR, 0666, adp)) {
1136 ad_setname(adp, newname);
1138 ad_close( adp, ADFLAGS_HF );
1145 convert a Mac long name to an utf8 name,
1147 size_t mtoUTF8(const struct vol *vol, const char *src, size_t srclen, char *dest, size_t destlen)
1151 if ((size_t)-1 == (outlen = convert_string ( vol->v_maccharset, CH_UTF8_MAC, src, srclen, dest, destlen)) ) {
1157 /* ---------------- */
1158 int copy_path_name(const struct vol *vol, char *newname, char *ibuf)
1165 if ( type != 2 && !(afp_version >= 30 && type == 3) ) {
1171 if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
1172 if (afp_version >= 30) {
1173 /* convert it to UTF8
1175 if ((plen = mtoUTF8(vol, ibuf, plen, newname, AFPOBJ_TMPSIZ)) == (size_t)-1)
1179 strncpy( newname, ibuf, plen );
1180 newname[ plen ] = '\0';
1182 if (strlen(newname) != plen) {
1183 /* there's \0 in newname, e.g. it's a pathname not
1191 memcpy(&hint, ibuf, sizeof(hint));
1192 ibuf += sizeof(hint);
1194 memcpy(&len16, ibuf, sizeof(len16));
1195 ibuf += sizeof(len16);
1196 plen = ntohs(len16);
1199 if (plen > AFPOBJ_TMPSIZ) {
1202 strncpy( newname, ibuf, plen );
1203 newname[ plen ] = '\0';
1204 if (strlen(newname) != plen) {
1213 /* -----------------------------------
1215 int afp_copyfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1217 struct vol *s_vol, *d_vol;
1219 char *newname, *p, *upath;
1220 struct path *s_path;
1221 u_int32_t sdid, ddid;
1222 int err, retvalue = AFP_OK;
1223 u_int16_t svid, dvid;
1225 struct adouble ad, *adp;
1231 memcpy(&svid, ibuf, sizeof( svid ));
1232 ibuf += sizeof( svid );
1233 if (NULL == ( s_vol = getvolbyvid( svid )) ) {
1234 return( AFPERR_PARAM );
1237 memcpy(&sdid, ibuf, sizeof( sdid ));
1238 ibuf += sizeof( sdid );
1239 if (NULL == ( dir = dirlookup( s_vol, sdid )) ) {
1243 memcpy(&dvid, ibuf, sizeof( dvid ));
1244 ibuf += sizeof( dvid );
1245 memcpy(&ddid, ibuf, sizeof( ddid ));
1246 ibuf += sizeof( ddid );
1248 if (NULL == ( s_path = cname( s_vol, dir, &ibuf )) ) {
1249 return get_afp_errno(AFPERR_PARAM);
1251 if ( path_isadir(s_path) ) {
1252 return( AFPERR_BADTYPE );
1255 /* don't allow copies when the file is open.
1256 * XXX: the spec only calls for read/deny write access.
1257 * however, copyfile doesn't have any of that info,
1258 * and locks need to stay coherent. as a result,
1259 * we just balk if the file is opened already. */
1261 adp = of_ad(s_vol, s_path, &ad);
1263 if (ad_open(s_path->u_name , ADFLAGS_DF |ADFLAGS_HF | ADFLAGS_NOHF, O_RDONLY, 0, adp) < 0) {
1264 return AFPERR_DENYCONF;
1266 denyreadset = (getforkmode(adp, ADEID_DFORK, AD_FILELOCK_DENY_RD) != 0 ||
1267 getforkmode(adp, ADEID_RFORK, AD_FILELOCK_DENY_RD) != 0 );
1270 retvalue = AFPERR_DENYCONF;
1274 newname = obj->newtmp;
1275 strcpy( newname, s_path->m_name );
1277 p = ctoupath( s_vol, curdir, newname );
1279 retvalue = AFPERR_PARAM;
1284 /* FIXME svid != dvid && dvid's user can't read svid */
1286 if (NULL == ( d_vol = getvolbyvid( dvid )) ) {
1287 retvalue = AFPERR_PARAM;
1291 if (d_vol->v_flags & AFPVOL_RO) {
1292 retvalue = AFPERR_VLOCK;
1296 if (NULL == ( dir = dirlookup( d_vol, ddid )) ) {
1297 retvalue = afp_errno;
1301 if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) {
1302 retvalue = get_afp_errno(AFPERR_NOOBJ);
1306 if ( *s_path->m_name != '\0' ) {
1307 retvalue =path_error(s_path, AFPERR_NOOBJ);
1311 /* one of the handful of places that knows about the path type */
1312 if (copy_path_name(d_vol, newname, ibuf) < 0) {
1313 retvalue = AFPERR_PARAM;
1316 /* newname is always only a filename so curdir *is* its
1319 if (NULL == (upath = mtoupath(d_vol, newname, curdir->d_did, utf8_encoding()))) {
1320 retvalue =AFPERR_PARAM;
1324 if ( (err = copyfile(s_vol, d_vol, -1, p, upath , newname, adp)) < 0 ) {
1331 if (vol->v_flags & AFPVOL_DROPBOX) {
1332 retvalue=matchfile2dirperms(upath, vol, ddid); /* FIXME sdir or ddid */
1334 #endif /* DROPKLUDGE */
1336 setvoltime(obj, d_vol );
1339 ad_close( adp, ADFLAGS_DF |ADFLAGS_HF );
1343 /* ----------------------- */
1344 static int copy_all(const int dfd, const void *buf,
1350 LOG(log_debug9, logtype_afpd, "begin copy_all:");
1353 while (buflen > 0) {
1354 if ((cc = write(dfd, buf, buflen)) < 0) {
1366 LOG(log_debug9, logtype_afpd, "end copy_all:");
1372 /* --------------------------
1373 * copy only the fork data stream
1375 static int copy_fork(int eid, struct adouble *add, struct adouble *ads)
1382 if (eid == ADEID_DFORK) {
1383 sfd = ad_data_fileno(ads);
1384 dfd = ad_data_fileno(add);
1387 sfd = ad_reso_fileno(ads);
1388 dfd = ad_reso_fileno(add);
1391 if ((off_t)-1 == lseek(sfd, ad_getentryoff(ads, eid), SEEK_SET))
1394 if ((off_t)-1 == lseek(dfd, ad_getentryoff(add, eid), SEEK_SET))
1397 #if 0 /* ifdef SENDFILE_FLAVOR_LINUX */
1398 /* doesn't work With 2.6 FIXME, only check for EBADFD ? */
1402 #define BUF 128*1024*1024
1404 if (fstat(sfd, &st) == 0) {
1407 if ( offset >= st.st_size) {
1410 size = (st.st_size -offset > BUF)?BUF:st.st_size -offset;
1411 if ((cc = sys_sendfile(dfd, sfd, &offset, size)) < 0) {
1414 case EINVAL: /* there's no guarantee that all fs support sendfile */
1423 lseek(sfd, offset, SEEK_SET);
1427 if ((cc = read(sfd, filebuf, sizeof(filebuf))) < 0) {
1434 if (!cc || ((err = copy_all(dfd, filebuf, cc)) < 0)) {
1441 /* ----------------------------------
1442 * if newname is NULL (from directory.c) we don't want to copy the resource fork.
1443 * because we are doing it elsewhere.
1444 * currently if newname is NULL then adp is NULL.
1446 int copyfile(const struct vol *s_vol,
1447 const struct vol *d_vol,
1452 struct adouble *adp)
1454 struct adouble ads, add;
1461 LOG(log_debug, logtype_afpd, "copyfile(sfd:%d,s:'%s',d:'%s',n:'%s')",
1462 sfd, src, dst, newname);
1465 ad_init(&ads, s_vol->v_adouble, s_vol->v_ad_options);
1469 adflags = ADFLAGS_DF;
1471 adflags |= ADFLAGS_HF;
1474 if (ad_openat(sfd, src, adflags | ADFLAGS_NOHF, O_RDONLY, 0, adp) < 0) {
1479 if (ad_meta_fileno(adp) == -1 && ad_reso_fileno(adp) == -1) { /* META / HF */
1480 /* no resource fork, don't create one for dst file */
1481 adflags &= ~ADFLAGS_HF;
1484 stat_result = fstat(ad_data_fileno(adp), &st); /* saving stat exit code, thus saving us on one more stat later on */
1486 if (stat_result < 0) {
1487 /* unlikely but if fstat fails, the default file mode will be 0666. */
1488 st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
1491 ad_init(&add, d_vol->v_adouble, d_vol->v_ad_options);
1492 if (ad_open(dst , adflags, O_RDWR|O_CREAT|O_EXCL, st.st_mode, &add) < 0) {
1494 ad_close( adp, adflags );
1495 if (EEXIST != ret_err) {
1496 deletefile(d_vol, -1, dst, 0);
1499 return AFPERR_EXIST;
1503 * XXX if the source and the dest don't use the same resource type it's broken
1505 if (ad_reso_fileno(adp) == -1 || 0 == (err = copy_fork(ADEID_RFORK, &add, adp))){
1506 /* copy the data fork */
1507 if ((err = copy_fork(ADEID_DFORK, &add, adp)) == 0) {
1508 err = d_vol->vfs->vfs_copyfile(d_vol, sfd, src, dst);
1516 if (!ret_err && newname && (adflags & ADFLAGS_HF)) {
1517 /* set the new name in the resource fork */
1518 ad_copy_header(&add, adp);
1519 ad_setname(&add, newname);
1522 ad_close( adp, adflags );
1524 if (ad_close( &add, adflags ) <0) {
1529 deletefile(d_vol, -1, dst, 0);
1531 else if (stat_result == 0) {
1532 /* set dest modification date to src date */
1535 ut.actime = ut.modtime = st.st_mtime;
1537 /* FIXME netatalk doesn't use resource fork file date
1538 * but maybe we should set its modtime too.
1543 switch ( ret_err ) {
1549 return AFPERR_DFULL;
1551 return AFPERR_NOOBJ;
1553 return AFPERR_ACCESS;
1555 return AFPERR_VLOCK;
1557 return AFPERR_PARAM;
1561 /* -----------------------------------
1562 vol: not NULL delete cnid entry. then we are in curdir and file is a only filename
1563 checkAttrib: 1 check kFPDeleteInhibitBit (deletfile called by afp_delete)
1565 when deletefile is called we don't have lock on it, file is closed (for us)
1566 untrue if called by renamefile
1568 ad_open always try to open file RDWR first and ad_lock takes care of
1569 WRITE lock on read only file.
1572 static int check_attrib(struct adouble *adp)
1574 u_int16_t bshort = 0;
1576 ad_getattr(adp, &bshort);
1578 * Does kFPDeleteInhibitBit (bit 8) set?
1580 if ((bshort & htons(ATTRBIT_NODELETE))) {
1581 return AFPERR_OLOCK;
1583 if ((bshort & htons(ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
1589 * dirfd can be used for unlinkat semantics
1591 int deletefile(const struct vol *vol, int dirfd, char *file, int checkAttrib)
1594 struct adouble *adp = NULL;
1595 int adflags, err = AFP_OK;
1598 LOG(log_debug, logtype_afpd, "deletefile('%s')", file);
1600 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
1602 /* was EACCESS error try to get only metadata */
1603 /* we never want to create a resource fork here, we are going to delete it
1604 * moreover sometimes deletefile is called with a no existent file and
1605 * ad_open would create a 0 byte resource fork
1607 if ( ad_metadataat(dirfd, file, ADFLAGS_OPENFORKS, &ad) == 0 ) {
1608 if ((err = check_attrib(&ad))) {
1609 ad_close_metadata(&ad);
1616 /* try to open both forks at once */
1617 adflags = ADFLAGS_DF;
1618 if ( ad_openat(dirfd, file, adflags |ADFLAGS_HF|ADFLAGS_NOHF, O_RDONLY, 0, &ad ) < 0 ) {
1623 case EACCES: /* maybe it's a file with no write mode for us */
1624 break; /* was return AFPERR_ACCESS;*/
1637 if ( adp && ad_reso_fileno( adp ) != -1 ) { /* there's a resource fork */
1638 adflags |= ADFLAGS_HF;
1639 /* FIXME we have a pb here because we want to know if a file is open
1640 * there's a 'priority inversion' if you can't open the ressource fork RW
1641 * you can delete it if it's open because you can't get a write lock.
1643 * ADLOCK_FILELOCK means the whole ressource fork, not only after the
1646 * FIXME it doesn't work for RFORK open read only and fork open without deny mode
1648 if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
1654 if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
1656 } else if (!(err = vol->vfs->vfs_deletefile(vol, dirfd, file)) && !(err = netatalk_unlinkat(dirfd, file )) ) {
1658 if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file)))) {
1659 cnid_delete(vol->v_cdb, id);
1665 ad_close_metadata(&ad);
1668 ad_close( &ad, adflags ); /* ad_close removes locks if any */
1673 /* ------------------------------------ */
1674 /* return a file id */
1675 int afp_createid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1684 struct path *s_path;
1690 memcpy(&vid, ibuf, sizeof(vid));
1691 ibuf += sizeof(vid);
1693 if (NULL == ( vol = getvolbyvid( vid )) ) {
1694 return( AFPERR_PARAM);
1697 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1701 if (vol->v_flags & AFPVOL_RO)
1702 return AFPERR_VLOCK;
1704 memcpy(&did, ibuf, sizeof( did ));
1705 ibuf += sizeof(did);
1707 if (NULL == ( dir = dirlookup( vol, did )) ) {
1708 return afp_errno; /* was AFPERR_PARAM */
1711 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
1712 return get_afp_errno(AFPERR_NOOBJ); /* was AFPERR_PARAM */
1715 if ( path_isadir(s_path) ) {
1716 return( AFPERR_BADTYPE );
1719 upath = s_path->u_name;
1720 switch (s_path->st_errno) {
1722 break; /* success */
1725 return AFPERR_ACCESS;
1727 return AFPERR_NOOBJ;
1729 return AFPERR_PARAM;
1732 if ((id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath)))) {
1733 memcpy(rbuf, &id, sizeof(id));
1734 *rbuflen = sizeof(id);
1735 return AFPERR_EXISTID;
1738 if ((id = get_id(vol, NULL, st, did, upath, len)) != CNID_INVALID) {
1739 memcpy(rbuf, &id, sizeof(id));
1740 *rbuflen = sizeof(id);
1747 /* ------------------------------- */
1753 static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
1756 struct reenum *param = data;
1757 struct vol *vol = param->vol;
1758 cnid_t did = param->did;
1761 if ( lstat(de->d_name, &path.st)<0 )
1764 /* update or add to cnid */
1765 aint = cnid_add(vol->v_cdb, &path.st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */
1767 #if AD_VERSION > AD_VERSION1
1768 if (aint != CNID_INVALID && !S_ISDIR(path.st.st_mode)) {
1769 struct adouble ad, *adp;
1773 path.u_name = de->d_name;
1775 adp = of_ad(vol, &path, &ad);
1777 if ( ad_open_metadata( de->d_name, 0, 0, adp ) < 0 ) {
1780 if (ad_setid(adp, path.st.st_dev, path.st.st_ino, aint, did, vol->v_stamp)) {
1783 ad_close_metadata(adp);
1785 #endif /* AD_VERSION > AD_VERSION1 */
1790 /* --------------------
1791 * Ok the db is out of synch with the dir.
1792 * but if it's a deleted file we don't want to do it again and again.
1795 reenumerate_id(struct vol *vol, char *name, struct dir *dir)
1801 if (vol->v_cdb == NULL) {
1805 /* FIXME use of_statdir ? */
1806 if (lstat(name, &st)) {
1810 if (dirreenumerate(dir, &st)) {
1811 /* we already did it once and the dir haven't been modified */
1816 data.did = dir->d_did;
1817 if ((ret = for_each_dirent(vol, name, reenumerate_loop, (void *)&data)) >= 0) {
1818 setdiroffcnt(curdir, &st, ret);
1819 dir->d_flags |= DIRF_CNID;
1825 /* ------------------------------
1826 resolve a file id */
1827 int afp_resolveid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1836 u_int16_t vid, bitmap;
1838 static char buffer[12 + MAXPATHLEN + 1];
1839 int len = 12 + MAXPATHLEN + 1;
1844 memcpy(&vid, ibuf, sizeof(vid));
1845 ibuf += sizeof(vid);
1847 if (NULL == ( vol = getvolbyvid( vid )) ) {
1848 return( AFPERR_PARAM);
1851 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1855 memcpy(&id, ibuf, sizeof( id ));
1860 /* some MacOS versions after a catsearch do a *lot* of afp_resolveid with 0 */
1864 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1865 return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
1868 if (NULL == ( dir = dirlookup( vol, id )) ) {
1869 return AFPERR_NOID; /* idem AFPERR_PARAM */
1871 if (movecwd(vol, dir) < 0) {
1875 return AFPERR_ACCESS;
1879 return AFPERR_PARAM;
1883 memset(&path, 0, sizeof(path));
1884 path.u_name = upath;
1885 if ( of_stat(&path) < 0 ) {
1887 /* with nfs and our working directory is deleted */
1888 if (errno == ESTALE) {
1892 if ( errno == ENOENT && !retry) {
1893 /* cnid db is out of sync, reenumerate the directory and update ids */
1894 reenumerate_id(vol, ".", dir);
1902 return AFPERR_ACCESS;
1906 return AFPERR_PARAM;
1910 /* directories are bad */
1911 if (S_ISDIR(path.st.st_mode)) {
1912 /* OS9 and OSX don't return the same error code */
1913 return (afp_version >=30)?AFPERR_NOID:AFPERR_BADTYPE;
1916 memcpy(&bitmap, ibuf, sizeof(bitmap));
1917 bitmap = ntohs( bitmap );
1918 if (NULL == (path.m_name = utompath(vol, upath, cnid, utf8_encoding()))) {
1922 if (AFP_OK != (err = getfilparams(vol, bitmap, &path , curdir,
1923 rbuf + sizeof(bitmap), &buflen))) {
1926 *rbuflen = buflen + sizeof(bitmap);
1927 memcpy(rbuf, ibuf, sizeof(bitmap));
1932 /* ------------------------------ */
1933 int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1943 static char buffer[12 + MAXPATHLEN + 1];
1944 int len = 12 + MAXPATHLEN + 1;
1949 memcpy(&vid, ibuf, sizeof(vid));
1950 ibuf += sizeof(vid);
1952 if (NULL == ( vol = getvolbyvid( vid )) ) {
1953 return( AFPERR_PARAM);
1956 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1960 if (vol->v_flags & AFPVOL_RO)
1961 return AFPERR_VLOCK;
1963 memcpy(&id, ibuf, sizeof( id ));
1967 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1971 if (NULL == ( dir = dirlookup( vol, id )) ) {
1972 if (afp_errno == AFPERR_NOOBJ) {
1976 return( AFPERR_PARAM );
1980 if ((movecwd(vol, dir) < 0) || (lstat(upath, &st) < 0)) {
1984 return AFPERR_ACCESS;
1989 /* still try to delete the id */
1993 return AFPERR_PARAM;
1996 else if (S_ISDIR(st.st_mode)) /* directories are bad */
1997 return AFPERR_BADTYPE;
2000 if (cnid_delete(vol->v_cdb, fileid)) {
2003 return AFPERR_VLOCK;
2006 return AFPERR_ACCESS;
2008 return AFPERR_PARAM;
2015 /* ------------------------------ */
2016 static struct adouble *find_adouble(struct path *path, struct ofork **of, struct adouble *adp)
2020 if (path->st_errno) {
2021 switch (path->st_errno) {
2023 afp_errno = AFPERR_NOID;
2027 afp_errno = AFPERR_ACCESS;
2030 afp_errno = AFPERR_PARAM;
2035 /* we use file_access both for legacy Mac perm and
2036 * for unix privilege, rename will take care of folder perms
2038 if (file_access(path, OPENACC_WR ) < 0) {
2039 afp_errno = AFPERR_ACCESS;
2043 if ((*of = of_findname(path))) {
2044 /* reuse struct adouble so it won't break locks */
2048 ret = ad_open( path->u_name, ADFLAGS_HF, O_RDONLY, 0, adp);
2050 if ( !ret && ad_reso_fileno(adp) != -1 && !(adp->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
2052 * The user must have the Read & Write privilege for both files in order to use this command.
2054 ad_close(adp, ADFLAGS_HF);
2055 afp_errno = AFPERR_ACCESS;
2062 #define APPLETEMP ".AppleTempXXXXXX"
2064 int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
2066 struct stat srcst, destst;
2068 struct dir *dir, *sdir;
2069 char *spath, temp[17], *p;
2070 char *supath, *upath;
2075 struct adouble *adsp = NULL;
2076 struct adouble *addp = NULL;
2077 struct ofork *s_of = NULL;
2078 struct ofork *d_of = NULL;
2091 memcpy(&vid, ibuf, sizeof(vid));
2092 ibuf += sizeof(vid);
2094 if (NULL == ( vol = getvolbyvid( vid )) ) {
2095 return( AFPERR_PARAM);
2098 if ((vol->v_flags & AFPVOL_RO))
2099 return AFPERR_VLOCK;
2101 /* source and destination dids */
2102 memcpy(&sid, ibuf, sizeof(sid));
2103 ibuf += sizeof(sid);
2104 memcpy(&did, ibuf, sizeof(did));
2105 ibuf += sizeof(did);
2108 if (NULL == (dir = dirlookup( vol, sid )) ) {
2109 return afp_errno; /* was AFPERR_PARAM */
2112 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2113 return get_afp_errno(AFPERR_NOOBJ);
2116 if ( path_isadir(path) ) {
2117 return AFPERR_BADTYPE; /* it's a dir */
2120 /* save some stuff */
2123 spath = obj->oldtmp;
2124 supath = obj->newtmp;
2125 strcpy(spath, path->m_name);
2126 strcpy(supath, path->u_name); /* this is for the cnid changing */
2127 p = absupath( vol, sdir, supath);
2129 /* pathname too long */
2130 return AFPERR_PARAM ;
2133 ad_init(&ads, vol->v_adouble, vol->v_ad_options);
2134 if (!(adsp = find_adouble( path, &s_of, &ads))) {
2138 /* ***** from here we may have resource fork open **** */
2140 /* look for the source cnid. if it doesn't exist, don't worry about
2142 sid = cnid_lookup(vol->v_cdb, &srcst, sdir->d_did, supath,slen = strlen(supath));
2144 if (NULL == ( dir = dirlookup( vol, did )) ) {
2145 err = afp_errno; /* was AFPERR_PARAM */
2146 goto err_exchangefile;
2149 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2150 err = get_afp_errno(AFPERR_NOOBJ);
2151 goto err_exchangefile;
2154 if ( path_isadir(path) ) {
2155 err = AFPERR_BADTYPE;
2156 goto err_exchangefile;
2159 /* FPExchangeFiles is the only call that can return the SameObj
2161 if ((curdir == sdir) && strcmp(spath, path->m_name) == 0) {
2162 err = AFPERR_SAMEOBJ;
2163 goto err_exchangefile;
2166 ad_init(&add, vol->v_adouble, vol->v_ad_options);
2167 if (!(addp = find_adouble( path, &d_of, &add))) {
2169 goto err_exchangefile;
2173 /* they are not on the same device and at least one is open
2174 * FIXME broken for for crossdev and adouble v2
2177 crossdev = (srcst.st_dev != destst.st_dev);
2178 if (/* (d_of || s_of) && */ crossdev) {
2180 goto err_exchangefile;
2183 /* look for destination id. */
2184 upath = path->u_name;
2185 did = cnid_lookup(vol->v_cdb, &destst, curdir->d_did, upath, dlen = strlen(upath));
2187 /* construct a temp name.
2188 * NOTE: the temp file will be in the dest file's directory. it
2189 * will also be inaccessible from AFP. */
2190 memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
2191 if (!mktemp(temp)) {
2193 goto err_exchangefile;
2197 /* FIXME we need to close fork for copy, both s_of and d_of are null */
2198 ad_close(adsp, ADFLAGS_HF);
2199 ad_close(addp, ADFLAGS_HF);
2202 /* now, quickly rename the file. we error if we can't. */
2203 if ((err = renamefile(vol, -1, p, temp, temp, adsp)) != AFP_OK)
2204 goto err_exchangefile;
2205 of_rename(vol, s_of, sdir, spath, curdir, temp);
2207 /* rename destination to source */
2208 if ((err = renamefile(vol, -1, upath, p, spath, addp)) != AFP_OK)
2209 goto err_src_to_tmp;
2210 of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
2212 /* rename temp to destination */
2213 if ((err = renamefile(vol, -1, temp, upath, path->m_name, adsp)) != AFP_OK)
2214 goto err_dest_to_src;
2215 of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
2217 /* id's need switching. src -> dest and dest -> src.
2218 * we need to re-stat() if it was a cross device copy.
2221 cnid_delete(vol->v_cdb, sid);
2224 cnid_delete(vol->v_cdb, did);
2226 if ((did && ( (crossdev && lstat( upath, &srcst) < 0) ||
2227 cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
2229 (sid && ( (crossdev && lstat(p, &destst) < 0) ||
2230 cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
2235 err = AFPERR_ACCESS;
2240 goto err_temp_to_dest;
2243 /* here we need to reopen if crossdev */
2244 if (sid && ad_setid(addp, destst.st_dev, destst.st_ino, sid, sdir->d_did, vol->v_stamp))
2249 if (did && ad_setid(adsp, srcst.st_dev, srcst.st_ino, did, curdir->d_did, vol->v_stamp))
2254 /* change perms, src gets dest perm and vice versa */
2259 LOG(log_error, logtype_afpd, "seteuid failed %s", strerror(errno));
2260 err = AFP_OK; /* ignore error */
2261 goto err_temp_to_dest;
2265 * we need to exchange ACL entries as well
2267 /* exchange_acls(vol, p, upath); */
2272 path->m_name = NULL;
2273 path->u_name = upath;
2275 setfilunixmode(vol, path, destst.st_mode);
2276 setfilowner(vol, destst.st_uid, destst.st_gid, path);
2283 setfilunixmode(vol, path, srcst.st_mode);
2284 setfilowner(vol, srcst.st_uid, srcst.st_gid, path);
2286 if ( setegid(gid) < 0 || seteuid(uid) < 0) {
2287 LOG(log_error, logtype_afpd, "can't seteuid back %s", strerror(errno));
2292 goto err_exchangefile;
2294 /* all this stuff is so that we can unwind a failed operation
2297 /* rename dest to temp */
2298 renamefile(vol, -1, upath, temp, temp, adsp);
2299 of_rename(vol, s_of, curdir, upath, curdir, temp);
2302 /* rename source back to dest */
2303 renamefile(vol, -1, p, upath, path->m_name, addp);
2304 of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
2307 /* rename temp back to source */
2308 renamefile(vol, -1, temp, p, spath, adsp);
2309 of_rename(vol, s_of, curdir, temp, sdir, spath);
2312 if ( !s_of && adsp && ad_meta_fileno(adsp) != -1 ) { /* META */
2313 ad_close(adsp, ADFLAGS_HF);
2315 if ( !d_of && addp && ad_meta_fileno(addp) != -1 ) {/* META */
2316 ad_close(addp, ADFLAGS_HF);