2 $Id: cmd_dbd_scanvol.c,v 1.1 2009-05-06 11:54:24 franklahm Exp $
4 Copyright (c) 2009 Frank Lahm <franklahm@gmail.com>
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
18 dbd specs and implementation progress
19 =====================================
21 St Type Check (Status of implementation progress, type: file/dir)
23 OK D Make sure .AppleDouble dir exist, create if missing. Error creating
24 it is fatal as that shouldn't happen as root
25 OK F Make sure ad file exists
26 OK F/D Delete orphaned ad-files, log dirs in ad-dir
27 OK F/D Check name encoding by roundtripping, log on error
28 .. F/D try: read CNID from ad file (if cnid caching is on)
29 try: fetch CNID from database
30 on mismatch: keep CNID from file, update database
31 on no CNID id ad file: write CNID from database to ad file
32 on no CNID in database: add CNID from ad file to database
33 on no CNID at all: create one and store in both places
38 ad_setid( &ad, s_path->st.st_dev, s_path->st.st_ino, dir->d_did, did, vol->v_stamp);
43 #endif /* HAVE_CONFIG_H */
47 #include <sys/types.h>
54 #include <atalk/adouble.h>
55 #include <atalk/unicode.h>
56 #include <atalk/volinfo.h>
61 static DBD *dbd_rebuild;
62 static struct volinfo *volinfo;
63 static dbd_flags_t dbd_flags;
64 static char cwdbuf[MAXPATHLEN+1];
65 static char *netatalk_dirs[] = {
72 Taken form afpd/desktop.c
74 static char *utompath(char *upath)
76 static char mpath[ MAXPATHLEN + 2]; /* for convert_charset dest_len parameter +2 */
78 uint16_t flags = CONV_IGNORE | CONV_UNESCAPEHEX;
86 outlen = strlen(upath);
88 if ((volinfo->v_casefold & AFPVOL_UTOMUPPER))
89 flags |= CONV_TOUPPER;
90 else if ((volinfo->v_casefold & AFPVOL_UTOMLOWER))
91 flags |= CONV_TOLOWER;
93 if ((volinfo->v_flags & AFPVOL_EILSEQ)) {
94 flags |= CONV__EILSEQ;
97 /* convert charsets */
98 if ((size_t)-1 == ( outlen = convert_charset(volinfo->v_volcharset,
100 volinfo->v_maccharset,
101 u, outlen, mpath, MAXPATHLEN, &flags)) ) {
102 dbd_log( LOGSTD, "Conversion from %s to %s for %s failed.",
103 volinfo->v_volcodepage, volinfo->v_maccodepage, u);
111 Taken form afpd/desktop.c
113 static char *mtoupath(char *mpath)
115 static char upath[ MAXPATHLEN + 2]; /* for convert_charset dest_len parameter +2 */
124 if ( *mpath == '\0' ) {
128 /* set conversion flags */
129 if (!(volinfo->v_flags & AFPVOL_NOHEX))
130 flags |= CONV_ESCAPEHEX;
131 if (!(volinfo->v_flags & AFPVOL_USEDOTS))
132 flags |= CONV_ESCAPEDOTS;
134 if ((volinfo->v_casefold & AFPVOL_MTOUUPPER))
135 flags |= CONV_TOUPPER;
136 else if ((volinfo->v_casefold & AFPVOL_MTOULOWER))
137 flags |= CONV_TOLOWER;
139 if ((volinfo->v_flags & AFPVOL_EILSEQ)) {
140 flags |= CONV__EILSEQ;
149 if ((size_t)-1 == (outlen = convert_charset(CH_UTF8_MAC,
150 volinfo->v_volcharset,
151 volinfo->v_maccharset,
152 m, inplen, u, outlen, &flags)) ) {
153 dbd_log( LOGSTD, "conversion from UTF8-MAC to %s for %s failed.",
154 volinfo->v_volcodepage, mpath);
162 Check for wrong encoding e.g. "." at the beginning is not CAP encoded (:2e) although volume is default !AFPVOL_USEDOTS.
163 We do it by roundtripiping from volcharset to UTF8-MAC and back and then compare the result.
165 static int check_name_encoding(char *uname)
169 roundtripped = mtoupath(utompath(uname));
171 dbd_log( LOGSTD, "Error checking encoding for %s/%s", cwdbuf, uname);
175 if ( STRCMP(uname, !=, roundtripped)) {
176 dbd_log( LOGSTD, "Bad encoding for %s/%s", cwdbuf, uname);
184 Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop"
185 Returns pointer to name or NULL.
187 static const char *check_netatalk_dirs(const char *name)
191 for (c=0; netatalk_dirs[c]; c++) {
192 if ((strcmp(name, netatalk_dirs[c])) == 0)
193 return netatalk_dirs[c];
199 Check for .AppleDouble file, create if missing
201 static int check_adfile(const char *fname, const struct stat *st)
207 adname = volinfo->ad_path(fname, 0);
209 if ((ret = access( adname, F_OK)) != 0) {
210 if (errno != ENOENT) {
211 dbd_log(LOGSTD, "Access error for ad-file '%s/%s': %s",
212 cwdbuf, adname, strerror(errno));
215 /* Missing. Log and create it */
216 dbd_log(LOGSTD, "Missing AppleDoube file '%s/%s'", cwdbuf, adname);
218 if (dbd_flags & DBD_FLAGS_SCAN)
219 /* Scan only requested, dont change anything */
223 ad_init(&ad, volinfo->v_adouble, volinfo->v_flags);
225 if ((ret = ad_open_metadata( fname, 0, O_CREAT, &ad)) != 0) {
226 dbd_log( LOGSTD, "Error creating AppleDouble file '%s/%s': %s",
227 cwdbuf, adname, strerror(errno));
231 /* Set name in ad-file */
232 ad_setname(&ad, utompath((char *)fname));
234 ad_close_metadata(&ad);
236 chown(adname, st->st_uid, st->st_gid);
237 /* FIXME: should we inherit mode too here ? */
239 chmod(adname, st->st_mode);
246 Check for .AppleDouble folder, create if missing
248 static int check_addir(int volroot)
254 if ((ret = access(ADv2_DIRNAME, F_OK)) != 0) {
255 if (errno != ENOENT) {
256 dbd_log(LOGSTD, "Access error in directory %s: %s", cwdbuf, strerror(errno));
259 /* Missing. Log and create it */
260 dbd_log(LOGSTD, "Missing %s directory %s", ADv2_DIRNAME, cwdbuf);
262 if (dbd_flags & DBD_FLAGS_SCAN)
263 /* Scan only requested, dont change anything */
266 /* Create ad dir and set name and id */
267 ad_init(&ad, volinfo->v_adouble, volinfo->v_flags);
269 if ((ret = ad_open_metadata( ".", ADFLAGS_DIR, O_CREAT, &ad)) != 0) {
270 dbd_log( LOGSTD, "Error creating AppleDouble dir in %s: %s", cwdbuf, strerror(errno));
274 /* Get basename of cwd from cwdbuf */
275 char *mname = utompath(strrchr(cwdbuf, '/') + 1);
277 /* Update name in ad file */
278 ad_setname(&ad, mname);
280 ad_close_metadata(&ad);
282 /* Inherit owner/group from "." to ".AppleDouble" and ".Parent" */
283 if ((stat(".", &st)) != 0) {
284 dbd_log( LOGSTD, "Couldnt stat %s: %s", cwdbuf, strerror(errno));
287 chown(ADv2_DIRNAME, st.st_uid, st.st_gid);
288 chown(volinfo->ad_path(".", ADFLAGS_DIR), st.st_uid, st.st_gid);
294 static int read_addir(void)
299 static char fname[NAME_MAX] = "../";
301 if ((chdir(ADv2_DIRNAME)) != 0) {
302 dbd_log(LOGSTD, "Couldn't chdir to '%s/%s': %s",
303 cwdbuf, ADv2_DIRNAME, strerror(errno));
307 if ((dp = opendir(".")) == NULL) {
308 dbd_log(LOGSTD, "Couldn't open the directory '%s/%s': %s",
309 cwdbuf, ADv2_DIRNAME, strerror(errno));
313 while ((ep = readdir(dp))) {
314 /* Check if its "." or ".." */
315 if (DIR_DOT_OR_DOTDOT(ep->d_name))
318 if ((stat(ep->d_name, &st)) < 0) {
319 dbd_log( LOGSTD, "Lost file while reading dir '%s/%s/%s', probably removed: %s",
320 cwdbuf, ADv2_DIRNAME, ep->d_name, strerror(errno));
325 if (S_ISDIR(st.st_mode)) {
326 dbd_log( LOGSTD, "Unexpected directory '%s' in AppleDouble dir '%s/%s'",
327 ep->d_name, cwdbuf, ADv2_DIRNAME);
332 if (STRCMP(ep->d_name, ==, ".Parent"))
335 /* Check for data file */
336 strcpy(fname+3, ep->d_name);
337 if ((access( fname, F_OK)) != 0) {
338 if (errno != ENOENT) {
339 dbd_log(LOGSTD, "Access error for file '%s/%s': %s",
340 cwdbuf, ep->d_name, strerror(errno));
343 /* Orphaned ad-file*/
344 dbd_log(LOGSTD, "Orphaned AppleDoube file '%s/%s/%s'",
345 cwdbuf, ADv2_DIRNAME, ep->d_name);
347 if (dbd_flags & DBD_FLAGS_SCAN)
348 /* Scan only requested, dont change anything */
351 if ((unlink(ep->d_name)) != 0) {
352 dbd_log(LOGSTD, "Error unlinking orphaned AppleDoube file '%s/%s/%s'",
353 cwdbuf, ADv2_DIRNAME, ep->d_name);
359 if ((chdir("..")) != 0) {
360 dbd_log(LOGSTD, "Couldn't chdir back to '%s' from AppleDouble dir: %s",
361 cwdbuf, strerror(errno));
362 /* This really is EOT! */
371 static int dbd_readdir(int volroot, cnid_t did)
373 int cwd, ret = 0, encoding_ok;
377 static struct stat st; /* Save some stack space */
379 /* Check for .AppleDouble folder */
380 if ((check_addir(volroot)) != 0)
383 /* Check AppleDouble files in AppleDouble folder */
384 if ((ret = read_addir()) != 0)
387 if ((dp = opendir (".")) == NULL) {
388 dbd_log(LOGSTD, "Couldn't open the directory: %s",strerror(errno));
392 while ((ep = readdir (dp))) {
393 /* Check if its "." or ".." */
394 if (DIR_DOT_OR_DOTDOT(ep->d_name))
397 /* Check for netatalk special folders e.g. ".AppleDB" or ".AppleDesktop" */
398 if ((name = check_netatalk_dirs(ep->d_name)) != NULL) {
400 dbd_log(LOGSTD, "Nested %s in %s", name, cwdbuf);
404 /* Skip .AppleDouble dir in this loop */
405 if (STRCMP(ep->d_name, == , ADv2_DIRNAME))
408 if ((ret = stat(ep->d_name, &st)) < 0) {
409 dbd_log( LOGSTD, "Lost file while reading dir '%s/%s', probably removed: %s", cwdbuf, ep->d_name, strerror(errno));
413 /**************************************************************************
415 **************************************************************************/
418 if ( -1 == (encoding_ok = check_name_encoding(ep->d_name)) ) {
419 /* If its a file: skipp all other tests now ! */
420 /* For dirs we skip tests too, but later */
421 if (S_ISREG(st.st_mode))
425 /* Check for appledouble file, create if missing */
426 if (S_ISREG(st.st_mode)) {
427 if ( 0 != check_adfile(ep->d_name, &st))
431 /**************************************************************************
433 **************************************************************************/
434 if (S_ISDIR(st.st_mode)) {
436 strcat(cwdbuf, ep->d_name);
437 dbd_log( LOGDEBUG, "Entering directory: %s", cwdbuf);
438 if (-1 == (cwd = open(".", O_RDONLY))) {
439 dbd_log( LOGSTD, "Cant open directory '%s': %s", cwdbuf, strerror(errno));
442 if (0 != chdir(ep->d_name)) {
443 dbd_log( LOGSTD, "Cant chdir to directory '%s': %s", cwdbuf, strerror(errno));
448 ret = dbd_readdir(0, did);
452 *(strrchr(cwdbuf, '/')) = 0;
459 Use results of previous checks
467 static int scanvol(struct volinfo *vi, dbd_flags_t flags)
469 /* Dont scanvol on no-appledouble vols */
470 if (vi->v_flags & AFPVOL_NOADOUBLE) {
471 dbd_log( LOGSTD, "Volume without AppleDouble support: skipping volume scanning.");
475 /* Make this stuff accessible from all funcs easily */
479 /* Run with umask 0 */
483 strcpy(cwdbuf, volinfo->v_path);
484 if (cwdbuf[strlen(cwdbuf) - 1] == '/')
485 cwdbuf[strlen(cwdbuf) - 1] = 0;
486 chdir(volinfo->v_path);
488 /* Start recursion */
489 if (dbd_readdir(1, 2) < 0) /* 2 = volumeroot CNID */
495 int cmd_dbd_scanvol(DBD *dbd, struct volinfo *volinfo, dbd_flags_t flags)
499 /* We only support unicode volumes ! */
500 if ( volinfo->v_volcharset != CH_UTF8) {
501 dbd_log( LOGSTD, "Not a Unicode volume: %s, %u != %u", volinfo->v_volcodepage, volinfo->v_volcharset, CH_UTF8);
505 /* open/create dbd, copy rootinfo key */
506 if (NULL == (dbd_rebuild = dbif_init(NULL)))
508 if (0 != (dbif_open(dbd_rebuild, NULL, 0)))
510 if (0 != (ret = dbif_copy_rootinfokey(dbd, dbd_rebuild)))
513 dbd_log( LOGSTD, "dumping rebuilddb");
514 dbif_dump(dbd_rebuild, 0);
517 if ( (scanvol(volinfo, flags)) != 0)
521 dbif_close(dbd_rebuild);