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