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