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