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 );
464 if (adp->ad_rlen > 0xffffffff)
467 aint = htonl( adp->ad_rlen);
471 memcpy(data, &aint, sizeof( aint ));
472 data += sizeof( aint );
475 /* Current client needs ProDOS info block for this file.
476 Use simple heuristic and let the Mac "type" string tell
477 us what the PD file code should be. Everything gets a
478 subtype of 0x0000 unless the original value was hashed
479 to "pXYZ" when we created it. See IA, Ver 2.
480 <shirsch@adelphia.net> */
481 case FILPBIT_PDINFO :
482 if (obj->afp_version >= 30) { /* UTF8 name */
483 utf8 = kTextEncodingUTF8;
485 data += sizeof( uint16_t );
487 memcpy(data, &aint, sizeof( aint ));
488 data += sizeof( aint );
492 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
494 if ( memcmp( fdType, "TEXT", 4 ) == 0 ) {
498 else if ( memcmp( fdType, "PSYS", 4 ) == 0 ) {
502 else if ( memcmp( fdType, "PS16", 4 ) == 0 ) {
506 else if ( memcmp( fdType, "BINA", 4 ) == 0 ) {
510 else if ( fdType[0] == 'p' ) {
512 ashort = (fdType[2] * 256) + fdType[3];
526 memcpy(data, &ashort, sizeof( ashort ));
527 data += sizeof( ashort );
528 memset(data, 0, sizeof( ashort ));
529 data += sizeof( ashort );
532 case FILPBIT_EXTDFLEN:
533 aint = htonl(st->st_size >> 32);
534 memcpy(data, &aint, sizeof( aint ));
535 data += sizeof( aint );
536 aint = htonl(st->st_size);
537 memcpy(data, &aint, sizeof( aint ));
538 data += sizeof( aint );
540 case FILPBIT_EXTRFLEN:
543 aint = htonl(adp->ad_rlen >> 32);
544 memcpy(data, &aint, sizeof( aint ));
545 data += sizeof( aint );
547 aint = htonl(adp->ad_rlen);
548 memcpy(data, &aint, sizeof( aint ));
549 data += sizeof( aint );
551 case FILPBIT_UNIXPR :
552 /* accessmode may change st_mode with ACLs */
553 accessmode(obj, vol, upath, &ma, dir , st);
555 aint = htonl(st->st_uid);
556 memcpy( data, &aint, sizeof( aint ));
557 data += sizeof( aint );
558 aint = htonl(st->st_gid);
559 memcpy( data, &aint, sizeof( aint ));
560 data += sizeof( aint );
563 type == slnk indicates an OSX style symlink,
564 we have to add S_IFLNK to the mode, otherwise
565 10.3 clients freak out. */
569 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
570 if ( memcmp( fdType, "slnk", 4 ) == 0 ) {
576 memcpy( data, &aint, sizeof( aint ));
577 data += sizeof( aint );
579 *data++ = ma.ma_user;
580 *data++ = ma.ma_world;
581 *data++ = ma.ma_group;
582 *data++ = ma.ma_owner;
586 return( AFPERR_BITMAP );
592 ashort = htons( data - buf );
593 memcpy(l_nameoff, &ashort, sizeof( ashort ));
594 data = set_name(vol, data, dir->d_did, path->m_name, id, 0);
597 ashort = htons( data - buf );
598 memcpy(utf_nameoff, &ashort, sizeof( ashort ));
599 data = set_name(vol, data, dir->d_did, path->m_name, id, utf8);
601 *buflen = data - buf;
605 /* ----------------------- */
606 int getfilparams(const AFPObj *obj, struct vol *vol, uint16_t bitmap, struct path *path,
607 struct dir *dir, char *buf, size_t *buflen, int in_enumerate)
609 struct adouble ad, *adp;
612 int flags; /* uninitialized ok */
614 LOG(log_debug, logtype_afpd, "getfilparams(\"%s\")", path->u_name);
616 opened = PARAM_NEED_ADP(bitmap);
622 * Dont check for and resturn open fork attributes when enumerating
623 * This saves a lot of syscalls, the client will hopefully only use the result
624 * in FPGetFileParms where we return the correct value
626 flags = (!in_enumerate &&(bitmap & (1 << FILPBIT_ATTR))) ? ADFLAGS_CHECK_OF : 0;
628 adp = of_ad(vol, path, &ad);
629 upath = path->u_name;
631 if ( ad_metadata( upath, flags, adp) < 0 ) {
634 LOG(log_error, logtype_afpd, "getfilparams(%s): %s: check resource fork permission?",
635 upath, strerror(errno));
636 return AFPERR_ACCESS;
638 LOG(log_error, logtype_afpd, "getfilparams(%s): bad resource fork", upath);
647 rc = getmetadata(obj, vol, bitmap, path, dir, buf, buflen, adp);
650 ad_close(adp, ADFLAGS_HF | flags);
655 /* ----------------------------- */
656 int afp_createfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
661 struct ofork *of = NULL;
663 int creatf, did, openf, retvalue = AFP_OK;
669 creatf = (unsigned char) *ibuf++;
671 memcpy(&vid, ibuf, sizeof( vid ));
672 ibuf += sizeof( vid );
674 if (NULL == ( vol = getvolbyvid( vid )) )
675 return( AFPERR_PARAM );
677 if (vol->v_flags & AFPVOL_RO)
680 memcpy(&did, ibuf, sizeof( did));
681 ibuf += sizeof( did );
683 if (NULL == ( dir = dirlookup( vol, did )) )
686 if (NULL == ( s_path = cname( vol, dir, &ibuf )) )
687 return get_afp_errno(AFPERR_PARAM);
688 if ( *s_path->m_name == '\0' )
689 return( AFPERR_BADTYPE );
691 upath = s_path->u_name;
694 /* if upath is deleted we already in trouble anyway */
695 if ((of = of_findname(vol, s_path))) {
703 openf = ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_TRUNC;
705 /* on a soft create, if the file is open then ad_open won't fail
706 because open syscall is not called */
707 openf = ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_EXCL;
709 if (ad_open(&ad, upath, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | openf, 0666) < 0) {
713 case ENOENT : /* we were already in 'did folder' so chdir() didn't fail */
714 return ( AFPERR_NOOBJ );
716 return( AFPERR_EXIST );
718 return( AFPERR_ACCESS );
721 LOG(log_info, logtype_afpd, "afp_createfile: DISK FULL");
722 return( AFPERR_DFULL );
724 return( AFPERR_PARAM );
727 if ( ad_meta_fileno( &ad ) == -1 ) { /* Hard META / HF */
728 /* FIXME with hard create on an existing file, we already
729 * corrupted the data file.
731 netatalk_unlink( upath );
732 ad_close( &ad, ADFLAGS_DF );
733 return AFPERR_ACCESS;
736 path = s_path->m_name;
737 ad_setname(&ad, path);
740 if (lstat(upath, &st) != 0) {
741 LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): stat: %s",
742 upath, strerror(errno));
743 ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF);
748 if ((id = get_id(vol, &ad, &st, dir->d_did, upath, strlen(upath))) == CNID_INVALID) {
749 LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): CNID error", upath);
750 goto createfile_iderr;
752 (void)ad_setid(&ad, st.st_dev, st.st_ino, id, dir->d_did, vol->v_stamp);
756 ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF );
757 fce_register(FCE_FILE_CREATE, fullpathname(upath), NULL, fce_file);
761 setvoltime(obj, vol );
766 int afp_setfilparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
772 uint16_t vid, bitmap;
777 memcpy(&vid, ibuf, sizeof( vid ));
778 ibuf += sizeof( vid );
779 if (NULL == ( vol = getvolbyvid( vid )) ) {
780 return( AFPERR_PARAM );
783 if (vol->v_flags & AFPVOL_RO)
786 memcpy(&did, ibuf, sizeof( did ));
787 ibuf += sizeof( did );
788 if (NULL == ( dir = dirlookup( vol, did )) ) {
789 return afp_errno; /* was AFPERR_NOOBJ */
792 memcpy(&bitmap, ibuf, sizeof( bitmap ));
793 bitmap = ntohs( bitmap );
794 ibuf += sizeof( bitmap );
796 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
797 return get_afp_errno(AFPERR_PARAM);
800 if (path_isadir(s_path)) {
801 return( AFPERR_BADTYPE ); /* it's a directory */
804 if ( s_path->st_errno != 0 ) {
805 return( AFPERR_NOOBJ );
808 if ((u_long)ibuf & 1 ) {
812 if (AFP_OK == ( rc = setfilparams(obj, vol, s_path, bitmap, ibuf )) ) {
813 setvoltime(obj, vol );
820 * cf AFP3.0.pdf page 252 for change_mdate and change_parent_mdate logic
823 extern struct path Cur_Path;
825 int setfilparams(const AFPObj *obj, struct vol *vol,
826 struct path *path, uint16_t f_bitmap, char *buf )
828 struct adouble ad, *adp;
830 int bit, isad = 1, err = AFP_OK;
832 u_char achar, *fdType, xyy[4]; /* uninitialized, OK 310105 */
833 uint16_t ashort, bshort, oshort;
836 uint16_t upriv_bit = 0;
838 int change_mdate = 0;
839 int change_parent_mdate = 0;
844 uint16_t bitmap = f_bitmap;
845 uint32_t cdate,bdate;
846 u_char finder_buf[32];
847 int symlinked = S_ISLNK(path->st.st_mode);
850 char symbuf[MAXPATHLEN+1];
853 LOG(log_debug9, logtype_afpd, "begin setfilparams:");
856 adp = of_ad(vol, path, &ad);
857 upath = path->u_name;
859 if (!vol_unix_priv(vol) && check_access(obj, vol, upath, OPENACC_WR ) < 0) {
860 return AFPERR_ACCESS;
863 /* with unix priv maybe we have to change adouble file priv first */
865 while ( bitmap != 0 ) {
866 while (( bitmap & 1 ) == 0 ) {
873 memcpy(&ashort, buf, sizeof( ashort ));
874 buf += sizeof( ashort );
878 memcpy(&cdate, buf, sizeof(cdate));
879 buf += sizeof( cdate );
882 memcpy(&newdate, buf, sizeof( newdate ));
883 buf += sizeof( newdate );
887 memcpy(&bdate, buf, sizeof( bdate));
888 buf += sizeof( bdate );
892 if (memcmp(buf,"slnkrhap",8) == 0
893 && !(S_ISLNK(path->st.st_mode))
894 && !(vol->v_flags & AFPVOL_FOLLOWSYM)) {
895 /* request to turn this into a symlink */
896 if ((fp = open(path->u_name, O_RDONLY)) == -1) {
898 goto setfilparam_done;
900 len = read(fp, symbuf, MAXPATHLEN);
904 goto setfilparam_done;
906 if (unlink(path->u_name) != 0) {
908 goto setfilparam_done;
911 if (symlink(symbuf, path->u_name) != 0) {
913 goto setfilparam_done;
918 memcpy(finder_buf, buf, 32 );
921 case FILPBIT_UNIXPR :
922 if (!vol_unix_priv(vol)) {
923 /* this volume doesn't use unix priv */
929 change_parent_mdate = 1;
931 memcpy( &aint, buf, sizeof( aint ));
932 f_uid = ntohl (aint);
933 buf += sizeof( aint );
934 memcpy( &aint, buf, sizeof( aint ));
935 f_gid = ntohl (aint);
936 buf += sizeof( aint );
937 setfilowner(vol, f_uid, f_gid, path);
939 memcpy( &upriv, buf, sizeof( upriv ));
940 buf += sizeof( upriv );
941 upriv = ntohl (upriv);
942 if ((upriv & S_IWUSR)) {
943 setfilunixmode(vol, path, upriv);
950 case FILPBIT_PDINFO :
951 if (obj->afp_version < 30) { /* else it's UTF8 name */
954 /* Keep special case to support crlf translations */
955 if ((unsigned int) achar == 0x04) {
956 fdType = (u_char *)"TEXT";
959 xyy[0] = ( u_char ) 'p';
970 /* break while loop */
979 /* second try with adouble open
981 if (ad_open(adp, upath, ADFLAGS_HF | ADFLAGS_RDWR | ADFLAGS_CREATE, 0666) < 0) {
982 LOG(log_debug, logtype_afpd, "setfilparams: ad_open_metadata error");
984 * For some things, we don't need an adouble header:
985 * - change of modification date
986 * - UNIX privs (Bug-ID #2863424)
988 if (!symlinked && f_bitmap & ~(1<<FILPBIT_MDATE | 1<<FILPBIT_UNIXPR)) {
989 LOG(log_debug, logtype_afpd, "setfilparams: need adouble access");
990 return AFPERR_ACCESS;
992 LOG(log_debug, logtype_afpd, "setfilparams: no adouble perms, but only FILPBIT_MDATE and/or FILPBIT_UNIXPR");
994 } else if ((ad_get_MD_flags( adp ) & O_CREAT) ) {
995 ad_setname(adp, path->m_name);
997 if ((id = get_id(vol, adp, &path->st, curdir->d_did, upath, strlen(upath))) == CNID_INVALID) {
998 LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): CNID error", upath);
1001 (void)ad_setid(adp, path->st.st_dev, path->st.st_ino, id, curdir->d_did, vol->v_stamp);
1006 while ( bitmap != 0 ) {
1007 while (( bitmap & 1 ) == 0 ) {
1014 ad_getattr(adp, &bshort);
1016 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
1017 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
1021 if ((bshort & htons(ATTRBIT_INVISIBLE)) != (oshort & htons(ATTRBIT_INVISIBLE)))
1022 change_parent_mdate = 1;
1023 ad_setattr(adp, bshort);
1025 case FILPBIT_CDATE :
1026 ad_setdate(adp, AD_DATE_CREATE, cdate);
1028 case FILPBIT_MDATE :
1030 case FILPBIT_BDATE :
1031 ad_setdate(adp, AD_DATE_BACKUP, bdate);
1033 case FILPBIT_FINFO :
1034 if (default_type( ad_entry( adp, ADEID_FINDERI ))
1036 ((em = getextmap( path->m_name )) &&
1037 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1038 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1039 || ((em = getdefextmap()) &&
1040 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1041 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1043 memcpy(finder_buf, ufinderi, 8 );
1045 memcpy(ad_entry( adp, ADEID_FINDERI ), finder_buf, 32 );
1047 case FILPBIT_UNIXPR :
1049 setfilunixmode(vol, path, upriv);
1052 case FILPBIT_PDINFO :
1053 if (obj->afp_version < 30) { /* else it's UTF8 name */
1054 memcpy(ad_entry( adp, ADEID_FINDERI ), fdType, 4 );
1055 memcpy(ad_entry( adp, ADEID_FINDERI ) + 4, "pdos", 4 );
1060 err = AFPERR_BITMAP;
1061 goto setfilparam_done;
1068 if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
1069 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1073 ad_setdate(adp, AD_DATE_MODIFY, newdate);
1074 ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
1080 ad_close(adp, ADFLAGS_HF);
1083 if (change_parent_mdate && gettimeofday(&tv, NULL) == 0) {
1084 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1085 bitmap = 1<<FILPBIT_MDATE;
1086 setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
1090 LOG(log_debug9, logtype_afpd, "end setfilparams:");
1096 * renamefile and copyfile take the old and new unix pathnames
1097 * and the new mac name.
1099 * sdir_fd source dir fd to which src path is relative (for openat et al semantics)
1100 * passing -1 means this is not used, src path is a full path
1101 * src the source path
1102 * dst the dest filename in current dir
1103 * newname the dest mac name
1104 * adp adouble struct of src file, if open, or & zeroed one
1107 int renamefile(struct vol *vol, struct dir *ddir, int sdir_fd, char *src, char *dst, char *newname, struct adouble *adp)
1111 LOG(log_debug, logtype_afpd,
1112 "renamefile: src[%d, \"%s\"] -> dst[\"%s\"]", sdir_fd, src, dst);
1114 if ( unix_rename( sdir_fd, src, -1, dst ) < 0 ) {
1117 return( AFPERR_NOOBJ );
1120 return( AFPERR_ACCESS );
1122 return AFPERR_VLOCK;
1123 case EXDEV : /* Cross device move -- try copy */
1124 /* NOTE: with open file it's an error because after the copy we will
1125 * get two files, it's fixable for our process (eg reopen the new file, get the
1126 * locks, and so on. But it doesn't solve the case with a second process
1128 if (adp->ad_open_forks) {
1129 /* FIXME warning in syslog so admin'd know there's a conflict ?*/
1130 return AFPERR_OLOCK; /* little lie */
1132 if (AFP_OK != ( rc = copyfile(vol, vol, ddir, sdir_fd, src, dst, newname, NULL )) ) {
1133 /* on error copyfile delete dest */
1136 return deletefile(vol, sdir_fd, src, 0);
1138 return( AFPERR_PARAM );
1142 if (vol->vfs->vfs_renamefile(vol, sdir_fd, src, dst) < 0 ) {
1146 /* try to undo the data fork rename,
1147 * we know we are on the same device
1150 unix_rename(-1, dst, sdir_fd, src );
1151 /* return the first error */
1154 return AFPERR_NOOBJ;
1157 return AFPERR_ACCESS ;
1159 return AFPERR_VLOCK;
1161 return AFPERR_PARAM ;
1166 /* don't care if we can't open the newly renamed ressource fork */
1167 if (ad_open(adp, dst, ADFLAGS_HF | ADFLAGS_RDWR) == 0) {
1168 ad_setname(adp, newname);
1170 ad_close( adp, ADFLAGS_HF );
1177 convert a Mac long name to an utf8 name,
1179 size_t mtoUTF8(const struct vol *vol, const char *src, size_t srclen, char *dest, size_t destlen)
1183 if ((size_t)-1 == (outlen = convert_string ( vol->v_maccharset, CH_UTF8_MAC, src, srclen, dest, destlen)) ) {
1189 /* ---------------- */
1190 int copy_path_name(const struct vol *vol, char *newname, char *ibuf)
1197 if ( type != 2 && !(vol->v_obj->afp_version >= 30 && type == 3) ) {
1203 if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
1204 if (vol->v_obj->afp_version >= 30) {
1205 /* convert it to UTF8
1207 if ((plen = mtoUTF8(vol, ibuf, plen, newname, AFPOBJ_TMPSIZ)) == (size_t)-1)
1211 strncpy( newname, ibuf, plen );
1212 newname[ plen ] = '\0';
1214 if (strlen(newname) != plen) {
1215 /* there's \0 in newname, e.g. it's a pathname not
1223 memcpy(&hint, ibuf, sizeof(hint));
1224 ibuf += sizeof(hint);
1226 memcpy(&len16, ibuf, sizeof(len16));
1227 ibuf += sizeof(len16);
1228 plen = ntohs(len16);
1231 if (plen > AFPOBJ_TMPSIZ) {
1234 strncpy( newname, ibuf, plen );
1235 newname[ plen ] = '\0';
1236 if (strlen(newname) != plen) {
1245 /* -----------------------------------
1247 int afp_copyfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1249 struct vol *s_vol, *d_vol;
1251 char *newname, *p, *upath;
1252 struct path *s_path;
1253 uint32_t sdid, ddid;
1254 int err, retvalue = AFP_OK;
1255 uint16_t svid, dvid;
1257 struct adouble ad, *adp;
1263 memcpy(&svid, ibuf, sizeof( svid ));
1264 ibuf += sizeof( svid );
1265 if (NULL == ( s_vol = getvolbyvid( svid )) ) {
1266 return( AFPERR_PARAM );
1269 memcpy(&sdid, ibuf, sizeof( sdid ));
1270 ibuf += sizeof( sdid );
1271 if (NULL == ( dir = dirlookup( s_vol, sdid )) ) {
1275 memcpy(&dvid, ibuf, sizeof( dvid ));
1276 ibuf += sizeof( dvid );
1277 memcpy(&ddid, ibuf, sizeof( ddid ));
1278 ibuf += sizeof( ddid );
1280 if (NULL == ( s_path = cname( s_vol, dir, &ibuf )) ) {
1281 return get_afp_errno(AFPERR_PARAM);
1283 if ( path_isadir(s_path) ) {
1284 return( AFPERR_BADTYPE );
1287 /* don't allow copies when the file is open.
1288 * XXX: the spec only calls for read/deny write access.
1289 * however, copyfile doesn't have any of that info,
1290 * and locks need to stay coherent. as a result,
1291 * we just balk if the file is opened already. */
1293 adp = of_ad(s_vol, s_path, &ad);
1295 if (ad_open(adp, s_path->u_name, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | ADFLAGS_RDONLY | ADFLAGS_SETSHRMD) < 0) {
1296 return AFPERR_DENYCONF;
1298 #ifdef HAVE_FSHARE_T
1300 shmd.f_access = F_RDACC;
1301 shmd.f_deny = F_NODNY;
1302 if (fcntl(ad_data_fileno(adp), F_SHARE, &shmd) != 0) {
1303 retvalue = AFPERR_DENYCONF;
1306 if (AD_RSRC_OPEN(adp) && fcntl(ad_reso_fileno(adp), F_SHARE, &shmd) != 0) {
1307 retvalue = AFPERR_DENYCONF;
1311 denyreadset = (ad_testlock(adp, ADEID_DFORK, AD_FILELOCK_DENY_RD) != 0 ||
1312 ad_testlock(adp, ADEID_RFORK, AD_FILELOCK_DENY_RD) != 0 );
1315 retvalue = AFPERR_DENYCONF;
1319 newname = obj->newtmp;
1320 strcpy( newname, s_path->m_name );
1322 p = ctoupath( s_vol, curdir, newname );
1324 retvalue = AFPERR_PARAM;
1328 if (NULL == ( d_vol = getvolbyvid( dvid )) ) {
1329 retvalue = AFPERR_PARAM;
1333 if (d_vol->v_flags & AFPVOL_RO) {
1334 retvalue = AFPERR_VLOCK;
1338 if (NULL == ( dir = dirlookup( d_vol, ddid )) ) {
1339 retvalue = afp_errno;
1343 if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) {
1344 retvalue = get_afp_errno(AFPERR_NOOBJ);
1348 if ( *s_path->m_name != '\0' ) {
1349 retvalue =path_error(s_path, AFPERR_NOOBJ);
1353 /* one of the handful of places that knows about the path type */
1354 if (copy_path_name(d_vol, newname, ibuf) < 0) {
1355 retvalue = AFPERR_PARAM;
1358 /* newname is always only a filename so curdir *is* its
1361 if (NULL == (upath = mtoupath(d_vol, newname, curdir->d_did, utf8_encoding(d_vol->v_obj)))) {
1362 retvalue =AFPERR_PARAM;
1366 if ( (err = copyfile(s_vol, d_vol, curdir, -1, p, upath , newname, adp)) < 0 ) {
1372 setvoltime(obj, d_vol );
1375 ad_close( adp, ADFLAGS_DF |ADFLAGS_HF | ADFLAGS_SETSHRMD);
1379 /* ----------------------------------
1380 * if newname is NULL (from directory.c) we don't want to copy the resource fork.
1381 * because we are doing it elsewhere.
1382 * currently if newname is NULL then adp is NULL.
1384 int copyfile(struct vol *s_vol,
1391 struct adouble *adp)
1393 struct adouble ads, add;
1400 LOG(log_debug, logtype_afpd, "copyfile(sfd:%d,s:'%s',d:'%s',n:'%s')",
1401 sfd, src, dst, newname);
1404 ad_init(&ads, s_vol);
1408 adflags = ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | ADFLAGS_RF | ADFLAGS_NORF;
1410 if (ad_openat(adp, sfd, src, adflags | ADFLAGS_RDONLY) < 0) {
1415 if (!AD_META_OPEN(adp))
1416 /* no resource fork, don't create one for dst file */
1417 adflags &= ~ADFLAGS_HF;
1419 if (!AD_RSRC_OPEN(adp))
1420 /* no resource fork, don't create one for dst file */
1421 adflags &= ~ADFLAGS_RF;
1423 stat_result = fstat(ad_data_fileno(adp), &st); /* saving stat exit code, thus saving us on one more stat later on */
1425 if (stat_result < 0) {
1426 /* unlikely but if fstat fails, the default file mode will be 0666. */
1427 st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
1430 ad_init(&add, d_vol);
1431 if (ad_open(&add, dst, adflags | ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_EXCL, st.st_mode | S_IRUSR | S_IWUSR) < 0) {
1433 ad_close( adp, adflags );
1434 if (EEXIST != ret_err) {
1435 deletefile(d_vol, -1, dst, 0);
1438 return AFPERR_EXIST;
1441 if ((err = copy_fork(ADEID_DFORK, &add, adp)) != 0)
1442 LOG(log_error, logtype_afpd, "copyfile('%s'): %s", src, strerror(errno));
1445 if ((err = d_vol->vfs->vfs_copyfile(d_vol, sfd, src, dst)) != 0)
1446 LOG(log_error, logtype_afpd, "copyfile('%s'): %s", src, strerror(errno));
1451 if (AD_META_OPEN(&add)) {
1452 if (AD_META_OPEN(adp))
1453 ad_copy_header(&add, adp);
1454 ad_setname(&add, dst);
1457 if (fstat(ad_meta_fileno(&add), &stdest) != 0) {
1461 if ((id = get_id(d_vol, &add, &stdest, d_dir->d_did, dst, strlen(dst))) == CNID_INVALID) {
1465 (void)ad_setid(&add, stdest.st_dev, stdest.st_ino, id, d_dir->d_did, d_vol->v_stamp);
1470 ad_close( adp, adflags );
1472 if (ad_close( &add, adflags ) <0) {
1477 deletefile(d_vol, -1, dst, 0);
1479 else if (stat_result == 0) {
1480 /* set dest modification date to src date */
1483 ut.actime = ut.modtime = st.st_mtime;
1485 /* FIXME netatalk doesn't use resource fork file date
1486 * but maybe we should set its modtime too.
1491 switch ( ret_err ) {
1497 LOG(log_info, logtype_afpd, "copyfile: DISK FULL");
1498 return AFPERR_DFULL;
1500 return AFPERR_NOOBJ;
1502 return AFPERR_ACCESS;
1504 return AFPERR_VLOCK;
1506 return AFPERR_PARAM;
1510 /* -----------------------------------
1511 vol: not NULL delete cnid entry. then we are in curdir and file is a only filename
1512 checkAttrib: 1 check kFPDeleteInhibitBit (deletfile called by afp_delete)
1514 when deletefile is called we don't have lock on it, file is closed (for us)
1515 untrue if called by renamefile
1517 ad_open always try to open file RDWR first and ad_lock takes care of
1518 WRITE lock on read only file.
1521 static int check_attrib(struct adouble *adp)
1523 uint16_t bshort = 0;
1525 ad_getattr(adp, &bshort);
1527 * Does kFPDeleteInhibitBit (bit 8) set?
1529 if ((bshort & htons(ATTRBIT_NODELETE))) {
1530 return AFPERR_OLOCK;
1532 if ((bshort & htons(ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
1538 * dirfd can be used for unlinkat semantics
1540 int deletefile(const struct vol *vol, int dirfd, char *file, int checkAttrib)
1543 struct adouble *adp = NULL;
1544 int adflags, err = AFP_OK;
1547 LOG(log_debug, logtype_afpd, "deletefile('%s')", file);
1551 /* was EACCESS error try to get only metadata */
1552 /* we never want to create a resource fork here, we are going to delete it
1553 * moreover sometimes deletefile is called with a no existent file and
1554 * ad_open would create a 0 byte resource fork
1556 if ( ad_metadataat(dirfd, file, ADFLAGS_CHECK_OF, &ad) == 0 ) {
1557 if ((err = check_attrib(&ad))) {
1558 ad_close(&ad, ADFLAGS_HF | ADFLAGS_CHECK_OF);
1565 /* try to open both forks at once */
1566 adflags = ADFLAGS_DF;
1567 if (ad_openat(&ad, dirfd, file, adflags | ADFLAGS_RF | ADFLAGS_NORF | ADFLAGS_RDONLY) < 0 ) {
1572 case EACCES: /* maybe it's a file with no write mode for us */
1573 break; /* was return AFPERR_ACCESS;*/
1586 if ( adp && AD_RSRC_OPEN(adp) ) { /* there's a resource fork */
1587 adflags |= ADFLAGS_RF;
1588 /* FIXME we have a pb here because we want to know if a file is open
1589 * there's a 'priority inversion' if you can't open the ressource fork RW
1590 * you can delete it if it's open because you can't get a write lock.
1592 * ADLOCK_FILELOCK means the whole ressource fork, not only after the
1595 * FIXME it doesn't work for RFORK open read only and fork open without deny mode
1597 if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
1603 if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
1604 LOG(log_error, logtype_afpd, "deletefile('%s'): ad_tmplock error: %s", file, strerror(errno));
1606 } else if (!(err = vol->vfs->vfs_deletefile(vol, dirfd, file)) && !(err = netatalk_unlinkat(dirfd, file )) ) {
1609 AFP_CNID_START("cnid_get");
1610 id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file));
1613 AFP_CNID_START("cnid_delete");
1614 cnid_delete(vol->v_cdb, id);
1622 ad_close(&ad, ADFLAGS_HF | ADFLAGS_CHECK_OF);
1625 ad_close( &ad, adflags ); /* ad_close removes locks if any */
1630 /* ------------------------------------ */
1631 /* return a file id */
1632 int afp_createid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1641 struct path *s_path;
1647 memcpy(&vid, ibuf, sizeof(vid));
1648 ibuf += sizeof(vid);
1650 if (NULL == ( vol = getvolbyvid( vid )) ) {
1651 return( AFPERR_PARAM);
1654 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1658 if (vol->v_flags & AFPVOL_RO)
1659 return AFPERR_VLOCK;
1661 memcpy(&did, ibuf, sizeof( did ));
1662 ibuf += sizeof(did);
1664 if (NULL == ( dir = dirlookup( vol, did )) ) {
1665 return afp_errno; /* was AFPERR_PARAM */
1668 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
1669 return get_afp_errno(AFPERR_NOOBJ); /* was AFPERR_PARAM */
1672 if ( path_isadir(s_path) ) {
1673 return( AFPERR_BADTYPE );
1676 upath = s_path->u_name;
1677 switch (s_path->st_errno) {
1679 break; /* success */
1682 return AFPERR_ACCESS;
1684 return AFPERR_NOOBJ;
1686 return AFPERR_PARAM;
1689 AFP_CNID_START("cnid_lookup");
1690 id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath));
1693 memcpy(rbuf, &id, sizeof(id));
1694 *rbuflen = sizeof(id);
1695 return AFPERR_EXISTID;
1698 if ((id = get_id(vol, NULL, st, did, upath, len)) != CNID_INVALID) {
1699 memcpy(rbuf, &id, sizeof(id));
1700 *rbuflen = sizeof(id);
1707 /* ------------------------------- */
1713 static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
1716 struct reenum *param = data;
1717 struct vol *vol = param->vol;
1718 cnid_t did = param->did;
1721 if (ostat(de->d_name, &path.st, vol_syml_opt(vol)) < 0)
1724 /* update or add to cnid */
1725 AFP_CNID_START("cnid_add");
1726 aint = cnid_add(vol->v_cdb, &path.st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */
1732 /* --------------------
1733 * Ok the db is out of synch with the dir.
1734 * but if it's a deleted file we don't want to do it again and again.
1737 reenumerate_id(struct vol *vol, char *name, struct dir *dir)
1743 if (vol->v_cdb == NULL) {
1747 /* FIXME use of_statdir ? */
1748 if (ostat(name, &st, vol_syml_opt(vol))) {
1752 if (dirreenumerate(dir, &st)) {
1753 /* we already did it once and the dir haven't been modified */
1754 return dir->d_offcnt;
1758 data.did = dir->d_did;
1759 if ((ret = for_each_dirent(vol, name, reenumerate_loop, (void *)&data)) >= 0) {
1760 setdiroffcnt(curdir, &st, ret);
1761 dir->d_flags |= DIRF_CNID;
1767 /* ------------------------------
1768 resolve a file id */
1769 int afp_resolveid(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1778 uint16_t vid, bitmap;
1780 static char buffer[12 + MAXPATHLEN + 1];
1781 int len = 12 + MAXPATHLEN + 1;
1786 memcpy(&vid, ibuf, sizeof(vid));
1787 ibuf += sizeof(vid);
1789 if (NULL == ( vol = getvolbyvid( vid )) ) {
1790 return( AFPERR_PARAM);
1793 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1797 memcpy(&id, ibuf, sizeof( id ));
1802 /* some MacOS versions after a catsearch do a *lot* of afp_resolveid with 0 */
1806 AFP_CNID_START("cnid_resolve");
1807 upath = cnid_resolve(vol->v_cdb, &id, buffer, len);
1809 if (upath == NULL) {
1810 return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
1813 if (NULL == ( dir = dirlookup( vol, id )) ) {
1814 return AFPERR_NOID; /* idem AFPERR_PARAM */
1816 if (movecwd(vol, dir) < 0) {
1820 return AFPERR_ACCESS;
1824 return AFPERR_PARAM;
1828 memset(&path, 0, sizeof(path));
1829 path.u_name = upath;
1830 if (of_stat(vol, &path) < 0 ) {
1832 /* with nfs and our working directory is deleted */
1833 if (errno == ESTALE) {
1837 if ( errno == ENOENT && !retry) {
1838 /* cnid db is out of sync, reenumerate the directory and update ids */
1839 reenumerate_id(vol, ".", dir);
1847 return AFPERR_ACCESS;
1851 return AFPERR_PARAM;
1855 /* directories are bad */
1856 if (S_ISDIR(path.st.st_mode)) {
1857 /* OS9 and OSX don't return the same error code */
1858 return (obj->afp_version >=30)?AFPERR_NOID:AFPERR_BADTYPE;
1861 memcpy(&bitmap, ibuf, sizeof(bitmap));
1862 bitmap = ntohs( bitmap );
1863 if (NULL == (path.m_name = utompath(vol, upath, cnid, utf8_encoding(obj)))) {
1867 if (AFP_OK != (err = getfilparams(obj, vol, bitmap, &path , curdir,
1868 rbuf + sizeof(bitmap), &buflen, 0))) {
1871 *rbuflen = buflen + sizeof(bitmap);
1872 memcpy(rbuf, ibuf, sizeof(bitmap));
1877 /* ------------------------------ */
1878 int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1888 static char buffer[12 + MAXPATHLEN + 1];
1889 int len = 12 + MAXPATHLEN + 1;
1894 memcpy(&vid, ibuf, sizeof(vid));
1895 ibuf += sizeof(vid);
1897 if (NULL == ( vol = getvolbyvid( vid )) ) {
1898 return( AFPERR_PARAM);
1901 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1905 if (vol->v_flags & AFPVOL_RO)
1906 return AFPERR_VLOCK;
1908 memcpy(&id, ibuf, sizeof( id ));
1912 AFP_CNID_START("cnid_resolve");
1913 upath = cnid_resolve(vol->v_cdb, &id, buffer, len);
1915 if (upath == NULL) {
1919 if (NULL == ( dir = dirlookup( vol, id )) ) {
1920 if (afp_errno == AFPERR_NOOBJ) {
1924 return( AFPERR_PARAM );
1928 if ((movecwd(vol, dir) < 0) || (ostat(upath, &st, vol_syml_opt(vol)) < 0)) {
1932 return AFPERR_ACCESS;
1937 /* still try to delete the id */
1941 return AFPERR_PARAM;
1944 else if (S_ISDIR(st.st_mode)) /* directories are bad */
1945 return AFPERR_BADTYPE;
1948 AFP_CNID_START("cnid_delete");
1949 if (cnid_delete(vol->v_cdb, fileid)) {
1953 return AFPERR_VLOCK;
1956 return AFPERR_ACCESS;
1958 return AFPERR_PARAM;
1965 /* ------------------------------ */
1966 static struct adouble *find_adouble(const AFPObj *obj, struct vol *vol, struct path *path, struct ofork **of, struct adouble *adp)
1970 if (path->st_errno) {
1971 switch (path->st_errno) {
1973 afp_errno = AFPERR_NOID;
1977 afp_errno = AFPERR_ACCESS;
1980 afp_errno = AFPERR_PARAM;
1985 /* we use file_access both for legacy Mac perm and
1986 * for unix privilege, rename will take care of folder perms
1988 if (file_access(obj, vol, path, OPENACC_WR ) < 0) {
1989 afp_errno = AFPERR_ACCESS;
1993 if ((*of = of_findname(vol, path))) {
1994 /* reuse struct adouble so it won't break locks */
1998 ret = ad_open(adp, path->u_name, ADFLAGS_HF | ADFLAGS_RDWR);
2000 if ( !ret && ad_reso_fileno(adp) != -1 && !(adp->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
2002 * The user must have the Read & Write privilege for both files in order to use this command.
2004 ad_close(adp, ADFLAGS_HF);
2005 afp_errno = AFPERR_ACCESS;
2012 #define APPLETEMP ".AppleTempXXXXXX"
2014 int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
2016 struct stat srcst, destst;
2018 struct dir *dir, *sdir;
2019 char *spath, temp[17], *p;
2020 char *supath, *upath;
2025 struct adouble *adsp = NULL;
2026 struct adouble *addp = NULL;
2027 struct ofork *s_of = NULL;
2028 struct ofork *d_of = NULL;
2038 memcpy(&vid, ibuf, sizeof(vid));
2039 ibuf += sizeof(vid);
2041 if (NULL == ( vol = getvolbyvid( vid )) ) {
2042 return( AFPERR_PARAM);
2045 if ((vol->v_flags & AFPVOL_RO))
2046 return AFPERR_VLOCK;
2048 /* source and destination dids */
2049 memcpy(&sid, ibuf, sizeof(sid));
2050 ibuf += sizeof(sid);
2051 memcpy(&did, ibuf, sizeof(did));
2052 ibuf += sizeof(did);
2055 if (NULL == (dir = dirlookup( vol, sid )) ) {
2056 return afp_errno; /* was AFPERR_PARAM */
2059 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2060 return get_afp_errno(AFPERR_NOOBJ);
2063 if ( path_isadir(path) ) {
2064 return AFPERR_BADTYPE; /* it's a dir */
2067 /* save some stuff */
2070 spath = obj->oldtmp;
2071 supath = obj->newtmp;
2072 strcpy(spath, path->m_name);
2073 strcpy(supath, path->u_name); /* this is for the cnid changing */
2074 p = absupath( vol, sdir, supath);
2076 /* pathname too long */
2077 return AFPERR_PARAM ;
2081 if (!(adsp = find_adouble(obj, vol, path, &s_of, &ads))) {
2085 /* ***** from here we may have resource fork open **** */
2087 /* look for the source cnid. if it doesn't exist, don't worry about
2089 AFP_CNID_START("cnid_lookup");
2090 sid = cnid_lookup(vol->v_cdb, &srcst, sdir->d_did, supath,slen = strlen(supath));
2093 if (NULL == ( dir = dirlookup( vol, did )) ) {
2094 err = afp_errno; /* was AFPERR_PARAM */
2095 goto err_exchangefile;
2098 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2099 err = get_afp_errno(AFPERR_NOOBJ);
2100 goto err_exchangefile;
2103 if ( path_isadir(path) ) {
2104 err = AFPERR_BADTYPE;
2105 goto err_exchangefile;
2108 /* FPExchangeFiles is the only call that can return the SameObj
2110 if ((curdir == sdir) && strcmp(spath, path->m_name) == 0) {
2111 err = AFPERR_SAMEOBJ;
2112 goto err_exchangefile;
2116 if (!(addp = find_adouble(obj, vol, path, &d_of, &add))) {
2118 goto err_exchangefile;
2122 /* they are not on the same device and at least one is open
2123 * FIXME broken for for crossdev and adouble v2
2126 crossdev = (srcst.st_dev != destst.st_dev);
2127 if (/* (d_of || s_of) && */ crossdev) {
2129 goto err_exchangefile;
2132 /* look for destination id. */
2133 upath = path->u_name;
2134 AFP_CNID_START("cnid_lookup");
2135 did = cnid_lookup(vol->v_cdb, &destst, curdir->d_did, upath, dlen = strlen(upath));
2138 /* construct a temp name.
2139 * NOTE: the temp file will be in the dest file's directory. it
2140 * will also be inaccessible from AFP. */
2141 memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
2143 if ((fd = mkstemp(temp)) == -1) {
2145 goto err_exchangefile;
2150 /* FIXME we need to close fork for copy, both s_of and d_of are null */
2151 ad_close(adsp, ADFLAGS_HF);
2152 ad_close(addp, ADFLAGS_HF);
2155 /* now, quickly rename the file. we error if we can't. */
2156 if ((err = renamefile(vol, curdir, -1, p, temp, temp, adsp)) != AFP_OK)
2157 goto err_exchangefile;
2158 of_rename(vol, s_of, sdir, spath, curdir, temp);
2160 /* rename destination to source */
2161 if ((err = renamefile(vol, curdir, -1, upath, p, spath, addp)) != AFP_OK)
2162 goto err_src_to_tmp;
2163 of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
2165 /* rename temp to destination */
2166 if ((err = renamefile(vol, curdir, -1, temp, upath, path->m_name, adsp)) != AFP_OK)
2167 goto err_dest_to_src;
2168 of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
2171 * id's need switching. src -> dest and dest -> src.
2172 * we need to re-stat() if it was a cross device copy.
2175 AFP_CNID_START("cnid_delete");
2176 cnid_delete(vol->v_cdb, sid);
2180 AFP_CNID_START("cnid_delete");
2181 cnid_delete(vol->v_cdb, did);
2185 if ((did && ( (crossdev && ostat(upath, &srcst, vol_syml_opt(vol)) < 0) ||
2186 cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
2188 (sid && ( (crossdev && ostat(p, &destst, vol_syml_opt(vol)) < 0) ||
2189 cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
2194 err = AFPERR_ACCESS;
2199 goto err_temp_to_dest;
2202 /* here we need to reopen if crossdev */
2203 if (sid && ad_setid(addp, destst.st_dev, destst.st_ino, sid, sdir->d_did, vol->v_stamp))
2208 if (did && ad_setid(adsp, srcst.st_dev, srcst.st_ino, did, curdir->d_did, vol->v_stamp))
2213 /* change perms, src gets dest perm and vice versa */
2218 * we need to exchange ACL entries as well
2220 /* exchange_acls(vol, p, upath); */
2225 path->m_name = NULL;
2226 path->u_name = upath;
2228 setfilunixmode(vol, path, destst.st_mode);
2229 setfilowner(vol, destst.st_uid, destst.st_gid, path);
2236 setfilunixmode(vol, path, srcst.st_mode);
2237 setfilowner(vol, srcst.st_uid, srcst.st_gid, path);
2242 goto err_exchangefile;
2244 /* all this stuff is so that we can unwind a failed operation
2247 /* rename dest to temp */
2248 renamefile(vol, curdir, -1, upath, temp, temp, adsp);
2249 of_rename(vol, s_of, curdir, upath, curdir, temp);
2252 /* rename source back to dest */
2253 renamefile(vol, curdir, -1, p, upath, path->m_name, addp);
2254 of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
2257 /* rename temp back to source */
2258 renamefile(vol, curdir, -1, temp, p, spath, adsp);
2259 of_rename(vol, s_of, curdir, temp, sdir, spath);
2262 if ( !s_of && adsp && ad_meta_fileno(adsp) != -1 ) { /* META */
2263 ad_close(adsp, ADFLAGS_HF);
2265 if ( !d_of && addp && ad_meta_fileno(addp) != -1 ) {/* META */
2266 ad_close(addp, ADFLAGS_HF);
2270 if ((cached = dircache_search_by_did(vol, sid)) != NULL)
2271 (void)dir_remove(vol, cached);
2272 if ((cached = dircache_search_by_did(vol, did)) != NULL)
2273 (void)dir_remove(vol, cached);