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>
27 #include <atalk/netatalk_conf.h>
29 #include "directory.h"
38 /* the format for the finderinfo fields (from IM: Toolbox Essentials):
39 * field bytes subfield bytes
42 * ioFlFndrInfo 16 -> type 4 type field
43 * creator 4 creator field
44 * flags 2 finder flags:
46 * location 4 location in window
47 * folder 2 window that contains file
49 * ioFlXFndrInfo 16 -> iconID 2 icon id
51 * script 1 script system
53 * commentID 2 comment id
54 * putawayID 4 home directory id
57 const u_char ufinderi[ADEDLEN_FINDERI] = {
58 0, 0, 0, 0, 0, 0, 0, 0,
59 1, 0, 0, 0, 0, 0, 0, 0,
60 0, 0, 0, 0, 0, 0, 0, 0,
61 0, 0, 0, 0, 0, 0, 0, 0
64 static const u_char old_ufinderi[] = {
65 'T', 'E', 'X', 'T', 'U', 'N', 'I', 'X'
68 /* ----------------------
70 static int default_type(void *finder)
72 if (!memcmp(finder, ufinderi, 8) || !memcmp(finder, old_ufinderi, 8))
77 /* FIXME path : unix or mac name ? (for now it's unix name ) */
78 void *get_finderinfo(const struct vol *vol, const char *upath, struct adouble *adp, void *data, int islink)
81 void *ad_finder = NULL;
85 ad_finder = ad_entry(adp, ADEID_FINDERI);
88 memcpy(data, ad_finder, ADEDLEN_FINDERI);
90 if (default_type(ad_finder))
94 memcpy(data, ufinderi, ADEDLEN_FINDERI);
96 if (vol_inv_dots(vol) && *upath == '.') { /* make it invisible */
99 ashort = htons(FINDERINFO_INVISIBLE);
100 memcpy((char *)data + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
104 if (islink && !vol_syml_opt(vol)) {
106 memcpy(&linkflag, (char *)data + FINDERINFO_FRFLAGOFF, 2);
107 linkflag |= htons(FINDERINFO_ISALIAS);
108 memcpy((char *)data + FINDERINFO_FRFLAGOFF, &linkflag, 2);
109 memcpy((char *)data + FINDERINFO_FRTYPEOFF,"slnk",4);
110 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));
122 /* ---------------------
124 char *set_name(const struct vol *vol, char *data, cnid_t pid, char *name, cnid_t id, uint32_t utf8)
129 aint = strlen( name );
133 if (utf8_encoding(vol->v_obj)) {
134 /* but name is an utf8 mac name */
137 /* global static variable... */
139 if (!(u = mtoupath(vol, name, pid, 1)) || !(m = utompath(vol, u, id, 0))) {
148 if (aint > MACFILELEN)
155 if (aint > UTF8FILELEN_EARLY) /* FIXME safeguard, anyway if no ascii char it's game over*/
156 aint = UTF8FILELEN_EARLY;
158 utf8 = vol->v_kTextEncoding;
159 memcpy(data, &utf8, sizeof(utf8));
160 data += sizeof(utf8);
163 memcpy(data, &temp, sizeof(temp));
164 data += sizeof(temp);
167 memcpy( data, src, aint );
177 * FIXME: PDINFO is UTF8 and doesn't need adp
179 #define PARAM_NEED_ADP(b) ((b) & ((1 << FILPBIT_ATTR) |\
180 (1 << FILPBIT_CDATE) |\
181 (1 << FILPBIT_MDATE) |\
182 (1 << FILPBIT_BDATE) |\
183 (1 << FILPBIT_FINFO) |\
184 (1 << FILPBIT_RFLEN) |\
185 (1 << FILPBIT_EXTRFLEN) |\
186 (1 << FILPBIT_PDINFO) |\
187 (1 << FILPBIT_FNUM) |\
188 (1 << FILPBIT_UNIXPR)))
191 * @brief Get CNID for did/upath args both from database and adouble file
193 * 1. Get the objects CNID as stored in its adouble file
194 * 2. Get the objects CNID from the database
195 * 3. If there's a problem with a "dbd" database, fallback to "tdb" in memory
196 * 4. In case 2 and 3 differ, store 3 in the adouble file
198 * @param vol (rw) volume
199 * @param adp (rw) adouble struct of object upath, might be NULL
200 * @param st (r) stat of upath, must NOT be NULL
201 * @param did (r) parent CNID of upath
202 * @param upath (r) name of object
203 * @param len (r) strlen of upath
205 uint32_t get_id(struct vol *vol,
207 const struct stat *st,
212 static int first = 1; /* mark if this func is called the first time */
214 uint32_t dbcnid = CNID_INVALID;
217 if (vol->v_cdb != NULL) {
218 /* prime aint with what we think is the cnid, set did to zero for
219 catching moved files */
220 adcnid = ad_getid(adp, st->st_dev, st->st_ino, 0, vol->v_stamp); /* (1) */
222 AFP_CNID_START("cnid_add");
223 dbcnid = cnid_add(vol->v_cdb, st, did, upath, len, adcnid); /* (2) */
226 /* Throw errors if cnid_add fails. */
227 if (dbcnid == CNID_INVALID) {
229 case CNID_ERR_CLOSE: /* the db is closed */
232 LOG(log_error, logtype_afpd, "get_id: Incorrect parameters passed to cnid_add");
233 afp_errno = AFPERR_PARAM;
236 afp_errno = AFPERR_PARAM;
239 /* Close CNID backend if "dbd" and switch to temp in-memory "tdb" */
240 /* we have to do it here for "dbd" because it uses "lazy opening" */
241 /* In order to not end in a loop somehow with goto restart below */
243 if (first && (strcmp(vol->v_cnidscheme, "dbd") == 0)) { /* (3) */
244 cnid_close(vol->v_cdb);
245 free(vol->v_cnidscheme);
246 vol->v_cnidscheme = strdup("tdb");
248 int flags = CNID_FLAG_MEMORY;
249 if ((vol->v_flags & AFPVOL_NODEV)) {
250 flags |= CNID_FLAG_NODEV;
252 LOG(log_error, logtype_afpd, "Reopen volume %s using in memory temporary CNID DB.",
254 vol->v_cdb = cnid_open(vol->v_path, vol->v_umask, "tdb", flags, NULL, NULL);
256 if (!(vol->v_flags & AFPVOL_TM)) {
257 vol->v_flags |= AFPVOL_RO;
258 setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB instead."
259 "Check server messages for details. Switching to read-only mode.");
260 kill(getpid(), SIGUSR2);
262 goto restart; /* now 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!");
268 afp_errno = AFPERR_MISC;
272 else if (adp && adcnid && (adcnid != dbcnid)) { /* 4 */
273 /* Update the ressource fork. For a folder adp is always null */
274 LOG(log_debug, logtype_afpd, "get_id(%s/%s): calling ad_setid(old: %u, new: %u)",
275 getcwdpath(), upath, htonl(adcnid), htonl(dbcnid));
276 if (ad_setid(adp, st->st_dev, st->st_ino, dbcnid, did, vol->v_stamp)) {
277 if (ad_flush(adp) != 0)
278 LOG(log_error, logtype_afpd, "get_id(\"%s\"): can't flush", fullpathname(upath));
288 /* -------------------------- */
289 int getmetadata(const AFPObj *obj,
292 struct path *path, struct dir *dir,
293 char *buf, size_t *buflen, struct adouble *adp)
295 char *data, *l_nameoff = NULL, *upath;
296 char *utf_nameoff = NULL;
301 u_char achar, fdType[4];
306 LOG(log_debug, logtype_afpd, "getmetadata(\"%s\")", path->u_name);
308 upath = path->u_name;
312 if ( ((bitmap & ( (1 << FILPBIT_FINFO)|(1 << FILPBIT_LNAME)|(1 <<FILPBIT_PDINFO) ) ) && !path->m_name)
313 || (bitmap & ( (1 << FILPBIT_LNAME) ) && utf8_encoding(obj)) /* FIXME should be m_name utf8 filename */
314 || (bitmap & (1 << FILPBIT_FNUM))) {
317 struct dir *cachedfile;
318 int len = strlen(upath);
319 if ((cachedfile = dircache_search_by_name(vol, dir, upath, len)) != NULL)
320 id = cachedfile->d_did;
322 id = get_id(vol, adp, st, dir->d_did, upath, len);
324 /* Add it to the cache */
325 LOG(log_debug, logtype_afpd, "getmetadata: caching: did:%u, \"%s\", cnid:%u",
326 ntohl(dir->d_did), upath, ntohl(id));
328 /* Get macname from unixname first */
329 if (path->m_name == NULL) {
330 if ((path->m_name = utompath(vol, upath, id, utf8_encoding(obj))) == NULL) {
331 LOG(log_error, logtype_afpd, "getmetadata: utompath error");
337 if (((fullpath = bstrcpy(dir->d_fullpath)) == NULL)
338 || (bconchar(fullpath, '/') != BSTR_OK)
339 || (bcatcstr(fullpath, upath)) != BSTR_OK) {
340 LOG(log_error, logtype_afpd, "getmetadata: fullpath: %s", strerror(errno));
345 if ((cachedfile = dir_new(path->m_name, upath, vol, dir->d_did, id, fullpath, st)) == NULL) {
346 LOG(log_error, logtype_afpd, "getmetadata: error from dir_new");
350 if ((dircache_add(vol, cachedfile)) != 0) {
351 LOG(log_error, logtype_afpd, "getmetadata: fatal dircache error");
359 if (id == CNID_INVALID)
363 path->m_name = utompath(vol, upath, id, utf8_encoding(vol->v_obj));
366 while ( bitmap != 0 ) {
367 while (( bitmap & 1 ) == 0 ) {
375 ad_getattr(adp, &ashort);
376 } else if (vol_inv_dots(vol) && *upath == '.') {
377 ashort = htons(ATTRBIT_INVISIBLE);
381 /* FIXME do we want a visual clue if the file is read only
384 accessmode(vol, ".", &ma, dir , NULL);
385 if ((ma.ma_user & AR_UWRITE)) {
386 accessmode(vol, upath, &ma, dir , st);
387 if (!(ma.ma_user & AR_UWRITE)) {
388 ashort |= htons(ATTRBIT_NOWRITE);
392 memcpy(data, &ashort, sizeof( ashort ));
393 data += sizeof( ashort );
394 LOG(log_debug, logtype_afpd, "metadata('%s'): AFP Attributes: %04x",
395 path->u_name, ntohs(ashort));
399 memcpy(data, &dir->d_did, sizeof( uint32_t ));
400 data += sizeof( uint32_t );
401 LOG(log_debug, logtype_afpd, "metadata('%s'): Parent DID: %u",
402 path->u_name, ntohl(dir->d_did));
406 if (!adp || (ad_getdate(adp, AD_DATE_CREATE, &aint) < 0))
407 aint = AD_DATE_FROM_UNIX(st->st_mtime);
408 memcpy(data, &aint, sizeof( aint ));
409 data += sizeof( aint );
413 if ( adp && (ad_getdate(adp, AD_DATE_MODIFY, &aint) == 0)) {
414 if ((st->st_mtime > AD_DATE_TO_UNIX(aint))) {
415 aint = AD_DATE_FROM_UNIX(st->st_mtime);
418 aint = AD_DATE_FROM_UNIX(st->st_mtime);
420 memcpy(data, &aint, sizeof( int ));
421 data += sizeof( int );
425 if (!adp || (ad_getdate(adp, AD_DATE_BACKUP, &aint) < 0))
426 aint = AD_DATE_START;
427 memcpy(data, &aint, sizeof( int ));
428 data += sizeof( int );
432 get_finderinfo(vol, upath, adp, (char *)data,S_ISLNK(st->st_mode));
433 data += ADEDLEN_FINDERI;
438 data += sizeof( uint16_t );
442 memset(data, 0, sizeof(uint16_t));
443 data += sizeof( uint16_t );
447 memcpy(data, &id, sizeof( id ));
448 data += sizeof( id );
449 LOG(log_debug, logtype_afpd, "metadata('%s'): CNID: %u",
450 path->u_name, ntohl(id));
454 if (st->st_size > 0xffffffff)
457 aint = htonl( st->st_size );
458 memcpy(data, &aint, sizeof( aint ));
459 data += sizeof( aint );
462 case FILPBIT_RFLEN: {
465 if (adp->ad_rlen > 0xffffffff)
468 aint = htonl( adp->ad_rlen);
470 rlen = ad_reso_size(path->u_name, 0, NULL);
471 if (rlen > 0xffffffff)
475 memcpy(data, &aint, sizeof( aint ));
476 data += sizeof( aint );
480 /* Current client needs ProDOS info block for this file.
481 Use simple heuristic and let the Mac "type" string tell
482 us what the PD file code should be. Everything gets a
483 subtype of 0x0000 unless the original value was hashed
484 to "pXYZ" when we created it. See IA, Ver 2.
485 <shirsch@adelphia.net> */
486 case FILPBIT_PDINFO :
487 if (obj->afp_version >= 30) { /* UTF8 name */
488 utf8 = kTextEncodingUTF8;
490 data += sizeof( uint16_t );
492 memcpy(data, &aint, sizeof( aint ));
493 data += sizeof( aint );
497 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
499 if ( memcmp( fdType, "TEXT", 4 ) == 0 ) {
503 else if ( memcmp( fdType, "PSYS", 4 ) == 0 ) {
507 else if ( memcmp( fdType, "PS16", 4 ) == 0 ) {
511 else if ( memcmp( fdType, "BINA", 4 ) == 0 ) {
515 else if ( fdType[0] == 'p' ) {
517 ashort = (fdType[2] * 256) + fdType[3];
531 memcpy(data, &ashort, sizeof( ashort ));
532 data += sizeof( ashort );
533 memset(data, 0, sizeof( ashort ));
534 data += sizeof( ashort );
537 case FILPBIT_EXTDFLEN:
538 aint = htonl(st->st_size >> 32);
539 memcpy(data, &aint, sizeof( aint ));
540 data += sizeof( aint );
541 aint = htonl(st->st_size);
542 memcpy(data, &aint, sizeof( aint ));
543 data += sizeof( aint );
545 case FILPBIT_EXTRFLEN:
547 aint = htonl(adp->ad_rlen >> 32);
548 memcpy(data, &aint, sizeof( aint ));
549 data += sizeof( aint );
550 aint = htonl(adp->ad_rlen);
551 memcpy(data, &aint, sizeof( aint ));
552 data += sizeof( aint );
554 int64_t rlen = hton64(ad_reso_size(path->u_name, 0, NULL));
555 memcpy(data, &rlen, sizeof(rlen));
556 data += sizeof(rlen);
559 case FILPBIT_UNIXPR :
560 /* accessmode may change st_mode with ACLs */
561 accessmode(obj, vol, upath, &ma, dir , st);
563 aint = htonl(st->st_uid);
564 memcpy( data, &aint, sizeof( aint ));
565 data += sizeof( aint );
566 aint = htonl(st->st_gid);
567 memcpy( data, &aint, sizeof( aint ));
568 data += sizeof( aint );
571 type == slnk indicates an OSX style symlink,
572 we have to add S_IFLNK to the mode, otherwise
573 10.3 clients freak out. */
577 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
578 if ( memcmp( fdType, "slnk", 4 ) == 0 ) {
584 memcpy( data, &aint, sizeof( aint ));
585 data += sizeof( aint );
587 *data++ = ma.ma_user;
588 *data++ = ma.ma_world;
589 *data++ = ma.ma_group;
590 *data++ = ma.ma_owner;
594 return( AFPERR_BITMAP );
600 ashort = htons( data - buf );
601 memcpy(l_nameoff, &ashort, sizeof( ashort ));
602 data = set_name(vol, data, dir->d_did, path->m_name, id, 0);
605 ashort = htons( data - buf );
606 memcpy(utf_nameoff, &ashort, sizeof( ashort ));
607 data = set_name(vol, data, dir->d_did, path->m_name, id, utf8);
609 *buflen = data - buf;
613 /* ----------------------- */
614 int getfilparams(const AFPObj *obj, struct vol *vol, uint16_t bitmap, struct path *path,
615 struct dir *dir, char *buf, size_t *buflen, int in_enumerate)
617 struct adouble ad, *adp;
620 int flags; /* uninitialized ok */
622 LOG(log_debug, logtype_afpd, "getfilparams(\"%s\")", path->u_name);
624 opened = PARAM_NEED_ADP(bitmap);
630 * Dont check for and resturn open fork attributes when enumerating
631 * This saves a lot of syscalls, the client will hopefully only use the result
632 * in FPGetFileParms where we return the correct value
634 flags = (!in_enumerate &&(bitmap & (1 << FILPBIT_ATTR))) ? ADFLAGS_CHECK_OF : 0;
636 adp = of_ad(vol, path, &ad);
637 upath = path->u_name;
639 if ( ad_metadata( upath, flags, adp) < 0 ) {
642 LOG(log_error, logtype_afpd, "getfilparams(%s): %s: check resource fork permission?",
643 upath, strerror(errno));
644 return AFPERR_ACCESS;
646 LOG(log_error, logtype_afpd, "getfilparams(%s): bad resource fork", upath);
655 rc = getmetadata(obj, vol, bitmap, path, dir, buf, buflen, adp);
658 ad_close(adp, ADFLAGS_HF | flags);
663 /* ----------------------------- */
664 int afp_createfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
669 struct ofork *of = NULL;
671 int creatf, did, openf, retvalue = AFP_OK;
677 creatf = (unsigned char) *ibuf++;
679 memcpy(&vid, ibuf, sizeof( vid ));
680 ibuf += sizeof( vid );
682 if (NULL == ( vol = getvolbyvid( vid )) )
683 return( AFPERR_PARAM );
685 if (vol->v_flags & AFPVOL_RO)
688 memcpy(&did, ibuf, sizeof( did));
689 ibuf += sizeof( did );
691 if (NULL == ( dir = dirlookup( vol, did )) )
694 if (NULL == ( s_path = cname( vol, dir, &ibuf )) )
695 return get_afp_errno(AFPERR_PARAM);
696 if ( *s_path->m_name == '\0' )
697 return( AFPERR_BADTYPE );
699 upath = s_path->u_name;
702 /* if upath is deleted we already in trouble anyway */
703 if ((of = of_findname(vol, s_path))) {
711 openf = ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_TRUNC;
713 /* on a soft create, if the file is open then ad_open won't fail
714 because open syscall is not called */
715 openf = ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_EXCL;
717 if (ad_open(&ad, upath, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | openf, 0666) < 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 LOG(log_info, logtype_afpd, "afp_createfile: DISK FULL");
730 return( AFPERR_DFULL );
732 return( AFPERR_PARAM );
735 if ( ad_meta_fileno( &ad ) == -1 ) { /* Hard META / HF */
736 /* FIXME with hard create on an existing file, we already
737 * corrupted the data file.
739 netatalk_unlink( upath );
740 ad_close( &ad, ADFLAGS_DF );
741 return AFPERR_ACCESS;
744 path = s_path->m_name;
745 ad_setname(&ad, path);
748 if (lstat(upath, &st) != 0) {
749 LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): stat: %s",
750 upath, strerror(errno));
751 ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF);
756 if ((id = get_id(vol, &ad, &st, dir->d_did, upath, strlen(upath))) == CNID_INVALID) {
757 LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): CNID error", upath);
758 goto createfile_iderr;
760 (void)ad_setid(&ad, st.st_dev, st.st_ino, id, dir->d_did, vol->v_stamp);
764 ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF );
765 fce_register(FCE_FILE_CREATE, fullpathname(upath), NULL, fce_file);
769 setvoltime(obj, vol );
774 int afp_setfilparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
780 uint16_t vid, bitmap;
785 memcpy(&vid, ibuf, sizeof( vid ));
786 ibuf += sizeof( vid );
787 if (NULL == ( vol = getvolbyvid( vid )) ) {
788 return( AFPERR_PARAM );
791 if (vol->v_flags & AFPVOL_RO)
794 memcpy(&did, ibuf, sizeof( did ));
795 ibuf += sizeof( did );
796 if (NULL == ( dir = dirlookup( vol, did )) ) {
797 return afp_errno; /* was AFPERR_NOOBJ */
800 memcpy(&bitmap, ibuf, sizeof( bitmap ));
801 bitmap = ntohs( bitmap );
802 ibuf += sizeof( bitmap );
804 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
805 return get_afp_errno(AFPERR_PARAM);
808 if (path_isadir(s_path)) {
809 return( AFPERR_BADTYPE ); /* it's a directory */
812 if ( s_path->st_errno != 0 ) {
813 return( AFPERR_NOOBJ );
816 if ((u_long)ibuf & 1 ) {
820 if (AFP_OK == ( rc = setfilparams(obj, vol, s_path, bitmap, ibuf )) ) {
821 setvoltime(obj, vol );
828 * cf AFP3.0.pdf page 252 for change_mdate and change_parent_mdate logic
831 extern struct path Cur_Path;
833 int setfilparams(const AFPObj *obj, struct vol *vol,
834 struct path *path, uint16_t f_bitmap, char *buf )
836 struct adouble ad, *adp;
838 int bit, isad = 1, err = AFP_OK;
840 u_char achar, *fdType, xyy[4]; /* uninitialized, OK 310105 */
841 uint16_t ashort, bshort, oshort;
844 uint16_t upriv_bit = 0;
846 int change_mdate = 0;
847 int change_parent_mdate = 0;
852 uint16_t bitmap = f_bitmap;
853 uint32_t cdate,bdate;
854 u_char finder_buf[32];
855 int symlinked = S_ISLNK(path->st.st_mode);
858 char symbuf[MAXPATHLEN+1];
861 LOG(log_debug9, logtype_afpd, "begin setfilparams:");
864 adp = of_ad(vol, path, &ad);
865 upath = path->u_name;
867 if (!vol_unix_priv(vol) && check_access(obj, vol, upath, OPENACC_WR ) < 0) {
868 return AFPERR_ACCESS;
871 /* with unix priv maybe we have to change adouble file priv first */
873 while ( bitmap != 0 ) {
874 while (( bitmap & 1 ) == 0 ) {
881 memcpy(&ashort, buf, sizeof( ashort ));
882 buf += sizeof( ashort );
886 memcpy(&cdate, buf, sizeof(cdate));
887 buf += sizeof( cdate );
890 memcpy(&newdate, buf, sizeof( newdate ));
891 buf += sizeof( newdate );
895 memcpy(&bdate, buf, sizeof( bdate));
896 buf += sizeof( bdate );
900 if (memcmp(buf,"slnkrhap",8) == 0
901 && !(S_ISLNK(path->st.st_mode))
902 && !(vol->v_flags & AFPVOL_FOLLOWSYM)) {
903 /* request to turn this into a symlink */
904 if ((fp = open(path->u_name, O_RDONLY)) == -1) {
906 goto setfilparam_done;
908 len = read(fp, symbuf, MAXPATHLEN);
912 goto setfilparam_done;
914 if (unlink(path->u_name) != 0) {
916 goto setfilparam_done;
919 if (symlink(symbuf, path->u_name) != 0) {
921 goto setfilparam_done;
926 memcpy(finder_buf, buf, 32 );
929 case FILPBIT_UNIXPR :
930 if (!vol_unix_priv(vol)) {
931 /* this volume doesn't use unix priv */
937 change_parent_mdate = 1;
939 memcpy( &aint, buf, sizeof( aint ));
940 f_uid = ntohl (aint);
941 buf += sizeof( aint );
942 memcpy( &aint, buf, sizeof( aint ));
943 f_gid = ntohl (aint);
944 buf += sizeof( aint );
945 setfilowner(vol, f_uid, f_gid, path);
947 memcpy( &upriv, buf, sizeof( upriv ));
948 buf += sizeof( upriv );
949 upriv = ntohl (upriv);
950 if ((upriv & S_IWUSR)) {
951 setfilunixmode(vol, path, upriv);
958 case FILPBIT_PDINFO :
959 if (obj->afp_version < 30) { /* else it's UTF8 name */
962 /* Keep special case to support crlf translations */
963 if ((unsigned int) achar == 0x04) {
964 fdType = (u_char *)"TEXT";
967 xyy[0] = ( u_char ) 'p';
978 /* break while loop */
987 /* second try with adouble open
989 if (ad_open(adp, upath, ADFLAGS_HF | ADFLAGS_RDWR | ADFLAGS_CREATE, 0666) < 0) {
990 LOG(log_debug, logtype_afpd, "setfilparams: ad_open_metadata error");
992 * For some things, we don't need an adouble header:
993 * - change of modification date
994 * - UNIX privs (Bug-ID #2863424)
996 if (!symlinked && f_bitmap & ~(1<<FILPBIT_MDATE | 1<<FILPBIT_UNIXPR)) {
997 LOG(log_debug, logtype_afpd, "setfilparams: need adouble access");
998 return AFPERR_ACCESS;
1000 LOG(log_debug, logtype_afpd, "setfilparams: no adouble perms, but only FILPBIT_MDATE and/or FILPBIT_UNIXPR");
1002 } else if ((ad_get_MD_flags( adp ) & O_CREAT) ) {
1003 ad_setname(adp, path->m_name);
1005 if ((id = get_id(vol, adp, &path->st, curdir->d_did, upath, strlen(upath))) == CNID_INVALID) {
1006 LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): CNID error", upath);
1009 (void)ad_setid(adp, path->st.st_dev, path->st.st_ino, id, curdir->d_did, vol->v_stamp);
1014 while ( bitmap != 0 ) {
1015 while (( bitmap & 1 ) == 0 ) {
1022 ad_getattr(adp, &bshort);
1024 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
1025 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
1029 if ((bshort & htons(ATTRBIT_INVISIBLE)) != (oshort & htons(ATTRBIT_INVISIBLE)))
1030 change_parent_mdate = 1;
1031 ad_setattr(adp, bshort);
1033 case FILPBIT_CDATE :
1034 ad_setdate(adp, AD_DATE_CREATE, cdate);
1036 case FILPBIT_MDATE :
1038 case FILPBIT_BDATE :
1039 ad_setdate(adp, AD_DATE_BACKUP, bdate);
1041 case FILPBIT_FINFO :
1042 if (default_type( ad_entry( adp, ADEID_FINDERI ))
1044 ((em = getextmap( path->m_name )) &&
1045 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1046 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1047 || ((em = getdefextmap()) &&
1048 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1049 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1051 memcpy(finder_buf, ufinderi, 8 );
1053 memcpy(ad_entry( adp, ADEID_FINDERI ), finder_buf, 32 );
1055 case FILPBIT_UNIXPR :
1057 setfilunixmode(vol, path, upriv);
1060 case FILPBIT_PDINFO :
1061 if (obj->afp_version < 30) { /* else it's UTF8 name */
1062 memcpy(ad_entry( adp, ADEID_FINDERI ), fdType, 4 );
1063 memcpy(ad_entry( adp, ADEID_FINDERI ) + 4, "pdos", 4 );
1068 err = AFPERR_BITMAP;
1069 goto setfilparam_done;
1076 if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
1077 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1081 ad_setdate(adp, AD_DATE_MODIFY, newdate);
1082 ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
1088 ad_close(adp, ADFLAGS_HF);
1091 if (change_parent_mdate && gettimeofday(&tv, NULL) == 0) {
1092 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1093 bitmap = 1<<FILPBIT_MDATE;
1094 setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
1098 LOG(log_debug9, logtype_afpd, "end setfilparams:");
1104 * renamefile and copyfile take the old and new unix pathnames
1105 * and the new mac name.
1107 * sdir_fd source dir fd to which src path is relative (for openat et al semantics)
1108 * passing -1 means this is not used, src path is a full path
1109 * src the source path
1110 * dst the dest filename in current dir
1111 * newname the dest mac name
1112 * adp adouble struct of src file, if open, or & zeroed one
1115 int renamefile(struct vol *vol, struct dir *ddir, int sdir_fd, char *src, char *dst, char *newname, struct adouble *adp)
1119 LOG(log_debug, logtype_afpd,
1120 "renamefile: src[%d, \"%s\"] -> dst[\"%s\"]", sdir_fd, src, dst);
1122 if ( unix_rename( sdir_fd, src, -1, dst ) < 0 ) {
1125 return( AFPERR_NOOBJ );
1128 return( AFPERR_ACCESS );
1130 return AFPERR_VLOCK;
1131 case EXDEV : /* Cross device move -- try copy */
1132 /* NOTE: with open file it's an error because after the copy we will
1133 * get two files, it's fixable for our process (eg reopen the new file, get the
1134 * locks, and so on. But it doesn't solve the case with a second process
1136 if (adp->ad_open_forks) {
1137 /* FIXME warning in syslog so admin'd know there's a conflict ?*/
1138 return AFPERR_OLOCK; /* little lie */
1140 if (AFP_OK != ( rc = copyfile(vol, vol, ddir, sdir_fd, src, dst, newname, NULL )) ) {
1141 /* on error copyfile delete dest */
1144 return deletefile(vol, sdir_fd, src, 0);
1146 return( AFPERR_PARAM );
1150 if (vol->vfs->vfs_renamefile(vol, sdir_fd, src, dst) < 0 ) {
1154 /* try to undo the data fork rename,
1155 * we know we are on the same device
1158 unix_rename(-1, dst, sdir_fd, src );
1159 /* return the first error */
1162 return AFPERR_NOOBJ;
1165 return AFPERR_ACCESS ;
1167 return AFPERR_VLOCK;
1169 return AFPERR_PARAM ;
1174 /* don't care if we can't open the newly renamed ressource fork */
1175 if (ad_open(adp, dst, ADFLAGS_HF | ADFLAGS_RDWR) == 0) {
1176 ad_setname(adp, newname);
1178 ad_close( adp, ADFLAGS_HF );
1185 convert a Mac long name to an utf8 name,
1187 size_t mtoUTF8(const struct vol *vol, const char *src, size_t srclen, char *dest, size_t destlen)
1191 if ((size_t)-1 == (outlen = convert_string ( vol->v_maccharset, CH_UTF8_MAC, src, srclen, dest, destlen)) ) {
1197 /* ---------------- */
1198 int copy_path_name(const struct vol *vol, char *newname, char *ibuf)
1205 if ( type != 2 && !(vol->v_obj->afp_version >= 30 && type == 3) ) {
1211 if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
1212 if (vol->v_obj->afp_version >= 30) {
1213 /* convert it to UTF8
1215 if ((plen = mtoUTF8(vol, ibuf, plen, newname, AFPOBJ_TMPSIZ)) == (size_t)-1)
1219 strncpy( newname, ibuf, plen );
1220 newname[ plen ] = '\0';
1222 if (strlen(newname) != plen) {
1223 /* there's \0 in newname, e.g. it's a pathname not
1231 memcpy(&hint, ibuf, sizeof(hint));
1232 ibuf += sizeof(hint);
1234 memcpy(&len16, ibuf, sizeof(len16));
1235 ibuf += sizeof(len16);
1236 plen = ntohs(len16);
1239 if (plen > AFPOBJ_TMPSIZ) {
1242 strncpy( newname, ibuf, plen );
1243 newname[ plen ] = '\0';
1244 if (strlen(newname) != plen) {
1253 /* -----------------------------------
1255 int afp_copyfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1257 struct vol *s_vol, *d_vol;
1259 char *newname, *p, *upath;
1260 struct path *s_path;
1261 uint32_t sdid, ddid;
1262 int err, retvalue = AFP_OK;
1263 uint16_t svid, dvid;
1265 struct adouble ad, *adp;
1271 memcpy(&svid, ibuf, sizeof( svid ));
1272 ibuf += sizeof( svid );
1273 if (NULL == ( s_vol = getvolbyvid( svid )) ) {
1274 return( AFPERR_PARAM );
1277 memcpy(&sdid, ibuf, sizeof( sdid ));
1278 ibuf += sizeof( sdid );
1279 if (NULL == ( dir = dirlookup( s_vol, sdid )) ) {
1283 memcpy(&dvid, ibuf, sizeof( dvid ));
1284 ibuf += sizeof( dvid );
1285 memcpy(&ddid, ibuf, sizeof( ddid ));
1286 ibuf += sizeof( ddid );
1288 if (NULL == ( s_path = cname( s_vol, dir, &ibuf )) ) {
1289 return get_afp_errno(AFPERR_PARAM);
1291 if ( path_isadir(s_path) ) {
1292 return( AFPERR_BADTYPE );
1295 /* don't allow copies when the file is open.
1296 * XXX: the spec only calls for read/deny write access.
1297 * however, copyfile doesn't have any of that info,
1298 * and locks need to stay coherent. as a result,
1299 * we just balk if the file is opened already. */
1301 adp = of_ad(s_vol, s_path, &ad);
1303 if (ad_open(adp, s_path->u_name, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | ADFLAGS_RDONLY | ADFLAGS_SETSHRMD) < 0) {
1304 return AFPERR_DENYCONF;
1306 #ifdef HAVE_FSHARE_T
1308 shmd.f_access = F_RDACC;
1309 shmd.f_deny = F_NODNY;
1310 if (fcntl(ad_data_fileno(adp), F_SHARE, &shmd) != 0) {
1311 retvalue = AFPERR_DENYCONF;
1314 if (AD_RSRC_OPEN(adp) && fcntl(ad_reso_fileno(adp), F_SHARE, &shmd) != 0) {
1315 retvalue = AFPERR_DENYCONF;
1319 denyreadset = (ad_testlock(adp, ADEID_DFORK, AD_FILELOCK_DENY_RD) != 0 ||
1320 ad_testlock(adp, ADEID_RFORK, AD_FILELOCK_DENY_RD) != 0 );
1323 retvalue = AFPERR_DENYCONF;
1327 newname = obj->newtmp;
1328 strcpy( newname, s_path->m_name );
1330 p = ctoupath( s_vol, curdir, newname );
1332 retvalue = AFPERR_PARAM;
1336 if (NULL == ( d_vol = getvolbyvid( dvid )) ) {
1337 retvalue = AFPERR_PARAM;
1341 if (d_vol->v_flags & AFPVOL_RO) {
1342 retvalue = AFPERR_VLOCK;
1346 if (NULL == ( dir = dirlookup( d_vol, ddid )) ) {
1347 retvalue = afp_errno;
1351 if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) {
1352 retvalue = get_afp_errno(AFPERR_NOOBJ);
1356 if ( *s_path->m_name != '\0' ) {
1357 retvalue =path_error(s_path, AFPERR_NOOBJ);
1361 /* one of the handful of places that knows about the path type */
1362 if (copy_path_name(d_vol, newname, ibuf) < 0) {
1363 retvalue = AFPERR_PARAM;
1366 /* newname is always only a filename so curdir *is* its
1369 if (NULL == (upath = mtoupath(d_vol, newname, curdir->d_did, utf8_encoding(d_vol->v_obj)))) {
1370 retvalue =AFPERR_PARAM;
1374 if ( (err = copyfile(s_vol, d_vol, curdir, -1, p, upath , newname, adp)) < 0 ) {
1380 setvoltime(obj, d_vol );
1383 ad_close( adp, ADFLAGS_DF |ADFLAGS_HF | ADFLAGS_SETSHRMD);
1387 /* ----------------------------------
1388 * if newname is NULL (from directory.c) we don't want to copy the resource fork.
1389 * because we are doing it elsewhere.
1390 * currently if newname is NULL then adp is NULL.
1392 int copyfile(struct vol *s_vol,
1399 struct adouble *adp)
1401 struct adouble ads, add;
1408 LOG(log_debug, logtype_afpd, "copyfile(sfd:%d,s:'%s',d:'%s',n:'%s')",
1409 sfd, src, dst, newname);
1412 ad_init(&ads, s_vol);
1416 adflags = ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | ADFLAGS_RF | ADFLAGS_NORF;
1418 if (ad_openat(adp, sfd, src, adflags | ADFLAGS_RDONLY) < 0) {
1423 if (!AD_META_OPEN(adp))
1424 /* no resource fork, don't create one for dst file */
1425 adflags &= ~ADFLAGS_HF;
1427 if (!AD_RSRC_OPEN(adp))
1428 /* no resource fork, don't create one for dst file */
1429 adflags &= ~ADFLAGS_RF;
1431 stat_result = fstat(ad_data_fileno(adp), &st); /* saving stat exit code, thus saving us on one more stat later on */
1433 if (stat_result < 0) {
1434 /* unlikely but if fstat fails, the default file mode will be 0666. */
1435 st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
1438 ad_init(&add, d_vol);
1439 if (ad_open(&add, dst, adflags | ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_EXCL, st.st_mode | S_IRUSR | S_IWUSR) < 0) {
1441 ad_close( adp, adflags );
1442 if (EEXIST != ret_err) {
1443 deletefile(d_vol, -1, dst, 0);
1446 return AFPERR_EXIST;
1449 if ((err = copy_fork(ADEID_DFORK, &add, adp)) != 0)
1450 LOG(log_error, logtype_afpd, "copyfile('%s'): %s", src, strerror(errno));
1453 if ((err = d_vol->vfs->vfs_copyfile(d_vol, sfd, src, dst)) != 0)
1454 LOG(log_error, logtype_afpd, "copyfile('%s'): %s", src, strerror(errno));
1459 if (AD_META_OPEN(&add)) {
1460 if (AD_META_OPEN(adp))
1461 ad_copy_header(&add, adp);
1462 ad_setname(&add, dst);
1465 if (fstat(ad_meta_fileno(&add), &stdest) != 0) {
1469 if ((id = get_id(d_vol, &add, &stdest, d_dir->d_did, dst, strlen(dst))) == CNID_INVALID) {
1473 (void)ad_setid(&add, stdest.st_dev, stdest.st_ino, id, d_dir->d_did, d_vol->v_stamp);
1478 ad_close( adp, adflags );
1480 if (ad_close( &add, adflags ) <0) {
1485 deletefile(d_vol, -1, dst, 0);
1487 else if (stat_result == 0) {
1488 /* set dest modification date to src date */
1491 ut.actime = ut.modtime = st.st_mtime;
1493 /* FIXME netatalk doesn't use resource fork file date
1494 * but maybe we should set its modtime too.
1499 switch ( ret_err ) {
1505 LOG(log_info, logtype_afpd, "copyfile: DISK FULL");
1506 return AFPERR_DFULL;
1508 return AFPERR_NOOBJ;
1510 return AFPERR_ACCESS;
1512 return AFPERR_VLOCK;
1514 return AFPERR_PARAM;
1518 /* -----------------------------------
1519 vol: not NULL delete cnid entry. then we are in curdir and file is a only filename
1520 checkAttrib: 1 check kFPDeleteInhibitBit (deletfile called by afp_delete)
1522 when deletefile is called we don't have lock on it, file is closed (for us)
1523 untrue if called by renamefile
1525 ad_open always try to open file RDWR first and ad_lock takes care of
1526 WRITE lock on read only file.
1529 static int check_attrib(struct adouble *adp)
1531 uint16_t bshort = 0;
1533 ad_getattr(adp, &bshort);
1535 * Does kFPDeleteInhibitBit (bit 8) set?
1537 if ((bshort & htons(ATTRBIT_NODELETE))) {
1538 return AFPERR_OLOCK;
1540 if ((bshort & htons(ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
1546 * dirfd can be used for unlinkat semantics
1548 int deletefile(const struct vol *vol, int dirfd, char *file, int checkAttrib)
1551 struct adouble *adp = NULL;
1552 int adflags, err = AFP_OK;
1555 LOG(log_debug, logtype_afpd, "deletefile('%s')", file);
1559 /* was EACCESS error try to get only metadata */
1560 /* we never want to create a resource fork here, we are going to delete it
1561 * moreover sometimes deletefile is called with a no existent file and
1562 * ad_open would create a 0 byte resource fork
1564 if ( ad_metadataat(dirfd, file, ADFLAGS_CHECK_OF, &ad) == 0 ) {
1565 if ((err = check_attrib(&ad))) {
1566 ad_close(&ad, ADFLAGS_HF | ADFLAGS_CHECK_OF);
1573 /* try to open both forks at once */
1574 adflags = ADFLAGS_DF;
1575 if (ad_openat(&ad, dirfd, file, adflags | ADFLAGS_RF | ADFLAGS_NORF | ADFLAGS_RDONLY) < 0 ) {
1580 case EACCES: /* maybe it's a file with no write mode for us */
1581 break; /* was return AFPERR_ACCESS;*/
1594 if ( adp && AD_RSRC_OPEN(adp) ) { /* there's a resource fork */
1595 adflags |= ADFLAGS_RF;
1596 /* FIXME we have a pb here because we want to know if a file is open
1597 * there's a 'priority inversion' if you can't open the ressource fork RW
1598 * you can delete it if it's open because you can't get a write lock.
1600 * ADLOCK_FILELOCK means the whole ressource fork, not only after the
1603 * FIXME it doesn't work for RFORK open read only and fork open without deny mode
1605 if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
1611 if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
1612 LOG(log_error, logtype_afpd, "deletefile('%s'): ad_tmplock error: %s", file, strerror(errno));
1614 } else if (!(err = vol->vfs->vfs_deletefile(vol, dirfd, file)) && !(err = netatalk_unlinkat(dirfd, file )) ) {
1617 AFP_CNID_START("cnid_get");
1618 id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file));
1621 AFP_CNID_START("cnid_delete");
1622 cnid_delete(vol->v_cdb, id);
1630 ad_close(&ad, ADFLAGS_HF | ADFLAGS_CHECK_OF);
1633 ad_close( &ad, adflags ); /* ad_close removes locks if any */
1638 /* ------------------------------------ */
1639 /* return a file id */
1640 int afp_createid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1649 struct path *s_path;
1655 memcpy(&vid, ibuf, sizeof(vid));
1656 ibuf += sizeof(vid);
1658 if (NULL == ( vol = getvolbyvid( vid )) ) {
1659 return( AFPERR_PARAM);
1662 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1666 if (vol->v_flags & AFPVOL_RO)
1667 return AFPERR_VLOCK;
1669 memcpy(&did, ibuf, sizeof( did ));
1670 ibuf += sizeof(did);
1672 if (NULL == ( dir = dirlookup( vol, did )) ) {
1673 return afp_errno; /* was AFPERR_PARAM */
1676 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
1677 return get_afp_errno(AFPERR_NOOBJ); /* was AFPERR_PARAM */
1680 if ( path_isadir(s_path) ) {
1681 return( AFPERR_BADTYPE );
1684 upath = s_path->u_name;
1685 switch (s_path->st_errno) {
1687 break; /* success */
1690 return AFPERR_ACCESS;
1692 return AFPERR_NOOBJ;
1694 return AFPERR_PARAM;
1697 AFP_CNID_START("cnid_lookup");
1698 id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath));
1701 memcpy(rbuf, &id, sizeof(id));
1702 *rbuflen = sizeof(id);
1703 return AFPERR_EXISTID;
1706 if ((id = get_id(vol, NULL, st, did, upath, len)) != CNID_INVALID) {
1707 memcpy(rbuf, &id, sizeof(id));
1708 *rbuflen = sizeof(id);
1715 /* ------------------------------- */
1721 static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
1724 struct reenum *param = data;
1725 struct vol *vol = param->vol;
1726 cnid_t did = param->did;
1729 if (ostat(de->d_name, &path.st, vol_syml_opt(vol)) < 0)
1732 /* update or add to cnid */
1733 AFP_CNID_START("cnid_add");
1734 aint = cnid_add(vol->v_cdb, &path.st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */
1740 /* --------------------
1741 * Ok the db is out of synch with the dir.
1742 * but if it's a deleted file we don't want to do it again and again.
1745 reenumerate_id(struct vol *vol, char *name, struct dir *dir)
1751 if (vol->v_cdb == NULL) {
1755 /* FIXME use of_statdir ? */
1756 if (ostat(name, &st, vol_syml_opt(vol))) {
1760 if (dirreenumerate(dir, &st)) {
1761 /* we already did it once and the dir haven't been modified */
1762 return dir->d_offcnt;
1766 data.did = dir->d_did;
1767 if ((ret = for_each_dirent(vol, name, reenumerate_loop, (void *)&data)) >= 0) {
1768 setdiroffcnt(curdir, &st, ret);
1769 dir->d_flags |= DIRF_CNID;
1775 /* ------------------------------
1776 resolve a file id */
1777 int afp_resolveid(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1786 uint16_t vid, bitmap;
1788 static char buffer[12 + MAXPATHLEN + 1];
1789 int len = 12 + MAXPATHLEN + 1;
1794 memcpy(&vid, ibuf, sizeof(vid));
1795 ibuf += sizeof(vid);
1797 if (NULL == ( vol = getvolbyvid( vid )) ) {
1798 return( AFPERR_PARAM);
1801 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1805 memcpy(&id, ibuf, sizeof( id ));
1810 /* some MacOS versions after a catsearch do a *lot* of afp_resolveid with 0 */
1814 AFP_CNID_START("cnid_resolve");
1815 upath = cnid_resolve(vol->v_cdb, &id, buffer, len);
1817 if (upath == NULL) {
1818 return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
1821 if (NULL == ( dir = dirlookup( vol, id )) ) {
1822 return AFPERR_NOID; /* idem AFPERR_PARAM */
1824 if (movecwd(vol, dir) < 0) {
1828 return AFPERR_ACCESS;
1832 return AFPERR_PARAM;
1836 memset(&path, 0, sizeof(path));
1837 path.u_name = upath;
1838 if (of_stat(vol, &path) < 0 ) {
1840 /* with nfs and our working directory is deleted */
1841 if (errno == ESTALE) {
1845 if ( errno == ENOENT && !retry) {
1846 /* cnid db is out of sync, reenumerate the directory and update ids */
1847 reenumerate_id(vol, ".", dir);
1855 return AFPERR_ACCESS;
1859 return AFPERR_PARAM;
1863 /* directories are bad */
1864 if (S_ISDIR(path.st.st_mode)) {
1865 /* OS9 and OSX don't return the same error code */
1866 return (obj->afp_version >=30)?AFPERR_NOID:AFPERR_BADTYPE;
1869 memcpy(&bitmap, ibuf, sizeof(bitmap));
1870 bitmap = ntohs( bitmap );
1871 if (NULL == (path.m_name = utompath(vol, upath, cnid, utf8_encoding(obj)))) {
1875 if (AFP_OK != (err = getfilparams(obj, vol, bitmap, &path , curdir,
1876 rbuf + sizeof(bitmap), &buflen, 0))) {
1879 *rbuflen = buflen + sizeof(bitmap);
1880 memcpy(rbuf, ibuf, sizeof(bitmap));
1885 /* ------------------------------ */
1886 int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1896 static char buffer[12 + MAXPATHLEN + 1];
1897 int len = 12 + MAXPATHLEN + 1;
1902 memcpy(&vid, ibuf, sizeof(vid));
1903 ibuf += sizeof(vid);
1905 if (NULL == ( vol = getvolbyvid( vid )) ) {
1906 return( AFPERR_PARAM);
1909 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1913 if (vol->v_flags & AFPVOL_RO)
1914 return AFPERR_VLOCK;
1916 memcpy(&id, ibuf, sizeof( id ));
1920 AFP_CNID_START("cnid_resolve");
1921 upath = cnid_resolve(vol->v_cdb, &id, buffer, len);
1923 if (upath == NULL) {
1927 if (NULL == ( dir = dirlookup( vol, id )) ) {
1928 if (afp_errno == AFPERR_NOOBJ) {
1932 return( AFPERR_PARAM );
1936 if ((movecwd(vol, dir) < 0) || (ostat(upath, &st, vol_syml_opt(vol)) < 0)) {
1940 return AFPERR_ACCESS;
1945 /* still try to delete the id */
1949 return AFPERR_PARAM;
1952 else if (S_ISDIR(st.st_mode)) /* directories are bad */
1953 return AFPERR_BADTYPE;
1956 AFP_CNID_START("cnid_delete");
1957 if (cnid_delete(vol->v_cdb, fileid)) {
1961 return AFPERR_VLOCK;
1964 return AFPERR_ACCESS;
1966 return AFPERR_PARAM;
1973 /* ------------------------------ */
1974 static struct adouble *find_adouble(const AFPObj *obj, struct vol *vol, struct path *path, struct ofork **of, struct adouble *adp)
1978 if (path->st_errno) {
1979 switch (path->st_errno) {
1981 afp_errno = AFPERR_NOID;
1985 afp_errno = AFPERR_ACCESS;
1988 afp_errno = AFPERR_PARAM;
1993 /* we use file_access both for legacy Mac perm and
1994 * for unix privilege, rename will take care of folder perms
1996 if (file_access(obj, vol, path, OPENACC_WR ) < 0) {
1997 afp_errno = AFPERR_ACCESS;
2001 if ((*of = of_findname(vol, path))) {
2002 /* reuse struct adouble so it won't break locks */
2006 ret = ad_open(adp, path->u_name, ADFLAGS_HF | ADFLAGS_RDWR);
2008 if ( !ret && ad_reso_fileno(adp) != -1 && !(adp->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
2010 * The user must have the Read & Write privilege for both files in order to use this command.
2012 ad_close(adp, ADFLAGS_HF);
2013 afp_errno = AFPERR_ACCESS;
2020 #define APPLETEMP ".AppleTempXXXXXX"
2022 int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
2024 struct stat srcst, destst;
2026 struct dir *dir, *sdir;
2027 char *spath, temp[17], *p;
2028 char *supath, *upath;
2033 struct adouble *adsp = NULL;
2034 struct adouble *addp = NULL;
2035 struct ofork *s_of = NULL;
2036 struct ofork *d_of = NULL;
2046 memcpy(&vid, ibuf, sizeof(vid));
2047 ibuf += sizeof(vid);
2049 if (NULL == ( vol = getvolbyvid( vid )) ) {
2050 return( AFPERR_PARAM);
2053 if ((vol->v_flags & AFPVOL_RO))
2054 return AFPERR_VLOCK;
2056 /* source and destination dids */
2057 memcpy(&sid, ibuf, sizeof(sid));
2058 ibuf += sizeof(sid);
2059 memcpy(&did, ibuf, sizeof(did));
2060 ibuf += sizeof(did);
2063 if (NULL == (dir = dirlookup( vol, sid )) ) {
2064 return afp_errno; /* was AFPERR_PARAM */
2067 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2068 return get_afp_errno(AFPERR_NOOBJ);
2071 if ( path_isadir(path) ) {
2072 return AFPERR_BADTYPE; /* it's a dir */
2075 /* save some stuff */
2078 spath = obj->oldtmp;
2079 supath = obj->newtmp;
2080 strcpy(spath, path->m_name);
2081 strcpy(supath, path->u_name); /* this is for the cnid changing */
2082 p = absupath( vol, sdir, supath);
2084 /* pathname too long */
2085 return AFPERR_PARAM ;
2089 if (!(adsp = find_adouble(obj, vol, path, &s_of, &ads))) {
2093 /* ***** from here we may have resource fork open **** */
2095 /* look for the source cnid. if it doesn't exist, don't worry about
2097 AFP_CNID_START("cnid_lookup");
2098 sid = cnid_lookup(vol->v_cdb, &srcst, sdir->d_did, supath,slen = strlen(supath));
2101 if (NULL == ( dir = dirlookup( vol, did )) ) {
2102 err = afp_errno; /* was AFPERR_PARAM */
2103 goto err_exchangefile;
2106 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2107 err = get_afp_errno(AFPERR_NOOBJ);
2108 goto err_exchangefile;
2111 if ( path_isadir(path) ) {
2112 err = AFPERR_BADTYPE;
2113 goto err_exchangefile;
2116 /* FPExchangeFiles is the only call that can return the SameObj
2118 if ((curdir == sdir) && strcmp(spath, path->m_name) == 0) {
2119 err = AFPERR_SAMEOBJ;
2120 goto err_exchangefile;
2124 if (!(addp = find_adouble(obj, vol, path, &d_of, &add))) {
2126 goto err_exchangefile;
2130 /* they are not on the same device and at least one is open
2131 * FIXME broken for for crossdev and adouble v2
2134 crossdev = (srcst.st_dev != destst.st_dev);
2135 if (/* (d_of || s_of) && */ crossdev) {
2137 goto err_exchangefile;
2140 /* look for destination id. */
2141 upath = path->u_name;
2142 AFP_CNID_START("cnid_lookup");
2143 did = cnid_lookup(vol->v_cdb, &destst, curdir->d_did, upath, dlen = strlen(upath));
2146 /* construct a temp name.
2147 * NOTE: the temp file will be in the dest file's directory. it
2148 * will also be inaccessible from AFP. */
2149 memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
2151 if ((fd = mkstemp(temp)) == -1) {
2153 goto err_exchangefile;
2158 /* FIXME we need to close fork for copy, both s_of and d_of are null */
2159 ad_close(adsp, ADFLAGS_HF);
2160 ad_close(addp, ADFLAGS_HF);
2163 /* now, quickly rename the file. we error if we can't. */
2164 if ((err = renamefile(vol, curdir, -1, p, temp, temp, adsp)) != AFP_OK)
2165 goto err_exchangefile;
2166 of_rename(vol, s_of, sdir, spath, curdir, temp);
2168 /* rename destination to source */
2169 if ((err = renamefile(vol, curdir, -1, upath, p, spath, addp)) != AFP_OK)
2170 goto err_src_to_tmp;
2171 of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
2173 /* rename temp to destination */
2174 if ((err = renamefile(vol, curdir, -1, temp, upath, path->m_name, adsp)) != AFP_OK)
2175 goto err_dest_to_src;
2176 of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
2179 * id's need switching. src -> dest and dest -> src.
2180 * we need to re-stat() if it was a cross device copy.
2183 AFP_CNID_START("cnid_delete");
2184 cnid_delete(vol->v_cdb, sid);
2188 AFP_CNID_START("cnid_delete");
2189 cnid_delete(vol->v_cdb, did);
2193 if ((did && ( (crossdev && ostat(upath, &srcst, vol_syml_opt(vol)) < 0) ||
2194 cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
2196 (sid && ( (crossdev && ostat(p, &destst, vol_syml_opt(vol)) < 0) ||
2197 cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
2202 err = AFPERR_ACCESS;
2207 goto err_temp_to_dest;
2210 /* here we need to reopen if crossdev */
2211 if (sid && ad_setid(addp, destst.st_dev, destst.st_ino, sid, sdir->d_did, vol->v_stamp))
2216 if (did && ad_setid(adsp, srcst.st_dev, srcst.st_ino, did, curdir->d_did, vol->v_stamp))
2221 /* change perms, src gets dest perm and vice versa */
2226 * we need to exchange ACL entries as well
2228 /* exchange_acls(vol, p, upath); */
2233 path->m_name = NULL;
2234 path->u_name = upath;
2236 setfilunixmode(vol, path, destst.st_mode);
2237 setfilowner(vol, destst.st_uid, destst.st_gid, path);
2244 setfilunixmode(vol, path, srcst.st_mode);
2245 setfilowner(vol, srcst.st_uid, srcst.st_gid, path);
2250 goto err_exchangefile;
2252 /* all this stuff is so that we can unwind a failed operation
2255 /* rename dest to temp */
2256 renamefile(vol, curdir, -1, upath, temp, temp, adsp);
2257 of_rename(vol, s_of, curdir, upath, curdir, temp);
2260 /* rename source back to dest */
2261 renamefile(vol, curdir, -1, p, upath, path->m_name, addp);
2262 of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
2265 /* rename temp back to source */
2266 renamefile(vol, curdir, -1, temp, p, spath, adsp);
2267 of_rename(vol, s_of, curdir, temp, sdir, spath);
2270 if ( !s_of && adsp && ad_meta_fileno(adsp) != -1 ) { /* META */
2271 ad_close(adsp, ADFLAGS_HF);
2273 if ( !d_of && addp && ad_meta_fileno(addp) != -1 ) {/* META */
2274 ad_close(addp, ADFLAGS_HF);
2278 if ((cached = dircache_search_by_did(vol, sid)) != NULL)
2279 (void)dir_remove(vol, cached);
2280 if ((cached = dircache_search_by_did(vol, did)) != NULL)
2281 (void)dir_remove(vol, cached);