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)))
206 * @brief Get CNID for did/upath args both from database and adouble file
208 * 1. Get the objects CNID as stored in its adouble file
209 * 2. Get the objects CNID from the database
210 * 3. If there's a problem with a "dbd" database, fallback to "tdb" in memory
211 * 4. In case 2 and 3 differ, store 3 in the adouble file
213 * @param vol (rw) volume
214 * @param adp (rw) adouble struct of object upath, might be NULL
215 * @param st (r) stat of upath, must NOT be NULL
216 * @param did (r) parent CNID of upath
217 * @param upath (r) name of object
218 * @param len (r) strlen of upath
220 uint32_t get_id(struct vol *vol,
222 const struct stat *st,
227 static int first = 1; /* mark if this func is called the first time */
229 u_int32_t dbcnid = CNID_INVALID;
232 if (vol->v_cdb != NULL) {
233 /* prime aint with what we think is the cnid, set did to zero for
234 catching moved files */
235 adcnid = ad_getid(adp, st->st_dev, st->st_ino, 0, vol->v_stamp); /* (1) */
237 dbcnid = cnid_add(vol->v_cdb, st, did, upath, len, adcnid); /* (2) */
238 /* Throw errors if cnid_add fails. */
239 if (dbcnid == CNID_INVALID) {
241 case CNID_ERR_CLOSE: /* the db is closed */
244 LOG(log_error, logtype_afpd, "get_id: Incorrect parameters passed to cnid_add");
245 afp_errno = AFPERR_PARAM;
248 afp_errno = AFPERR_PARAM;
251 /* Close CNID backend if "dbd" and switch to temp in-memory "tdb" */
252 /* we have to do it here for "dbd" because it uses "lazy opening" */
253 /* In order to not end in a loop somehow with goto restart below */
255 if (first && (strcmp(vol->v_cnidscheme, "dbd") == 0)) { /* (3) */
256 cnid_close(vol->v_cdb);
257 free(vol->v_cnidscheme);
258 vol->v_cnidscheme = strdup("tdb");
260 int flags = CNID_FLAG_MEMORY;
261 if ((vol->v_flags & AFPVOL_NODEV)) {
262 flags |= CNID_FLAG_NODEV;
264 LOG(log_error, logtype_afpd, "Reopen volume %s using in memory temporary CNID DB.",
266 vol->v_cdb = cnid_open(vol->v_path, vol->v_umask, "tdb", flags, NULL, NULL);
268 /* deactivate cnid caching/storing in AppleDouble files and set ro mode*/
269 vol->v_flags &= ~AFPVOL_CACHE;
270 vol->v_flags |= AFPVOL_RO;
272 /* kill ourself with SIGUSR2 aka msg pending */
273 setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB instead."
274 "Check server messages for details. Switching to read-only mode.");
275 kill(getpid(), SIGUSR2);
277 goto restart; /* not try again with the temp CNID db */
280 setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB failed too!"
281 "Check server messages for details, can't recover from this state!");
285 afp_errno = AFPERR_MISC;
289 else if (adp && (adcnid != dbcnid)) { /* 4 */
290 /* Update the ressource fork. For a folder adp is always null */
291 LOG(log_note, logtype_afpd, "get_id: calling ad_setid(old: %u, new: %u)",
292 htonl(adcnid), htonl(dbcnid));
293 if (ad_setid(adp, st->st_dev, st->st_ino, dbcnid, did, vol->v_stamp)) {
304 /* -------------------------- */
305 int getmetadata(struct vol *vol,
307 struct path *path, struct dir *dir,
308 char *buf, size_t *buflen, struct adouble *adp)
310 char *data, *l_nameoff = NULL, *upath;
311 char *utf_nameoff = NULL;
316 u_char achar, fdType[4];
321 upath = path->u_name;
325 if ( ((bitmap & ( (1 << FILPBIT_FINFO)|(1 << FILPBIT_LNAME)|(1 <<FILPBIT_PDINFO) ) ) && !path->m_name)
326 || (bitmap & ( (1 << FILPBIT_LNAME) ) && utf8_encoding()) /* FIXME should be m_name utf8 filename */
327 || (bitmap & (1 << FILPBIT_FNUM))) {
329 struct dir *cachedfile;
330 int len = strlen(upath);
331 if ((cachedfile = dircache_search_by_name(vol, dir, upath, len)) != NULL)
332 id = cachedfile->d_did;
334 id = get_id(vol, adp, st, dir->d_did, upath, len);
336 /* Add it to the cache */
337 LOG(log_debug, logtype_afpd, "getmetadata: caching: did:%u, \"%s\", cnid:%u",
338 ntohl(dir->d_did), upath, ntohl(id));
340 /* Get macname from unixname first */
341 if (path->m_name == NULL) {
342 if ((path->m_name = utompath(vol, upath, id, utf8_encoding())) == NULL) {
343 LOG(log_error, logtype_afpd, "getmetadata: utompath error");
348 if ((cachedfile = dir_new(path->m_name, upath, vol, dir->d_did, id, NULL)) == NULL) {
349 LOG(log_error, logtype_afpd, "getmetadata: error from dir_new");
352 if ((dircache_add(cachedfile)) != 0) {
353 LOG(log_error, logtype_afpd, "getmetadata: fatal dircache error");
361 if (id == CNID_INVALID)
365 path->m_name = utompath(vol, upath, id, utf8_encoding());
368 while ( bitmap != 0 ) {
369 while (( bitmap & 1 ) == 0 ) {
377 ad_getattr(adp, &ashort);
378 } else if (vol_inv_dots(vol) && *upath == '.') {
379 ashort = htons(ATTRBIT_INVISIBLE);
383 /* FIXME do we want a visual clue if the file is read only
386 accessmode( ".", &ma, dir , NULL);
387 if ((ma.ma_user & AR_UWRITE)) {
388 accessmode( upath, &ma, dir , st);
389 if (!(ma.ma_user & AR_UWRITE)) {
390 ashort |= htons(ATTRBIT_NOWRITE);
394 memcpy(data, &ashort, sizeof( ashort ));
395 data += sizeof( ashort );
396 LOG(log_debug, logtype_afpd, "metadata('%s'): AFP Attributes: %04x",
397 path->u_name, ntohs(ashort));
401 memcpy(data, &dir->d_did, sizeof( u_int32_t ));
402 data += sizeof( u_int32_t );
403 LOG(log_debug, logtype_afpd, "metadata('%s'): Parent DID: %u",
404 path->u_name, ntohl(dir->d_did));
408 if (!adp || (ad_getdate(adp, AD_DATE_CREATE, &aint) < 0))
409 aint = AD_DATE_FROM_UNIX(st->st_mtime);
410 memcpy(data, &aint, sizeof( aint ));
411 data += sizeof( aint );
415 if ( adp && (ad_getdate(adp, AD_DATE_MODIFY, &aint) == 0)) {
416 if ((st->st_mtime > AD_DATE_TO_UNIX(aint))) {
417 aint = AD_DATE_FROM_UNIX(st->st_mtime);
420 aint = AD_DATE_FROM_UNIX(st->st_mtime);
422 memcpy(data, &aint, sizeof( int ));
423 data += sizeof( int );
427 if (!adp || (ad_getdate(adp, AD_DATE_BACKUP, &aint) < 0))
428 aint = AD_DATE_START;
429 memcpy(data, &aint, sizeof( int ));
430 data += sizeof( int );
434 get_finderinfo(vol, upath, adp, (char *)data,S_ISLNK(st->st_mode));
435 data += ADEDLEN_FINDERI;
440 data += sizeof( u_int16_t );
444 memset(data, 0, sizeof(u_int16_t));
445 data += sizeof( u_int16_t );
449 memcpy(data, &id, sizeof( id ));
450 data += sizeof( id );
451 LOG(log_debug, logtype_afpd, "metadata('%s'): CNID: %u",
452 path->u_name, ntohl(id));
456 if (st->st_size > 0xffffffff)
459 aint = htonl( st->st_size );
460 memcpy(data, &aint, sizeof( aint ));
461 data += sizeof( aint );
466 if (adp->ad_rlen > 0xffffffff)
469 aint = htonl( adp->ad_rlen);
473 memcpy(data, &aint, sizeof( aint ));
474 data += sizeof( aint );
477 /* Current client needs ProDOS info block for this file.
478 Use simple heuristic and let the Mac "type" string tell
479 us what the PD file code should be. Everything gets a
480 subtype of 0x0000 unless the original value was hashed
481 to "pXYZ" when we created it. See IA, Ver 2.
482 <shirsch@adelphia.net> */
483 case FILPBIT_PDINFO :
484 if (afp_version >= 30) { /* UTF8 name */
485 utf8 = kTextEncodingUTF8;
487 data += sizeof( u_int16_t );
489 memcpy(data, &aint, sizeof( aint ));
490 data += sizeof( aint );
494 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
496 if ( memcmp( fdType, "TEXT", 4 ) == 0 ) {
500 else if ( memcmp( fdType, "PSYS", 4 ) == 0 ) {
504 else if ( memcmp( fdType, "PS16", 4 ) == 0 ) {
508 else if ( memcmp( fdType, "BINA", 4 ) == 0 ) {
512 else if ( fdType[0] == 'p' ) {
514 ashort = (fdType[2] * 256) + fdType[3];
528 memcpy(data, &ashort, sizeof( ashort ));
529 data += sizeof( ashort );
530 memset(data, 0, sizeof( ashort ));
531 data += sizeof( ashort );
534 case FILPBIT_EXTDFLEN:
535 aint = htonl(st->st_size >> 32);
536 memcpy(data, &aint, sizeof( aint ));
537 data += sizeof( aint );
538 aint = htonl(st->st_size);
539 memcpy(data, &aint, sizeof( aint ));
540 data += sizeof( aint );
542 case FILPBIT_EXTRFLEN:
545 aint = htonl(adp->ad_rlen >> 32);
546 memcpy(data, &aint, sizeof( aint ));
547 data += sizeof( aint );
549 aint = htonl(adp->ad_rlen);
550 memcpy(data, &aint, sizeof( aint ));
551 data += sizeof( aint );
553 case FILPBIT_UNIXPR :
554 /* accessmode may change st_mode with ACLs */
555 accessmode( upath, &ma, dir , st);
557 aint = htonl(st->st_uid);
558 memcpy( data, &aint, sizeof( aint ));
559 data += sizeof( aint );
560 aint = htonl(st->st_gid);
561 memcpy( data, &aint, sizeof( aint ));
562 data += sizeof( aint );
565 type == slnk indicates an OSX style symlink,
566 we have to add S_IFLNK to the mode, otherwise
567 10.3 clients freak out. */
571 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
572 if ( memcmp( fdType, "slnk", 4 ) == 0 ) {
578 memcpy( data, &aint, sizeof( aint ));
579 data += sizeof( aint );
581 *data++ = ma.ma_user;
582 *data++ = ma.ma_world;
583 *data++ = ma.ma_group;
584 *data++ = ma.ma_owner;
588 return( AFPERR_BITMAP );
594 ashort = htons( data - buf );
595 memcpy(l_nameoff, &ashort, sizeof( ashort ));
596 data = set_name(vol, data, dir->d_did, path->m_name, id, 0);
599 ashort = htons( data - buf );
600 memcpy(utf_nameoff, &ashort, sizeof( ashort ));
601 data = set_name(vol, data, dir->d_did, path->m_name, id, utf8);
603 *buflen = data - buf;
607 /* ----------------------- */
608 int getfilparams(struct vol *vol,
610 struct path *path, struct dir *dir,
611 char *buf, size_t *buflen )
613 struct adouble ad, *adp;
617 opened = PARAM_NEED_ADP(bitmap);
622 int flags = (bitmap & (1 << FILPBIT_ATTR))?ADFLAGS_OPENFORKS:0;
624 adp = of_ad(vol, path, &ad);
625 upath = path->u_name;
627 if ( ad_metadata( upath, flags|ADFLAGS_CREATE, adp) < 0 ) {
630 LOG(log_error, logtype_afpd, "getfilparams(%s): %s: check resource fork permission?",
631 upath, strerror(errno));
632 return AFPERR_ACCESS;
634 LOG(log_error, logtype_afpd, "getfilparams(%s): bad resource fork", upath);
643 rc = getmetadata(vol, bitmap, path, dir, buf, buflen, adp);
645 ad_close_metadata( adp);
651 /* ----------------------------- */
652 int afp_createfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
654 struct adouble ad, *adp;
657 struct ofork *of = NULL;
659 int creatf, did, openf, retvalue = AFP_OK;
665 creatf = (unsigned char) *ibuf++;
667 memcpy(&vid, ibuf, sizeof( vid ));
668 ibuf += sizeof( vid );
670 if (NULL == ( vol = getvolbyvid( vid )) ) {
671 return( AFPERR_PARAM );
674 if (vol->v_flags & AFPVOL_RO)
677 memcpy(&did, ibuf, sizeof( did));
678 ibuf += sizeof( did );
680 if (NULL == ( dir = dirlookup( vol, did )) ) {
684 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
685 return get_afp_errno(AFPERR_PARAM);
688 if ( *s_path->m_name == '\0' ) {
689 return( AFPERR_BADTYPE );
692 upath = s_path->u_name;
694 /* if upath is deleted we already in trouble anyway */
695 if ((of = of_findname(s_path))) {
698 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
702 /* on a hard create, fail if file exists and is open */
705 openf = O_RDWR|O_CREAT|O_TRUNC;
707 /* on a soft create, if the file is open then ad_open won't fail
708 because open syscall is not called
713 openf = O_RDWR|O_CREAT|O_EXCL;
716 if ( ad_open( upath, ADFLAGS_DF|ADFLAGS_HF|ADFLAGS_NOHF|ADFLAGS_CREATE,
717 openf, 0666, adp) < 0 ) {
721 case ENOENT : /* we were already in 'did folder' so chdir() didn't fail */
722 return ( AFPERR_NOOBJ );
724 return( AFPERR_EXIST );
726 return( AFPERR_ACCESS );
729 return( AFPERR_DFULL );
731 return( AFPERR_PARAM );
734 if ( ad_reso_fileno( adp ) == -1 ) { /* Hard META / HF */
735 /* on noadouble volumes, just creating the data fork is ok */
736 if (vol_noadouble(vol)) {
737 ad_close( adp, ADFLAGS_DF );
738 goto createfile_done;
740 /* FIXME with hard create on an existing file, we already
741 * corrupted the data file.
743 netatalk_unlink( upath );
744 ad_close( adp, ADFLAGS_DF );
745 return AFPERR_ACCESS;
748 path = s_path->m_name;
749 ad_setname(adp, path);
751 ad_close( adp, ADFLAGS_DF|ADFLAGS_HF );
757 if (vol->v_flags & AFPVOL_DROPBOX) {
758 retvalue = matchfile2dirperms(upath, vol, did);
760 #endif /* DROPKLUDGE */
762 setvoltime(obj, vol );
767 int afp_setfilparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
773 u_int16_t vid, bitmap;
778 memcpy(&vid, ibuf, sizeof( vid ));
779 ibuf += sizeof( vid );
780 if (NULL == ( vol = getvolbyvid( vid )) ) {
781 return( AFPERR_PARAM );
784 if (vol->v_flags & AFPVOL_RO)
787 memcpy(&did, ibuf, sizeof( did ));
788 ibuf += sizeof( did );
789 if (NULL == ( dir = dirlookup( vol, did )) ) {
790 return afp_errno; /* was AFPERR_NOOBJ */
793 memcpy(&bitmap, ibuf, sizeof( bitmap ));
794 bitmap = ntohs( bitmap );
795 ibuf += sizeof( bitmap );
797 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
798 return get_afp_errno(AFPERR_PARAM);
801 if (path_isadir(s_path)) {
802 return( AFPERR_BADTYPE ); /* it's a directory */
805 if ( s_path->st_errno != 0 ) {
806 return( AFPERR_NOOBJ );
809 if ((u_long)ibuf & 1 ) {
813 if (AFP_OK == ( rc = setfilparams(vol, s_path, bitmap, ibuf )) ) {
814 setvoltime(obj, vol );
821 * cf AFP3.0.pdf page 252 for change_mdate and change_parent_mdate logic
824 extern struct path Cur_Path;
826 int setfilparams(struct vol *vol,
827 struct path *path, u_int16_t f_bitmap, char *buf )
829 struct adouble ad, *adp;
831 int bit, isad = 1, err = AFP_OK;
833 u_char achar, *fdType, xyy[4]; /* uninitialized, OK 310105 */
834 u_int16_t ashort, bshort, oshort;
837 u_int16_t upriv_bit = 0;
841 int change_mdate = 0;
842 int change_parent_mdate = 0;
847 u_int16_t bitmap = f_bitmap;
848 u_int32_t cdate,bdate;
849 u_char finder_buf[32];
852 LOG(log_debug9, logtype_afpd, "begin setfilparams:");
855 adp = of_ad(vol, path, &ad);
856 upath = path->u_name;
858 if (!vol_unix_priv(vol) && check_access(upath, OPENACC_WR ) < 0) {
859 return AFPERR_ACCESS;
862 /* with unix priv maybe we have to change adouble file priv first */
864 while ( bitmap != 0 ) {
865 while (( bitmap & 1 ) == 0 ) {
872 memcpy(&ashort, buf, sizeof( ashort ));
873 buf += sizeof( ashort );
877 memcpy(&cdate, buf, sizeof(cdate));
878 buf += sizeof( cdate );
881 memcpy(&newdate, buf, sizeof( newdate ));
882 buf += sizeof( newdate );
886 memcpy(&bdate, buf, sizeof( bdate));
887 buf += sizeof( bdate );
891 memcpy(finder_buf, buf, 32 );
892 if (memcmp(buf,"slnkrhap",8)==0 && !S_ISLNK(path->st.st_mode)){
897 char buf[PATH_MAX+1];
898 if ((fp=open(path->u_name,O_RDONLY))>=0){
899 if ((len=read(fp,buf,PATH_MAX+1))){
900 if (unlink(path->u_name)==0){
902 erc = symlink(buf, path->u_name);
911 goto setfilparam_done;
916 case FILPBIT_UNIXPR :
917 if (!vol_unix_priv(vol)) {
918 /* this volume doesn't use unix priv */
924 change_parent_mdate = 1;
926 memcpy( &aint, buf, sizeof( aint ));
927 f_uid = ntohl (aint);
928 buf += sizeof( aint );
929 memcpy( &aint, buf, sizeof( aint ));
930 f_gid = ntohl (aint);
931 buf += sizeof( aint );
932 setfilowner(vol, f_uid, f_gid, path);
934 memcpy( &upriv, buf, sizeof( upriv ));
935 buf += sizeof( upriv );
936 upriv = ntohl (upriv);
937 if ((upriv & S_IWUSR)) {
938 setfilunixmode(vol, path, upriv);
945 case FILPBIT_PDINFO :
946 if (afp_version < 30) { /* else it's UTF8 name */
949 /* Keep special case to support crlf translations */
950 if ((unsigned int) achar == 0x04) {
951 fdType = (u_char *)"TEXT";
954 xyy[0] = ( u_char ) 'p';
965 /* break while loop */
974 /* second try with adouble open
976 if ( ad_open_metadata( upath, 0, O_CREAT, adp) < 0) {
977 LOG(log_debug, logtype_afpd, "setfilparams: ad_open_metadata error");
979 * For some things, we don't need an adouble header:
980 * - change of modification date
981 * - UNIX privs (Bug-ID #2863424)
983 if (!vol_noadouble(vol) && (f_bitmap & ~(1<<FILPBIT_MDATE | 1<<FILPBIT_UNIXPR))) {
984 LOG(log_debug, logtype_afpd, "setfilparams: need adouble access");
985 return AFPERR_ACCESS;
987 LOG(log_debug, logtype_afpd, "setfilparams: no adouble perms, but only FILPBIT_MDATE and/or FILPBIT_UNIXPR");
989 } else if ((ad_get_HF_flags( adp ) & O_CREAT) ) {
990 ad_setname(adp, path->m_name);
995 while ( bitmap != 0 ) {
996 while (( bitmap & 1 ) == 0 ) {
1003 ad_getattr(adp, &bshort);
1005 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
1006 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
1010 if ((bshort & htons(ATTRBIT_INVISIBLE)) != (oshort & htons(ATTRBIT_INVISIBLE)))
1011 change_parent_mdate = 1;
1012 ad_setattr(adp, bshort);
1014 case FILPBIT_CDATE :
1015 ad_setdate(adp, AD_DATE_CREATE, cdate);
1017 case FILPBIT_MDATE :
1019 case FILPBIT_BDATE :
1020 ad_setdate(adp, AD_DATE_BACKUP, bdate);
1022 case FILPBIT_FINFO :
1023 if (default_type( ad_entry( adp, ADEID_FINDERI ))
1025 ((em = getextmap( path->m_name )) &&
1026 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1027 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1028 || ((em = getdefextmap()) &&
1029 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1030 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1032 memcpy(finder_buf, ufinderi, 8 );
1034 memcpy(ad_entry( adp, ADEID_FINDERI ), finder_buf, 32 );
1036 case FILPBIT_UNIXPR :
1038 setfilunixmode(vol, path, upriv);
1041 case FILPBIT_PDINFO :
1042 if (afp_version < 30) { /* else it's UTF8 name */
1043 memcpy(ad_entry( adp, ADEID_FINDERI ), fdType, 4 );
1044 memcpy(ad_entry( adp, ADEID_FINDERI ) + 4, "pdos", 4 );
1049 err = AFPERR_BITMAP;
1050 goto setfilparam_done;
1057 if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
1058 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1062 ad_setdate(adp, AD_DATE_MODIFY, newdate);
1063 ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
1069 ad_close_metadata( adp);
1073 if (change_parent_mdate && gettimeofday(&tv, NULL) == 0) {
1074 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1075 bitmap = 1<<FILPBIT_MDATE;
1076 setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
1080 LOG(log_debug9, logtype_afpd, "end setfilparams:");
1086 * renamefile and copyfile take the old and new unix pathnames
1087 * and the new mac name.
1089 * sdir_fd source dir fd to which src path is relative (for openat et al semantics)
1090 * passing -1 means this is not used, src path is a full path
1091 * src the source path
1092 * dst the dest filename in current dir
1093 * newname the dest mac name
1094 * adp adouble struct of src file, if open, or & zeroed one
1097 int renamefile(const struct vol *vol, int sdir_fd, char *src, char *dst, char *newname, struct adouble *adp)
1101 if ( unix_rename( sdir_fd, src, -1, dst ) < 0 ) {
1104 return( AFPERR_NOOBJ );
1107 return( AFPERR_ACCESS );
1109 return AFPERR_VLOCK;
1110 case EXDEV : /* Cross device move -- try copy */
1111 /* NOTE: with open file it's an error because after the copy we will
1112 * get two files, it's fixable for our process (eg reopen the new file, get the
1113 * locks, and so on. But it doesn't solve the case with a second process
1115 if (adp->ad_open_forks) {
1116 /* FIXME warning in syslog so admin'd know there's a conflict ?*/
1117 return AFPERR_OLOCK; /* little lie */
1119 if (AFP_OK != ( rc = copyfile(vol, vol, sdir_fd, src, dst, newname, NULL )) ) {
1120 /* on error copyfile delete dest */
1123 return deletefile(vol, sdir_fd, src, 0);
1125 return( AFPERR_PARAM );
1129 if (vol->vfs->vfs_renamefile(vol, sdir_fd, src, dst) < 0 ) {
1133 /* try to undo the data fork rename,
1134 * we know we are on the same device
1137 unix_rename(-1, dst, sdir_fd, src );
1138 /* return the first error */
1141 return AFPERR_NOOBJ;
1144 return AFPERR_ACCESS ;
1146 return AFPERR_VLOCK;
1148 return AFPERR_PARAM ;
1153 /* don't care if we can't open the newly renamed ressource fork
1155 if (!ad_open( dst, ADFLAGS_HF, O_RDWR, 0666, adp)) {
1156 ad_setname(adp, newname);
1158 ad_close( adp, ADFLAGS_HF );
1165 convert a Mac long name to an utf8 name,
1167 size_t mtoUTF8(const struct vol *vol, const char *src, size_t srclen, char *dest, size_t destlen)
1171 if ((size_t)-1 == (outlen = convert_string ( vol->v_maccharset, CH_UTF8_MAC, src, srclen, dest, destlen)) ) {
1177 /* ---------------- */
1178 int copy_path_name(const struct vol *vol, char *newname, char *ibuf)
1185 if ( type != 2 && !(afp_version >= 30 && type == 3) ) {
1191 if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
1192 if (afp_version >= 30) {
1193 /* convert it to UTF8
1195 if ((plen = mtoUTF8(vol, ibuf, plen, newname, AFPOBJ_TMPSIZ)) == (size_t)-1)
1199 strncpy( newname, ibuf, plen );
1200 newname[ plen ] = '\0';
1202 if (strlen(newname) != plen) {
1203 /* there's \0 in newname, e.g. it's a pathname not
1211 memcpy(&hint, ibuf, sizeof(hint));
1212 ibuf += sizeof(hint);
1214 memcpy(&len16, ibuf, sizeof(len16));
1215 ibuf += sizeof(len16);
1216 plen = ntohs(len16);
1219 if (plen > AFPOBJ_TMPSIZ) {
1222 strncpy( newname, ibuf, plen );
1223 newname[ plen ] = '\0';
1224 if (strlen(newname) != plen) {
1233 /* -----------------------------------
1235 int afp_copyfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1237 struct vol *s_vol, *d_vol;
1239 char *newname, *p, *upath;
1240 struct path *s_path;
1241 u_int32_t sdid, ddid;
1242 int err, retvalue = AFP_OK;
1243 u_int16_t svid, dvid;
1245 struct adouble ad, *adp;
1251 memcpy(&svid, ibuf, sizeof( svid ));
1252 ibuf += sizeof( svid );
1253 if (NULL == ( s_vol = getvolbyvid( svid )) ) {
1254 return( AFPERR_PARAM );
1257 memcpy(&sdid, ibuf, sizeof( sdid ));
1258 ibuf += sizeof( sdid );
1259 if (NULL == ( dir = dirlookup( s_vol, sdid )) ) {
1263 memcpy(&dvid, ibuf, sizeof( dvid ));
1264 ibuf += sizeof( dvid );
1265 memcpy(&ddid, ibuf, sizeof( ddid ));
1266 ibuf += sizeof( ddid );
1268 if (NULL == ( s_path = cname( s_vol, dir, &ibuf )) ) {
1269 return get_afp_errno(AFPERR_PARAM);
1271 if ( path_isadir(s_path) ) {
1272 return( AFPERR_BADTYPE );
1275 /* don't allow copies when the file is open.
1276 * XXX: the spec only calls for read/deny write access.
1277 * however, copyfile doesn't have any of that info,
1278 * and locks need to stay coherent. as a result,
1279 * we just balk if the file is opened already. */
1281 adp = of_ad(s_vol, s_path, &ad);
1283 if (ad_open(s_path->u_name , ADFLAGS_DF |ADFLAGS_HF | ADFLAGS_NOHF, O_RDONLY, 0, adp) < 0) {
1284 return AFPERR_DENYCONF;
1286 denyreadset = (getforkmode(adp, ADEID_DFORK, AD_FILELOCK_DENY_RD) != 0 ||
1287 getforkmode(adp, ADEID_RFORK, AD_FILELOCK_DENY_RD) != 0 );
1290 retvalue = AFPERR_DENYCONF;
1294 newname = obj->newtmp;
1295 strcpy( newname, s_path->m_name );
1297 p = ctoupath( s_vol, curdir, newname );
1299 retvalue = AFPERR_PARAM;
1304 /* FIXME svid != dvid && dvid's user can't read svid */
1306 if (NULL == ( d_vol = getvolbyvid( dvid )) ) {
1307 retvalue = AFPERR_PARAM;
1311 if (d_vol->v_flags & AFPVOL_RO) {
1312 retvalue = AFPERR_VLOCK;
1316 if (NULL == ( dir = dirlookup( d_vol, ddid )) ) {
1317 retvalue = afp_errno;
1321 if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) {
1322 retvalue = get_afp_errno(AFPERR_NOOBJ);
1326 if ( *s_path->m_name != '\0' ) {
1327 retvalue =path_error(s_path, AFPERR_NOOBJ);
1331 /* one of the handful of places that knows about the path type */
1332 if (copy_path_name(d_vol, newname, ibuf) < 0) {
1333 retvalue = AFPERR_PARAM;
1336 /* newname is always only a filename so curdir *is* its
1339 if (NULL == (upath = mtoupath(d_vol, newname, curdir->d_did, utf8_encoding()))) {
1340 retvalue =AFPERR_PARAM;
1344 if ( (err = copyfile(s_vol, d_vol, -1, p, upath , newname, adp)) < 0 ) {
1351 if (vol->v_flags & AFPVOL_DROPBOX) {
1352 retvalue=matchfile2dirperms(upath, vol, ddid); /* FIXME sdir or ddid */
1354 #endif /* DROPKLUDGE */
1356 setvoltime(obj, d_vol );
1359 ad_close( adp, ADFLAGS_DF |ADFLAGS_HF );
1363 /* ----------------------- */
1364 static int copy_all(const int dfd, const void *buf,
1370 LOG(log_debug9, logtype_afpd, "begin copy_all:");
1373 while (buflen > 0) {
1374 if ((cc = write(dfd, buf, buflen)) < 0) {
1386 LOG(log_debug9, logtype_afpd, "end copy_all:");
1392 /* --------------------------
1393 * copy only the fork data stream
1395 static int copy_fork(int eid, struct adouble *add, struct adouble *ads)
1402 if (eid == ADEID_DFORK) {
1403 sfd = ad_data_fileno(ads);
1404 dfd = ad_data_fileno(add);
1407 sfd = ad_reso_fileno(ads);
1408 dfd = ad_reso_fileno(add);
1411 if ((off_t)-1 == lseek(sfd, ad_getentryoff(ads, eid), SEEK_SET))
1414 if ((off_t)-1 == lseek(dfd, ad_getentryoff(add, eid), SEEK_SET))
1417 #if 0 /* ifdef SENDFILE_FLAVOR_LINUX */
1418 /* doesn't work With 2.6 FIXME, only check for EBADFD ? */
1422 #define BUF 128*1024*1024
1424 if (fstat(sfd, &st) == 0) {
1427 if ( offset >= st.st_size) {
1430 size = (st.st_size -offset > BUF)?BUF:st.st_size -offset;
1431 if ((cc = sys_sendfile(dfd, sfd, &offset, size)) < 0) {
1434 case EINVAL: /* there's no guarantee that all fs support sendfile */
1443 lseek(sfd, offset, SEEK_SET);
1447 if ((cc = read(sfd, filebuf, sizeof(filebuf))) < 0) {
1454 if (!cc || ((err = copy_all(dfd, filebuf, cc)) < 0)) {
1461 /* ----------------------------------
1462 * if newname is NULL (from directory.c) we don't want to copy the resource fork.
1463 * because we are doing it elsewhere.
1464 * currently if newname is NULL then adp is NULL.
1466 int copyfile(const struct vol *s_vol,
1467 const struct vol *d_vol,
1472 struct adouble *adp)
1474 struct adouble ads, add;
1481 LOG(log_debug, logtype_afpd, "copyfile(sfd:%d,s:'%s',d:'%s',n:'%s')",
1482 sfd, src, dst, newname);
1485 ad_init(&ads, s_vol->v_adouble, s_vol->v_ad_options);
1489 adflags = ADFLAGS_DF;
1491 adflags |= ADFLAGS_HF;
1494 if (ad_openat(sfd, src, adflags | ADFLAGS_NOHF, O_RDONLY, 0, adp) < 0) {
1499 if (ad_meta_fileno(adp) == -1 && ad_reso_fileno(adp) == -1) { /* META / HF */
1500 /* no resource fork, don't create one for dst file */
1501 adflags &= ~ADFLAGS_HF;
1504 stat_result = fstat(ad_data_fileno(adp), &st); /* saving stat exit code, thus saving us on one more stat later on */
1506 if (stat_result < 0) {
1507 /* unlikely but if fstat fails, the default file mode will be 0666. */
1508 st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
1511 ad_init(&add, d_vol->v_adouble, d_vol->v_ad_options);
1512 if (ad_open(dst , adflags, O_RDWR|O_CREAT|O_EXCL, st.st_mode, &add) < 0) {
1514 ad_close( adp, adflags );
1515 if (EEXIST != ret_err) {
1516 deletefile(d_vol, -1, dst, 0);
1519 return AFPERR_EXIST;
1523 * XXX if the source and the dest don't use the same resource type it's broken
1525 if (ad_reso_fileno(adp) == -1 || 0 == (err = copy_fork(ADEID_RFORK, &add, adp))){
1526 /* copy the data fork */
1527 if ((err = copy_fork(ADEID_DFORK, &add, adp)) == 0) {
1528 err = d_vol->vfs->vfs_copyfile(d_vol, sfd, src, dst);
1536 if (!ret_err && newname && (adflags & ADFLAGS_HF)) {
1537 /* set the new name in the resource fork */
1538 ad_copy_header(&add, adp);
1539 ad_setname(&add, newname);
1542 ad_close( adp, adflags );
1544 if (ad_close( &add, adflags ) <0) {
1549 deletefile(d_vol, -1, dst, 0);
1551 else if (stat_result == 0) {
1552 /* set dest modification date to src date */
1555 ut.actime = ut.modtime = st.st_mtime;
1557 /* FIXME netatalk doesn't use resource fork file date
1558 * but maybe we should set its modtime too.
1563 switch ( ret_err ) {
1569 return AFPERR_DFULL;
1571 return AFPERR_NOOBJ;
1573 return AFPERR_ACCESS;
1575 return AFPERR_VLOCK;
1577 return AFPERR_PARAM;
1581 /* -----------------------------------
1582 vol: not NULL delete cnid entry. then we are in curdir and file is a only filename
1583 checkAttrib: 1 check kFPDeleteInhibitBit (deletfile called by afp_delete)
1585 when deletefile is called we don't have lock on it, file is closed (for us)
1586 untrue if called by renamefile
1588 ad_open always try to open file RDWR first and ad_lock takes care of
1589 WRITE lock on read only file.
1592 static int check_attrib(struct adouble *adp)
1594 u_int16_t bshort = 0;
1596 ad_getattr(adp, &bshort);
1598 * Does kFPDeleteInhibitBit (bit 8) set?
1600 if ((bshort & htons(ATTRBIT_NODELETE))) {
1601 return AFPERR_OLOCK;
1603 if ((bshort & htons(ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
1609 * dirfd can be used for unlinkat semantics
1611 int deletefile(const struct vol *vol, int dirfd, char *file, int checkAttrib)
1614 struct adouble *adp = NULL;
1615 int adflags, err = AFP_OK;
1618 LOG(log_debug, logtype_afpd, "deletefile('%s')", file);
1620 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
1622 /* was EACCESS error try to get only metadata */
1623 /* we never want to create a resource fork here, we are going to delete it
1624 * moreover sometimes deletefile is called with a no existent file and
1625 * ad_open would create a 0 byte resource fork
1627 if ( ad_metadataat(dirfd, file, ADFLAGS_OPENFORKS, &ad) == 0 ) {
1628 if ((err = check_attrib(&ad))) {
1629 ad_close_metadata(&ad);
1636 /* try to open both forks at once */
1637 adflags = ADFLAGS_DF;
1638 if ( ad_openat(dirfd, file, adflags |ADFLAGS_HF|ADFLAGS_NOHF, O_RDONLY, 0, &ad ) < 0 ) {
1643 case EACCES: /* maybe it's a file with no write mode for us */
1644 break; /* was return AFPERR_ACCESS;*/
1657 if ( adp && ad_reso_fileno( adp ) != -1 ) { /* there's a resource fork */
1658 adflags |= ADFLAGS_HF;
1659 /* FIXME we have a pb here because we want to know if a file is open
1660 * there's a 'priority inversion' if you can't open the ressource fork RW
1661 * you can delete it if it's open because you can't get a write lock.
1663 * ADLOCK_FILELOCK means the whole ressource fork, not only after the
1666 * FIXME it doesn't work for RFORK open read only and fork open without deny mode
1668 if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
1674 if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
1676 } else if (!(err = vol->vfs->vfs_deletefile(vol, dirfd, file)) && !(err = netatalk_unlinkat(dirfd, file )) ) {
1678 if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file)))) {
1679 cnid_delete(vol->v_cdb, id);
1685 ad_close_metadata(&ad);
1688 ad_close( &ad, adflags ); /* ad_close removes locks if any */
1693 /* ------------------------------------ */
1694 /* return a file id */
1695 int afp_createid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1704 struct path *s_path;
1710 memcpy(&vid, ibuf, sizeof(vid));
1711 ibuf += sizeof(vid);
1713 if (NULL == ( vol = getvolbyvid( vid )) ) {
1714 return( AFPERR_PARAM);
1717 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1721 if (vol->v_flags & AFPVOL_RO)
1722 return AFPERR_VLOCK;
1724 memcpy(&did, ibuf, sizeof( did ));
1725 ibuf += sizeof(did);
1727 if (NULL == ( dir = dirlookup( vol, did )) ) {
1728 return afp_errno; /* was AFPERR_PARAM */
1731 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
1732 return get_afp_errno(AFPERR_NOOBJ); /* was AFPERR_PARAM */
1735 if ( path_isadir(s_path) ) {
1736 return( AFPERR_BADTYPE );
1739 upath = s_path->u_name;
1740 switch (s_path->st_errno) {
1742 break; /* success */
1745 return AFPERR_ACCESS;
1747 return AFPERR_NOOBJ;
1749 return AFPERR_PARAM;
1752 if ((id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath)))) {
1753 memcpy(rbuf, &id, sizeof(id));
1754 *rbuflen = sizeof(id);
1755 return AFPERR_EXISTID;
1758 if ((id = get_id(vol, NULL, st, did, upath, len)) != CNID_INVALID) {
1759 memcpy(rbuf, &id, sizeof(id));
1760 *rbuflen = sizeof(id);
1767 /* ------------------------------- */
1773 static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
1776 struct reenum *param = data;
1777 struct vol *vol = param->vol;
1778 cnid_t did = param->did;
1781 if ( lstat(de->d_name, &path.st)<0 )
1784 /* update or add to cnid */
1785 aint = cnid_add(vol->v_cdb, &path.st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */
1787 #if AD_VERSION > AD_VERSION1
1788 if (aint != CNID_INVALID && !S_ISDIR(path.st.st_mode)) {
1789 struct adouble ad, *adp;
1793 path.u_name = de->d_name;
1795 adp = of_ad(vol, &path, &ad);
1797 if ( ad_open_metadata( de->d_name, 0, 0, adp ) < 0 ) {
1800 if (ad_setid(adp, path.st.st_dev, path.st.st_ino, aint, did, vol->v_stamp)) {
1803 ad_close_metadata(adp);
1805 #endif /* AD_VERSION > AD_VERSION1 */
1810 /* --------------------
1811 * Ok the db is out of synch with the dir.
1812 * but if it's a deleted file we don't want to do it again and again.
1815 reenumerate_id(struct vol *vol, char *name, struct dir *dir)
1821 if (vol->v_cdb == NULL) {
1825 /* FIXME use of_statdir ? */
1826 if (lstat(name, &st)) {
1830 if (dirreenumerate(dir, &st)) {
1831 /* we already did it once and the dir haven't been modified */
1836 data.did = dir->d_did;
1837 if ((ret = for_each_dirent(vol, name, reenumerate_loop, (void *)&data)) >= 0) {
1838 setdiroffcnt(curdir, &st, ret);
1839 dir->d_flags |= DIRF_CNID;
1845 /* ------------------------------
1846 resolve a file id */
1847 int afp_resolveid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1856 u_int16_t vid, bitmap;
1858 static char buffer[12 + MAXPATHLEN + 1];
1859 int len = 12 + MAXPATHLEN + 1;
1864 memcpy(&vid, ibuf, sizeof(vid));
1865 ibuf += sizeof(vid);
1867 if (NULL == ( vol = getvolbyvid( vid )) ) {
1868 return( AFPERR_PARAM);
1871 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1875 memcpy(&id, ibuf, sizeof( id ));
1880 /* some MacOS versions after a catsearch do a *lot* of afp_resolveid with 0 */
1884 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1885 return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
1888 if (NULL == ( dir = dirlookup( vol, id )) ) {
1889 return AFPERR_NOID; /* idem AFPERR_PARAM */
1891 if (movecwd(vol, dir) < 0) {
1895 return AFPERR_ACCESS;
1899 return AFPERR_PARAM;
1903 memset(&path, 0, sizeof(path));
1904 path.u_name = upath;
1905 if ( of_stat(&path) < 0 ) {
1907 /* with nfs and our working directory is deleted */
1908 if (errno == ESTALE) {
1912 if ( errno == ENOENT && !retry) {
1913 /* cnid db is out of sync, reenumerate the directory and update ids */
1914 reenumerate_id(vol, ".", dir);
1922 return AFPERR_ACCESS;
1926 return AFPERR_PARAM;
1930 /* directories are bad */
1931 if (S_ISDIR(path.st.st_mode)) {
1932 /* OS9 and OSX don't return the same error code */
1933 return (afp_version >=30)?AFPERR_NOID:AFPERR_BADTYPE;
1936 memcpy(&bitmap, ibuf, sizeof(bitmap));
1937 bitmap = ntohs( bitmap );
1938 if (NULL == (path.m_name = utompath(vol, upath, cnid, utf8_encoding()))) {
1942 if (AFP_OK != (err = getfilparams(vol, bitmap, &path , curdir,
1943 rbuf + sizeof(bitmap), &buflen))) {
1946 *rbuflen = buflen + sizeof(bitmap);
1947 memcpy(rbuf, ibuf, sizeof(bitmap));
1952 /* ------------------------------ */
1953 int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1963 static char buffer[12 + MAXPATHLEN + 1];
1964 int len = 12 + MAXPATHLEN + 1;
1969 memcpy(&vid, ibuf, sizeof(vid));
1970 ibuf += sizeof(vid);
1972 if (NULL == ( vol = getvolbyvid( vid )) ) {
1973 return( AFPERR_PARAM);
1976 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1980 if (vol->v_flags & AFPVOL_RO)
1981 return AFPERR_VLOCK;
1983 memcpy(&id, ibuf, sizeof( id ));
1987 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1991 if (NULL == ( dir = dirlookup( vol, id )) ) {
1992 if (afp_errno == AFPERR_NOOBJ) {
1996 return( AFPERR_PARAM );
2000 if ((movecwd(vol, dir) < 0) || (lstat(upath, &st) < 0)) {
2004 return AFPERR_ACCESS;
2009 /* still try to delete the id */
2013 return AFPERR_PARAM;
2016 else if (S_ISDIR(st.st_mode)) /* directories are bad */
2017 return AFPERR_BADTYPE;
2020 if (cnid_delete(vol->v_cdb, fileid)) {
2023 return AFPERR_VLOCK;
2026 return AFPERR_ACCESS;
2028 return AFPERR_PARAM;
2035 /* ------------------------------ */
2036 static struct adouble *find_adouble(struct path *path, struct ofork **of, struct adouble *adp)
2040 if (path->st_errno) {
2041 switch (path->st_errno) {
2043 afp_errno = AFPERR_NOID;
2047 afp_errno = AFPERR_ACCESS;
2050 afp_errno = AFPERR_PARAM;
2055 /* we use file_access both for legacy Mac perm and
2056 * for unix privilege, rename will take care of folder perms
2058 if (file_access(path, OPENACC_WR ) < 0) {
2059 afp_errno = AFPERR_ACCESS;
2063 if ((*of = of_findname(path))) {
2064 /* reuse struct adouble so it won't break locks */
2068 ret = ad_open( path->u_name, ADFLAGS_HF, O_RDONLY, 0, adp);
2070 if ( !ret && ad_reso_fileno(adp) != -1 && !(adp->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
2072 * The user must have the Read & Write privilege for both files in order to use this command.
2074 ad_close(adp, ADFLAGS_HF);
2075 afp_errno = AFPERR_ACCESS;
2082 #define APPLETEMP ".AppleTempXXXXXX"
2084 int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
2086 struct stat srcst, destst;
2088 struct dir *dir, *sdir;
2089 char *spath, temp[17], *p;
2090 char *supath, *upath;
2095 struct adouble *adsp = NULL;
2096 struct adouble *addp = NULL;
2097 struct ofork *s_of = NULL;
2098 struct ofork *d_of = NULL;
2111 memcpy(&vid, ibuf, sizeof(vid));
2112 ibuf += sizeof(vid);
2114 if (NULL == ( vol = getvolbyvid( vid )) ) {
2115 return( AFPERR_PARAM);
2118 if ((vol->v_flags & AFPVOL_RO))
2119 return AFPERR_VLOCK;
2121 /* source and destination dids */
2122 memcpy(&sid, ibuf, sizeof(sid));
2123 ibuf += sizeof(sid);
2124 memcpy(&did, ibuf, sizeof(did));
2125 ibuf += sizeof(did);
2128 if (NULL == (dir = dirlookup( vol, sid )) ) {
2129 return afp_errno; /* was AFPERR_PARAM */
2132 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2133 return get_afp_errno(AFPERR_NOOBJ);
2136 if ( path_isadir(path) ) {
2137 return AFPERR_BADTYPE; /* it's a dir */
2140 /* save some stuff */
2143 spath = obj->oldtmp;
2144 supath = obj->newtmp;
2145 strcpy(spath, path->m_name);
2146 strcpy(supath, path->u_name); /* this is for the cnid changing */
2147 p = absupath( vol, sdir, supath);
2149 /* pathname too long */
2150 return AFPERR_PARAM ;
2153 ad_init(&ads, vol->v_adouble, vol->v_ad_options);
2154 if (!(adsp = find_adouble( path, &s_of, &ads))) {
2158 /* ***** from here we may have resource fork open **** */
2160 /* look for the source cnid. if it doesn't exist, don't worry about
2162 sid = cnid_lookup(vol->v_cdb, &srcst, sdir->d_did, supath,slen = strlen(supath));
2164 if (NULL == ( dir = dirlookup( vol, did )) ) {
2165 err = afp_errno; /* was AFPERR_PARAM */
2166 goto err_exchangefile;
2169 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2170 err = get_afp_errno(AFPERR_NOOBJ);
2171 goto err_exchangefile;
2174 if ( path_isadir(path) ) {
2175 err = AFPERR_BADTYPE;
2176 goto err_exchangefile;
2179 /* FPExchangeFiles is the only call that can return the SameObj
2181 if ((curdir == sdir) && strcmp(spath, path->m_name) == 0) {
2182 err = AFPERR_SAMEOBJ;
2183 goto err_exchangefile;
2186 ad_init(&add, vol->v_adouble, vol->v_ad_options);
2187 if (!(addp = find_adouble( path, &d_of, &add))) {
2189 goto err_exchangefile;
2193 /* they are not on the same device and at least one is open
2194 * FIXME broken for for crossdev and adouble v2
2197 crossdev = (srcst.st_dev != destst.st_dev);
2198 if (/* (d_of || s_of) && */ crossdev) {
2200 goto err_exchangefile;
2203 /* look for destination id. */
2204 upath = path->u_name;
2205 did = cnid_lookup(vol->v_cdb, &destst, curdir->d_did, upath, dlen = strlen(upath));
2207 /* construct a temp name.
2208 * NOTE: the temp file will be in the dest file's directory. it
2209 * will also be inaccessible from AFP. */
2210 memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
2211 if (!mktemp(temp)) {
2213 goto err_exchangefile;
2217 /* FIXME we need to close fork for copy, both s_of and d_of are null */
2218 ad_close(adsp, ADFLAGS_HF);
2219 ad_close(addp, ADFLAGS_HF);
2222 /* now, quickly rename the file. we error if we can't. */
2223 if ((err = renamefile(vol, -1, p, temp, temp, adsp)) != AFP_OK)
2224 goto err_exchangefile;
2225 of_rename(vol, s_of, sdir, spath, curdir, temp);
2227 /* rename destination to source */
2228 if ((err = renamefile(vol, -1, upath, p, spath, addp)) != AFP_OK)
2229 goto err_src_to_tmp;
2230 of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
2232 /* rename temp to destination */
2233 if ((err = renamefile(vol, -1, temp, upath, path->m_name, adsp)) != AFP_OK)
2234 goto err_dest_to_src;
2235 of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
2237 /* id's need switching. src -> dest and dest -> src.
2238 * we need to re-stat() if it was a cross device copy.
2241 cnid_delete(vol->v_cdb, sid);
2244 cnid_delete(vol->v_cdb, did);
2246 if ((did && ( (crossdev && lstat( upath, &srcst) < 0) ||
2247 cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
2249 (sid && ( (crossdev && lstat(p, &destst) < 0) ||
2250 cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
2255 err = AFPERR_ACCESS;
2260 goto err_temp_to_dest;
2263 /* here we need to reopen if crossdev */
2264 if (sid && ad_setid(addp, destst.st_dev, destst.st_ino, sid, sdir->d_did, vol->v_stamp))
2269 if (did && ad_setid(adsp, srcst.st_dev, srcst.st_ino, did, curdir->d_did, vol->v_stamp))
2274 /* change perms, src gets dest perm and vice versa */
2279 LOG(log_error, logtype_afpd, "seteuid failed %s", strerror(errno));
2280 err = AFP_OK; /* ignore error */
2281 goto err_temp_to_dest;
2285 * we need to exchange ACL entries as well
2287 /* exchange_acls(vol, p, upath); */
2292 path->m_name = NULL;
2293 path->u_name = upath;
2295 setfilunixmode(vol, path, destst.st_mode);
2296 setfilowner(vol, destst.st_uid, destst.st_gid, path);
2303 setfilunixmode(vol, path, srcst.st_mode);
2304 setfilowner(vol, srcst.st_uid, srcst.st_gid, path);
2306 if ( setegid(gid) < 0 || seteuid(uid) < 0) {
2307 LOG(log_error, logtype_afpd, "can't seteuid back %s", strerror(errno));
2312 goto err_exchangefile;
2314 /* all this stuff is so that we can unwind a failed operation
2317 /* rename dest to temp */
2318 renamefile(vol, -1, upath, temp, temp, adsp);
2319 of_rename(vol, s_of, curdir, upath, curdir, temp);
2322 /* rename source back to dest */
2323 renamefile(vol, -1, p, upath, path->m_name, addp);
2324 of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
2327 /* rename temp back to source */
2328 renamefile(vol, -1, temp, p, spath, adsp);
2329 of_rename(vol, s_of, curdir, temp, sdir, spath);
2332 if ( !s_of && adsp && ad_meta_fileno(adsp) != -1 ) { /* META */
2333 ad_close(adsp, ADFLAGS_HF);
2335 if ( !d_of && addp && ad_meta_fileno(addp) != -1 ) {/* META */
2336 ad_close(addp, ADFLAGS_HF);