1/*
2 * Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
3 * Copyright (c) 1991, 1993, 1994
4 * The Regents of the University of California.  All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 * 4. Neither the name of the University nor the names of its contributors
15 *    may be used to endorse or promote products derived from this software
16 *    without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31#ifdef HAVE_CONFIG_H
32#include "config.h"
33#endif /* HAVE_CONFIG_H */
34
35#include <sys/types.h>
36#include <sys/param.h>
37#include <sys/stat.h>
38#include <sys/mman.h>
39
40#include <errno.h>
41#include <fcntl.h>
42#include <limits.h>
43#include <stdio.h>
44#include <stdlib.h>
45#include <sysexits.h>
46#include <unistd.h>
47#include <stdarg.h>
48#include <string.h>
49#include <libgen.h>
50
51#ifdef HAVE_SOLARIS_ACLS
52#include <sys/acl.h>
53#endif  /* HAVE_SOLARIS_ACLS */
54
55#ifdef HAVE_POSIX_ACLS
56#include <sys/types.h>
57#include <sys/acl.h>
58#endif /* HAVE_POSIX_ACLS */
59
60#include <atalk/util.h>
61#include <atalk/cnid.h>
62#include <atalk/bstrlib.h>
63#include <atalk/bstradd.h>
64#include <atalk/logger.h>
65#include <atalk/errchk.h>
66#include <atalk/unicode.h>
67#include <atalk/globals.h>
68#include <atalk/netatalk_conf.h>
69
70
71#include "ad.h"
72
73int log_verbose;             /* Logging flag */
74
75void _log(enum logtype lt, char *fmt, ...)
76{
77    int len;
78    static char logbuffer[1024];
79    va_list args;
80
81    if ( (lt == STD) || (log_verbose == 1)) {
82        va_start(args, fmt);
83        len = vsnprintf(logbuffer, 1023, fmt, args);
84        va_end(args);
85        logbuffer[1023] = 0;
86
87        printf("%s\n", logbuffer);
88    }
89}
90
91/*!
92 * Load volinfo and initialize struct vol
93 *
94 * Only opens "dbd" volumes !
95 *
96 * @param path   (r)  path to evaluate
97 * @param vol    (rw) structure to initialize
98 *
99 * @returns 0 on success, exits on error
100 */
101int openvol(AFPObj *obj, const char *path, afpvol_t *vol)
102{
103    int flags = 0;
104
105    memset(vol, 0, sizeof(afpvol_t));
106
107    if ((vol->vol = getvolbypath(obj, path)) == NULL)
108        return -1;
109
110    if (STRCMP(vol->vol->v_cnidscheme, != , "dbd"))
111        ERROR("\"%s\" isn't a \"dbd\" CNID volume!", vol->vol->v_path);
112
113    /* Sanity checks to ensure we can touch this volume */
114    if (vol->vol->v_adouble != AD_VERSION2
115        && vol->vol->v_adouble != AD_VERSION_EA)
116        ERROR("Unsupported adouble versions: %u", vol->vol->v_adouble);
117
118    if (vol->vol->v_vfs_ea != AFPVOL_EA_SYS)
119        ERROR("Unsupported Extended Attributes option: %u", vol->vol->v_vfs_ea);
120
121    if ((vol->vol->v_flags & AFPVOL_NODEV))
122        flags |= CNID_FLAG_NODEV;
123
124    if ((vol->vol->v_cdb = cnid_open(vol->vol->v_path,
125                                     0000,
126                                     "dbd",
127                                     flags,
128                                     vol->vol->v_cnidserver,
129                                     vol->vol->v_cnidport)) == NULL)
130        ERROR("Cant initialize CNID database connection for %s", vol->vol->v_path);
131
132    cnid_getstamp(vol->vol->v_cdb,
133                  vol->db_stamp,
134                  sizeof(vol->db_stamp));
135
136    return 0;
137}
138
139void closevol(afpvol_t *vol)
140{
141    if (vol->vol) {
142        if (vol->vol->v_cdb) {
143            cnid_close(vol->vol->v_cdb);
144            vol->vol->v_cdb = NULL;
145        }
146    }
147    memset(vol, 0, sizeof(afpvol_t));
148}
149
150/*
151  Taken form afpd/desktop.c
152*/
153char *utompath(const struct vol *vol, const char *upath)
154{
155    static char  mpath[ MAXPATHLEN + 2]; /* for convert_charset dest_len parameter +2 */
156    char         *m;
157    const char   *u;
158    uint16_t     flags = CONV_IGNORE | CONV_UNESCAPEHEX;
159    size_t       outlen;
160
161    if (!upath)
162        return NULL;
163
164    m = mpath;
165    u = upath;
166    outlen = strlen(upath);
167
168    if ((vol->v_casefold & AFPVOL_UTOMUPPER))
169        flags |= CONV_TOUPPER;
170    else if ((vol->v_casefold & AFPVOL_UTOMLOWER))
171        flags |= CONV_TOLOWER;
172
173    if ((vol->v_flags & AFPVOL_EILSEQ)) {
174        flags |= CONV__EILSEQ;
175    }
176
177    /* convert charsets */
178    if ((size_t)-1 == ( outlen = convert_charset(vol->v_volcharset,
179                                                 CH_UTF8_MAC,
180                                                 vol->v_maccharset,
181                                                 u, outlen, mpath, MAXPATHLEN, &flags)) ) {
182        SLOG("Conversion from %s to %s for %s failed.",
183             vol->v_volcodepage, vol->v_maccodepage, u);
184        return NULL;
185    }
186
187    return(m);
188}
189
190
191/*!
192 * Convert dot encoding of basename _in place_
193 *
194 * path arg can be "[/][dir/ | ...]filename". It will be converted in place
195 * possible encoding ".file" as ":2efile" which means the result will be
196 * longer then the original which means provide a big enough buffer.
197 *
198 * @param svol   (r)  source volume
199 * @param dvol   (r)  destinatio volume
200 * @param path   (rw) path to convert _in place_
201 * @param buflen (r)  size of path buffer (max strlen == buflen -1)
202 *
203 * @returns 0 on sucess, -1 on error
204 */
205int convert_dots_encoding(const afpvol_t *svol, const afpvol_t *dvol, char *path, size_t buflen)
206{
207    static charset_t from = (charset_t) -1;
208    static char buf[MAXPATHLEN+2];
209    char *bname = stripped_slashes_basename(path);
210    int pos = bname - path;
211    uint16_t flags = 0;
212
213    if ( ! svol->vol->v_path) {
214        /* no source volume: escape special chars (eg ':') */
215        from = dvol->vol->v_volcharset; /* src = dst charset */
216        if (dvol->vol->v_adouble == AD_VERSION2)
217            flags |= CONV_ESCAPEHEX;
218    } else {
219        from = svol->vol->v_volcharset;
220    }
221
222    int len = convert_charset(from,
223                              dvol->vol->v_volcharset,
224                              dvol->vol->v_maccharset,
225                              bname, strlen(bname),
226                              buf, MAXPATHLEN,
227                              &flags);
228    if (len == -1)
229        return -1;
230
231    if (strlcpy(bname, buf, MAXPATHLEN - pos) > MAXPATHLEN - pos)
232        return -1;
233    return 0;
234}
235
236/*!
237 * ResolvesCNID of a given paths
238 *
239 * path might be:
240 * (a) relative:
241 *     "dir/subdir" with cwd: "/afp_volume/topdir"
242 * (b) absolute:
243 *     "/afp_volume/dir/subdir"
244 *
245 * path MUST be pointing inside vol, this is usually the case as vol has been build from
246 * path using loadvolinfo and friends.
247 *
248 * @param vol  (r) pointer to afpvol_t
249 * @param path (r) path, see above
250 * @param did  (rw) parent CNID of returned CNID
251 *
252 * @returns CNID of path
253 */
254cnid_t cnid_for_path(const afpvol_t *vol,
255                     const char *path,
256                     cnid_t *did)
257{
258    EC_INIT;
259
260    cnid_t cnid;
261    bstring rpath = NULL;
262    bstring statpath = NULL;
263    struct bstrList *l = NULL;
264    struct stat st;
265
266    cnid = htonl(2);
267
268    EC_NULL(rpath = rel_path_in_vol(path, vol->vol->v_path));
269    EC_NULL(statpath = bfromcstr(vol->vol->v_path));
270    EC_ZERO(bcatcstr(statpath, "/"));
271
272    l = bsplit(rpath, '/');
273    for (int i = 0; i < l->qty ; i++) {
274        *did = cnid;
275
276        EC_ZERO(bconcat(statpath, l->entry[i]));
277        EC_ZERO_LOGSTR(lstat(cfrombstr(statpath), &st),
278                       "lstat(rpath: %s, elem: %s): %s: %s",
279                       cfrombstr(rpath), cfrombstr(l->entry[i]),
280                       cfrombstr(statpath), strerror(errno));
281
282        if ((cnid = cnid_add(vol->vol->v_cdb,
283                             &st,
284                             *did,
285                             cfrombstr(l->entry[i]),
286                             blength(l->entry[i]),
287                             0)) == CNID_INVALID) {
288            EC_FAIL;
289        }
290        EC_ZERO(bcatcstr(statpath, "/"));
291    }
292
293EC_CLEANUP:
294    bdestroy(rpath);
295    bstrListDestroy(l);
296    bdestroy(statpath);
297    if (ret != 0)
298        return CNID_INVALID;
299
300    return cnid;
301}
302
303/*!
304 * Resolves CNID of a given paths parent directory
305 *
306 * path might be:
307 * (a) relative:
308 *     "dir/subdir" with cwd: "/afp_volume/topdir"
309 * (b) absolute:
310 *     "/afp_volume/dir/subdir"
311 *
312 * path MUST be pointing inside vol, this is usually the case as vol has been build from
313 * path using loadvolinfo and friends.
314 *
315 * @param vol  (r) pointer to afpvol_t
316 * @param path (r) path, see above
317 * @param did  (rw) parent CNID of returned CNID
318 *
319 * @returns CNID of path
320 */
321cnid_t cnid_for_paths_parent(const afpvol_t *vol,
322                             const char *path,
323                             cnid_t *did)
324{
325    EC_INIT;
326
327    cnid_t cnid;
328    bstring rpath = NULL;
329    bstring statpath = NULL;
330    struct bstrList *l = NULL;
331    struct stat st;
332
333    *did = htonl(1);
334    cnid = htonl(2);
335
336    EC_NULL(rpath = rel_path_in_vol(path, vol->vol->v_path));
337    EC_NULL(statpath = bfromcstr(vol->vol->v_path));
338
339    l = bsplit(rpath, '/');
340    if (l->qty == 1)
341        /* only one path element, means parent dir cnid is volume root = 2 */
342        goto EC_CLEANUP;
343    for (int i = 0; i < (l->qty - 1); i++) {
344        *did = cnid;
345        EC_ZERO(bconcat(statpath, l->entry[i]));
346        EC_ZERO_LOGSTR(lstat(cfrombstr(statpath), &st),
347                       "lstat(rpath: %s, elem: %s): %s: %s",
348                       cfrombstr(rpath), cfrombstr(l->entry[i]),
349                       cfrombstr(statpath), strerror(errno));
350
351        if ((cnid = cnid_add(vol->vol->v_cdb,
352                             &st,
353                             *did,
354                             cfrombstr(l->entry[i]),
355                             blength(l->entry[i]),
356                             0)) == CNID_INVALID) {
357            EC_FAIL;
358        }
359        EC_ZERO(bcatcstr(statpath, "/"));
360    }
361
362EC_CLEANUP:
363    bdestroy(rpath);
364    bstrListDestroy(l);
365    bdestroy(statpath);
366    if (ret != 0)
367        return CNID_INVALID;
368
369    return cnid;
370}
371
372