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"
53 /* the format for the finderinfo fields (from IM: Toolbox Essentials):
54 * field bytes subfield bytes
57 * ioFlFndrInfo 16 -> type 4 type field
58 * creator 4 creator field
59 * flags 2 finder flags:
61 * location 4 location in window
62 * folder 2 window that contains file
64 * ioFlXFndrInfo 16 -> iconID 2 icon id
66 * script 1 script system
68 * commentID 2 comment id
69 * putawayID 4 home directory id
72 const u_char ufinderi[ADEDLEN_FINDERI] = {
73 0, 0, 0, 0, 0, 0, 0, 0,
74 1, 0, 0, 0, 0, 0, 0, 0,
75 0, 0, 0, 0, 0, 0, 0, 0,
76 0, 0, 0, 0, 0, 0, 0, 0
79 static const u_char old_ufinderi[] = {
80 'T', 'E', 'X', 'T', 'U', 'N', 'I', 'X'
83 /* ----------------------
85 static int default_type(void *finder)
87 if (!memcmp(finder, ufinderi, 8) || !memcmp(finder, old_ufinderi, 8))
92 /* FIXME path : unix or mac name ? (for now it's unix name ) */
93 void *get_finderinfo(const struct vol *vol, const char *upath, struct adouble *adp, void *data, int islink)
96 void *ad_finder = NULL;
100 ad_finder = ad_entry(adp, ADEID_FINDERI);
103 memcpy(data, ad_finder, ADEDLEN_FINDERI);
105 if (default_type(ad_finder))
109 memcpy(data, ufinderi, ADEDLEN_FINDERI);
111 if (vol_inv_dots(vol) && *upath == '.') { /* make it invisible */
114 ashort = htons(FINDERINFO_INVISIBLE);
115 memcpy((char *)data + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
121 memcpy(&linkflag, (char *)data + FINDERINFO_FRFLAGOFF, 2);
122 linkflag |= htons(FINDERINFO_ISALIAS);
123 memcpy((char *)data + FINDERINFO_FRFLAGOFF, &linkflag, 2);
124 memcpy((char *)data + FINDERINFO_FRTYPEOFF,"slnk",4);
125 memcpy((char *)data + FINDERINFO_FRCREATOFF,"rhap",4);
129 /** Only enter if no appledouble information and no finder information found. */
130 if (chk_ext && (em = getextmap( upath ))) {
131 memcpy(data, em->em_type, sizeof( em->em_type ));
132 memcpy((char *)data + 4, em->em_creator, sizeof(em->em_creator));
137 /* ---------------------
139 char *set_name(const struct vol *vol, char *data, cnid_t pid, char *name, cnid_t id, u_int32_t utf8)
144 aint = strlen( name );
148 if (utf8_encoding()) {
149 /* but name is an utf8 mac name */
152 /* global static variable... */
154 if (!(u = mtoupath(vol, name, pid, 1)) || !(m = utompath(vol, u, id, 0))) {
163 if (aint > MACFILELEN)
170 if (aint > 255) /* FIXME safeguard, anyway if no ascii char it's game over*/
173 utf8 = vol->v_kTextEncoding;
174 memcpy(data, &utf8, sizeof(utf8));
175 data += sizeof(utf8);
178 memcpy(data, &temp, sizeof(temp));
179 data += sizeof(temp);
182 memcpy( data, src, aint );
192 * FIXME: PDINFO is UTF8 and doesn't need adp
194 #define PARAM_NEED_ADP(b) ((b) & ((1 << FILPBIT_ATTR) |\
195 (1 << FILPBIT_CDATE) |\
196 (1 << FILPBIT_MDATE) |\
197 (1 << FILPBIT_BDATE) |\
198 (1 << FILPBIT_FINFO) |\
199 (1 << FILPBIT_RFLEN) |\
200 (1 << FILPBIT_EXTRFLEN) |\
201 (1 << FILPBIT_PDINFO) |\
202 (1 << FILPBIT_FNUM) |\
203 (1 << FILPBIT_UNIXPR)))
205 /* -------------------------- */
206 uint32_t get_id(struct vol *vol, struct adouble *adp, const struct stat *st,
207 const cnid_t did, char *upath, const int len)
209 static int first = 1; /* mark if this func is called the first time */
211 u_int32_t dbcnid = CNID_INVALID;
214 if (vol->v_cdb != NULL) {
215 /* prime aint with what we think is the cnid, set did to zero for
216 catching moved files */
217 adcnid = ad_getid(adp, st->st_dev, st->st_ino, 0, vol->v_stamp);
219 dbcnid = cnid_add(vol->v_cdb, st, did, upath, len, adcnid);
220 /* Throw errors if cnid_add fails. */
221 if (dbcnid == CNID_INVALID) {
223 case CNID_ERR_CLOSE: /* the db is closed */
226 LOG(log_error, logtype_afpd, "get_id: Incorrect parameters passed to cnid_add");
227 afp_errno = AFPERR_PARAM;
230 afp_errno = AFPERR_PARAM;
233 /* Close CNID backend if "dbd" and switch to temp in-memory "tdb" */
234 /* we have to do it here for "dbd" because it uses "lazy opening" */
235 /* In order to not end in a loop somehow with goto restart below */
237 if (first && (strcmp(vol->v_cnidscheme, "dbd") == 0)) {
238 cnid_close(vol->v_cdb);
239 free(vol->v_cnidscheme);
240 vol->v_cnidscheme = strdup("tdb");
242 int flags = CNID_FLAG_MEMORY;
243 if ((vol->v_flags & AFPVOL_NODEV)) {
244 flags |= CNID_FLAG_NODEV;
246 LOG(log_error, logtype_afpd, "Reopen volume %s using in memory temporary CNID DB.",
248 vol->v_cdb = cnid_open(vol->v_path, vol->v_umask, "tdb", flags, NULL, NULL);
250 /* deactivate cnid caching/storing in AppleDouble files and set ro mode*/
251 vol->v_flags &= ~AFPVOL_CACHE;
252 vol->v_flags |= AFPVOL_RO;
254 /* kill ourself with SIGUSR2 aka msg pending */
255 setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB instead."
256 "Check server messages for details. Switching to read-only mode.");
257 kill(getpid(), SIGUSR2);
259 goto restart; /* not try again with the temp CNID db */
262 setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB failed too!"
263 "Check server messages for details, can't recover from this state!");
267 afp_errno = AFPERR_MISC;
271 else if (adp && (adcnid != dbcnid)) {
272 /* Update the ressource fork. For a folder adp is always null */
273 LOG(log_debug, logtype_afpd, "get_id: calling ad_setid. adcnid: %u, dbcnid: %u", htonl(adcnid), htonl(dbcnid));
274 if (ad_setid(adp, st->st_dev, st->st_ino, dbcnid, did, vol->v_stamp)) {
285 /* -------------------------- */
286 int getmetadata(struct vol *vol,
288 struct path *path, struct dir *dir,
289 char *buf, size_t *buflen, struct adouble *adp)
291 char *data, *l_nameoff = NULL, *upath;
292 char *utf_nameoff = NULL;
297 u_char achar, fdType[4];
302 upath = path->u_name;
306 if ( ((bitmap & ( (1 << FILPBIT_FINFO)|(1 << FILPBIT_LNAME)|(1 <<FILPBIT_PDINFO) ) ) && !path->m_name)
307 || (bitmap & ( (1 << FILPBIT_LNAME) ) && utf8_encoding()) /* FIXME should be m_name utf8 filename */
308 || (bitmap & (1 << FILPBIT_FNUM))) {
310 struct dir *cachedfile;
311 int len = strlen(upath);
312 if ((cachedfile = dircache_search_by_name(vol, dir, upath, len)) != NULL)
313 id = cachedfile->d_did;
315 id = get_id(vol, adp, st, dir->d_did, upath, len);
317 /* Add it to the cache */
318 LOG(log_debug, logtype_afpd, "getmetadata: caching: did:%u, \"%s\", cnid:%u",
319 ntohl(dir->d_did), upath, ntohl(id));
321 /* Get macname from unixname first */
322 if (path->m_name == NULL) {
323 if ((path->m_name = utompath(vol, upath, id, utf8_encoding())) == NULL) {
324 LOG(log_error, logtype_afpd, "getmetadata: utompath error");
329 if ((cachedfile = dir_new(path->m_name, upath, vol, dir->d_did, id, NULL)) == NULL) {
330 LOG(log_error, logtype_afpd, "getmetadata: error from dir_new");
333 if ((dircache_add(cachedfile)) != 0) {
334 LOG(log_error, logtype_afpd, "getmetadata: fatal dircache error");
342 if (id == CNID_INVALID)
346 path->m_name = utompath(vol, upath, id, utf8_encoding());
349 while ( bitmap != 0 ) {
350 while (( bitmap & 1 ) == 0 ) {
358 ad_getattr(adp, &ashort);
359 } else if (vol_inv_dots(vol) && *upath == '.') {
360 ashort = htons(ATTRBIT_INVISIBLE);
364 /* FIXME do we want a visual clue if the file is read only
367 accessmode( ".", &ma, dir , NULL);
368 if ((ma.ma_user & AR_UWRITE)) {
369 accessmode( upath, &ma, dir , st);
370 if (!(ma.ma_user & AR_UWRITE)) {
371 ashort |= htons(ATTRBIT_NOWRITE);
375 memcpy(data, &ashort, sizeof( ashort ));
376 data += sizeof( ashort );
377 LOG(log_debug, logtype_afpd, "metadata('%s'): AFP Attributes: %04x",
378 path->u_name, ntohs(ashort));
382 memcpy(data, &dir->d_did, sizeof( u_int32_t ));
383 data += sizeof( u_int32_t );
384 LOG(log_debug, logtype_afpd, "metadata('%s'): Parent DID: %u",
385 path->u_name, ntohl(dir->d_did));
389 if (!adp || (ad_getdate(adp, AD_DATE_CREATE, &aint) < 0))
390 aint = AD_DATE_FROM_UNIX(st->st_mtime);
391 memcpy(data, &aint, sizeof( aint ));
392 data += sizeof( aint );
396 if ( adp && (ad_getdate(adp, AD_DATE_MODIFY, &aint) == 0)) {
397 if ((st->st_mtime > AD_DATE_TO_UNIX(aint))) {
398 aint = AD_DATE_FROM_UNIX(st->st_mtime);
401 aint = AD_DATE_FROM_UNIX(st->st_mtime);
403 memcpy(data, &aint, sizeof( int ));
404 data += sizeof( int );
408 if (!adp || (ad_getdate(adp, AD_DATE_BACKUP, &aint) < 0))
409 aint = AD_DATE_START;
410 memcpy(data, &aint, sizeof( int ));
411 data += sizeof( int );
415 get_finderinfo(vol, upath, adp, (char *)data,S_ISLNK(st->st_mode));
416 data += ADEDLEN_FINDERI;
421 data += sizeof( u_int16_t );
425 memset(data, 0, sizeof(u_int16_t));
426 data += sizeof( u_int16_t );
430 memcpy(data, &id, sizeof( id ));
431 data += sizeof( id );
432 LOG(log_debug, logtype_afpd, "metadata('%s'): CNID: %u",
433 path->u_name, ntohl(id));
437 if (st->st_size > 0xffffffff)
440 aint = htonl( st->st_size );
441 memcpy(data, &aint, sizeof( aint ));
442 data += sizeof( aint );
447 if (adp->ad_rlen > 0xffffffff)
450 aint = htonl( adp->ad_rlen);
454 memcpy(data, &aint, sizeof( aint ));
455 data += sizeof( aint );
458 /* Current client needs ProDOS info block for this file.
459 Use simple heuristic and let the Mac "type" string tell
460 us what the PD file code should be. Everything gets a
461 subtype of 0x0000 unless the original value was hashed
462 to "pXYZ" when we created it. See IA, Ver 2.
463 <shirsch@adelphia.net> */
464 case FILPBIT_PDINFO :
465 if (afp_version >= 30) { /* UTF8 name */
466 utf8 = kTextEncodingUTF8;
468 data += sizeof( u_int16_t );
470 memcpy(data, &aint, sizeof( aint ));
471 data += sizeof( aint );
475 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
477 if ( memcmp( fdType, "TEXT", 4 ) == 0 ) {
481 else if ( memcmp( fdType, "PSYS", 4 ) == 0 ) {
485 else if ( memcmp( fdType, "PS16", 4 ) == 0 ) {
489 else if ( memcmp( fdType, "BINA", 4 ) == 0 ) {
493 else if ( fdType[0] == 'p' ) {
495 ashort = (fdType[2] * 256) + fdType[3];
509 memcpy(data, &ashort, sizeof( ashort ));
510 data += sizeof( ashort );
511 memset(data, 0, sizeof( ashort ));
512 data += sizeof( ashort );
515 case FILPBIT_EXTDFLEN:
516 aint = htonl(st->st_size >> 32);
517 memcpy(data, &aint, sizeof( aint ));
518 data += sizeof( aint );
519 aint = htonl(st->st_size);
520 memcpy(data, &aint, sizeof( aint ));
521 data += sizeof( aint );
523 case FILPBIT_EXTRFLEN:
526 aint = htonl(adp->ad_rlen >> 32);
527 memcpy(data, &aint, sizeof( aint ));
528 data += sizeof( aint );
530 aint = htonl(adp->ad_rlen);
531 memcpy(data, &aint, sizeof( aint ));
532 data += sizeof( aint );
534 case FILPBIT_UNIXPR :
535 /* accessmode may change st_mode with ACLs */
536 accessmode( upath, &ma, dir , st);
538 aint = htonl(st->st_uid);
539 memcpy( data, &aint, sizeof( aint ));
540 data += sizeof( aint );
541 aint = htonl(st->st_gid);
542 memcpy( data, &aint, sizeof( aint ));
543 data += sizeof( aint );
546 type == slnk indicates an OSX style symlink,
547 we have to add S_IFLNK to the mode, otherwise
548 10.3 clients freak out. */
552 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
553 if ( memcmp( fdType, "slnk", 4 ) == 0 ) {
559 memcpy( data, &aint, sizeof( aint ));
560 data += sizeof( aint );
562 *data++ = ma.ma_user;
563 *data++ = ma.ma_world;
564 *data++ = ma.ma_group;
565 *data++ = ma.ma_owner;
569 return( AFPERR_BITMAP );
575 ashort = htons( data - buf );
576 memcpy(l_nameoff, &ashort, sizeof( ashort ));
577 data = set_name(vol, data, dir->d_did, path->m_name, id, 0);
580 ashort = htons( data - buf );
581 memcpy(utf_nameoff, &ashort, sizeof( ashort ));
582 data = set_name(vol, data, dir->d_did, path->m_name, id, utf8);
584 *buflen = data - buf;
588 /* ----------------------- */
589 int getfilparams(struct vol *vol,
591 struct path *path, struct dir *dir,
592 char *buf, size_t *buflen )
594 struct adouble ad, *adp;
598 opened = PARAM_NEED_ADP(bitmap);
603 int flags = (bitmap & (1 << FILPBIT_ATTR))?ADFLAGS_OPENFORKS:0;
605 adp = of_ad(vol, path, &ad);
606 upath = path->u_name;
608 if ( ad_metadata( upath, flags|ADFLAGS_CREATE, adp) < 0 ) {
611 LOG(log_error, logtype_afpd, "getfilparams(%s): %s: check resource fork permission?",
612 upath, strerror(errno));
613 return AFPERR_ACCESS;
615 LOG(log_error, logtype_afpd, "getfilparams(%s): bad resource fork", upath);
624 rc = getmetadata(vol, bitmap, path, dir, buf, buflen, adp);
626 ad_close_metadata( adp);
632 /* ----------------------------- */
633 int afp_createfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
635 struct adouble ad, *adp;
638 struct ofork *of = NULL;
640 int creatf, did, openf, retvalue = AFP_OK;
646 creatf = (unsigned char) *ibuf++;
648 memcpy(&vid, ibuf, sizeof( vid ));
649 ibuf += sizeof( vid );
651 if (NULL == ( vol = getvolbyvid( vid )) ) {
652 return( AFPERR_PARAM );
655 if (vol->v_flags & AFPVOL_RO)
658 memcpy(&did, ibuf, sizeof( did));
659 ibuf += sizeof( did );
661 if (NULL == ( dir = dirlookup( vol, did )) ) {
665 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
666 return get_afp_errno(AFPERR_PARAM);
669 if ( *s_path->m_name == '\0' ) {
670 return( AFPERR_BADTYPE );
673 upath = s_path->u_name;
675 /* if upath is deleted we already in trouble anyway */
676 if ((of = of_findname(s_path))) {
679 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
683 /* on a hard create, fail if file exists and is open */
686 openf = O_RDWR|O_CREAT|O_TRUNC;
688 /* on a soft create, if the file is open then ad_open won't fail
689 because open syscall is not called
694 openf = O_RDWR|O_CREAT|O_EXCL;
697 if ( ad_open( upath, ADFLAGS_DF|ADFLAGS_HF|ADFLAGS_NOHF|ADFLAGS_CREATE,
698 openf, 0666, adp) < 0 ) {
702 case ENOENT : /* we were already in 'did folder' so chdir() didn't fail */
703 return ( AFPERR_NOOBJ );
705 return( AFPERR_EXIST );
707 return( AFPERR_ACCESS );
710 return( AFPERR_DFULL );
712 return( AFPERR_PARAM );
715 if ( ad_reso_fileno( adp ) == -1 ) { /* Hard META / HF */
716 /* on noadouble volumes, just creating the data fork is ok */
717 if (vol_noadouble(vol)) {
718 ad_close( adp, ADFLAGS_DF );
719 goto createfile_done;
721 /* FIXME with hard create on an existing file, we already
722 * corrupted the data file.
724 netatalk_unlink( upath );
725 ad_close( adp, ADFLAGS_DF );
726 return AFPERR_ACCESS;
729 path = s_path->m_name;
730 ad_setname(adp, path);
732 ad_close( adp, ADFLAGS_DF|ADFLAGS_HF );
738 if (vol->v_flags & AFPVOL_DROPBOX) {
739 retvalue = matchfile2dirperms(upath, vol, did);
741 #endif /* DROPKLUDGE */
743 setvoltime(obj, vol );
748 int afp_setfilparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
754 u_int16_t vid, bitmap;
759 memcpy(&vid, ibuf, sizeof( vid ));
760 ibuf += sizeof( vid );
761 if (NULL == ( vol = getvolbyvid( vid )) ) {
762 return( AFPERR_PARAM );
765 if (vol->v_flags & AFPVOL_RO)
768 memcpy(&did, ibuf, sizeof( did ));
769 ibuf += sizeof( did );
770 if (NULL == ( dir = dirlookup( vol, did )) ) {
771 return afp_errno; /* was AFPERR_NOOBJ */
774 memcpy(&bitmap, ibuf, sizeof( bitmap ));
775 bitmap = ntohs( bitmap );
776 ibuf += sizeof( bitmap );
778 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
779 return get_afp_errno(AFPERR_PARAM);
782 if (path_isadir(s_path)) {
783 return( AFPERR_BADTYPE ); /* it's a directory */
786 if ( s_path->st_errno != 0 ) {
787 return( AFPERR_NOOBJ );
790 if ((u_long)ibuf & 1 ) {
794 if (AFP_OK == ( rc = setfilparams(vol, s_path, bitmap, ibuf )) ) {
795 setvoltime(obj, vol );
802 * cf AFP3.0.pdf page 252 for change_mdate and change_parent_mdate logic
805 extern struct path Cur_Path;
807 int setfilparams(struct vol *vol,
808 struct path *path, u_int16_t f_bitmap, char *buf )
810 struct adouble ad, *adp;
812 int bit, isad = 1, err = AFP_OK;
814 u_char achar, *fdType, xyy[4]; /* uninitialized, OK 310105 */
815 u_int16_t ashort, bshort, oshort;
818 u_int16_t upriv_bit = 0;
822 int change_mdate = 0;
823 int change_parent_mdate = 0;
828 u_int16_t bitmap = f_bitmap;
829 u_int32_t cdate,bdate;
830 u_char finder_buf[32];
833 LOG(log_debug9, logtype_afpd, "begin setfilparams:");
836 adp = of_ad(vol, path, &ad);
837 upath = path->u_name;
839 if (!vol_unix_priv(vol) && check_access(upath, OPENACC_WR ) < 0) {
840 return AFPERR_ACCESS;
843 /* with unix priv maybe we have to change adouble file priv first */
845 while ( bitmap != 0 ) {
846 while (( bitmap & 1 ) == 0 ) {
853 memcpy(&ashort, buf, sizeof( ashort ));
854 buf += sizeof( ashort );
858 memcpy(&cdate, buf, sizeof(cdate));
859 buf += sizeof( cdate );
862 memcpy(&newdate, buf, sizeof( newdate ));
863 buf += sizeof( newdate );
867 memcpy(&bdate, buf, sizeof( bdate));
868 buf += sizeof( bdate );
872 memcpy(finder_buf, buf, 32 );
873 if (memcmp(buf,"slnkrhap",8)==0 && !S_ISLNK(path->st.st_mode)){
878 char buf[PATH_MAX+1];
879 if ((fp=open(path->u_name,O_RDONLY))>=0){
880 if ((len=read(fp,buf,PATH_MAX+1))){
881 if (unlink(path->u_name)==0){
883 erc = symlink(buf, path->u_name);
892 goto setfilparam_done;
897 case FILPBIT_UNIXPR :
898 if (!vol_unix_priv(vol)) {
899 /* this volume doesn't use unix priv */
905 change_parent_mdate = 1;
907 memcpy( &aint, buf, sizeof( aint ));
908 f_uid = ntohl (aint);
909 buf += sizeof( aint );
910 memcpy( &aint, buf, sizeof( aint ));
911 f_gid = ntohl (aint);
912 buf += sizeof( aint );
913 setfilowner(vol, f_uid, f_gid, path);
915 memcpy( &upriv, buf, sizeof( upriv ));
916 buf += sizeof( upriv );
917 upriv = ntohl (upriv);
918 if ((upriv & S_IWUSR)) {
919 setfilunixmode(vol, path, upriv);
926 case FILPBIT_PDINFO :
927 if (afp_version < 30) { /* else it's UTF8 name */
930 /* Keep special case to support crlf translations */
931 if ((unsigned int) achar == 0x04) {
932 fdType = (u_char *)"TEXT";
935 xyy[0] = ( u_char ) 'p';
946 /* break while loop */
955 /* second try with adouble open
957 if ( ad_open_metadata( upath, 0, O_CREAT, adp) < 0) {
958 LOG(log_debug, logtype_afpd, "setfilparams: ad_open_metadata error");
960 * For some things, we don't need an adouble header:
961 * - change of modification date
962 * - UNIX privs (Bug-ID #2863424)
964 if (!vol_noadouble(vol) && (f_bitmap & ~(1<<FILPBIT_MDATE | 1<<FILPBIT_UNIXPR))) {
965 LOG(log_debug, logtype_afpd, "setfilparams: need adouble access");
966 return AFPERR_ACCESS;
968 LOG(log_debug, logtype_afpd, "setfilparams: no adouble perms, but only FILPBIT_MDATE and/or FILPBIT_UNIXPR");
970 } else if ((ad_get_HF_flags( adp ) & O_CREAT) ) {
971 ad_setname(adp, path->m_name);
976 while ( bitmap != 0 ) {
977 while (( bitmap & 1 ) == 0 ) {
984 ad_getattr(adp, &bshort);
986 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
987 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
991 if ((bshort & htons(ATTRBIT_INVISIBLE)) != (oshort & htons(ATTRBIT_INVISIBLE)))
992 change_parent_mdate = 1;
993 ad_setattr(adp, bshort);
996 ad_setdate(adp, AD_DATE_CREATE, cdate);
1000 case FILPBIT_BDATE :
1001 ad_setdate(adp, AD_DATE_BACKUP, bdate);
1003 case FILPBIT_FINFO :
1004 if (default_type( ad_entry( adp, ADEID_FINDERI ))
1006 ((em = getextmap( path->m_name )) &&
1007 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1008 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1009 || ((em = getdefextmap()) &&
1010 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1011 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1013 memcpy(finder_buf, ufinderi, 8 );
1015 memcpy(ad_entry( adp, ADEID_FINDERI ), finder_buf, 32 );
1017 case FILPBIT_UNIXPR :
1019 setfilunixmode(vol, path, upriv);
1022 case FILPBIT_PDINFO :
1023 if (afp_version < 30) { /* else it's UTF8 name */
1024 memcpy(ad_entry( adp, ADEID_FINDERI ), fdType, 4 );
1025 memcpy(ad_entry( adp, ADEID_FINDERI ) + 4, "pdos", 4 );
1030 err = AFPERR_BITMAP;
1031 goto setfilparam_done;
1038 if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
1039 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1043 ad_setdate(adp, AD_DATE_MODIFY, newdate);
1044 ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
1050 ad_close_metadata( adp);
1054 if (change_parent_mdate && gettimeofday(&tv, NULL) == 0) {
1055 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1056 bitmap = 1<<FILPBIT_MDATE;
1057 setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
1061 LOG(log_debug9, logtype_afpd, "end setfilparams:");
1067 * renamefile and copyfile take the old and new unix pathnames
1068 * and the new mac name.
1070 * sdir_fd source dir fd to which src path is relative (for openat et al semantics)
1071 * passing -1 means this is not used, src path is a full path
1072 * src the source path
1073 * dst the dest filename in current dir
1074 * newname the dest mac name
1075 * adp adouble struct of src file, if open, or & zeroed one
1078 int renamefile(const struct vol *vol, int sdir_fd, char *src, char *dst, char *newname, struct adouble *adp)
1082 if ( unix_rename( sdir_fd, src, -1, dst ) < 0 ) {
1085 return( AFPERR_NOOBJ );
1088 return( AFPERR_ACCESS );
1090 return AFPERR_VLOCK;
1091 case EXDEV : /* Cross device move -- try copy */
1092 /* NOTE: with open file it's an error because after the copy we will
1093 * get two files, it's fixable for our process (eg reopen the new file, get the
1094 * locks, and so on. But it doesn't solve the case with a second process
1096 if (adp->ad_open_forks) {
1097 /* FIXME warning in syslog so admin'd know there's a conflict ?*/
1098 return AFPERR_OLOCK; /* little lie */
1100 if (AFP_OK != ( rc = copyfile(vol, vol, sdir_fd, src, dst, newname, NULL )) ) {
1101 /* on error copyfile delete dest */
1104 return deletefile(vol, sdir_fd, src, 0);
1106 return( AFPERR_PARAM );
1110 if (vol->vfs->vfs_renamefile(vol, sdir_fd, src, dst) < 0 ) {
1114 /* try to undo the data fork rename,
1115 * we know we are on the same device
1118 unix_rename(-1, dst, sdir_fd, src );
1119 /* return the first error */
1122 return AFPERR_NOOBJ;
1125 return AFPERR_ACCESS ;
1127 return AFPERR_VLOCK;
1129 return AFPERR_PARAM ;
1134 /* don't care if we can't open the newly renamed ressource fork
1136 if (!ad_open( dst, ADFLAGS_HF, O_RDWR, 0666, adp)) {
1137 ad_setname(adp, newname);
1139 ad_close( adp, ADFLAGS_HF );
1146 convert a Mac long name to an utf8 name,
1148 size_t mtoUTF8(const struct vol *vol, const char *src, size_t srclen, char *dest, size_t destlen)
1152 if ((size_t)-1 == (outlen = convert_string ( vol->v_maccharset, CH_UTF8_MAC, src, srclen, dest, destlen)) ) {
1158 /* ---------------- */
1159 int copy_path_name(const struct vol *vol, char *newname, char *ibuf)
1166 if ( type != 2 && !(afp_version >= 30 && type == 3) ) {
1172 if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
1173 if (afp_version >= 30) {
1174 /* convert it to UTF8
1176 if ((plen = mtoUTF8(vol, ibuf, plen, newname, AFPOBJ_TMPSIZ)) == (size_t)-1)
1180 strncpy( newname, ibuf, plen );
1181 newname[ plen ] = '\0';
1183 if (strlen(newname) != plen) {
1184 /* there's \0 in newname, e.g. it's a pathname not
1192 memcpy(&hint, ibuf, sizeof(hint));
1193 ibuf += sizeof(hint);
1195 memcpy(&len16, ibuf, sizeof(len16));
1196 ibuf += sizeof(len16);
1197 plen = ntohs(len16);
1200 if (plen > AFPOBJ_TMPSIZ) {
1203 strncpy( newname, ibuf, plen );
1204 newname[ plen ] = '\0';
1205 if (strlen(newname) != plen) {
1214 /* -----------------------------------
1216 int afp_copyfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1218 struct vol *s_vol, *d_vol;
1220 char *newname, *p, *upath;
1221 struct path *s_path;
1222 u_int32_t sdid, ddid;
1223 int err, retvalue = AFP_OK;
1224 u_int16_t svid, dvid;
1226 struct adouble ad, *adp;
1232 memcpy(&svid, ibuf, sizeof( svid ));
1233 ibuf += sizeof( svid );
1234 if (NULL == ( s_vol = getvolbyvid( svid )) ) {
1235 return( AFPERR_PARAM );
1238 memcpy(&sdid, ibuf, sizeof( sdid ));
1239 ibuf += sizeof( sdid );
1240 if (NULL == ( dir = dirlookup( s_vol, sdid )) ) {
1244 memcpy(&dvid, ibuf, sizeof( dvid ));
1245 ibuf += sizeof( dvid );
1246 memcpy(&ddid, ibuf, sizeof( ddid ));
1247 ibuf += sizeof( ddid );
1249 if (NULL == ( s_path = cname( s_vol, dir, &ibuf )) ) {
1250 return get_afp_errno(AFPERR_PARAM);
1252 if ( path_isadir(s_path) ) {
1253 return( AFPERR_BADTYPE );
1256 /* don't allow copies when the file is open.
1257 * XXX: the spec only calls for read/deny write access.
1258 * however, copyfile doesn't have any of that info,
1259 * and locks need to stay coherent. as a result,
1260 * we just balk if the file is opened already. */
1262 adp = of_ad(s_vol, s_path, &ad);
1264 if (ad_open(s_path->u_name , ADFLAGS_DF |ADFLAGS_HF | ADFLAGS_NOHF, O_RDONLY, 0, adp) < 0) {
1265 return AFPERR_DENYCONF;
1267 denyreadset = (getforkmode(adp, ADEID_DFORK, AD_FILELOCK_DENY_RD) != 0 ||
1268 getforkmode(adp, ADEID_RFORK, AD_FILELOCK_DENY_RD) != 0 );
1271 retvalue = AFPERR_DENYCONF;
1275 newname = obj->newtmp;
1276 strcpy( newname, s_path->m_name );
1278 p = ctoupath( s_vol, curdir, newname );
1280 retvalue = AFPERR_PARAM;
1285 /* FIXME svid != dvid && dvid's user can't read svid */
1287 if (NULL == ( d_vol = getvolbyvid( dvid )) ) {
1288 retvalue = AFPERR_PARAM;
1292 if (d_vol->v_flags & AFPVOL_RO) {
1293 retvalue = AFPERR_VLOCK;
1297 if (NULL == ( dir = dirlookup( d_vol, ddid )) ) {
1298 retvalue = afp_errno;
1302 if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) {
1303 retvalue = get_afp_errno(AFPERR_NOOBJ);
1307 if ( *s_path->m_name != '\0' ) {
1308 retvalue =path_error(s_path, AFPERR_NOOBJ);
1312 /* one of the handful of places that knows about the path type */
1313 if (copy_path_name(d_vol, newname, ibuf) < 0) {
1314 retvalue = AFPERR_PARAM;
1317 /* newname is always only a filename so curdir *is* its
1320 if (NULL == (upath = mtoupath(d_vol, newname, curdir->d_did, utf8_encoding()))) {
1321 retvalue =AFPERR_PARAM;
1325 if ( (err = copyfile(s_vol, d_vol, -1, p, upath , newname, adp)) < 0 ) {
1332 if (vol->v_flags & AFPVOL_DROPBOX) {
1333 retvalue=matchfile2dirperms(upath, vol, ddid); /* FIXME sdir or ddid */
1335 #endif /* DROPKLUDGE */
1337 setvoltime(obj, d_vol );
1340 ad_close( adp, ADFLAGS_DF |ADFLAGS_HF );
1344 /* ----------------------- */
1345 static int copy_all(const int dfd, const void *buf,
1351 LOG(log_debug9, logtype_afpd, "begin copy_all:");
1354 while (buflen > 0) {
1355 if ((cc = write(dfd, buf, buflen)) < 0) {
1367 LOG(log_debug9, logtype_afpd, "end copy_all:");
1373 /* --------------------------
1374 * copy only the fork data stream
1376 static int copy_fork(int eid, struct adouble *add, struct adouble *ads)
1383 if (eid == ADEID_DFORK) {
1384 sfd = ad_data_fileno(ads);
1385 dfd = ad_data_fileno(add);
1388 sfd = ad_reso_fileno(ads);
1389 dfd = ad_reso_fileno(add);
1392 if ((off_t)-1 == lseek(sfd, ad_getentryoff(ads, eid), SEEK_SET))
1395 if ((off_t)-1 == lseek(dfd, ad_getentryoff(add, eid), SEEK_SET))
1398 #if 0 /* ifdef SENDFILE_FLAVOR_LINUX */
1399 /* doesn't work With 2.6 FIXME, only check for EBADFD ? */
1403 #define BUF 128*1024*1024
1405 if (fstat(sfd, &st) == 0) {
1408 if ( offset >= st.st_size) {
1411 size = (st.st_size -offset > BUF)?BUF:st.st_size -offset;
1412 if ((cc = sys_sendfile(dfd, sfd, &offset, size)) < 0) {
1415 case EINVAL: /* there's no guarantee that all fs support sendfile */
1424 lseek(sfd, offset, SEEK_SET);
1428 if ((cc = read(sfd, filebuf, sizeof(filebuf))) < 0) {
1435 if (!cc || ((err = copy_all(dfd, filebuf, cc)) < 0)) {
1442 /* ----------------------------------
1443 * if newname is NULL (from directory.c) we don't want to copy the resource fork.
1444 * because we are doing it elsewhere.
1445 * currently if newname is NULL then adp is NULL.
1447 int copyfile(const struct vol *s_vol,
1448 const struct vol *d_vol,
1453 struct adouble *adp)
1455 struct adouble ads, add;
1462 LOG(log_debug, logtype_afpd, "copyfile(sfd:%d,s:'%s',d:'%s',n:'%s')",
1463 sfd, src, dst, newname);
1466 ad_init(&ads, s_vol->v_adouble, s_vol->v_ad_options);
1470 adflags = ADFLAGS_DF;
1472 adflags |= ADFLAGS_HF;
1475 if (ad_openat(sfd, src, adflags | ADFLAGS_NOHF, O_RDONLY, 0, adp) < 0) {
1480 if (ad_meta_fileno(adp) == -1 && ad_reso_fileno(adp) == -1) { /* META / HF */
1481 /* no resource fork, don't create one for dst file */
1482 adflags &= ~ADFLAGS_HF;
1485 stat_result = fstat(ad_data_fileno(adp), &st); /* saving stat exit code, thus saving us on one more stat later on */
1487 if (stat_result < 0) {
1488 /* unlikely but if fstat fails, the default file mode will be 0666. */
1489 st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
1492 ad_init(&add, d_vol->v_adouble, d_vol->v_ad_options);
1493 if (ad_open(dst , adflags, O_RDWR|O_CREAT|O_EXCL, st.st_mode, &add) < 0) {
1495 ad_close( adp, adflags );
1496 if (EEXIST != ret_err) {
1497 deletefile(d_vol, -1, dst, 0);
1500 return AFPERR_EXIST;
1504 * XXX if the source and the dest don't use the same resource type it's broken
1506 if (ad_reso_fileno(adp) == -1 || 0 == (err = copy_fork(ADEID_RFORK, &add, adp))){
1507 /* copy the data fork */
1508 if ((err = copy_fork(ADEID_DFORK, &add, adp)) == 0) {
1509 err = d_vol->vfs->vfs_copyfile(d_vol, sfd, src, dst);
1517 if (!ret_err && newname && (adflags & ADFLAGS_HF)) {
1518 /* set the new name in the resource fork */
1519 ad_copy_header(&add, adp);
1520 ad_setname(&add, newname);
1523 ad_close( adp, adflags );
1525 if (ad_close( &add, adflags ) <0) {
1530 deletefile(d_vol, -1, dst, 0);
1532 else if (stat_result == 0) {
1533 /* set dest modification date to src date */
1536 ut.actime = ut.modtime = st.st_mtime;
1538 /* FIXME netatalk doesn't use resource fork file date
1539 * but maybe we should set its modtime too.
1544 switch ( ret_err ) {
1550 return AFPERR_DFULL;
1552 return AFPERR_NOOBJ;
1554 return AFPERR_ACCESS;
1556 return AFPERR_VLOCK;
1558 return AFPERR_PARAM;
1562 /* -----------------------------------
1563 vol: not NULL delete cnid entry. then we are in curdir and file is a only filename
1564 checkAttrib: 1 check kFPDeleteInhibitBit (deletfile called by afp_delete)
1566 when deletefile is called we don't have lock on it, file is closed (for us)
1567 untrue if called by renamefile
1569 ad_open always try to open file RDWR first and ad_lock takes care of
1570 WRITE lock on read only file.
1573 static int check_attrib(struct adouble *adp)
1575 u_int16_t bshort = 0;
1577 ad_getattr(adp, &bshort);
1579 * Does kFPDeleteInhibitBit (bit 8) set?
1581 if ((bshort & htons(ATTRBIT_NODELETE))) {
1582 return AFPERR_OLOCK;
1584 if ((bshort & htons(ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
1590 * dirfd can be used for unlinkat semantics
1592 int deletefile(const struct vol *vol, int dirfd, char *file, int checkAttrib)
1595 struct adouble *adp = NULL;
1596 int adflags, err = AFP_OK;
1599 LOG(log_debug, logtype_afpd, "deletefile('%s')", file);
1601 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
1603 /* was EACCESS error try to get only metadata */
1604 /* we never want to create a resource fork here, we are going to delete it
1605 * moreover sometimes deletefile is called with a no existent file and
1606 * ad_open would create a 0 byte resource fork
1608 if ( ad_metadataat(dirfd, file, ADFLAGS_OPENFORKS, &ad) == 0 ) {
1609 if ((err = check_attrib(&ad))) {
1610 ad_close_metadata(&ad);
1617 /* try to open both forks at once */
1618 adflags = ADFLAGS_DF;
1619 if ( ad_openat(dirfd, file, adflags |ADFLAGS_HF|ADFLAGS_NOHF, O_RDONLY, 0, &ad ) < 0 ) {
1624 case EACCES: /* maybe it's a file with no write mode for us */
1625 break; /* was return AFPERR_ACCESS;*/
1638 if ( adp && ad_reso_fileno( adp ) != -1 ) { /* there's a resource fork */
1639 adflags |= ADFLAGS_HF;
1640 /* FIXME we have a pb here because we want to know if a file is open
1641 * there's a 'priority inversion' if you can't open the ressource fork RW
1642 * you can delete it if it's open because you can't get a write lock.
1644 * ADLOCK_FILELOCK means the whole ressource fork, not only after the
1647 * FIXME it doesn't work for RFORK open read only and fork open without deny mode
1649 if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
1655 if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
1657 } else if (!(err = vol->vfs->vfs_deletefile(vol, dirfd, file)) && !(err = netatalk_unlinkat(dirfd, file )) ) {
1659 if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file)))) {
1660 cnid_delete(vol->v_cdb, id);
1666 ad_close_metadata(&ad);
1669 ad_close( &ad, adflags ); /* ad_close removes locks if any */
1674 /* ------------------------------------ */
1675 /* return a file id */
1676 int afp_createid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1685 struct path *s_path;
1691 memcpy(&vid, ibuf, sizeof(vid));
1692 ibuf += sizeof(vid);
1694 if (NULL == ( vol = getvolbyvid( vid )) ) {
1695 return( AFPERR_PARAM);
1698 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1702 if (vol->v_flags & AFPVOL_RO)
1703 return AFPERR_VLOCK;
1705 memcpy(&did, ibuf, sizeof( did ));
1706 ibuf += sizeof(did);
1708 if (NULL == ( dir = dirlookup( vol, did )) ) {
1709 return afp_errno; /* was AFPERR_PARAM */
1712 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
1713 return get_afp_errno(AFPERR_NOOBJ); /* was AFPERR_PARAM */
1716 if ( path_isadir(s_path) ) {
1717 return( AFPERR_BADTYPE );
1720 upath = s_path->u_name;
1721 switch (s_path->st_errno) {
1723 break; /* success */
1726 return AFPERR_ACCESS;
1728 return AFPERR_NOOBJ;
1730 return AFPERR_PARAM;
1733 if ((id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath)))) {
1734 memcpy(rbuf, &id, sizeof(id));
1735 *rbuflen = sizeof(id);
1736 return AFPERR_EXISTID;
1739 if ((id = get_id(vol, NULL, st, did, upath, len)) != CNID_INVALID) {
1740 memcpy(rbuf, &id, sizeof(id));
1741 *rbuflen = sizeof(id);
1748 /* ------------------------------- */
1754 static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
1757 struct reenum *param = data;
1758 struct vol *vol = param->vol;
1759 cnid_t did = param->did;
1762 if ( lstat(de->d_name, &path.st)<0 )
1765 /* update or add to cnid */
1766 aint = cnid_add(vol->v_cdb, &path.st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */
1768 #if AD_VERSION > AD_VERSION1
1769 if (aint != CNID_INVALID && !S_ISDIR(path.st.st_mode)) {
1770 struct adouble ad, *adp;
1774 path.u_name = de->d_name;
1776 adp = of_ad(vol, &path, &ad);
1778 if ( ad_open_metadata( de->d_name, 0, 0, adp ) < 0 ) {
1781 if (ad_setid(adp, path.st.st_dev, path.st.st_ino, aint, did, vol->v_stamp)) {
1784 ad_close_metadata(adp);
1786 #endif /* AD_VERSION > AD_VERSION1 */
1791 /* --------------------
1792 * Ok the db is out of synch with the dir.
1793 * but if it's a deleted file we don't want to do it again and again.
1796 reenumerate_id(struct vol *vol, char *name, struct dir *dir)
1802 if (vol->v_cdb == NULL) {
1806 /* FIXME use of_statdir ? */
1807 if (lstat(name, &st)) {
1811 if (dirreenumerate(dir, &st)) {
1812 /* we already did it once and the dir haven't been modified */
1817 data.did = dir->d_did;
1818 if ((ret = for_each_dirent(vol, name, reenumerate_loop, (void *)&data)) >= 0) {
1819 setdiroffcnt(curdir, &st, ret);
1820 dir->d_flags |= DIRF_CNID;
1826 /* ------------------------------
1827 resolve a file id */
1828 int afp_resolveid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1837 u_int16_t vid, bitmap;
1839 static char buffer[12 + MAXPATHLEN + 1];
1840 int len = 12 + MAXPATHLEN + 1;
1845 memcpy(&vid, ibuf, sizeof(vid));
1846 ibuf += sizeof(vid);
1848 if (NULL == ( vol = getvolbyvid( vid )) ) {
1849 return( AFPERR_PARAM);
1852 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1856 memcpy(&id, ibuf, sizeof( id ));
1861 /* some MacOS versions after a catsearch do a *lot* of afp_resolveid with 0 */
1865 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1866 return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
1869 if (NULL == ( dir = dirlookup( vol, id )) ) {
1870 return AFPERR_NOID; /* idem AFPERR_PARAM */
1872 if (movecwd(vol, dir) < 0) {
1876 return AFPERR_ACCESS;
1880 return AFPERR_PARAM;
1884 memset(&path, 0, sizeof(path));
1885 path.u_name = upath;
1886 if ( of_stat(&path) < 0 ) {
1888 /* with nfs and our working directory is deleted */
1889 if (errno == ESTALE) {
1893 if ( errno == ENOENT && !retry) {
1894 /* cnid db is out of sync, reenumerate the directory and update ids */
1895 reenumerate_id(vol, ".", dir);
1903 return AFPERR_ACCESS;
1907 return AFPERR_PARAM;
1911 /* directories are bad */
1912 if (S_ISDIR(path.st.st_mode)) {
1913 /* OS9 and OSX don't return the same error code */
1914 return (afp_version >=30)?AFPERR_NOID:AFPERR_BADTYPE;
1917 memcpy(&bitmap, ibuf, sizeof(bitmap));
1918 bitmap = ntohs( bitmap );
1919 if (NULL == (path.m_name = utompath(vol, upath, cnid, utf8_encoding()))) {
1923 if (AFP_OK != (err = getfilparams(vol, bitmap, &path , curdir,
1924 rbuf + sizeof(bitmap), &buflen))) {
1927 *rbuflen = buflen + sizeof(bitmap);
1928 memcpy(rbuf, ibuf, sizeof(bitmap));
1933 /* ------------------------------ */
1934 int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1944 static char buffer[12 + MAXPATHLEN + 1];
1945 int len = 12 + MAXPATHLEN + 1;
1950 memcpy(&vid, ibuf, sizeof(vid));
1951 ibuf += sizeof(vid);
1953 if (NULL == ( vol = getvolbyvid( vid )) ) {
1954 return( AFPERR_PARAM);
1957 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1961 if (vol->v_flags & AFPVOL_RO)
1962 return AFPERR_VLOCK;
1964 memcpy(&id, ibuf, sizeof( id ));
1968 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1972 if (NULL == ( dir = dirlookup( vol, id )) ) {
1973 if (afp_errno == AFPERR_NOOBJ) {
1977 return( AFPERR_PARAM );
1981 if ((movecwd(vol, dir) < 0) || (lstat(upath, &st) < 0)) {
1985 return AFPERR_ACCESS;
1990 /* still try to delete the id */
1994 return AFPERR_PARAM;
1997 else if (S_ISDIR(st.st_mode)) /* directories are bad */
1998 return AFPERR_BADTYPE;
2001 if (cnid_delete(vol->v_cdb, fileid)) {
2004 return AFPERR_VLOCK;
2007 return AFPERR_ACCESS;
2009 return AFPERR_PARAM;
2016 /* ------------------------------ */
2017 static struct adouble *find_adouble(struct path *path, struct ofork **of, struct adouble *adp)
2021 if (path->st_errno) {
2022 switch (path->st_errno) {
2024 afp_errno = AFPERR_NOID;
2028 afp_errno = AFPERR_ACCESS;
2031 afp_errno = AFPERR_PARAM;
2036 /* we use file_access both for legacy Mac perm and
2037 * for unix privilege, rename will take care of folder perms
2039 if (file_access(path, OPENACC_WR ) < 0) {
2040 afp_errno = AFPERR_ACCESS;
2044 if ((*of = of_findname(path))) {
2045 /* reuse struct adouble so it won't break locks */
2049 ret = ad_open( path->u_name, ADFLAGS_HF, O_RDONLY, 0, adp);
2051 if ( !ret && ad_reso_fileno(adp) != -1 && !(adp->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
2053 * The user must have the Read & Write privilege for both files in order to use this command.
2055 ad_close(adp, ADFLAGS_HF);
2056 afp_errno = AFPERR_ACCESS;
2063 #define APPLETEMP ".AppleTempXXXXXX"
2065 int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
2067 struct stat srcst, destst;
2069 struct dir *dir, *sdir;
2070 char *spath, temp[17], *p;
2071 char *supath, *upath;
2076 struct adouble *adsp = NULL;
2077 struct adouble *addp = NULL;
2078 struct ofork *s_of = NULL;
2079 struct ofork *d_of = NULL;
2092 memcpy(&vid, ibuf, sizeof(vid));
2093 ibuf += sizeof(vid);
2095 if (NULL == ( vol = getvolbyvid( vid )) ) {
2096 return( AFPERR_PARAM);
2099 if ((vol->v_flags & AFPVOL_RO))
2100 return AFPERR_VLOCK;
2102 /* source and destination dids */
2103 memcpy(&sid, ibuf, sizeof(sid));
2104 ibuf += sizeof(sid);
2105 memcpy(&did, ibuf, sizeof(did));
2106 ibuf += sizeof(did);
2109 if (NULL == (dir = dirlookup( vol, sid )) ) {
2110 return afp_errno; /* was AFPERR_PARAM */
2113 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2114 return get_afp_errno(AFPERR_NOOBJ);
2117 if ( path_isadir(path) ) {
2118 return AFPERR_BADTYPE; /* it's a dir */
2121 /* save some stuff */
2124 spath = obj->oldtmp;
2125 supath = obj->newtmp;
2126 strcpy(spath, path->m_name);
2127 strcpy(supath, path->u_name); /* this is for the cnid changing */
2128 p = absupath( vol, sdir, supath);
2130 /* pathname too long */
2131 return AFPERR_PARAM ;
2134 ad_init(&ads, vol->v_adouble, vol->v_ad_options);
2135 if (!(adsp = find_adouble( path, &s_of, &ads))) {
2139 /* ***** from here we may have resource fork open **** */
2141 /* look for the source cnid. if it doesn't exist, don't worry about
2143 sid = cnid_lookup(vol->v_cdb, &srcst, sdir->d_did, supath,slen = strlen(supath));
2145 if (NULL == ( dir = dirlookup( vol, did )) ) {
2146 err = afp_errno; /* was AFPERR_PARAM */
2147 goto err_exchangefile;
2150 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2151 err = get_afp_errno(AFPERR_NOOBJ);
2152 goto err_exchangefile;
2155 if ( path_isadir(path) ) {
2156 err = AFPERR_BADTYPE;
2157 goto err_exchangefile;
2160 /* FPExchangeFiles is the only call that can return the SameObj
2162 if ((curdir == sdir) && strcmp(spath, path->m_name) == 0) {
2163 err = AFPERR_SAMEOBJ;
2164 goto err_exchangefile;
2167 ad_init(&add, vol->v_adouble, vol->v_ad_options);
2168 if (!(addp = find_adouble( path, &d_of, &add))) {
2170 goto err_exchangefile;
2174 /* they are not on the same device and at least one is open
2175 * FIXME broken for for crossdev and adouble v2
2178 crossdev = (srcst.st_dev != destst.st_dev);
2179 if (/* (d_of || s_of) && */ crossdev) {
2181 goto err_exchangefile;
2184 /* look for destination id. */
2185 upath = path->u_name;
2186 did = cnid_lookup(vol->v_cdb, &destst, curdir->d_did, upath, dlen = strlen(upath));
2188 /* construct a temp name.
2189 * NOTE: the temp file will be in the dest file's directory. it
2190 * will also be inaccessible from AFP. */
2191 memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
2192 if (!mktemp(temp)) {
2194 goto err_exchangefile;
2198 /* FIXME we need to close fork for copy, both s_of and d_of are null */
2199 ad_close(adsp, ADFLAGS_HF);
2200 ad_close(addp, ADFLAGS_HF);
2203 /* now, quickly rename the file. we error if we can't. */
2204 if ((err = renamefile(vol, -1, p, temp, temp, adsp)) != AFP_OK)
2205 goto err_exchangefile;
2206 of_rename(vol, s_of, sdir, spath, curdir, temp);
2208 /* rename destination to source */
2209 if ((err = renamefile(vol, -1, upath, p, spath, addp)) != AFP_OK)
2210 goto err_src_to_tmp;
2211 of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
2213 /* rename temp to destination */
2214 if ((err = renamefile(vol, -1, temp, upath, path->m_name, adsp)) != AFP_OK)
2215 goto err_dest_to_src;
2216 of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
2218 /* id's need switching. src -> dest and dest -> src.
2219 * we need to re-stat() if it was a cross device copy.
2222 cnid_delete(vol->v_cdb, sid);
2225 cnid_delete(vol->v_cdb, did);
2227 if ((did && ( (crossdev && lstat( upath, &srcst) < 0) ||
2228 cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
2230 (sid && ( (crossdev && lstat(p, &destst) < 0) ||
2231 cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
2236 err = AFPERR_ACCESS;
2241 goto err_temp_to_dest;
2244 /* here we need to reopen if crossdev */
2245 if (sid && ad_setid(addp, destst.st_dev, destst.st_ino, sid, sdir->d_did, vol->v_stamp))
2250 if (did && ad_setid(adsp, srcst.st_dev, srcst.st_ino, did, curdir->d_did, vol->v_stamp))
2255 /* change perms, src gets dest perm and vice versa */
2260 LOG(log_error, logtype_afpd, "seteuid failed %s", strerror(errno));
2261 err = AFP_OK; /* ignore error */
2262 goto err_temp_to_dest;
2266 * we need to exchange ACL entries as well
2268 /* exchange_acls(vol, p, upath); */
2273 path->m_name = NULL;
2274 path->u_name = upath;
2276 setfilunixmode(vol, path, destst.st_mode);
2277 setfilowner(vol, destst.st_uid, destst.st_gid, path);
2284 setfilunixmode(vol, path, srcst.st_mode);
2285 setfilowner(vol, srcst.st_uid, srcst.st_gid, path);
2287 if ( setegid(gid) < 0 || seteuid(uid) < 0) {
2288 LOG(log_error, logtype_afpd, "can't seteuid back %s", strerror(errno));
2293 goto err_exchangefile;
2295 /* all this stuff is so that we can unwind a failed operation
2298 /* rename dest to temp */
2299 renamefile(vol, -1, upath, temp, temp, adsp);
2300 of_rename(vol, s_of, curdir, upath, curdir, temp);
2303 /* rename source back to dest */
2304 renamefile(vol, -1, p, upath, path->m_name, addp);
2305 of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
2308 /* rename temp back to source */
2309 renamefile(vol, -1, temp, p, spath, adsp);
2310 of_rename(vol, s_of, curdir, temp, sdir, spath);
2313 if ( !s_of && adsp && ad_meta_fileno(adsp) != -1 ) { /* META */
2314 ad_close(adsp, ADFLAGS_HF);
2316 if ( !d_of && addp && ad_meta_fileno(addp) != -1 ) {/* META */
2317 ad_close(addp, ADFLAGS_HF);