]> arthur.barton.de Git - netatalk.git/blob - libatalk/adouble/ad_conv.c
Resource fork conversion from AppleDouble v2 broken, bug #568
[netatalk.git] / libatalk / adouble / ad_conv.c
1 /*
2  * Copyright (c) 2012 Frank Lahm
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 /*!
16  * @file
17  * Part of Netatalk's AppleDouble implementatation
18  * @sa include/atalk/adouble.h
19  */
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif /* HAVE_CONFIG_H */
24
25 #include <errno.h>
26 #include <sys/param.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <stdarg.h>
30 #include <arpa/inet.h>
31
32 #include <atalk/logger.h>
33 #include <atalk/adouble.h>
34 #include <atalk/util.h>
35 #include <atalk/unix.h>
36 #include <atalk/ea.h>
37 #include <atalk/bstrlib.h>
38 #include <atalk/bstradd.h>
39 #include <atalk/compat.h>
40 #include <atalk/errchk.h>
41 #include <atalk/volume.h>
42
43 #include "ad_lock.h"
44
45 static char emptyfilad[32] = {0,0,0,0,0,0,0,0,
46                               0,0,0,0,0,0,0,0,
47                               0,0,0,0,0,0,0,0,
48                               0,0,0,0,0,0,0,0};
49
50 static char emptydirad[32] = {0,0,0,0,0,0,0,0,
51                               0,0,0,0,0,0,1,0,
52                               0,0,0,0,0,0,0,0,
53                               0,0,0,0,0,0,0,0};
54
55 static int ad_conv_v22ea_hf(const char *path, const struct stat *sp, const struct vol *vol)
56 {
57     EC_INIT;
58     struct adouble adv2;
59     struct adouble adea;
60     int adflags;
61     uint32_t ctime, mtime, afpinfo = 0;
62     char *emptyad;
63
64     LOG(log_debug, logtype_ad,"ad_conv_v22ea_hf(\"%s\"): BEGIN", fullpathname(path));
65
66     switch (S_IFMT & sp->st_mode) {
67     case S_IFREG:
68     case S_IFDIR:
69         break;
70     default:
71         return 0;
72     }
73
74     ad_init(&adea, vol);
75     ad_init_old(&adv2, AD_VERSION2, adea.ad_options);
76
77     adflags = S_ISDIR(sp->st_mode) ? ADFLAGS_DIR : 0;
78
79     /* Open and lock adouble:v2 file */
80     EC_ZERO( ad_open(&adv2, path, adflags | ADFLAGS_HF | ADFLAGS_RDWR) );
81
82     EC_NEG1_LOG( ad_tmplock(&adv2, ADEID_RFORK, ADLOCK_WR | ADLOCK_FILELOCK, 0, 0, 0) );
83     EC_NEG1_LOG( adv2.ad_ops->ad_header_read(path, &adv2, sp) );
84
85     /* Check if it's a non-empty header */
86     if (S_ISREG(sp->st_mode))
87         emptyad = &emptyfilad[0];
88     else
89         emptyad = &emptydirad[0];
90
91     if (ad_getentrylen(&adv2, ADEID_COMMENT) != 0)
92         goto copy;
93     if (ad_getentryoff(&adv2, ADEID_FINDERI)
94         && (ad_getentrylen(&adv2, ADEID_FINDERI) == ADEDLEN_FINDERI)
95         && (memcmp(ad_entry(&adv2, ADEID_FINDERI), emptyad, ADEDLEN_FINDERI) != 0))
96         goto copy;
97     if (ad_getentryoff(&adv2, ADEID_FILEDATESI)) {
98         EC_ZERO_LOG( ad_getdate(&adv2, AD_DATE_CREATE | AD_DATE_UNIX, &ctime) );
99         EC_ZERO_LOG( ad_getdate(&adv2, AD_DATE_MODIFY | AD_DATE_UNIX, &mtime) );
100         if ((ctime != mtime) || (mtime != sp->st_mtime))
101             goto copy;
102     }
103     if (ad_getentryoff(&adv2, ADEID_AFPFILEI)) {
104         if (memcmp(ad_entry(&adv2, ADEID_AFPFILEI), &afpinfo, ADEDLEN_AFPFILEI) != 0)
105             goto copy;
106     }
107
108     LOG(log_debug, logtype_ad,"ad_conv_v22ea_hf(\"%s\"): default adouble", fullpathname(path), ret);
109     goto EC_CLEANUP;
110
111 copy:
112     /* Create a adouble:ea meta EA */
113     LOG(log_debug, logtype_ad,"ad_conv_v22ea_hf(\"%s\"): copying adouble", fullpathname(path), ret);
114     EC_ZERO_LOGSTR( ad_open(&adea, path, adflags | ADFLAGS_HF | ADFLAGS_RDWR | ADFLAGS_CREATE),
115                     "ad_conv_v22ea_hf(\"%s\"): error creating metadata EA: %s",
116                     fullpathname(path), strerror(errno));
117     EC_ZERO_LOG( ad_copy_header(&adea, &adv2) );
118     ad_flush(&adea);
119
120 EC_CLEANUP:
121     EC_ZERO_LOG( ad_close(&adv2, ADFLAGS_HF | ADFLAGS_SETSHRMD) );
122     EC_ZERO_LOG( ad_close(&adea, ADFLAGS_HF | ADFLAGS_SETSHRMD) );
123     LOG(log_debug, logtype_ad,"ad_conv_v22ea_hf(\"%s\"): END: %d", fullpathname(path), ret);
124     EC_EXIT;
125 }
126
127 static int ad_conv_v22ea_rf(const char *path, const struct stat *sp, const struct vol *vol)
128 {
129     EC_INIT;
130     struct adouble adv2;
131     struct adouble adea;
132
133     LOG(log_debug, logtype_ad,"ad_conv_v22ea_rf(\"%s\"): BEGIN", fullpathname(path));
134
135     switch (S_IFMT & sp->st_mode) {
136     case S_IFREG:
137         break;
138     default:
139         return 0;
140     }
141
142     if (S_ISDIR(sp->st_mode))
143         return 0;
144
145     ad_init(&adea, vol);
146     ad_init_old(&adv2, AD_VERSION2, adea.ad_options);
147
148     /* Open and lock adouble:v2 file */
149     EC_ZERO( ad_open(&adv2, path, ADFLAGS_HF | ADFLAGS_RF | ADFLAGS_RDWR) );
150
151     if (adv2.ad_rlen > 0) {
152         EC_NEG1_LOG( ad_tmplock(&adv2, ADEID_RFORK, ADLOCK_WR | ADLOCK_FILELOCK, 0, 0, 0) );
153
154         /* Create a adouble:ea resource fork */
155         EC_ZERO_LOG( ad_open(&adea, path, ADFLAGS_RF|ADFLAGS_RDWR|ADFLAGS_CREATE|ADFLAGS_SETSHRMD, 0666) );
156
157         EC_ZERO_LOG( copy_fork(ADEID_RFORK, &adea, &adv2) );
158         adea.ad_rlen = adv2.ad_rlen;
159         ad_flush(&adea);
160         fchmod(ad_reso_fileno(&adea), sp->st_mode & 0666);
161         fchown(ad_reso_fileno(&adea), sp->st_uid, sp->st_gid);
162     }
163
164 EC_CLEANUP:
165     EC_ZERO_LOG( ad_close(&adv2, ADFLAGS_HF | ADFLAGS_RF) );
166     EC_ZERO_LOG( ad_close(&adea, ADFLAGS_HF | ADFLAGS_RF) );
167     LOG(log_debug, logtype_ad,"ad_conv_v22ea_rf(\"%s\"): END: %d", fullpathname(path), ret);
168     EC_EXIT;
169 }
170
171 static int ad_conv_v22ea(const char *path, const struct stat *sp, const struct vol *vol)
172 {
173     EC_INIT;
174     const char *adpath;
175     int adflags = S_ISDIR(sp->st_mode) ? ADFLAGS_DIR : 0;
176
177     become_root();
178
179     if (ad_conv_v22ea_hf(path, sp, vol) != 0)
180         goto delete;
181     if (ad_conv_v22ea_rf(path, sp, vol) != 0)
182         goto delete;
183
184 delete:
185     EC_NULL( adpath = ad_path(path, adflags) );
186     LOG(log_debug, logtype_ad,"ad_conv_v22ea_hf(\"%s\"): deleting adouble:v2 file: \"%s\"",
187         path, fullpathname(adpath));
188
189     unlink(adpath);
190
191 EC_CLEANUP:
192     if (errno == ENOENT)
193         EC_STATUS(0);
194
195     unbecome_root();
196
197     EC_EXIT;
198 }
199
200 /*!
201  * Remove hexencoded dots and slashes (":2e" and ":2f")
202  */
203 static int ad_conv_dehex(const char *path, const struct stat *sp, const struct vol *vol, const char **newpathp)
204 {
205     EC_INIT;
206     static char buf[MAXPATHLEN];
207     int adflags = S_ISDIR(sp->st_mode) ? ADFLAGS_DIR : 0;
208     bstring newpath = NULL;
209     static bstring str2e = NULL;
210     static bstring str2f = NULL;
211     static bstring strdot = NULL;
212     static bstring strcolon = NULL;
213     char *newadpath = NULL;
214
215     if (str2e == NULL) {
216         str2e = bfromcstr(":2e");
217         str2f = bfromcstr(":2f");
218         strdot = bfromcstr(".");
219         strcolon = bfromcstr(":");
220     }
221
222     LOG(log_debug, logtype_ad,"ad_conv_dehex(\"%s\"): BEGIN", fullpathname(path));
223
224     *newpathp = NULL;
225
226     if (((strstr(path, ":2e")) == NULL) && ((strstr(path, ":2f")) == NULL) )
227         goto EC_CLEANUP;
228
229     EC_NULL( newpath = bfromcstr(path) );
230
231     EC_ZERO( bfindreplace(newpath, str2e, strdot, 0) );
232     EC_ZERO( bfindreplace(newpath, str2f, strcolon, 0) );
233     
234     become_root();
235     if (adflags != ADFLAGS_DIR) {
236         if ((newadpath = strdup(vol->ad_path(bdata(newpath), 0))) == NULL) {
237             unbecome_root();
238             EC_FAIL;
239         }
240         rename(vol->ad_path(path, 0), newadpath);
241     }
242     rename(path, bdata(newpath));
243     unbecome_root();
244
245     strlcpy(buf, bdata(newpath), sizeof(buf));
246     *newpathp = buf;
247
248 EC_CLEANUP:
249     if (newpath)
250         bdestroy(newpath);
251     if (newadpath)
252         free(newadpath);
253     EC_EXIT;
254 }
255
256 /*!
257  * AppleDouble and encoding conversion on the fly
258  *
259  * @param path      (r) path to file or directory
260  * @param sp        (r) stat(path)
261  * @param vol       (r) volume handle
262  * @param newpath   (w) if encoding changed, new name. Can be NULL.
263  *
264  * @returns         -1 on internal error, otherwise 0. newpath is NULL if no character conversion was done,
265  *                  otherwise newpath points to a static string with the converted name
266  */
267 int ad_convert(const char *path, const struct stat *sp, const struct vol *vol, const char **newpath)
268 {
269     EC_INIT;
270     const char *p;
271
272     LOG(log_debug, logtype_ad,"ad_convert(\"%s\"): BEGIN", fullpathname(path));
273
274     if (newpath)
275         *newpath = NULL;
276
277     if (vol->v_flags & AFPVOL_RO)
278         EC_EXIT_STATUS(0);
279
280     if ((vol->v_adouble == AD_VERSION_EA) && !(vol->v_flags & AFPVOL_NOV2TOEACONV))
281         EC_ZERO( ad_conv_v22ea(path, sp, vol) );
282
283     if (vol->v_adouble == AD_VERSION_EA) {
284         EC_ZERO( ad_conv_dehex(path, sp, vol, &p) );
285         if (p && newpath)
286             *newpath = p;
287     }
288
289 EC_CLEANUP:
290     LOG(log_debug, logtype_ad,"ad_convert(\"%s\"): END: %d", fullpathname(path), ret);
291     EC_EXIT;
292 }
293