]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/desktop.c
Performance tuning: sendfilev, MSG_MORE and do not apply AFP read locks by default
[netatalk.git] / etc / afpd / desktop.c
1 /*
2  * See COPYRIGHT.
3  *
4  * bug:
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.
8  */
9
10 #ifdef HAVE_CONFIG_H
11 #include "config.h"
12 #endif /* HAVE_CONFIG_H */
13
14 #include <stdio.h>
15 #include <string.h>
16 #include <ctype.h>
17
18 #include <errno.h>
19
20 #include <atalk/adouble.h>
21 #include <sys/uio.h>
22 #include <sys/param.h>
23 #include <sys/socket.h>
24 #include <arpa/inet.h>
25
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>
34
35 #include "volume.h"
36 #include "directory.h"
37 #include "fork.h"
38 #include "desktop.h"
39 #include "mangle.h"
40
41 static void create_appledesktop_folder(const struct vol * vol)
42 {
43     bstring olddtpath = NULL, dtpath = NULL;
44     struct stat st;
45     char *cmd_argv[4];
46
47     olddtpath = bfromcstr(vol->v_path);
48     bcatcstr(olddtpath, "/" APPLEDESKTOP);
49
50     dtpath = bfromcstr(vol->v_dbpath);
51     bcatcstr(dtpath, "/" APPLEDESKTOP);
52
53     if (lstat(bdata(dtpath), &st) != 0) {
54
55         become_root();
56
57         if (lstat(bdata(olddtpath), &st) == 0) {
58             cmd_argv[0] = "mv";
59             cmd_argv[1] = bdata(olddtpath);
60             cmd_argv[2] = bdata(dtpath);
61             cmd_argv[3] = NULL;
62             if (run_cmd("mv", cmd_argv) != 0) {
63                 LOG(log_error, logtype_afpd, "moving .AppleDesktop from \"%s\" to \"%s\" failed",
64                     bdata(olddtpath), bdata(dtpath));
65                 mkdir(bdata(dtpath), 0777);
66             }
67         } else {
68             mkdir(bdata(dtpath), 0777);
69         }
70
71         unbecome_root();
72     }
73
74     bdestroy(dtpath);
75     bdestroy(olddtpath);
76 }
77
78 int afp_opendt(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
79 {
80     struct vol  *vol;
81     uint16_t    vid;
82
83     ibuf += 2;
84
85     memcpy( &vid, ibuf, sizeof(vid));
86     if (NULL == ( vol = getvolbyvid( vid )) ) {
87         *rbuflen = 0;
88         return( AFPERR_PARAM );
89     }
90
91     create_appledesktop_folder(vol);
92
93     memcpy( rbuf, &vid, sizeof(vid));
94     *rbuflen = sizeof(vid);
95     return( AFP_OK );
96 }
97
98 int afp_closedt(AFPObj *obj _U_, char *ibuf _U_, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
99 {
100     *rbuflen = 0;
101     return( AFP_OK );
102 }
103
104 static struct savedt    si = { { 0, 0, 0, 0 }, -1, 0, 0 };
105
106 static char *icon_dtfile(struct vol *vol, u_char creator[ 4 ])
107 {
108     return dtfile( vol, creator, ".icon" );
109 }
110
111 static int iconopen(struct vol *vol, u_char creator[ 4 ], int flags, int mode)
112 {
113     char        *dtf, *adt, *adts;
114
115     if ( si.sdt_fd != -1 ) {
116         if ( memcmp( si.sdt_creator, creator, sizeof( CreatorType )) == 0 &&
117                 si.sdt_vid == vol->v_vid ) {
118             return 0;
119         }
120         close( si.sdt_fd );
121         si.sdt_fd = -1;
122     }
123
124     dtf = icon_dtfile( vol, creator);
125
126     if (( si.sdt_fd = open( dtf, flags, ad_mode( dtf, mode ))) < 0 ) {
127         if ( errno == ENOENT && ( flags & O_CREAT )) {
128             if (( adts = strrchr( dtf, '/' )) == NULL ) {
129                 return -1;
130             }
131             *adts = '\0';
132             if (( adt = strrchr( dtf, '/' )) == NULL ) {
133                 return -1;
134             }
135             *adt = '\0';
136             (void) ad_mkdir( dtf, DIRBITS | 0777 );
137             *adt = '/';
138             (void) ad_mkdir( dtf, DIRBITS | 0777 );
139             *adts = '/';
140
141             if (( si.sdt_fd = open( dtf, flags, ad_mode( dtf, mode ))) < 0 ) {
142                 LOG(log_error, logtype_afpd, "iconopen(%s): open: %s", dtf, strerror(errno) );
143                 return -1;
144             }
145         } else {
146             return -1;
147         }
148     }
149
150     memcpy( si.sdt_creator, creator, sizeof( CreatorType ));
151     si.sdt_vid = vol->v_vid;
152     si.sdt_index = 1;
153     return 0;
154 }
155
156 int afp_addicon(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
157 {
158     struct vol          *vol;
159     u_char              fcreator[ 4 ], imh[ 12 ], irh[ 12 ], *p;
160     int                 itype, cc = AFP_OK, iovcnt = 0;
161     size_t              buflen;
162     uint32_t           ftype, itag;
163     uint16_t            bsize, rsize, vid;
164
165     buflen = *rbuflen;
166     *rbuflen = 0;
167     ibuf += 2;
168
169     memcpy( &vid, ibuf, sizeof( vid ));
170     ibuf += sizeof( vid );
171     if (NULL == ( vol = getvolbyvid( vid )) ) {
172         cc = AFPERR_PARAM;
173         goto addicon_err;
174     }
175
176     memcpy( fcreator, ibuf, sizeof( fcreator ));
177     ibuf += sizeof( fcreator );
178
179     memcpy( &ftype, ibuf, sizeof( ftype ));
180     ibuf += sizeof( ftype );
181
182     itype = (unsigned char) *ibuf;
183     ibuf += 2;
184
185     memcpy( &itag, ibuf, sizeof( itag ));
186     ibuf += sizeof( itag );
187
188     memcpy( &bsize, ibuf, sizeof( bsize ));
189     bsize = ntohs( bsize );
190
191     if ( si.sdt_fd != -1 ) {
192         (void)close( si.sdt_fd );
193         si.sdt_fd = -1;
194     }
195
196     if (iconopen( vol, fcreator, O_RDWR|O_CREAT, 0666 ) < 0) {
197         cc = AFPERR_NOITEM;
198         goto addicon_err;
199     }
200
201     if (lseek( si.sdt_fd, (off_t) 0L, SEEK_SET ) < 0) {
202         close(si.sdt_fd);
203         si.sdt_fd = -1;
204         LOG(log_error, logtype_afpd, "afp_addicon(%s): lseek: %s", icon_dtfile(vol, fcreator), strerror(errno) );
205         cc = AFPERR_PARAM;
206         goto addicon_err;
207     }
208
209     /*
210      * Read icon elements until we find a match to replace, or
211      * we get to the end to insert.
212      */
213     p = imh;
214     memcpy( p, &itag, sizeof( itag ));
215     p += sizeof( itag );
216     memcpy( p, &ftype, sizeof( ftype ));
217     p += sizeof( ftype );
218     *p++ = itype;
219     *p++ = 0;
220     bsize = htons( bsize );
221     memcpy( p, &bsize, sizeof( bsize ));
222     bsize = ntohs( bsize );
223     while (( cc = read( si.sdt_fd, irh, sizeof( irh ))) > 0 ) {
224         memcpy( &rsize, irh + 10, sizeof( rsize ));
225         rsize = ntohs( rsize );
226         /*
227          * Is this our set of headers?
228          */
229         if ( memcmp( irh, imh, sizeof( irh ) - sizeof( u_short )) == 0 ) {
230             /*
231              * Is the size correct?
232              */
233             if ( bsize != rsize )
234                 cc = AFPERR_ITYPE;
235             break;
236         }
237
238         if ( lseek( si.sdt_fd, (off_t) rsize, SEEK_CUR ) < 0 ) {
239             LOG(log_error, logtype_afpd, "afp_addicon(%s): lseek: %s", icon_dtfile(vol, fcreator),strerror(errno) );
240             cc = AFPERR_PARAM;
241         }
242     }
243
244     /*
245      * Some error occurred, return.
246      */
247 addicon_err:
248     if ( cc < 0 ) {
249         dsi_writeinit(obj->dsi, rbuf, buflen);
250         dsi_writeflush(obj->dsi);
251         return cc;
252     }
253
254     DSI *dsi = obj->dsi;
255
256     iovcnt = dsi_writeinit(dsi, rbuf, buflen);
257
258     /* add headers at end of file */
259     if ((cc == 0) && (write(si.sdt_fd, imh, sizeof(imh)) < 0)) {
260         LOG(log_error, logtype_afpd, "afp_addicon(%s): write: %s", icon_dtfile(vol, fcreator), strerror(errno));
261         dsi_writeflush(dsi);
262         return AFPERR_PARAM;
263     }
264
265     if ((cc = write(si.sdt_fd, rbuf, iovcnt)) < 0) {
266         LOG(log_error, logtype_afpd, "afp_addicon(%s): write: %s", icon_dtfile(vol, fcreator), strerror(errno));
267         dsi_writeflush(dsi);
268         return AFPERR_PARAM;
269     }
270
271     while ((iovcnt = dsi_write(dsi, rbuf, buflen))) {
272         if ((cc = write(si.sdt_fd, rbuf, iovcnt)) < 0) {
273             LOG(log_error, logtype_afpd, "afp_addicon(%s): write: %s", icon_dtfile(vol, fcreator), strerror(errno));
274             dsi_writeflush(dsi);
275             return AFPERR_PARAM;
276         }
277     }
278
279     close( si.sdt_fd );
280     si.sdt_fd = -1;
281     return( AFP_OK );
282 }
283
284 static const u_char     utag[] = { 0, 0, 0, 0 };
285 static const u_char     ucreator[] = { 0, 0, 0, 0 };/* { 'U', 'N', 'I', 'X' };*/
286 static const u_char     utype[] = { 0, 0, 0, 0 };/* { 'T', 'E', 'X', 'T' };*/
287 static const short      usize = 256;
288
289 #if 0
290 static const u_char     uicon[] = {
291     0x1F, 0xFF, 0xFC, 0x00, 0x10, 0x00, 0x06, 0x00,
292     0x10, 0x00, 0x05, 0x00, 0x10, 0x00, 0x04, 0x80,
293     0x10, 0x00, 0x04, 0x40, 0x10, 0x00, 0x04, 0x20,
294     0x10, 0x00, 0x07, 0xF0, 0x10, 0x00, 0x00, 0x10,
295     0x10, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x10,
296     0x10, 0x00, 0x00, 0x10, 0x10, 0x80, 0x02, 0x10,
297     0x11, 0x80, 0x03, 0x10, 0x12, 0x80, 0x02, 0x90,
298     0x12, 0x80, 0x02, 0x90, 0x14, 0x80, 0x02, 0x50,
299     0x14, 0x87, 0xC2, 0x50, 0x14, 0x58, 0x34, 0x50,
300     0x14, 0x20, 0x08, 0x50, 0x12, 0x16, 0xD0, 0x90,
301     0x11, 0x01, 0x01, 0x10, 0x12, 0x80, 0x02, 0x90,
302     0x12, 0x9C, 0x72, 0x90, 0x14, 0x22, 0x88, 0x50,
303     0x14, 0x41, 0x04, 0x50, 0x14, 0x49, 0x24, 0x50,
304     0x14, 0x55, 0x54, 0x50, 0x14, 0x5D, 0x74, 0x50,
305     0x14, 0x5D, 0x74, 0x50, 0x12, 0x49, 0x24, 0x90,
306     0x12, 0x22, 0x88, 0x90, 0x1F, 0xFF, 0xFF, 0xF0,
307     0x1F, 0xFF, 0xFC, 0x00, 0x1F, 0xFF, 0xFE, 0x00,
308     0x1F, 0xFF, 0xFF, 0x00, 0x1F, 0xFF, 0xFF, 0x80,
309     0x1F, 0xFF, 0xFF, 0xC0, 0x1F, 0xFF, 0xFF, 0xE0,
310     0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
311     0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
312     0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
313     0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
314     0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
315     0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
316     0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
317     0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
318     0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
319     0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
320     0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
321     0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
322     0x1F, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xF0,
323 };
324 #endif
325
326 int afp_geticoninfo(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
327 {
328     struct vol  *vol;
329     unsigned char       fcreator[ 4 ], ih[ 12 ];
330     uint16_t    vid, iindex, bsize;
331
332     *rbuflen = 0;
333     ibuf += 2;
334
335     memcpy( &vid, ibuf, sizeof( vid ));
336     ibuf += sizeof( vid );
337     if (NULL == ( vol = getvolbyvid( vid )) ) {
338         return( AFPERR_PARAM );
339     }
340
341     memcpy( fcreator, ibuf, sizeof( fcreator ));
342     ibuf += sizeof( fcreator );
343     memcpy( &iindex, ibuf, sizeof( iindex ));
344     iindex = ntohs( iindex );
345
346     if ( memcmp( fcreator, ucreator, sizeof( ucreator )) == 0 ) {
347         if ( iindex > 1 ) {
348             return( AFPERR_NOITEM );
349         }
350         memcpy( ih, utag, sizeof( utag ));
351         memcpy( ih + sizeof( utag ), utype, sizeof( utype ));
352         *( ih + sizeof( utag ) + sizeof( utype )) = 1;
353         *( ih + sizeof( utag ) + sizeof( utype ) + 1 ) = 0;
354         memcpy( ih + sizeof( utag ) + sizeof( utype ) + 2, &usize,
355                 sizeof( usize ));
356         memcpy( rbuf, ih, sizeof( ih ));
357         *rbuflen = sizeof( ih );
358         return( AFP_OK );
359     }
360
361     if ( iconopen( vol, fcreator, O_RDONLY, 0 ) < 0) {
362         return( AFPERR_NOITEM );
363     }
364
365     if ( iindex < si.sdt_index ) {
366         if ( lseek( si.sdt_fd, (off_t) 0L, SEEK_SET ) < 0 ) {
367             return( AFPERR_PARAM );
368         }
369         si.sdt_index = 1;
370     }
371
372     /*
373      * Position to the correct spot.
374      */
375     for (;;) {
376         if ( read( si.sdt_fd, ih, sizeof( ih )) != sizeof( ih )) {
377             close( si.sdt_fd );
378             si.sdt_fd = -1;
379             return( AFPERR_NOITEM );
380         }
381         memcpy( &bsize, ih + 10, sizeof( bsize ));
382         bsize = ntohs(bsize);
383         if ( lseek( si.sdt_fd, (off_t) bsize, SEEK_CUR ) < 0 ) {
384             LOG(log_error, logtype_afpd, "afp_iconinfo(%s): lseek: %s", icon_dtfile(vol, fcreator), strerror(errno) );
385             return( AFPERR_PARAM );
386         }
387         if ( si.sdt_index == iindex ) {
388             memcpy( rbuf, ih, sizeof( ih ));
389             *rbuflen = sizeof( ih );
390             return( AFP_OK );
391         }
392         si.sdt_index++;
393     }
394 }
395
396
397 int afp_geticon(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
398 {
399     struct vol  *vol;
400     off_t       offset;
401     ssize_t     rc, buflen;
402     u_char      fcreator[ 4 ], ftype[ 4 ], itype, ih[ 12 ];
403     uint16_t    vid, bsize, rsize;
404
405     buflen = *rbuflen;
406     *rbuflen = 0;
407     ibuf += 2;
408
409     memcpy( &vid, ibuf, sizeof( vid ));
410     ibuf += sizeof( vid );
411     if (NULL == ( vol = getvolbyvid( vid )) ) {
412         return( AFPERR_PARAM );
413     }
414
415     memcpy( fcreator, ibuf, sizeof( fcreator ));
416     ibuf += sizeof( fcreator );
417     memcpy( ftype, ibuf, sizeof( ftype ));
418     ibuf += sizeof( ftype );
419     itype = (unsigned char) *ibuf++;
420     ibuf++;
421     memcpy( &bsize, ibuf, sizeof( bsize ));
422     bsize = ntohs( bsize );
423
424 #if 0
425     if ( memcmp( fcreator, ucreator, sizeof( ucreator )) == 0 &&
426             memcmp( ftype, utype, sizeof( utype )) == 0 &&
427             itype == 1 &&
428             bsize <= usize ) {
429         memcpy( rbuf, uicon, bsize);
430         *rbuflen = bsize;
431         return( AFP_OK );
432     }
433 #endif
434
435     if ( iconopen( vol, fcreator, O_RDONLY, 0 ) < 0) {
436         return( AFPERR_NOITEM );
437     }
438
439     if ( lseek( si.sdt_fd, (off_t) 0L, SEEK_SET ) < 0 ) {
440         close(si.sdt_fd);
441         si.sdt_fd = -1;
442         LOG(log_error, logtype_afpd, "afp_geticon(%s): lseek: %s", icon_dtfile(vol, fcreator), strerror(errno));
443         return( AFPERR_PARAM );
444     }
445
446     si.sdt_index = 1;
447     offset = 0;
448     while (( rc = read( si.sdt_fd, ih, sizeof( ih ))) > 0 ) {
449         si.sdt_index++;
450         offset += sizeof(ih);
451         if ( memcmp( ih + sizeof( int ), ftype, sizeof( ftype )) == 0 &&
452                 *(ih + sizeof( int ) + sizeof( ftype )) == itype ) {
453             break;
454         }
455         memcpy( &rsize, ih + 10, sizeof( rsize ));
456         rsize = ntohs( rsize );
457         if ( lseek( si.sdt_fd, (off_t) rsize, SEEK_CUR ) < 0 ) {
458             LOG(log_error, logtype_afpd, "afp_geticon(%s): lseek: %s", icon_dtfile(vol, fcreator), strerror(errno) );
459             return( AFPERR_PARAM );
460         }
461         offset += rsize;
462     }
463
464     if ( rc < 0 ) {
465         LOG(log_error, logtype_afpd, "afp_geticon(%s): read: %s", icon_dtfile(vol, fcreator), strerror(errno));
466         return( AFPERR_PARAM );
467     }
468
469     if ( rc == 0 ) {
470         return( AFPERR_NOITEM );
471     }
472
473     memcpy( &rsize, ih + 10, sizeof( rsize ));
474     rsize = ntohs( rsize );
475 #define min(a,b)        ((a)<(b)?(a):(b))
476     rc = min( bsize, rsize );
477
478     if (buflen < rc) {
479         DSI *dsi = obj->dsi;
480         struct stat st;
481         off_t size;
482
483         size = (fstat(si.sdt_fd, &st) < 0) ? 0 : st.st_size;
484         if (size < rc + offset) {
485             return AFPERR_PARAM;
486         }
487
488 #ifndef WITH_SENDFILE
489         if ((buflen = dsi_readinit(dsi, rbuf, buflen, rc, AFP_OK)) < 0)
490             goto geticon_exit;
491 #endif
492
493         *rbuflen = buflen;
494         /* do to the streaming nature, we have to exit if we encounter
495          * a problem. much confusion results otherwise. */
496         while (*rbuflen > 0) {
497 #ifdef WITH_SENDFILE
498             if (dsi_stream_read_file(dsi, si.sdt_fd, offset, dsi->datasize, AFP_OK) < 0) {
499                 switch (errno) {
500                 case ENOSYS:
501                 case EINVAL:  /* there's no guarantee that all fs support sendfile */
502                     break;
503                 default:
504                     goto geticon_exit;
505                 }
506             }
507             else {
508                 dsi_readdone(dsi);
509                 return AFP_OK;
510             }
511 #endif
512             buflen = read(si.sdt_fd, rbuf, *rbuflen);
513             if (buflen < 0)
514                 goto geticon_exit;
515
516             /* dsi_read() also returns buffer size of next allocation */
517             buflen = dsi_read(dsi, rbuf, buflen); /* send it off */
518             if (buflen < 0)
519                 goto geticon_exit;
520
521             *rbuflen = buflen;
522         }
523         
524         dsi_readdone(dsi);
525         return AFP_OK;
526
527 geticon_exit:
528         LOG(log_error, logtype_afpd, "afp_geticon(%s): %s", icon_dtfile(vol, fcreator), strerror(errno));
529         dsi_readdone(dsi);
530         obj->exit(EXITERR_SYS);
531         return AFP_OK;
532
533     } else {
534         if ( read( si.sdt_fd, rbuf, rc ) < rc ) {
535             return( AFPERR_PARAM );
536         }
537         *rbuflen = rc;
538     }
539     return AFP_OK;
540 }
541
542 /* ---------------------- */
543 static const char               hexdig[] = "0123456789abcdef";
544 char *dtfile(const struct vol *vol, u_char creator[], char *ext )
545 {
546     static char path[ MAXPATHLEN + 1];
547     char        *p;
548     unsigned int i;
549
550     strcpy( path, vol->v_dbpath );
551     strcat( path, "/" APPLEDESKTOP "/" );
552     for ( p = path; *p != '\0'; p++ )
553         ;
554
555     if ( !isprint( creator[ 0 ] ) || creator[ 0 ] == '/' ) {
556         *p++ = hexdig[ ( creator[ 0 ] & 0xf0 ) >> 4 ];
557         *p++ = hexdig[ creator[ 0 ] & 0x0f ];
558     } else {
559         *p++ = creator[ 0 ];
560     }
561
562     *p++ = '/';
563
564     for ( i = 0; i < sizeof( CreatorType ); i++ ) {
565         if ( !isprint( creator[ i ] ) || creator[ i ] == '/' ) {
566             *p++ = hexdig[ ( creator[ i ] & 0xf0 ) >> 4 ];
567             *p++ = hexdig[ creator[ i ] & 0x0f ];
568         } else {
569             *p++ = creator[ i ];
570         }
571     }
572     *p = '\0';
573     strcat( path, ext );
574
575     return( path );
576 }
577
578 /* ---------------------------
579  * mpath is only a filename 
580  * did filename parent directory ID.
581 */
582
583 char *mtoupath(const struct vol *vol, char *mpath, cnid_t did, int utf8)
584 {
585     static char  upath[ MAXPATHLEN + 2]; /* for convert_charset dest_len parameter +2 */
586     char        *m, *u;
587     size_t       inplen;
588     size_t       outlen;
589     uint16_t     flags;
590         
591     if ( *mpath == '\0' ) {
592         strcpy(upath, ".");
593         return upath;
594     }
595
596     /* set conversion flags */
597     flags = vol->v_mtou_flags;
598     
599     m = demangle(vol, mpath, did);
600     if (m != mpath) {
601         return m;
602     }
603
604     m = mpath;
605     u = upath;
606
607     inplen = strlen(m);
608     outlen = MAXPATHLEN;
609
610     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)) ) {
611         LOG(log_error, logtype_afpd, "conversion from %s to %s for %s failed.", (utf8)?"UTF8-MAC":vol->v_maccodepage, vol->v_volcodepage, mpath);
612             return NULL;
613     }
614
615 #ifdef DEBUG
616     LOG(log_debug9, logtype_afpd, "mtoupath: '%s':'%s'", mpath, upath);
617 #endif /* DEBUG */
618     return( upath );
619 }
620
621 /* --------------- 
622  * id filename ID
623 */
624 char *utompath(const struct vol *vol, char *upath, cnid_t id, int utf8)
625 {
626     static char  mpath[ MAXPATHLEN + 2]; /* for convert_charset dest_len parameter +2 */
627     char        *m, *u;
628     uint16_t    flags;
629     size_t       outlen;
630
631     m = mpath;
632     outlen = strlen(upath);
633
634     flags = vol->v_utom_flags;
635
636     u = upath;
637
638     /* convert charsets */
639     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)) ) { 
640         LOG(log_error, logtype_afpd, "Conversion from %s to %s for %s (%u) failed.", vol->v_volcodepage, vol->v_maccodepage, u, ntohl(id));
641         goto utompath_error;
642     }
643
644     flags = !!(flags & CONV_REQMANGLE);
645
646     if (utf8)
647         flags |= 2;
648
649     m = mangle(vol, mpath, outlen, upath, id, flags);
650
651 #ifdef DEBUG
652     LOG(log_debug9, logtype_afpd, "utompath: '%s':'%s':'%2.2X'", upath, m, ntohl(id));
653 #endif /* DEBUG */
654     return(m);
655
656 utompath_error:
657     u = "???";
658     m = mangle(vol, u, strlen(u), upath, id, (utf8)?3:1);
659     return(m);
660 }
661
662 /* ------------------------- */
663 static int ad_addcomment(const AFPObj *obj, struct vol *vol, struct path *path, char *ibuf)
664 {
665     struct ofork        *of;
666     char                *name, *upath;
667     int                 isadir;
668     int                 clen;
669     struct adouble      ad, *adp;
670
671     clen = (u_char)*ibuf++;
672     clen = min( clen, 199 );
673
674     upath = path->u_name;
675     if (check_access(obj, vol, upath, OPENACC_WR ) < 0) {
676         return AFPERR_ACCESS;
677     }
678     
679     isadir = path_isadir(path);
680     if (isadir || !(of = of_findname(path))) {
681         ad_init(&ad, vol);
682         adp = &ad;
683     } else
684         adp = of->of_ad;
685
686     if (ad_open(adp, upath,
687                 ADFLAGS_HF | ( (isadir) ? ADFLAGS_DIR : 0) | ADFLAGS_CREATE | ADFLAGS_RDWR,
688                 0666) < 0 ) {
689         return( AFPERR_ACCESS );
690     }
691
692     if (ad_getentryoff(adp, ADEID_COMMENT)) {
693         if ( (ad_get_MD_flags( adp ) & O_CREAT) ) {
694             if ( *path->m_name == '\0' ) {
695                 name = (char *)curdir->d_m_name->data;
696             } else {
697                 name = path->m_name;
698             }
699             ad_setname(adp, name);
700         }
701         ad_setentrylen( adp, ADEID_COMMENT, clen );
702         memcpy( ad_entry( adp, ADEID_COMMENT ), ibuf, clen );
703         ad_flush( adp );
704     }
705     ad_close(adp, ADFLAGS_HF);
706     return( AFP_OK );
707 }
708
709 /* ----------------------------- */
710 int afp_addcomment(AFPObj *obj, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
711 {
712     struct vol          *vol;
713     struct dir          *dir;
714     struct path         *path;
715     uint32_t           did;
716     uint16_t            vid;
717
718     *rbuflen = 0;
719     ibuf += 2;
720
721     memcpy( &vid, ibuf, sizeof( vid ));
722     ibuf += sizeof( vid );
723     if (NULL == ( vol = getvolbyvid( vid )) ) {
724         return( AFPERR_PARAM );
725     }
726
727     memcpy( &did, ibuf, sizeof( did ));
728     ibuf += sizeof( did );
729     if (NULL == ( dir = dirlookup( vol, did )) ) {
730         return afp_errno;
731     }
732
733     if (NULL == ( path = cname( vol, dir, &ibuf )) ) {
734         return get_afp_errno(AFPERR_NOOBJ);
735     }
736
737     if ((u_long)ibuf & 1 ) {
738         ibuf++;
739     }
740
741     return ad_addcomment(obj, vol, path, ibuf);
742 }
743
744 /* -------------------- */
745 static int ad_getcomment(struct vol *vol, struct path *path, char *rbuf, size_t *rbuflen)
746 {
747     struct adouble      ad, *adp;
748     struct ofork        *of;
749     char                *upath;
750     int                 isadir;
751     int                 clen;
752
753     upath = path->u_name;
754     isadir = path_isadir(path);
755     if (isadir || !(of = of_findname(path))) {
756         ad_init(&ad, vol);
757         adp = &ad;
758     } else
759         adp = of->of_ad;
760         
761     if ( ad_metadata( upath, ((isadir) ? ADFLAGS_DIR : 0), adp) < 0 ) {
762         return( AFPERR_NOITEM );
763     }
764
765     if (!ad_getentryoff(adp, ADEID_COMMENT)) {
766         ad_close(adp, ADFLAGS_HF);
767         return AFPERR_NOITEM;
768     }
769     /*
770      * Make sure the AD file is not bogus.
771      */
772     if ( ad_getentrylen( adp, ADEID_COMMENT ) <= 0 ||
773             ad_getentrylen( adp, ADEID_COMMENT ) > 199 ) {
774         ad_close(adp, ADFLAGS_HF);
775         return( AFPERR_NOITEM );
776     }
777
778     clen = min( ad_getentrylen( adp, ADEID_COMMENT ), 128 ); /* OSX only use 128, greater kill Adobe CS2 */
779     *rbuf++ = clen;
780     memcpy( rbuf, ad_entry( adp, ADEID_COMMENT ), clen);
781     *rbuflen = clen + 1;
782     ad_close(adp, ADFLAGS_HF);
783
784     return( AFP_OK );
785 }
786
787 /* -------------------- */
788 int afp_getcomment(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
789 {
790     struct vol          *vol;
791     struct dir          *dir;
792     struct path         *s_path;
793     uint32_t            did;
794     uint16_t            vid;
795     
796     *rbuflen = 0;
797     ibuf += 2;
798
799     memcpy( &vid, ibuf, sizeof( vid ));
800     ibuf += sizeof( vid );
801     if (NULL == ( vol = getvolbyvid( vid )) ) {
802         return( AFPERR_PARAM );
803     }
804
805     memcpy( &did, ibuf, sizeof( did ));
806     ibuf += sizeof( did );
807     if (NULL == ( dir = dirlookup( vol, did )) ) {
808         return afp_errno;
809     }
810
811     if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
812         return get_afp_errno(AFPERR_NOOBJ);
813     }
814
815     return ad_getcomment(vol, s_path, rbuf, rbuflen);
816 }
817
818 /* ----------------------- */
819 static int ad_rmvcomment(const AFPObj *obj, struct vol *vol, struct path *path)
820 {
821     struct adouble      ad, *adp;
822     struct ofork        *of;
823     int                 isadir;
824     char                *upath;
825
826     upath = path->u_name;
827     if (check_access(obj, vol, upath, OPENACC_WR ) < 0) {
828         return AFPERR_ACCESS;
829     }
830
831     isadir = path_isadir(path);
832     if (isadir || !(of = of_findname(path))) {
833         ad_init(&ad, vol);
834         adp = &ad;
835     } else
836         adp = of->of_ad;
837
838     if ( ad_open(adp, upath, ADFLAGS_HF | ADFLAGS_RDWR | ((isadir) ? ADFLAGS_DIR : 0)) < 0 ) {
839         switch ( errno ) {
840         case ENOENT :
841             return( AFPERR_NOITEM );
842         case EACCES :
843             return( AFPERR_ACCESS );
844         default :
845             return( AFPERR_PARAM );
846         }
847     }
848
849     if (ad_getentryoff(adp, ADEID_COMMENT)) {
850         ad_setentrylen( adp, ADEID_COMMENT, 0 );
851         ad_flush( adp );
852     }
853     ad_close(adp, ADFLAGS_HF);
854     return( AFP_OK );
855 }
856
857 /* ----------------------- */
858 int afp_rmvcomment(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
859 {
860     struct vol          *vol;
861     struct dir          *dir;
862     struct path         *s_path;
863     uint32_t            did;
864     uint16_t            vid;
865
866     *rbuflen = 0;
867     ibuf += 2;
868
869     memcpy( &vid, ibuf, sizeof( vid ));
870     ibuf += sizeof( vid );
871     if (NULL == ( vol = getvolbyvid( vid )) ) {
872         return( AFPERR_PARAM );
873     }
874
875     memcpy( &did, ibuf, sizeof( did ));
876     ibuf += sizeof( did );
877     if (NULL == ( dir = dirlookup( vol, did )) ) {
878         return afp_errno;
879     }
880
881     if (NULL == ( s_path = cname( vol, dir, &ibuf ))) {
882         return get_afp_errno(AFPERR_NOOBJ);
883     }
884     
885     return ad_rmvcomment(obj, vol, s_path);
886 }