2 * Copyright (c) 1990,1993 Regents of The University of Michigan.
3 * All Rights Reserved. See COPYRIGHT.
8 #endif /* HAVE_CONFIG_H */
16 #include <sys/param.h>
18 #include <atalk/adouble.h>
19 #include <atalk/vfs.h>
20 #include <atalk/logger.h>
21 #include <atalk/afp.h>
22 #include <atalk/util.h>
23 #include <atalk/cnid.h>
24 #include <atalk/unix.h>
25 #include <atalk/globals.h>
26 #include <atalk/fce_api.h>
28 #include "directory.h"
37 /* the format for the finderinfo fields (from IM: Toolbox Essentials):
38 * field bytes subfield bytes
41 * ioFlFndrInfo 16 -> type 4 type field
42 * creator 4 creator field
43 * flags 2 finder flags:
45 * location 4 location in window
46 * folder 2 window that contains file
48 * ioFlXFndrInfo 16 -> iconID 2 icon id
50 * script 1 script system
52 * commentID 2 comment id
53 * putawayID 4 home directory id
56 const u_char ufinderi[ADEDLEN_FINDERI] = {
57 0, 0, 0, 0, 0, 0, 0, 0,
58 1, 0, 0, 0, 0, 0, 0, 0,
59 0, 0, 0, 0, 0, 0, 0, 0,
60 0, 0, 0, 0, 0, 0, 0, 0
63 static const u_char old_ufinderi[] = {
64 'T', 'E', 'X', 'T', 'U', 'N', 'I', 'X'
67 /* ----------------------
69 static int default_type(void *finder)
71 if (!memcmp(finder, ufinderi, 8) || !memcmp(finder, old_ufinderi, 8))
76 /* FIXME path : unix or mac name ? (for now it's unix name ) */
77 void *get_finderinfo(const struct vol *vol, const char *upath, struct adouble *adp, void *data, int islink)
80 void *ad_finder = NULL;
84 ad_finder = ad_entry(adp, ADEID_FINDERI);
87 memcpy(data, ad_finder, ADEDLEN_FINDERI);
89 if (default_type(ad_finder))
93 memcpy(data, ufinderi, ADEDLEN_FINDERI);
95 if (vol_inv_dots(vol) && *upath == '.') { /* make it invisible */
98 ashort = htons(FINDERINFO_INVISIBLE);
99 memcpy((char *)data + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
105 memcpy(&linkflag, (char *)data + FINDERINFO_FRFLAGOFF, 2);
106 linkflag |= htons(FINDERINFO_ISALIAS);
107 memcpy((char *)data + FINDERINFO_FRFLAGOFF, &linkflag, 2);
108 memcpy((char *)data + FINDERINFO_FRTYPEOFF,"slnk",4);
109 memcpy((char *)data + FINDERINFO_FRCREATOFF,"rhap",4);
113 /** Only enter if no appledouble information and no finder information found. */
114 if (chk_ext && (em = getextmap( upath ))) {
115 memcpy(data, em->em_type, sizeof( em->em_type ));
116 memcpy((char *)data + 4, em->em_creator, sizeof(em->em_creator));
121 /* ---------------------
123 char *set_name(const struct vol *vol, char *data, cnid_t pid, char *name, cnid_t id, uint32_t utf8)
128 aint = strlen( name );
132 if (utf8_encoding()) {
133 /* but name is an utf8 mac name */
136 /* global static variable... */
138 if (!(u = mtoupath(vol, name, pid, 1)) || !(m = utompath(vol, u, id, 0))) {
147 if (aint > MACFILELEN)
154 if (aint > UTF8FILELEN_EARLY) /* FIXME safeguard, anyway if no ascii char it's game over*/
155 aint = UTF8FILELEN_EARLY;
157 utf8 = vol->v_kTextEncoding;
158 memcpy(data, &utf8, sizeof(utf8));
159 data += sizeof(utf8);
162 memcpy(data, &temp, sizeof(temp));
163 data += sizeof(temp);
166 memcpy( data, src, aint );
176 * FIXME: PDINFO is UTF8 and doesn't need adp
178 #define PARAM_NEED_ADP(b) ((b) & ((1 << FILPBIT_ATTR) |\
179 (1 << FILPBIT_CDATE) |\
180 (1 << FILPBIT_MDATE) |\
181 (1 << FILPBIT_BDATE) |\
182 (1 << FILPBIT_FINFO) |\
183 (1 << FILPBIT_RFLEN) |\
184 (1 << FILPBIT_EXTRFLEN) |\
185 (1 << FILPBIT_PDINFO) |\
186 (1 << FILPBIT_FNUM) |\
187 (1 << FILPBIT_UNIXPR)))
190 * @brief Get CNID for did/upath args both from database and adouble file
192 * 1. Get the objects CNID as stored in its adouble file
193 * 2. Get the objects CNID from the database
194 * 3. If there's a problem with a "dbd" database, fallback to "tdb" in memory
195 * 4. In case 2 and 3 differ, store 3 in the adouble file
197 * @param vol (rw) volume
198 * @param adp (rw) adouble struct of object upath, might be NULL
199 * @param st (r) stat of upath, must NOT be NULL
200 * @param did (r) parent CNID of upath
201 * @param upath (r) name of object
202 * @param len (r) strlen of upath
204 uint32_t get_id(struct vol *vol,
206 const struct stat *st,
211 static int first = 1; /* mark if this func is called the first time */
213 uint32_t dbcnid = CNID_INVALID;
216 if (vol->v_cdb != NULL) {
217 /* prime aint with what we think is the cnid, set did to zero for
218 catching moved files */
219 adcnid = ad_getid(adp, st->st_dev, st->st_ino, 0, vol->v_stamp); /* (1) */
221 dbcnid = cnid_add(vol->v_cdb, st, did, upath, len, adcnid); /* (2) */
222 /* Throw errors if cnid_add fails. */
223 if (dbcnid == CNID_INVALID) {
225 case CNID_ERR_CLOSE: /* the db is closed */
228 LOG(log_error, logtype_afpd, "get_id: Incorrect parameters passed to cnid_add");
229 afp_errno = AFPERR_PARAM;
232 afp_errno = AFPERR_PARAM;
235 /* Close CNID backend if "dbd" and switch to temp in-memory "tdb" */
236 /* we have to do it here for "dbd" because it uses "lazy opening" */
237 /* In order to not end in a loop somehow with goto restart below */
239 if (first && (strcmp(vol->v_cnidscheme, "dbd") == 0)) { /* (3) */
240 cnid_close(vol->v_cdb);
241 free(vol->v_cnidscheme);
242 vol->v_cnidscheme = strdup("tdb");
244 int flags = CNID_FLAG_MEMORY;
245 if ((vol->v_flags & AFPVOL_NODEV)) {
246 flags |= CNID_FLAG_NODEV;
248 LOG(log_error, logtype_afpd, "Reopen volume %s using in memory temporary CNID DB.",
250 vol->v_cdb = cnid_open(vol->v_path, vol->v_umask, "tdb", flags, NULL, NULL);
252 /* deactivate cnid caching/storing in AppleDouble files and set ro mode*/
253 vol->v_flags &= ~AFPVOL_CACHE;
254 vol->v_flags |= AFPVOL_RO;
256 /* kill ourself with SIGUSR2 aka msg pending */
257 setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB instead."
258 "Check server messages for details. Switching to read-only mode.");
259 kill(getpid(), SIGUSR2);
261 goto restart; /* not try again with the temp CNID db */
264 setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB failed too!"
265 "Check server messages for details, can't recover from this state!");
269 afp_errno = AFPERR_MISC;
273 else if (adp && (adcnid != dbcnid)) { /* 4 */
274 /* Update the ressource fork. For a folder adp is always null */
275 LOG(log_debug, logtype_afpd, "get_id(%s/%s): calling ad_setid(old: %u, new: %u)",
276 getcwdpath(), upath, htonl(adcnid), htonl(dbcnid));
277 if (ad_setid(adp, st->st_dev, st->st_ino, dbcnid, did, vol->v_stamp)) {
288 /* -------------------------- */
289 int getmetadata(struct vol *vol,
291 struct path *path, struct dir *dir,
292 char *buf, size_t *buflen, struct adouble *adp)
294 char *data, *l_nameoff = NULL, *upath;
295 char *utf_nameoff = NULL;
300 u_char achar, fdType[4];
305 LOG(log_debug, logtype_afpd, "getmetadata(\"%s\")", path->u_name);
307 upath = path->u_name;
311 if ( ((bitmap & ( (1 << FILPBIT_FINFO)|(1 << FILPBIT_LNAME)|(1 <<FILPBIT_PDINFO) ) ) && !path->m_name)
312 || (bitmap & ( (1 << FILPBIT_LNAME) ) && utf8_encoding()) /* FIXME should be m_name utf8 filename */
313 || (bitmap & (1 << FILPBIT_FNUM))) {
316 struct dir *cachedfile;
317 int len = strlen(upath);
318 if ((cachedfile = dircache_search_by_name(vol, dir, upath, len)) != NULL)
319 id = cachedfile->d_did;
321 id = get_id(vol, adp, st, dir->d_did, upath, len);
323 /* Add it to the cache */
324 LOG(log_debug, logtype_afpd, "getmetadata: caching: did:%u, \"%s\", cnid:%u",
325 ntohl(dir->d_did), upath, ntohl(id));
327 /* Get macname from unixname first */
328 if (path->m_name == NULL) {
329 if ((path->m_name = utompath(vol, upath, id, utf8_encoding())) == NULL) {
330 LOG(log_error, logtype_afpd, "getmetadata: utompath error");
336 if (((fullpath = bstrcpy(dir->d_fullpath)) == NULL)
337 || (bconchar(fullpath, '/') != BSTR_OK)
338 || (bcatcstr(fullpath, upath)) != BSTR_OK) {
339 LOG(log_error, logtype_afpd, "getmetadata: fullpath: %s", strerror(errno));
343 if ((cachedfile = dir_new(path->m_name, upath, vol, dir->d_did, id, fullpath, st)) == NULL) {
344 LOG(log_error, logtype_afpd, "getmetadata: error from dir_new");
348 if ((dircache_add(vol, cachedfile)) != 0) {
349 LOG(log_error, logtype_afpd, "getmetadata: fatal dircache error");
357 if (id == CNID_INVALID)
361 path->m_name = utompath(vol, upath, id, utf8_encoding());
364 while ( bitmap != 0 ) {
365 while (( bitmap & 1 ) == 0 ) {
373 ad_getattr(adp, &ashort);
374 } else if (vol_inv_dots(vol) && *upath == '.') {
375 ashort = htons(ATTRBIT_INVISIBLE);
379 /* FIXME do we want a visual clue if the file is read only
382 accessmode(vol, ".", &ma, dir , NULL);
383 if ((ma.ma_user & AR_UWRITE)) {
384 accessmode(vol, upath, &ma, dir , st);
385 if (!(ma.ma_user & AR_UWRITE)) {
386 ashort |= htons(ATTRBIT_NOWRITE);
390 memcpy(data, &ashort, sizeof( ashort ));
391 data += sizeof( ashort );
392 LOG(log_debug, logtype_afpd, "metadata('%s'): AFP Attributes: %04x",
393 path->u_name, ntohs(ashort));
397 memcpy(data, &dir->d_did, sizeof( uint32_t ));
398 data += sizeof( uint32_t );
399 LOG(log_debug, logtype_afpd, "metadata('%s'): Parent DID: %u",
400 path->u_name, ntohl(dir->d_did));
404 if (!adp || (ad_getdate(adp, AD_DATE_CREATE, &aint) < 0))
405 aint = AD_DATE_FROM_UNIX(st->st_mtime);
406 memcpy(data, &aint, sizeof( aint ));
407 data += sizeof( aint );
411 if ( adp && (ad_getdate(adp, AD_DATE_MODIFY, &aint) == 0)) {
412 if ((st->st_mtime > AD_DATE_TO_UNIX(aint))) {
413 aint = AD_DATE_FROM_UNIX(st->st_mtime);
416 aint = AD_DATE_FROM_UNIX(st->st_mtime);
418 memcpy(data, &aint, sizeof( int ));
419 data += sizeof( int );
423 if (!adp || (ad_getdate(adp, AD_DATE_BACKUP, &aint) < 0))
424 aint = AD_DATE_START;
425 memcpy(data, &aint, sizeof( int ));
426 data += sizeof( int );
430 get_finderinfo(vol, upath, adp, (char *)data,S_ISLNK(st->st_mode));
431 data += ADEDLEN_FINDERI;
436 data += sizeof( uint16_t );
440 memset(data, 0, sizeof(uint16_t));
441 data += sizeof( uint16_t );
445 memcpy(data, &id, sizeof( id ));
446 data += sizeof( id );
447 LOG(log_debug, logtype_afpd, "metadata('%s'): CNID: %u",
448 path->u_name, ntohl(id));
452 if (st->st_size > 0xffffffff)
455 aint = htonl( st->st_size );
456 memcpy(data, &aint, sizeof( aint ));
457 data += sizeof( aint );
462 if (adp->ad_rlen > 0xffffffff)
465 aint = htonl( adp->ad_rlen);
469 memcpy(data, &aint, sizeof( aint ));
470 data += sizeof( aint );
473 /* Current client needs ProDOS info block for this file.
474 Use simple heuristic and let the Mac "type" string tell
475 us what the PD file code should be. Everything gets a
476 subtype of 0x0000 unless the original value was hashed
477 to "pXYZ" when we created it. See IA, Ver 2.
478 <shirsch@adelphia.net> */
479 case FILPBIT_PDINFO :
480 if (afp_version >= 30) { /* UTF8 name */
481 utf8 = kTextEncodingUTF8;
483 data += sizeof( uint16_t );
485 memcpy(data, &aint, sizeof( aint ));
486 data += sizeof( aint );
490 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
492 if ( memcmp( fdType, "TEXT", 4 ) == 0 ) {
496 else if ( memcmp( fdType, "PSYS", 4 ) == 0 ) {
500 else if ( memcmp( fdType, "PS16", 4 ) == 0 ) {
504 else if ( memcmp( fdType, "BINA", 4 ) == 0 ) {
508 else if ( fdType[0] == 'p' ) {
510 ashort = (fdType[2] * 256) + fdType[3];
524 memcpy(data, &ashort, sizeof( ashort ));
525 data += sizeof( ashort );
526 memset(data, 0, sizeof( ashort ));
527 data += sizeof( ashort );
530 case FILPBIT_EXTDFLEN:
531 aint = htonl(st->st_size >> 32);
532 memcpy(data, &aint, sizeof( aint ));
533 data += sizeof( aint );
534 aint = htonl(st->st_size);
535 memcpy(data, &aint, sizeof( aint ));
536 data += sizeof( aint );
538 case FILPBIT_EXTRFLEN:
541 aint = htonl(adp->ad_rlen >> 32);
542 memcpy(data, &aint, sizeof( aint ));
543 data += sizeof( aint );
545 aint = htonl(adp->ad_rlen);
546 memcpy(data, &aint, sizeof( aint ));
547 data += sizeof( aint );
549 case FILPBIT_UNIXPR :
550 /* accessmode may change st_mode with ACLs */
551 accessmode(vol, upath, &ma, dir , st);
553 aint = htonl(st->st_uid);
554 memcpy( data, &aint, sizeof( aint ));
555 data += sizeof( aint );
556 aint = htonl(st->st_gid);
557 memcpy( data, &aint, sizeof( aint ));
558 data += sizeof( aint );
561 type == slnk indicates an OSX style symlink,
562 we have to add S_IFLNK to the mode, otherwise
563 10.3 clients freak out. */
567 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
568 if ( memcmp( fdType, "slnk", 4 ) == 0 ) {
574 memcpy( data, &aint, sizeof( aint ));
575 data += sizeof( aint );
577 *data++ = ma.ma_user;
578 *data++ = ma.ma_world;
579 *data++ = ma.ma_group;
580 *data++ = ma.ma_owner;
584 return( AFPERR_BITMAP );
590 ashort = htons( data - buf );
591 memcpy(l_nameoff, &ashort, sizeof( ashort ));
592 data = set_name(vol, data, dir->d_did, path->m_name, id, 0);
595 ashort = htons( data - buf );
596 memcpy(utf_nameoff, &ashort, sizeof( ashort ));
597 data = set_name(vol, data, dir->d_did, path->m_name, id, utf8);
599 *buflen = data - buf;
603 /* ----------------------- */
604 int getfilparams(struct vol *vol,
606 struct path *path, struct dir *dir,
607 char *buf, size_t *buflen )
609 struct adouble ad, *adp;
613 LOG(log_debug, logtype_afpd, "getfilparams(\"%s\")", path->u_name);
615 opened = PARAM_NEED_ADP(bitmap);
620 int flags = (bitmap & (1 << FILPBIT_ATTR)) ? ADFLAGS_CHECK_OF : 0;
622 adp = of_ad(vol, path, &ad);
623 upath = path->u_name;
625 if ( ad_metadata( upath, flags, adp) < 0 ) {
628 LOG(log_error, logtype_afpd, "getfilparams(%s): %s: check resource fork permission?",
629 upath, strerror(errno));
630 return AFPERR_ACCESS;
632 LOG(log_error, logtype_afpd, "getfilparams(%s): bad resource fork", upath);
641 rc = getmetadata(vol, bitmap, path, dir, buf, buflen, adp);
642 ad_close_metadata( adp);
647 /* ----------------------------- */
648 int afp_createfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
653 struct ofork *of = NULL;
655 int creatf, did, openf, retvalue = AFP_OK;
661 creatf = (unsigned char) *ibuf++;
663 memcpy(&vid, ibuf, sizeof( vid ));
664 ibuf += sizeof( vid );
666 if (NULL == ( vol = getvolbyvid( vid )) )
667 return( AFPERR_PARAM );
669 if (vol->v_flags & AFPVOL_RO)
672 memcpy(&did, ibuf, sizeof( did));
673 ibuf += sizeof( did );
675 if (NULL == ( dir = dirlookup( vol, did )) )
678 if (NULL == ( s_path = cname( vol, dir, &ibuf )) )
679 return get_afp_errno(AFPERR_PARAM);
681 if ( *s_path->m_name == '\0' )
682 return( AFPERR_BADTYPE );
684 upath = s_path->u_name;
685 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
687 /* if upath is deleted we already in trouble anyway */
688 if ((of = of_findname(s_path))) {
696 openf = O_RDWR|O_CREAT|O_TRUNC;
698 /* on a soft create, if the file is open then ad_open won't fail
699 because open syscall is not called */
700 openf = O_RDWR|O_CREAT|O_EXCL;
702 if ( ad_open(&ad, upath, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF,
703 openf, 0666, openf, 0666) < 0 ) {
707 case ENOENT : /* we were already in 'did folder' so chdir() didn't fail */
708 return ( AFPERR_NOOBJ );
710 return( AFPERR_EXIST );
712 return( AFPERR_ACCESS );
715 return( AFPERR_DFULL );
717 return( AFPERR_PARAM );
720 if ( ad_meta_fileno( &ad ) == -1 ) { /* Hard META / HF */
721 /* on noadouble volumes, just creating the data fork is ok */
722 if (vol_noadouble(vol)) {
723 ad_close( &ad, ADFLAGS_DF );
724 goto createfile_done;
726 /* FIXME with hard create on an existing file, we already
727 * corrupted the data file.
729 netatalk_unlink( upath );
730 ad_close( &ad, ADFLAGS_DF );
731 return AFPERR_ACCESS;
734 path = s_path->m_name;
735 ad_setname(&ad, path);
738 if (lstat(upath, &st) != 0) {
739 LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): stat: %s",
740 upath, strerror(errno));
741 ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF);
745 (void)get_id(vol, &ad, &st, dir->d_did, upath, strlen(upath));
748 ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF );
749 fce_register_new_file(s_path);
754 setvoltime(obj, vol );
759 int afp_setfilparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
765 uint16_t vid, bitmap;
770 memcpy(&vid, ibuf, sizeof( vid ));
771 ibuf += sizeof( vid );
772 if (NULL == ( vol = getvolbyvid( vid )) ) {
773 return( AFPERR_PARAM );
776 if (vol->v_flags & AFPVOL_RO)
779 memcpy(&did, ibuf, sizeof( did ));
780 ibuf += sizeof( did );
781 if (NULL == ( dir = dirlookup( vol, did )) ) {
782 return afp_errno; /* was AFPERR_NOOBJ */
785 memcpy(&bitmap, ibuf, sizeof( bitmap ));
786 bitmap = ntohs( bitmap );
787 ibuf += sizeof( bitmap );
789 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
790 return get_afp_errno(AFPERR_PARAM);
793 if (path_isadir(s_path)) {
794 return( AFPERR_BADTYPE ); /* it's a directory */
797 if ( s_path->st_errno != 0 ) {
798 return( AFPERR_NOOBJ );
801 if ((u_long)ibuf & 1 ) {
805 if (AFP_OK == ( rc = setfilparams(vol, s_path, bitmap, ibuf )) ) {
806 setvoltime(obj, vol );
813 * cf AFP3.0.pdf page 252 for change_mdate and change_parent_mdate logic
816 extern struct path Cur_Path;
818 int setfilparams(struct vol *vol,
819 struct path *path, uint16_t f_bitmap, char *buf )
821 struct adouble ad, *adp;
823 int bit, isad = 1, err = AFP_OK;
825 u_char achar, *fdType, xyy[4]; /* uninitialized, OK 310105 */
826 uint16_t ashort, bshort, oshort;
829 uint16_t upriv_bit = 0;
833 int change_mdate = 0;
834 int change_parent_mdate = 0;
839 uint16_t bitmap = f_bitmap;
840 uint32_t cdate,bdate;
841 u_char finder_buf[32];
844 LOG(log_debug9, logtype_afpd, "begin setfilparams:");
847 adp = of_ad(vol, path, &ad);
848 upath = path->u_name;
850 if (!vol_unix_priv(vol) && check_access(upath, OPENACC_WR ) < 0) {
851 return AFPERR_ACCESS;
854 /* with unix priv maybe we have to change adouble file priv first */
856 while ( bitmap != 0 ) {
857 while (( bitmap & 1 ) == 0 ) {
864 memcpy(&ashort, buf, sizeof( ashort ));
865 buf += sizeof( ashort );
869 memcpy(&cdate, buf, sizeof(cdate));
870 buf += sizeof( cdate );
873 memcpy(&newdate, buf, sizeof( newdate ));
874 buf += sizeof( newdate );
878 memcpy(&bdate, buf, sizeof( bdate));
879 buf += sizeof( bdate );
883 memcpy(finder_buf, buf, 32 );
884 if (memcmp(buf,"slnkrhap",8)==0 && !S_ISLNK(path->st.st_mode)){
889 char buf[PATH_MAX+1];
890 if ((fp=open(path->u_name,O_RDONLY))>=0){
891 if ((len=read(fp,buf,PATH_MAX+1))){
892 if (unlink(path->u_name)==0){
894 erc = symlink(buf, path->u_name);
903 goto setfilparam_done;
908 case FILPBIT_UNIXPR :
909 if (!vol_unix_priv(vol)) {
910 /* this volume doesn't use unix priv */
916 change_parent_mdate = 1;
918 memcpy( &aint, buf, sizeof( aint ));
919 f_uid = ntohl (aint);
920 buf += sizeof( aint );
921 memcpy( &aint, buf, sizeof( aint ));
922 f_gid = ntohl (aint);
923 buf += sizeof( aint );
924 setfilowner(vol, f_uid, f_gid, path);
926 memcpy( &upriv, buf, sizeof( upriv ));
927 buf += sizeof( upriv );
928 upriv = ntohl (upriv);
929 if ((upriv & S_IWUSR)) {
930 setfilunixmode(vol, path, upriv);
937 case FILPBIT_PDINFO :
938 if (afp_version < 30) { /* else it's UTF8 name */
941 /* Keep special case to support crlf translations */
942 if ((unsigned int) achar == 0x04) {
943 fdType = (u_char *)"TEXT";
946 xyy[0] = ( u_char ) 'p';
957 /* break while loop */
966 /* second try with adouble open
968 if ( ad_open(adp, upath, ADFLAGS_HF, O_RDWR | O_CREAT, 0666) < 0) {
969 LOG(log_debug, logtype_afpd, "setfilparams: ad_open_metadata error");
971 * For some things, we don't need an adouble header:
972 * - change of modification date
973 * - UNIX privs (Bug-ID #2863424)
975 if (!vol_noadouble(vol) && (f_bitmap & ~(1<<FILPBIT_MDATE | 1<<FILPBIT_UNIXPR))) {
976 LOG(log_debug, logtype_afpd, "setfilparams: need adouble access");
977 return AFPERR_ACCESS;
979 LOG(log_debug, logtype_afpd, "setfilparams: no adouble perms, but only FILPBIT_MDATE and/or FILPBIT_UNIXPR");
981 } else if ((ad_get_MD_flags( adp ) & O_CREAT) ) {
982 ad_setname(adp, path->m_name);
987 while ( bitmap != 0 ) {
988 while (( bitmap & 1 ) == 0 ) {
995 ad_getattr(adp, &bshort);
997 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
998 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
1002 if ((bshort & htons(ATTRBIT_INVISIBLE)) != (oshort & htons(ATTRBIT_INVISIBLE)))
1003 change_parent_mdate = 1;
1004 ad_setattr(adp, bshort);
1006 case FILPBIT_CDATE :
1007 ad_setdate(adp, AD_DATE_CREATE, cdate);
1009 case FILPBIT_MDATE :
1011 case FILPBIT_BDATE :
1012 ad_setdate(adp, AD_DATE_BACKUP, bdate);
1014 case FILPBIT_FINFO :
1015 if (default_type( ad_entry( adp, ADEID_FINDERI ))
1017 ((em = getextmap( path->m_name )) &&
1018 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1019 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1020 || ((em = getdefextmap()) &&
1021 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1022 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1024 memcpy(finder_buf, ufinderi, 8 );
1026 memcpy(ad_entry( adp, ADEID_FINDERI ), finder_buf, 32 );
1028 case FILPBIT_UNIXPR :
1030 setfilunixmode(vol, path, upriv);
1033 case FILPBIT_PDINFO :
1034 if (afp_version < 30) { /* else it's UTF8 name */
1035 memcpy(ad_entry( adp, ADEID_FINDERI ), fdType, 4 );
1036 memcpy(ad_entry( adp, ADEID_FINDERI ) + 4, "pdos", 4 );
1041 err = AFPERR_BITMAP;
1042 goto setfilparam_done;
1049 if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
1050 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1054 ad_setdate(adp, AD_DATE_MODIFY, newdate);
1055 ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
1061 ad_close_metadata( adp);
1064 if (change_parent_mdate && gettimeofday(&tv, NULL) == 0) {
1065 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1066 bitmap = 1<<FILPBIT_MDATE;
1067 setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
1071 LOG(log_debug9, logtype_afpd, "end setfilparams:");
1077 * renamefile and copyfile take the old and new unix pathnames
1078 * and the new mac name.
1080 * sdir_fd source dir fd to which src path is relative (for openat et al semantics)
1081 * passing -1 means this is not used, src path is a full path
1082 * src the source path
1083 * dst the dest filename in current dir
1084 * newname the dest mac name
1085 * adp adouble struct of src file, if open, or & zeroed one
1088 int renamefile(const struct vol *vol, int sdir_fd, char *src, char *dst, char *newname, struct adouble *adp)
1092 LOG(log_debug, logtype_afpd,
1093 "renamefile: src[%d, \"%s\"] -> dst[\"%s\"]", sdir_fd, src, dst);
1095 if ( unix_rename( sdir_fd, src, -1, dst ) < 0 ) {
1098 return( AFPERR_NOOBJ );
1101 return( AFPERR_ACCESS );
1103 return AFPERR_VLOCK;
1104 case EXDEV : /* Cross device move -- try copy */
1105 /* NOTE: with open file it's an error because after the copy we will
1106 * get two files, it's fixable for our process (eg reopen the new file, get the
1107 * locks, and so on. But it doesn't solve the case with a second process
1109 if (adp->ad_open_forks) {
1110 /* FIXME warning in syslog so admin'd know there's a conflict ?*/
1111 return AFPERR_OLOCK; /* little lie */
1113 if (AFP_OK != ( rc = copyfile(vol, vol, sdir_fd, src, dst, newname, NULL )) ) {
1114 /* on error copyfile delete dest */
1117 return deletefile(vol, sdir_fd, src, 0);
1119 return( AFPERR_PARAM );
1123 if (vol->vfs->vfs_renamefile(vol, sdir_fd, src, dst) < 0 ) {
1127 /* try to undo the data fork rename,
1128 * we know we are on the same device
1131 unix_rename(-1, dst, sdir_fd, src );
1132 /* return the first error */
1135 return AFPERR_NOOBJ;
1138 return AFPERR_ACCESS ;
1140 return AFPERR_VLOCK;
1142 return AFPERR_PARAM ;
1147 /* don't care if we can't open the newly renamed ressource fork */
1148 if (ad_open(adp, dst, ADFLAGS_HF, O_RDWR) == 0) {
1149 ad_setname(adp, newname);
1151 ad_close( adp, ADFLAGS_HF );
1158 convert a Mac long name to an utf8 name,
1160 size_t mtoUTF8(const struct vol *vol, const char *src, size_t srclen, char *dest, size_t destlen)
1164 if ((size_t)-1 == (outlen = convert_string ( vol->v_maccharset, CH_UTF8_MAC, src, srclen, dest, destlen)) ) {
1170 /* ---------------- */
1171 int copy_path_name(const struct vol *vol, char *newname, char *ibuf)
1178 if ( type != 2 && !(afp_version >= 30 && type == 3) ) {
1184 if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
1185 if (afp_version >= 30) {
1186 /* convert it to UTF8
1188 if ((plen = mtoUTF8(vol, ibuf, plen, newname, AFPOBJ_TMPSIZ)) == (size_t)-1)
1192 strncpy( newname, ibuf, plen );
1193 newname[ plen ] = '\0';
1195 if (strlen(newname) != plen) {
1196 /* there's \0 in newname, e.g. it's a pathname not
1204 memcpy(&hint, ibuf, sizeof(hint));
1205 ibuf += sizeof(hint);
1207 memcpy(&len16, ibuf, sizeof(len16));
1208 ibuf += sizeof(len16);
1209 plen = ntohs(len16);
1212 if (plen > AFPOBJ_TMPSIZ) {
1215 strncpy( newname, ibuf, plen );
1216 newname[ plen ] = '\0';
1217 if (strlen(newname) != plen) {
1226 /* -----------------------------------
1228 int afp_copyfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1230 struct vol *s_vol, *d_vol;
1232 char *newname, *p, *upath;
1233 struct path *s_path;
1234 uint32_t sdid, ddid;
1235 int err, retvalue = AFP_OK;
1236 uint16_t svid, dvid;
1238 struct adouble ad, *adp;
1244 memcpy(&svid, ibuf, sizeof( svid ));
1245 ibuf += sizeof( svid );
1246 if (NULL == ( s_vol = getvolbyvid( svid )) ) {
1247 return( AFPERR_PARAM );
1250 memcpy(&sdid, ibuf, sizeof( sdid ));
1251 ibuf += sizeof( sdid );
1252 if (NULL == ( dir = dirlookup( s_vol, sdid )) ) {
1256 memcpy(&dvid, ibuf, sizeof( dvid ));
1257 ibuf += sizeof( dvid );
1258 memcpy(&ddid, ibuf, sizeof( ddid ));
1259 ibuf += sizeof( ddid );
1261 if (NULL == ( s_path = cname( s_vol, dir, &ibuf )) ) {
1262 return get_afp_errno(AFPERR_PARAM);
1264 if ( path_isadir(s_path) ) {
1265 return( AFPERR_BADTYPE );
1268 /* don't allow copies when the file is open.
1269 * XXX: the spec only calls for read/deny write access.
1270 * however, copyfile doesn't have any of that info,
1271 * and locks need to stay coherent. as a result,
1272 * we just balk if the file is opened already. */
1274 adp = of_ad(s_vol, s_path, &ad);
1276 if (ad_open(adp, s_path->u_name, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF, O_RDONLY, O_RDONLY) < 0) {
1277 return AFPERR_DENYCONF;
1279 denyreadset = (ad_testlock(adp, ADEID_DFORK, AD_FILELOCK_DENY_RD) != 0 ||
1280 ad_testlock(adp, ADEID_RFORK, AD_FILELOCK_DENY_RD) != 0 );
1283 retvalue = AFPERR_DENYCONF;
1287 newname = obj->newtmp;
1288 strcpy( newname, s_path->m_name );
1290 p = ctoupath( s_vol, curdir, newname );
1292 retvalue = AFPERR_PARAM;
1296 if (NULL == ( d_vol = getvolbyvid( dvid )) ) {
1297 retvalue = AFPERR_PARAM;
1301 if (d_vol->v_flags & AFPVOL_RO) {
1302 retvalue = AFPERR_VLOCK;
1306 if (NULL == ( dir = dirlookup( d_vol, ddid )) ) {
1307 retvalue = afp_errno;
1311 if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) {
1312 retvalue = get_afp_errno(AFPERR_NOOBJ);
1316 if ( *s_path->m_name != '\0' ) {
1317 retvalue =path_error(s_path, AFPERR_NOOBJ);
1321 /* one of the handful of places that knows about the path type */
1322 if (copy_path_name(d_vol, newname, ibuf) < 0) {
1323 retvalue = AFPERR_PARAM;
1326 /* newname is always only a filename so curdir *is* its
1329 if (NULL == (upath = mtoupath(d_vol, newname, curdir->d_did, utf8_encoding()))) {
1330 retvalue =AFPERR_PARAM;
1334 if ( (err = copyfile(s_vol, d_vol, -1, p, upath , newname, adp)) < 0 ) {
1340 setvoltime(obj, d_vol );
1343 ad_close( adp, ADFLAGS_DF |ADFLAGS_HF );
1347 /* ----------------------- */
1348 static int copy_all(const int dfd, const void *buf,
1354 LOG(log_debug9, logtype_afpd, "begin copy_all:");
1357 while (buflen > 0) {
1358 if ((cc = write(dfd, buf, buflen)) < 0) {
1370 LOG(log_debug9, logtype_afpd, "end copy_all:");
1376 /* --------------------------
1377 * copy only the fork data stream
1379 static int copy_fork(int eid, struct adouble *add, struct adouble *ads)
1386 if (eid == ADEID_DFORK) {
1387 sfd = ad_data_fileno(ads);
1388 dfd = ad_data_fileno(add);
1391 sfd = ad_reso_fileno(ads);
1392 dfd = ad_reso_fileno(add);
1395 if ((off_t)-1 == lseek(sfd, ad_getentryoff(ads, eid), SEEK_SET))
1398 if ((off_t)-1 == lseek(dfd, ad_getentryoff(add, eid), SEEK_SET))
1401 #if 0 /* ifdef SENDFILE_FLAVOR_LINUX */
1402 /* doesn't work With 2.6 FIXME, only check for EBADFD ? */
1406 #define BUF 128*1024*1024
1408 if (fstat(sfd, &st) == 0) {
1411 if ( offset >= st.st_size) {
1414 size = (st.st_size -offset > BUF)?BUF:st.st_size -offset;
1415 if ((cc = sys_sendfile(dfd, sfd, &offset, size)) < 0) {
1418 case EINVAL: /* there's no guarantee that all fs support sendfile */
1427 lseek(sfd, offset, SEEK_SET);
1431 if ((cc = read(sfd, filebuf, sizeof(filebuf))) < 0) {
1438 if (!cc || ((err = copy_all(dfd, filebuf, cc)) < 0)) {
1445 /* ----------------------------------
1446 * if newname is NULL (from directory.c) we don't want to copy the resource fork.
1447 * because we are doing it elsewhere.
1448 * currently if newname is NULL then adp is NULL.
1450 int copyfile(const struct vol *s_vol,
1451 const struct vol *d_vol,
1456 struct adouble *adp)
1458 struct adouble ads, add;
1465 LOG(log_debug, logtype_afpd, "copyfile(sfd:%d,s:'%s',d:'%s',n:'%s')",
1466 sfd, src, dst, newname);
1469 ad_init(&ads, s_vol->v_adouble, s_vol->v_ad_options);
1473 adflags = ADFLAGS_DF;
1475 adflags |= ADFLAGS_HF;
1478 if (ad_openat(adp, sfd, src, adflags | ADFLAGS_NOHF, O_RDONLY, O_RDONLY) < 0) {
1483 if (ad_meta_fileno(adp) == -1 && ad_reso_fileno(adp) == -1) { /* META / HF */
1484 /* no resource fork, don't create one for dst file */
1485 adflags &= ~ADFLAGS_HF;
1488 stat_result = fstat(ad_data_fileno(adp), &st); /* saving stat exit code, thus saving us on one more stat later on */
1490 if (stat_result < 0) {
1491 /* unlikely but if fstat fails, the default file mode will be 0666. */
1492 st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
1495 ad_init(&add, d_vol->v_adouble, d_vol->v_ad_options);
1496 if (ad_open(&add, dst, adflags, O_RDWR|O_CREAT|O_EXCL, st.st_mode, O_RDWR|O_CREAT|O_EXCL, st.st_mode) < 0) {
1498 ad_close( adp, adflags );
1499 if (EEXIST != ret_err) {
1500 deletefile(d_vol, -1, dst, 0);
1503 return AFPERR_EXIST;
1507 * XXX if the source and the dest don't use the same resource type it's broken
1509 if (ad_reso_fileno(adp) == -1 || 0 == (err = copy_fork(ADEID_RFORK, &add, adp))){
1510 /* copy the data fork */
1511 if ((err = copy_fork(ADEID_DFORK, &add, adp)) == 0) {
1512 if (ad_meta_fileno(adp) != -1)
1513 err = d_vol->vfs->vfs_copyfile(d_vol, sfd, src, dst);
1521 if (!ret_err && newname && (adflags & ADFLAGS_HF)) {
1522 /* set the new name in the resource fork */
1523 ad_copy_header(&add, adp);
1524 ad_setname(&add, newname);
1527 ad_close( adp, adflags );
1529 if (ad_close( &add, adflags ) <0) {
1534 deletefile(d_vol, -1, dst, 0);
1536 else if (stat_result == 0) {
1537 /* set dest modification date to src date */
1540 ut.actime = ut.modtime = st.st_mtime;
1542 /* FIXME netatalk doesn't use resource fork file date
1543 * but maybe we should set its modtime too.
1548 switch ( ret_err ) {
1554 return AFPERR_DFULL;
1556 return AFPERR_NOOBJ;
1558 return AFPERR_ACCESS;
1560 return AFPERR_VLOCK;
1562 return AFPERR_PARAM;
1566 /* -----------------------------------
1567 vol: not NULL delete cnid entry. then we are in curdir and file is a only filename
1568 checkAttrib: 1 check kFPDeleteInhibitBit (deletfile called by afp_delete)
1570 when deletefile is called we don't have lock on it, file is closed (for us)
1571 untrue if called by renamefile
1573 ad_open always try to open file RDWR first and ad_lock takes care of
1574 WRITE lock on read only file.
1577 static int check_attrib(struct adouble *adp)
1579 uint16_t bshort = 0;
1581 ad_getattr(adp, &bshort);
1583 * Does kFPDeleteInhibitBit (bit 8) set?
1585 if ((bshort & htons(ATTRBIT_NODELETE))) {
1586 return AFPERR_OLOCK;
1588 if ((bshort & htons(ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
1594 * dirfd can be used for unlinkat semantics
1596 int deletefile(const struct vol *vol, int dirfd, char *file, int checkAttrib)
1599 struct adouble *adp = NULL;
1600 int adflags, err = AFP_OK;
1603 LOG(log_debug, logtype_afpd, "deletefile('%s')", file);
1605 ad_init(&ad, vol->v_adouble, vol->v_ad_options);
1607 /* was EACCESS error try to get only metadata */
1608 /* we never want to create a resource fork here, we are going to delete it
1609 * moreover sometimes deletefile is called with a no existent file and
1610 * ad_open would create a 0 byte resource fork
1612 if ( ad_metadataat(dirfd, file, ADFLAGS_CHECK_OF, &ad) == 0 ) {
1613 if ((err = check_attrib(&ad))) {
1614 ad_close_metadata(&ad);
1621 /* try to open both forks at once */
1622 adflags = ADFLAGS_DF;
1623 if ( ad_openat(&ad, dirfd, file, adflags |ADFLAGS_HF|ADFLAGS_NOHF, O_RDONLY, O_RDONLY) < 0 ) {
1628 case EACCES: /* maybe it's a file with no write mode for us */
1629 break; /* was return AFPERR_ACCESS;*/
1642 if ( adp && ad_reso_fileno( adp ) != -1 ) { /* there's a resource fork */
1643 adflags |= ADFLAGS_HF;
1644 /* FIXME we have a pb here because we want to know if a file is open
1645 * there's a 'priority inversion' if you can't open the ressource fork RW
1646 * you can delete it if it's open because you can't get a write lock.
1648 * ADLOCK_FILELOCK means the whole ressource fork, not only after the
1651 * FIXME it doesn't work for RFORK open read only and fork open without deny mode
1653 if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
1659 if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
1661 } else if (!(err = vol->vfs->vfs_deletefile(vol, dirfd, file)) && !(err = netatalk_unlinkat(dirfd, file )) ) {
1663 if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file)))) {
1664 cnid_delete(vol->v_cdb, id);
1670 ad_close_metadata(&ad);
1673 ad_close( &ad, adflags ); /* ad_close removes locks if any */
1678 /* ------------------------------------ */
1679 /* return a file id */
1680 int afp_createid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1689 struct path *s_path;
1695 memcpy(&vid, ibuf, sizeof(vid));
1696 ibuf += sizeof(vid);
1698 if (NULL == ( vol = getvolbyvid( vid )) ) {
1699 return( AFPERR_PARAM);
1702 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1706 if (vol->v_flags & AFPVOL_RO)
1707 return AFPERR_VLOCK;
1709 memcpy(&did, ibuf, sizeof( did ));
1710 ibuf += sizeof(did);
1712 if (NULL == ( dir = dirlookup( vol, did )) ) {
1713 return afp_errno; /* was AFPERR_PARAM */
1716 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
1717 return get_afp_errno(AFPERR_NOOBJ); /* was AFPERR_PARAM */
1720 if ( path_isadir(s_path) ) {
1721 return( AFPERR_BADTYPE );
1724 upath = s_path->u_name;
1725 switch (s_path->st_errno) {
1727 break; /* success */
1730 return AFPERR_ACCESS;
1732 return AFPERR_NOOBJ;
1734 return AFPERR_PARAM;
1737 if ((id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath)))) {
1738 memcpy(rbuf, &id, sizeof(id));
1739 *rbuflen = sizeof(id);
1740 return AFPERR_EXISTID;
1743 if ((id = get_id(vol, NULL, st, did, upath, len)) != CNID_INVALID) {
1744 memcpy(rbuf, &id, sizeof(id));
1745 *rbuflen = sizeof(id);
1752 /* ------------------------------- */
1758 static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
1761 struct reenum *param = data;
1762 struct vol *vol = param->vol;
1763 cnid_t did = param->did;
1766 if ( lstat(de->d_name, &path.st) < 0 )
1769 /* update or add to cnid */
1770 aint = cnid_add(vol->v_cdb, &path.st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */
1775 /* --------------------
1776 * Ok the db is out of synch with the dir.
1777 * but if it's a deleted file we don't want to do it again and again.
1780 reenumerate_id(struct vol *vol, char *name, struct dir *dir)
1786 if (vol->v_cdb == NULL) {
1790 /* FIXME use of_statdir ? */
1791 if (lstat(name, &st)) {
1795 if (dirreenumerate(dir, &st)) {
1796 /* we already did it once and the dir haven't been modified */
1797 return dir->d_offcnt;
1801 data.did = dir->d_did;
1802 if ((ret = for_each_dirent(vol, name, reenumerate_loop, (void *)&data)) >= 0) {
1803 setdiroffcnt(curdir, &st, ret);
1804 dir->d_flags |= DIRF_CNID;
1810 /* ------------------------------
1811 resolve a file id */
1812 int afp_resolveid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1821 uint16_t vid, bitmap;
1823 static char buffer[12 + MAXPATHLEN + 1];
1824 int len = 12 + MAXPATHLEN + 1;
1829 memcpy(&vid, ibuf, sizeof(vid));
1830 ibuf += sizeof(vid);
1832 if (NULL == ( vol = getvolbyvid( vid )) ) {
1833 return( AFPERR_PARAM);
1836 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1840 memcpy(&id, ibuf, sizeof( id ));
1845 /* some MacOS versions after a catsearch do a *lot* of afp_resolveid with 0 */
1849 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1850 return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
1853 if (NULL == ( dir = dirlookup( vol, id )) ) {
1854 return AFPERR_NOID; /* idem AFPERR_PARAM */
1856 if (movecwd(vol, dir) < 0) {
1860 return AFPERR_ACCESS;
1864 return AFPERR_PARAM;
1868 memset(&path, 0, sizeof(path));
1869 path.u_name = upath;
1870 if ( of_stat(&path) < 0 ) {
1872 /* with nfs and our working directory is deleted */
1873 if (errno == ESTALE) {
1877 if ( errno == ENOENT && !retry) {
1878 /* cnid db is out of sync, reenumerate the directory and update ids */
1879 reenumerate_id(vol, ".", dir);
1887 return AFPERR_ACCESS;
1891 return AFPERR_PARAM;
1895 /* directories are bad */
1896 if (S_ISDIR(path.st.st_mode)) {
1897 /* OS9 and OSX don't return the same error code */
1898 return (afp_version >=30)?AFPERR_NOID:AFPERR_BADTYPE;
1901 memcpy(&bitmap, ibuf, sizeof(bitmap));
1902 bitmap = ntohs( bitmap );
1903 if (NULL == (path.m_name = utompath(vol, upath, cnid, utf8_encoding()))) {
1907 if (AFP_OK != (err = getfilparams(vol, bitmap, &path , curdir,
1908 rbuf + sizeof(bitmap), &buflen))) {
1911 *rbuflen = buflen + sizeof(bitmap);
1912 memcpy(rbuf, ibuf, sizeof(bitmap));
1917 /* ------------------------------ */
1918 int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1928 static char buffer[12 + MAXPATHLEN + 1];
1929 int len = 12 + MAXPATHLEN + 1;
1934 memcpy(&vid, ibuf, sizeof(vid));
1935 ibuf += sizeof(vid);
1937 if (NULL == ( vol = getvolbyvid( vid )) ) {
1938 return( AFPERR_PARAM);
1941 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1945 if (vol->v_flags & AFPVOL_RO)
1946 return AFPERR_VLOCK;
1948 memcpy(&id, ibuf, sizeof( id ));
1952 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1956 if (NULL == ( dir = dirlookup( vol, id )) ) {
1957 if (afp_errno == AFPERR_NOOBJ) {
1961 return( AFPERR_PARAM );
1965 if ((movecwd(vol, dir) < 0) || (lstat(upath, &st) < 0)) {
1969 return AFPERR_ACCESS;
1974 /* still try to delete the id */
1978 return AFPERR_PARAM;
1981 else if (S_ISDIR(st.st_mode)) /* directories are bad */
1982 return AFPERR_BADTYPE;
1985 if (cnid_delete(vol->v_cdb, fileid)) {
1988 return AFPERR_VLOCK;
1991 return AFPERR_ACCESS;
1993 return AFPERR_PARAM;
2000 /* ------------------------------ */
2001 static struct adouble *find_adouble(struct path *path, struct ofork **of, struct adouble *adp)
2005 if (path->st_errno) {
2006 switch (path->st_errno) {
2008 afp_errno = AFPERR_NOID;
2012 afp_errno = AFPERR_ACCESS;
2015 afp_errno = AFPERR_PARAM;
2020 /* we use file_access both for legacy Mac perm and
2021 * for unix privilege, rename will take care of folder perms
2023 if (file_access(path, OPENACC_WR ) < 0) {
2024 afp_errno = AFPERR_ACCESS;
2028 if ((*of = of_findname(path))) {
2029 /* reuse struct adouble so it won't break locks */
2033 ret = ad_open(adp, path->u_name, ADFLAGS_HF, O_RDONLY);
2035 if ( !ret && ad_reso_fileno(adp) != -1 && !(adp->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
2037 * The user must have the Read & Write privilege for both files in order to use this command.
2039 ad_close(adp, ADFLAGS_HF);
2040 afp_errno = AFPERR_ACCESS;
2047 #define APPLETEMP ".AppleTempXXXXXX"
2049 int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
2051 struct stat srcst, destst;
2053 struct dir *dir, *sdir;
2054 char *spath, temp[17], *p;
2055 char *supath, *upath;
2060 struct adouble *adsp = NULL;
2061 struct adouble *addp = NULL;
2062 struct ofork *s_of = NULL;
2063 struct ofork *d_of = NULL;
2076 memcpy(&vid, ibuf, sizeof(vid));
2077 ibuf += sizeof(vid);
2079 if (NULL == ( vol = getvolbyvid( vid )) ) {
2080 return( AFPERR_PARAM);
2083 if ((vol->v_flags & AFPVOL_RO))
2084 return AFPERR_VLOCK;
2086 /* source and destination dids */
2087 memcpy(&sid, ibuf, sizeof(sid));
2088 ibuf += sizeof(sid);
2089 memcpy(&did, ibuf, sizeof(did));
2090 ibuf += sizeof(did);
2093 if (NULL == (dir = dirlookup( vol, sid )) ) {
2094 return afp_errno; /* was AFPERR_PARAM */
2097 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2098 return get_afp_errno(AFPERR_NOOBJ);
2101 if ( path_isadir(path) ) {
2102 return AFPERR_BADTYPE; /* it's a dir */
2105 /* save some stuff */
2108 spath = obj->oldtmp;
2109 supath = obj->newtmp;
2110 strcpy(spath, path->m_name);
2111 strcpy(supath, path->u_name); /* this is for the cnid changing */
2112 p = absupath( vol, sdir, supath);
2114 /* pathname too long */
2115 return AFPERR_PARAM ;
2118 ad_init(&ads, vol->v_adouble, vol->v_ad_options);
2119 if (!(adsp = find_adouble( path, &s_of, &ads))) {
2123 /* ***** from here we may have resource fork open **** */
2125 /* look for the source cnid. if it doesn't exist, don't worry about
2127 sid = cnid_lookup(vol->v_cdb, &srcst, sdir->d_did, supath,slen = strlen(supath));
2129 if (NULL == ( dir = dirlookup( vol, did )) ) {
2130 err = afp_errno; /* was AFPERR_PARAM */
2131 goto err_exchangefile;
2134 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2135 err = get_afp_errno(AFPERR_NOOBJ);
2136 goto err_exchangefile;
2139 if ( path_isadir(path) ) {
2140 err = AFPERR_BADTYPE;
2141 goto err_exchangefile;
2144 /* FPExchangeFiles is the only call that can return the SameObj
2146 if ((curdir == sdir) && strcmp(spath, path->m_name) == 0) {
2147 err = AFPERR_SAMEOBJ;
2148 goto err_exchangefile;
2151 ad_init(&add, vol->v_adouble, vol->v_ad_options);
2152 if (!(addp = find_adouble( path, &d_of, &add))) {
2154 goto err_exchangefile;
2158 /* they are not on the same device and at least one is open
2159 * FIXME broken for for crossdev and adouble v2
2162 crossdev = (srcst.st_dev != destst.st_dev);
2163 if (/* (d_of || s_of) && */ crossdev) {
2165 goto err_exchangefile;
2168 /* look for destination id. */
2169 upath = path->u_name;
2170 did = cnid_lookup(vol->v_cdb, &destst, curdir->d_did, upath, dlen = strlen(upath));
2172 /* construct a temp name.
2173 * NOTE: the temp file will be in the dest file's directory. it
2174 * will also be inaccessible from AFP. */
2175 memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
2176 if (!mktemp(temp)) {
2178 goto err_exchangefile;
2182 /* FIXME we need to close fork for copy, both s_of and d_of are null */
2183 ad_close(adsp, ADFLAGS_HF);
2184 ad_close(addp, ADFLAGS_HF);
2187 /* now, quickly rename the file. we error if we can't. */
2188 if ((err = renamefile(vol, -1, p, temp, temp, adsp)) != AFP_OK)
2189 goto err_exchangefile;
2190 of_rename(vol, s_of, sdir, spath, curdir, temp);
2192 /* rename destination to source */
2193 if ((err = renamefile(vol, -1, upath, p, spath, addp)) != AFP_OK)
2194 goto err_src_to_tmp;
2195 of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
2197 /* rename temp to destination */
2198 if ((err = renamefile(vol, -1, temp, upath, path->m_name, adsp)) != AFP_OK)
2199 goto err_dest_to_src;
2200 of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
2202 /* id's need switching. src -> dest and dest -> src.
2203 * we need to re-stat() if it was a cross device copy.
2206 cnid_delete(vol->v_cdb, sid);
2208 cnid_delete(vol->v_cdb, did);
2210 if ((did && ( (crossdev && lstat( upath, &srcst) < 0) ||
2211 cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
2213 (sid && ( (crossdev && lstat(p, &destst) < 0) ||
2214 cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
2219 err = AFPERR_ACCESS;
2224 goto err_temp_to_dest;
2227 /* here we need to reopen if crossdev */
2228 if (sid && ad_setid(addp, destst.st_dev, destst.st_ino, sid, sdir->d_did, vol->v_stamp))
2233 if (did && ad_setid(adsp, srcst.st_dev, srcst.st_ino, did, curdir->d_did, vol->v_stamp))
2238 /* change perms, src gets dest perm and vice versa */
2243 LOG(log_error, logtype_afpd, "seteuid failed %s", strerror(errno));
2244 err = AFP_OK; /* ignore error */
2245 goto err_temp_to_dest;
2249 * we need to exchange ACL entries as well
2251 /* exchange_acls(vol, p, upath); */
2256 path->m_name = NULL;
2257 path->u_name = upath;
2259 setfilunixmode(vol, path, destst.st_mode);
2260 setfilowner(vol, destst.st_uid, destst.st_gid, path);
2267 setfilunixmode(vol, path, srcst.st_mode);
2268 setfilowner(vol, srcst.st_uid, srcst.st_gid, path);
2270 if ( setegid(gid) < 0 || seteuid(uid) < 0) {
2271 LOG(log_error, logtype_afpd, "can't seteuid back %s", strerror(errno));
2276 goto err_exchangefile;
2278 /* all this stuff is so that we can unwind a failed operation
2281 /* rename dest to temp */
2282 renamefile(vol, -1, upath, temp, temp, adsp);
2283 of_rename(vol, s_of, curdir, upath, curdir, temp);
2286 /* rename source back to dest */
2287 renamefile(vol, -1, p, upath, path->m_name, addp);
2288 of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
2291 /* rename temp back to source */
2292 renamefile(vol, -1, temp, p, spath, adsp);
2293 of_rename(vol, s_of, curdir, temp, sdir, spath);
2296 if ( !s_of && adsp && ad_meta_fileno(adsp) != -1 ) { /* META */
2297 ad_close(adsp, ADFLAGS_HF);
2299 if ( !d_of && addp && ad_meta_fileno(addp) != -1 ) {/* META */
2300 ad_close(addp, ADFLAGS_HF);
2304 if ((cached = dircache_search_by_did(vol, sid)) != NULL)
2305 (void)dir_remove(vol, cached);
2306 if ((cached = dircache_search_by_did(vol, did)) != NULL)
2307 (void)dir_remove(vol, cached);