• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/router/netatalk-3.0.5/bin/uniconv/
1/*
2   uniconv - convert volume encodings
3   Copyright (C) Bjoern Fernhomberg 2004
4
5   This program is free software; you can redistribute it and/or modify
6   it under the terms of the GNU General Public License as published by
7   the Free Software Foundation; either version 2 of the License, or
8   (at your option) any later version.
9
10   This program is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   GNU General Public License for more details.
14
15   You should have received a copy of the GNU General Public License
16   along with this program; if not, write to the Free Software
17   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18*/
19
20
21#ifdef HAVE_CONFIG_H
22#include "config.h"
23#endif /* HAVE_CONFIG_H */
24#include <errno.h>
25#include <stdlib.h>
26#include <stdio.h>
27#include <fcntl.h>
28#include <unistd.h>
29#include <string.h>
30#include <sys/mman.h>
31#include <sys/stat.h>
32#include <sys/time.h>
33#include <ctype.h>
34#include <sys/types.h>
35#include <sys/param.h>
36#include <pwd.h>
37#include <dirent.h>
38#include <arpa/inet.h>
39
40#include <atalk/afp.h>
41#include <atalk/unicode.h>
42#include <atalk/util.h>
43#include <atalk/logger.h>
44#include <atalk/compat.h>
45
46#include "atalk/cnid.h"
47#ifndef MAXPATHLEN
48#define MAXPATHLEN 4096
49#endif
50
51static struct _cnid_db *cdb;
52static char curpath[MAXPATHLEN];
53static cnid_t cdir_id;
54static char db_stamp[ADEDLEN_PRIVSYN];
55
56static charset_t ch_from;
57char* from_charset;
58static charset_t ch_to;
59char* to_charset;
60static charset_t ch_mac;
61char* mac_charset;
62static int usedots = 0;
63static uint16_t conv_flags = 0;
64static int dry_run = 0;
65static int verbose=0;
66char *cnid_type;
67
68char             Cnid_srv[256] = "localhost";
69int              Cnid_port = 4700;
70
71extern  struct charset_functions charset_iso8859_adapted;
72
73#ifndef MAX
74#define MAX(a,b) (((a)>(b))?(a):(b))
75#endif
76
77
78#define VETO "./../.AppleDB/.AppleDouble/.AppleDesktop/.Parent/"
79
80static int veto(const char *path)
81{
82    int i,j;
83    char *veto_str = VETO;
84
85
86    if ((path == NULL))
87        return 0;
88
89    for(i=0, j=0; veto_str[i] != '\0'; i++) {
90        if (veto_str[i] == '/') {
91            if ((j>0) && (path[j] == '\0'))
92                return 1;
93            j = 0;
94        } else {
95            if (veto_str[i] != path[j]) {
96                while ((veto_str[i] != '/')
97                        && (veto_str[i] != '\0'))
98                    i++;
99                j = 0;
100                continue;
101            }
102            j++;
103        }
104    }
105    return 0;
106}
107
108
109static int do_rename( char* src, char *dst, struct stat *st)
110{
111    char        adsrc[ MAXPATHLEN + 1];
112    struct      stat tmp_st;
113
114    if (!stat(dst, &tmp_st)) {
115	fprintf (stderr, "error: cannot rename %s to %s, destination exists\n", src, dst);
116	return -1;
117    }
118
119    if ( rename( src, dst ) < 0 ) {
120	fprintf (stderr, "error: cannot rename %s to %s, %s\n", src, dst, strerror(errno));
121	return -1;
122    }
123
124    if (S_ISDIR(st->st_mode))
125	return 0;
126
127    strcpy( adsrc, ad_path( src, 0 ));
128
129    if (rename( adsrc, ad_path( dst, 0 )) < 0 ) {
130        struct stat ad_st;
131
132        if (errno == ENOENT) {
133            if (stat(adsrc, &ad_st)) /* source has no ressource fork, */
134		return 0;
135	}
136	else {
137		fprintf (stderr, "failed to rename resource fork, error: %s\n", strerror(errno));
138                return -1;
139 	}
140
141    }
142    return 0;
143}
144
145
146static char *convert_name(char *name, struct stat *st, cnid_t cur_did)
147{
148	static char   buffer[MAXPATHLEN +2];  /* for convert_charset dest_len parameter +2 */
149	size_t outlen = 0;
150	unsigned char *p,*q;
151	int require_conversion = 0;
152    uint16_t    flags = conv_flags;
153	cnid_t id;
154
155	p = (unsigned char *)name;
156	q = (unsigned char *)buffer;
157
158	/* optimize for ascii case */
159	while (*p != 0) {
160		if ( *p >= 0x80 || *p == ':') {
161			require_conversion = 1;
162			break;
163		}
164		p++;
165	}
166
167	if (!require_conversion) {
168		if (verbose > 1)
169		    fprintf(stdout, "no conversion required\n");
170		return name;
171	}
172
173	/* convert charsets */
174	q=(unsigned char *)buffer;
175	p=(unsigned char *)name;
176
177	outlen = convert_charset(ch_from, ch_to, ch_mac, (char *)p, strlen((char *)p), (char *)q, sizeof(buffer) -2, &flags);
178	if ((size_t)-1 == outlen) {
179  	   if ( ch_to == CH_UTF8) {
180		/* maybe name is already in UTF8? */
181		flags = conv_flags;
182		q = (unsigned char *)buffer;
183		p = (unsigned char *)name;
184		outlen = convert_charset(ch_to, ch_to, ch_mac, (char *)p, strlen((char *)p), (char *)q, sizeof(buffer) -2, &flags);
185		if ((size_t)-1 == outlen) {
186			/* it's not UTF8... */
187        		fprintf(stderr, "ERROR: conversion from '%s' to '%s' for '%s' in DID %u failed!!!\n",
188                  		from_charset, to_charset, name, ntohl(cur_did));
189			return name;
190		}
191
192		if (!strcmp(buffer, name)) {
193	   		return name;
194		}
195           }
196 	   fprintf(stderr, "ERROR: conversion from '%s' to '%s' for '%s' in DID %u failed. Please check this!\n",
197                  	from_charset, to_charset, name, ntohl(cur_did));
198	   return name;
199	}
200
201	if (strcmp (name, buffer)) {
202	    if (dry_run) {
203    		fprintf(stdout, "dry_run: would rename %s to %s.\n", name, buffer);
204	    }
205	    else if (!do_rename(name, buffer, st)) {
206		if (CNID_INVALID != (id = cnid_add(cdb, st, cur_did, buffer, strlen(buffer), 0)))
207       	    		fprintf(stdout, "converted '%s' to '%s' (ID %u, DID %u).\n",
208                                name, buffer, ntohl(id), ntohl(cur_did));
209	    }
210	}
211	else if (verbose > 1)
212	    fprintf(stdout, "no conversion required\n");
213
214	return (buffer);
215}
216
217static int check_dirent(char** name, cnid_t cur_did)
218{
219	struct stat st;
220	int ret = 0;
221
222	if (veto(*name))
223		return 0;
224
225        if (stat(*name, &st) != 0) {
226        	switch (errno) {
227                    case ELOOP:
228                    case ENOENT:
229			return 0;
230                    default:
231			return (-1);
232                }
233        }
234
235	if (S_ISDIR(st.st_mode)){
236		ret = 1;
237	}
238
239	if (verbose > 1)
240	    fprintf(stdout, "Checking: '%s' - ", *name);
241
242	*name = convert_name(*name, &st, cur_did);
243
244	return ret;
245}
246
247static int check_adouble(DIR *curdir, char * path _U_)
248{
249	DIR *adouble;
250	struct dirent* entry;
251	struct dirent* ad_entry;
252	int found = 0;
253
254	strlcat(curpath, "/", sizeof(curpath));
255	strlcat(curpath, ".AppleDouble", sizeof(curpath));
256
257	if (NULL == (adouble = opendir(curpath))) {
258		return(-1);
259	}
260
261	while (NULL != (ad_entry=readdir(adouble)) ) {
262		if (veto(ad_entry->d_name))
263			break;
264		found = 0;
265		rewinddir(curdir);
266		while (NULL != (entry=readdir(curdir)) ) {
267			if (!strcmp(ad_entry->d_name, entry->d_name)) {
268				found = 1;
269				break;
270			}
271		}
272		if (!found) {
273			fprintf (stderr, "found orphaned resource file %s", ad_entry->d_name);
274		}
275	}
276
277	rewinddir(curdir);
278	closedir(adouble);
279	return (0);
280}
281
282static cnid_t add_dir_db(char *name, cnid_t cur_did)
283{
284	cnid_t id, did;
285	struct stat st;
286
287	if (CNID_INVALID != ( id = cnid_get(cdb, cur_did, name, strlen(name))) )
288	    return id;
289
290	if (dry_run) {
291		return 0;
292	}
293
294        did = cur_did;
295	if (stat(name, &st)) {
296             fprintf( stderr, "dir '%s' cannot be stat'ed, error %u\n", name, errno);
297	     return 0;
298        }
299
300        id = cnid_add(cdb, &st, did, name, strlen(name), 0);
301
302	fprintf (stderr, "added '%s' to DID %u as %u\n", name, ntohl(did), ntohl(id));
303	return id;
304}
305
306static int getdir(DIR *curdir, char ***names)
307{
308	struct dirent* entry;
309	char **tmp = NULL, **new=NULL;
310	char *name;
311	int i=0;
312
313        while ((entry=readdir(curdir)) != NULL) {
314		new = (char **) realloc (tmp, (i+1) * sizeof(char*));
315		if (new == NULL) {
316			fprintf(stderr, "out of memory");
317			exit (-1);
318		}
319		tmp = new;
320		name = strdup(entry->d_name);
321		if (name == NULL) {
322			fprintf(stderr, "out of memory");
323			exit (-1);
324		}
325		tmp[i]= (char*) name;
326		i++;
327        };
328
329	*names = tmp;
330	return i;
331}
332
333static int checkdir(DIR *curdir, char *path, cnid_t cur_did)
334{
335	DIR* cdir;
336	int ret = 0;
337	cnid_t id;
338	char *name, *tmp;
339        int n;
340	size_t len=strlen(curpath);
341
342	char **names;
343
344	chdir(path);
345
346	check_adouble(curdir, path);
347	curpath[len] = 0;
348
349	if (verbose)
350	    fprintf( stdout, "\nchecking DIR '%s' with ID %u\n", path, ntohl(cur_did));
351
352	n = getdir(curdir, &names);
353
354	while (n--) {
355		name = names[n];
356		tmp = strdup(name);
357                ret = check_dirent(&name, cur_did);
358		if (ret==1) {
359                    id = add_dir_db(name, cur_did);
360		    if ( id == 0 && !dry_run )
361			continue;  /* skip, no ID */
362		    if ( dry_run )
363			name = tmp;
364                    strlcat(curpath, "/", sizeof(curpath));
365                    strlcat(curpath, name, sizeof(curpath));
366                    cdir = opendir(curpath);
367		    if (cdir == NULL) {
368	    	 	fprintf( stderr, "ERROR: cannot open DIR '%s' with ID %u\n", curpath, ntohl(cur_did));
369			continue;
370		    }
371                    checkdir(cdir, curpath, id);
372                    closedir(cdir);
373                    curpath[len] = 0;
374                    chdir(path);
375		    if (verbose)
376	    	 	fprintf( stdout, "returned to DIR '%s' with ID %u\n", path, ntohl(cur_did));
377                }
378                free(names[n]);
379		free(tmp);
380        }
381	free(names);
382	if (verbose)
383	    fprintf( stdout, "leaving DIR '%s' with ID %u\n\n", path, ntohl(cur_did));
384
385	return 0;
386}
387
388static int init(char* path)
389{
390	DIR* startdir;
391
392    if (NULL == (cdb = cnid_open (path, 0, cnid_type, 0, "localhost", "4700")) ) {
393                fprintf (stderr, "ERROR: cannot open CNID database in '%s'\n", path);
394                fprintf (stderr, "ERROR: check the logs for reasons, aborting\n");
395		return -1;
396        }
397        cnid_getstamp(cdb, db_stamp, sizeof(db_stamp));
398	cdir_id = htonl(2);
399
400	startdir = opendir(path);
401	strlcpy(curpath, path, sizeof(curpath));
402	checkdir (startdir, path, cdir_id);
403	closedir(startdir);
404
405	cnid_close(cdb);
406
407	return (0);
408}
409
410static void usage( char * name )
411{
412    fprintf( stderr, "usage:\t%s [-ndv] -c cnid -f fromcode -t tocode [-m maccode] path\n", name );
413    fprintf( stderr, "Try `%s -h' for more information.\n", name );
414    exit( 1 );
415}
416
417static void print_version (void)
418{
419    fprintf( stderr, "uniconv - Netatalk %s\n", VERSION );
420}
421
422static void help (void)
423{
424    fprintf (stdout, "\nuniconv, a tool to convert between various Netatalk volume encodings\n");
425    fprintf (stdout, "\nUsage:  uniconv [-ndv] -c cnid -f fromcode -t tocode [-m maccode] path\n\n");
426    fprintf (stdout, "Examples:\n");
427    fprintf (stdout, "     uniconv -c dbd -f ASCII -t UTF8 -m MAC_ROMAN /path/to/share\n");
428    fprintf (stdout, "     uniconv -c cdb -f ISO-8859-1 -t UTF8 -m MAC_ROMAN /path/to/share\n");
429    fprintf (stdout, "     uniconv -c cdb -f ISO-8859-ADAPTED -t ASCII -m MAC_ROMAN /path/to/share\n");
430    fprintf (stdout, "     uniconv -f UTF8 -t ASCII -m MAC_ROMAN /path/to/share\n\n");
431    fprintf (stdout, "Options:\n");
432    fprintf (stdout, "\t-f\tencoding to convert from, use ASCII for CAP encoded volumes\n");
433    fprintf (stdout, "\t-t\tvolume encoding to convert to, e.g. UTF8.\n");
434    fprintf (stdout, "\t-m\tMacintosh client codepage, required for CAP encoded volumes.\n");
435    fprintf (stdout, "\t\tDefaults to `MAC_ROMAN'\n");
436    fprintf (stdout, "\t-n\t`dry run', don't change anything.\n");
437    fprintf (stdout, "\t-d\tDon't CAP encode leading dots (:2e).\n");
438    fprintf (stdout, "\t-c\tCNID backend used on this volume, usually cdb or dbd.\n");
439    fprintf (stdout, "\t\tIf not specified, the default cnid backend `%s' is used\n", DEFAULT_CNID_SCHEME);
440    fprintf (stdout, "\t-v\tVerbose output, use twice for maximum logging.\n");
441    fprintf (stdout, "\t-V\tPrint version and exit\n");
442    fprintf (stdout, "\t-h\tThis help screen\n\n");
443    fprintf (stdout, "WARNING:\n");
444    fprintf (stdout, "     Setting the wrong options might render your data unusable!!!\n");
445    fprintf (stdout, "     Make sure you know what you are doing. Always backup your data first.\n\n");
446    fprintf (stdout, "     It is *strongly* recommended to do a `dry run' first and to check the\n");
447    fprintf (stdout, "     output for conversion errors.\n");
448    fprintf (stdout, "     USE AT YOUR OWN RISK!!!\n\n");
449
450}
451
452
453int main(int argc, char *argv[])
454{
455        char path[MAXPATHLEN];
456        int  c;
457
458	path[0]= 0;
459        conv_flags = CONV_UNESCAPEHEX | CONV_ESCAPEHEX | CONV_ESCAPEDOTS;
460
461#ifdef HAVE_SETLINEBUF
462        setlinebuf(stdout);
463#endif
464
465        while ((c = getopt (argc, argv, "f:m:t:c:dnvVh")) != -1)
466        switch (c)
467        {
468	case 'f':
469		from_charset = strdup(optarg);
470		break;
471	case 't':
472		to_charset = strdup(optarg);
473		break;
474	case 'm':
475		mac_charset = strdup(optarg);
476		break;
477	case 'd':
478		conv_flags &= ~CONV_ESCAPEDOTS;
479		usedots = 1;
480		break;
481	case 'n':
482		fprintf (stderr, "doing dry run, volume will *not* be changed\n");
483		dry_run = 1;
484		break;
485	case 'c':
486		cnid_type = strdup(optarg);
487		fprintf (stderr, "CNID backend set to: %s\n", cnid_type);
488		break;
489	case 'v':
490		verbose++;
491		break;
492	case 'V':
493		print_version();
494		exit (1);
495		break;
496        case 'h':
497		help();
498		exit(1);
499                break;
500        default:
501		break;
502        }
503
504        if ( argc - optind != 1 ) {
505            usage( argv[0] );
506            exit( 1 );
507        }
508        set_processname("uniconv");
509
510	if ( from_charset == NULL || to_charset == NULL) {
511		fprintf (stderr, "required charsets not specified\n");
512		exit(-1);
513	}
514
515	if ( mac_charset == NULL )
516		mac_charset = "MAC_ROMAN";
517
518	if ( cnid_type == NULL)
519		cnid_type = DEFAULT_CNID_SCHEME;
520
521
522	/* get path */
523	strlcpy(path, argv[optind], sizeof(path));
524
525	/* deal with relative path */
526	if (chdir(path)) {
527                fprintf (stderr, "ERROR: cannot chdir to '%s'\n", path);
528                return (-1);
529	}
530
531        if (NULL == (getcwd(path, sizeof(path))) ) {
532                fprintf (stderr, "ERROR: getcwd failed\n");
533                return (-1);
534        }
535
536	/* set charsets */
537	atalk_register_charset(&charset_iso8859_adapted);
538
539    	if ( (charset_t) -1 == ( ch_from = add_charset(from_charset)) ) {
540        	fprintf( stderr, "Setting codepage %s as source codepage failed", from_charset);
541		exit (-1);
542    	}
543
544    	if ( (charset_t) -1 == ( ch_to = add_charset(to_charset)) ) {
545        	fprintf( stderr, "Setting codepage %s as destination codepage failed", to_charset);
546		exit (-1);
547    	}
548
549    	if ( (charset_t) -1 == ( ch_mac = add_charset(mac_charset)) ) {
550        	fprintf( stderr, "Setting codepage %s as mac codepage failed", mac_charset);
551		exit (-1);
552    	}
553
554	cnid_init();
555	init(path);
556
557	return (0);
558}
559