]> arthur.barton.de Git - netatalk.git/blob - libatalk/adouble/ad_flush.c
Fixes
[netatalk.git] / libatalk / adouble / ad_flush.c
1 /*
2  * Copyright (c) 1990,1991 Regents of The University of Michigan.
3  * Copyright (c) 2010      Frank Lahm
4  * All Rights Reserved.
5  *
6  * Permission to use, copy, modify, and distribute this software and
7  * its documentation for any purpose and without fee is hereby granted,
8  * provided that the above copyright notice appears in all copies and
9  * that both that copyright notice and this permission notice appear
10  * in supporting documentation, and that the name of The University
11  * of Michigan not be used in advertising or publicity pertaining to
12  * distribution of the software without specific, written prior
13  * permission. This software is supplied as is without expressed or
14  * implied warranties of any kind.
15  *
16  *  Research Systems Unix Group
17  *  The University of Michigan
18  *  c/o Mike Clark
19  *  535 W. William Street
20  *  Ann Arbor, Michigan
21  *  +1-313-763-0525
22  *  netatalk@itd.umich.edu
23  */
24
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif /* HAVE_CONFIG_H */
28
29 #include <string.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <errno.h>
33 #include <arpa/inet.h>
34
35 #include <atalk/adouble.h>
36 #include <atalk/ea.h>
37 #include <atalk/logger.h>
38 #include <atalk/bstrlib.h>
39 #include <atalk/bstradd.h>
40
41 #include "ad_lock.h"
42
43 static const uint32_t set_eid[] = {
44     0,1,2,3,4,5,6,7,8,
45     9,10,11,12,13,14,15,
46     AD_DEV, AD_INO, AD_SYN, AD_ID
47 };
48
49 #define EID_DISK(a) (set_eid[a])
50
51 int fsetrsrcea(struct adouble *ad, int fd, const char *eaname, const void *value, size_t size, int flags)
52 {
53     if ((ad->ad_maxeafssize == 0) || (ad->ad_maxeafssize >= size)) {
54         LOG(log_debug, logtype_default, "fsetrsrcea(\"%s\"): size: %zu", eaname, size);
55         if (sys_fsetxattr(fd, eaname, value, size, 0) == -1)
56             return -1;
57         return 0;
58     }
59
60     /* rsrcfork is larger then maximum EA support by fs so we have to split it */
61     int i;
62     int eas = (size / ad->ad_maxeafssize);
63     size_t remain = size - (eas * ad->ad_maxeafssize);
64     bstring eachunk;
65
66     LOG(log_debug, logtype_default, "fsetrsrcea(\"%s\"): size: %zu, maxea: %zu, eas: %d, remain: %zu",
67         eaname, size, ad->ad_maxeafssize, eas, remain);
68
69     for (i = 0; i < eas; i++) {
70         if ((eachunk = bformat("%s.%d", eaname, i + 1)) == NULL)
71             return -1;
72         if (sys_fsetxattr(fd, bdata(eachunk), value + (i * ad->ad_maxeafssize), ad->ad_maxeafssize, 0) == -1) {
73             LOG(log_error, logtype_default, "fsetrsrcea(\"%s\"): %s", bdata(eachunk), strerror(errno));
74             bdestroy(eachunk);
75             return -1;
76         }
77         bdestroy(eachunk);
78     }
79
80     if ((eachunk = bformat("%s.%d", eaname, i + 1)) == NULL)
81         return -1;
82     if (sys_fsetxattr(fd, bdata(eachunk), value + (i * ad->ad_maxeafssize), remain, 0) == -1) {
83         LOG(log_error, logtype_default, "fsetrsrcea(\"%s\"): %s", bdata(eachunk), strerror(errno));
84         return -1;
85     }
86     bdestroy(eachunk);
87
88     return 0;
89 }
90
91 /*
92  * Rebuild any header information that might have changed.
93  */
94 int  ad_rebuild_adouble_header(struct adouble *ad)
95 {
96     uint32_t       eid;
97     uint32_t       temp;
98     uint16_t       nent;
99     char        *buf, *nentp;
100     int             len;
101
102     buf = ad->ad_data;
103
104     temp = htonl( ad->ad_magic );
105     memcpy(buf, &temp, sizeof( temp ));
106     buf += sizeof( temp );
107
108     temp = htonl( ad->ad_version );
109     memcpy(buf, &temp, sizeof( temp ));
110     buf += sizeof( temp );
111
112     buf += sizeof( ad->ad_filler );
113
114     nentp = buf;
115     buf += sizeof( nent );
116     for ( eid = 0, nent = 0; eid < ADEID_MAX; eid++ ) {
117         if ( ad->ad_eid[ eid ].ade_off == 0 ) {
118             continue;
119         }
120         temp = htonl( EID_DISK(eid) );
121         memcpy(buf, &temp, sizeof( temp ));
122         buf += sizeof( temp );
123
124         temp = htonl( ad->ad_eid[ eid ].ade_off );
125         memcpy(buf, &temp, sizeof( temp ));
126         buf += sizeof( temp );
127
128         temp = htonl( ad->ad_eid[ eid ].ade_len );
129         memcpy(buf, &temp, sizeof( temp ));
130         buf += sizeof( temp );
131         nent++;
132     }
133     nent = htons( nent );
134     memcpy(nentp, &nent, sizeof( nent ));
135
136     switch (ad->ad_vers) {
137     case AD_VERSION2:
138         len = ad_getentryoff(ad, ADEID_RFORK);
139         break;
140     case AD_VERSION_EA:
141         len = AD_DATASZ_EA;
142         break;
143     default:
144         LOG(log_error, logtype_afpd, "Unexpected adouble version");
145         len = 0;
146         break;
147     }
148
149     return len;
150 }
151
152 /* -------------------
153  * XXX copy only header with same size or comment
154  * doesn't work well for adouble with different version.
155  *
156  */
157 int ad_copy_header(struct adouble *add, struct adouble *ads)
158 {
159     uint32_t       eid;
160     uint32_t       len;
161
162     for ( eid = 0; eid < ADEID_MAX; eid++ ) {
163         if ( ads->ad_eid[ eid ].ade_off == 0 ) {
164             continue;
165         }
166
167         if ( add->ad_eid[ eid ].ade_off == 0 ) {
168             continue;
169         }
170
171         len = ads->ad_eid[ eid ].ade_len;
172         if (!len) {
173             continue;
174         }
175
176         if (eid != ADEID_COMMENT && add->ad_eid[ eid ].ade_len != len ) {
177             continue;
178         }
179
180         ad_setentrylen( add, eid, len );
181         memcpy( ad_entry( add, eid ), ad_entry( ads, eid ), len );
182     }
183     add->ad_rlen = ads->ad_rlen;
184     return 0;
185 }
186
187 int ad_flush(struct adouble *ad)
188 {
189     int len;
190
191     LOG(log_debug, logtype_default, "ad_flush(%s)", adflags2logstr(ad->ad_adflags));
192
193     struct ad_fd *adf;
194
195     switch (ad->ad_vers) {
196     case AD_VERSION2:
197         adf = ad->ad_mdp;
198         break;
199     case AD_VERSION_EA:
200         adf = &ad->ad_data_fork;
201         break;
202     default:
203         LOG(log_error, logtype_afpd, "ad_flush: unexpected adouble version");
204         return -1;
205     }
206
207     if ((adf->adf_flags & O_RDWR)) {
208         if (ad_getentryoff(ad, ADEID_RFORK)) {
209             if (ad->ad_rlen > 0xffffffff)
210                 ad_setentrylen(ad, ADEID_RFORK, 0xffffffff);
211             else
212                 ad_setentrylen(ad, ADEID_RFORK, ad->ad_rlen);
213         }
214         len = ad->ad_ops->ad_rebuild_header(ad);
215
216         switch (ad->ad_vers) {
217         case AD_VERSION2:
218             if (adf_pwrite(ad->ad_mdp, ad->ad_data, len, 0) != len) {
219                 if (errno == 0)
220                     errno = EIO;
221                 return( -1 );
222             }
223             break;
224         case AD_VERSION_EA:
225             if (AD_META_OPEN(ad)) {
226                 if (sys_fsetxattr(ad_data_fileno(ad), AD_EA_META, ad->ad_data, AD_DATASZ_EA, 0) != 0) {
227                     LOG(log_error, logtype_afpd, "ad_flush: sys_fsetxattr error: %s",
228                         strerror(errno));
229                     return -1;
230                 }
231             }
232 #ifndef HAVE_EAFD
233             if (AD_RSRC_OPEN(ad) && (ad->ad_rlen > 0)) {
234                 if (fsetrsrcea(ad, ad_data_fileno(ad), AD_EA_RESO, ad->ad_resforkbuf, ad->ad_rlen, 0) == -1)
235                     return -1;
236             }
237 #endif
238             break;
239         default:
240             LOG(log_error, logtype_afpd, "ad_flush: unexpected adouble version");
241             return -1;
242         }
243     }
244
245     return( 0 );
246 }
247
248 static int ad_data_closefd(struct adouble *ad)
249 {
250     int ret = 0;
251
252     if (ad_data_fileno(ad) == -2) {
253         free(ad->ad_data_fork.adf_syml);
254         ad->ad_data_fork.adf_syml = NULL;
255     } else {
256         if (close(ad_data_fileno(ad)) < 0)
257             ret = -1;
258     }
259     ad_data_fileno(ad) = -1;
260     return ret;
261 }
262
263 /*!
264  * Close a struct adouble freeing all resources
265  */
266 int ad_close(struct adouble *ad, int adflags)
267 {
268     int err = 0;
269
270     if (ad == NULL)
271         return err;
272
273     LOG(log_debug, logtype_default, "ad_close(%s)", adflags2logstr(adflags));
274
275     if ((adflags & ADFLAGS_DF)
276         && (ad_data_fileno(ad) >= 0 || ad_data_fileno(ad) == -2) /* -2 means symlink */
277         && --ad->ad_data_fork.adf_refcount == 0) {
278         if (ad_data_closefd(ad) < 0)
279             err = -1;
280         adf_lock_free(&ad->ad_data_fork);
281     }
282
283     if ((adflags & ADFLAGS_HF)) {
284         switch (ad->ad_vers) {
285         case AD_VERSION2:
286             if ((ad_meta_fileno(ad) != -1) && !(--ad->ad_mdp->adf_refcount)) {
287                 if (close( ad_meta_fileno(ad) ) < 0)
288                     err = -1;
289                 ad_meta_fileno(ad) = -1;
290                 adf_lock_free(ad->ad_mdp);
291             }
292             break;
293
294         case AD_VERSION_EA:
295             if ((ad_data_fileno(ad) >= 0 || ad_data_fileno(ad) == -2) /* -2 means symlink */ 
296                 && !(--ad->ad_data_fork.adf_refcount)) {
297                 if (ad_data_closefd(ad) < 0)
298                     err = -1;
299                 adf_lock_free(&ad->ad_data_fork);
300             }
301             break;
302
303         default:
304             LOG(log_error, logtype_default, "ad_close: unknown AD version");
305             errno = EIO;
306             return -1;
307         }
308     }
309
310     if ((adflags & ADFLAGS_RF)) {
311         switch (ad->ad_vers) {
312         case AD_VERSION2:
313             /* Do nothing as ADFLAGS_RF == ADFLAGS_HF */
314             break;
315
316         case AD_VERSION_EA:
317 #ifndef HAVE_EAFD
318             LOG(log_debug, logtype_default, "ad_close: ad->ad_rlen: %zu", ad->ad_rlen);
319             if (ad->ad_rlen > 0)
320                 if (fsetrsrcea(ad, ad_data_fileno(ad), AD_EA_RESO, ad->ad_resforkbuf, ad->ad_rlen, 0) == -1)
321                     err = -1;
322 #endif
323             if ((ad_data_fileno(ad) >= 0 || ad_data_fileno(ad) == -2) /* -2 means symlink */ 
324                 && !(--ad->ad_data_fork.adf_refcount)) {
325                 if (ad_data_closefd(ad) < 0)
326                     err = -1;
327                 adf_lock_free(&ad->ad_data_fork);
328                 if (ad->ad_resforkbuf)
329                     free(ad->ad_resforkbuf);
330                 ad->ad_resforkbuf = NULL;
331                 ad->ad_rlen = 0;
332             }
333             break;
334
335         default:
336             LOG(log_error, logtype_default, "ad_close: unknown AD version");
337             errno = EIO;
338             return -1;
339         }
340     }
341
342     return err;
343 }