5 * afp_XXXcomment are (the only) functions able to open
6 * a ressource fork when there's no data fork, eg after
7 * it was removed with samba.
12 #endif /* HAVE_CONFIG_H */
20 #include <atalk/adouble.h>
22 #include <sys/param.h>
23 #include <sys/socket.h>
24 #include <arpa/inet.h>
26 #include <atalk/dsi.h>
27 #include <atalk/afp.h>
28 #include <atalk/util.h>
29 #include <atalk/logger.h>
30 #include <atalk/globals.h>
31 #include <atalk/netatalk_conf.h>
32 #include <atalk/unix.h>
33 #include <atalk/bstrlib.h>
36 #include "directory.h"
41 static void create_appledesktop_folder(const struct vol * vol)
46 dtpath = bfromcstr(vol->v_dbpath);
47 bcatcstr(dtpath, ".");
49 if (stat(bdata(dtpath), &st) != 0) {
51 mkdir(bdata(dtpath), 0777);
58 int afp_opendt(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
65 memcpy( &vid, ibuf, sizeof(vid));
66 if (NULL == ( vol = getvolbyvid( vid )) ) {
68 return( AFPERR_PARAM );
71 create_appledesktop_folder(vol);
73 memcpy( rbuf, &vid, sizeof(vid));
74 *rbuflen = sizeof(vid);
78 int afp_closedt(AFPObj *obj _U_, char *ibuf _U_, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
84 static struct savedt si = { { 0, 0, 0, 0 }, -1, 0, 0 };
86 static char *icon_dtfile(struct vol *vol, u_char creator[ 4 ])
88 return dtfile( vol, creator, ".icon" );
91 static int iconopen(struct vol *vol, u_char creator[ 4 ], int flags, int mode)
93 char *dtf, *adt, *adts;
95 if ( si.sdt_fd != -1 ) {
96 if ( memcmp( si.sdt_creator, creator, sizeof( CreatorType )) == 0 &&
97 si.sdt_vid == vol->v_vid ) {
104 dtf = icon_dtfile( vol, creator);
106 if (( si.sdt_fd = open( dtf, flags, ad_mode( dtf, mode ))) < 0 ) {
107 if ( errno == ENOENT && ( flags & O_CREAT )) {
108 if (( adts = strrchr( dtf, '/' )) == NULL ) {
112 if (( adt = strrchr( dtf, '/' )) == NULL ) {
116 (void) ad_mkdir( dtf, DIRBITS | 0777 );
118 (void) ad_mkdir( dtf, DIRBITS | 0777 );
121 if (( si.sdt_fd = open( dtf, flags, ad_mode( dtf, mode ))) < 0 ) {
122 LOG(log_error, logtype_afpd, "iconopen(%s): open: %s", dtf, strerror(errno) );
130 memcpy( si.sdt_creator, creator, sizeof( CreatorType ));
131 si.sdt_vid = vol->v_vid;
136 int afp_addicon(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
139 u_char fcreator[ 4 ], imh[ 12 ], irh[ 12 ], *p;
140 int itype, cc = AFP_OK, iovcnt = 0;
142 uint32_t ftype, itag;
143 uint16_t bsize, rsize, vid;
149 memcpy( &vid, ibuf, sizeof( vid ));
150 ibuf += sizeof( vid );
151 if (NULL == ( vol = getvolbyvid( vid )) ) {
156 memcpy( fcreator, ibuf, sizeof( fcreator ));
157 ibuf += sizeof( fcreator );
159 memcpy( &ftype, ibuf, sizeof( ftype ));
160 ibuf += sizeof( ftype );
162 itype = (unsigned char) *ibuf;
165 memcpy( &itag, ibuf, sizeof( itag ));
166 ibuf += sizeof( itag );
168 memcpy( &bsize, ibuf, sizeof( bsize ));
169 bsize = ntohs( bsize );
171 if ( si.sdt_fd != -1 ) {
172 (void)close( si.sdt_fd );
176 if (iconopen( vol, fcreator, O_RDWR|O_CREAT, 0666 ) < 0) {
181 if (lseek( si.sdt_fd, (off_t) 0L, SEEK_SET ) < 0) {
184 LOG(log_error, logtype_afpd, "afp_addicon(%s): lseek: %s", icon_dtfile(vol, fcreator), strerror(errno) );
190 * Read icon elements until we find a match to replace, or
191 * we get to the end to insert.
194 memcpy( p, &itag, sizeof( itag ));
196 memcpy( p, &ftype, sizeof( ftype ));
197 p += sizeof( ftype );
200 bsize = htons( bsize );
201 memcpy( p, &bsize, sizeof( bsize ));
202 bsize = ntohs( bsize );
203 while (( cc = read( si.sdt_fd, irh, sizeof( irh ))) > 0 ) {
204 memcpy( &rsize, irh + 10, sizeof( rsize ));
205 rsize = ntohs( rsize );
207 * Is this our set of headers?
209 if ( memcmp( irh, imh, sizeof( irh ) - sizeof( u_short )) == 0 ) {
211 * Is the size correct?
213 if ( bsize != rsize )
218 if ( lseek( si.sdt_fd, (off_t) rsize, SEEK_CUR ) < 0 ) {
219 LOG(log_error, logtype_afpd, "afp_addicon(%s): lseek: %s", icon_dtfile(vol, fcreator),strerror(errno) );
225 * Some error occurred, return.
229 dsi_writeinit(obj->dsi, rbuf, buflen);
230 dsi_writeflush(obj->dsi);
236 iovcnt = dsi_writeinit(dsi, rbuf, buflen);
238 /* add headers at end of file */
239 if ((cc == 0) && (write(si.sdt_fd, imh, sizeof(imh)) < 0)) {
240 LOG(log_error, logtype_afpd, "afp_addicon(%s): write: %s", icon_dtfile(vol, fcreator), strerror(errno));
245 if ((cc = write(si.sdt_fd, rbuf, iovcnt)) < 0) {
246 LOG(log_error, logtype_afpd, "afp_addicon(%s): write: %s", icon_dtfile(vol, fcreator), strerror(errno));
251 while ((iovcnt = dsi_write(dsi, rbuf, buflen))) {
252 if ((cc = write(si.sdt_fd, rbuf, iovcnt)) < 0) {
253 LOG(log_error, logtype_afpd, "afp_addicon(%s): write: %s", icon_dtfile(vol, fcreator), strerror(errno));
264 static const u_char utag[] = { 0, 0, 0, 0 };
265 static const u_char ucreator[] = { 0, 0, 0, 0 };/* { 'U', 'N', 'I', 'X' };*/
266 static const u_char utype[] = { 0, 0, 0, 0 };/* { 'T', 'E', 'X', 'T' };*/
267 static const short usize = 256;
270 static const u_char uicon[] = {
271 0x1F, 0xFF, 0xFC, 0x00, 0x10, 0x00, 0x06, 0x00,
272 0x10, 0x00, 0x05, 0x00, 0x10, 0x00, 0x04, 0x80,
273 0x10, 0x00, 0x04, 0x40, 0x10, 0x00, 0x04, 0x20,
274 0x10, 0x00, 0x07, 0xF0, 0x10, 0x00, 0x00, 0x10,
275 0x10, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x10,
276 0x10, 0x00, 0x00, 0x10, 0x10, 0x80, 0x02, 0x10,
277 0x11, 0x80, 0x03, 0x10, 0x12, 0x80, 0x02, 0x90,
278 0x12, 0x80, 0x02, 0x90, 0x14, 0x80, 0x02, 0x50,
279 0x14, 0x87, 0xC2, 0x50, 0x14, 0x58, 0x34, 0x50,
280 0x14, 0x20, 0x08, 0x50, 0x12, 0x16, 0xD0, 0x90,
281 0x11, 0x01, 0x01, 0x10, 0x12, 0x80, 0x02, 0x90,
282 0x12, 0x9C, 0x72, 0x90, 0x14, 0x22, 0x88, 0x50,
283 0x14, 0x41, 0x04, 0x50, 0x14, 0x49, 0x24, 0x50,
284 0x14, 0x55, 0x54, 0x50, 0x14, 0x5D, 0x74, 0x50,
285 0x14, 0x5D, 0x74, 0x50, 0x12, 0x49, 0x24, 0x90,
286 0x12, 0x22, 0x88, 0x90, 0x1F, 0xFF, 0xFF, 0xF0,
287 0x1F, 0xFF, 0xFC, 0x00, 0x1F, 0xFF, 0xFE, 0x00,
288 0x1F, 0xFF, 0xFF, 0x00, 0x1F, 0xFF, 0xFF, 0x80,
289 0x1F, 0xFF, 0xFF, 0xC0, 0x1F, 0xFF, 0xFF, 0xE0,
290 0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
291 0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
292 0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
293 0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
294 0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
295 0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
296 0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
297 0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
298 0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
299 0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
300 0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
301 0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
302 0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
306 int afp_geticoninfo(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
309 unsigned char fcreator[ 4 ], ih[ 12 ];
310 uint16_t vid, iindex, bsize;
315 memcpy( &vid, ibuf, sizeof( vid ));
316 ibuf += sizeof( vid );
317 if (NULL == ( vol = getvolbyvid( vid )) ) {
318 return( AFPERR_PARAM );
321 memcpy( fcreator, ibuf, sizeof( fcreator ));
322 ibuf += sizeof( fcreator );
323 memcpy( &iindex, ibuf, sizeof( iindex ));
324 iindex = ntohs( iindex );
326 if ( memcmp( fcreator, ucreator, sizeof( ucreator )) == 0 ) {
328 return( AFPERR_NOITEM );
330 memcpy( ih, utag, sizeof( utag ));
331 memcpy( ih + sizeof( utag ), utype, sizeof( utype ));
332 *( ih + sizeof( utag ) + sizeof( utype )) = 1;
333 *( ih + sizeof( utag ) + sizeof( utype ) + 1 ) = 0;
334 memcpy( ih + sizeof( utag ) + sizeof( utype ) + 2, &usize,
336 memcpy( rbuf, ih, sizeof( ih ));
337 *rbuflen = sizeof( ih );
341 if ( iconopen( vol, fcreator, O_RDONLY, 0 ) < 0) {
342 return( AFPERR_NOITEM );
345 if ( iindex < si.sdt_index ) {
346 if ( lseek( si.sdt_fd, (off_t) 0L, SEEK_SET ) < 0 ) {
347 return( AFPERR_PARAM );
353 * Position to the correct spot.
356 if ( read( si.sdt_fd, ih, sizeof( ih )) != sizeof( ih )) {
359 return( AFPERR_NOITEM );
361 memcpy( &bsize, ih + 10, sizeof( bsize ));
362 bsize = ntohs(bsize);
363 if ( lseek( si.sdt_fd, (off_t) bsize, SEEK_CUR ) < 0 ) {
364 LOG(log_error, logtype_afpd, "afp_iconinfo(%s): lseek: %s", icon_dtfile(vol, fcreator), strerror(errno) );
365 return( AFPERR_PARAM );
367 if ( si.sdt_index == iindex ) {
368 memcpy( rbuf, ih, sizeof( ih ));
369 *rbuflen = sizeof( ih );
377 int afp_geticon(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
382 u_char fcreator[ 4 ], ftype[ 4 ], itype, ih[ 12 ];
383 uint16_t vid, bsize, rsize;
389 memcpy( &vid, ibuf, sizeof( vid ));
390 ibuf += sizeof( vid );
391 if (NULL == ( vol = getvolbyvid( vid )) ) {
392 return( AFPERR_PARAM );
395 memcpy( fcreator, ibuf, sizeof( fcreator ));
396 ibuf += sizeof( fcreator );
397 memcpy( ftype, ibuf, sizeof( ftype ));
398 ibuf += sizeof( ftype );
399 itype = (unsigned char) *ibuf++;
401 memcpy( &bsize, ibuf, sizeof( bsize ));
402 bsize = ntohs( bsize );
405 if ( memcmp( fcreator, ucreator, sizeof( ucreator )) == 0 &&
406 memcmp( ftype, utype, sizeof( utype )) == 0 &&
409 memcpy( rbuf, uicon, bsize);
415 if ( iconopen( vol, fcreator, O_RDONLY, 0 ) < 0) {
416 return( AFPERR_NOITEM );
419 if ( lseek( si.sdt_fd, (off_t) 0L, SEEK_SET ) < 0 ) {
422 LOG(log_error, logtype_afpd, "afp_geticon(%s): lseek: %s", icon_dtfile(vol, fcreator), strerror(errno));
423 return( AFPERR_PARAM );
428 while (( rc = read( si.sdt_fd, ih, sizeof( ih ))) > 0 ) {
430 offset += sizeof(ih);
431 if ( memcmp( ih + sizeof( int ), ftype, sizeof( ftype )) == 0 &&
432 *(ih + sizeof( int ) + sizeof( ftype )) == itype ) {
435 memcpy( &rsize, ih + 10, sizeof( rsize ));
436 rsize = ntohs( rsize );
437 if ( lseek( si.sdt_fd, (off_t) rsize, SEEK_CUR ) < 0 ) {
438 LOG(log_error, logtype_afpd, "afp_geticon(%s): lseek: %s", icon_dtfile(vol, fcreator), strerror(errno) );
439 return( AFPERR_PARAM );
445 LOG(log_error, logtype_afpd, "afp_geticon(%s): read: %s", icon_dtfile(vol, fcreator), strerror(errno));
446 return( AFPERR_PARAM );
450 return( AFPERR_NOITEM );
453 memcpy( &rsize, ih + 10, sizeof( rsize ));
454 rsize = ntohs( rsize );
455 #define min(a,b) ((a)<(b)?(a):(b))
456 rc = min( bsize, rsize );
463 size = (fstat(si.sdt_fd, &st) < 0) ? 0 : st.st_size;
464 if (size < rc + offset) {
468 if ((buflen = dsi_readinit(dsi, rbuf, buflen, rc, AFP_OK)) < 0)
472 /* do to the streaming nature, we have to exit if we encounter
473 * a problem. much confusion results otherwise. */
474 while (*rbuflen > 0) {
476 if (dsi_stream_read_file(dsi, si.sdt_fd, offset, dsi->datasize) < 0) {
479 case EINVAL: /* there's no guarantee that all fs support sendfile */
490 buflen = read(si.sdt_fd, rbuf, *rbuflen);
494 /* dsi_read() also returns buffer size of next allocation */
495 buflen = dsi_read(dsi, rbuf, buflen); /* send it off */
506 LOG(log_error, logtype_afpd, "afp_geticon(%s): %s", icon_dtfile(vol, fcreator), strerror(errno));
508 obj->exit(EXITERR_SYS);
512 if ( read( si.sdt_fd, rbuf, rc ) < rc ) {
513 return( AFPERR_PARAM );
520 /* ---------------------- */
521 static const char hexdig[] = "0123456789abcdef";
522 char *dtfile(const struct vol *vol, u_char creator[], char *ext )
524 static char path[ MAXPATHLEN + 1];
528 strcpy( path, vol->v_dbpath );
529 strcat( path, "/" APPLEDESKTOP "/" );
530 for ( p = path; *p != '\0'; p++ )
533 if ( !isprint( creator[ 0 ] ) || creator[ 0 ] == '/' ) {
534 *p++ = hexdig[ ( creator[ 0 ] & 0xf0 ) >> 4 ];
535 *p++ = hexdig[ creator[ 0 ] & 0x0f ];
542 for ( i = 0; i < sizeof( CreatorType ); i++ ) {
543 if ( !isprint( creator[ i ] ) || creator[ i ] == '/' ) {
544 *p++ = hexdig[ ( creator[ i ] & 0xf0 ) >> 4 ];
545 *p++ = hexdig[ creator[ i ] & 0x0f ];
556 /* ---------------------------
557 * mpath is only a filename
558 * did filename parent directory ID.
561 char *mtoupath(const struct vol *vol, char *mpath, cnid_t did, int utf8)
563 static char upath[ MAXPATHLEN + 2]; /* for convert_charset dest_len parameter +2 */
569 if ( *mpath == '\0' ) {
574 /* set conversion flags */
575 flags = vol->v_mtou_flags;
577 m = demangle(vol, mpath, did);
588 if ((size_t)-1 == (outlen = convert_charset ( (utf8)?CH_UTF8_MAC:vol->v_maccharset, vol->v_volcharset, vol->v_maccharset, m, inplen, u, outlen, &flags)) ) {
589 LOG(log_error, logtype_afpd, "conversion from %s to %s for %s failed.", (utf8)?"UTF8-MAC":vol->v_maccodepage, vol->v_volcodepage, mpath);
594 LOG(log_debug9, logtype_afpd, "mtoupath: '%s':'%s'", mpath, upath);
602 char *utompath(const struct vol *vol, char *upath, cnid_t id, int utf8)
604 static char mpath[ MAXPATHLEN + 2]; /* for convert_charset dest_len parameter +2 */
610 outlen = strlen(upath);
612 flags = vol->v_utom_flags;
616 /* convert charsets */
617 if ((size_t)-1 == ( outlen = convert_charset ( vol->v_volcharset, (utf8)?CH_UTF8_MAC:vol->v_maccharset, vol->v_maccharset, u, outlen, mpath, MAXPATHLEN, &flags)) ) {
618 LOG(log_error, logtype_afpd, "Conversion from %s to %s for %s (%u) failed.", vol->v_volcodepage, vol->v_maccodepage, u, ntohl(id));
622 flags = !!(flags & CONV_REQMANGLE);
627 m = mangle(vol, mpath, outlen, upath, id, flags);
630 LOG(log_debug9, logtype_afpd, "utompath: '%s':'%s':'%2.2X'", upath, m, ntohl(id));
636 m = mangle(vol, u, strlen(u), upath, id, (utf8)?3:1);
640 /* ------------------------- */
641 static int ad_addcomment(const AFPObj *obj, struct vol *vol, struct path *path, char *ibuf)
647 struct adouble ad, *adp;
649 clen = (u_char)*ibuf++;
650 clen = min( clen, 199 );
652 upath = path->u_name;
653 if (check_access(obj, vol, upath, OPENACC_WR ) < 0) {
654 return AFPERR_ACCESS;
657 isadir = path_isadir(path);
658 if (isadir || !(of = of_findname(path))) {
664 if (ad_open(adp, upath,
665 ADFLAGS_HF | ( (isadir) ? ADFLAGS_DIR : 0) | ADFLAGS_CREATE | ADFLAGS_RDWR,
667 return( AFPERR_ACCESS );
670 if (ad_getentryoff(adp, ADEID_COMMENT)) {
671 if ( (ad_get_MD_flags( adp ) & O_CREAT) ) {
672 if ( *path->m_name == '\0' ) {
673 name = (char *)curdir->d_m_name->data;
677 ad_setname(adp, name);
679 ad_setentrylen( adp, ADEID_COMMENT, clen );
680 memcpy( ad_entry( adp, ADEID_COMMENT ), ibuf, clen );
683 ad_close(adp, ADFLAGS_HF);
687 /* ----------------------------- */
688 int afp_addcomment(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
699 memcpy( &vid, ibuf, sizeof( vid ));
700 ibuf += sizeof( vid );
701 if (NULL == ( vol = getvolbyvid( vid )) ) {
702 return( AFPERR_PARAM );
705 memcpy( &did, ibuf, sizeof( did ));
706 ibuf += sizeof( did );
707 if (NULL == ( dir = dirlookup( vol, did )) ) {
711 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
712 return get_afp_errno(AFPERR_NOOBJ);
715 if ((u_long)ibuf & 1 ) {
719 return ad_addcomment(obj, vol, path, ibuf);
722 /* -------------------- */
723 static int ad_getcomment(struct vol *vol, struct path *path, char *rbuf, size_t *rbuflen)
725 struct adouble ad, *adp;
731 upath = path->u_name;
732 isadir = path_isadir(path);
733 if (isadir || !(of = of_findname(path))) {
739 if ( ad_metadata( upath, ((isadir) ? ADFLAGS_DIR : 0), adp) < 0 ) {
740 return( AFPERR_NOITEM );
743 if (!ad_getentryoff(adp, ADEID_COMMENT)) {
744 ad_close(adp, ADFLAGS_HF);
745 return AFPERR_NOITEM;
748 * Make sure the AD file is not bogus.
750 if ( ad_getentrylen( adp, ADEID_COMMENT ) <= 0 ||
751 ad_getentrylen( adp, ADEID_COMMENT ) > 199 ) {
752 ad_close(adp, ADFLAGS_HF);
753 return( AFPERR_NOITEM );
756 clen = min( ad_getentrylen( adp, ADEID_COMMENT ), 128 ); /* OSX only use 128, greater kill Adobe CS2 */
758 memcpy( rbuf, ad_entry( adp, ADEID_COMMENT ), clen);
760 ad_close(adp, ADFLAGS_HF);
765 /* -------------------- */
766 int afp_getcomment(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
777 memcpy( &vid, ibuf, sizeof( vid ));
778 ibuf += sizeof( vid );
779 if (NULL == ( vol = getvolbyvid( vid )) ) {
780 return( AFPERR_PARAM );
783 memcpy( &did, ibuf, sizeof( did ));
784 ibuf += sizeof( did );
785 if (NULL == ( dir = dirlookup( vol, did )) ) {
789 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
790 return get_afp_errno(AFPERR_NOOBJ);
793 return ad_getcomment(vol, s_path, rbuf, rbuflen);
796 /* ----------------------- */
797 static int ad_rmvcomment(const AFPObj *obj, struct vol *vol, struct path *path)
799 struct adouble ad, *adp;
804 upath = path->u_name;
805 if (check_access(obj, vol, upath, OPENACC_WR ) < 0) {
806 return AFPERR_ACCESS;
809 isadir = path_isadir(path);
810 if (isadir || !(of = of_findname(path))) {
816 if ( ad_open(adp, upath, ADFLAGS_HF | ADFLAGS_RDWR | ((isadir) ? ADFLAGS_DIR : 0)) < 0 ) {
819 return( AFPERR_NOITEM );
821 return( AFPERR_ACCESS );
823 return( AFPERR_PARAM );
827 if (ad_getentryoff(adp, ADEID_COMMENT)) {
828 ad_setentrylen( adp, ADEID_COMMENT, 0 );
831 ad_close(adp, ADFLAGS_HF);
835 /* ----------------------- */
836 int afp_rmvcomment(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
847 memcpy( &vid, ibuf, sizeof( vid ));
848 ibuf += sizeof( vid );
849 if (NULL == ( vol = getvolbyvid( vid )) ) {
850 return( AFPERR_PARAM );
853 memcpy( &did, ibuf, sizeof( did ));
854 ibuf += sizeof( did );
855 if (NULL == ( dir = dirlookup( vol, did )) ) {
859 if (NULL == ( s_path = cname( vol, dir, &ibuf ))) {
860 return get_afp_errno(AFPERR_NOOBJ);
863 return ad_rmvcomment(obj, vol, s_path);