]> arthur.barton.de Git - netatalk.git/blob - libatalk/adouble/ad_conv.c
Merge remote branch 'origin/develop' into nohex
[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     const char *adpath;
61     int adflags;
62     uint32_t ctime, mtime, afpinfo = 0;
63     char *emptyad;
64
65     LOG(log_debug, logtype_default,"ad_conv_v22ea_hf(\"%s\"): BEGIN", fullpathname(path));
66
67     ad_init(&adea, vol);
68     ad_init_old(&adv2, AD_VERSION2, adea.ad_options);
69     adflags = S_ISDIR(sp->st_mode) ? ADFLAGS_DIR : 0;
70
71     /* Open and lock adouble:v2 file */
72     EC_ZERO( ad_open(&adv2, path, adflags | ADFLAGS_HF | ADFLAGS_RDWR) );
73
74     EC_NEG1_LOG( ad_tmplock(&adv2, ADEID_RFORK, ADLOCK_WR | ADLOCK_FILELOCK, 0, 0, 0) );
75     EC_NEG1_LOG( adv2.ad_ops->ad_header_read(path, &adv2, sp) );
76
77     /* Check if it's a non-empty header */
78     if (S_ISREG(sp->st_mode))
79         emptyad = &emptyfilad[0];
80     else
81         emptyad = &emptydirad[0];
82
83     if (ad_getentrylen(&adv2, ADEID_COMMENT) != 0)
84         goto copy;
85     if (ad_getentryoff(&adv2, ADEID_FINDERI)
86         && (ad_getentrylen(&adv2, ADEID_FINDERI) == ADEDLEN_FINDERI)
87         && (memcmp(ad_entry(&adv2, ADEID_FINDERI), emptyad, ADEDLEN_FINDERI) != 0))
88         goto copy;
89     if (ad_getentryoff(&adv2, ADEID_FILEDATESI)) {
90         EC_ZERO_LOG( ad_getdate(&adv2, AD_DATE_CREATE | AD_DATE_UNIX, &ctime) );
91         EC_ZERO_LOG( ad_getdate(&adv2, AD_DATE_MODIFY | AD_DATE_UNIX, &mtime) );
92         if ((ctime != mtime) || (mtime != sp->st_mtime))
93             goto copy;
94     }
95     if (ad_getentryoff(&adv2, ADEID_AFPFILEI)) {
96         if (memcmp(ad_entry(&adv2, ADEID_AFPFILEI), &afpinfo, ADEDLEN_AFPFILEI) != 0)
97             goto copy;
98     }
99
100     LOG(log_debug, logtype_default,"ad_conv_v22ea_hf(\"%s\"): default adouble", fullpathname(path), ret);
101     goto EC_CLEANUP;
102
103 copy:
104     /* Create a adouble:ea meta EA */
105     LOG(log_debug, logtype_default,"ad_conv_v22ea_hf(\"%s\"): copying adouble", fullpathname(path), ret);
106     EC_ZERO_LOG( ad_open(&adea, path, adflags | ADFLAGS_HF | ADFLAGS_RDWR | ADFLAGS_CREATE) );
107     EC_ZERO_LOG( ad_copy_header(&adea, &adv2) );
108     ad_flush(&adea);
109
110 EC_CLEANUP:
111     EC_ZERO_LOG( ad_close(&adv2, ADFLAGS_HF | ADFLAGS_SETSHRMD) );
112     EC_ZERO_LOG( ad_close(&adea, ADFLAGS_HF | ADFLAGS_SETSHRMD) );
113     LOG(log_debug, logtype_default,"ad_conv_v22ea_hf(\"%s\"): END: %d", fullpathname(path), ret);
114     EC_EXIT;
115 }
116
117 static int ad_conv_v22ea_rf(const char *path, const struct stat *sp, const struct vol *vol)
118 {
119     EC_INIT;
120     struct adouble adv2;
121     struct adouble adea;
122
123     LOG(log_debug, logtype_default,"ad_conv_v22ea_rf(\"%s\"): BEGIN", fullpathname(path));
124
125     if (S_ISDIR(sp->st_mode))
126         return 0;
127
128     ad_init(&adea, vol);
129     ad_init_old(&adv2, AD_VERSION2, adea.ad_options);
130
131     /* Open and lock adouble:v2 file */
132     EC_ZERO( ad_open(&adv2, path, ADFLAGS_HF | ADFLAGS_RF | ADFLAGS_RDWR) );
133
134     if (adv2.ad_rlen > 0) {
135         EC_NEG1_LOG( ad_tmplock(&adv2, ADEID_RFORK, ADLOCK_WR | ADLOCK_FILELOCK, 0, 0, 0) );
136
137         /* Create a adouble:ea resource fork */
138         EC_ZERO_LOG( ad_open(&adea, path, ADFLAGS_HF | ADFLAGS_RF | ADFLAGS_RDWR | ADFLAGS_CREATE, 0666) );
139
140         EC_ZERO_LOG( copy_fork(ADEID_RFORK, &adea, &adv2) );
141         adea.ad_rlen = adv2.ad_rlen;
142         ad_flush(&adea);
143     }
144
145 EC_CLEANUP:
146     EC_ZERO_LOG( ad_close(&adv2, ADFLAGS_HF | ADFLAGS_RF) );
147     EC_ZERO_LOG( ad_close(&adea, ADFLAGS_HF | ADFLAGS_RF) );
148     LOG(log_debug, logtype_default,"ad_conv_v22ea_rf(\"%s\"): END: %d", fullpathname(path), ret);
149     EC_EXIT;
150 }
151
152 static int ad_conv_v22ea(const char *path, const struct stat *sp, const struct vol *vol)
153 {
154     EC_INIT;
155     const char *adpath;
156     int adflags = S_ISDIR(sp->st_mode) ? ADFLAGS_DIR : 0;
157
158     EC_ZERO( ad_conv_v22ea_hf(path, sp, vol) );
159     EC_ZERO( ad_conv_v22ea_rf(path, sp, vol) );
160
161     EC_NULL( adpath = ad_path(path, adflags) );
162     LOG(log_debug, logtype_default,"ad_conv_v22ea_hf(\"%s\"): deleting adouble:v2 file: \"%s\"",
163         path, fullpathname(adpath));
164
165     become_root();
166     EC_ZERO_LOG( unlink(adpath) );
167     unbecome_root();
168
169 EC_CLEANUP:
170     if (errno == ENOENT)
171         EC_STATUS(0);
172     EC_EXIT;
173 }
174
175 /*!
176  * Remove hexencoded dots and slashes (":2e" and ":2f")
177  */
178 static int ad_conv_dehex(const char *path, const struct stat *sp, const struct vol *vol, const char **newpathp)
179 {
180     EC_INIT;
181     static char buf[MAXPATHLEN];
182     const char *adpath, *p;
183     int adflags = S_ISDIR(sp->st_mode) ? ADFLAGS_DIR : 0;
184     bstring newpath = NULL;
185
186     LOG(log_debug, logtype_default,"ad_conv_dehex(\"%s\"): BEGIN", fullpathname(path));
187
188     *newpathp = NULL;
189
190     if ((p = strchr(path, ':')) == NULL)
191         goto EC_CLEANUP;
192
193     EC_NULL( newpath = bfromcstr(path) );
194
195     EC_ZERO( bfindreplace(newpath, bfromcstr(":2e"), bfromcstr("."), 0) );
196     EC_ZERO( bfindreplace(newpath, bfromcstr(":2f"), bfromcstr(":"), 0) );
197     
198     become_root();
199     if (adflags != ADFLAGS_DIR)
200         rename(vol->ad_path(path, 0), vol->ad_path(bdata(newpath), 0));
201     rename(path, bdata(newpath));
202     unbecome_root();
203
204     strlcpy(buf, bdata(newpath), sizeof(buf));
205     *newpathp = buf;
206
207 EC_CLEANUP:
208     if (newpath)
209         bdestroy(newpath);
210     EC_EXIT;
211 }
212
213 /*!
214  * AppleDouble and encoding conversion on the fly
215  *
216  * @param path      (r) path to file or directory
217  * @param sp        (r) stat(path)
218  * @param vol       (r) volume handle
219  * @param newpath   (w) if encoding changed, new name. Can be NULL.
220  *
221  * @returns         -1 on internal error, otherwise 0. newpath is NULL if no character conversion was done,
222  *                  otherwise newpath points to a static string with the converted name
223  */
224 int ad_convert(const char *path, const struct stat *sp, const struct vol *vol, const char **newpath)
225 {
226     EC_INIT;
227     const char *p;
228
229     LOG(log_debug, logtype_default,"ad_convert(\"%s\"): BEGIN", fullpathname(path));
230
231     if (newpath)
232         *newpath = NULL;
233
234     if ((vol->v_adouble == AD_VERSION_EA) && !(vol->v_flags & AFPVOL_NOV2TOEACONV))
235         EC_ZERO( ad_conv_v22ea(path, sp, vol) );
236
237     if (vol->v_adouble == AD_VERSION_EA) {
238         EC_ZERO( ad_conv_dehex(path, sp, vol, &p) );
239         if (p && newpath)
240             *newpath = p;
241     }
242
243 EC_CLEANUP:
244     LOG(log_debug, logtype_default,"ad_convert(\"%s\"): END: %d", fullpathname(path), ret);
245     EC_EXIT;
246 }
247