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 dbcnid = cnid_add(vol->v_cdb, st, did, upath, len, adcnid); /* (2) */
224 /* Throw errors if cnid_add fails. */
225 if (dbcnid == CNID_INVALID) {
227 case CNID_ERR_CLOSE: /* the db is closed */
230 LOG(log_error, logtype_afpd, "get_id: Incorrect parameters passed to cnid_add");
231 afp_errno = AFPERR_PARAM;
234 afp_errno = AFPERR_PARAM;
237 /* Close CNID backend if "dbd" and switch to temp in-memory "tdb" */
238 /* we have to do it here for "dbd" because it uses "lazy opening" */
239 /* In order to not end in a loop somehow with goto restart below */
241 if (first && (strcmp(vol->v_cnidscheme, "dbd") == 0)) { /* (3) */
242 cnid_close(vol->v_cdb);
243 free(vol->v_cnidscheme);
244 vol->v_cnidscheme = strdup("tdb");
246 int flags = CNID_FLAG_MEMORY;
247 if ((vol->v_flags & AFPVOL_NODEV)) {
248 flags |= CNID_FLAG_NODEV;
250 LOG(log_error, logtype_afpd, "Reopen volume %s using in memory temporary CNID DB.",
252 vol->v_cdb = cnid_open(vol->v_path, vol->v_umask, "tdb", flags, NULL, NULL);
254 if (!(vol->v_flags & AFPVOL_TM)) {
255 vol->v_flags |= AFPVOL_RO;
256 setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB instead."
257 "Check server messages for details. Switching to read-only mode.");
258 kill(getpid(), SIGUSR2);
260 goto restart; /* now try again with the temp CNID db */
262 setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB failed too!"
263 "Check server messages for details, can't recover from this state!");
266 afp_errno = AFPERR_MISC;
270 else if (adp && adcnid && (adcnid != dbcnid)) { /* 4 */
271 /* Update the ressource fork. For a folder adp is always null */
272 LOG(log_debug, logtype_afpd, "get_id(%s/%s): calling ad_setid(old: %u, new: %u)",
273 getcwdpath(), upath, htonl(adcnid), htonl(dbcnid));
274 if (ad_setid(adp, st->st_dev, st->st_ino, dbcnid, did, vol->v_stamp)) {
275 if (ad_flush(adp) != 0)
276 LOG(log_error, logtype_afpd, "get_id(\"%s\"): can't flush", fullpathname(upath));
286 /* -------------------------- */
287 int getmetadata(const AFPObj *obj,
290 struct path *path, struct dir *dir,
291 char *buf, size_t *buflen, struct adouble *adp)
293 char *data, *l_nameoff = NULL, *upath;
294 char *utf_nameoff = NULL;
299 u_char achar, fdType[4];
304 LOG(log_debug, logtype_afpd, "getmetadata(\"%s\")", path->u_name);
306 upath = path->u_name;
310 if ( ((bitmap & ( (1 << FILPBIT_FINFO)|(1 << FILPBIT_LNAME)|(1 <<FILPBIT_PDINFO) ) ) && !path->m_name)
311 || (bitmap & ( (1 << FILPBIT_LNAME) ) && utf8_encoding(obj)) /* FIXME should be m_name utf8 filename */
312 || (bitmap & (1 << FILPBIT_FNUM))) {
315 struct dir *cachedfile;
316 int len = strlen(upath);
317 if ((cachedfile = dircache_search_by_name(vol, dir, upath, len)) != NULL)
318 id = cachedfile->d_did;
320 id = get_id(vol, adp, st, dir->d_did, upath, len);
322 /* Add it to the cache */
323 LOG(log_debug, logtype_afpd, "getmetadata: caching: did:%u, \"%s\", cnid:%u",
324 ntohl(dir->d_did), upath, ntohl(id));
326 /* Get macname from unixname first */
327 if (path->m_name == NULL) {
328 if ((path->m_name = utompath(vol, upath, id, utf8_encoding(obj))) == NULL) {
329 LOG(log_error, logtype_afpd, "getmetadata: utompath error");
335 if (((fullpath = bstrcpy(dir->d_fullpath)) == NULL)
336 || (bconchar(fullpath, '/') != BSTR_OK)
337 || (bcatcstr(fullpath, upath)) != BSTR_OK) {
338 LOG(log_error, logtype_afpd, "getmetadata: fullpath: %s", strerror(errno));
343 if ((cachedfile = dir_new(path->m_name, upath, vol, dir->d_did, id, fullpath, st)) == NULL) {
344 LOG(log_error, logtype_afpd, "getmetadata: error from dir_new");
348 if ((dircache_add(vol, cachedfile)) != 0) {
349 LOG(log_error, logtype_afpd, "getmetadata: fatal dircache error");
357 if (id == CNID_INVALID)
361 path->m_name = utompath(vol, upath, id, utf8_encoding(vol->v_obj));
364 while ( bitmap != 0 ) {
365 while (( bitmap & 1 ) == 0 ) {
373 ad_getattr(adp, &ashort);
374 } else if (vol_inv_dots(vol) && *upath == '.') {
375 ashort = htons(ATTRBIT_INVISIBLE);
379 /* FIXME do we want a visual clue if the file is read only
382 accessmode(vol, ".", &ma, dir , NULL);
383 if ((ma.ma_user & AR_UWRITE)) {
384 accessmode(vol, upath, &ma, dir , st);
385 if (!(ma.ma_user & AR_UWRITE)) {
386 ashort |= htons(ATTRBIT_NOWRITE);
390 memcpy(data, &ashort, sizeof( ashort ));
391 data += sizeof( ashort );
392 LOG(log_debug, logtype_afpd, "metadata('%s'): AFP Attributes: %04x",
393 path->u_name, ntohs(ashort));
397 memcpy(data, &dir->d_did, sizeof( uint32_t ));
398 data += sizeof( uint32_t );
399 LOG(log_debug, logtype_afpd, "metadata('%s'): Parent DID: %u",
400 path->u_name, ntohl(dir->d_did));
404 if (!adp || (ad_getdate(adp, AD_DATE_CREATE, &aint) < 0))
405 aint = AD_DATE_FROM_UNIX(st->st_mtime);
406 memcpy(data, &aint, sizeof( aint ));
407 data += sizeof( aint );
411 if ( adp && (ad_getdate(adp, AD_DATE_MODIFY, &aint) == 0)) {
412 if ((st->st_mtime > AD_DATE_TO_UNIX(aint))) {
413 aint = AD_DATE_FROM_UNIX(st->st_mtime);
416 aint = AD_DATE_FROM_UNIX(st->st_mtime);
418 memcpy(data, &aint, sizeof( int ));
419 data += sizeof( int );
423 if (!adp || (ad_getdate(adp, AD_DATE_BACKUP, &aint) < 0))
424 aint = AD_DATE_START;
425 memcpy(data, &aint, sizeof( int ));
426 data += sizeof( int );
430 get_finderinfo(vol, upath, adp, (char *)data,S_ISLNK(st->st_mode));
431 data += ADEDLEN_FINDERI;
436 data += sizeof( uint16_t );
440 memset(data, 0, sizeof(uint16_t));
441 data += sizeof( uint16_t );
445 memcpy(data, &id, sizeof( id ));
446 data += sizeof( id );
447 LOG(log_debug, logtype_afpd, "metadata('%s'): CNID: %u",
448 path->u_name, ntohl(id));
452 if (st->st_size > 0xffffffff)
455 aint = htonl( st->st_size );
456 memcpy(data, &aint, sizeof( aint ));
457 data += sizeof( aint );
462 if (adp->ad_rlen > 0xffffffff)
465 aint = htonl( adp->ad_rlen);
469 memcpy(data, &aint, sizeof( aint ));
470 data += sizeof( aint );
473 /* Current client needs ProDOS info block for this file.
474 Use simple heuristic and let the Mac "type" string tell
475 us what the PD file code should be. Everything gets a
476 subtype of 0x0000 unless the original value was hashed
477 to "pXYZ" when we created it. See IA, Ver 2.
478 <shirsch@adelphia.net> */
479 case FILPBIT_PDINFO :
480 if (obj->afp_version >= 30) { /* UTF8 name */
481 utf8 = kTextEncodingUTF8;
483 data += sizeof( uint16_t );
485 memcpy(data, &aint, sizeof( aint ));
486 data += sizeof( aint );
490 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
492 if ( memcmp( fdType, "TEXT", 4 ) == 0 ) {
496 else if ( memcmp( fdType, "PSYS", 4 ) == 0 ) {
500 else if ( memcmp( fdType, "PS16", 4 ) == 0 ) {
504 else if ( memcmp( fdType, "BINA", 4 ) == 0 ) {
508 else if ( fdType[0] == 'p' ) {
510 ashort = (fdType[2] * 256) + fdType[3];
524 memcpy(data, &ashort, sizeof( ashort ));
525 data += sizeof( ashort );
526 memset(data, 0, sizeof( ashort ));
527 data += sizeof( ashort );
530 case FILPBIT_EXTDFLEN:
531 aint = htonl(st->st_size >> 32);
532 memcpy(data, &aint, sizeof( aint ));
533 data += sizeof( aint );
534 aint = htonl(st->st_size);
535 memcpy(data, &aint, sizeof( aint ));
536 data += sizeof( aint );
538 case FILPBIT_EXTRFLEN:
541 aint = htonl(adp->ad_rlen >> 32);
542 memcpy(data, &aint, sizeof( aint ));
543 data += sizeof( aint );
545 aint = htonl(adp->ad_rlen);
546 memcpy(data, &aint, sizeof( aint ));
547 data += sizeof( aint );
549 case FILPBIT_UNIXPR :
550 /* accessmode may change st_mode with ACLs */
551 accessmode(obj, vol, upath, &ma, dir , st);
553 aint = htonl(st->st_uid);
554 memcpy( data, &aint, sizeof( aint ));
555 data += sizeof( aint );
556 aint = htonl(st->st_gid);
557 memcpy( data, &aint, sizeof( aint ));
558 data += sizeof( aint );
561 type == slnk indicates an OSX style symlink,
562 we have to add S_IFLNK to the mode, otherwise
563 10.3 clients freak out. */
567 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
568 if ( memcmp( fdType, "slnk", 4 ) == 0 ) {
574 memcpy( data, &aint, sizeof( aint ));
575 data += sizeof( aint );
577 *data++ = ma.ma_user;
578 *data++ = ma.ma_world;
579 *data++ = ma.ma_group;
580 *data++ = ma.ma_owner;
584 return( AFPERR_BITMAP );
590 ashort = htons( data - buf );
591 memcpy(l_nameoff, &ashort, sizeof( ashort ));
592 data = set_name(vol, data, dir->d_did, path->m_name, id, 0);
595 ashort = htons( data - buf );
596 memcpy(utf_nameoff, &ashort, sizeof( ashort ));
597 data = set_name(vol, data, dir->d_did, path->m_name, id, utf8);
599 *buflen = data - buf;
603 /* ----------------------- */
604 int getfilparams(const AFPObj *obj, struct vol *vol, uint16_t bitmap, struct path *path,
605 struct dir *dir, char *buf, size_t *buflen, int in_enumerate)
607 struct adouble ad, *adp;
610 int flags; /* uninitialized ok */
612 LOG(log_debug, logtype_afpd, "getfilparams(\"%s\")", path->u_name);
614 opened = PARAM_NEED_ADP(bitmap);
620 * Dont check for and resturn open fork attributes when enumerating
621 * This saves a lot of syscalls, the client will hopefully only use the result
622 * in FPGetFileParms where we return the correct value
624 flags = (!in_enumerate &&(bitmap & (1 << FILPBIT_ATTR))) ? ADFLAGS_CHECK_OF : 0;
626 adp = of_ad(vol, path, &ad);
627 upath = path->u_name;
629 if ( ad_metadata( upath, flags, adp) < 0 ) {
632 LOG(log_error, logtype_afpd, "getfilparams(%s): %s: check resource fork permission?",
633 upath, strerror(errno));
634 return AFPERR_ACCESS;
636 LOG(log_error, logtype_afpd, "getfilparams(%s): bad resource fork", upath);
645 rc = getmetadata(obj, vol, bitmap, path, dir, buf, buflen, adp);
648 ad_close(adp, ADFLAGS_HF | flags);
653 /* ----------------------------- */
654 int afp_createfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
659 struct ofork *of = NULL;
661 int creatf, did, openf, retvalue = AFP_OK;
667 creatf = (unsigned char) *ibuf++;
669 memcpy(&vid, ibuf, sizeof( vid ));
670 ibuf += sizeof( vid );
672 if (NULL == ( vol = getvolbyvid( vid )) )
673 return( AFPERR_PARAM );
675 if (vol->v_flags & AFPVOL_RO)
678 memcpy(&did, ibuf, sizeof( did));
679 ibuf += sizeof( did );
681 if (NULL == ( dir = dirlookup( vol, did )) )
684 if (NULL == ( s_path = cname( vol, dir, &ibuf )) )
685 return get_afp_errno(AFPERR_PARAM);
686 if ( *s_path->m_name == '\0' )
687 return( AFPERR_BADTYPE );
689 upath = s_path->u_name;
692 /* if upath is deleted we already in trouble anyway */
693 if ((of = of_findname(vol, s_path))) {
701 openf = ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_TRUNC;
703 /* on a soft create, if the file is open then ad_open won't fail
704 because open syscall is not called */
705 openf = ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_EXCL;
707 if (ad_open(&ad, upath, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | openf, 0666) < 0) {
711 case ENOENT : /* we were already in 'did folder' so chdir() didn't fail */
712 return ( AFPERR_NOOBJ );
714 return( AFPERR_EXIST );
716 return( AFPERR_ACCESS );
719 LOG(log_info, logtype_afpd, "afp_createfile: DISK FULL");
720 return( AFPERR_DFULL );
722 return( AFPERR_PARAM );
725 if ( ad_meta_fileno( &ad ) == -1 ) { /* Hard META / HF */
726 /* FIXME with hard create on an existing file, we already
727 * corrupted the data file.
729 netatalk_unlink( upath );
730 ad_close( &ad, ADFLAGS_DF );
731 return AFPERR_ACCESS;
734 path = s_path->m_name;
735 ad_setname(&ad, path);
738 if (lstat(upath, &st) != 0) {
739 LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): stat: %s",
740 upath, strerror(errno));
741 ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF);
746 if ((id = get_id(vol, &ad, &st, dir->d_did, upath, strlen(upath))) == CNID_INVALID) {
747 LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): CNID error", upath);
748 goto createfile_iderr;
750 (void)ad_setid(&ad, st.st_dev, st.st_ino, id, dir->d_did, vol->v_stamp);
754 ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF );
755 fce_register(FCE_FILE_CREATE, fullpathname(upath), NULL, fce_file);
759 setvoltime(obj, vol );
764 int afp_setfilparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
770 uint16_t vid, bitmap;
775 memcpy(&vid, ibuf, sizeof( vid ));
776 ibuf += sizeof( vid );
777 if (NULL == ( vol = getvolbyvid( vid )) ) {
778 return( AFPERR_PARAM );
781 if (vol->v_flags & AFPVOL_RO)
784 memcpy(&did, ibuf, sizeof( did ));
785 ibuf += sizeof( did );
786 if (NULL == ( dir = dirlookup( vol, did )) ) {
787 return afp_errno; /* was AFPERR_NOOBJ */
790 memcpy(&bitmap, ibuf, sizeof( bitmap ));
791 bitmap = ntohs( bitmap );
792 ibuf += sizeof( bitmap );
794 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
795 return get_afp_errno(AFPERR_PARAM);
798 if (path_isadir(s_path)) {
799 return( AFPERR_BADTYPE ); /* it's a directory */
802 if ( s_path->st_errno != 0 ) {
803 return( AFPERR_NOOBJ );
806 if ((u_long)ibuf & 1 ) {
810 if (AFP_OK == ( rc = setfilparams(obj, vol, s_path, bitmap, ibuf )) ) {
811 setvoltime(obj, vol );
818 * cf AFP3.0.pdf page 252 for change_mdate and change_parent_mdate logic
821 extern struct path Cur_Path;
823 int setfilparams(const AFPObj *obj, struct vol *vol,
824 struct path *path, uint16_t f_bitmap, char *buf )
826 struct adouble ad, *adp;
828 int bit, isad = 1, err = AFP_OK;
830 u_char achar, *fdType, xyy[4]; /* uninitialized, OK 310105 */
831 uint16_t ashort, bshort, oshort;
834 uint16_t upriv_bit = 0;
836 int change_mdate = 0;
837 int change_parent_mdate = 0;
842 uint16_t bitmap = f_bitmap;
843 uint32_t cdate,bdate;
844 u_char finder_buf[32];
845 int symlinked = S_ISLNK(path->st.st_mode);
848 char symbuf[MAXPATHLEN+1];
851 LOG(log_debug9, logtype_afpd, "begin setfilparams:");
854 adp = of_ad(vol, path, &ad);
855 upath = path->u_name;
857 if (!vol_unix_priv(vol) && check_access(obj, vol, upath, OPENACC_WR ) < 0) {
858 return AFPERR_ACCESS;
861 /* with unix priv maybe we have to change adouble file priv first */
863 while ( bitmap != 0 ) {
864 while (( bitmap & 1 ) == 0 ) {
871 memcpy(&ashort, buf, sizeof( ashort ));
872 buf += sizeof( ashort );
876 memcpy(&cdate, buf, sizeof(cdate));
877 buf += sizeof( cdate );
880 memcpy(&newdate, buf, sizeof( newdate ));
881 buf += sizeof( newdate );
885 memcpy(&bdate, buf, sizeof( bdate));
886 buf += sizeof( bdate );
890 if (memcmp(buf,"slnkrhap",8) == 0
891 && !(S_ISLNK(path->st.st_mode))
892 && !(vol->v_flags & AFPVOL_FOLLOWSYM)) {
893 /* request to turn this into a symlink */
894 if ((fp = open(path->u_name, O_RDONLY)) == -1) {
896 goto setfilparam_done;
898 len = read(fp, symbuf, MAXPATHLEN);
902 goto setfilparam_done;
904 if (unlink(path->u_name) != 0) {
906 goto setfilparam_done;
909 if (symlink(symbuf, path->u_name) != 0) {
911 goto setfilparam_done;
916 memcpy(finder_buf, buf, 32 );
919 case FILPBIT_UNIXPR :
920 if (!vol_unix_priv(vol)) {
921 /* this volume doesn't use unix priv */
927 change_parent_mdate = 1;
929 memcpy( &aint, buf, sizeof( aint ));
930 f_uid = ntohl (aint);
931 buf += sizeof( aint );
932 memcpy( &aint, buf, sizeof( aint ));
933 f_gid = ntohl (aint);
934 buf += sizeof( aint );
935 setfilowner(vol, f_uid, f_gid, path);
937 memcpy( &upriv, buf, sizeof( upriv ));
938 buf += sizeof( upriv );
939 upriv = ntohl (upriv);
940 if ((upriv & S_IWUSR)) {
941 setfilunixmode(vol, path, upriv);
948 case FILPBIT_PDINFO :
949 if (obj->afp_version < 30) { /* else it's UTF8 name */
952 /* Keep special case to support crlf translations */
953 if ((unsigned int) achar == 0x04) {
954 fdType = (u_char *)"TEXT";
957 xyy[0] = ( u_char ) 'p';
968 /* break while loop */
977 /* second try with adouble open
979 if (ad_open(adp, upath, ADFLAGS_HF | ADFLAGS_RDWR | ADFLAGS_CREATE, 0666) < 0) {
980 LOG(log_debug, logtype_afpd, "setfilparams: ad_open_metadata error");
982 * For some things, we don't need an adouble header:
983 * - change of modification date
984 * - UNIX privs (Bug-ID #2863424)
986 if (!symlinked && f_bitmap & ~(1<<FILPBIT_MDATE | 1<<FILPBIT_UNIXPR)) {
987 LOG(log_debug, logtype_afpd, "setfilparams: need adouble access");
988 return AFPERR_ACCESS;
990 LOG(log_debug, logtype_afpd, "setfilparams: no adouble perms, but only FILPBIT_MDATE and/or FILPBIT_UNIXPR");
992 } else if ((ad_get_MD_flags( adp ) & O_CREAT) ) {
993 ad_setname(adp, path->m_name);
995 if ((id = get_id(vol, adp, &path->st, curdir->d_did, upath, strlen(upath))) == CNID_INVALID) {
996 LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): CNID error", upath);
999 (void)ad_setid(adp, path->st.st_dev, path->st.st_ino, id, curdir->d_did, vol->v_stamp);
1004 while ( bitmap != 0 ) {
1005 while (( bitmap & 1 ) == 0 ) {
1012 ad_getattr(adp, &bshort);
1014 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
1015 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
1019 if ((bshort & htons(ATTRBIT_INVISIBLE)) != (oshort & htons(ATTRBIT_INVISIBLE)))
1020 change_parent_mdate = 1;
1021 ad_setattr(adp, bshort);
1023 case FILPBIT_CDATE :
1024 ad_setdate(adp, AD_DATE_CREATE, cdate);
1026 case FILPBIT_MDATE :
1028 case FILPBIT_BDATE :
1029 ad_setdate(adp, AD_DATE_BACKUP, bdate);
1031 case FILPBIT_FINFO :
1032 if (default_type( ad_entry( adp, ADEID_FINDERI ))
1034 ((em = getextmap( path->m_name )) &&
1035 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1036 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1037 || ((em = getdefextmap()) &&
1038 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1039 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1041 memcpy(finder_buf, ufinderi, 8 );
1043 memcpy(ad_entry( adp, ADEID_FINDERI ), finder_buf, 32 );
1045 case FILPBIT_UNIXPR :
1047 setfilunixmode(vol, path, upriv);
1050 case FILPBIT_PDINFO :
1051 if (obj->afp_version < 30) { /* else it's UTF8 name */
1052 memcpy(ad_entry( adp, ADEID_FINDERI ), fdType, 4 );
1053 memcpy(ad_entry( adp, ADEID_FINDERI ) + 4, "pdos", 4 );
1058 err = AFPERR_BITMAP;
1059 goto setfilparam_done;
1066 if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
1067 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1071 ad_setdate(adp, AD_DATE_MODIFY, newdate);
1072 ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
1078 ad_close(adp, ADFLAGS_HF);
1081 if (change_parent_mdate && gettimeofday(&tv, NULL) == 0) {
1082 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1083 bitmap = 1<<FILPBIT_MDATE;
1084 setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
1088 LOG(log_debug9, logtype_afpd, "end setfilparams:");
1094 * renamefile and copyfile take the old and new unix pathnames
1095 * and the new mac name.
1097 * sdir_fd source dir fd to which src path is relative (for openat et al semantics)
1098 * passing -1 means this is not used, src path is a full path
1099 * src the source path
1100 * dst the dest filename in current dir
1101 * newname the dest mac name
1102 * adp adouble struct of src file, if open, or & zeroed one
1105 int renamefile(struct vol *vol, struct dir *ddir, int sdir_fd, char *src, char *dst, char *newname, struct adouble *adp)
1109 LOG(log_debug, logtype_afpd,
1110 "renamefile: src[%d, \"%s\"] -> dst[\"%s\"]", sdir_fd, src, dst);
1112 if ( unix_rename( sdir_fd, src, -1, dst ) < 0 ) {
1115 return( AFPERR_NOOBJ );
1118 return( AFPERR_ACCESS );
1120 return AFPERR_VLOCK;
1121 case EXDEV : /* Cross device move -- try copy */
1122 /* NOTE: with open file it's an error because after the copy we will
1123 * get two files, it's fixable for our process (eg reopen the new file, get the
1124 * locks, and so on. But it doesn't solve the case with a second process
1126 if (adp->ad_open_forks) {
1127 /* FIXME warning in syslog so admin'd know there's a conflict ?*/
1128 return AFPERR_OLOCK; /* little lie */
1130 if (AFP_OK != ( rc = copyfile(vol, vol, ddir, sdir_fd, src, dst, newname, NULL )) ) {
1131 /* on error copyfile delete dest */
1134 return deletefile(vol, sdir_fd, src, 0);
1136 return( AFPERR_PARAM );
1140 if (vol->vfs->vfs_renamefile(vol, sdir_fd, src, dst) < 0 ) {
1144 /* try to undo the data fork rename,
1145 * we know we are on the same device
1148 unix_rename(-1, dst, sdir_fd, src );
1149 /* return the first error */
1152 return AFPERR_NOOBJ;
1155 return AFPERR_ACCESS ;
1157 return AFPERR_VLOCK;
1159 return AFPERR_PARAM ;
1164 /* don't care if we can't open the newly renamed ressource fork */
1165 if (ad_open(adp, dst, ADFLAGS_HF | ADFLAGS_RDWR) == 0) {
1166 ad_setname(adp, newname);
1168 ad_close( adp, ADFLAGS_HF );
1175 convert a Mac long name to an utf8 name,
1177 size_t mtoUTF8(const struct vol *vol, const char *src, size_t srclen, char *dest, size_t destlen)
1181 if ((size_t)-1 == (outlen = convert_string ( vol->v_maccharset, CH_UTF8_MAC, src, srclen, dest, destlen)) ) {
1187 /* ---------------- */
1188 int copy_path_name(const struct vol *vol, char *newname, char *ibuf)
1195 if ( type != 2 && !(vol->v_obj->afp_version >= 30 && type == 3) ) {
1201 if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
1202 if (vol->v_obj->afp_version >= 30) {
1203 /* convert it to UTF8
1205 if ((plen = mtoUTF8(vol, ibuf, plen, newname, AFPOBJ_TMPSIZ)) == (size_t)-1)
1209 strncpy( newname, ibuf, plen );
1210 newname[ plen ] = '\0';
1212 if (strlen(newname) != plen) {
1213 /* there's \0 in newname, e.g. it's a pathname not
1221 memcpy(&hint, ibuf, sizeof(hint));
1222 ibuf += sizeof(hint);
1224 memcpy(&len16, ibuf, sizeof(len16));
1225 ibuf += sizeof(len16);
1226 plen = ntohs(len16);
1229 if (plen > AFPOBJ_TMPSIZ) {
1232 strncpy( newname, ibuf, plen );
1233 newname[ plen ] = '\0';
1234 if (strlen(newname) != plen) {
1243 /* -----------------------------------
1245 int afp_copyfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1247 struct vol *s_vol, *d_vol;
1249 char *newname, *p, *upath;
1250 struct path *s_path;
1251 uint32_t sdid, ddid;
1252 int err, retvalue = AFP_OK;
1253 uint16_t svid, dvid;
1255 struct adouble ad, *adp;
1261 memcpy(&svid, ibuf, sizeof( svid ));
1262 ibuf += sizeof( svid );
1263 if (NULL == ( s_vol = getvolbyvid( svid )) ) {
1264 return( AFPERR_PARAM );
1267 memcpy(&sdid, ibuf, sizeof( sdid ));
1268 ibuf += sizeof( sdid );
1269 if (NULL == ( dir = dirlookup( s_vol, sdid )) ) {
1273 memcpy(&dvid, ibuf, sizeof( dvid ));
1274 ibuf += sizeof( dvid );
1275 memcpy(&ddid, ibuf, sizeof( ddid ));
1276 ibuf += sizeof( ddid );
1278 if (NULL == ( s_path = cname( s_vol, dir, &ibuf )) ) {
1279 return get_afp_errno(AFPERR_PARAM);
1281 if ( path_isadir(s_path) ) {
1282 return( AFPERR_BADTYPE );
1285 /* don't allow copies when the file is open.
1286 * XXX: the spec only calls for read/deny write access.
1287 * however, copyfile doesn't have any of that info,
1288 * and locks need to stay coherent. as a result,
1289 * we just balk if the file is opened already. */
1291 adp = of_ad(s_vol, s_path, &ad);
1293 if (ad_open(adp, s_path->u_name, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | ADFLAGS_RDONLY | ADFLAGS_SETSHRMD) < 0) {
1294 return AFPERR_DENYCONF;
1296 #ifdef HAVE_FSHARE_T
1298 shmd.f_access = F_RDACC;
1299 shmd.f_deny = F_NODNY;
1300 if (fcntl(ad_data_fileno(adp), F_SHARE, &shmd) != 0) {
1301 retvalue = AFPERR_DENYCONF;
1304 if (AD_RSRC_OPEN(adp) && fcntl(ad_reso_fileno(adp), F_SHARE, &shmd) != 0) {
1305 retvalue = AFPERR_DENYCONF;
1309 denyreadset = (ad_testlock(adp, ADEID_DFORK, AD_FILELOCK_DENY_RD) != 0 ||
1310 ad_testlock(adp, ADEID_RFORK, AD_FILELOCK_DENY_RD) != 0 );
1313 retvalue = AFPERR_DENYCONF;
1317 newname = obj->newtmp;
1318 strcpy( newname, s_path->m_name );
1320 p = ctoupath( s_vol, curdir, newname );
1322 retvalue = AFPERR_PARAM;
1326 if (NULL == ( d_vol = getvolbyvid( dvid )) ) {
1327 retvalue = AFPERR_PARAM;
1331 if (d_vol->v_flags & AFPVOL_RO) {
1332 retvalue = AFPERR_VLOCK;
1336 if (NULL == ( dir = dirlookup( d_vol, ddid )) ) {
1337 retvalue = afp_errno;
1341 if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) {
1342 retvalue = get_afp_errno(AFPERR_NOOBJ);
1346 if ( *s_path->m_name != '\0' ) {
1347 retvalue =path_error(s_path, AFPERR_NOOBJ);
1351 /* one of the handful of places that knows about the path type */
1352 if (copy_path_name(d_vol, newname, ibuf) < 0) {
1353 retvalue = AFPERR_PARAM;
1356 /* newname is always only a filename so curdir *is* its
1359 if (NULL == (upath = mtoupath(d_vol, newname, curdir->d_did, utf8_encoding(d_vol->v_obj)))) {
1360 retvalue =AFPERR_PARAM;
1364 if ( (err = copyfile(s_vol, d_vol, curdir, -1, p, upath , newname, adp)) < 0 ) {
1370 setvoltime(obj, d_vol );
1373 ad_close( adp, ADFLAGS_DF |ADFLAGS_HF | ADFLAGS_SETSHRMD);
1377 /* ----------------------------------
1378 * if newname is NULL (from directory.c) we don't want to copy the resource fork.
1379 * because we are doing it elsewhere.
1380 * currently if newname is NULL then adp is NULL.
1382 int copyfile(struct vol *s_vol,
1389 struct adouble *adp)
1391 struct adouble ads, add;
1398 LOG(log_debug, logtype_afpd, "copyfile(sfd:%d,s:'%s',d:'%s',n:'%s')",
1399 sfd, src, dst, newname);
1402 ad_init(&ads, s_vol);
1406 adflags = ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | ADFLAGS_RF | ADFLAGS_NORF;
1408 if (ad_openat(adp, sfd, src, adflags | ADFLAGS_RDONLY) < 0) {
1413 if (!AD_META_OPEN(adp))
1414 /* no resource fork, don't create one for dst file */
1415 adflags &= ~ADFLAGS_HF;
1417 if (!AD_RSRC_OPEN(adp))
1418 /* no resource fork, don't create one for dst file */
1419 adflags &= ~ADFLAGS_RF;
1421 stat_result = fstat(ad_data_fileno(adp), &st); /* saving stat exit code, thus saving us on one more stat later on */
1423 if (stat_result < 0) {
1424 /* unlikely but if fstat fails, the default file mode will be 0666. */
1425 st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
1428 ad_init(&add, d_vol);
1429 if (ad_open(&add, dst, adflags | ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_EXCL, st.st_mode | S_IRUSR | S_IWUSR) < 0) {
1431 ad_close( adp, adflags );
1432 if (EEXIST != ret_err) {
1433 deletefile(d_vol, -1, dst, 0);
1436 return AFPERR_EXIST;
1439 if ((err = copy_fork(ADEID_DFORK, &add, adp)) != 0)
1440 LOG(log_error, logtype_afpd, "copyfile('%s'): %s", src, strerror(errno));
1443 if ((err = d_vol->vfs->vfs_copyfile(d_vol, sfd, src, dst)) != 0)
1444 LOG(log_error, logtype_afpd, "copyfile('%s'): %s", src, strerror(errno));
1449 if (AD_META_OPEN(&add)) {
1450 if (AD_META_OPEN(adp))
1451 ad_copy_header(&add, adp);
1452 ad_setname(&add, dst);
1455 if (fstat(ad_meta_fileno(&add), &stdest) != 0) {
1459 if ((id = get_id(d_vol, &add, &stdest, d_dir->d_did, dst, strlen(dst))) == CNID_INVALID) {
1463 (void)ad_setid(&add, stdest.st_dev, stdest.st_ino, id, d_dir->d_did, d_vol->v_stamp);
1468 ad_close( adp, adflags );
1470 if (ad_close( &add, adflags ) <0) {
1475 deletefile(d_vol, -1, dst, 0);
1477 else if (stat_result == 0) {
1478 /* set dest modification date to src date */
1481 ut.actime = ut.modtime = st.st_mtime;
1483 /* FIXME netatalk doesn't use resource fork file date
1484 * but maybe we should set its modtime too.
1489 switch ( ret_err ) {
1495 LOG(log_info, logtype_afpd, "copyfile: DISK FULL");
1496 return AFPERR_DFULL;
1498 return AFPERR_NOOBJ;
1500 return AFPERR_ACCESS;
1502 return AFPERR_VLOCK;
1504 return AFPERR_PARAM;
1508 /* -----------------------------------
1509 vol: not NULL delete cnid entry. then we are in curdir and file is a only filename
1510 checkAttrib: 1 check kFPDeleteInhibitBit (deletfile called by afp_delete)
1512 when deletefile is called we don't have lock on it, file is closed (for us)
1513 untrue if called by renamefile
1515 ad_open always try to open file RDWR first and ad_lock takes care of
1516 WRITE lock on read only file.
1519 static int check_attrib(struct adouble *adp)
1521 uint16_t bshort = 0;
1523 ad_getattr(adp, &bshort);
1525 * Does kFPDeleteInhibitBit (bit 8) set?
1527 if ((bshort & htons(ATTRBIT_NODELETE))) {
1528 return AFPERR_OLOCK;
1530 if ((bshort & htons(ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
1536 * dirfd can be used for unlinkat semantics
1538 int deletefile(const struct vol *vol, int dirfd, char *file, int checkAttrib)
1541 struct adouble *adp = NULL;
1542 int adflags, err = AFP_OK;
1545 LOG(log_debug, logtype_afpd, "deletefile('%s')", file);
1549 /* was EACCESS error try to get only metadata */
1550 /* we never want to create a resource fork here, we are going to delete it
1551 * moreover sometimes deletefile is called with a no existent file and
1552 * ad_open would create a 0 byte resource fork
1554 if ( ad_metadataat(dirfd, file, ADFLAGS_CHECK_OF, &ad) == 0 ) {
1555 if ((err = check_attrib(&ad))) {
1556 ad_close(&ad, ADFLAGS_HF | ADFLAGS_CHECK_OF);
1563 /* try to open both forks at once */
1564 adflags = ADFLAGS_DF;
1565 if (ad_openat(&ad, dirfd, file, adflags | ADFLAGS_RF | ADFLAGS_NORF | ADFLAGS_RDONLY) < 0 ) {
1570 case EACCES: /* maybe it's a file with no write mode for us */
1571 break; /* was return AFPERR_ACCESS;*/
1584 if ( adp && AD_RSRC_OPEN(adp) ) { /* there's a resource fork */
1585 adflags |= ADFLAGS_RF;
1586 /* FIXME we have a pb here because we want to know if a file is open
1587 * there's a 'priority inversion' if you can't open the ressource fork RW
1588 * you can delete it if it's open because you can't get a write lock.
1590 * ADLOCK_FILELOCK means the whole ressource fork, not only after the
1593 * FIXME it doesn't work for RFORK open read only and fork open without deny mode
1595 if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
1601 if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
1602 LOG(log_error, logtype_afpd, "deletefile('%s'): ad_tmplock error: %s", file, strerror(errno));
1604 } else if (!(err = vol->vfs->vfs_deletefile(vol, dirfd, file)) && !(err = netatalk_unlinkat(dirfd, file )) ) {
1606 if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file)))) {
1607 cnid_delete(vol->v_cdb, id);
1613 ad_close(&ad, ADFLAGS_HF | ADFLAGS_CHECK_OF);
1616 ad_close( &ad, adflags ); /* ad_close removes locks if any */
1621 /* ------------------------------------ */
1622 /* return a file id */
1623 int afp_createid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1632 struct path *s_path;
1638 memcpy(&vid, ibuf, sizeof(vid));
1639 ibuf += sizeof(vid);
1641 if (NULL == ( vol = getvolbyvid( vid )) ) {
1642 return( AFPERR_PARAM);
1645 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1649 if (vol->v_flags & AFPVOL_RO)
1650 return AFPERR_VLOCK;
1652 memcpy(&did, ibuf, sizeof( did ));
1653 ibuf += sizeof(did);
1655 if (NULL == ( dir = dirlookup( vol, did )) ) {
1656 return afp_errno; /* was AFPERR_PARAM */
1659 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
1660 return get_afp_errno(AFPERR_NOOBJ); /* was AFPERR_PARAM */
1663 if ( path_isadir(s_path) ) {
1664 return( AFPERR_BADTYPE );
1667 upath = s_path->u_name;
1668 switch (s_path->st_errno) {
1670 break; /* success */
1673 return AFPERR_ACCESS;
1675 return AFPERR_NOOBJ;
1677 return AFPERR_PARAM;
1680 if ((id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath)))) {
1681 memcpy(rbuf, &id, sizeof(id));
1682 *rbuflen = sizeof(id);
1683 return AFPERR_EXISTID;
1686 if ((id = get_id(vol, NULL, st, did, upath, len)) != CNID_INVALID) {
1687 memcpy(rbuf, &id, sizeof(id));
1688 *rbuflen = sizeof(id);
1695 /* ------------------------------- */
1701 static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
1704 struct reenum *param = data;
1705 struct vol *vol = param->vol;
1706 cnid_t did = param->did;
1709 if (ostat(de->d_name, &path.st, vol_syml_opt(vol)) < 0)
1712 /* update or add to cnid */
1713 aint = cnid_add(vol->v_cdb, &path.st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */
1718 /* --------------------
1719 * Ok the db is out of synch with the dir.
1720 * but if it's a deleted file we don't want to do it again and again.
1723 reenumerate_id(struct vol *vol, char *name, struct dir *dir)
1729 if (vol->v_cdb == NULL) {
1733 /* FIXME use of_statdir ? */
1734 if (ostat(name, &st, vol_syml_opt(vol))) {
1738 if (dirreenumerate(dir, &st)) {
1739 /* we already did it once and the dir haven't been modified */
1740 return dir->d_offcnt;
1744 data.did = dir->d_did;
1745 if ((ret = for_each_dirent(vol, name, reenumerate_loop, (void *)&data)) >= 0) {
1746 setdiroffcnt(curdir, &st, ret);
1747 dir->d_flags |= DIRF_CNID;
1753 /* ------------------------------
1754 resolve a file id */
1755 int afp_resolveid(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1764 uint16_t vid, bitmap;
1766 static char buffer[12 + MAXPATHLEN + 1];
1767 int len = 12 + MAXPATHLEN + 1;
1772 memcpy(&vid, ibuf, sizeof(vid));
1773 ibuf += sizeof(vid);
1775 if (NULL == ( vol = getvolbyvid( vid )) ) {
1776 return( AFPERR_PARAM);
1779 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1783 memcpy(&id, ibuf, sizeof( id ));
1788 /* some MacOS versions after a catsearch do a *lot* of afp_resolveid with 0 */
1792 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1793 return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
1796 if (NULL == ( dir = dirlookup( vol, id )) ) {
1797 return AFPERR_NOID; /* idem AFPERR_PARAM */
1799 if (movecwd(vol, dir) < 0) {
1803 return AFPERR_ACCESS;
1807 return AFPERR_PARAM;
1811 memset(&path, 0, sizeof(path));
1812 path.u_name = upath;
1813 if (of_stat(vol, &path) < 0 ) {
1815 /* with nfs and our working directory is deleted */
1816 if (errno == ESTALE) {
1820 if ( errno == ENOENT && !retry) {
1821 /* cnid db is out of sync, reenumerate the directory and update ids */
1822 reenumerate_id(vol, ".", dir);
1830 return AFPERR_ACCESS;
1834 return AFPERR_PARAM;
1838 /* directories are bad */
1839 if (S_ISDIR(path.st.st_mode)) {
1840 /* OS9 and OSX don't return the same error code */
1841 return (obj->afp_version >=30)?AFPERR_NOID:AFPERR_BADTYPE;
1844 memcpy(&bitmap, ibuf, sizeof(bitmap));
1845 bitmap = ntohs( bitmap );
1846 if (NULL == (path.m_name = utompath(vol, upath, cnid, utf8_encoding(obj)))) {
1850 if (AFP_OK != (err = getfilparams(obj, vol, bitmap, &path , curdir,
1851 rbuf + sizeof(bitmap), &buflen, 0))) {
1854 *rbuflen = buflen + sizeof(bitmap);
1855 memcpy(rbuf, ibuf, sizeof(bitmap));
1860 /* ------------------------------ */
1861 int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1871 static char buffer[12 + MAXPATHLEN + 1];
1872 int len = 12 + MAXPATHLEN + 1;
1877 memcpy(&vid, ibuf, sizeof(vid));
1878 ibuf += sizeof(vid);
1880 if (NULL == ( vol = getvolbyvid( vid )) ) {
1881 return( AFPERR_PARAM);
1884 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1888 if (vol->v_flags & AFPVOL_RO)
1889 return AFPERR_VLOCK;
1891 memcpy(&id, ibuf, sizeof( id ));
1895 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1899 if (NULL == ( dir = dirlookup( vol, id )) ) {
1900 if (afp_errno == AFPERR_NOOBJ) {
1904 return( AFPERR_PARAM );
1908 if ((movecwd(vol, dir) < 0) || (ostat(upath, &st, vol_syml_opt(vol)) < 0)) {
1912 return AFPERR_ACCESS;
1917 /* still try to delete the id */
1921 return AFPERR_PARAM;
1924 else if (S_ISDIR(st.st_mode)) /* directories are bad */
1925 return AFPERR_BADTYPE;
1928 if (cnid_delete(vol->v_cdb, fileid)) {
1931 return AFPERR_VLOCK;
1934 return AFPERR_ACCESS;
1936 return AFPERR_PARAM;
1943 /* ------------------------------ */
1944 static struct adouble *find_adouble(const AFPObj *obj, struct vol *vol, struct path *path, struct ofork **of, struct adouble *adp)
1948 if (path->st_errno) {
1949 switch (path->st_errno) {
1951 afp_errno = AFPERR_NOID;
1955 afp_errno = AFPERR_ACCESS;
1958 afp_errno = AFPERR_PARAM;
1963 /* we use file_access both for legacy Mac perm and
1964 * for unix privilege, rename will take care of folder perms
1966 if (file_access(obj, vol, path, OPENACC_WR ) < 0) {
1967 afp_errno = AFPERR_ACCESS;
1971 if ((*of = of_findname(vol, path))) {
1972 /* reuse struct adouble so it won't break locks */
1976 ret = ad_open(adp, path->u_name, ADFLAGS_HF | ADFLAGS_RDWR);
1978 if ( !ret && ad_reso_fileno(adp) != -1 && !(adp->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
1980 * The user must have the Read & Write privilege for both files in order to use this command.
1982 ad_close(adp, ADFLAGS_HF);
1983 afp_errno = AFPERR_ACCESS;
1990 #define APPLETEMP ".AppleTempXXXXXX"
1992 int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1994 struct stat srcst, destst;
1996 struct dir *dir, *sdir;
1997 char *spath, temp[17], *p;
1998 char *supath, *upath;
2003 struct adouble *adsp = NULL;
2004 struct adouble *addp = NULL;
2005 struct ofork *s_of = NULL;
2006 struct ofork *d_of = NULL;
2016 memcpy(&vid, ibuf, sizeof(vid));
2017 ibuf += sizeof(vid);
2019 if (NULL == ( vol = getvolbyvid( vid )) ) {
2020 return( AFPERR_PARAM);
2023 if ((vol->v_flags & AFPVOL_RO))
2024 return AFPERR_VLOCK;
2026 /* source and destination dids */
2027 memcpy(&sid, ibuf, sizeof(sid));
2028 ibuf += sizeof(sid);
2029 memcpy(&did, ibuf, sizeof(did));
2030 ibuf += sizeof(did);
2033 if (NULL == (dir = dirlookup( vol, sid )) ) {
2034 return afp_errno; /* was AFPERR_PARAM */
2037 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2038 return get_afp_errno(AFPERR_NOOBJ);
2041 if ( path_isadir(path) ) {
2042 return AFPERR_BADTYPE; /* it's a dir */
2045 /* save some stuff */
2048 spath = obj->oldtmp;
2049 supath = obj->newtmp;
2050 strcpy(spath, path->m_name);
2051 strcpy(supath, path->u_name); /* this is for the cnid changing */
2052 p = absupath( vol, sdir, supath);
2054 /* pathname too long */
2055 return AFPERR_PARAM ;
2059 if (!(adsp = find_adouble(obj, vol, path, &s_of, &ads))) {
2063 /* ***** from here we may have resource fork open **** */
2065 /* look for the source cnid. if it doesn't exist, don't worry about
2067 sid = cnid_lookup(vol->v_cdb, &srcst, sdir->d_did, supath,slen = strlen(supath));
2069 if (NULL == ( dir = dirlookup( vol, did )) ) {
2070 err = afp_errno; /* was AFPERR_PARAM */
2071 goto err_exchangefile;
2074 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2075 err = get_afp_errno(AFPERR_NOOBJ);
2076 goto err_exchangefile;
2079 if ( path_isadir(path) ) {
2080 err = AFPERR_BADTYPE;
2081 goto err_exchangefile;
2084 /* FPExchangeFiles is the only call that can return the SameObj
2086 if ((curdir == sdir) && strcmp(spath, path->m_name) == 0) {
2087 err = AFPERR_SAMEOBJ;
2088 goto err_exchangefile;
2092 if (!(addp = find_adouble(obj, vol, path, &d_of, &add))) {
2094 goto err_exchangefile;
2098 /* they are not on the same device and at least one is open
2099 * FIXME broken for for crossdev and adouble v2
2102 crossdev = (srcst.st_dev != destst.st_dev);
2103 if (/* (d_of || s_of) && */ crossdev) {
2105 goto err_exchangefile;
2108 /* look for destination id. */
2109 upath = path->u_name;
2110 did = cnid_lookup(vol->v_cdb, &destst, curdir->d_did, upath, dlen = strlen(upath));
2112 /* construct a temp name.
2113 * NOTE: the temp file will be in the dest file's directory. it
2114 * will also be inaccessible from AFP. */
2115 memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
2117 if ((fd = mkstemp(temp)) == -1) {
2119 goto err_exchangefile;
2124 /* FIXME we need to close fork for copy, both s_of and d_of are null */
2125 ad_close(adsp, ADFLAGS_HF);
2126 ad_close(addp, ADFLAGS_HF);
2129 /* now, quickly rename the file. we error if we can't. */
2130 if ((err = renamefile(vol, curdir, -1, p, temp, temp, adsp)) != AFP_OK)
2131 goto err_exchangefile;
2132 of_rename(vol, s_of, sdir, spath, curdir, temp);
2134 /* rename destination to source */
2135 if ((err = renamefile(vol, curdir, -1, upath, p, spath, addp)) != AFP_OK)
2136 goto err_src_to_tmp;
2137 of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
2139 /* rename temp to destination */
2140 if ((err = renamefile(vol, curdir, -1, temp, upath, path->m_name, adsp)) != AFP_OK)
2141 goto err_dest_to_src;
2142 of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
2144 /* id's need switching. src -> dest and dest -> src.
2145 * we need to re-stat() if it was a cross device copy.
2148 cnid_delete(vol->v_cdb, sid);
2150 cnid_delete(vol->v_cdb, did);
2152 if ((did && ( (crossdev && ostat(upath, &srcst, vol_syml_opt(vol)) < 0) ||
2153 cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
2155 (sid && ( (crossdev && ostat(p, &destst, vol_syml_opt(vol)) < 0) ||
2156 cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
2161 err = AFPERR_ACCESS;
2166 goto err_temp_to_dest;
2169 /* here we need to reopen if crossdev */
2170 if (sid && ad_setid(addp, destst.st_dev, destst.st_ino, sid, sdir->d_did, vol->v_stamp))
2175 if (did && ad_setid(adsp, srcst.st_dev, srcst.st_ino, did, curdir->d_did, vol->v_stamp))
2180 /* change perms, src gets dest perm and vice versa */
2185 * we need to exchange ACL entries as well
2187 /* exchange_acls(vol, p, upath); */
2192 path->m_name = NULL;
2193 path->u_name = upath;
2195 setfilunixmode(vol, path, destst.st_mode);
2196 setfilowner(vol, destst.st_uid, destst.st_gid, path);
2203 setfilunixmode(vol, path, srcst.st_mode);
2204 setfilowner(vol, srcst.st_uid, srcst.st_gid, path);
2209 goto err_exchangefile;
2211 /* all this stuff is so that we can unwind a failed operation
2214 /* rename dest to temp */
2215 renamefile(vol, curdir, -1, upath, temp, temp, adsp);
2216 of_rename(vol, s_of, curdir, upath, curdir, temp);
2219 /* rename source back to dest */
2220 renamefile(vol, curdir, -1, p, upath, path->m_name, addp);
2221 of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
2224 /* rename temp back to source */
2225 renamefile(vol, curdir, -1, temp, p, spath, adsp);
2226 of_rename(vol, s_of, curdir, temp, sdir, spath);
2229 if ( !s_of && adsp && ad_meta_fileno(adsp) != -1 ) { /* META */
2230 ad_close(adsp, ADFLAGS_HF);
2232 if ( !d_of && addp && ad_meta_fileno(addp) != -1 ) {/* META */
2233 ad_close(addp, ADFLAGS_HF);
2237 if ((cached = dircache_search_by_did(vol, sid)) != NULL)
2238 (void)dir_remove(vol, cached);
2239 if ((cached = dircache_search_by_did(vol, did)) != NULL)
2240 (void)dir_remove(vol, cached);