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/volinfo.h>
63#include <atalk/bstrlib.h>
64#include <atalk/bstradd.h>
65#include <atalk/logger.h>
66#include <atalk/errchk.h>
67#include <atalk/unicode.h>
68
69#include "ad.h"
70
71int log_verbose;             /* Logging flag */
72
73void _log(enum logtype lt, char *fmt, ...)
74{
75    int len;
76    static char logbuffer[1024];
77    va_list args;
78
79    if ( (lt == STD) || (log_verbose == 1)) {
80        va_start(args, fmt);
81        len = vsnprintf(logbuffer, 1023, fmt, args);
82        va_end(args);
83        logbuffer[1023] = 0;
84
85        printf("%s\n", logbuffer);
86    }
87}
88
89/*!
90 * Load volinfo and initialize struct vol
91 *
92 * Only opens "dbd" volumes !
93 *
94 * @param path   (r)  path to evaluate
95 * @param vol    (rw) structure to initialize
96 *
97 * @returns 0 on success, exits on error
98 */
99int openvol(const char *path, afpvol_t *vol)
100{
101    int flags = 0;
102
103    memset(vol, 0, sizeof(afpvol_t));
104
105    /* try to find a .AppleDesktop/.volinfo */
106    if (loadvolinfo((char *)path, &vol->volinfo) != 0)
107        return -1;
108
109    if (STRCMP(vol->volinfo.v_cnidscheme, != , "dbd"))
110        ERROR("\"%s\" isn't a \"dbd\" CNID volume!", vol->volinfo.v_path);
111
112    if (vol_load_charsets(&vol->volinfo) == -1)
113        ERROR("Error loading charsets!");
114
115    /* Sanity checks to ensure we can touch this volume */
116    if (vol->volinfo.v_adouble != AD_VERSION2)
117        ERROR("Unsupported adouble versions: %u", vol->volinfo.v_adouble);
118
119    if (vol->volinfo.v_vfs_ea != AFPVOL_EA_SYS)
120        ERROR("Unsupported Extended Attributes option: %u", vol->volinfo.v_vfs_ea);
121
122    /* initialize sufficient struct vol for VFS initialisation */
123    vol->volume.v_adouble = AD_VERSION2;
124    vol->volume.v_vfs_ea = AFPVOL_EA_SYS;
125    initvol_vfs(&vol->volume);
126
127    if ((vol->volinfo.v_flags & AFPVOL_NODEV))
128        flags |= CNID_FLAG_NODEV;
129
130    if ((vol->volume.v_cdb = cnid_open(vol->volinfo.v_path,
131                                       0000,
132                                       "dbd",
133                                       flags,
134                                       vol->volinfo.v_dbd_host,
135                                       vol->volinfo.v_dbd_port)) == NULL)
136        ERROR("Cant initialize CNID database connection for %s", vol->volinfo.v_path);
137
138    cnid_getstamp(vol->volume.v_cdb,
139                  vol->db_stamp,
140                  sizeof(vol->db_stamp));
141
142    return 0;
143}
144
145void closevol(afpvol_t *vol)
146{
147    if (vol->volume.v_cdb)
148        cnid_close(vol->volume.v_cdb);
149
150    memset(vol, 0, sizeof(afpvol_t));
151}
152
153/*
154  Taken form afpd/desktop.c
155*/
156char *utompath(const struct volinfo *volinfo, const char *upath)
157{
158    static char  mpath[ MAXPATHLEN + 2]; /* for convert_charset dest_len parameter +2 */
159    char         *m;
160    const char   *u;
161    uint16_t     flags = CONV_IGNORE | CONV_UNESCAPEHEX;
162    size_t       outlen;
163
164    if (!upath)
165        return NULL;
166
167    m = mpath;
168    u = upath;
169    outlen = strlen(upath);
170
171    if ((volinfo->v_casefold & AFPVOL_UTOMUPPER))
172        flags |= CONV_TOUPPER;
173    else if ((volinfo->v_casefold & AFPVOL_UTOMLOWER))
174        flags |= CONV_TOLOWER;
175
176    if ((volinfo->v_flags & AFPVOL_EILSEQ)) {
177        flags |= CONV__EILSEQ;
178    }
179
180    /* convert charsets */
181    if ((size_t)-1 == ( outlen = convert_charset(volinfo->v_volcharset,
182                                                 CH_UTF8_MAC,
183                                                 volinfo->v_maccharset,
184                                                 u, outlen, mpath, MAXPATHLEN, &flags)) ) {
185        SLOG("Conversion from %s to %s for %s failed.",
186             volinfo->v_volcodepage, volinfo->v_maccodepage, u);
187        return NULL;
188    }
189
190    return(m);
191}
192
193
194/*!
195 * Convert dot encoding of basename _in place_
196 *
197 * path arg can be "[/][dir/ | ...]filename". It will be converted in place
198 * possible encoding ".file" as ":2efile" which means the result will be
199 * longer then the original which means provide a big enough buffer.
200 *
201 * @param svol   (r)  source volume
202 * @param dvol   (r)  destinatio volume
203 * @param path   (rw) path to convert _in place_
204 * @param buflen (r)  size of path buffer (max strlen == buflen -1)
205 *
206 * @returns 0 on sucess, -1 on error
207 */
208int convert_dots_encoding(const afpvol_t *svol, const afpvol_t *dvol, char *path, size_t buflen)
209{
210    static charset_t from = (charset_t) -1;
211    static char buf[MAXPATHLEN+2];
212    char *bname = stripped_slashes_basename(path);
213    int pos = bname - path;
214    uint16_t flags = 0;
215
216    if ( ! svol->volinfo.v_path) {
217        /* no source volume: escape special chars (eg ':') */
218        from = dvol->volinfo.v_volcharset; /* src = dst charset */
219        flags |= CONV_ESCAPEHEX;
220    } else {
221        from = svol->volinfo.v_volcharset;
222    }
223
224    if ( (svol->volinfo.v_path)
225         && ! (svol->volinfo.v_flags & AFPVOL_USEDOTS)
226         && (dvol->volinfo.v_flags & AFPVOL_USEDOTS)) {
227        /* source is without dots, destination is with */
228        flags |= CONV_UNESCAPEHEX;
229    } else if (! (dvol->volinfo.v_flags & AFPVOL_USEDOTS)) {
230        flags |= CONV_ESCAPEDOTS;
231    }
232
233    int len = convert_charset(from,
234                              dvol->volinfo.v_volcharset,
235                              dvol->volinfo.v_maccharset,
236                              bname, strlen(bname),
237                              buf, MAXPATHLEN,
238                              &flags);
239    if (len == -1)
240        return -1;
241
242    if (strlcpy(bname, buf, MAXPATHLEN - pos) > MAXPATHLEN - pos)
243        return -1;
244    return 0;
245}
246
247/*!
248 * ResolvesCNID of a given paths
249 *
250 * path might be:
251 * (a) relative:
252 *     "dir/subdir" with cwd: "/afp_volume/topdir"
253 * (b) absolute:
254 *     "/afp_volume/dir/subdir"
255 *
256 * path MUST be pointing inside vol, this is usually the case as vol has been build from
257 * path using loadvolinfo and friends.
258 *
259 * @param vol  (r) pointer to afpvol_t
260 * @param path (r) path, see above
261 * @param did  (rw) parent CNID of returned CNID
262 *
263 * @returns CNID of path
264 */
265cnid_t cnid_for_path(const afpvol_t *vol,
266                     const char *path,
267                     cnid_t *did)
268{
269    EC_INIT;
270
271    cnid_t cnid;
272    bstring rpath = NULL;
273    bstring statpath = NULL;
274    struct bstrList *l = NULL;
275    struct stat st;
276
277    cnid = htonl(2);
278
279    EC_NULL(rpath = rel_path_in_vol(path, vol->volinfo.v_path));
280    EC_NULL(statpath = bfromcstr(vol->volinfo.v_path));
281    EC_ZERO(bcatcstr(statpath, "/"));
282
283    l = bsplit(rpath, '/');
284    for (int i = 0; i < l->qty ; i++) {
285        *did = cnid;
286
287        EC_ZERO(bconcat(statpath, l->entry[i]));
288        EC_ZERO_LOGSTR(lstat(cfrombstr(statpath), &st),
289                       "lstat(rpath: %s, elem: %s): %s: %s",
290                       cfrombstr(rpath), cfrombstr(l->entry[i]),
291                       cfrombstr(statpath), strerror(errno));
292
293        if ((cnid = cnid_add(vol->volume.v_cdb,
294                             &st,
295                             *did,
296                             cfrombstr(l->entry[i]),
297                             blength(l->entry[i]),
298                             0)) == CNID_INVALID) {
299            EC_FAIL;
300        }
301        EC_ZERO(bcatcstr(statpath, "/"));
302    }
303
304EC_CLEANUP:
305    bdestroy(rpath);
306    bstrListDestroy(l);
307    bdestroy(statpath);
308    if (ret != 0)
309        return CNID_INVALID;
310
311    return cnid;
312}
313
314/*!
315 * Resolves CNID of a given paths parent directory
316 *
317 * path might be:
318 * (a) relative:
319 *     "dir/subdir" with cwd: "/afp_volume/topdir"
320 * (b) absolute:
321 *     "/afp_volume/dir/subdir"
322 *
323 * path MUST be pointing inside vol, this is usually the case as vol has been build from
324 * path using loadvolinfo and friends.
325 *
326 * @param vol  (r) pointer to afpvol_t
327 * @param path (r) path, see above
328 * @param did  (rw) parent CNID of returned CNID
329 *
330 * @returns CNID of path
331 */
332cnid_t cnid_for_paths_parent(const afpvol_t *vol,
333                             const char *path,
334                             cnid_t *did)
335{
336    EC_INIT;
337
338    cnid_t cnid;
339    bstring rpath = NULL;
340    bstring statpath = NULL;
341    struct bstrList *l = NULL;
342    struct stat st;
343
344    *did = htonl(1);
345    cnid = htonl(2);
346
347    EC_NULL(rpath = rel_path_in_vol(path, vol->volinfo.v_path));
348    EC_NULL(statpath = bfromcstr(vol->volinfo.v_path));
349
350    l = bsplit(rpath, '/');
351    if (l->qty == 1)
352        /* only one path element, means parent dir cnid is volume root = 2 */
353        goto EC_CLEANUP;
354    for (int i = 0; i < (l->qty - 1); i++) {
355        *did = cnid;
356        EC_ZERO(bconcat(statpath, l->entry[i]));
357        EC_ZERO_LOGSTR(lstat(cfrombstr(statpath), &st),
358                       "lstat(rpath: %s, elem: %s): %s: %s",
359                       cfrombstr(rpath), cfrombstr(l->entry[i]),
360                       cfrombstr(statpath), strerror(errno));
361
362        if ((cnid = cnid_add(vol->volume.v_cdb,
363                             &st,
364                             *did,
365                             cfrombstr(l->entry[i]),
366                             blength(l->entry[i]),
367                             0)) == CNID_INVALID) {
368            EC_FAIL;
369        }
370        EC_ZERO(bcatcstr(statpath, "/"));
371    }
372
373EC_CLEANUP:
374    bdestroy(rpath);
375    bstrListDestroy(l);
376    bdestroy(statpath);
377    if (ret != 0)
378        return CNID_INVALID;
379
380    return cnid;
381}
382
383