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));
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(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 LOG(log_debug9, logtype_afpd, "begin setfilparams:");
847 adp = of_ad(vol, path, &ad);
848 upath = path->u_name;
850 if (!vol_unix_priv(vol) && check_access(obj, vol, upath, OPENACC_WR ) < 0) {
851 return AFPERR_ACCESS;
854 /* with unix priv maybe we have to change adouble file priv first */
856 while ( bitmap != 0 ) {
857 while (( bitmap & 1 ) == 0 ) {
864 memcpy(&ashort, buf, sizeof( ashort ));
865 buf += sizeof( ashort );
869 memcpy(&cdate, buf, sizeof(cdate));
870 buf += sizeof( cdate );
873 memcpy(&newdate, buf, sizeof( newdate ));
874 buf += sizeof( newdate );
878 memcpy(&bdate, buf, sizeof( bdate));
879 buf += sizeof( bdate );
883 memcpy(finder_buf, buf, 32 );
884 if (memcmp(buf, "slnkrhap", 8) == 0 && !S_ISLNK(path->st.st_mode)) {
888 char buf[PATH_MAX+1];
889 if ((fp = open(path->u_name, O_RDONLY)) >= 0) {
890 if ((len = read(fp, buf, PATH_MAX+1))) {
891 if (unlink(path->u_name) == 0) {
893 erc = symlink(buf, path->u_name);
902 goto setfilparam_done;
908 case FILPBIT_UNIXPR :
909 if (!vol_unix_priv(vol)) {
910 /* this volume doesn't use unix priv */
916 change_parent_mdate = 1;
918 memcpy( &aint, buf, sizeof( aint ));
919 f_uid = ntohl (aint);
920 buf += sizeof( aint );
921 memcpy( &aint, buf, sizeof( aint ));
922 f_gid = ntohl (aint);
923 buf += sizeof( aint );
924 setfilowner(vol, f_uid, f_gid, path);
926 memcpy( &upriv, buf, sizeof( upriv ));
927 buf += sizeof( upriv );
928 upriv = ntohl (upriv);
929 if ((upriv & S_IWUSR)) {
930 setfilunixmode(vol, path, upriv);
937 case FILPBIT_PDINFO :
938 if (obj->afp_version < 30) { /* else it's UTF8 name */
941 /* Keep special case to support crlf translations */
942 if ((unsigned int) achar == 0x04) {
943 fdType = (u_char *)"TEXT";
946 xyy[0] = ( u_char ) 'p';
957 /* break while loop */
966 /* second try with adouble open
968 if (ad_open(adp, upath, ADFLAGS_HF | ADFLAGS_RDWR | ADFLAGS_CREATE, 0666) < 0) {
969 LOG(log_debug, logtype_afpd, "setfilparams: ad_open_metadata error");
971 * For some things, we don't need an adouble header:
972 * - change of modification date
973 * - UNIX privs (Bug-ID #2863424)
975 if (!symlinked && f_bitmap & ~(1<<FILPBIT_MDATE | 1<<FILPBIT_UNIXPR)) {
976 LOG(log_debug, logtype_afpd, "setfilparams: need adouble access");
977 return AFPERR_ACCESS;
979 LOG(log_debug, logtype_afpd, "setfilparams: no adouble perms, but only FILPBIT_MDATE and/or FILPBIT_UNIXPR");
981 } else if ((ad_get_MD_flags( adp ) & O_CREAT) ) {
982 ad_setname(adp, path->m_name);
984 if ((id = get_id(vol, adp, &path->st, curdir->d_did, upath, strlen(upath))) == CNID_INVALID) {
985 LOG(log_error, logtype_afpd, "afp_createfile(\"%s\"): CNID error", upath);
988 (void)ad_setid(adp, path->st.st_dev, path->st.st_ino, id, curdir->d_did, vol->v_stamp);
993 while ( bitmap != 0 ) {
994 while (( bitmap & 1 ) == 0 ) {
1001 ad_getattr(adp, &bshort);
1003 if ( ntohs( ashort ) & ATTRBIT_SETCLR ) {
1004 bshort |= htons( ntohs( ashort ) & ~ATTRBIT_SETCLR );
1008 if ((bshort & htons(ATTRBIT_INVISIBLE)) != (oshort & htons(ATTRBIT_INVISIBLE)))
1009 change_parent_mdate = 1;
1010 ad_setattr(adp, bshort);
1012 case FILPBIT_CDATE :
1013 ad_setdate(adp, AD_DATE_CREATE, cdate);
1015 case FILPBIT_MDATE :
1017 case FILPBIT_BDATE :
1018 ad_setdate(adp, AD_DATE_BACKUP, bdate);
1020 case FILPBIT_FINFO :
1021 if (default_type( ad_entry( adp, ADEID_FINDERI ))
1023 ((em = getextmap( path->m_name )) &&
1024 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1025 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1026 || ((em = getdefextmap()) &&
1027 !memcmp(finder_buf, em->em_type, sizeof( em->em_type )) &&
1028 !memcmp(finder_buf + 4, em->em_creator,sizeof( em->em_creator)))
1030 memcpy(finder_buf, ufinderi, 8 );
1032 memcpy(ad_entry( adp, ADEID_FINDERI ), finder_buf, 32 );
1034 case FILPBIT_UNIXPR :
1036 setfilunixmode(vol, path, upriv);
1039 case FILPBIT_PDINFO :
1040 if (obj->afp_version < 30) { /* else it's UTF8 name */
1041 memcpy(ad_entry( adp, ADEID_FINDERI ), fdType, 4 );
1042 memcpy(ad_entry( adp, ADEID_FINDERI ) + 4, "pdos", 4 );
1047 err = AFPERR_BITMAP;
1048 goto setfilparam_done;
1055 if (change_mdate && newdate == 0 && gettimeofday(&tv, NULL) == 0) {
1056 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1060 ad_setdate(adp, AD_DATE_MODIFY, newdate);
1061 ut.actime = ut.modtime = AD_DATE_TO_UNIX(newdate);
1067 ad_close(adp, ADFLAGS_HF);
1070 if (change_parent_mdate && gettimeofday(&tv, NULL) == 0) {
1071 newdate = AD_DATE_FROM_UNIX(tv.tv_sec);
1072 bitmap = 1<<FILPBIT_MDATE;
1073 setdirparams(vol, &Cur_Path, bitmap, (char *)&newdate);
1077 LOG(log_debug9, logtype_afpd, "end setfilparams:");
1083 * renamefile and copyfile take the old and new unix pathnames
1084 * and the new mac name.
1086 * sdir_fd source dir fd to which src path is relative (for openat et al semantics)
1087 * passing -1 means this is not used, src path is a full path
1088 * src the source path
1089 * dst the dest filename in current dir
1090 * newname the dest mac name
1091 * adp adouble struct of src file, if open, or & zeroed one
1094 int renamefile(struct vol *vol, struct dir *ddir, int sdir_fd, char *src, char *dst, char *newname, struct adouble *adp)
1098 LOG(log_debug, logtype_afpd,
1099 "renamefile: src[%d, \"%s\"] -> dst[\"%s\"]", sdir_fd, src, dst);
1101 if ( unix_rename( sdir_fd, src, -1, dst ) < 0 ) {
1104 return( AFPERR_NOOBJ );
1107 return( AFPERR_ACCESS );
1109 return AFPERR_VLOCK;
1110 case EXDEV : /* Cross device move -- try copy */
1111 /* NOTE: with open file it's an error because after the copy we will
1112 * get two files, it's fixable for our process (eg reopen the new file, get the
1113 * locks, and so on. But it doesn't solve the case with a second process
1115 if (adp->ad_open_forks) {
1116 /* FIXME warning in syslog so admin'd know there's a conflict ?*/
1117 return AFPERR_OLOCK; /* little lie */
1119 if (AFP_OK != ( rc = copyfile(vol, vol, ddir, sdir_fd, src, dst, newname, NULL )) ) {
1120 /* on error copyfile delete dest */
1123 return deletefile(vol, sdir_fd, src, 0);
1125 return( AFPERR_PARAM );
1129 if (vol->vfs->vfs_renamefile(vol, sdir_fd, src, dst) < 0 ) {
1133 /* try to undo the data fork rename,
1134 * we know we are on the same device
1137 unix_rename(-1, dst, sdir_fd, src );
1138 /* return the first error */
1141 return AFPERR_NOOBJ;
1144 return AFPERR_ACCESS ;
1146 return AFPERR_VLOCK;
1148 return AFPERR_PARAM ;
1153 /* don't care if we can't open the newly renamed ressource fork */
1154 if (ad_open(adp, dst, ADFLAGS_HF | ADFLAGS_RDWR) == 0) {
1155 ad_setname(adp, newname);
1157 ad_close( adp, ADFLAGS_HF );
1164 convert a Mac long name to an utf8 name,
1166 size_t mtoUTF8(const struct vol *vol, const char *src, size_t srclen, char *dest, size_t destlen)
1170 if ((size_t)-1 == (outlen = convert_string ( vol->v_maccharset, CH_UTF8_MAC, src, srclen, dest, destlen)) ) {
1176 /* ---------------- */
1177 int copy_path_name(const struct vol *vol, char *newname, char *ibuf)
1184 if ( type != 2 && !(vol->v_obj->afp_version >= 30 && type == 3) ) {
1190 if (( plen = (unsigned char)*ibuf++ ) != 0 ) {
1191 if (vol->v_obj->afp_version >= 30) {
1192 /* convert it to UTF8
1194 if ((plen = mtoUTF8(vol, ibuf, plen, newname, AFPOBJ_TMPSIZ)) == (size_t)-1)
1198 strncpy( newname, ibuf, plen );
1199 newname[ plen ] = '\0';
1201 if (strlen(newname) != plen) {
1202 /* there's \0 in newname, e.g. it's a pathname not
1210 memcpy(&hint, ibuf, sizeof(hint));
1211 ibuf += sizeof(hint);
1213 memcpy(&len16, ibuf, sizeof(len16));
1214 ibuf += sizeof(len16);
1215 plen = ntohs(len16);
1218 if (plen > AFPOBJ_TMPSIZ) {
1221 strncpy( newname, ibuf, plen );
1222 newname[ plen ] = '\0';
1223 if (strlen(newname) != plen) {
1232 /* -----------------------------------
1234 int afp_copyfile(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1236 struct vol *s_vol, *d_vol;
1238 char *newname, *p, *upath;
1239 struct path *s_path;
1240 uint32_t sdid, ddid;
1241 int err, retvalue = AFP_OK;
1242 uint16_t svid, dvid;
1244 struct adouble ad, *adp;
1250 memcpy(&svid, ibuf, sizeof( svid ));
1251 ibuf += sizeof( svid );
1252 if (NULL == ( s_vol = getvolbyvid( svid )) ) {
1253 return( AFPERR_PARAM );
1256 memcpy(&sdid, ibuf, sizeof( sdid ));
1257 ibuf += sizeof( sdid );
1258 if (NULL == ( dir = dirlookup( s_vol, sdid )) ) {
1262 memcpy(&dvid, ibuf, sizeof( dvid ));
1263 ibuf += sizeof( dvid );
1264 memcpy(&ddid, ibuf, sizeof( ddid ));
1265 ibuf += sizeof( ddid );
1267 if (NULL == ( s_path = cname( s_vol, dir, &ibuf )) ) {
1268 return get_afp_errno(AFPERR_PARAM);
1270 if ( path_isadir(s_path) ) {
1271 return( AFPERR_BADTYPE );
1274 /* don't allow copies when the file is open.
1275 * XXX: the spec only calls for read/deny write access.
1276 * however, copyfile doesn't have any of that info,
1277 * and locks need to stay coherent. as a result,
1278 * we just balk if the file is opened already. */
1280 adp = of_ad(s_vol, s_path, &ad);
1282 if (ad_open(adp, s_path->u_name, ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | ADFLAGS_RDONLY | ADFLAGS_SETSHRMD) < 0) {
1283 return AFPERR_DENYCONF;
1285 #ifdef HAVE_FSHARE_T
1287 shmd.f_access = F_RDACC;
1288 shmd.f_deny = F_NODNY;
1289 if (fcntl(ad_data_fileno(adp), F_SHARE, &shmd) != 0) {
1290 retvalue = AFPERR_DENYCONF;
1293 if (AD_RSRC_OPEN(adp) && fcntl(ad_reso_fileno(adp), F_SHARE, &shmd) != 0) {
1294 retvalue = AFPERR_DENYCONF;
1298 denyreadset = (ad_testlock(adp, ADEID_DFORK, AD_FILELOCK_DENY_RD) != 0 ||
1299 ad_testlock(adp, ADEID_RFORK, AD_FILELOCK_DENY_RD) != 0 );
1302 retvalue = AFPERR_DENYCONF;
1306 newname = obj->newtmp;
1307 strcpy( newname, s_path->m_name );
1309 p = ctoupath( s_vol, curdir, newname );
1311 retvalue = AFPERR_PARAM;
1315 if (NULL == ( d_vol = getvolbyvid( dvid )) ) {
1316 retvalue = AFPERR_PARAM;
1320 if (d_vol->v_flags & AFPVOL_RO) {
1321 retvalue = AFPERR_VLOCK;
1325 if (NULL == ( dir = dirlookup( d_vol, ddid )) ) {
1326 retvalue = afp_errno;
1330 if (( s_path = cname( d_vol, dir, &ibuf )) == NULL ) {
1331 retvalue = get_afp_errno(AFPERR_NOOBJ);
1335 if ( *s_path->m_name != '\0' ) {
1336 retvalue =path_error(s_path, AFPERR_NOOBJ);
1340 /* one of the handful of places that knows about the path type */
1341 if (copy_path_name(d_vol, newname, ibuf) < 0) {
1342 retvalue = AFPERR_PARAM;
1345 /* newname is always only a filename so curdir *is* its
1348 if (NULL == (upath = mtoupath(d_vol, newname, curdir->d_did, utf8_encoding(d_vol->v_obj)))) {
1349 retvalue =AFPERR_PARAM;
1353 if ( (err = copyfile(s_vol, d_vol, curdir, -1, p, upath , newname, adp)) < 0 ) {
1359 setvoltime(obj, d_vol );
1362 ad_close( adp, ADFLAGS_DF |ADFLAGS_HF | ADFLAGS_SETSHRMD);
1366 /* ----------------------------------
1367 * if newname is NULL (from directory.c) we don't want to copy the resource fork.
1368 * because we are doing it elsewhere.
1369 * currently if newname is NULL then adp is NULL.
1371 int copyfile(struct vol *s_vol,
1378 struct adouble *adp)
1380 struct adouble ads, add;
1387 LOG(log_debug, logtype_afpd, "copyfile(sfd:%d,s:'%s',d:'%s',n:'%s')",
1388 sfd, src, dst, newname);
1391 ad_init(&ads, s_vol);
1395 adflags = ADFLAGS_DF | ADFLAGS_HF | ADFLAGS_NOHF | ADFLAGS_RF | ADFLAGS_NORF;
1397 if (ad_openat(adp, sfd, src, adflags | ADFLAGS_RDONLY) < 0) {
1402 if (!AD_META_OPEN(adp))
1403 /* no resource fork, don't create one for dst file */
1404 adflags &= ~ADFLAGS_HF;
1406 if (!AD_RSRC_OPEN(adp))
1407 /* no resource fork, don't create one for dst file */
1408 adflags &= ~ADFLAGS_RF;
1410 stat_result = fstat(ad_data_fileno(adp), &st); /* saving stat exit code, thus saving us on one more stat later on */
1412 if (stat_result < 0) {
1413 /* unlikely but if fstat fails, the default file mode will be 0666. */
1414 st.st_mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
1417 ad_init(&add, d_vol);
1418 if (ad_open(&add, dst, adflags | ADFLAGS_RDWR | ADFLAGS_CREATE | ADFLAGS_EXCL, st.st_mode | S_IRUSR | S_IWUSR) < 0) {
1420 ad_close( adp, adflags );
1421 if (EEXIST != ret_err) {
1422 deletefile(d_vol, -1, dst, 0);
1425 return AFPERR_EXIST;
1428 if ((err = copy_fork(ADEID_DFORK, &add, adp)) != 0)
1429 LOG(log_error, logtype_afpd, "copyfile('%s'): %s", src, strerror(errno));
1432 if ((err = d_vol->vfs->vfs_copyfile(d_vol, sfd, src, dst)) != 0)
1433 LOG(log_error, logtype_afpd, "copyfile('%s'): %s", src, strerror(errno));
1438 if (AD_META_OPEN(&add)) {
1439 if (AD_META_OPEN(adp))
1440 ad_copy_header(&add, adp);
1441 ad_setname(&add, dst);
1444 if (fstat(ad_meta_fileno(&add), &stdest) != 0) {
1448 if ((id = get_id(d_vol, &add, &stdest, d_dir->d_did, dst, strlen(dst))) == CNID_INVALID) {
1452 (void)ad_setid(&add, stdest.st_dev, stdest.st_ino, id, d_dir->d_did, d_vol->v_stamp);
1457 ad_close( adp, adflags );
1459 if (ad_close( &add, adflags ) <0) {
1464 deletefile(d_vol, -1, dst, 0);
1466 else if (stat_result == 0) {
1467 /* set dest modification date to src date */
1470 ut.actime = ut.modtime = st.st_mtime;
1472 /* FIXME netatalk doesn't use resource fork file date
1473 * but maybe we should set its modtime too.
1478 switch ( ret_err ) {
1484 LOG(log_info, logtype_afpd, "copyfile: DISK FULL");
1485 return AFPERR_DFULL;
1487 return AFPERR_NOOBJ;
1489 return AFPERR_ACCESS;
1491 return AFPERR_VLOCK;
1493 return AFPERR_PARAM;
1497 /* -----------------------------------
1498 vol: not NULL delete cnid entry. then we are in curdir and file is a only filename
1499 checkAttrib: 1 check kFPDeleteInhibitBit (deletfile called by afp_delete)
1501 when deletefile is called we don't have lock on it, file is closed (for us)
1502 untrue if called by renamefile
1504 ad_open always try to open file RDWR first and ad_lock takes care of
1505 WRITE lock on read only file.
1508 static int check_attrib(struct adouble *adp)
1510 uint16_t bshort = 0;
1512 ad_getattr(adp, &bshort);
1514 * Does kFPDeleteInhibitBit (bit 8) set?
1516 if ((bshort & htons(ATTRBIT_NODELETE))) {
1517 return AFPERR_OLOCK;
1519 if ((bshort & htons(ATTRBIT_DOPEN | ATTRBIT_ROPEN))) {
1525 * dirfd can be used for unlinkat semantics
1527 int deletefile(const struct vol *vol, int dirfd, char *file, int checkAttrib)
1530 struct adouble *adp = NULL;
1531 int adflags, err = AFP_OK;
1534 LOG(log_debug, logtype_afpd, "deletefile('%s')", file);
1538 /* was EACCESS error try to get only metadata */
1539 /* we never want to create a resource fork here, we are going to delete it
1540 * moreover sometimes deletefile is called with a no existent file and
1541 * ad_open would create a 0 byte resource fork
1543 if ( ad_metadataat(dirfd, file, ADFLAGS_CHECK_OF, &ad) == 0 ) {
1544 if ((err = check_attrib(&ad))) {
1545 ad_close(&ad, ADFLAGS_HF | ADFLAGS_CHECK_OF);
1552 /* try to open both forks at once */
1553 adflags = ADFLAGS_DF;
1554 if (ad_openat(&ad, dirfd, file, adflags | ADFLAGS_RF | ADFLAGS_NORF | ADFLAGS_RDONLY) < 0 ) {
1559 case EACCES: /* maybe it's a file with no write mode for us */
1560 break; /* was return AFPERR_ACCESS;*/
1573 if ( adp && AD_RSRC_OPEN(adp) != -1 ) { /* there's a resource fork */
1574 adflags |= ADFLAGS_RF;
1575 /* FIXME we have a pb here because we want to know if a file is open
1576 * there's a 'priority inversion' if you can't open the ressource fork RW
1577 * you can delete it if it's open because you can't get a write lock.
1579 * ADLOCK_FILELOCK means the whole ressource fork, not only after the
1582 * FIXME it doesn't work for RFORK open read only and fork open without deny mode
1584 if (ad_tmplock(&ad, ADEID_RFORK, ADLOCK_WR |ADLOCK_FILELOCK, 0, 0, 0) < 0 ) {
1590 if (adp && ad_tmplock( &ad, ADEID_DFORK, ADLOCK_WR, 0, 0, 0 ) < 0) {
1591 LOG(log_error, logtype_afpd, "deletefile('%s'): ad_tmplock error: %s", file, strerror(errno));
1593 } else if (!(err = vol->vfs->vfs_deletefile(vol, dirfd, file)) && !(err = netatalk_unlinkat(dirfd, file )) ) {
1595 if (checkAttrib && (id = cnid_get(vol->v_cdb, curdir->d_did, file, strlen(file)))) {
1596 cnid_delete(vol->v_cdb, id);
1602 ad_close(&ad, ADFLAGS_HF | ADFLAGS_CHECK_OF);
1605 ad_close( &ad, adflags ); /* ad_close removes locks if any */
1610 /* ------------------------------------ */
1611 /* return a file id */
1612 int afp_createid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1621 struct path *s_path;
1627 memcpy(&vid, ibuf, sizeof(vid));
1628 ibuf += sizeof(vid);
1630 if (NULL == ( vol = getvolbyvid( vid )) ) {
1631 return( AFPERR_PARAM);
1634 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1638 if (vol->v_flags & AFPVOL_RO)
1639 return AFPERR_VLOCK;
1641 memcpy(&did, ibuf, sizeof( did ));
1642 ibuf += sizeof(did);
1644 if (NULL == ( dir = dirlookup( vol, did )) ) {
1645 return afp_errno; /* was AFPERR_PARAM */
1648 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
1649 return get_afp_errno(AFPERR_NOOBJ); /* was AFPERR_PARAM */
1652 if ( path_isadir(s_path) ) {
1653 return( AFPERR_BADTYPE );
1656 upath = s_path->u_name;
1657 switch (s_path->st_errno) {
1659 break; /* success */
1662 return AFPERR_ACCESS;
1664 return AFPERR_NOOBJ;
1666 return AFPERR_PARAM;
1669 if ((id = cnid_lookup(vol->v_cdb, st, did, upath, len = strlen(upath)))) {
1670 memcpy(rbuf, &id, sizeof(id));
1671 *rbuflen = sizeof(id);
1672 return AFPERR_EXISTID;
1675 if ((id = get_id(vol, NULL, st, did, upath, len)) != CNID_INVALID) {
1676 memcpy(rbuf, &id, sizeof(id));
1677 *rbuflen = sizeof(id);
1684 /* ------------------------------- */
1690 static int reenumerate_loop(struct dirent *de, char *mname _U_, void *data)
1693 struct reenum *param = data;
1694 struct vol *vol = param->vol;
1695 cnid_t did = param->did;
1698 if ( lstat(de->d_name, &path.st) < 0 )
1701 /* update or add to cnid */
1702 aint = cnid_add(vol->v_cdb, &path.st, did, de->d_name, strlen(de->d_name), 0); /* ignore errors */
1707 /* --------------------
1708 * Ok the db is out of synch with the dir.
1709 * but if it's a deleted file we don't want to do it again and again.
1712 reenumerate_id(struct vol *vol, char *name, struct dir *dir)
1718 if (vol->v_cdb == NULL) {
1722 /* FIXME use of_statdir ? */
1723 if (lstat(name, &st)) {
1727 if (dirreenumerate(dir, &st)) {
1728 /* we already did it once and the dir haven't been modified */
1729 return dir->d_offcnt;
1733 data.did = dir->d_did;
1734 if ((ret = for_each_dirent(vol, name, reenumerate_loop, (void *)&data)) >= 0) {
1735 setdiroffcnt(curdir, &st, ret);
1736 dir->d_flags |= DIRF_CNID;
1742 /* ------------------------------
1743 resolve a file id */
1744 int afp_resolveid(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
1753 uint16_t vid, bitmap;
1755 static char buffer[12 + MAXPATHLEN + 1];
1756 int len = 12 + MAXPATHLEN + 1;
1761 memcpy(&vid, ibuf, sizeof(vid));
1762 ibuf += sizeof(vid);
1764 if (NULL == ( vol = getvolbyvid( vid )) ) {
1765 return( AFPERR_PARAM);
1768 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1772 memcpy(&id, ibuf, sizeof( id ));
1777 /* some MacOS versions after a catsearch do a *lot* of afp_resolveid with 0 */
1781 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1782 return AFPERR_NOID; /* was AFPERR_BADID, but help older Macs */
1785 if (NULL == ( dir = dirlookup( vol, id )) ) {
1786 return AFPERR_NOID; /* idem AFPERR_PARAM */
1788 if (movecwd(vol, dir) < 0) {
1792 return AFPERR_ACCESS;
1796 return AFPERR_PARAM;
1800 memset(&path, 0, sizeof(path));
1801 path.u_name = upath;
1802 if ( of_stat(&path) < 0 ) {
1804 /* with nfs and our working directory is deleted */
1805 if (errno == ESTALE) {
1809 if ( errno == ENOENT && !retry) {
1810 /* cnid db is out of sync, reenumerate the directory and update ids */
1811 reenumerate_id(vol, ".", dir);
1819 return AFPERR_ACCESS;
1823 return AFPERR_PARAM;
1827 /* directories are bad */
1828 if (S_ISDIR(path.st.st_mode)) {
1829 /* OS9 and OSX don't return the same error code */
1830 return (obj->afp_version >=30)?AFPERR_NOID:AFPERR_BADTYPE;
1833 memcpy(&bitmap, ibuf, sizeof(bitmap));
1834 bitmap = ntohs( bitmap );
1835 if (NULL == (path.m_name = utompath(vol, upath, cnid, utf8_encoding(obj)))) {
1839 if (AFP_OK != (err = getfilparams(obj, vol, bitmap, &path , curdir,
1840 rbuf + sizeof(bitmap), &buflen, 0))) {
1843 *rbuflen = buflen + sizeof(bitmap);
1844 memcpy(rbuf, ibuf, sizeof(bitmap));
1849 /* ------------------------------ */
1850 int afp_deleteid(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1860 static char buffer[12 + MAXPATHLEN + 1];
1861 int len = 12 + MAXPATHLEN + 1;
1866 memcpy(&vid, ibuf, sizeof(vid));
1867 ibuf += sizeof(vid);
1869 if (NULL == ( vol = getvolbyvid( vid )) ) {
1870 return( AFPERR_PARAM);
1873 if (vol->v_cdb == NULL || !(vol->v_cdb->flags & CNID_FLAG_PERSISTENT)) {
1877 if (vol->v_flags & AFPVOL_RO)
1878 return AFPERR_VLOCK;
1880 memcpy(&id, ibuf, sizeof( id ));
1884 if (NULL == (upath = cnid_resolve(vol->v_cdb, &id, buffer, len)) ) {
1888 if (NULL == ( dir = dirlookup( vol, id )) ) {
1889 if (afp_errno == AFPERR_NOOBJ) {
1893 return( AFPERR_PARAM );
1897 if ((movecwd(vol, dir) < 0) || (lstat(upath, &st) < 0)) {
1901 return AFPERR_ACCESS;
1906 /* still try to delete the id */
1910 return AFPERR_PARAM;
1913 else if (S_ISDIR(st.st_mode)) /* directories are bad */
1914 return AFPERR_BADTYPE;
1917 if (cnid_delete(vol->v_cdb, fileid)) {
1920 return AFPERR_VLOCK;
1923 return AFPERR_ACCESS;
1925 return AFPERR_PARAM;
1932 /* ------------------------------ */
1933 static struct adouble *find_adouble(const AFPObj *obj, struct vol *vol, struct path *path, struct ofork **of, struct adouble *adp)
1937 if (path->st_errno) {
1938 switch (path->st_errno) {
1940 afp_errno = AFPERR_NOID;
1944 afp_errno = AFPERR_ACCESS;
1947 afp_errno = AFPERR_PARAM;
1952 /* we use file_access both for legacy Mac perm and
1953 * for unix privilege, rename will take care of folder perms
1955 if (file_access(obj, vol, path, OPENACC_WR ) < 0) {
1956 afp_errno = AFPERR_ACCESS;
1960 if ((*of = of_findname(path))) {
1961 /* reuse struct adouble so it won't break locks */
1965 ret = ad_open(adp, path->u_name, ADFLAGS_HF | ADFLAGS_RDWR);
1967 if ( !ret && ad_reso_fileno(adp) != -1 && !(adp->ad_resource_fork.adf_flags & ( O_RDWR | O_WRONLY))) {
1969 * The user must have the Read & Write privilege for both files in order to use this command.
1971 ad_close(adp, ADFLAGS_HF);
1972 afp_errno = AFPERR_ACCESS;
1979 #define APPLETEMP ".AppleTempXXXXXX"
1981 int afp_exchangefiles(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
1983 struct stat srcst, destst;
1985 struct dir *dir, *sdir;
1986 char *spath, temp[17], *p;
1987 char *supath, *upath;
1992 struct adouble *adsp = NULL;
1993 struct adouble *addp = NULL;
1994 struct ofork *s_of = NULL;
1995 struct ofork *d_of = NULL;
2005 memcpy(&vid, ibuf, sizeof(vid));
2006 ibuf += sizeof(vid);
2008 if (NULL == ( vol = getvolbyvid( vid )) ) {
2009 return( AFPERR_PARAM);
2012 if ((vol->v_flags & AFPVOL_RO))
2013 return AFPERR_VLOCK;
2015 /* source and destination dids */
2016 memcpy(&sid, ibuf, sizeof(sid));
2017 ibuf += sizeof(sid);
2018 memcpy(&did, ibuf, sizeof(did));
2019 ibuf += sizeof(did);
2022 if (NULL == (dir = dirlookup( vol, sid )) ) {
2023 return afp_errno; /* was AFPERR_PARAM */
2026 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2027 return get_afp_errno(AFPERR_NOOBJ);
2030 if ( path_isadir(path) ) {
2031 return AFPERR_BADTYPE; /* it's a dir */
2034 /* save some stuff */
2037 spath = obj->oldtmp;
2038 supath = obj->newtmp;
2039 strcpy(spath, path->m_name);
2040 strcpy(supath, path->u_name); /* this is for the cnid changing */
2041 p = absupath( vol, sdir, supath);
2043 /* pathname too long */
2044 return AFPERR_PARAM ;
2048 if (!(adsp = find_adouble(obj, vol, path, &s_of, &ads))) {
2052 /* ***** from here we may have resource fork open **** */
2054 /* look for the source cnid. if it doesn't exist, don't worry about
2056 sid = cnid_lookup(vol->v_cdb, &srcst, sdir->d_did, supath,slen = strlen(supath));
2058 if (NULL == ( dir = dirlookup( vol, did )) ) {
2059 err = afp_errno; /* was AFPERR_PARAM */
2060 goto err_exchangefile;
2063 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
2064 err = get_afp_errno(AFPERR_NOOBJ);
2065 goto err_exchangefile;
2068 if ( path_isadir(path) ) {
2069 err = AFPERR_BADTYPE;
2070 goto err_exchangefile;
2073 /* FPExchangeFiles is the only call that can return the SameObj
2075 if ((curdir == sdir) && strcmp(spath, path->m_name) == 0) {
2076 err = AFPERR_SAMEOBJ;
2077 goto err_exchangefile;
2081 if (!(addp = find_adouble(obj, vol, path, &d_of, &add))) {
2083 goto err_exchangefile;
2087 /* they are not on the same device and at least one is open
2088 * FIXME broken for for crossdev and adouble v2
2091 crossdev = (srcst.st_dev != destst.st_dev);
2092 if (/* (d_of || s_of) && */ crossdev) {
2094 goto err_exchangefile;
2097 /* look for destination id. */
2098 upath = path->u_name;
2099 did = cnid_lookup(vol->v_cdb, &destst, curdir->d_did, upath, dlen = strlen(upath));
2101 /* construct a temp name.
2102 * NOTE: the temp file will be in the dest file's directory. it
2103 * will also be inaccessible from AFP. */
2104 memcpy(temp, APPLETEMP, sizeof(APPLETEMP));
2105 if (!mktemp(temp)) {
2107 goto err_exchangefile;
2111 /* FIXME we need to close fork for copy, both s_of and d_of are null */
2112 ad_close(adsp, ADFLAGS_HF);
2113 ad_close(addp, ADFLAGS_HF);
2116 /* now, quickly rename the file. we error if we can't. */
2117 if ((err = renamefile(vol, curdir, -1, p, temp, temp, adsp)) != AFP_OK)
2118 goto err_exchangefile;
2119 of_rename(vol, s_of, sdir, spath, curdir, temp);
2121 /* rename destination to source */
2122 if ((err = renamefile(vol, curdir, -1, upath, p, spath, addp)) != AFP_OK)
2123 goto err_src_to_tmp;
2124 of_rename(vol, d_of, curdir, path->m_name, sdir, spath);
2126 /* rename temp to destination */
2127 if ((err = renamefile(vol, curdir, -1, temp, upath, path->m_name, adsp)) != AFP_OK)
2128 goto err_dest_to_src;
2129 of_rename(vol, s_of, curdir, temp, curdir, path->m_name);
2131 /* id's need switching. src -> dest and dest -> src.
2132 * we need to re-stat() if it was a cross device copy.
2135 cnid_delete(vol->v_cdb, sid);
2137 cnid_delete(vol->v_cdb, did);
2139 if ((did && ( (crossdev && lstat( upath, &srcst) < 0) ||
2140 cnid_update(vol->v_cdb, did, &srcst, curdir->d_did,upath, dlen) < 0))
2142 (sid && ( (crossdev && lstat(p, &destst) < 0) ||
2143 cnid_update(vol->v_cdb, sid, &destst, sdir->d_did,supath, slen) < 0))
2148 err = AFPERR_ACCESS;
2153 goto err_temp_to_dest;
2156 /* here we need to reopen if crossdev */
2157 if (sid && ad_setid(addp, destst.st_dev, destst.st_ino, sid, sdir->d_did, vol->v_stamp))
2162 if (did && ad_setid(adsp, srcst.st_dev, srcst.st_ino, did, curdir->d_did, vol->v_stamp))
2167 /* change perms, src gets dest perm and vice versa */
2172 * we need to exchange ACL entries as well
2174 /* exchange_acls(vol, p, upath); */
2179 path->m_name = NULL;
2180 path->u_name = upath;
2182 setfilunixmode(vol, path, destst.st_mode);
2183 setfilowner(vol, destst.st_uid, destst.st_gid, path);
2190 setfilunixmode(vol, path, srcst.st_mode);
2191 setfilowner(vol, srcst.st_uid, srcst.st_gid, path);
2196 goto err_exchangefile;
2198 /* all this stuff is so that we can unwind a failed operation
2201 /* rename dest to temp */
2202 renamefile(vol, curdir, -1, upath, temp, temp, adsp);
2203 of_rename(vol, s_of, curdir, upath, curdir, temp);
2206 /* rename source back to dest */
2207 renamefile(vol, curdir, -1, p, upath, path->m_name, addp);
2208 of_rename(vol, d_of, sdir, spath, curdir, path->m_name);
2211 /* rename temp back to source */
2212 renamefile(vol, curdir, -1, temp, p, spath, adsp);
2213 of_rename(vol, s_of, curdir, temp, sdir, spath);
2216 if ( !s_of && adsp && ad_meta_fileno(adsp) != -1 ) { /* META */
2217 ad_close(adsp, ADFLAGS_HF);
2219 if ( !d_of && addp && ad_meta_fileno(addp) != -1 ) {/* META */
2220 ad_close(addp, ADFLAGS_HF);
2224 if ((cached = dircache_search_by_did(vol, sid)) != NULL)
2225 (void)dir_remove(vol, cached);
2226 if ((cached = dircache_search_by_did(vol, did)) != NULL)
2227 (void)dir_remove(vol, cached);