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>
28 #include <atalk/spotlight.h>
30 #include "directory.h"
39 /* the format for the finderinfo fields (from IM: Toolbox Essentials):
40 * field bytes subfield bytes
43 * ioFlFndrInfo 16 -> type 4 type field
44 * creator 4 creator field
45 * flags 2 finder flags:
47 * location 4 location in window
48 * folder 2 window that contains file
50 * ioFlXFndrInfo 16 -> iconID 2 icon id
52 * script 1 script system
54 * commentID 2 comment id
55 * putawayID 4 home directory id
58 const u_char ufinderi[ADEDLEN_FINDERI] = {
59 0, 0, 0, 0, 0, 0, 0, 0,
60 1, 0, 0, 0, 0, 0, 0, 0,
61 0, 0, 0, 0, 0, 0, 0, 0,
62 0, 0, 0, 0, 0, 0, 0, 0
65 static const u_char old_ufinderi[] = {
66 'T', 'E', 'X', 'T', 'U', 'N', 'I', 'X'
69 /* ----------------------
71 static int default_type(void *finder)
73 if (!memcmp(finder, ufinderi, 8) || !memcmp(finder, old_ufinderi, 8))
78 /* FIXME path : unix or mac name ? (for now it's unix name ) */
79 void *get_finderinfo(const struct vol *vol, const char *upath, struct adouble *adp, void *data, int islink)
82 void *ad_finder = NULL;
86 ad_finder = ad_entry(adp, ADEID_FINDERI);
89 memcpy(data, ad_finder, ADEDLEN_FINDERI);
91 if (default_type(ad_finder))
95 memcpy(data, ufinderi, ADEDLEN_FINDERI);
97 if (vol_inv_dots(vol) && *upath == '.') { /* make it invisible */
100 ashort = htons(FINDERINFO_INVISIBLE);
101 memcpy((char *)data + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
105 if (islink && !vol_syml_opt(vol)) {
107 memcpy(&linkflag, (char *)data + FINDERINFO_FRFLAGOFF, 2);
108 linkflag |= htons(FINDERINFO_ISALIAS);
109 memcpy((char *)data + FINDERINFO_FRFLAGOFF, &linkflag, 2);
110 memcpy((char *)data + FINDERINFO_FRTYPEOFF,"slnk",4);
111 memcpy((char *)data + FINDERINFO_FRCREATOFF,"rhap",4);
114 /** Only enter if no appledouble information and no finder information found. */
115 if (chk_ext && (em = getextmap( upath ))) {
116 memcpy(data, em->em_type, sizeof( em->em_type ));
117 memcpy((char *)data + 4, em->em_creator, sizeof(em->em_creator));
123 /* ---------------------
125 char *set_name(const struct vol *vol, char *data, cnid_t pid, char *name, cnid_t id, uint32_t utf8)
130 aint = strlen( name );
134 if (utf8_encoding(vol->v_obj)) {
135 /* but name is an utf8 mac name */
138 /* global static variable... */
140 if (!(u = mtoupath(vol, name, pid, 1)) || !(m = utompath(vol, u, id, 0))) {
149 if (aint > MACFILELEN)
156 if (aint > UTF8FILELEN_EARLY) /* FIXME safeguard, anyway if no ascii char it's game over*/
157 aint = UTF8FILELEN_EARLY;
159 utf8 = vol->v_kTextEncoding;
160 memcpy(data, &utf8, sizeof(utf8));
161 data += sizeof(utf8);
164 memcpy(data, &temp, sizeof(temp));
165 data += sizeof(temp);
168 memcpy( data, src, aint );
178 * FIXME: PDINFO is UTF8 and doesn't need adp
180 #define PARAM_NEED_ADP(b) ((b) & ((1 << FILPBIT_ATTR) |\
181 (1 << FILPBIT_CDATE) |\
182 (1 << FILPBIT_MDATE) |\
183 (1 << FILPBIT_BDATE) |\
184 (1 << FILPBIT_FINFO) |\
185 (1 << FILPBIT_RFLEN) |\
186 (1 << FILPBIT_EXTRFLEN) |\
187 (1 << FILPBIT_PDINFO) |\
188 (1 << FILPBIT_FNUM) |\
189 (1 << FILPBIT_UNIXPR)))
192 * @brief Get CNID for did/upath args both from database and adouble file
194 * 1. Get the objects CNID as stored in its adouble file
195 * 2. Get the objects CNID from the database
196 * 3. If there's a problem with a "dbd" database, fallback to "tdb" in memory
197 * 4. In case 2 and 3 differ, store 3 in the adouble file
199 * @param vol (rw) volume
200 * @param adp (rw) adouble struct of object upath, might be NULL
201 * @param st (r) stat of upath, must NOT be NULL
202 * @param did (r) parent CNID of upath
203 * @param upath (r) name of object
204 * @param len (r) strlen of upath
206 uint32_t get_id(struct vol *vol,
208 const struct stat *st,
213 static int first = 1; /* mark if this func is called the first time */
215 uint32_t dbcnid = CNID_INVALID;
218 if (vol->v_cdb != NULL) {
219 /* prime aint with what we think is the cnid, set did to zero for
220 catching moved files */
221 adcnid = ad_getid(adp, st->st_dev, st->st_ino, 0, vol->v_stamp); /* (1) */
223 AFP_CNID_START("cnid_add");
224 dbcnid = cnid_add(vol->v_cdb, st, did, upath, len, adcnid); /* (2) */
227 /* Throw errors if cnid_add fails. */
228 if (dbcnid == CNID_INVALID) {
230 case CNID_ERR_CLOSE: /* the db is closed */
233 LOG(log_error, logtype_afpd, "get_id: Incorrect parameters passed to cnid_add");
234 afp_errno = AFPERR_PARAM;
237 afp_errno = AFPERR_PARAM;
240 /* Close CNID backend if "dbd" and switch to temp in-memory "tdb" */
241 /* we have to do it here for "dbd" because it uses "lazy opening" */
242 /* In order to not end in a loop somehow with goto restart below */
244 if (first && (strcmp(vol->v_cnidscheme, "dbd") == 0)) { /* (3) */
245 cnid_close(vol->v_cdb);
246 free(vol->v_cnidscheme);
247 vol->v_cnidscheme = strdup("tdb");
249 int flags = CNID_FLAG_MEMORY;
250 if ((vol->v_flags & AFPVOL_NODEV)) {
251 flags |= CNID_FLAG_NODEV;
253 LOG(log_error, logtype_afpd, "Reopen volume %s using in memory temporary CNID DB.",
255 vol->v_cdb = cnid_open(vol->v_path, vol->v_umask, "tdb", flags, NULL, NULL);
257 if (!(vol->v_flags & AFPVOL_TM)) {
258 vol->v_flags |= AFPVOL_RO;
259 setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB instead."
260 "Check server messages for details. Switching to read-only mode.");
261 kill(getpid(), SIGUSR2);
263 goto restart; /* now try again with the temp CNID db */
265 setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB failed too!"
266 "Check server messages for details, can't recover from this state!");
269 afp_errno = AFPERR_MISC;
273 else if (adp && adcnid && (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)) {
278 if (ad_flush(adp) != 0)
279 LOG(log_error, logtype_afpd, "get_id(\"%s\"): can't flush", fullpathname(upath));
289 /* -------------------------- */
290 int getmetadata(const AFPObj *obj,
293 struct path *path, struct dir *dir,
294 char *buf, size_t *buflen, struct adouble *adp)
296 char *data, *l_nameoff = NULL, *upath;
297 char *utf_nameoff = NULL;
302 u_char achar, fdType[4];
307 LOG(log_debug, logtype_afpd, "getmetadata(\"%s\")", path->u_name);
309 upath = path->u_name;
313 if ( ((bitmap & ( (1 << FILPBIT_FINFO)|(1 << FILPBIT_LNAME)|(1 <<FILPBIT_PDINFO) ) ) && !path->m_name)
314 || (bitmap & ( (1 << FILPBIT_LNAME) ) && utf8_encoding(obj)) /* FIXME should be m_name utf8 filename */
315 || (bitmap & (1 << FILPBIT_FNUM))) {
318 struct dir *cachedfile;
319 int len = strlen(upath);
320 if ((cachedfile = dircache_search_by_name(vol, dir, upath, len)) != NULL)
321 id = cachedfile->d_did;
323 id = get_id(vol, adp, st, dir->d_did, upath, len);
325 /* Add it to the cache */
326 LOG(log_debug, logtype_afpd, "getmetadata: caching: did:%u, \"%s\", cnid:%u",
327 ntohl(dir->d_did), upath, ntohl(id));
329 /* Get macname from unixname first */
330 if (path->m_name == NULL) {
331 if ((path->m_name = utompath(vol, upath, id, utf8_encoding(obj))) == NULL) {
332 LOG(log_error, logtype_afpd, "getmetadata: utompath error");
338 if (((fullpath = bstrcpy(dir->d_fullpath)) == NULL)
339 || (bconchar(fullpath, '/') != BSTR_OK)
340 || (bcatcstr(fullpath, upath)) != BSTR_OK) {
341 LOG(log_error, logtype_afpd, "getmetadata: fullpath: %s", strerror(errno));
346 if ((cachedfile = dir_new(path->m_name, upath, vol, dir->d_did, id, fullpath, st)) == NULL) {
347 LOG(log_error, logtype_afpd, "getmetadata: error from dir_new");
351 if ((dircache_add(vol, cachedfile)) != 0) {
352 LOG(log_error, logtype_afpd, "getmetadata: fatal dircache error");
360 if (id == CNID_INVALID)
364 path->m_name = utompath(vol, upath, id, utf8_encoding(vol->v_obj));
367 while ( bitmap != 0 ) {
368 while (( bitmap & 1 ) == 0 ) {
376 ad_getattr(adp, &ashort);
377 } else if (vol_inv_dots(vol) && *upath == '.') {
378 ashort = htons(ATTRBIT_INVISIBLE);
382 /* FIXME do we want a visual clue if the file is read only
385 accessmode(vol, ".", &ma, dir , NULL);
386 if ((ma.ma_user & AR_UWRITE)) {
387 accessmode(vol, upath, &ma, dir , st);
388 if (!(ma.ma_user & AR_UWRITE)) {
389 ashort |= htons(ATTRBIT_NOWRITE);
393 memcpy(data, &ashort, sizeof( ashort ));
394 data += sizeof( ashort );
395 LOG(log_debug, logtype_afpd, "metadata('%s'): AFP Attributes: %04x",
396 path->u_name, ntohs(ashort));
400 memcpy(data, &dir->d_did, sizeof( uint32_t ));
401 data += sizeof( uint32_t );
402 LOG(log_debug, logtype_afpd, "metadata('%s'): Parent DID: %u",
403 path->u_name, ntohl(dir->d_did));
407 if (!adp || (ad_getdate(adp, AD_DATE_CREATE, &aint) < 0))
408 aint = AD_DATE_FROM_UNIX(st->st_mtime);
409 memcpy(data, &aint, sizeof( aint ));
410 data += sizeof( aint );
414 if ( adp && (ad_getdate(adp, AD_DATE_MODIFY, &aint) == 0)) {
415 if ((st->st_mtime > AD_DATE_TO_UNIX(aint))) {
416 aint = AD_DATE_FROM_UNIX(st->st_mtime);
419 aint = AD_DATE_FROM_UNIX(st->st_mtime);
421 memcpy(data, &aint, sizeof( int ));
422 data += sizeof( int );
426 if (!adp || (ad_getdate(adp, AD_DATE_BACKUP, &aint) < 0))
427 aint = AD_DATE_START;
428 memcpy(data, &aint, sizeof( int ));
429 data += sizeof( int );
433 get_finderinfo(vol, upath, adp, (char *)data,S_ISLNK(st->st_mode));
434 data += ADEDLEN_FINDERI;
439 data += sizeof( uint16_t );
443 memset(data, 0, sizeof(uint16_t));
444 data += sizeof( uint16_t );
448 memcpy(data, &id, sizeof( id ));
449 data += sizeof( id );
450 LOG(log_debug, logtype_afpd, "metadata('%s'): CNID: %u",
451 path->u_name, ntohl(id));
455 if (st->st_size > 0xffffffff)
458 aint = htonl( st->st_size );
459 memcpy(data, &aint, sizeof( aint ));
460 data += sizeof( aint );
465 if (adp->ad_rlen > 0xffffffff)
468 aint = htonl( adp->ad_rlen);
472 memcpy(data, &aint, sizeof( aint ));
473 data += sizeof( aint );
476 /* Current client needs ProDOS info block for this file.
477 Use simple heuristic and let the Mac "type" string tell
478 us what the PD file code should be. Everything gets a
479 subtype of 0x0000 unless the original value was hashed
480 to "pXYZ" when we created it. See IA, Ver 2.
481 <shirsch@adelphia.net> */
482 case FILPBIT_PDINFO :
483 if (obj->afp_version >= 30) { /* UTF8 name */
484 utf8 = kTextEncodingUTF8;
486 data += sizeof( uint16_t );
488 memcpy(data, &aint, sizeof( aint ));
489 data += sizeof( aint );
493 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
495 if ( memcmp( fdType, "TEXT", 4 ) == 0 ) {
499 else if ( memcmp( fdType, "PSYS", 4 ) == 0 ) {
503 else if ( memcmp( fdType, "PS16", 4 ) == 0 ) {
507 else if ( memcmp( fdType, "BINA", 4 ) == 0 ) {
511 else if ( fdType[0] == 'p' ) {
513 ashort = (fdType[2] * 256) + fdType[3];
527 memcpy(data, &ashort, sizeof( ashort ));
528 data += sizeof( ashort );
529 memset(data, 0, sizeof( ashort ));
530 data += sizeof( ashort );
533 case FILPBIT_EXTDFLEN:
534 aint = htonl(st->st_size >> 32);
535 memcpy(data, &aint, sizeof( aint ));
536 data += sizeof( aint );
537 aint = htonl(st->st_size);
538 memcpy(data, &aint, sizeof( aint ));
539 data += sizeof( aint );
541 case FILPBIT_EXTRFLEN:
544 aint = htonl(adp->ad_rlen >> 32);
545 memcpy(data, &aint, sizeof( aint ));
546 data += sizeof( aint );
548 aint = htonl(adp->ad_rlen);
549 memcpy(data, &aint, sizeof( aint ));
550 data += sizeof( aint );
552 case FILPBIT_UNIXPR :
553 /* accessmode may change st_mode with ACLs */
554 accessmode(obj, vol, upath, &ma, dir , st);
556 aint = htonl(st->st_uid);
557 memcpy( data, &aint, sizeof( aint ));
558 data += sizeof( aint );
559 aint = htonl(st->st_gid);
560 memcpy( data, &aint, sizeof( aint ));
561 data += sizeof( aint );
564 type == slnk indicates an OSX style symlink,
565 we have to add S_IFLNK to the mode, otherwise
566 10.3 clients freak out. */
570 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
571 if ( memcmp( fdType, "slnk", 4 ) == 0 ) {
577 memcpy( data, &aint, sizeof( aint ));
578 data += sizeof( aint );
580 *data++ = ma.ma_user;
581 *data++ = ma.ma_world;
582 *data++ = ma.ma_group;
583 *data++ = ma.ma_owner;
587 return( AFPERR_BITMAP );
593 ashort = htons( data - buf );
594 memcpy(l_nameoff, &ashort, sizeof( ashort ));
595 data = set_name(vol, data, dir->d_did, path->m_name, id, 0);
598 ashort = htons( data - buf );
599 memcpy(utf_nameoff, &ashort, sizeof( ashort ));
600 data = set_name(vol, data, dir->d_did, path->m_name, id, utf8);
602 *buflen = data - buf;
606 /* ----------------------- */
607 int getfilparams(const AFPObj *obj, struct vol *vol, uint16_t bitmap, struct path *path,
608 struct dir *dir, char *buf, size_t *buflen, int in_enumerate)
610 struct adouble ad, *adp;
613 int flags; /* uninitialized ok */
615 LOG(log_debug, logtype_afpd, "getfilparams(\"%s\")", path->u_name);
617 opened = PARAM_NEED_ADP(bitmap);
623 * Dont check for and resturn open fork attributes when enumerating
624 * This saves a lot of syscalls, the client will hopefully only use the result
625 * in FPGetFileParms where we return the correct value
627 flags = (!in_enumerate &&(bitmap & (1 << FILPBIT_ATTR))) ? ADFLAGS_CHECK_OF : 0;
629 adp = of_ad(vol, path, &ad);
630 upath = path->u_name;
632 if ( ad_metadata( upath, flags, adp) < 0 ) {
635 LOG(log_error, logtype_afpd, "getfilparams(%s): %s: check resource fork permission?",
636 upath, strerror(errno));
637 return AFPERR_ACCESS;
639 LOG(log_error, logtype_afpd, "getfilparams(%s): bad resource fork", upath);
648 rc = getmetadata(obj, vol, bitmap, path, dir, buf, buflen, adp);
651 ad_close(adp, ADFLAGS_HF | flags);
656 /* ----------------------------- */
657 int afp_createfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
662 struct ofork *of = NULL;
664 int creatf, did, openf, retvalue = AFP_OK;
670 creatf = (unsigned char) *ibuf++;
672 memcpy(&vid, ibuf, sizeof( vid ));
673 ibuf += sizeof( vid );
675 if (NULL == ( vol = getvolbyvid( vid )) )
676 return( AFPERR_PARAM );
678 if (vol->v_flags & AFPVOL_RO)
681 memcpy(&did, ibuf, sizeof( did));
682 ibuf += sizeof( did );
684 if (NULL == ( dir = dirlookup( vol, did )) )
687 if (NULL == ( s_path = cname( vol, dir, &ibuf )) )
688 return get_afp_errno(AFPERR_PARAM);
689 if ( *s_path->m_name == '\0' )
690 return( AFPERR_BADTYPE );
692 upath = s_path->u_name;
695 /* if upath is deleted we already in trouble anyway */
696 if ((of = of_findname(vol, s_path))) {
704 openf = ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_TRUNC;
706 /* on a soft create, if the file is open then ad_open won't fail
707 because open syscall is not called */
708 openf = ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_EXCL;
710 if (ad_open(&ad, upath, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | openf, 0666) < 0) {
714 case ENOENT : /* we were already in 'did folder' so chdir() didn't fail */
715 return ( AFPERR_NOOBJ );
717 return( AFPERR_EXIST );
719 return( AFPERR_ACCESS );
722 LOG(log_info, logtype_afpd, "afp_createfile: DISK FULL");
723 return( AFPERR_DFULL );
725 return( AFPERR_PARAM );
728 if ( ad_meta_fileno( &ad ) == -1 ) { /* Hard META / HF */
729 /* FIXME with hard create on an existing file, we already
730 * corrupted the data file.
732 netatalk_unlink( upath );
733 ad_close( &ad, ADFLAGS_DF );
734 return AFPERR_ACCESS;
737 path = s_path->m_name;
738 ad_setname(&ad, path);
741 if (lstat(upath, &st) != 0) {
742 LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): stat: %s",
743 upath, strerror(errno));
744 ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF);
749 if ((id = get_id(vol, &ad, &st, dir->d_did, upath, strlen(upath))) == CNID_INVALID) {
750 LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): CNID error", upath);
751 goto createfile_iderr;
753 (void)ad_setid(&ad, st.st_dev, st.st_ino, id, dir->d_did, vol->v_stamp);
757 ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF );
758 fce_register(FCE_FILE_CREATE, fullpathname(upath), NULL, fce_file);
762 setvoltime(obj, vol );
767 int afp_setfilparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
773 uint16_t vid, bitmap;
778 memcpy(&vid, ibuf, sizeof( vid ));
779 ibuf += sizeof( vid );
780 if (NULL == ( vol = getvolbyvid( vid )) ) {
781 return( AFPERR_PARAM );
784 if (vol->v_flags & AFPVOL_RO)
787 memcpy(&did, ibuf, sizeof( did ));
788 ibuf += sizeof( did );
789 if (NULL == ( dir = dirlookup( vol, did )) ) {
790 return afp_errno; /* was AFPERR_NOOBJ */
793 memcpy(&bitmap, ibuf, sizeof( bitmap ));
794 bitmap = ntohs( bitmap );
795 ibuf += sizeof( bitmap );
797 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
798 return get_afp_errno(AFPERR_PARAM);
801 if (path_isadir(s_path)) {
802 return( AFPERR_BADTYPE ); /* it's a directory */
805 if ( s_path->st_errno != 0 ) {
806 return( AFPERR_NOOBJ );
809 if ((u_long)ibuf & 1 ) {
813 if (AFP_OK == ( rc = setfilparams(obj, vol, s_path, bitmap, ibuf )) ) {
814 setvoltime(obj, vol );
821 * cf AFP3.0.pdf page 252 for change_mdate and change_parent_mdate logic
824 extern struct path Cur_Path;
826 int setfilparams(const AFPObj *obj, struct vol *vol,
827 struct path *path, uint16_t f_bitmap, char *buf )
829 struct adouble ad, *adp;
831 int bit, isad = 1, err = AFP_OK;
833 u_char achar, *fdType, xyy[4]; /* uninitialized, OK 310105 */
834 uint16_t ashort, bshort, oshort;
837 uint16_t upriv_bit = 0;
839 int change_mdate = 0;
840 int change_parent_mdate = 0;
845 uint16_t bitmap = f_bitmap;
846 uint32_t cdate,bdate;
847 u_char finder_buf[32];
848 int symlinked = S_ISLNK(path->st.st_mode);
851 char symbuf[MAXPATHLEN+1];
854 LOG(log_debug9, logtype_afpd, "begin setfilparams:");
857 adp = of_ad(vol, path, &ad);
858 upath = path->u_name;
860 if (!vol_unix_priv(vol) && check_access(obj, vol, upath, OPENACC_WR ) < 0) {
861 return AFPERR_ACCESS;
864 /* with unix priv maybe we have to change adouble file priv first */
866 while ( bitmap != 0 ) {
867 while (( bitmap & 1 ) == 0 ) {
874 memcpy(&ashort, buf, sizeof( ashort ));
875 buf += sizeof( ashort );
879 memcpy(&cdate, buf, sizeof(cdate));
880 buf += sizeof( cdate );
883 memcpy(&newdate, buf, sizeof( newdate ));
884 buf += sizeof( newdate );
888 memcpy(&bdate, buf, sizeof( bdate));
889 buf += sizeof( bdate );
893 if (memcmp(buf,"slnkrhap",8) == 0
894 && !(S_ISLNK(path->st.st_mode))
895 && !(vol->v_flags & AFPVOL_FOLLOWSYM)) {
896 /* request to turn this into a symlink */
897 if ((fp = open(path->u_name, O_RDONLY)) == -1) {
899 goto setfilparam_done;
901 len = read(fp, symbuf, MAXPATHLEN);
905 goto setfilparam_done;
907 if (unlink(path->u_name) != 0) {
909 goto setfilparam_done;
912 if (symlink(symbuf, path->u_name) != 0) {
914 goto setfilparam_done;
919 memcpy(finder_buf, buf, 32 );
922 case FILPBIT_UNIXPR :
923 if (!vol_unix_priv(vol)) {
924 /* this volume doesn't use unix priv */
930 change_parent_mdate = 1;
932 memcpy( &aint, buf, sizeof( aint ));
933 f_uid = ntohl (aint);
934 buf += sizeof( aint );
935 memcpy( &aint, buf, sizeof( aint ));
936 f_gid = ntohl (aint);
937 buf += sizeof( aint );
938 setfilowner(vol, f_uid, f_gid, path);
940 memcpy( &upriv, buf, sizeof( upriv ));
941 buf += sizeof( upriv );
942 upriv = ntohl (upriv);
943 if ((upriv & S_IWUSR)) {
944 setfilunixmode(vol, path, upriv);
951 case FILPBIT_PDINFO :
952 if (obj->afp_version < 30) { /* else it's UTF8 name */
955 /* Keep special case to support crlf translations */
956 if ((unsigned int) achar == 0x04) {
957 fdType = (u_char *)"TEXT";
960 xyy[0] = ( u_char ) 'p';
971 /* break while loop */
980 /* second try with adouble open
982 if (ad_open(adp, upath, ADFLAGS_HF | ADFLAGS_RDWR | ADFLAGS_CREATE, 0666) < 0) {
983 LOG(log_debug, logtype_afpd, "setfilparams: ad_open_metadata error");
985 * For some things, we don't need an adouble header:
986 * - change of modification date
987 * - UNIX privs (Bug-ID #2863424)
989 if (!symlinked && f_bitmap & ~(1<<FILPBIT_MDATE | 1<<FILPBIT_UNIXPR)) {
990 LOG(log_debug, logtype_afpd, "setfilparams: need adouble access");
991 return AFPERR_ACCESS;
993 LOG(log_debug, logtype_afpd, "setfilparams: no adouble perms, but only FILPBIT_MDATE and/or FILPBIT_UNIXPR");
995 } else if ((ad_get_MD_flags( adp ) & O_CREAT) ) {
996 ad_setname(adp, path->m_name);
998 if ((id = get_id(vol, adp, &path->st, curdir->d_did, upath, strlen(upath))) == CNID_INVALID) {
999 LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): CNID error", upath);
1002 (void)ad_setid(adp, path->st.st_dev, path->st.st_ino, id, curdir->d_did, vol->v_stamp);
1007 while ( bitmap != 0 ) {
1008 while (( bitmap & 1 ) == 0 ) {
1015 ad_getattr(adp, &bshort);
1017 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
1018 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
1022 if ((bshort & htons(ATTRBIT_INVISIBLE)) != (oshort & htons(ATTRBIT_INVISIBLE)))
1023 change_parent_mdate = 1;
1024 ad_setattr(adp, bshort);
1026 case FILPBIT_CDATE :
1027 ad_setdate(adp, AD_DATE_CREATE, cdate);
1029 case FILPBIT_MDATE :
1031 case FILPBIT_BDATE :
1032 ad_setdate(adp, AD_DATE_BACKUP, bdate);
1034 case FILPBIT_FINFO :
1035 if (default_type( ad_entry( adp, ADEID_FINDERI ))
1037 ((em = getextmap( path->m_name )) &&
1038 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1039 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1040 || ((em = getdefextmap()) &&
1041 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1042 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1044 memcpy(finder_buf, ufinderi, 8 );
1046 memcpy(ad_entry( adp, ADEID_FINDERI ), finder_buf, 32 );
1048 case FILPBIT_UNIXPR :
1050 setfilunixmode(vol, path, upriv);
1053 case FILPBIT_PDINFO :
1054 if (obj->afp_version < 30) { /* else it's UTF8 name */
1055 memcpy(ad_entry( adp, ADEID_FINDERI ), fdType, 4 );
1056 memcpy(ad_entry( adp, ADEID_FINDERI ) + 4, "pdos", 4 );
1061 err = AFPERR_BITMAP;
1062 goto setfilparam_done;
1069 if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
1070 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1074 ad_setdate(adp, AD_DATE_MODIFY, newdate);
1075 ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
1081 ad_close(adp, ADFLAGS_HF);
1084 if (change_parent_mdate && gettimeofday(&tv, NULL) == 0) {
1085 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1086 bitmap = 1<<FILPBIT_MDATE;
1087 setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
1091 LOG(log_debug9, logtype_afpd, "end setfilparams:");
1097 * renamefile and copyfile take the old and new unix pathnames
1098 * and the new mac name.
1100 * sdir_fd source dir fd to which src path is relative (for openat et al semantics)
1101 * passing -1 means this is not used, src path is a full path
1102 * src the source path
1103 * dst the dest filename in current dir
1104 * newname the dest mac name
1105 * adp adouble struct of src file, if open, or & zeroed one
1108 int renamefile(struct vol *vol, struct dir *ddir, int sdir_fd, char *src, char *dst, char *newname, struct adouble *adp)
1112 LOG(log_debug, logtype_afpd,
1113 "renamefile: src[%d, \"%s\"] -> dst[\"%s\"]", sdir_fd, src, dst);
1115 if ( unix_rename( sdir_fd, src, -1, dst ) < 0 ) {
1118 return( AFPERR_NOOBJ );
1121 return( AFPERR_ACCESS );
1123 return AFPERR_VLOCK;
1124 case EXDEV : /* Cross device move -- try copy */
1125 /* NOTE: with open file it's an error because after the copy we will
1126 * get two files, it's fixable for our process (eg reopen the new file, get the
1127 * locks, and so on. But it doesn't solve the case with a second process
1129 if (adp->ad_open_forks) {
1130 /* FIXME warning in syslog so admin'd know there's a conflict ?*/
1131 return AFPERR_OLOCK; /* little lie */
1133 if (AFP_OK != ( rc = copyfile(vol, vol, ddir, sdir_fd, src, dst, newname, NULL )) ) {
1134 /* on error copyfile delete dest */
1137 return deletefile(vol, sdir_fd, src, 0);
1139 return( AFPERR_PARAM );
1143 if (vol->vfs->vfs_renamefile(vol, sdir_fd, src, dst) < 0 ) {
1147 /* try to undo the data fork rename,
1148 * we know we are on the same device
1151 unix_rename(-1, dst, sdir_fd, src );
1152 /* return the first error */
1155 return AFPERR_NOOBJ;
1158 return AFPERR_ACCESS ;
1160 return AFPERR_VLOCK;
1162 return AFPERR_PARAM ;
1167 /* don't care if we can't open the newly renamed ressource fork */
1168 if (ad_open(adp, dst, ADFLAGS_HF | ADFLAGS_RDWR) == 0) {
1169 ad_setname(adp, newname);
1171 ad_close( adp, ADFLAGS_HF );
1178 convert a Mac long name to an utf8 name,
1180 size_t mtoUTF8(const struct vol *vol, const char *src, size_t srclen, char *dest, size_t destlen)
1184 if ((size_t)-1 == (outlen = convert_string ( vol->v_maccharset, CH_UTF8_MAC, src, srclen, dest, destlen)) ) {
1190 /* ---------------- */
1191 int copy_path_name(const struct vol *vol, char *newname, char *ibuf)
1198 if ( type != 2 && !(vol->v_obj->afp_version >= 30 && type == 3) ) {
1204 if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
1205 if (vol->v_obj->afp_version >= 30) {
1206 /* convert it to UTF8
1208 if ((plen = mtoUTF8(vol, ibuf, plen, newname, AFPOBJ_TMPSIZ)) == (size_t)-1)
1212 strncpy( newname, ibuf, plen );
1213 newname[ plen ] = '\0';
1215 if (strlen(newname) != plen) {
1216 /* there's \0 in newname, e.g. it's a pathname not
1224 memcpy(&hint, ibuf, sizeof(hint));
1225 ibuf += sizeof(hint);
1227 memcpy(&len16, ibuf, sizeof(len16));
1228 ibuf += sizeof(len16);
1229 plen = ntohs(len16);
1232 if (plen > AFPOBJ_TMPSIZ) {
1235 strncpy( newname, ibuf, plen );
1236 newname[ plen ] = '\0';
1237 if (strlen(newname) != plen) {
1246 /* -----------------------------------
1248 int afp_copyfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1250 struct vol *s_vol, *d_vol;
1252 char *newname, *p, *upath;
1253 struct path *s_path;
1254 uint32_t sdid, ddid;
1255 int err, retvalue = AFP_OK;
1256 uint16_t svid, dvid;
1258 struct adouble ad, *adp;
1264 memcpy(&svid, ibuf, sizeof( svid ));
1265 ibuf += sizeof( svid );
1266 if (NULL == ( s_vol = getvolbyvid( svid )) ) {
1267 return( AFPERR_PARAM );
1270 memcpy(&sdid, ibuf, sizeof( sdid ));
1271 ibuf += sizeof( sdid );
1272 if (NULL == ( dir = dirlookup( s_vol, sdid )) ) {
1276 memcpy(&dvid, ibuf, sizeof( dvid ));
1277 ibuf += sizeof( dvid );
1278 memcpy(&ddid, ibuf, sizeof( ddid ));
1279 ibuf += sizeof( ddid );
1281 if (NULL == ( s_path = cname( s_vol, dir, &ibuf )) ) {
1282 return get_afp_errno(AFPERR_PARAM);
1284 if ( path_isadir(s_path) ) {
1285 return( AFPERR_BADTYPE );
1288 /* don't allow copies when the file is open.
1289 * XXX: the spec only calls for read/deny write access.
1290 * however, copyfile doesn't have any of that info,
1291 * and locks need to stay coherent. as a result,
1292 * we just balk if the file is opened already. */
1294 adp = of_ad(s_vol, s_path, &ad);
1296 if (ad_open(adp, s_path->u_name, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | ADFLAGS_RDONLY | ADFLAGS_SETSHRMD) < 0) {
1297 return AFPERR_DENYCONF;
1299 #ifdef HAVE_FSHARE_T
1301 shmd.f_access = F_RDACC;
1302 shmd.f_deny = F_NODNY;
1303 if (fcntl(ad_data_fileno(adp), F_SHARE, &shmd) != 0) {
1304 retvalue = AFPERR_DENYCONF;
1307 if (AD_RSRC_OPEN(adp) && fcntl(ad_reso_fileno(adp), F_SHARE, &shmd) != 0) {
1308 retvalue = AFPERR_DENYCONF;
1312 denyreadset = (ad_testlock(adp, ADEID_DFORK, AD_FILELOCK_DENY_RD) != 0 ||
1313 ad_testlock(adp, ADEID_RFORK, AD_FILELOCK_DENY_RD) != 0 );
1316 retvalue = AFPERR_DENYCONF;
1320 newname = obj->newtmp;
1321 strcpy( newname, s_path->m_name );
1323 p = ctoupath( s_vol, curdir, newname );
1325 retvalue = AFPERR_PARAM;
1329 if (NULL == ( d_vol = getvolbyvid( dvid )) ) {
1330 retvalue = AFPERR_PARAM;
1334 if (d_vol->v_flags & AFPVOL_RO) {
1335 retvalue = AFPERR_VLOCK;
1339 if (NULL == ( dir = dirlookup( d_vol, ddid )) ) {
1340 retvalue = afp_errno;
1344 if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) {
1345 retvalue = get_afp_errno(AFPERR_NOOBJ);
1349 if ( *s_path->m_name != '\0' ) {
1350 retvalue =path_error(s_path, AFPERR_NOOBJ);
1354 /* one of the handful of places that knows about the path type */
1355 if (copy_path_name(d_vol, newname, ibuf) < 0) {
1356 retvalue = AFPERR_PARAM;
1359 /* newname is always only a filename so curdir *is* its
1362 if (NULL == (upath = mtoupath(d_vol, newname, curdir->d_did, utf8_encoding(d_vol->v_obj)))) {
1363 retvalue =AFPERR_PARAM;
1367 if ( (err = copyfile(s_vol, d_vol, curdir, -1, p, upath , newname, adp)) < 0 ) {
1373 setvoltime(obj, d_vol );
1376 ad_close( adp, ADFLAGS_DF |ADFLAGS_HF | ADFLAGS_SETSHRMD);
1380 /* ----------------------------------
1381 * if newname is NULL (from directory.c) we don't want to copy the resource fork.
1382 * because we are doing it elsewhere.
1383 * currently if newname is NULL then adp is NULL.
1385 int copyfile(struct vol *s_vol,
1392 struct adouble *adp)
1394 struct adouble ads, add;
1401 LOG(log_debug, logtype_afpd, "copyfile(sfd:%d,s:'%s',d:'%s',n:'%s')",
1402 sfd, src, dst, newname);
1405 ad_init(&ads, s_vol);
1409 adflags = ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | ADFLAGS_RF | ADFLAGS_NORF;
1411 if (ad_openat(adp, sfd, src, adflags | ADFLAGS_RDONLY) < 0) {
1416 if (!AD_META_OPEN(adp))
1417 /* no resource fork, don't create one for dst file */
1418 adflags &= ~ADFLAGS_HF;
1420 if (!AD_RSRC_OPEN(adp))
1421 /* no resource fork, don't create one for dst file */
1422 adflags &= ~ADFLAGS_RF;
1424 stat_result = fstat(ad_data_fileno(adp), &st); /* saving stat exit code, thus saving us on one more stat later on */
1426 if (stat_result < 0) {
1427 /* unlikely but if fstat fails, the default file mode will be 0666. */
1428 st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
1431 ad_init(&add, d_vol);
1432 if (ad_open(&add, dst, adflags | ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_EXCL, st.st_mode | S_IRUSR | S_IWUSR) < 0) {
1434 ad_close( adp, adflags );
1435 if (EEXIST != ret_err) {
1436 deletefile(d_vol, -1, dst, 0);
1439 return AFPERR_EXIST;
1442 if ((err = copy_fork(ADEID_DFORK, &add, adp)) != 0)
1443 LOG(log_error, logtype_afpd, "copyfile('%s'): %s", src, strerror(errno));
1446 if ((err = d_vol->vfs->vfs_copyfile(d_vol, sfd, src, dst)) != 0)
1447 LOG(log_error, logtype_afpd, "copyfile('%s'): %s", src, strerror(errno));
1452 if (AD_META_OPEN(&add)) {
1453 if (AD_META_OPEN(adp))
1454 ad_copy_header(&add, adp);
1455 ad_setname(&add, dst);
1458 if (fstat(ad_meta_fileno(&add), &stdest) != 0) {
1462 if ((id = get_id(d_vol, &add, &stdest, d_dir->d_did, dst, strlen(dst))) == CNID_INVALID) {
1466 (void)ad_setid(&add, stdest.st_dev, stdest.st_ino, id, d_dir->d_did, d_vol->v_stamp);
1471 ad_close( adp, adflags );
1473 if (ad_close( &add, adflags ) <0) {
1478 deletefile(d_vol, -1, dst, 0);
1480 else if (stat_result == 0) {
1481 /* set dest modification date to src date */
1484 ut.actime = ut.modtime = st.st_mtime;
1486 /* FIXME netatalk doesn't use resource fork file date
1487 * but maybe we should set its modtime too.
1492 switch ( ret_err ) {
1498 LOG(log_info, logtype_afpd, "copyfile: DISK FULL");
1499 return AFPERR_DFULL;
1501 return AFPERR_NOOBJ;
1503 return AFPERR_ACCESS;
1505 return AFPERR_VLOCK;
1507 return AFPERR_PARAM;
1511 /* -----------------------------------
1512 vol: not NULL delete cnid entry. then we are in curdir and file is a only filename
1513 checkAttrib: 1 check kFPDeleteInhibitBit (deletfile called by afp_delete)
1515 when deletefile is called we don't have lock on it, file is closed (for us)
1516 untrue if called by renamefile
1518 ad_open always try to open file RDWR first and ad_lock takes care of
1519 WRITE lock on read only file.
1522 static int check_attrib(struct adouble *adp)
1524 uint16_t bshort = 0;
1526 ad_getattr(adp, &bshort);
1528 * Does kFPDeleteInhibitBit (bit 8) set?
1530 if ((bshort & htons(ATTRBIT_NODELETE))) {
1531 return AFPERR_OLOCK;
1533 if ((bshort & htons(ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
1539 * dirfd can be used for unlinkat semantics
1541 int deletefile(const struct vol *vol, int dirfd, char *file, int checkAttrib)
1544 struct adouble *adp = NULL;
1545 int adflags, err = AFP_OK;
1548 LOG(log_debug, logtype_afpd, "deletefile('%s')", file);
1552 /* was EACCESS error try to get only metadata */
1553 /* we never want to create a resource fork here, we are going to delete it
1554 * moreover sometimes deletefile is called with a no existent file and
1555 * ad_open would create a 0 byte resource fork
1557 if ( ad_metadataat(dirfd, file, ADFLAGS_CHECK_OF, &ad) == 0 ) {
1558 if ((err = check_attrib(&ad))) {
1559 ad_close(&ad, ADFLAGS_HF | ADFLAGS_CHECK_OF);
1566 /* try to open both forks at once */
1567 adflags = ADFLAGS_DF;
1568 if (ad_openat(&ad, dirfd, file, adflags | ADFLAGS_RF | ADFLAGS_NORF | ADFLAGS_RDONLY) < 0 ) {
1573 case EACCES: /* maybe it's a file with no write mode for us */
1574 break; /* was return AFPERR_ACCESS;*/
1587 if ( adp && AD_RSRC_OPEN(adp) ) { /* there's a resource fork */
1588 adflags |= ADFLAGS_RF;
1589 /* FIXME we have a pb here because we want to know if a file is open
1590 * there's a 'priority inversion' if you can't open the ressource fork RW
1591 * you can delete it if it's open because you can't get a write lock.
1593 * ADLOCK_FILELOCK means the whole ressource fork, not only after the
1596 * FIXME it doesn't work for RFORK open read only and fork open without deny mode
1598 if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
1604 if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
1605 LOG(log_error, logtype_afpd, "deletefile('%s'): ad_tmplock error: %s", file, strerror(errno));
1607 } else if (!(err = vol->vfs->vfs_deletefile(vol, dirfd, file)) && !(err = netatalk_unlinkat(dirfd, file )) ) {
1610 AFP_CNID_START("cnid_get");
1611 id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file));
1614 AFP_CNID_START("cnid_delete");
1615 cnid_delete(vol->v_cdb, id);
1623 ad_close(&ad, ADFLAGS_HF | ADFLAGS_CHECK_OF);
1626 ad_close( &ad, adflags ); /* ad_close removes locks if any */
1631 /* ------------------------------------ */
1632 /* return a file id */
1633 int afp_createid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1642 struct path *s_path;
1648 memcpy(&vid, ibuf, sizeof(vid));
1649 ibuf += sizeof(vid);
1651 if (NULL == ( vol = getvolbyvid( vid )) ) {
1652 return( AFPERR_PARAM);
1655 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1659 if (vol->v_flags & AFPVOL_RO)
1660 return AFPERR_VLOCK;
1662 memcpy(&did, ibuf, sizeof( did ));
1663 ibuf += sizeof(did);
1665 if (NULL == ( dir = dirlookup( vol, did )) ) {
1666 return afp_errno; /* was AFPERR_PARAM */
1669 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
1670 return get_afp_errno(AFPERR_NOOBJ); /* was AFPERR_PARAM */
1673 if ( path_isadir(s_path) ) {
1674 return( AFPERR_BADTYPE );
1677 upath = s_path->u_name;
1678 switch (s_path->st_errno) {
1680 break; /* success */
1683 return AFPERR_ACCESS;
1685 return AFPERR_NOOBJ;
1687 return AFPERR_PARAM;
1690 AFP_CNID_START("cnid_lookup");
1691 id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath));
1694 memcpy(rbuf, &id, sizeof(id));
1695 *rbuflen = sizeof(id);
1696 return AFPERR_EXISTID;
1699 if ((id = get_id(vol, NULL, st, did, upath, len)) != CNID_INVALID) {
1700 memcpy(rbuf, &id, sizeof(id));
1701 *rbuflen = sizeof(id);
1708 /* ------------------------------- */
1714 static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
1717 struct reenum *param = data;
1718 struct vol *vol = param->vol;
1719 cnid_t did = param->did;
1722 if (ostat(de->d_name, &path.st, vol_syml_opt(vol)) < 0)
1725 /* update or add to cnid */
1726 AFP_CNID_START("cnid_add");
1727 aint = cnid_add(vol->v_cdb, &path.st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */
1733 /* --------------------
1734 * Ok the db is out of synch with the dir.
1735 * but if it's a deleted file we don't want to do it again and again.
1738 reenumerate_id(struct vol *vol, char *name, struct dir *dir)
1744 if (vol->v_cdb == NULL) {
1748 /* FIXME use of_statdir ? */
1749 if (ostat(name, &st, vol_syml_opt(vol))) {
1753 if (dirreenumerate(dir, &st)) {
1754 /* we already did it once and the dir haven't been modified */
1755 return dir->d_offcnt;
1759 data.did = dir->d_did;
1760 if ((ret = for_each_dirent(vol, name, reenumerate_loop, (void *)&data)) >= 0) {
1761 setdiroffcnt(curdir, &st, ret);
1762 dir->d_flags |= DIRF_CNID;
1768 /* ------------------------------
1769 resolve a file id */
1770 int afp_resolveid(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1779 uint16_t vid, bitmap;
1781 static char buffer[12 + MAXPATHLEN + 1];
1782 int len = 12 + MAXPATHLEN + 1;
1787 memcpy(&vid, ibuf, sizeof(vid));
1788 ibuf += sizeof(vid);
1790 if (NULL == ( vol = getvolbyvid( vid )) ) {
1791 return( AFPERR_PARAM);
1794 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1798 memcpy(&id, ibuf, sizeof( id ));
1803 /* some MacOS versions after a catsearch do a *lot* of afp_resolveid with 0 */
1807 AFP_CNID_START("cnid_resolve");
1808 upath = cnid_resolve(vol->v_cdb, &id, buffer, len);
1810 if (upath == NULL) {
1811 return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
1814 if (NULL == ( dir = dirlookup( vol, id )) ) {
1815 return AFPERR_NOID; /* idem AFPERR_PARAM */
1817 if (movecwd(vol, dir) < 0) {
1821 return AFPERR_ACCESS;
1825 return AFPERR_PARAM;
1829 memset(&path, 0, sizeof(path));
1830 path.u_name = upath;
1831 if (of_stat(vol, &path) < 0 ) {
1833 /* with nfs and our working directory is deleted */
1834 if (errno == ESTALE) {
1838 if ( errno == ENOENT && !retry) {
1839 /* cnid db is out of sync, reenumerate the directory and update ids */
1840 reenumerate_id(vol, ".", dir);
1848 return AFPERR_ACCESS;
1852 return AFPERR_PARAM;
1856 /* directories are bad */
1857 if (S_ISDIR(path.st.st_mode)) {
1858 /* OS9 and OSX don't return the same error code */
1859 return (obj->afp_version >=30)?AFPERR_NOID:AFPERR_BADTYPE;
1862 memcpy(&bitmap, ibuf, sizeof(bitmap));
1863 bitmap = ntohs( bitmap );
1864 if (NULL == (path.m_name = utompath(vol, upath, cnid, utf8_encoding(obj)))) {
1868 if (AFP_OK != (err = getfilparams(obj, vol, bitmap, &path , curdir,
1869 rbuf + sizeof(bitmap), &buflen, 0))) {
1872 *rbuflen = buflen + sizeof(bitmap);
1873 memcpy(rbuf, ibuf, sizeof(bitmap));
1878 /* ------------------------------ */
1879 int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1889 static char buffer[12 + MAXPATHLEN + 1];
1890 int len = 12 + MAXPATHLEN + 1;
1895 memcpy(&vid, ibuf, sizeof(vid));
1896 ibuf += sizeof(vid);
1898 if (NULL == ( vol = getvolbyvid( vid )) ) {
1899 return( AFPERR_PARAM);
1902 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1906 if (vol->v_flags & AFPVOL_RO)
1907 return AFPERR_VLOCK;
1909 memcpy(&id, ibuf, sizeof( id ));
1913 AFP_CNID_START("cnid_resolve");
1914 upath = cnid_resolve(vol->v_cdb, &id, buffer, len);
1916 if (upath == NULL) {
1920 if (NULL == ( dir = dirlookup( vol, id )) ) {
1921 if (afp_errno == AFPERR_NOOBJ) {
1925 return( AFPERR_PARAM );
1929 if ((movecwd(vol, dir) < 0) || (ostat(upath, &st, vol_syml_opt(vol)) < 0)) {
1933 return AFPERR_ACCESS;
1938 /* still try to delete the id */
1942 return AFPERR_PARAM;
1945 else if (S_ISDIR(st.st_mode)) /* directories are bad */
1946 return AFPERR_BADTYPE;
1949 AFP_CNID_START("cnid_delete");
1950 if (cnid_delete(vol->v_cdb, fileid)) {
1954 return AFPERR_VLOCK;
1957 return AFPERR_ACCESS;
1959 return AFPERR_PARAM;
1966 /* ------------------------------ */
1967 static struct adouble *find_adouble(const AFPObj *obj, struct vol *vol, struct path *path, struct ofork **of, struct adouble *adp)
1971 if (path->st_errno) {
1972 switch (path->st_errno) {
1974 afp_errno = AFPERR_NOID;
1978 afp_errno = AFPERR_ACCESS;
1981 afp_errno = AFPERR_PARAM;
1986 /* we use file_access both for legacy Mac perm and
1987 * for unix privilege, rename will take care of folder perms
1989 if (file_access(obj, vol, path, OPENACC_WR ) < 0) {
1990 afp_errno = AFPERR_ACCESS;
1994 if ((*of = of_findname(vol, path))) {
1995 /* reuse struct adouble so it won't break locks */
1999 ret = ad_open(adp, path->u_name, ADFLAGS_HF | ADFLAGS_RDWR);
2001 if ( !ret && ad_reso_fileno(adp) != -1 && !(adp->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
2003 * The user must have the Read & Write privilege for both files in order to use this command.
2005 ad_close(adp, ADFLAGS_HF);
2006 afp_errno = AFPERR_ACCESS;
2013 #define APPLETEMP ".AppleTempXXXXXX"
2015 int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
2017 struct stat srcst, destst;
2019 struct dir *dir, *sdir;
2020 char *spath, temp[17], *p;
2021 char *supath, *upath;
2026 struct adouble *adsp = NULL;
2027 struct adouble *addp = NULL;
2028 struct ofork *s_of = NULL;
2029 struct ofork *d_of = NULL;
2039 memcpy(&vid, ibuf, sizeof(vid));
2040 ibuf += sizeof(vid);
2042 if (NULL == ( vol = getvolbyvid( vid )) ) {
2043 return( AFPERR_PARAM);
2046 if ((vol->v_flags & AFPVOL_RO))
2047 return AFPERR_VLOCK;
2049 /* source and destination dids */
2050 memcpy(&sid, ibuf, sizeof(sid));
2051 ibuf += sizeof(sid);
2052 memcpy(&did, ibuf, sizeof(did));
2053 ibuf += sizeof(did);
2056 if (NULL == (dir = dirlookup( vol, sid )) ) {
2057 return afp_errno; /* was AFPERR_PARAM */
2060 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2061 return get_afp_errno(AFPERR_NOOBJ);
2064 if ( path_isadir(path) ) {
2065 return AFPERR_BADTYPE; /* it's a dir */
2068 /* save some stuff */
2071 spath = obj->oldtmp;
2072 supath = obj->newtmp;
2073 strcpy(spath, path->m_name);
2074 strcpy(supath, path->u_name); /* this is for the cnid changing */
2075 p = absupath( vol, sdir, supath);
2077 /* pathname too long */
2078 return AFPERR_PARAM ;
2082 if (!(adsp = find_adouble(obj, vol, path, &s_of, &ads))) {
2086 /* ***** from here we may have resource fork open **** */
2088 /* look for the source cnid. if it doesn't exist, don't worry about
2090 AFP_CNID_START("cnid_lookup");
2091 sid = cnid_lookup(vol->v_cdb, &srcst, sdir->d_did, supath,slen = strlen(supath));
2094 if (NULL == ( dir = dirlookup( vol, did )) ) {
2095 err = afp_errno; /* was AFPERR_PARAM */
2096 goto err_exchangefile;
2099 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2100 err = get_afp_errno(AFPERR_NOOBJ);
2101 goto err_exchangefile;
2104 if ( path_isadir(path) ) {
2105 err = AFPERR_BADTYPE;
2106 goto err_exchangefile;
2109 /* FPExchangeFiles is the only call that can return the SameObj
2111 if ((curdir == sdir) && strcmp(spath, path->m_name) == 0) {
2112 err = AFPERR_SAMEOBJ;
2113 goto err_exchangefile;
2117 if (!(addp = find_adouble(obj, vol, path, &d_of, &add))) {
2119 goto err_exchangefile;
2123 /* they are not on the same device and at least one is open
2124 * FIXME broken for for crossdev and adouble v2
2127 crossdev = (srcst.st_dev != destst.st_dev);
2128 if (/* (d_of || s_of) && */ crossdev) {
2130 goto err_exchangefile;
2133 /* look for destination id. */
2134 upath = path->u_name;
2135 AFP_CNID_START("cnid_lookup");
2136 did = cnid_lookup(vol->v_cdb, &destst, curdir->d_did, upath, dlen = strlen(upath));
2139 /* construct a temp name.
2140 * NOTE: the temp file will be in the dest file's directory. it
2141 * will also be inaccessible from AFP. */
2142 memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
2144 if ((fd = mkstemp(temp)) == -1) {
2146 goto err_exchangefile;
2151 /* FIXME we need to close fork for copy, both s_of and d_of are null */
2152 ad_close(adsp, ADFLAGS_HF);
2153 ad_close(addp, ADFLAGS_HF);
2156 /* now, quickly rename the file. we error if we can't. */
2157 if ((err = renamefile(vol, curdir, -1, p, temp, temp, adsp)) != AFP_OK)
2158 goto err_exchangefile;
2159 of_rename(vol, s_of, sdir, spath, curdir, temp);
2161 /* rename destination to source */
2162 if ((err = renamefile(vol, curdir, -1, upath, p, spath, addp)) != AFP_OK)
2163 goto err_src_to_tmp;
2164 of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
2166 /* rename temp to destination */
2167 if ((err = renamefile(vol, curdir, -1, temp, upath, path->m_name, adsp)) != AFP_OK)
2168 goto err_dest_to_src;
2169 of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
2172 * id's need switching. src -> dest and dest -> src.
2173 * we need to re-stat() if it was a cross device copy.
2176 AFP_CNID_START("cnid_delete");
2177 cnid_delete(vol->v_cdb, sid);
2181 AFP_CNID_START("cnid_delete");
2182 cnid_delete(vol->v_cdb, did);
2186 if ((did && ( (crossdev && ostat(upath, &srcst, vol_syml_opt(vol)) < 0) ||
2187 cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
2189 (sid && ( (crossdev && ostat(p, &destst, vol_syml_opt(vol)) < 0) ||
2190 cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
2195 err = AFPERR_ACCESS;
2200 goto err_temp_to_dest;
2203 /* here we need to reopen if crossdev */
2204 if (sid && ad_setid(addp, destst.st_dev, destst.st_ino, sid, sdir->d_did, vol->v_stamp))
2209 if (did && ad_setid(adsp, srcst.st_dev, srcst.st_ino, did, curdir->d_did, vol->v_stamp))
2214 /* change perms, src gets dest perm and vice versa */
2219 * we need to exchange ACL entries as well
2221 /* exchange_acls(vol, p, upath); */
2226 path->m_name = NULL;
2227 path->u_name = upath;
2229 setfilunixmode(vol, path, destst.st_mode);
2230 setfilowner(vol, destst.st_uid, destst.st_gid, path);
2237 setfilunixmode(vol, path, srcst.st_mode);
2238 setfilowner(vol, srcst.st_uid, srcst.st_gid, path);
2243 goto err_exchangefile;
2245 /* all this stuff is so that we can unwind a failed operation
2248 /* rename dest to temp */
2249 renamefile(vol, curdir, -1, upath, temp, temp, adsp);
2250 of_rename(vol, s_of, curdir, upath, curdir, temp);
2253 /* rename source back to dest */
2254 renamefile(vol, curdir, -1, p, upath, path->m_name, addp);
2255 of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
2258 /* rename temp back to source */
2259 renamefile(vol, curdir, -1, temp, p, spath, adsp);
2260 of_rename(vol, s_of, curdir, temp, sdir, spath);
2263 if ( !s_of && adsp && ad_meta_fileno(adsp) != -1 ) { /* META */
2264 ad_close(adsp, ADFLAGS_HF);
2266 if ( !d_of && addp && ad_meta_fileno(addp) != -1 ) {/* META */
2267 ad_close(addp, ADFLAGS_HF);
2271 if ((cached = dircache_search_by_did(vol, sid)) != NULL)
2272 (void)dir_remove(vol, cached);
2273 if ((cached = dircache_search_by_did(vol, did)) != NULL)
2274 (void)dir_remove(vol, cached);