]> arthur.barton.de Git - netatalk.git/blob - etc/afpd/extattrs.c
afpd: Solaris locking problem, bug #559
[netatalk.git] / etc / afpd / extattrs.c
1 /*
2   Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
3
4   This program is free software; you can redistribute it and/or modify
5   it under the terms of the GNU General Public License as published by
6   the Free Software Foundation; either version 2 of the License, or
7   (at your option) any later version.
8
9   This program is distributed in the hope that it will be useful,
10   but WITHOUT ANY WARRANTY; without even the implied warranty of
11   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12   GNU General Public License for more details.
13 */
14
15 #ifdef HAVE_CONFIG_H
16 #include "config.h"
17 #endif /* HAVE_CONFIG_H */
18
19 #include <unistd.h>
20 #include <errno.h>
21 #include <stdlib.h>
22 #include <string.h>
23
24 #include <atalk/adouble.h>
25 #include <atalk/util.h>
26 #include <atalk/vfs.h>
27 #include <atalk/afp.h>
28 #include <atalk/logger.h>
29 #include <atalk/ea.h>
30 #include <atalk/globals.h>
31 #include <atalk/netatalk_conf.h>
32
33 #include "volume.h"
34 #include "desktop.h"
35 #include "directory.h"
36 #include "fork.h"
37 #include "extattrs.h"
38
39 static const char *ea_finderinfo = "com.apple.FinderInfo";
40 static const char *ea_resourcefork = "com.apple.ResourceFork";
41
42 /* This should be big enough to consecutively store the names of all attributes */
43 static char attrnamebuf[ATTRNAMEBUFSIZ];
44
45 #ifdef DEBUG
46 static void hexdump(void *m, size_t l) {
47     char *p = m;
48     int count = 0, len;
49     char buf[100];
50     char *bufp = buf;
51
52     while (l--) {
53         len = sprintf(bufp, "%02x ", *p++);
54         bufp += len;
55         count++;
56
57         if ((count % 16) == 0) {
58             LOG(log_debug9, logtype_afpd, "%s", buf);
59             bufp = buf;
60         }
61     }
62 }
63 #endif
64
65 /***************************************
66  * AFP funcs
67  ****************************************/
68
69 /*
70   Note: we're being called twice. Firstly the client only want the size of all
71   EA names, secondly it wants these names. In order to avoid scanning EAs twice
72   we cache them in a static buffer.
73 */
74 int afp_listextattr(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
75 {
76     int                 ret, oflag = 0, adflags = 0, fd = -1;
77     uint16_t            vid, bitmap, uint16;
78     uint32_t            did, maxreply, tmpattr;
79     struct vol          *vol;
80     struct dir          *dir;
81     struct path         *s_path;
82     struct stat         *st;
83     struct adouble      ad, *adp = NULL;
84     struct ofork        *opened = NULL;
85     char                *uname, *FinderInfo;
86     char                emptyFinderInfo[32] = { 0 };
87
88     static int          buf_valid = 0;
89     static size_t       attrbuflen = 0;
90
91     *rbuflen = 0;
92     ibuf += 2;
93
94     /* Get Bitmap and MaxReplySize first */
95     memcpy( &bitmap, ibuf +6, sizeof(bitmap));
96     bitmap = ntohs( bitmap );
97
98     memcpy( &maxreply, ibuf + 14, sizeof (maxreply));
99     maxreply = ntohl( maxreply );
100
101     /*
102       If its the first request with maxreply=0 or if we didn't mark our buffers valid for
103       whatever reason (just a safety check, it should be valid), then scan for attributes
104     */
105     if ((maxreply == 0) || (buf_valid == 0)) {
106
107         attrbuflen = 0;
108
109         memcpy( &vid, ibuf, sizeof(vid));
110         ibuf += sizeof(vid);
111         if (NULL == ( vol = getvolbyvid( vid )) ) {
112             LOG(log_debug, logtype_afpd, "afp_listextattr: getvolbyvid error: %s", strerror(errno));
113             return AFPERR_ACCESS;
114         }
115
116         memcpy( &did, ibuf, sizeof(did));
117         ibuf += sizeof(did);
118         if (NULL == ( dir = dirlookup( vol, did )) ) {
119             LOG(log_debug, logtype_afpd, "afp_listextattr: dirlookup error: %s", strerror(errno));
120             return afp_errno;
121         }
122
123         if (bitmap & kXAttrNoFollow)
124             oflag = O_NOFOLLOW;
125         /* Skip Bitmap, ReqCount, StartIndex and maxreply*/
126         ibuf += 12;
127
128         /* get name */
129         if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
130             LOG(log_debug, logtype_afpd, "afp_listextattr: cname error: %s", strerror(errno));
131             return AFPERR_NOOBJ;
132         }
133
134         st   = &s_path->st;
135         if (!s_path->st_valid) {
136             /* it's a dir in our cache, we didn't stat it, do it now */
137             of_statdir(vol, s_path);
138         }
139         if ( s_path->st_errno != 0 ) {
140             return( AFPERR_NOOBJ );
141         }
142
143         uname = s_path->u_name;
144         /*
145           We have to check the FinderInfo for the file, because if they aren't all 0
146           we must return the synthetic attribute "com.apple.FinderInfo".
147           Note: the client will never (never seen in traces) request that attribute
148           via FPGetExtAttr !
149         */
150
151         adp = &ad;
152         ad_init(adp, vol);
153
154         if (path_isadir(s_path)) {
155             LOG(log_debug, logtype_afpd, "afp_listextattr(%s): is a dir", uname);
156             adflags = ADFLAGS_DIR;
157         } else {
158             LOG(log_debug, logtype_afpd, "afp_listextattr(%s): is a file", uname);
159             opened = of_findname(vol, s_path);
160             if (opened) {
161                 adp = opened->of_ad;
162                 fd = ad_meta_fileno(adp);
163             }
164         }
165
166         if (ad_metadata(uname, adflags, adp) != 0 ) {
167             switch (errno) {
168             case ENOENT:
169                 break;
170             case EACCES:
171                 LOG(log_error, logtype_afpd, "afp_listextattr(%s): %s: check resource fork permission?",
172                     uname, strerror(errno));
173                 return AFPERR_ACCESS;
174             default:
175                 LOG(log_error, logtype_afpd, "afp_listextattr(%s): error getting metadata: %s", uname, strerror(errno));
176                 return AFPERR_MISC;
177             }
178         } else {
179             FinderInfo = ad_entry(adp, ADEID_FINDERI);
180             /* Check if FinderInfo equals default and empty FinderInfo*/
181             if (memcmp(FinderInfo, emptyFinderInfo, 32) != 0) {
182                 /* FinderInfo contains some non 0 bytes -> include "com.apple.FinderInfo" */
183                 strcpy(attrnamebuf, ea_finderinfo);
184                 attrbuflen += strlen(ea_finderinfo) + 1;
185                 LOG(log_debug7, logtype_afpd, "afp_listextattr(%s): sending com.apple.FinderInfo", uname);
186             }
187
188             /* Now check for Ressource fork and add virtual EA "com.apple.ResourceFork" if size > 0 */
189             LOG(log_debug7, logtype_afpd, "afp_listextattr(%s): Ressourcefork size: %llu", uname, adp->ad_rlen);
190
191             if (adp->ad_rlen > 0) {
192                 LOG(log_debug7, logtype_afpd, "afp_listextattr(%s): sending com.apple.RessourceFork.", uname);
193                 strcpy(attrnamebuf + attrbuflen, ea_resourcefork);
194                 attrbuflen += strlen(ea_resourcefork) + 1;
195             }
196         }
197         
198         ret = vol->vfs->vfs_ea_list(vol, attrnamebuf, &attrbuflen, uname, oflag, fd);
199
200         switch (ret) {
201         case AFPERR_BADTYPE:
202             /* its a symlink and client requested O_NOFOLLOW */
203             LOG(log_debug, logtype_afpd, "afp_listextattr(%s): encountered symlink with kXAttrNoFollow", uname);
204             attrbuflen = 0;
205             buf_valid = 0;
206             ret = AFP_OK;
207             goto exit;
208         case AFPERR_MISC:
209             attrbuflen = 0;
210             goto exit;
211         default:
212             buf_valid = 1;
213         }
214     } /* if ((maxreply == 0) || (buf_valid == 0)) */
215
216     /* Start building reply packet */
217     bitmap = htons(bitmap);
218     memcpy( rbuf, &bitmap, sizeof(bitmap));
219     rbuf += sizeof(bitmap);
220     *rbuflen += sizeof(bitmap);
221
222     tmpattr = htonl(attrbuflen);
223     memcpy( rbuf, &tmpattr, sizeof(tmpattr));
224     rbuf += sizeof(tmpattr);
225     *rbuflen += sizeof(tmpattr);
226
227     /* Only copy buffer if the client asked for it (2nd request, maxreply>0)
228        and we didnt have an error (buf_valid) */
229     if (maxreply && buf_valid) {
230         memcpy( rbuf, attrnamebuf, attrbuflen);
231         *rbuflen += attrbuflen;
232         buf_valid = 0;
233     }
234
235     ret = AFP_OK;
236
237 exit:
238     if (ret != AFP_OK)
239         buf_valid = 0;
240
241     if (adp)
242         ad_close(adp, ADFLAGS_HF);
243
244     return ret;
245 }
246
247 static char *to_stringz(char *ibuf, uint16_t len)
248 {
249 static char attrmname[256];
250
251     if (len > 255)
252         /* dont fool with us */
253         len = 255;
254
255     /* we must copy the name as its not 0-terminated and I DONT WANT TO WRITE to ibuf */
256     strlcpy(attrmname, ibuf, len + 1);
257     return attrmname;
258 }
259
260 int afp_getextattr(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf, size_t *rbuflen)
261 {
262     int                 ret, oflag = 0, fd = -1;
263     uint16_t            vid, bitmap, attrnamelen;
264     uint32_t            did, maxreply;
265     char                attruname[256];
266     struct vol          *vol;
267     struct dir          *dir;
268     struct path         *s_path;
269     struct adouble      ad, *adp = NULL;
270     struct ofork        *opened = NULL;
271
272
273     *rbuflen = 0;
274     ibuf += 2;
275
276     memcpy( &vid, ibuf, sizeof(vid));
277     ibuf += sizeof(vid);
278     if (NULL == ( vol = getvolbyvid( vid )) ) {
279         LOG(log_debug, logtype_afpd, "afp_getextattr: getvolbyvid error: %s", strerror(errno));
280         return AFPERR_ACCESS;
281     }
282
283     memcpy( &did, ibuf, sizeof(did));
284     ibuf += sizeof(did);
285     if (NULL == ( dir = dirlookup( vol, did )) ) {
286         LOG(log_debug, logtype_afpd, "afp_getextattr: dirlookup error: %s", strerror(errno));
287         return afp_errno;
288     }
289
290     memcpy( &bitmap, ibuf, sizeof(bitmap));
291     bitmap = ntohs( bitmap );
292     ibuf += sizeof(bitmap);
293
294     if (bitmap & kXAttrNoFollow)
295         oflag = O_NOFOLLOW;
296
297     /* Skip Offset and ReqCount */
298     ibuf += 16;
299
300     /* Get MaxReply */
301     memcpy(&maxreply, ibuf, sizeof(maxreply));
302     maxreply = ntohl(maxreply);
303     ibuf += sizeof(maxreply);
304
305     /* get name */
306     if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
307         LOG(log_debug, logtype_afpd, "afp_getextattr: cname error: %s", strerror(errno));
308         return AFPERR_NOOBJ;
309     }
310
311     if ((unsigned long)ibuf & 1)
312         ibuf++;
313
314     /* get length of EA name */
315     memcpy(&attrnamelen, ibuf, sizeof(attrnamelen));
316     attrnamelen = ntohs(attrnamelen);
317     ibuf += sizeof(attrnamelen);
318
319     LOG(log_debug, logtype_afpd, "afp_getextattr(%s): EA: %s", s_path->u_name, to_stringz(ibuf, attrnamelen));
320
321     /* Convert EA name in utf8 to unix charset */
322     if ( 0 >= convert_string(CH_UTF8_MAC, obj->options.unixcharset, ibuf, attrnamelen, attruname, 256) )
323         return AFPERR_MISC;
324
325     /* write bitmap now */
326     bitmap = htons(bitmap);
327     memcpy(rbuf, &bitmap, sizeof(bitmap));
328     rbuf += sizeof(bitmap);
329     *rbuflen += sizeof(bitmap);
330
331     if (path_isadir(s_path)) {
332         LOG(log_debug, logtype_afpd, "afp_getextattr(%s): is a dir", s_path->u_name);
333     } else {
334         LOG(log_debug, logtype_afpd, "afp_getextattr(%s): is a file", s_path->u_name);
335         opened = of_findname(vol, s_path);
336         if (opened) {
337             adp = opened->of_ad;
338             fd = ad_meta_fileno(adp);
339         }
340     }
341
342     /*
343       Switch on maxreply:
344       if its 0 we must return the size of the requested attribute,
345       if its non 0 we must return the attribute.
346     */
347     if (maxreply == 0)
348         ret = vol->vfs->vfs_ea_getsize(vol, rbuf, rbuflen, s_path->u_name, oflag, attruname, fd);
349     else
350         ret = vol->vfs->vfs_ea_getcontent(vol, rbuf, rbuflen, s_path->u_name, oflag, attruname, maxreply, fd);
351
352     return ret;
353 }
354
355 int afp_setextattr(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
356 {
357     int                 oflag = 0, ret, fd = -1;
358     uint16_t            vid, bitmap, attrnamelen;
359     uint32_t            did, attrsize;
360     char                attruname[256];
361     char                *attrmname;
362     struct vol          *vol;
363     struct dir          *dir;
364     struct path         *s_path;
365     struct adouble      ad, *adp = NULL;
366     struct ofork        *opened = NULL;
367
368     *rbuflen = 0;
369     ibuf += 2;
370
371     memcpy( &vid, ibuf, sizeof(vid));
372     ibuf += sizeof(vid);
373     if (NULL == ( vol = getvolbyvid( vid )) ) {
374         LOG(log_debug, logtype_afpd, "afp_setextattr: getvolbyvid error: %s", strerror(errno));
375         return AFPERR_ACCESS;
376     }
377
378     memcpy( &did, ibuf, sizeof(did));
379     ibuf += sizeof(did);
380     if (NULL == ( dir = dirlookup( vol, did )) ) {
381         LOG(log_debug, logtype_afpd, "afp_setextattr: dirlookup error: %s", strerror(errno));
382         return afp_errno;
383     }
384
385     memcpy( &bitmap, ibuf, sizeof(bitmap));
386     bitmap = ntohs( bitmap );
387     ibuf += sizeof(bitmap);
388
389     if (bitmap & kXAttrNoFollow)
390         oflag |= O_NOFOLLOW;
391
392     if (bitmap & kXAttrCreate)
393         oflag |= O_CREAT;
394     else if (bitmap & kXAttrReplace)
395         oflag |= O_TRUNC;
396
397     /* Skip Offset */
398     ibuf += 8;
399
400     /* get name */
401     if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
402         LOG(log_debug, logtype_afpd, "afp_setextattr: cname error: %s", strerror(errno));
403         return AFPERR_NOOBJ;
404     }
405
406     if (path_isadir(s_path)) {
407         LOG(log_debug, logtype_afpd, "afp_setextattr(%s): is a dir", s_path->u_name);
408     } else {
409         LOG(log_debug, logtype_afpd, "afp_setextattr(%s): is a file", s_path->u_name);
410         opened = of_findname(vol, s_path);
411         if (opened) {
412             adp = opened->of_ad;
413             fd = ad_meta_fileno(adp);
414         }
415     }
416
417
418     if ((unsigned long)ibuf & 1)
419         ibuf++;
420
421     /* get length of EA name */
422     memcpy(&attrnamelen, ibuf, sizeof(attrnamelen));
423     attrnamelen = ntohs(attrnamelen);
424     ibuf += sizeof(attrnamelen);
425     if (attrnamelen > 255)
426         return AFPERR_PARAM;
427
428     attrmname = ibuf;
429     /* Convert EA name in utf8 to unix charset */
430     if ( 0 >= convert_string(CH_UTF8_MAC, obj->options.unixcharset, attrmname, attrnamelen, attruname, 256))
431         return AFPERR_MISC;
432
433     ibuf += attrnamelen;
434     /* get EA size */
435     memcpy(&attrsize, ibuf, sizeof(attrsize));
436     attrsize = ntohl(attrsize);
437     ibuf += sizeof(attrsize);
438     if (attrsize > MAX_EA_SIZE)
439         /* we arbitrarily make this fatal */
440         return AFPERR_PARAM;
441
442     LOG(log_debug, logtype_afpd, "afp_setextattr(%s): EA: %s, size: %u", s_path->u_name, to_stringz(attrmname, attrnamelen), attrsize);
443
444     ret = vol->vfs->vfs_ea_set(vol, s_path->u_name, attruname, ibuf, attrsize, oflag, fd);
445
446     return ret;
447 }
448
449 int afp_remextattr(AFPObj *obj _U_, char *ibuf, size_t ibuflen _U_, char *rbuf _U_, size_t *rbuflen)
450 {
451     int                 oflag = 0, ret, fd = -1;
452     uint16_t            vid, bitmap, attrnamelen;
453     uint32_t            did;
454     char                attruname[256];
455     struct vol          *vol;
456     struct dir          *dir;
457     struct path         *s_path;
458     struct adouble      ad, *adp = NULL;
459     struct ofork        *opened = NULL;
460
461     *rbuflen = 0;
462     ibuf += 2;
463
464     memcpy( &vid, ibuf, sizeof(vid));
465     ibuf += sizeof(vid);
466     if (NULL == ( vol = getvolbyvid( vid )) ) {
467         LOG(log_debug, logtype_afpd, "afp_remextattr: getvolbyvid error: %s", strerror(errno));
468         return AFPERR_ACCESS;
469     }
470
471     memcpy( &did, ibuf, sizeof(did));
472     ibuf += sizeof(did);
473     if (NULL == ( dir = dirlookup( vol, did )) ) {
474         LOG(log_debug, logtype_afpd, "afp_remextattr: dirlookup error: %s", strerror(errno));
475         return afp_errno;
476     }
477
478     memcpy( &bitmap, ibuf, sizeof(bitmap));
479     bitmap = ntohs( bitmap );
480     ibuf += sizeof(bitmap);
481
482     if (bitmap & kXAttrNoFollow)
483         oflag |= O_NOFOLLOW;
484
485     /* get name */
486     if (NULL == ( s_path = cname( vol, dir, &ibuf )) ) {
487         LOG(log_debug, logtype_afpd, "afp_remextattr: cname error: %s", strerror(errno));
488         return AFPERR_NOOBJ;
489     }
490
491     if (path_isadir(s_path)) {
492         LOG(log_debug, logtype_afpd, "afp_remextattr(%s): is a dir", s_path->u_name);
493     } else {
494         LOG(log_debug, logtype_afpd, "afp_remextattr(%s): is a file", s_path->u_name);
495         opened = of_findname(vol, s_path);
496         if (opened) {
497             adp = opened->of_ad;
498             fd = ad_meta_fileno(adp);
499         }
500     }
501
502     if ((unsigned long)ibuf & 1)
503         ibuf++;
504
505     /* get length of EA name */
506     memcpy(&attrnamelen, ibuf, sizeof(attrnamelen));
507     attrnamelen = ntohs(attrnamelen);
508     ibuf += sizeof(attrnamelen);
509     if (attrnamelen > 255)
510         return AFPERR_PARAM;
511
512     /* Convert EA name in utf8 to unix charset */
513     if ( 0 >= (convert_string(CH_UTF8_MAC, obj->options.unixcharset,ibuf, attrnamelen, attruname, 256)) )
514         return AFPERR_MISC;
515
516     LOG(log_debug, logtype_afpd, "afp_remextattr(%s): EA: %s", s_path->u_name, to_stringz(ibuf, attrnamelen));
517
518     ret = vol->vfs->vfs_ea_remove(vol, s_path->u_name, attruname, oflag, fd);
519
520     return ret;
521 }
522