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);
95 if (vol_inv_dots(vol) && *upath == '.') { /* make it invisible */
98 ashort = htons(FINDERINFO_INVISIBLE);
99 memcpy((char *)data + FINDERINFO_FRFLAGOFF, &ashort, sizeof(ashort));
103 if (islink && !vol_syml_opt(vol)) {
105 memcpy(&linkflag, (char *)data + FINDERINFO_FRFLAGOFF, 2);
106 linkflag |= htons(FINDERINFO_ISALIAS);
107 memcpy((char *)data + FINDERINFO_FRFLAGOFF, &linkflag, 2);
108 memcpy((char *)data + FINDERINFO_FRTYPEOFF,"slnk",4);
109 memcpy((char *)data + FINDERINFO_FRCREATOFF,"rhap",4);
112 /** Only enter if no appledouble information and no finder information found. */
113 if (chk_ext && (em = getextmap( upath ))) {
114 memcpy(data, em->em_type, sizeof( em->em_type ));
115 memcpy((char *)data + 4, em->em_creator, sizeof(em->em_creator));
121 /* ---------------------
123 char *set_name(const struct vol *vol, char *data, cnid_t pid, char *name, cnid_t id, uint32_t utf8)
128 aint = strlen( name );
132 if (utf8_encoding(vol->v_obj)) {
133 /* but name is an utf8 mac name */
136 /* global static variable... */
138 if (!(u = mtoupath(vol, name, pid, 1)) || !(m = utompath(vol, u, id, 0))) {
147 if (aint > MACFILELEN)
154 if (aint > UTF8FILELEN_EARLY) /* FIXME safeguard, anyway if no ascii char it's game over*/
155 aint = UTF8FILELEN_EARLY;
157 utf8 = vol->v_kTextEncoding;
158 memcpy(data, &utf8, sizeof(utf8));
159 data += sizeof(utf8);
162 memcpy(data, &temp, sizeof(temp));
163 data += sizeof(temp);
166 memcpy( data, src, aint );
176 * FIXME: PDINFO is UTF8 and doesn't need adp
178 #define PARAM_NEED_ADP(b) ((b) & ((1 << FILPBIT_ATTR) |\
179 (1 << FILPBIT_CDATE) |\
180 (1 << FILPBIT_MDATE) |\
181 (1 << FILPBIT_BDATE) |\
182 (1 << FILPBIT_FINFO) |\
183 (1 << FILPBIT_RFLEN) |\
184 (1 << FILPBIT_EXTRFLEN) |\
185 (1 << FILPBIT_PDINFO) |\
186 (1 << FILPBIT_FNUM) |\
187 (1 << FILPBIT_UNIXPR)))
190 * @brief Get CNID for did/upath args both from database and adouble file
192 * 1. Get the objects CNID as stored in its adouble file
193 * 2. Get the objects CNID from the database
194 * 3. If there's a problem with a "dbd" database, fallback to "tdb" in memory
195 * 4. In case 2 and 3 differ, store 3 in the adouble file
197 * @param vol (rw) volume
198 * @param adp (rw) adouble struct of object upath, might be NULL
199 * @param st (r) stat of upath, must NOT be NULL
200 * @param did (r) parent CNID of upath
201 * @param upath (r) name of object
202 * @param len (r) strlen of upath
204 uint32_t get_id(struct vol *vol,
206 const struct stat *st,
211 static int first = 1; /* mark if this func is called the first time */
213 uint32_t dbcnid = CNID_INVALID;
216 if (vol->v_cdb != NULL) {
217 /* prime aint with what we think is the cnid, set did to zero for
218 catching moved files */
219 adcnid = ad_getid(adp, st->st_dev, st->st_ino, 0, vol->v_stamp); /* (1) */
221 dbcnid = cnid_add(vol->v_cdb, st, did, upath, len, adcnid); /* (2) */
222 /* Throw errors if cnid_add fails. */
223 if (dbcnid == CNID_INVALID) {
225 case CNID_ERR_CLOSE: /* the db is closed */
228 LOG(log_error, logtype_afpd, "get_id: Incorrect parameters passed to cnid_add");
229 afp_errno = AFPERR_PARAM;
232 afp_errno = AFPERR_PARAM;
235 /* Close CNID backend if "dbd" and switch to temp in-memory "tdb" */
236 /* we have to do it here for "dbd" because it uses "lazy opening" */
237 /* In order to not end in a loop somehow with goto restart below */
239 if (first && (strcmp(vol->v_cnidscheme, "dbd") == 0)) { /* (3) */
240 cnid_close(vol->v_cdb);
241 free(vol->v_cnidscheme);
242 vol->v_cnidscheme = strdup("tdb");
244 int flags = CNID_FLAG_MEMORY;
245 if ((vol->v_flags & AFPVOL_NODEV)) {
246 flags |= CNID_FLAG_NODEV;
248 LOG(log_error, logtype_afpd, "Reopen volume %s using in memory temporary CNID DB.",
250 vol->v_cdb = cnid_open(vol->v_path, vol->v_umask, "tdb", flags, NULL, NULL);
252 if (!(vol->v_flags & AFPVOL_TM)) {
253 vol->v_flags |= AFPVOL_RO;
254 setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB instead."
255 "Check server messages for details. Switching to read-only mode.");
256 kill(getpid(), SIGUSR2);
258 goto restart; /* now try again with the temp CNID db */
260 setmessage("Something wrong with the volume's CNID DB, using temporary CNID DB failed too!"
261 "Check server messages for details, can't recover from this state!");
264 afp_errno = AFPERR_MISC;
268 else if (adp && adcnid && (adcnid != dbcnid)) { /* 4 */
269 /* Update the ressource fork. For a folder adp is always null */
270 LOG(log_debug, logtype_afpd, "get_id(%s/%s): calling ad_setid(old: %u, new: %u)",
271 getcwdpath(), upath, htonl(adcnid), htonl(dbcnid));
272 if (ad_setid(adp, st->st_dev, st->st_ino, dbcnid, did, vol->v_stamp)) {
273 if (ad_flush(adp) != 0)
274 LOG(log_error, logtype_afpd, "get_id(\"%s\"): can't flush", fullpathname(upath));
284 /* -------------------------- */
285 int getmetadata(const AFPObj *obj,
288 struct path *path, struct dir *dir,
289 char *buf, size_t *buflen, struct adouble *adp)
291 char *data, *l_nameoff = NULL, *upath;
292 char *utf_nameoff = NULL;
297 u_char achar, fdType[4];
302 LOG(log_debug, logtype_afpd, "getmetadata(\"%s\")", path->u_name);
304 upath = path->u_name;
308 if ( ((bitmap & ( (1 << FILPBIT_FINFO)|(1 << FILPBIT_LNAME)|(1 <<FILPBIT_PDINFO) ) ) && !path->m_name)
309 || (bitmap & ( (1 << FILPBIT_LNAME) ) && utf8_encoding(obj)) /* FIXME should be m_name utf8 filename */
310 || (bitmap & (1 << FILPBIT_FNUM))) {
313 struct dir *cachedfile;
314 int len = strlen(upath);
315 if ((cachedfile = dircache_search_by_name(vol, dir, upath, len)) != NULL)
316 id = cachedfile->d_did;
318 id = get_id(vol, adp, st, dir->d_did, upath, len);
320 /* Add it to the cache */
321 LOG(log_debug, logtype_afpd, "getmetadata: caching: did:%u, \"%s\", cnid:%u",
322 ntohl(dir->d_did), upath, ntohl(id));
324 /* Get macname from unixname first */
325 if (path->m_name == NULL) {
326 if ((path->m_name = utompath(vol, upath, id, utf8_encoding(obj))) == NULL) {
327 LOG(log_error, logtype_afpd, "getmetadata: utompath error");
333 if (((fullpath = bstrcpy(dir->d_fullpath)) == NULL)
334 || (bconchar(fullpath, '/') != BSTR_OK)
335 || (bcatcstr(fullpath, upath)) != BSTR_OK) {
336 LOG(log_error, logtype_afpd, "getmetadata: fullpath: %s", strerror(errno));
340 if ((cachedfile = dir_new(path->m_name, upath, vol, dir->d_did, id, fullpath, st)) == NULL) {
341 LOG(log_error, logtype_afpd, "getmetadata: error from dir_new");
345 if ((dircache_add(vol, cachedfile)) != 0) {
346 LOG(log_error, logtype_afpd, "getmetadata: fatal dircache error");
354 if (id == CNID_INVALID)
358 path->m_name = utompath(vol, upath, id, utf8_encoding(vol->v_obj));
361 while ( bitmap != 0 ) {
362 while (( bitmap & 1 ) == 0 ) {
370 ad_getattr(adp, &ashort);
371 } else if (vol_inv_dots(vol) && *upath == '.') {
372 ashort = htons(ATTRBIT_INVISIBLE);
376 /* FIXME do we want a visual clue if the file is read only
379 accessmode(vol, ".", &ma, dir , NULL);
380 if ((ma.ma_user & AR_UWRITE)) {
381 accessmode(vol, upath, &ma, dir , st);
382 if (!(ma.ma_user & AR_UWRITE)) {
383 ashort |= htons(ATTRBIT_NOWRITE);
387 memcpy(data, &ashort, sizeof( ashort ));
388 data += sizeof( ashort );
389 LOG(log_debug, logtype_afpd, "metadata('%s'): AFP Attributes: %04x",
390 path->u_name, ntohs(ashort));
394 memcpy(data, &dir->d_did, sizeof( uint32_t ));
395 data += sizeof( uint32_t );
396 LOG(log_debug, logtype_afpd, "metadata('%s'): Parent DID: %u",
397 path->u_name, ntohl(dir->d_did));
401 if (!adp || (ad_getdate(adp, AD_DATE_CREATE, &aint) < 0))
402 aint = AD_DATE_FROM_UNIX(st->st_mtime);
403 memcpy(data, &aint, sizeof( aint ));
404 data += sizeof( aint );
408 if ( adp && (ad_getdate(adp, AD_DATE_MODIFY, &aint) == 0)) {
409 if ((st->st_mtime > AD_DATE_TO_UNIX(aint))) {
410 aint = AD_DATE_FROM_UNIX(st->st_mtime);
413 aint = AD_DATE_FROM_UNIX(st->st_mtime);
415 memcpy(data, &aint, sizeof( int ));
416 data += sizeof( int );
420 if (!adp || (ad_getdate(adp, AD_DATE_BACKUP, &aint) < 0))
421 aint = AD_DATE_START;
422 memcpy(data, &aint, sizeof( int ));
423 data += sizeof( int );
427 get_finderinfo(vol, upath, adp, (char *)data,S_ISLNK(st->st_mode));
428 data += ADEDLEN_FINDERI;
433 data += sizeof( uint16_t );
437 memset(data, 0, sizeof(uint16_t));
438 data += sizeof( uint16_t );
442 memcpy(data, &id, sizeof( id ));
443 data += sizeof( id );
444 LOG(log_debug, logtype_afpd, "metadata('%s'): CNID: %u",
445 path->u_name, ntohl(id));
449 if (st->st_size > 0xffffffff)
452 aint = htonl( st->st_size );
453 memcpy(data, &aint, sizeof( aint ));
454 data += sizeof( aint );
459 if (adp->ad_rlen > 0xffffffff)
462 aint = htonl( adp->ad_rlen);
466 memcpy(data, &aint, sizeof( aint ));
467 data += sizeof( aint );
470 /* Current client needs ProDOS info block for this file.
471 Use simple heuristic and let the Mac "type" string tell
472 us what the PD file code should be. Everything gets a
473 subtype of 0x0000 unless the original value was hashed
474 to "pXYZ" when we created it. See IA, Ver 2.
475 <shirsch@adelphia.net> */
476 case FILPBIT_PDINFO :
477 if (obj->afp_version >= 30) { /* UTF8 name */
478 utf8 = kTextEncodingUTF8;
480 data += sizeof( uint16_t );
482 memcpy(data, &aint, sizeof( aint ));
483 data += sizeof( aint );
487 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
489 if ( memcmp( fdType, "TEXT", 4 ) == 0 ) {
493 else if ( memcmp( fdType, "PSYS", 4 ) == 0 ) {
497 else if ( memcmp( fdType, "PS16", 4 ) == 0 ) {
501 else if ( memcmp( fdType, "BINA", 4 ) == 0 ) {
505 else if ( fdType[0] == 'p' ) {
507 ashort = (fdType[2] * 256) + fdType[3];
521 memcpy(data, &ashort, sizeof( ashort ));
522 data += sizeof( ashort );
523 memset(data, 0, sizeof( ashort ));
524 data += sizeof( ashort );
527 case FILPBIT_EXTDFLEN:
528 aint = htonl(st->st_size >> 32);
529 memcpy(data, &aint, sizeof( aint ));
530 data += sizeof( aint );
531 aint = htonl(st->st_size);
532 memcpy(data, &aint, sizeof( aint ));
533 data += sizeof( aint );
535 case FILPBIT_EXTRFLEN:
538 aint = htonl(adp->ad_rlen >> 32);
539 memcpy(data, &aint, sizeof( aint ));
540 data += sizeof( aint );
542 aint = htonl(adp->ad_rlen);
543 memcpy(data, &aint, sizeof( aint ));
544 data += sizeof( aint );
546 case FILPBIT_UNIXPR :
547 /* accessmode may change st_mode with ACLs */
548 accessmode(obj, vol, upath, &ma, dir , st);
550 aint = htonl(st->st_uid);
551 memcpy( data, &aint, sizeof( aint ));
552 data += sizeof( aint );
553 aint = htonl(st->st_gid);
554 memcpy( data, &aint, sizeof( aint ));
555 data += sizeof( aint );
558 type == slnk indicates an OSX style symlink,
559 we have to add S_IFLNK to the mode, otherwise
560 10.3 clients freak out. */
564 memcpy(fdType, ad_entry( adp, ADEID_FINDERI ), 4 );
565 if ( memcmp( fdType, "slnk", 4 ) == 0 ) {
571 memcpy( data, &aint, sizeof( aint ));
572 data += sizeof( aint );
574 *data++ = ma.ma_user;
575 *data++ = ma.ma_world;
576 *data++ = ma.ma_group;
577 *data++ = ma.ma_owner;
581 return( AFPERR_BITMAP );
587 ashort = htons( data - buf );
588 memcpy(l_nameoff, &ashort, sizeof( ashort ));
589 data = set_name(vol, data, dir->d_did, path->m_name, id, 0);
592 ashort = htons( data - buf );
593 memcpy(utf_nameoff, &ashort, sizeof( ashort ));
594 data = set_name(vol, data, dir->d_did, path->m_name, id, utf8);
596 *buflen = data - buf;
600 /* ----------------------- */
601 int getfilparams(const AFPObj *obj, struct vol *vol, uint16_t bitmap, struct path *path,
602 struct dir *dir, char *buf, size_t *buflen, int in_enumerate)
604 struct adouble ad, *adp;
609 LOG(log_debug, logtype_afpd, "getfilparams(\"%s\")", path->u_name);
611 opened = PARAM_NEED_ADP(bitmap);
617 * Dont check for and resturn open fork attributes when enumerating
618 * This saves a lot of syscalls, the client will hopefully only use the result
619 * in FPGetFileParms where we return the correct value
621 flags = (!in_enumerate &&(bitmap & (1 << FILPBIT_ATTR))) ? ADFLAGS_CHECK_OF : 0;
623 adp = of_ad(vol, path, &ad);
624 upath = path->u_name;
626 if ( ad_metadata( upath, flags, adp) < 0 ) {
629 LOG(log_error, logtype_afpd, "getfilparams(%s): %s: check resource fork permission?",
630 upath, strerror(errno));
631 return AFPERR_ACCESS;
633 LOG(log_error, logtype_afpd, "getfilparams(%s): bad resource fork", upath);
642 rc = getmetadata(obj, vol, bitmap, path, dir, buf, buflen, adp);
643 ad_close(adp, ADFLAGS_HF | flags);
648 /* ----------------------------- */
649 int afp_createfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
654 struct ofork *of = NULL;
656 int creatf, did, openf, retvalue = AFP_OK;
662 creatf = (unsigned char) *ibuf++;
664 memcpy(&vid, ibuf, sizeof( vid ));
665 ibuf += sizeof( vid );
667 if (NULL == ( vol = getvolbyvid( vid )) )
668 return( AFPERR_PARAM );
670 if (vol->v_flags & AFPVOL_RO)
673 memcpy(&did, ibuf, sizeof( did));
674 ibuf += sizeof( did );
676 if (NULL == ( dir = dirlookup( vol, did )) )
679 if (NULL == ( s_path = cname( vol, dir, &ibuf )) )
680 return get_afp_errno(AFPERR_PARAM);
681 if ( *s_path->m_name == '\0' )
682 return( AFPERR_BADTYPE );
684 upath = s_path->u_name;
687 /* if upath is deleted we already in trouble anyway */
688 if ((of = of_findname(vol, s_path))) {
696 openf = ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_TRUNC;
698 /* on a soft create, if the file is open then ad_open won't fail
699 because open syscall is not called */
700 openf = ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_EXCL;
702 if (ad_open(&ad, upath, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | openf, 0666) < 0) {
706 case ENOENT : /* we were already in 'did folder' so chdir() didn't fail */
707 return ( AFPERR_NOOBJ );
709 return( AFPERR_EXIST );
711 return( AFPERR_ACCESS );
714 LOG(log_info, logtype_afpd, "afp_createfile: DISK FULL");
715 return( AFPERR_DFULL );
717 return( AFPERR_PARAM );
720 if ( ad_meta_fileno( &ad ) == -1 ) { /* Hard META / HF */
721 /* FIXME with hard create on an existing file, we already
722 * corrupted the data file.
724 netatalk_unlink( upath );
725 ad_close( &ad, ADFLAGS_DF );
726 return AFPERR_ACCESS;
729 path = s_path->m_name;
730 ad_setname(&ad, path);
733 if (lstat(upath, &st) != 0) {
734 LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): stat: %s",
735 upath, strerror(errno));
736 ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF);
741 if ((id = get_id(vol, &ad, &st, dir->d_did, upath, strlen(upath))) == CNID_INVALID) {
742 LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): CNID error", upath);
743 goto createfile_iderr;
745 (void)ad_setid(&ad, st.st_dev, st.st_ino, id, dir->d_did, vol->v_stamp);
749 ad_close(&ad, ADFLAGS_DF|ADFLAGS_HF );
750 fce_register(FCE_FILE_CREATE, fullpathname(upath), NULL, fce_file);
755 setvoltime(obj, vol );
760 int afp_setfilparams(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
766 uint16_t vid, bitmap;
771 memcpy(&vid, ibuf, sizeof( vid ));
772 ibuf += sizeof( vid );
773 if (NULL == ( vol = getvolbyvid( vid )) ) {
774 return( AFPERR_PARAM );
777 if (vol->v_flags & AFPVOL_RO)
780 memcpy(&did, ibuf, sizeof( did ));
781 ibuf += sizeof( did );
782 if (NULL == ( dir = dirlookup( vol, did )) ) {
783 return afp_errno; /* was AFPERR_NOOBJ */
786 memcpy(&bitmap, ibuf, sizeof( bitmap ));
787 bitmap = ntohs( bitmap );
788 ibuf += sizeof( bitmap );
790 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
791 return get_afp_errno(AFPERR_PARAM);
794 if (path_isadir(s_path)) {
795 return( AFPERR_BADTYPE ); /* it's a directory */
798 if ( s_path->st_errno != 0 ) {
799 return( AFPERR_NOOBJ );
802 if ((u_long)ibuf & 1 ) {
806 if (AFP_OK == ( rc = setfilparams(obj, vol, s_path, bitmap, ibuf )) ) {
807 setvoltime(obj, vol );
814 * cf AFP3.0.pdf page 252 for change_mdate and change_parent_mdate logic
817 extern struct path Cur_Path;
819 int setfilparams(const AFPObj *obj, struct vol *vol,
820 struct path *path, uint16_t f_bitmap, char *buf )
822 struct adouble ad, *adp;
824 int bit, isad = 1, err = AFP_OK;
826 u_char achar, *fdType, xyy[4]; /* uninitialized, OK 310105 */
827 uint16_t ashort, bshort, oshort;
830 uint16_t upriv_bit = 0;
832 int change_mdate = 0;
833 int change_parent_mdate = 0;
838 uint16_t bitmap = f_bitmap;
839 uint32_t cdate,bdate;
840 u_char finder_buf[32];
841 int symlinked = S_ISLNK(path->st.st_mode);
844 char symbuf[MAXPATHLEN+1];
847 LOG(log_debug9, logtype_afpd, "begin setfilparams:");
850 adp = of_ad(vol, path, &ad);
851 upath = path->u_name;
853 if (!vol_unix_priv(vol) && check_access(obj, vol, upath, OPENACC_WR ) < 0) {
854 return AFPERR_ACCESS;
857 /* with unix priv maybe we have to change adouble file priv first */
859 while ( bitmap != 0 ) {
860 while (( bitmap & 1 ) == 0 ) {
867 memcpy(&ashort, buf, sizeof( ashort ));
868 buf += sizeof( ashort );
872 memcpy(&cdate, buf, sizeof(cdate));
873 buf += sizeof( cdate );
876 memcpy(&newdate, buf, sizeof( newdate ));
877 buf += sizeof( newdate );
881 memcpy(&bdate, buf, sizeof( bdate));
882 buf += sizeof( bdate );
886 if (memcmp(buf,"slnkrhap",8) == 0
887 && !(S_ISLNK(path->st.st_mode))
888 && !(vol->v_flags & AFPVOL_FOLLOWSYM)) {
889 /* request to turn this into a symlink */
890 if ((fp = open(path->u_name, O_RDONLY)) == -1) {
892 goto setfilparam_done;
894 len = read(fp, symbuf, MAXPATHLEN);
898 goto setfilparam_done;
900 if (unlink(path->u_name) != 0) {
902 goto setfilparam_done;
905 if (symlink(symbuf, path->u_name) != 0) {
907 goto setfilparam_done;
912 memcpy(finder_buf, buf, 32 );
915 case FILPBIT_UNIXPR :
916 if (!vol_unix_priv(vol)) {
917 /* this volume doesn't use unix priv */
923 change_parent_mdate = 1;
925 memcpy( &aint, buf, sizeof( aint ));
926 f_uid = ntohl (aint);
927 buf += sizeof( aint );
928 memcpy( &aint, buf, sizeof( aint ));
929 f_gid = ntohl (aint);
930 buf += sizeof( aint );
931 setfilowner(vol, f_uid, f_gid, path);
933 memcpy( &upriv, buf, sizeof( upriv ));
934 buf += sizeof( upriv );
935 upriv = ntohl (upriv);
936 if ((upriv & S_IWUSR)) {
937 setfilunixmode(vol, path, upriv);
944 case FILPBIT_PDINFO :
945 if (obj->afp_version < 30) { /* else it's UTF8 name */
948 /* Keep special case to support crlf translations */
949 if ((unsigned int) achar == 0x04) {
950 fdType = (u_char *)"TEXT";
953 xyy[0] = ( u_char ) 'p';
964 /* break while loop */
973 /* second try with adouble open
975 if (ad_open(adp, upath, ADFLAGS_HF | ADFLAGS_RDWR | ADFLAGS_CREATE, 0666) < 0) {
976 LOG(log_debug, logtype_afpd, "setfilparams: ad_open_metadata error");
978 * For some things, we don't need an adouble header:
979 * - change of modification date
980 * - UNIX privs (Bug-ID #2863424)
982 if (!symlinked && f_bitmap & ~(1<<FILPBIT_MDATE | 1<<FILPBIT_UNIXPR)) {
983 LOG(log_debug, logtype_afpd, "setfilparams: need adouble access");
984 return AFPERR_ACCESS;
986 LOG(log_debug, logtype_afpd, "setfilparams: no adouble perms, but only FILPBIT_MDATE and/or FILPBIT_UNIXPR");
988 } else if ((ad_get_MD_flags( adp ) & O_CREAT) ) {
989 ad_setname(adp, path->m_name);
991 if ((id = get_id(vol, adp, &path->st, curdir->d_did, upath, strlen(upath))) == CNID_INVALID) {
992 LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): CNID error", upath);
995 (void)ad_setid(adp, path->st.st_dev, path->st.st_ino, id, curdir->d_did, vol->v_stamp);
1000 while ( bitmap != 0 ) {
1001 while (( bitmap & 1 ) == 0 ) {
1008 ad_getattr(adp, &bshort);
1010 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
1011 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
1015 if ((bshort & htons(ATTRBIT_INVISIBLE)) != (oshort & htons(ATTRBIT_INVISIBLE)))
1016 change_parent_mdate = 1;
1017 ad_setattr(adp, bshort);
1019 case FILPBIT_CDATE :
1020 ad_setdate(adp, AD_DATE_CREATE, cdate);
1022 case FILPBIT_MDATE :
1024 case FILPBIT_BDATE :
1025 ad_setdate(adp, AD_DATE_BACKUP, bdate);
1027 case FILPBIT_FINFO :
1028 if (default_type( ad_entry( adp, ADEID_FINDERI ))
1030 ((em = getextmap( path->m_name )) &&
1031 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1032 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1033 || ((em = getdefextmap()) &&
1034 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1035 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1037 memcpy(finder_buf, ufinderi, 8 );
1039 memcpy(ad_entry( adp, ADEID_FINDERI ), finder_buf, 32 );
1041 case FILPBIT_UNIXPR :
1043 setfilunixmode(vol, path, upriv);
1046 case FILPBIT_PDINFO :
1047 if (obj->afp_version < 30) { /* else it's UTF8 name */
1048 memcpy(ad_entry( adp, ADEID_FINDERI ), fdType, 4 );
1049 memcpy(ad_entry( adp, ADEID_FINDERI ) + 4, "pdos", 4 );
1054 err = AFPERR_BITMAP;
1055 goto setfilparam_done;
1062 if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
1063 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1067 ad_setdate(adp, AD_DATE_MODIFY, newdate);
1068 ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
1074 ad_close(adp, ADFLAGS_HF);
1077 if (change_parent_mdate && gettimeofday(&tv, NULL) == 0) {
1078 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1079 bitmap = 1<<FILPBIT_MDATE;
1080 setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
1084 LOG(log_debug9, logtype_afpd, "end setfilparams:");
1090 * renamefile and copyfile take the old and new unix pathnames
1091 * and the new mac name.
1093 * sdir_fd source dir fd to which src path is relative (for openat et al semantics)
1094 * passing -1 means this is not used, src path is a full path
1095 * src the source path
1096 * dst the dest filename in current dir
1097 * newname the dest mac name
1098 * adp adouble struct of src file, if open, or & zeroed one
1101 int renamefile(struct vol *vol, struct dir *ddir, int sdir_fd, char *src, char *dst, char *newname, struct adouble *adp)
1105 LOG(log_debug, logtype_afpd,
1106 "renamefile: src[%d, \"%s\"] -> dst[\"%s\"]", sdir_fd, src, dst);
1108 if ( unix_rename( sdir_fd, src, -1, dst ) < 0 ) {
1111 return( AFPERR_NOOBJ );
1114 return( AFPERR_ACCESS );
1116 return AFPERR_VLOCK;
1117 case EXDEV : /* Cross device move -- try copy */
1118 /* NOTE: with open file it's an error because after the copy we will
1119 * get two files, it's fixable for our process (eg reopen the new file, get the
1120 * locks, and so on. But it doesn't solve the case with a second process
1122 if (adp->ad_open_forks) {
1123 /* FIXME warning in syslog so admin'd know there's a conflict ?*/
1124 return AFPERR_OLOCK; /* little lie */
1126 if (AFP_OK != ( rc = copyfile(vol, vol, ddir, sdir_fd, src, dst, newname, NULL )) ) {
1127 /* on error copyfile delete dest */
1130 return deletefile(vol, sdir_fd, src, 0);
1132 return( AFPERR_PARAM );
1136 if (vol->vfs->vfs_renamefile(vol, sdir_fd, src, dst) < 0 ) {
1140 /* try to undo the data fork rename,
1141 * we know we are on the same device
1144 unix_rename(-1, dst, sdir_fd, src );
1145 /* return the first error */
1148 return AFPERR_NOOBJ;
1151 return AFPERR_ACCESS ;
1153 return AFPERR_VLOCK;
1155 return AFPERR_PARAM ;
1160 /* don't care if we can't open the newly renamed ressource fork */
1161 if (ad_open(adp, dst, ADFLAGS_HF | ADFLAGS_RDWR) == 0) {
1162 ad_setname(adp, newname);
1164 ad_close( adp, ADFLAGS_HF );
1171 convert a Mac long name to an utf8 name,
1173 size_t mtoUTF8(const struct vol *vol, const char *src, size_t srclen, char *dest, size_t destlen)
1177 if ((size_t)-1 == (outlen = convert_string ( vol->v_maccharset, CH_UTF8_MAC, src, srclen, dest, destlen)) ) {
1183 /* ---------------- */
1184 int copy_path_name(const struct vol *vol, char *newname, char *ibuf)
1191 if ( type != 2 && !(vol->v_obj->afp_version >= 30 && type == 3) ) {
1197 if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
1198 if (vol->v_obj->afp_version >= 30) {
1199 /* convert it to UTF8
1201 if ((plen = mtoUTF8(vol, ibuf, plen, newname, AFPOBJ_TMPSIZ)) == (size_t)-1)
1205 strncpy( newname, ibuf, plen );
1206 newname[ plen ] = '\0';
1208 if (strlen(newname) != plen) {
1209 /* there's \0 in newname, e.g. it's a pathname not
1217 memcpy(&hint, ibuf, sizeof(hint));
1218 ibuf += sizeof(hint);
1220 memcpy(&len16, ibuf, sizeof(len16));
1221 ibuf += sizeof(len16);
1222 plen = ntohs(len16);
1225 if (plen > AFPOBJ_TMPSIZ) {
1228 strncpy( newname, ibuf, plen );
1229 newname[ plen ] = '\0';
1230 if (strlen(newname) != plen) {
1239 /* -----------------------------------
1241 int afp_copyfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1243 struct vol *s_vol, *d_vol;
1245 char *newname, *p, *upath;
1246 struct path *s_path;
1247 uint32_t sdid, ddid;
1248 int err, retvalue = AFP_OK;
1249 uint16_t svid, dvid;
1251 struct adouble ad, *adp;
1257 memcpy(&svid, ibuf, sizeof( svid ));
1258 ibuf += sizeof( svid );
1259 if (NULL == ( s_vol = getvolbyvid( svid )) ) {
1260 return( AFPERR_PARAM );
1263 memcpy(&sdid, ibuf, sizeof( sdid ));
1264 ibuf += sizeof( sdid );
1265 if (NULL == ( dir = dirlookup( s_vol, sdid )) ) {
1269 memcpy(&dvid, ibuf, sizeof( dvid ));
1270 ibuf += sizeof( dvid );
1271 memcpy(&ddid, ibuf, sizeof( ddid ));
1272 ibuf += sizeof( ddid );
1274 if (NULL == ( s_path = cname( s_vol, dir, &ibuf )) ) {
1275 return get_afp_errno(AFPERR_PARAM);
1277 if ( path_isadir(s_path) ) {
1278 return( AFPERR_BADTYPE );
1281 /* don't allow copies when the file is open.
1282 * XXX: the spec only calls for read/deny write access.
1283 * however, copyfile doesn't have any of that info,
1284 * and locks need to stay coherent. as a result,
1285 * we just balk if the file is opened already. */
1287 adp = of_ad(s_vol, s_path, &ad);
1289 if (ad_open(adp, s_path->u_name, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | ADFLAGS_RDONLY | ADFLAGS_SETSHRMD) < 0) {
1290 return AFPERR_DENYCONF;
1292 #ifdef HAVE_FSHARE_T
1294 shmd.f_access = F_RDACC;
1295 shmd.f_deny = F_NODNY;
1296 if (fcntl(ad_data_fileno(adp), F_SHARE, &shmd) != 0) {
1297 retvalue = AFPERR_DENYCONF;
1300 if (AD_RSRC_OPEN(adp) && fcntl(ad_reso_fileno(adp), F_SHARE, &shmd) != 0) {
1301 retvalue = AFPERR_DENYCONF;
1305 denyreadset = (ad_testlock(adp, ADEID_DFORK, AD_FILELOCK_DENY_RD) != 0 ||
1306 ad_testlock(adp, ADEID_RFORK, AD_FILELOCK_DENY_RD) != 0 );
1309 retvalue = AFPERR_DENYCONF;
1313 newname = obj->newtmp;
1314 strcpy( newname, s_path->m_name );
1316 p = ctoupath( s_vol, curdir, newname );
1318 retvalue = AFPERR_PARAM;
1322 if (NULL == ( d_vol = getvolbyvid( dvid )) ) {
1323 retvalue = AFPERR_PARAM;
1327 if (d_vol->v_flags & AFPVOL_RO) {
1328 retvalue = AFPERR_VLOCK;
1332 if (NULL == ( dir = dirlookup( d_vol, ddid )) ) {
1333 retvalue = afp_errno;
1337 if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) {
1338 retvalue = get_afp_errno(AFPERR_NOOBJ);
1342 if ( *s_path->m_name != '\0' ) {
1343 retvalue =path_error(s_path, AFPERR_NOOBJ);
1347 /* one of the handful of places that knows about the path type */
1348 if (copy_path_name(d_vol, newname, ibuf) < 0) {
1349 retvalue = AFPERR_PARAM;
1352 /* newname is always only a filename so curdir *is* its
1355 if (NULL == (upath = mtoupath(d_vol, newname, curdir->d_did, utf8_encoding(d_vol->v_obj)))) {
1356 retvalue =AFPERR_PARAM;
1360 if ( (err = copyfile(s_vol, d_vol, curdir, -1, p, upath , newname, adp)) < 0 ) {
1366 setvoltime(obj, d_vol );
1369 ad_close( adp, ADFLAGS_DF |ADFLAGS_HF | ADFLAGS_SETSHRMD);
1373 /* ----------------------------------
1374 * if newname is NULL (from directory.c) we don't want to copy the resource fork.
1375 * because we are doing it elsewhere.
1376 * currently if newname is NULL then adp is NULL.
1378 int copyfile(struct vol *s_vol,
1385 struct adouble *adp)
1387 struct adouble ads, add;
1394 LOG(log_debug, logtype_afpd, "copyfile(sfd:%d,s:'%s',d:'%s',n:'%s')",
1395 sfd, src, dst, newname);
1398 ad_init(&ads, s_vol);
1402 adflags = ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | ADFLAGS_RF | ADFLAGS_NORF;
1404 if (ad_openat(adp, sfd, src, adflags | ADFLAGS_RDONLY) < 0) {
1409 if (!AD_META_OPEN(adp))
1410 /* no resource fork, don't create one for dst file */
1411 adflags &= ~ADFLAGS_HF;
1413 if (!AD_RSRC_OPEN(adp))
1414 /* no resource fork, don't create one for dst file */
1415 adflags &= ~ADFLAGS_RF;
1417 stat_result = fstat(ad_data_fileno(adp), &st); /* saving stat exit code, thus saving us on one more stat later on */
1419 if (stat_result < 0) {
1420 /* unlikely but if fstat fails, the default file mode will be 0666. */
1421 st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
1424 ad_init(&add, d_vol);
1425 if (ad_open(&add, dst, adflags | ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_EXCL, st.st_mode | S_IRUSR | S_IWUSR) < 0) {
1427 ad_close( adp, adflags );
1428 if (EEXIST != ret_err) {
1429 deletefile(d_vol, -1, dst, 0);
1432 return AFPERR_EXIST;
1435 if ((err = copy_fork(ADEID_DFORK, &add, adp)) != 0)
1436 LOG(log_error, logtype_afpd, "copyfile('%s'): %s", src, strerror(errno));
1439 if ((err = d_vol->vfs->vfs_copyfile(d_vol, sfd, src, dst)) != 0)
1440 LOG(log_error, logtype_afpd, "copyfile('%s'): %s", src, strerror(errno));
1445 if (AD_META_OPEN(&add)) {
1446 if (AD_META_OPEN(adp))
1447 ad_copy_header(&add, adp);
1448 ad_setname(&add, dst);
1451 if (fstat(ad_meta_fileno(&add), &stdest) != 0) {
1455 if ((id = get_id(d_vol, &add, &stdest, d_dir->d_did, dst, strlen(dst))) == CNID_INVALID) {
1459 (void)ad_setid(&add, stdest.st_dev, stdest.st_ino, id, d_dir->d_did, d_vol->v_stamp);
1464 ad_close( adp, adflags );
1466 if (ad_close( &add, adflags ) <0) {
1471 deletefile(d_vol, -1, dst, 0);
1473 else if (stat_result == 0) {
1474 /* set dest modification date to src date */
1477 ut.actime = ut.modtime = st.st_mtime;
1479 /* FIXME netatalk doesn't use resource fork file date
1480 * but maybe we should set its modtime too.
1485 switch ( ret_err ) {
1491 LOG(log_info, logtype_afpd, "copyfile: DISK FULL");
1492 return AFPERR_DFULL;
1494 return AFPERR_NOOBJ;
1496 return AFPERR_ACCESS;
1498 return AFPERR_VLOCK;
1500 return AFPERR_PARAM;
1504 /* -----------------------------------
1505 vol: not NULL delete cnid entry. then we are in curdir and file is a only filename
1506 checkAttrib: 1 check kFPDeleteInhibitBit (deletfile called by afp_delete)
1508 when deletefile is called we don't have lock on it, file is closed (for us)
1509 untrue if called by renamefile
1511 ad_open always try to open file RDWR first and ad_lock takes care of
1512 WRITE lock on read only file.
1515 static int check_attrib(struct adouble *adp)
1517 uint16_t bshort = 0;
1519 ad_getattr(adp, &bshort);
1521 * Does kFPDeleteInhibitBit (bit 8) set?
1523 if ((bshort & htons(ATTRBIT_NODELETE))) {
1524 return AFPERR_OLOCK;
1526 if ((bshort & htons(ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
1532 * dirfd can be used for unlinkat semantics
1534 int deletefile(const struct vol *vol, int dirfd, char *file, int checkAttrib)
1537 struct adouble *adp = NULL;
1538 int adflags, err = AFP_OK;
1541 LOG(log_debug, logtype_afpd, "deletefile('%s')", file);
1545 /* was EACCESS error try to get only metadata */
1546 /* we never want to create a resource fork here, we are going to delete it
1547 * moreover sometimes deletefile is called with a no existent file and
1548 * ad_open would create a 0 byte resource fork
1550 if ( ad_metadataat(dirfd, file, ADFLAGS_CHECK_OF, &ad) == 0 ) {
1551 if ((err = check_attrib(&ad))) {
1552 ad_close(&ad, ADFLAGS_HF | ADFLAGS_CHECK_OF);
1559 /* try to open both forks at once */
1560 adflags = ADFLAGS_DF;
1561 if (ad_openat(&ad, dirfd, file, adflags | ADFLAGS_RF | ADFLAGS_NORF | ADFLAGS_RDONLY) < 0 ) {
1566 case EACCES: /* maybe it's a file with no write mode for us */
1567 break; /* was return AFPERR_ACCESS;*/
1580 if ( adp && AD_RSRC_OPEN(adp) != -1 ) { /* there's a resource fork */
1581 adflags |= ADFLAGS_RF;
1582 /* FIXME we have a pb here because we want to know if a file is open
1583 * there's a 'priority inversion' if you can't open the ressource fork RW
1584 * you can delete it if it's open because you can't get a write lock.
1586 * ADLOCK_FILELOCK means the whole ressource fork, not only after the
1589 * FIXME it doesn't work for RFORK open read only and fork open without deny mode
1591 if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
1597 if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
1598 LOG(log_error, logtype_afpd, "deletefile('%s'): ad_tmplock error: %s", file, strerror(errno));
1600 } else if (!(err = vol->vfs->vfs_deletefile(vol, dirfd, file)) && !(err = netatalk_unlinkat(dirfd, file )) ) {
1602 if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file)))) {
1603 cnid_delete(vol->v_cdb, id);
1609 ad_close(&ad, ADFLAGS_HF | ADFLAGS_CHECK_OF);
1612 ad_close( &ad, adflags ); /* ad_close removes locks if any */
1617 /* ------------------------------------ */
1618 /* return a file id */
1619 int afp_createid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1628 struct path *s_path;
1634 memcpy(&vid, ibuf, sizeof(vid));
1635 ibuf += sizeof(vid);
1637 if (NULL == ( vol = getvolbyvid( vid )) ) {
1638 return( AFPERR_PARAM);
1641 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1645 if (vol->v_flags & AFPVOL_RO)
1646 return AFPERR_VLOCK;
1648 memcpy(&did, ibuf, sizeof( did ));
1649 ibuf += sizeof(did);
1651 if (NULL == ( dir = dirlookup( vol, did )) ) {
1652 return afp_errno; /* was AFPERR_PARAM */
1655 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
1656 return get_afp_errno(AFPERR_NOOBJ); /* was AFPERR_PARAM */
1659 if ( path_isadir(s_path) ) {
1660 return( AFPERR_BADTYPE );
1663 upath = s_path->u_name;
1664 switch (s_path->st_errno) {
1666 break; /* success */
1669 return AFPERR_ACCESS;
1671 return AFPERR_NOOBJ;
1673 return AFPERR_PARAM;
1676 if ((id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath)))) {
1677 memcpy(rbuf, &id, sizeof(id));
1678 *rbuflen = sizeof(id);
1679 return AFPERR_EXISTID;
1682 if ((id = get_id(vol, NULL, st, did, upath, len)) != CNID_INVALID) {
1683 memcpy(rbuf, &id, sizeof(id));
1684 *rbuflen = sizeof(id);
1691 /* ------------------------------- */
1697 static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
1700 struct reenum *param = data;
1701 struct vol *vol = param->vol;
1702 cnid_t did = param->did;
1705 if (ostat(de->d_name, &path.st, vol_syml_opt(vol)) < 0)
1708 /* update or add to cnid */
1709 aint = cnid_add(vol->v_cdb, &path.st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */
1714 /* --------------------
1715 * Ok the db is out of synch with the dir.
1716 * but if it's a deleted file we don't want to do it again and again.
1719 reenumerate_id(struct vol *vol, char *name, struct dir *dir)
1725 if (vol->v_cdb == NULL) {
1729 /* FIXME use of_statdir ? */
1730 if (ostat(name, &st, vol_syml_opt(vol))) {
1734 if (dirreenumerate(dir, &st)) {
1735 /* we already did it once and the dir haven't been modified */
1736 return dir->d_offcnt;
1740 data.did = dir->d_did;
1741 if ((ret = for_each_dirent(vol, name, reenumerate_loop, (void *)&data)) >= 0) {
1742 setdiroffcnt(curdir, &st, ret);
1743 dir->d_flags |= DIRF_CNID;
1749 /* ------------------------------
1750 resolve a file id */
1751 int afp_resolveid(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1760 uint16_t vid, bitmap;
1762 static char buffer[12 + MAXPATHLEN + 1];
1763 int len = 12 + MAXPATHLEN + 1;
1768 memcpy(&vid, ibuf, sizeof(vid));
1769 ibuf += sizeof(vid);
1771 if (NULL == ( vol = getvolbyvid( vid )) ) {
1772 return( AFPERR_PARAM);
1775 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1779 memcpy(&id, ibuf, sizeof( id ));
1784 /* some MacOS versions after a catsearch do a *lot* of afp_resolveid with 0 */
1788 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1789 return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
1792 if (NULL == ( dir = dirlookup( vol, id )) ) {
1793 return AFPERR_NOID; /* idem AFPERR_PARAM */
1795 if (movecwd(vol, dir) < 0) {
1799 return AFPERR_ACCESS;
1803 return AFPERR_PARAM;
1807 memset(&path, 0, sizeof(path));
1808 path.u_name = upath;
1809 if (of_stat(vol, &path) < 0 ) {
1811 /* with nfs and our working directory is deleted */
1812 if (errno == ESTALE) {
1816 if ( errno == ENOENT && !retry) {
1817 /* cnid db is out of sync, reenumerate the directory and update ids */
1818 reenumerate_id(vol, ".", dir);
1826 return AFPERR_ACCESS;
1830 return AFPERR_PARAM;
1834 /* directories are bad */
1835 if (S_ISDIR(path.st.st_mode)) {
1836 /* OS9 and OSX don't return the same error code */
1837 return (obj->afp_version >=30)?AFPERR_NOID:AFPERR_BADTYPE;
1840 memcpy(&bitmap, ibuf, sizeof(bitmap));
1841 bitmap = ntohs( bitmap );
1842 if (NULL == (path.m_name = utompath(vol, upath, cnid, utf8_encoding(obj)))) {
1846 if (AFP_OK != (err = getfilparams(obj, vol, bitmap, &path , curdir,
1847 rbuf + sizeof(bitmap), &buflen, 0))) {
1850 *rbuflen = buflen + sizeof(bitmap);
1851 memcpy(rbuf, ibuf, sizeof(bitmap));
1856 /* ------------------------------ */
1857 int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1867 static char buffer[12 + MAXPATHLEN + 1];
1868 int len = 12 + MAXPATHLEN + 1;
1873 memcpy(&vid, ibuf, sizeof(vid));
1874 ibuf += sizeof(vid);
1876 if (NULL == ( vol = getvolbyvid( vid )) ) {
1877 return( AFPERR_PARAM);
1880 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1884 if (vol->v_flags & AFPVOL_RO)
1885 return AFPERR_VLOCK;
1887 memcpy(&id, ibuf, sizeof( id ));
1891 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1895 if (NULL == ( dir = dirlookup( vol, id )) ) {
1896 if (afp_errno == AFPERR_NOOBJ) {
1900 return( AFPERR_PARAM );
1904 if ((movecwd(vol, dir) < 0) || (ostat(upath, &st, vol_syml_opt(vol)) < 0)) {
1908 return AFPERR_ACCESS;
1913 /* still try to delete the id */
1917 return AFPERR_PARAM;
1920 else if (S_ISDIR(st.st_mode)) /* directories are bad */
1921 return AFPERR_BADTYPE;
1924 if (cnid_delete(vol->v_cdb, fileid)) {
1927 return AFPERR_VLOCK;
1930 return AFPERR_ACCESS;
1932 return AFPERR_PARAM;
1939 /* ------------------------------ */
1940 static struct adouble *find_adouble(const AFPObj *obj, const struct vol *vol, struct path *path, struct ofork **of, struct adouble *adp)
1944 if (path->st_errno) {
1945 switch (path->st_errno) {
1947 afp_errno = AFPERR_NOID;
1951 afp_errno = AFPERR_ACCESS;
1954 afp_errno = AFPERR_PARAM;
1959 /* we use file_access both for legacy Mac perm and
1960 * for unix privilege, rename will take care of folder perms
1962 if (file_access(obj, vol, path, OPENACC_WR ) < 0) {
1963 afp_errno = AFPERR_ACCESS;
1967 if ((*of = of_findname(vol, path))) {
1968 /* reuse struct adouble so it won't break locks */
1972 ret = ad_open(adp, path->u_name, ADFLAGS_HF | ADFLAGS_RDWR);
1974 if ( !ret && ad_reso_fileno(adp) != -1 && !(adp->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
1976 * The user must have the Read & Write privilege for both files in order to use this command.
1978 ad_close(adp, ADFLAGS_HF);
1979 afp_errno = AFPERR_ACCESS;
1986 #define APPLETEMP ".AppleTempXXXXXX"
1988 int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1990 struct stat srcst, destst;
1992 struct dir *dir, *sdir;
1993 char *spath, temp[17], *p;
1994 char *supath, *upath;
1999 struct adouble *adsp = NULL;
2000 struct adouble *addp = NULL;
2001 struct ofork *s_of = NULL;
2002 struct ofork *d_of = NULL;
2012 memcpy(&vid, ibuf, sizeof(vid));
2013 ibuf += sizeof(vid);
2015 if (NULL == ( vol = getvolbyvid( vid )) ) {
2016 return( AFPERR_PARAM);
2019 if ((vol->v_flags & AFPVOL_RO))
2020 return AFPERR_VLOCK;
2022 /* source and destination dids */
2023 memcpy(&sid, ibuf, sizeof(sid));
2024 ibuf += sizeof(sid);
2025 memcpy(&did, ibuf, sizeof(did));
2026 ibuf += sizeof(did);
2029 if (NULL == (dir = dirlookup( vol, sid )) ) {
2030 return afp_errno; /* was AFPERR_PARAM */
2033 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2034 return get_afp_errno(AFPERR_NOOBJ);
2037 if ( path_isadir(path) ) {
2038 return AFPERR_BADTYPE; /* it's a dir */
2041 /* save some stuff */
2044 spath = obj->oldtmp;
2045 supath = obj->newtmp;
2046 strcpy(spath, path->m_name);
2047 strcpy(supath, path->u_name); /* this is for the cnid changing */
2048 p = absupath( vol, sdir, supath);
2050 /* pathname too long */
2051 return AFPERR_PARAM ;
2055 if (!(adsp = find_adouble(obj, vol, path, &s_of, &ads))) {
2059 /* ***** from here we may have resource fork open **** */
2061 /* look for the source cnid. if it doesn't exist, don't worry about
2063 sid = cnid_lookup(vol->v_cdb, &srcst, sdir->d_did, supath,slen = strlen(supath));
2065 if (NULL == ( dir = dirlookup( vol, did )) ) {
2066 err = afp_errno; /* was AFPERR_PARAM */
2067 goto err_exchangefile;
2070 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2071 err = get_afp_errno(AFPERR_NOOBJ);
2072 goto err_exchangefile;
2075 if ( path_isadir(path) ) {
2076 err = AFPERR_BADTYPE;
2077 goto err_exchangefile;
2080 /* FPExchangeFiles is the only call that can return the SameObj
2082 if ((curdir == sdir) && strcmp(spath, path->m_name) == 0) {
2083 err = AFPERR_SAMEOBJ;
2084 goto err_exchangefile;
2088 if (!(addp = find_adouble(obj, vol, path, &d_of, &add))) {
2090 goto err_exchangefile;
2094 /* they are not on the same device and at least one is open
2095 * FIXME broken for for crossdev and adouble v2
2098 crossdev = (srcst.st_dev != destst.st_dev);
2099 if (/* (d_of || s_of) && */ crossdev) {
2101 goto err_exchangefile;
2104 /* look for destination id. */
2105 upath = path->u_name;
2106 did = cnid_lookup(vol->v_cdb, &destst, curdir->d_did, upath, dlen = strlen(upath));
2108 /* construct a temp name.
2109 * NOTE: the temp file will be in the dest file's directory. it
2110 * will also be inaccessible from AFP. */
2111 memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
2112 if (!mktemp(temp)) {
2114 goto err_exchangefile;
2118 /* FIXME we need to close fork for copy, both s_of and d_of are null */
2119 ad_close(adsp, ADFLAGS_HF);
2120 ad_close(addp, ADFLAGS_HF);
2123 /* now, quickly rename the file. we error if we can't. */
2124 if ((err = renamefile(vol, curdir, -1, p, temp, temp, adsp)) != AFP_OK)
2125 goto err_exchangefile;
2126 of_rename(vol, s_of, sdir, spath, curdir, temp);
2128 /* rename destination to source */
2129 if ((err = renamefile(vol, curdir, -1, upath, p, spath, addp)) != AFP_OK)
2130 goto err_src_to_tmp;
2131 of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
2133 /* rename temp to destination */
2134 if ((err = renamefile(vol, curdir, -1, temp, upath, path->m_name, adsp)) != AFP_OK)
2135 goto err_dest_to_src;
2136 of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
2138 /* id's need switching. src -> dest and dest -> src.
2139 * we need to re-stat() if it was a cross device copy.
2142 cnid_delete(vol->v_cdb, sid);
2144 cnid_delete(vol->v_cdb, did);
2146 if ((did && ( (crossdev && ostat(upath, &srcst, vol_syml_opt(vol)) < 0) ||
2147 cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
2149 (sid && ( (crossdev && ostat(p, &destst, vol_syml_opt(vol)) < 0) ||
2150 cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
2155 err = AFPERR_ACCESS;
2160 goto err_temp_to_dest;
2163 /* here we need to reopen if crossdev */
2164 if (sid && ad_setid(addp, destst.st_dev, destst.st_ino, sid, sdir->d_did, vol->v_stamp))
2169 if (did && ad_setid(adsp, srcst.st_dev, srcst.st_ino, did, curdir->d_did, vol->v_stamp))
2174 /* change perms, src gets dest perm and vice versa */
2179 * we need to exchange ACL entries as well
2181 /* exchange_acls(vol, p, upath); */
2186 path->m_name = NULL;
2187 path->u_name = upath;
2189 setfilunixmode(vol, path, destst.st_mode);
2190 setfilowner(vol, destst.st_uid, destst.st_gid, path);
2197 setfilunixmode(vol, path, srcst.st_mode);
2198 setfilowner(vol, srcst.st_uid, srcst.st_gid, path);
2203 goto err_exchangefile;
2205 /* all this stuff is so that we can unwind a failed operation
2208 /* rename dest to temp */
2209 renamefile(vol, curdir, -1, upath, temp, temp, adsp);
2210 of_rename(vol, s_of, curdir, upath, curdir, temp);
2213 /* rename source back to dest */
2214 renamefile(vol, curdir, -1, p, upath, path->m_name, addp);
2215 of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
2218 /* rename temp back to source */
2219 renamefile(vol, curdir, -1, temp, p, spath, adsp);
2220 of_rename(vol, s_of, curdir, temp, sdir, spath);
2223 if ( !s_of && adsp && ad_meta_fileno(adsp) != -1 ) { /* META */
2224 ad_close(adsp, ADFLAGS_HF);
2226 if ( !d_of && addp && ad_meta_fileno(addp) != -1 ) {/* META */
2227 ad_close(addp, ADFLAGS_HF);
2231 if ((cached = dircache_search_by_did(vol, sid)) != NULL)
2232 (void)dir_remove(vol, cached);
2233 if ((cached = dircache_search_by_did(vol, did)) != NULL)
2234 (void)dir_remove(vol, cached);