]> arthur.barton.de Git - netatalk.git/blob - libatalk/vfs/ea_solaris.c
9f367463566b1400b2965d05632c96d309b8e5e6
[netatalk.git] / libatalk / vfs / ea_solaris.c
1 /*
2   $Id: ea_solaris.c,v 1.2 2009-10-26 13:12:00 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 /* According to man fsattr.5 we must define _ATFILE_SOURCE */
17 #define _ATFILE_SOURCE
18
19 #ifdef HAVE_CONFIG_H
20 #include "config.h"
21 #endif /* HAVE_CONFIG_H */
22
23 #include <unistd.h>
24 #include <stdint.h>
25 #include <errno.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <fcntl.h>
31 #include <dirent.h>
32
33 #include <atalk/adouble.h>
34 #include <atalk/ea.h>
35 #include <atalk/afp.h>
36 #include <atalk/logger.h>
37 #include <atalk/volume.h>
38 #include <atalk/vfs.h>
39 #include <atalk/util.h>
40 #include <atalk/unix.h>
41
42
43 /**********************************************************************************
44  * Solaris EA VFS funcs
45  **********************************************************************************/
46
47 /*
48  * Function: sol_get_easize
49  *
50  * Purpose: get size of an EA on Solaris native EA
51  *
52  * Arguments:
53  *
54  *    vol          (r) current volume
55  *    rbuf         (w) DSI reply buffer
56  *    rbuflen      (rw) current length of data in reply buffer
57  *    uname        (r) filename
58  *    oflag        (r) link and create flag
59  *    attruname    (r) name of attribute
60  *
61  * Returns: AFP code: AFP_OK on success or appropiate AFP error code
62  *
63  * Effects:
64  *
65  * Copies EA size into rbuf in network order. Increments *rbuflen +4.
66  */
67 int sol_get_easize(VFS_FUNC_ARGS_EA_GETSIZE)
68 {
69     int                 ret, attrdirfd;
70     uint32_t            attrsize;
71     struct stat         st;
72
73     LOG(log_debug7, logtype_afpd, "sol_getextattr_size(%s): attribute: \"%s\"", uname, attruname);
74
75     if ( -1 == (attrdirfd = attropen(uname, ".", O_RDONLY | oflag))) {
76         if (errno == ELOOP) {
77             /* its a symlink and client requested O_NOFOLLOW  */
78             LOG(log_debug, logtype_afpd, "sol_getextattr_size(%s): encountered symlink with kXAttrNoFollow", uname);
79
80             memset(rbuf, 0, 4);
81             *rbuflen += 4;
82
83             return AFP_OK;
84         }
85         LOG(log_error, logtype_afpd, "sol_getextattr_size: attropen error: %s", strerror(errno));
86         return AFPERR_MISC;
87     }
88
89     if ( -1 == (fstatat(attrdirfd, attruname, &st, 0))) {
90         LOG(log_error, logtype_afpd, "sol_getextattr_size: fstatat error: %s", strerror(errno));
91         ret = AFPERR_MISC;
92         goto exit;
93     }
94     attrsize = (st.st_size > MAX_EA_SIZE) ? MAX_EA_SIZE : st.st_size;
95
96     /* Start building reply packet */
97
98     LOG(log_debug7, logtype_afpd, "sol_getextattr_size(%s): attribute: \"%s\", size: %u", uname, attruname, attrsize);
99
100     /* length of attribute data */
101     attrsize = htonl(attrsize);
102     memcpy(rbuf, &attrsize, 4);
103     *rbuflen += 4;
104
105     ret = AFP_OK;
106
107 exit:
108     close(attrdirfd);
109     return ret;
110 }
111
112 /*
113  * Function: sol_get_eacontent
114  *
115  * Purpose: copy Solaris native EA into rbuf
116  *
117  * Arguments:
118  *
119  *    vol          (r) current volume
120  *    rbuf         (w) DSI reply buffer
121  *    rbuflen      (rw) current length of data in reply buffer
122  *    uname        (r) filename
123  *    oflag        (r) link and create flag
124  *    attruname    (r) name of attribute
125  *    maxreply     (r) maximum EA size as of current specs/real-life
126  *
127  * Returns: AFP code: AFP_OK on success or appropiate AFP error code
128  *
129  * Effects:
130  *
131  * Copies EA into rbuf. Increments *rbuflen accordingly.
132  */
133 int sol_get_eacontent(VFS_FUNC_ARGS_EA_GETCONTENT)
134 {
135     int                 ret, attrdirfd;
136     size_t              toread, okread = 0, len;
137     char                *datalength;
138     struct stat         st;
139
140     if ( -1 == (attrdirfd = attropen(uname, attruname, O_RDONLY | oflag))) {
141         if (errno == ELOOP) {
142             /* its a symlink and client requested O_NOFOLLOW  */
143             LOG(log_debug, logtype_afpd, "sol_getextattr_content(%s): encountered symlink with kXAttrNoFollow", uname);
144
145             memset(rbuf, 0, 4);
146             *rbuflen += 4;
147
148             return AFP_OK;
149         }
150         LOG(log_error, logtype_afpd, "sol_getextattr_content(%s): attropen error: %s", attruname, strerror(errno));
151         return AFPERR_MISC;
152     }
153
154     if ( -1 == (fstat(attrdirfd, &st))) {
155         LOG(log_error, logtype_afpd, "sol_getextattr_content(%s): fstatat error: %s", attruname,strerror(errno));
156         ret = AFPERR_MISC;
157         goto exit;
158     }
159
160     /* Start building reply packet */
161
162     maxreply -= MAX_REPLY_EXTRA_BYTES;
163     if (maxreply > MAX_EA_SIZE)
164         maxreply = MAX_EA_SIZE;
165
166     /* But never send more than the client requested */
167     toread = (maxreply < st.st_size) ? maxreply : st.st_size;
168
169     LOG(log_debug7, logtype_afpd, "sol_getextattr_content(%s): attribute: \"%s\", size: %u", uname, attruname, maxreply);
170
171     /* remember where we must store length of attribute data in rbuf */
172     datalength = rbuf;
173     rbuf += 4;
174     *rbuflen += 4;
175
176     while (1) {
177         len = read(attrdirfd, rbuf, toread);
178         if (len == -1) {
179             LOG(log_error, logtype_afpd, "sol_getextattr_content(%s): read error: %s", attruname, strerror(errno));
180             ret = AFPERR_MISC;
181             goto exit;
182         }
183         okread += len;
184         rbuf += len;
185         *rbuflen += len;
186         if ((len == 0) || (okread == toread))
187             break;
188     }
189
190     okread = htonl((uint32_t)okread);
191     memcpy(datalength, &okread, 4);
192
193     ret = AFP_OK;
194
195 exit:
196     close(attrdirfd);
197     return ret;
198 }
199
200 /*
201  * Function: sol_list_eas
202  *
203  * Purpose: copy names of Solaris native EA into attrnamebuf
204  *
205  * Arguments:
206  *
207  *    vol          (r) current volume
208  *    attrnamebuf  (w) store names a consecutive C strings here
209  *    buflen       (rw) length of names in attrnamebuf
210  *    uname        (r) filename
211  *    oflag        (r) link and create flag
212  *
213  * Returns: AFP code: AFP_OK on success or appropiate AFP error code
214  *
215  * Effects:
216  *
217  * Copies names of all EAs of uname as consecutive C strings into rbuf.
218  * Increments *rbuflen accordingly.
219  */
220 int sol_list_eas(VFS_FUNC_ARGS_EA_LIST)
221 {
222     int ret, attrbuflen = *buflen, len, attrdirfd = 0;
223     struct dirent *dp;
224     DIR *dirp = NULL;
225
226     /* Now list file attribute dir */
227     if ( -1 == (attrdirfd = attropen( uname, ".", O_RDONLY | oflag))) {
228         if (errno == ELOOP) {
229             /* its a symlink and client requested O_NOFOLLOW */
230             ret = AFPERR_BADTYPE;
231             goto exit;
232         }
233         LOG(log_error, logtype_afpd, "sol_list_extattr(%s): error opening atttribute dir: %s", uname, strerror(errno));
234         ret = AFPERR_MISC;
235         goto exit;
236     }
237
238     if (NULL == (dirp = fdopendir(attrdirfd))) {
239         LOG(log_error, logtype_afpd, "sol_list_extattr(%s): error opening atttribute dir: %s", uname, strerror(errno));
240         ret = AFPERR_MISC;
241         goto exit;
242     }
243
244     while ((dp = readdir(dirp)))  {
245         /* check if its "." or ".." */
246         if ((strcmp(dp->d_name, ".") == 0) || (strcmp(dp->d_name, "..") == 0) ||
247             (strcmp(dp->d_name, "SUNWattr_ro") == 0) || (strcmp(dp->d_name, "SUNWattr_rw") == 0))
248             continue;
249
250         len = strlen(dp->d_name);
251
252         /* Convert name to CH_UTF8_MAC and directly store in in the reply buffer */
253         if ( 0 >= ( len = convert_string(vol->v_volcharset, CH_UTF8_MAC, dp->d_name, len, attrnamebuf + attrbuflen, 255)) ) {
254             ret = AFPERR_MISC;
255             goto exit;
256         }
257         if (len == 255)
258             /* convert_string didn't 0-terminate */
259             attrnamebuf[attrbuflen + 255] = 0;
260
261         LOG(log_debug7, logtype_afpd, "sol_list_extattr(%s): attribute: %s", uname, dp->d_name);
262
263         attrbuflen += len + 1;
264         if (attrbuflen > (ATTRNAMEBUFSIZ - 256)) {
265             /* Next EA name could overflow, so bail out with error.
266                FIXME: evantually malloc/memcpy/realloc whatever.
267                Is it worth it ? */
268             LOG(log_warning, logtype_afpd, "sol_list_extattr(%s): running out of buffer for EA names", uname);
269             ret = AFPERR_MISC;
270             goto exit;
271         }
272     }
273
274     ret = AFP_OK;
275
276 exit:
277     if (dirp)
278         closedir(dirp);
279
280     if (attrdirfd > 0)
281         close(attrdirfd);
282
283     *buflen = attrbuflen;
284     return ret;
285 }
286
287 /*
288  * Function: sol_set_ea
289  *
290  * Purpose: set a Solaris native EA
291  *
292  * Arguments:
293  *
294  *    vol          (r) current volume
295  *    uname        (r) filename
296  *    attruname    (r) EA name
297  *    ibuf         (r) buffer with EA content
298  *    attrsize     (r) length EA in ibuf
299  *    oflag        (r) link and create flag
300  *
301  * Returns: AFP code: AFP_OK on success or appropiate AFP error code
302  *
303  * Effects:
304  *
305  * Copies names of all EAs of uname as consecutive C strings into rbuf.
306  * Increments *rbuflen accordingly.
307  */
308 int sol_set_ea(VFS_FUNC_ARGS_EA_SET)
309 {
310     int attrdirfd;
311
312     if ( -1 == (attrdirfd = attropen(uname, attruname, oflag, 0666))) {
313         if (errno == ELOOP) {
314             /* its a symlink and client requested O_NOFOLLOW  */
315             LOG(log_debug, logtype_afpd, "afp_setextattr(%s): encountered symlink with kXAttrNoFollow", uname);
316             return AFP_OK;
317         }
318         LOG(log_error, logtype_afpd, "afp_setextattr(%s): attropen error: %s", uname, strerror(errno));
319         return AFPERR_MISC;
320     }
321
322     if ((write(attrdirfd, ibuf, attrsize)) != attrsize) {
323         LOG(log_error, logtype_afpd, "afp_setextattr(%s): read error: %s", attruname, strerror(errno));
324         return AFPERR_MISC;
325     }
326
327     return AFP_OK;
328 }
329
330 /*
331  * Function: sol_remove_ea
332  *
333  * Purpose: remove a Solaris native EA
334  *
335  * Arguments:
336  *
337  *    vol          (r) current volume
338  *    uname        (r) filename
339  *    attruname    (r) EA name
340  *    oflag        (r) link and create flag
341  *
342  * Returns: AFP code: AFP_OK on success or appropiate AFP error code
343  *
344  * Effects:
345  *
346  * Removes EA attruname from file uname.
347  */
348 int sol_remove_ea(VFS_FUNC_ARGS_EA_REMOVE)
349 {
350     int attrdirfd;
351
352     if ( -1 == (attrdirfd = attropen(uname, ".", oflag))) {
353         switch (errno) {
354         case ELOOP:
355             /* its a symlink and client requested O_NOFOLLOW  */
356             LOG(log_debug, logtype_afpd, "afp_remextattr(%s): encountered symlink with kXAttrNoFollow", uname);
357             return AFP_OK;
358         case EACCES:
359             LOG(log_debug, logtype_afpd, "afp_remextattr(%s): unlinkat error: %s", uname, strerror(errno));
360             return AFPERR_ACCESS;
361         default:
362             LOG(log_error, logtype_afpd, "afp_remextattr(%s): attropen error: %s", uname, strerror(errno));
363             return AFPERR_MISC;
364         }
365     }
366
367     if ( -1 == (unlinkat(attrdirfd, attruname, 0)) ) {
368         if (errno == EACCES) {
369             LOG(log_debug, logtype_afpd, "afp_remextattr(%s): unlinkat error: %s", uname, strerror(errno));
370             return AFPERR_ACCESS;
371         }
372         LOG(log_error, logtype_afpd, "afp_remextattr(%s): unlinkat error: %s", uname, strerror(errno));
373         return AFPERR_MISC;
374     }
375
376 }