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>
34 #include "directory.h"
40 int afp_opendt(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
47 memcpy( &vid, ibuf, sizeof(vid));
48 if (NULL == ( vol = getvolbyvid( vid )) ) {
50 return( AFPERR_PARAM );
53 memcpy( rbuf, &vid, sizeof(vid));
54 *rbuflen = sizeof(vid);
58 int afp_closedt(AFPObj *obj _U_, char *ibuf _U_, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
64 static struct savedt si = { { 0, 0, 0, 0 }, -1, 0, 0 };
66 static char *icon_dtfile(struct vol *vol, u_char creator[ 4 ])
68 return dtfile( vol, creator, ".icon" );
71 static int iconopen(struct vol *vol, u_char creator[ 4 ], int flags, int mode)
73 char *dtf, *adt, *adts;
75 if ( si.sdt_fd != -1 ) {
76 if ( memcmp( si.sdt_creator, creator, sizeof( CreatorType )) == 0 &&
77 si.sdt_vid == vol->v_vid ) {
84 dtf = icon_dtfile( vol, creator);
86 if (( si.sdt_fd = open( dtf, flags, ad_mode( dtf, mode ))) < 0 ) {
87 if ( errno == ENOENT && ( flags & O_CREAT )) {
88 if (( adts = strrchr( dtf, '/' )) == NULL ) {
92 if (( adt = strrchr( dtf, '/' )) == NULL ) {
96 (void) ad_mkdir( dtf, DIRBITS | 0777 );
98 (void) ad_mkdir( dtf, DIRBITS | 0777 );
101 if (( si.sdt_fd = open( dtf, flags, ad_mode( dtf, mode ))) < 0 ) {
102 LOG(log_error, logtype_afpd, "iconopen(%s): open: %s", dtf, strerror(errno) );
110 memcpy( si.sdt_creator, creator, sizeof( CreatorType ));
111 si.sdt_vid = vol->v_vid;
116 int afp_addicon(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
119 u_char fcreator[ 4 ], imh[ 12 ], irh[ 12 ], *p;
120 int itype, cc = AFP_OK, iovcnt = 0;
122 uint32_t ftype, itag;
123 uint16_t bsize, rsize, vid;
129 memcpy( &vid, ibuf, sizeof( vid ));
130 ibuf += sizeof( vid );
131 if (NULL == ( vol = getvolbyvid( vid )) ) {
136 memcpy( fcreator, ibuf, sizeof( fcreator ));
137 ibuf += sizeof( fcreator );
139 memcpy( &ftype, ibuf, sizeof( ftype ));
140 ibuf += sizeof( ftype );
142 itype = (unsigned char) *ibuf;
145 memcpy( &itag, ibuf, sizeof( itag ));
146 ibuf += sizeof( itag );
148 memcpy( &bsize, ibuf, sizeof( bsize ));
149 bsize = ntohs( bsize );
151 if ( si.sdt_fd != -1 ) {
152 (void)close( si.sdt_fd );
156 if (iconopen( vol, fcreator, O_RDWR|O_CREAT, 0666 ) < 0) {
161 if (lseek( si.sdt_fd, (off_t) 0L, SEEK_SET ) < 0) {
164 LOG(log_error, logtype_afpd, "afp_addicon(%s): lseek: %s", icon_dtfile(vol, fcreator), strerror(errno) );
170 * Read icon elements until we find a match to replace, or
171 * we get to the end to insert.
174 memcpy( p, &itag, sizeof( itag ));
176 memcpy( p, &ftype, sizeof( ftype ));
177 p += sizeof( ftype );
180 bsize = htons( bsize );
181 memcpy( p, &bsize, sizeof( bsize ));
182 bsize = ntohs( bsize );
183 while (( cc = read( si.sdt_fd, irh, sizeof( irh ))) > 0 ) {
184 memcpy( &rsize, irh + 10, sizeof( rsize ));
185 rsize = ntohs( rsize );
187 * Is this our set of headers?
189 if ( memcmp( irh, imh, sizeof( irh ) - sizeof( u_short )) == 0 ) {
191 * Is the size correct?
193 if ( bsize != rsize )
198 if ( lseek( si.sdt_fd, (off_t) rsize, SEEK_CUR ) < 0 ) {
199 LOG(log_error, logtype_afpd, "afp_addicon(%s): lseek: %s", icon_dtfile(vol, fcreator),strerror(errno) );
205 * Some error occurred, return.
209 dsi_writeinit(obj->dsi, rbuf, buflen);
210 dsi_writeflush(obj->dsi);
216 iovcnt = dsi_writeinit(dsi, rbuf, buflen);
218 /* add headers at end of file */
219 if ((cc == 0) && (write(si.sdt_fd, imh, sizeof(imh)) < 0)) {
220 LOG(log_error, logtype_afpd, "afp_addicon(%s): write: %s", icon_dtfile(vol, fcreator), strerror(errno));
225 if ((cc = write(si.sdt_fd, rbuf, iovcnt)) < 0) {
226 LOG(log_error, logtype_afpd, "afp_addicon(%s): write: %s", icon_dtfile(vol, fcreator), strerror(errno));
231 while ((iovcnt = dsi_write(dsi, rbuf, buflen))) {
232 if ((cc = write(si.sdt_fd, rbuf, iovcnt)) < 0) {
233 LOG(log_error, logtype_afpd, "afp_addicon(%s): write: %s", icon_dtfile(vol, fcreator), strerror(errno));
244 static const u_char utag[] = { 0, 0, 0, 0 };
245 static const u_char ucreator[] = { 0, 0, 0, 0 };/* { 'U', 'N', 'I', 'X' };*/
246 static const u_char utype[] = { 0, 0, 0, 0 };/* { 'T', 'E', 'X', 'T' };*/
247 static const short usize = 256;
250 static const u_char uicon[] = {
251 0x1F, 0xFF, 0xFC, 0x00, 0x10, 0x00, 0x06, 0x00,
252 0x10, 0x00, 0x05, 0x00, 0x10, 0x00, 0x04, 0x80,
253 0x10, 0x00, 0x04, 0x40, 0x10, 0x00, 0x04, 0x20,
254 0x10, 0x00, 0x07, 0xF0, 0x10, 0x00, 0x00, 0x10,
255 0x10, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x10,
256 0x10, 0x00, 0x00, 0x10, 0x10, 0x80, 0x02, 0x10,
257 0x11, 0x80, 0x03, 0x10, 0x12, 0x80, 0x02, 0x90,
258 0x12, 0x80, 0x02, 0x90, 0x14, 0x80, 0x02, 0x50,
259 0x14, 0x87, 0xC2, 0x50, 0x14, 0x58, 0x34, 0x50,
260 0x14, 0x20, 0x08, 0x50, 0x12, 0x16, 0xD0, 0x90,
261 0x11, 0x01, 0x01, 0x10, 0x12, 0x80, 0x02, 0x90,
262 0x12, 0x9C, 0x72, 0x90, 0x14, 0x22, 0x88, 0x50,
263 0x14, 0x41, 0x04, 0x50, 0x14, 0x49, 0x24, 0x50,
264 0x14, 0x55, 0x54, 0x50, 0x14, 0x5D, 0x74, 0x50,
265 0x14, 0x5D, 0x74, 0x50, 0x12, 0x49, 0x24, 0x90,
266 0x12, 0x22, 0x88, 0x90, 0x1F, 0xFF, 0xFF, 0xF0,
267 0x1F, 0xFF, 0xFC, 0x00, 0x1F, 0xFF, 0xFE, 0x00,
268 0x1F, 0xFF, 0xFF, 0x00, 0x1F, 0xFF, 0xFF, 0x80,
269 0x1F, 0xFF, 0xFF, 0xC0, 0x1F, 0xFF, 0xFF, 0xE0,
270 0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
271 0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
272 0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
273 0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
274 0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
275 0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
276 0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
277 0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
278 0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
279 0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
280 0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
281 0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
282 0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
286 int afp_geticoninfo(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
289 unsigned char fcreator[ 4 ], ih[ 12 ];
290 uint16_t vid, iindex, bsize;
295 memcpy( &vid, ibuf, sizeof( vid ));
296 ibuf += sizeof( vid );
297 if (NULL == ( vol = getvolbyvid( vid )) ) {
298 return( AFPERR_PARAM );
301 memcpy( fcreator, ibuf, sizeof( fcreator ));
302 ibuf += sizeof( fcreator );
303 memcpy( &iindex, ibuf, sizeof( iindex ));
304 iindex = ntohs( iindex );
306 if ( memcmp( fcreator, ucreator, sizeof( ucreator )) == 0 ) {
308 return( AFPERR_NOITEM );
310 memcpy( ih, utag, sizeof( utag ));
311 memcpy( ih + sizeof( utag ), utype, sizeof( utype ));
312 *( ih + sizeof( utag ) + sizeof( utype )) = 1;
313 *( ih + sizeof( utag ) + sizeof( utype ) + 1 ) = 0;
314 memcpy( ih + sizeof( utag ) + sizeof( utype ) + 2, &usize,
316 memcpy( rbuf, ih, sizeof( ih ));
317 *rbuflen = sizeof( ih );
321 if ( iconopen( vol, fcreator, O_RDONLY, 0 ) < 0) {
322 return( AFPERR_NOITEM );
325 if ( iindex < si.sdt_index ) {
326 if ( lseek( si.sdt_fd, (off_t) 0L, SEEK_SET ) < 0 ) {
327 return( AFPERR_PARAM );
333 * Position to the correct spot.
336 if ( read( si.sdt_fd, ih, sizeof( ih )) != sizeof( ih )) {
339 return( AFPERR_NOITEM );
341 memcpy( &bsize, ih + 10, sizeof( bsize ));
342 bsize = ntohs(bsize);
343 if ( lseek( si.sdt_fd, (off_t) bsize, SEEK_CUR ) < 0 ) {
344 LOG(log_error, logtype_afpd, "afp_iconinfo(%s): lseek: %s", icon_dtfile(vol, fcreator), strerror(errno) );
345 return( AFPERR_PARAM );
347 if ( si.sdt_index == iindex ) {
348 memcpy( rbuf, ih, sizeof( ih ));
349 *rbuflen = sizeof( ih );
357 int afp_geticon(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
362 u_char fcreator[ 4 ], ftype[ 4 ], itype, ih[ 12 ];
363 uint16_t vid, bsize, rsize;
369 memcpy( &vid, ibuf, sizeof( vid ));
370 ibuf += sizeof( vid );
371 if (NULL == ( vol = getvolbyvid( vid )) ) {
372 return( AFPERR_PARAM );
375 memcpy( fcreator, ibuf, sizeof( fcreator ));
376 ibuf += sizeof( fcreator );
377 memcpy( ftype, ibuf, sizeof( ftype ));
378 ibuf += sizeof( ftype );
379 itype = (unsigned char) *ibuf++;
381 memcpy( &bsize, ibuf, sizeof( bsize ));
382 bsize = ntohs( bsize );
385 if ( memcmp( fcreator, ucreator, sizeof( ucreator )) == 0 &&
386 memcmp( ftype, utype, sizeof( utype )) == 0 &&
389 memcpy( rbuf, uicon, bsize);
395 if ( iconopen( vol, fcreator, O_RDONLY, 0 ) < 0) {
396 return( AFPERR_NOITEM );
399 if ( lseek( si.sdt_fd, (off_t) 0L, SEEK_SET ) < 0 ) {
402 LOG(log_error, logtype_afpd, "afp_geticon(%s): lseek: %s", icon_dtfile(vol, fcreator), strerror(errno));
403 return( AFPERR_PARAM );
408 while (( rc = read( si.sdt_fd, ih, sizeof( ih ))) > 0 ) {
410 offset += sizeof(ih);
411 if ( memcmp( ih + sizeof( int ), ftype, sizeof( ftype )) == 0 &&
412 *(ih + sizeof( int ) + sizeof( ftype )) == itype ) {
415 memcpy( &rsize, ih + 10, sizeof( rsize ));
416 rsize = ntohs( rsize );
417 if ( lseek( si.sdt_fd, (off_t) rsize, SEEK_CUR ) < 0 ) {
418 LOG(log_error, logtype_afpd, "afp_geticon(%s): lseek: %s", icon_dtfile(vol, fcreator), strerror(errno) );
419 return( AFPERR_PARAM );
425 LOG(log_error, logtype_afpd, "afp_geticon(%s): read: %s", icon_dtfile(vol, fcreator), strerror(errno));
426 return( AFPERR_PARAM );
430 return( AFPERR_NOITEM );
433 memcpy( &rsize, ih + 10, sizeof( rsize ));
434 rsize = ntohs( rsize );
435 #define min(a,b) ((a)<(b)?(a):(b))
436 rc = min( bsize, rsize );
443 size = (fstat(si.sdt_fd, &st) < 0) ? 0 : st.st_size;
444 if (size < rc + offset) {
448 if ((buflen = dsi_readinit(dsi, rbuf, buflen, rc, AFP_OK)) < 0)
452 /* do to the streaming nature, we have to exit if we encounter
453 * a problem. much confusion results otherwise. */
454 while (*rbuflen > 0) {
456 if (!obj->options.flags & OPTION_DEBUG) {
457 if (dsi_stream_read_file(dsi, si.sdt_fd, offset, dsi->datasize) < 0) {
460 case EINVAL: /* there's no guarantee that all fs support sendfile */
472 buflen = read(si.sdt_fd, rbuf, *rbuflen);
476 /* dsi_read() also returns buffer size of next allocation */
477 buflen = dsi_read(dsi, rbuf, buflen); /* send it off */
488 LOG(log_error, logtype_afpd, "afp_geticon(%s): %s", icon_dtfile(vol, fcreator), strerror(errno));
490 obj->exit(EXITERR_SYS);
494 if ( read( si.sdt_fd, rbuf, rc ) < rc ) {
495 return( AFPERR_PARAM );
502 /* ---------------------- */
503 static const char hexdig[] = "0123456789abcdef";
504 char *dtfile(const struct vol *vol, u_char creator[], char *ext )
506 static char path[ MAXPATHLEN + 1];
510 strcpy( path, vol->v_path );
511 strcat( path, "/.AppleDesktop/" );
512 for ( p = path; *p != '\0'; p++ )
515 if ( !isprint( creator[ 0 ] ) || creator[ 0 ] == '/' ) {
516 *p++ = hexdig[ ( creator[ 0 ] & 0xf0 ) >> 4 ];
517 *p++ = hexdig[ creator[ 0 ] & 0x0f ];
524 for ( i = 0; i < sizeof( CreatorType ); i++ ) {
525 if ( !isprint( creator[ i ] ) || creator[ i ] == '/' ) {
526 *p++ = hexdig[ ( creator[ i ] & 0xf0 ) >> 4 ];
527 *p++ = hexdig[ creator[ i ] & 0x0f ];
538 /* ---------------------------
539 * mpath is only a filename
540 * did filename parent directory ID.
543 char *mtoupath(const struct vol *vol, char *mpath, cnid_t did, int utf8)
545 static char upath[ MAXPATHLEN + 2]; /* for convert_charset dest_len parameter +2 */
551 if ( *mpath == '\0' ) {
556 /* set conversion flags */
557 flags = vol->v_mtou_flags;
559 m = demangle(vol, mpath, did);
570 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)) ) {
571 LOG(log_error, logtype_afpd, "conversion from %s to %s for %s failed.", (utf8)?"UTF8-MAC":vol->v_maccodepage, vol->v_volcodepage, mpath);
576 LOG(log_debug9, logtype_afpd, "mtoupath: '%s':'%s'", mpath, upath);
584 char *utompath(const struct vol *vol, char *upath, cnid_t id, int utf8)
586 static char mpath[ MAXPATHLEN + 2]; /* for convert_charset dest_len parameter +2 */
592 outlen = strlen(upath);
594 flags = vol->v_utom_flags;
598 /* convert charsets */
599 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)) ) {
600 LOG(log_error, logtype_afpd, "Conversion from %s to %s for %s (%u) failed.", vol->v_volcodepage, vol->v_maccodepage, u, ntohl(id));
604 flags = !!(flags & CONV_REQMANGLE);
609 m = mangle(vol, mpath, outlen, upath, id, flags);
612 LOG(log_debug9, logtype_afpd, "utompath: '%s':'%s':'%2.2X'", upath, m, ntohl(id));
618 m = mangle(vol, u, strlen(u), upath, id, (utf8)?3:1);
622 /* ------------------------- */
623 static int ad_addcomment(const AFPObj *obj, struct vol *vol, struct path *path, char *ibuf)
629 struct adouble ad, *adp;
631 clen = (u_char)*ibuf++;
632 clen = min( clen, 199 );
634 upath = path->u_name;
635 if (check_access(obj, vol, upath, OPENACC_WR ) < 0) {
636 return AFPERR_ACCESS;
639 isadir = path_isadir(path);
640 if (isadir || !(of = of_findname(path))) {
646 if (ad_open(adp, upath,
647 ADFLAGS_HF | ( (isadir) ? ADFLAGS_DIR : 0) | ADFLAGS_CREATE | ADFLAGS_RDWR,
649 return( AFPERR_ACCESS );
652 if (ad_getentryoff(adp, ADEID_COMMENT)) {
653 if ( (ad_get_MD_flags( adp ) & O_CREAT) ) {
654 if ( *path->m_name == '\0' ) {
655 name = (char *)curdir->d_m_name->data;
659 ad_setname(adp, name);
661 ad_setentrylen( adp, ADEID_COMMENT, clen );
662 memcpy( ad_entry( adp, ADEID_COMMENT ), ibuf, clen );
665 ad_close(adp, ADFLAGS_HF);
669 /* ----------------------------- */
670 int afp_addcomment(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
681 memcpy( &vid, ibuf, sizeof( vid ));
682 ibuf += sizeof( vid );
683 if (NULL == ( vol = getvolbyvid( vid )) ) {
684 return( AFPERR_PARAM );
687 memcpy( &did, ibuf, sizeof( did ));
688 ibuf += sizeof( did );
689 if (NULL == ( dir = dirlookup( vol, did )) ) {
693 if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
694 return get_afp_errno(AFPERR_NOOBJ);
697 if ((u_long)ibuf & 1 ) {
701 return ad_addcomment(obj, vol, path, ibuf);
704 /* -------------------- */
705 static int ad_getcomment(struct vol *vol, struct path *path, char *rbuf, size_t *rbuflen)
707 struct adouble ad, *adp;
713 upath = path->u_name;
714 isadir = path_isadir(path);
715 if (isadir || !(of = of_findname(path))) {
721 if ( ad_metadata( upath, ((isadir) ? ADFLAGS_DIR : 0), adp) < 0 ) {
722 return( AFPERR_NOITEM );
725 if (!ad_getentryoff(adp, ADEID_COMMENT)) {
726 ad_close(adp, ADFLAGS_HF);
727 return AFPERR_NOITEM;
730 * Make sure the AD file is not bogus.
732 if ( ad_getentrylen( adp, ADEID_COMMENT ) <= 0 ||
733 ad_getentrylen( adp, ADEID_COMMENT ) > 199 ) {
734 ad_close(adp, ADFLAGS_HF);
735 return( AFPERR_NOITEM );
738 clen = min( ad_getentrylen( adp, ADEID_COMMENT ), 128 ); /* OSX only use 128, greater kill Adobe CS2 */
740 memcpy( rbuf, ad_entry( adp, ADEID_COMMENT ), clen);
742 ad_close(adp, ADFLAGS_HF);
747 /* -------------------- */
748 int afp_getcomment(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
759 memcpy( &vid, ibuf, sizeof( vid ));
760 ibuf += sizeof( vid );
761 if (NULL == ( vol = getvolbyvid( vid )) ) {
762 return( AFPERR_PARAM );
765 memcpy( &did, ibuf, sizeof( did ));
766 ibuf += sizeof( did );
767 if (NULL == ( dir = dirlookup( vol, did )) ) {
771 if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
772 return get_afp_errno(AFPERR_NOOBJ);
775 return ad_getcomment(vol, s_path, rbuf, rbuflen);
778 /* ----------------------- */
779 static int ad_rmvcomment(const AFPObj *obj, struct vol *vol, struct path *path)
781 struct adouble ad, *adp;
786 upath = path->u_name;
787 if (check_access(obj, vol, upath, OPENACC_WR ) < 0) {
788 return AFPERR_ACCESS;
791 isadir = path_isadir(path);
792 if (isadir || !(of = of_findname(path))) {
798 if ( ad_open(adp, upath, ADFLAGS_HF | ADFLAGS_RDWR | ((isadir) ? ADFLAGS_DIR : 0)) < 0 ) {
801 return( AFPERR_NOITEM );
803 return( AFPERR_ACCESS );
805 return( AFPERR_PARAM );
809 if (ad_getentryoff(adp, ADEID_COMMENT)) {
810 ad_setentrylen( adp, ADEID_COMMENT, 0 );
813 ad_close(adp, ADFLAGS_HF);
817 /* ----------------------- */
818 int afp_rmvcomment(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
829 memcpy( &vid, ibuf, sizeof( vid ));
830 ibuf += sizeof( vid );
831 if (NULL == ( vol = getvolbyvid( vid )) ) {
832 return( AFPERR_PARAM );
835 memcpy( &did, ibuf, sizeof( did ));
836 ibuf += sizeof( did );
837 if (NULL == ( dir = dirlookup( vol, did )) ) {
841 if (NULL == ( s_path = cname( vol, dir, &ibuf ))) {
842 return get_afp_errno(AFPERR_NOOBJ);
845 return ad_rmvcomment(obj, vol, s_path);