Annotation of embedaddon/rsync/util.c, revision 1.1.1.1.2.1
1.1 misho 1: /*
2: * Utility routines used in rsync.
3: *
4: * Copyright (C) 1996-2000 Andrew Tridgell
5: * Copyright (C) 1996 Paul Mackerras
6: * Copyright (C) 2001, 2002 Martin Pool <mbp@samba.org>
7: * Copyright (C) 2003-2009 Wayne Davison
8: *
9: * This program is free software; you can redistribute it and/or modify
10: * it under the terms of the GNU General Public License as published by
11: * the Free Software Foundation; either version 3 of the License, or
12: * (at your option) any later version.
13: *
14: * This program is distributed in the hope that it will be useful,
15: * but WITHOUT ANY WARRANTY; without even the implied warranty of
16: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17: * GNU General Public License for more details.
18: *
19: * You should have received a copy of the GNU General Public License along
20: * with this program; if not, visit the http://fsf.org website.
21: */
22:
23: #include "rsync.h"
24: #include "ifuncs.h"
25:
26: extern int verbose;
27: extern int module_id;
28: extern int modify_window;
29: extern int relative_paths;
30: extern int preserve_times;
31: extern int human_readable;
32: extern int preserve_xattrs;
33: extern char *module_dir;
34: extern unsigned int module_dirlen;
35: extern mode_t orig_umask;
36: extern char *partial_dir;
37: extern struct filter_list_struct daemon_filter_list;
38:
39: int sanitize_paths = 0;
40:
41: char curr_dir[MAXPATHLEN];
42: unsigned int curr_dir_len;
43: int curr_dir_depth; /* This is only set for a sanitizing daemon. */
44:
45: /* Set a fd into nonblocking mode. */
46: void set_nonblocking(int fd)
47: {
48: int val;
49:
50: if ((val = fcntl(fd, F_GETFL)) == -1)
51: return;
52: if (!(val & NONBLOCK_FLAG)) {
53: val |= NONBLOCK_FLAG;
54: fcntl(fd, F_SETFL, val);
55: }
56: }
57:
58: /* Set a fd into blocking mode. */
59: void set_blocking(int fd)
60: {
61: int val;
62:
63: if ((val = fcntl(fd, F_GETFL)) == -1)
64: return;
65: if (val & NONBLOCK_FLAG) {
66: val &= ~NONBLOCK_FLAG;
67: fcntl(fd, F_SETFL, val);
68: }
69: }
70:
71: /**
72: * Create a file descriptor pair - like pipe() but use socketpair if
73: * possible (because of blocking issues on pipes).
74: *
75: * Always set non-blocking.
76: */
77: int fd_pair(int fd[2])
78: {
79: int ret;
80:
81: #ifdef HAVE_SOCKETPAIR
82: ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fd);
83: #else
84: ret = pipe(fd);
85: #endif
86:
87: if (ret == 0) {
88: set_nonblocking(fd[0]);
89: set_nonblocking(fd[1]);
90: }
91:
92: return ret;
93: }
94:
95: void print_child_argv(const char *prefix, char **cmd)
96: {
97: rprintf(FCLIENT, "%s ", prefix);
98: for (; *cmd; cmd++) {
99: /* Look for characters that ought to be quoted. This
100: * is not a great quoting algorithm, but it's
101: * sufficient for a log message. */
102: if (strspn(*cmd, "abcdefghijklmnopqrstuvwxyz"
103: "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
104: "0123456789"
105: ",.-_=+@/") != strlen(*cmd)) {
106: rprintf(FCLIENT, "\"%s\" ", *cmd);
107: } else {
108: rprintf(FCLIENT, "%s ", *cmd);
109: }
110: }
111: rprintf(FCLIENT, "\n");
112: }
113:
114: NORETURN void out_of_memory(const char *str)
115: {
116: rprintf(FERROR, "ERROR: out of memory in %s [%s]\n", str, who_am_i());
117: exit_cleanup(RERR_MALLOC);
118: }
119:
120: NORETURN void overflow_exit(const char *str)
121: {
122: rprintf(FERROR, "ERROR: buffer overflow in %s [%s]\n", str, who_am_i());
123: exit_cleanup(RERR_MALLOC);
124: }
125:
126: /* This returns 0 for success, 1 for a symlink if symlink time-setting
127: * is not possible, or -1 for any other error. */
128: int set_modtime(const char *fname, time_t modtime, mode_t mode)
129: {
130: static int switch_step = 0;
131:
132: if (verbose > 2) {
133: rprintf(FINFO, "set modtime of %s to (%ld) %s",
134: fname, (long)modtime,
135: asctime(localtime(&modtime)));
136: }
137:
138: switch (switch_step) {
139: #ifdef HAVE_UTIMENSAT
140: #include "case_N.h"
141: if (do_utimensat(fname, modtime, 0) == 0)
142: break;
143: if (errno != ENOSYS)
144: return -1;
145: switch_step++;
146: /* FALLTHROUGH */
147: #endif
148:
149: #ifdef HAVE_LUTIMES
150: #include "case_N.h"
151: if (do_lutimes(fname, modtime, 0) == 0)
152: break;
153: if (errno != ENOSYS)
154: return -1;
155: switch_step++;
156: /* FALLTHROUGH */
157: #endif
158:
159: #include "case_N.h"
160: switch_step++;
161: if (preserve_times & PRESERVE_LINK_TIMES) {
162: preserve_times &= ~PRESERVE_LINK_TIMES;
163: if (S_ISLNK(mode))
164: return 1;
165: }
166: /* FALLTHROUGH */
167:
168: #include "case_N.h"
169: #ifdef HAVE_UTIMES
170: if (do_utimes(fname, modtime, 0) == 0)
171: break;
172: #else
173: if (do_utime(fname, modtime, 0) == 0)
174: break;
175: #endif
176:
177: return -1;
178: }
179:
180: return 0;
181: }
182:
183: /* This creates a new directory with default permissions. Since there
184: * might be some directory-default permissions affecting this, we can't
185: * force the permissions directly using the original umask and mkdir(). */
186: int mkdir_defmode(char *fname)
187: {
188: int ret;
189:
190: umask(orig_umask);
191: ret = do_mkdir(fname, ACCESSPERMS);
192: umask(0);
193:
194: return ret;
195: }
196:
197: /* Create any necessary directories in fname. Any missing directories are
198: * created with default permissions. */
199: int create_directory_path(char *fname)
200: {
201: char *p;
202: int ret = 0;
203:
204: while (*fname == '/')
205: fname++;
206: while (strncmp(fname, "./", 2) == 0)
207: fname += 2;
208:
209: umask(orig_umask);
210: p = fname;
211: while ((p = strchr(p,'/')) != NULL) {
212: *p = '\0';
213: if (do_mkdir(fname, ACCESSPERMS) < 0 && errno != EEXIST)
214: ret = -1;
215: *p++ = '/';
216: }
217: umask(0);
218:
219: return ret;
220: }
221:
222: /**
223: * Write @p len bytes at @p ptr to descriptor @p desc, retrying if
224: * interrupted.
225: *
226: * @retval len upon success
227: *
228: * @retval <0 write's (negative) error code
229: *
230: * Derived from GNU C's cccp.c.
231: */
232: int full_write(int desc, const char *ptr, size_t len)
233: {
234: int total_written;
235:
236: total_written = 0;
237: while (len > 0) {
238: int written = write(desc, ptr, len);
239: if (written < 0) {
240: if (errno == EINTR)
241: continue;
242: return written;
243: }
244: total_written += written;
245: ptr += written;
246: len -= written;
247: }
248: return total_written;
249: }
250:
251: /**
252: * Read @p len bytes at @p ptr from descriptor @p desc, retrying if
253: * interrupted.
254: *
255: * @retval >0 the actual number of bytes read
256: *
257: * @retval 0 for EOF
258: *
259: * @retval <0 for an error.
260: *
261: * Derived from GNU C's cccp.c. */
262: static int safe_read(int desc, char *ptr, size_t len)
263: {
264: int n_chars;
265:
266: if (len == 0)
267: return len;
268:
269: do {
270: n_chars = read(desc, ptr, len);
271: } while (n_chars < 0 && errno == EINTR);
272:
273: return n_chars;
274: }
275:
276: /* Copy a file. If ofd < 0, copy_file unlinks and opens the "dest" file.
277: * Otherwise, it just writes to and closes the provided file descriptor.
278: * In either case, if --xattrs are being preserved, the dest file will
279: * have its xattrs set from the source file.
280: *
281: * This is used in conjunction with the --temp-dir, --backup, and
282: * --copy-dest options. */
283: int copy_file(const char *source, const char *dest, int ofd,
284: mode_t mode, int create_bak_dir)
285: {
286: int ifd;
287: char buf[1024 * 8];
288: int len; /* Number of bytes read into `buf'. */
289:
290: if ((ifd = do_open(source, O_RDONLY, 0)) < 0) {
291: int save_errno = errno;
292: rsyserr(FERROR_XFER, errno, "open %s", full_fname(source));
293: errno = save_errno;
294: return -1;
295: }
296:
297: if (ofd < 0) {
298: if (robust_unlink(dest) && errno != ENOENT) {
299: int save_errno = errno;
300: rsyserr(FERROR_XFER, errno, "unlink %s", full_fname(dest));
301: errno = save_errno;
302: return -1;
303: }
304:
305: if ((ofd = do_open(dest, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, mode)) < 0) {
306: int save_errno = errno ? errno : EINVAL; /* 0 paranoia */
307: if (create_bak_dir && errno == ENOENT && make_bak_dir(dest) == 0) {
308: if ((ofd = do_open(dest, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, mode)) < 0)
309: save_errno = errno ? errno : save_errno;
310: else
311: save_errno = 0;
312: }
313: if (save_errno) {
314: rsyserr(FERROR_XFER, save_errno, "open %s", full_fname(dest));
315: close(ifd);
316: errno = save_errno;
317: return -1;
318: }
319: }
320: }
321:
322: while ((len = safe_read(ifd, buf, sizeof buf)) > 0) {
323: if (full_write(ofd, buf, len) < 0) {
324: int save_errno = errno;
325: rsyserr(FERROR_XFER, errno, "write %s", full_fname(dest));
326: close(ifd);
327: close(ofd);
328: errno = save_errno;
329: return -1;
330: }
331: }
332:
333: if (len < 0) {
334: int save_errno = errno;
335: rsyserr(FERROR_XFER, errno, "read %s", full_fname(source));
336: close(ifd);
337: close(ofd);
338: errno = save_errno;
339: return -1;
340: }
341:
342: if (close(ifd) < 0) {
343: rsyserr(FWARNING, errno, "close failed on %s",
344: full_fname(source));
345: }
346:
347: if (close(ofd) < 0) {
348: int save_errno = errno;
349: rsyserr(FERROR_XFER, errno, "close failed on %s",
350: full_fname(dest));
351: errno = save_errno;
352: return -1;
353: }
354:
355: #ifdef SUPPORT_XATTRS
356: if (preserve_xattrs)
357: copy_xattrs(source, dest);
358: #endif
359:
360: return 0;
361: }
362:
363: /* MAX_RENAMES should be 10**MAX_RENAMES_DIGITS */
364: #define MAX_RENAMES_DIGITS 3
365: #define MAX_RENAMES 1000
366:
367: /**
368: * Robust unlink: some OS'es (HPUX) refuse to unlink busy files, so
369: * rename to <path>/.rsyncNNN instead.
370: *
371: * Note that successive rsync runs will shuffle the filenames around a
372: * bit as long as the file is still busy; this is because this function
373: * does not know if the unlink call is due to a new file coming in, or
374: * --delete trying to remove old .rsyncNNN files, hence it renames it
375: * each time.
376: **/
377: int robust_unlink(const char *fname)
378: {
379: #ifndef ETXTBSY
380: return do_unlink(fname);
381: #else
382: static int counter = 1;
383: int rc, pos, start;
384: char path[MAXPATHLEN];
385:
386: rc = do_unlink(fname);
387: if (rc == 0 || errno != ETXTBSY)
388: return rc;
389:
390: if ((pos = strlcpy(path, fname, MAXPATHLEN)) >= MAXPATHLEN)
391: pos = MAXPATHLEN - 1;
392:
393: while (pos > 0 && path[pos-1] != '/')
394: pos--;
395: pos += strlcpy(path+pos, ".rsync", MAXPATHLEN-pos);
396:
397: if (pos > (MAXPATHLEN-MAX_RENAMES_DIGITS-1)) {
398: errno = ETXTBSY;
399: return -1;
400: }
401:
402: /* start where the last one left off to reduce chance of clashes */
403: start = counter;
404: do {
405: snprintf(&path[pos], MAX_RENAMES_DIGITS+1, "%03d", counter);
406: if (++counter >= MAX_RENAMES)
407: counter = 1;
408: } while ((rc = access(path, 0)) == 0 && counter != start);
409:
410: if (verbose > 0) {
411: rprintf(FWARNING, "renaming %s to %s because of text busy\n",
412: fname, path);
413: }
414:
415: /* maybe we should return rename()'s exit status? Nah. */
416: if (do_rename(fname, path) != 0) {
417: errno = ETXTBSY;
418: return -1;
419: }
420: return 0;
421: #endif
422: }
423:
424: /* Returns 0 on successful rename, 1 if we successfully copied the file
425: * across filesystems, -2 if copy_file() failed, and -1 on other errors.
426: * If partialptr is not NULL and we need to do a copy, copy the file into
427: * the active partial-dir instead of over the destination file. */
428: int robust_rename(const char *from, const char *to, const char *partialptr,
429: int mode)
430: {
431: int tries = 4;
432:
433: while (tries--) {
434: if (do_rename(from, to) == 0)
435: return 0;
436:
437: switch (errno) {
438: #ifdef ETXTBSY
439: case ETXTBSY:
440: if (robust_unlink(to) != 0) {
441: errno = ETXTBSY;
442: return -1;
443: }
444: errno = ETXTBSY;
445: break;
446: #endif
447: case EXDEV:
448: if (partialptr) {
449: if (!handle_partial_dir(partialptr,PDIR_CREATE))
450: return -2;
451: to = partialptr;
452: }
453: if (copy_file(from, to, -1, mode, 0) != 0)
454: return -2;
455: do_unlink(from);
456: return 1;
457: default:
458: return -1;
459: }
460: }
461: return -1;
462: }
463:
464: static pid_t all_pids[10];
465: static int num_pids;
466:
467: /** Fork and record the pid of the child. **/
468: pid_t do_fork(void)
469: {
470: pid_t newpid = fork();
471:
472: if (newpid != 0 && newpid != -1) {
473: all_pids[num_pids++] = newpid;
474: }
475: return newpid;
476: }
477:
478: /**
479: * Kill all children.
480: *
481: * @todo It would be kind of nice to make sure that they are actually
482: * all our children before we kill them, because their pids may have
483: * been recycled by some other process. Perhaps when we wait for a
484: * child, we should remove it from this array. Alternatively we could
485: * perhaps use process groups, but I think that would not work on
486: * ancient Unix versions that don't support them.
487: **/
488: void kill_all(int sig)
489: {
490: int i;
491:
492: for (i = 0; i < num_pids; i++) {
493: /* Let's just be a little careful where we
494: * point that gun, hey? See kill(2) for the
495: * magic caused by negative values. */
496: pid_t p = all_pids[i];
497:
498: if (p == getpid())
499: continue;
500: if (p <= 0)
501: continue;
502:
503: kill(p, sig);
504: }
505: }
506:
507: /** Turn a user name into a uid */
508: int name_to_uid(const char *name, uid_t *uid_p)
509: {
510: struct passwd *pass;
511: if (!name || !*name)
512: return 0;
513: if (!(pass = getpwnam(name)))
514: return 0;
515: *uid_p = pass->pw_uid;
516: return 1;
517: }
518:
519: /** Turn a group name into a gid */
520: int name_to_gid(const char *name, gid_t *gid_p)
521: {
522: struct group *grp;
523: if (!name || !*name)
524: return 0;
525: if (!(grp = getgrnam(name)))
526: return 0;
527: *gid_p = grp->gr_gid;
528: return 1;
529: }
530:
531: /** Lock a byte range in a open file */
532: int lock_range(int fd, int offset, int len)
533: {
534: struct flock lock;
535:
536: lock.l_type = F_WRLCK;
537: lock.l_whence = SEEK_SET;
538: lock.l_start = offset;
539: lock.l_len = len;
540: lock.l_pid = 0;
541:
542: return fcntl(fd,F_SETLK,&lock) == 0;
543: }
544:
545: #define ENSURE_MEMSPACE(buf, type, sz, req) \
546: if ((req) > sz && !(buf = realloc_array(buf, type, sz = MAX(sz * 2, req)))) \
547: out_of_memory("glob_expand")
548:
549: static inline void call_glob_match(const char *name, int len, int from_glob,
550: char *arg, int abpos, int fbpos);
551:
552: static struct glob_data {
553: char *arg_buf, *filt_buf, **argv;
554: int absize, fbsize, maxargs, argc;
555: } glob;
556:
557: static void glob_match(char *arg, int abpos, int fbpos)
558: {
559: int len;
560: char *slash;
561:
562: while (*arg == '.' && arg[1] == '/') {
563: if (fbpos < 0) {
564: ENSURE_MEMSPACE(glob.filt_buf, char, glob.fbsize, glob.absize);
565: memcpy(glob.filt_buf, glob.arg_buf, abpos + 1);
566: fbpos = abpos;
567: }
568: ENSURE_MEMSPACE(glob.arg_buf, char, glob.absize, abpos + 3);
569: glob.arg_buf[abpos++] = *arg++;
570: glob.arg_buf[abpos++] = *arg++;
571: glob.arg_buf[abpos] = '\0';
572: }
573: if ((slash = strchr(arg, '/')) != NULL) {
574: *slash = '\0';
575: len = slash - arg;
576: } else
577: len = strlen(arg);
578: if (strpbrk(arg, "*?[")) {
579: struct dirent *di;
580: DIR *d;
581:
582: if (!(d = opendir(abpos ? glob.arg_buf : ".")))
583: return;
584: while ((di = readdir(d)) != NULL) {
585: char *dname = d_name(di);
586: if (dname[0] == '.' && (dname[1] == '\0'
587: || (dname[1] == '.' && dname[2] == '\0')))
588: continue;
589: if (!wildmatch(arg, dname))
590: continue;
591: call_glob_match(dname, strlen(dname), 1,
592: slash ? arg + len + 1 : NULL,
593: abpos, fbpos);
594: }
595: closedir(d);
596: } else {
597: call_glob_match(arg, len, 0,
598: slash ? arg + len + 1 : NULL,
599: abpos, fbpos);
600: }
601: if (slash)
602: *slash = '/';
603: }
604:
605: static inline void call_glob_match(const char *name, int len, int from_glob,
606: char *arg, int abpos, int fbpos)
607: {
608: char *use_buf;
609:
610: ENSURE_MEMSPACE(glob.arg_buf, char, glob.absize, abpos + len + 2);
611: memcpy(glob.arg_buf + abpos, name, len);
612: abpos += len;
613: glob.arg_buf[abpos] = '\0';
614:
615: if (fbpos >= 0) {
616: ENSURE_MEMSPACE(glob.filt_buf, char, glob.fbsize, fbpos + len + 2);
617: memcpy(glob.filt_buf + fbpos, name, len);
618: fbpos += len;
619: glob.filt_buf[fbpos] = '\0';
620: use_buf = glob.filt_buf;
621: } else
622: use_buf = glob.arg_buf;
623:
624: if (from_glob || (arg && len)) {
625: STRUCT_STAT st;
626: int is_dir;
627:
628: if (do_stat(glob.arg_buf, &st) != 0)
629: return;
630: is_dir = S_ISDIR(st.st_mode) != 0;
631: if (arg && !is_dir)
632: return;
633:
634: if (daemon_filter_list.head
635: && check_filter(&daemon_filter_list, FLOG, use_buf, is_dir) < 0)
636: return;
637: }
638:
639: if (arg) {
640: glob.arg_buf[abpos++] = '/';
641: glob.arg_buf[abpos] = '\0';
642: if (fbpos >= 0) {
643: glob.filt_buf[fbpos++] = '/';
644: glob.filt_buf[fbpos] = '\0';
645: }
646: glob_match(arg, abpos, fbpos);
647: } else {
648: ENSURE_MEMSPACE(glob.argv, char *, glob.maxargs, glob.argc + 1);
649: if (!(glob.argv[glob.argc++] = strdup(glob.arg_buf)))
650: out_of_memory("glob_match");
651: }
652: }
653:
654: /* This routine performs wild-card expansion of the pathname in "arg". Any
655: * daemon-excluded files/dirs will not be matched by the wildcards. Returns 0
656: * if a wild-card string is the only returned item (due to matching nothing). */
657: int glob_expand(const char *arg, char ***argv_p, int *argc_p, int *maxargs_p)
658: {
659: int ret, save_argc;
660: char *s;
661:
662: if (!arg) {
663: if (glob.filt_buf)
664: free(glob.filt_buf);
665: free(glob.arg_buf);
666: memset(&glob, 0, sizeof glob);
667: return -1;
668: }
669:
670: if (sanitize_paths)
671: s = sanitize_path(NULL, arg, "", 0, SP_KEEP_DOT_DIRS);
672: else {
673: s = strdup(arg);
674: if (!s)
675: out_of_memory("glob_expand");
676: clean_fname(s, CFN_KEEP_DOT_DIRS
677: | CFN_KEEP_TRAILING_SLASH
678: | CFN_COLLAPSE_DOT_DOT_DIRS);
679: }
680:
681: ENSURE_MEMSPACE(glob.arg_buf, char, glob.absize, MAXPATHLEN);
682: *glob.arg_buf = '\0';
683:
684: glob.argc = save_argc = *argc_p;
685: glob.argv = *argv_p;
686: glob.maxargs = *maxargs_p;
687:
688: ENSURE_MEMSPACE(glob.argv, char *, glob.maxargs, 100);
689:
690: glob_match(s, 0, -1);
691:
692: /* The arg didn't match anything, so add the failed arg to the list. */
693: if (glob.argc == save_argc) {
694: ENSURE_MEMSPACE(glob.argv, char *, glob.maxargs, glob.argc + 1);
695: glob.argv[glob.argc++] = s;
696: ret = 0;
697: } else {
698: free(s);
699: ret = 1;
700: }
701:
702: *maxargs_p = glob.maxargs;
703: *argv_p = glob.argv;
704: *argc_p = glob.argc;
705:
706: return ret;
707: }
708:
709: /* This routine is only used in daemon mode. */
710: void glob_expand_module(char *base1, char *arg, char ***argv_p, int *argc_p, int *maxargs_p)
711: {
712: char *p, *s;
713: char *base = base1;
714: int base_len = strlen(base);
715:
716: if (!arg || !*arg)
717: return;
718:
719: if (strncmp(arg, base, base_len) == 0)
720: arg += base_len;
721:
722: if (!(arg = strdup(arg)))
723: out_of_memory("glob_expand_module");
724:
725: if (asprintf(&base," %s/", base1) <= 0)
726: out_of_memory("glob_expand_module");
727: base_len++;
728:
729: for (s = arg; *s; s = p + base_len) {
730: if ((p = strstr(s, base)) != NULL)
731: *p = '\0'; /* split it at this point */
732: glob_expand(s, argv_p, argc_p, maxargs_p);
733: if (!p)
734: break;
735: }
736:
737: free(arg);
738: free(base);
739: }
740:
741: /**
742: * Convert a string to lower case
743: **/
744: void strlower(char *s)
745: {
746: while (*s) {
747: if (isUpper(s))
748: *s = toLower(s);
749: s++;
750: }
751: }
752:
753: /* Join strings p1 & p2 into "dest" with a guaranteed '/' between them. (If
754: * p1 ends with a '/', no extra '/' is inserted.) Returns the length of both
755: * strings + 1 (if '/' was inserted), regardless of whether the null-terminated
756: * string fits into destsize. */
757: size_t pathjoin(char *dest, size_t destsize, const char *p1, const char *p2)
758: {
759: size_t len = strlcpy(dest, p1, destsize);
760: if (len < destsize - 1) {
761: if (!len || dest[len-1] != '/')
762: dest[len++] = '/';
763: if (len < destsize - 1)
764: len += strlcpy(dest + len, p2, destsize - len);
765: else {
766: dest[len] = '\0';
767: len += strlen(p2);
768: }
769: }
770: else
771: len += strlen(p2) + 1; /* Assume we'd insert a '/'. */
772: return len;
773: }
774:
775: /* Join any number of strings together, putting them in "dest". The return
776: * value is the length of all the strings, regardless of whether the null-
777: * terminated whole fits in destsize. Your list of string pointers must end
778: * with a NULL to indicate the end of the list. */
779: size_t stringjoin(char *dest, size_t destsize, ...)
780: {
781: va_list ap;
782: size_t len, ret = 0;
783: const char *src;
784:
785: va_start(ap, destsize);
786: while (1) {
787: if (!(src = va_arg(ap, const char *)))
788: break;
789: len = strlen(src);
790: ret += len;
791: if (destsize > 1) {
792: if (len >= destsize)
793: len = destsize - 1;
794: memcpy(dest, src, len);
795: destsize -= len;
796: dest += len;
797: }
798: }
799: *dest = '\0';
800: va_end(ap);
801:
802: return ret;
803: }
804:
805: int count_dir_elements(const char *p)
806: {
807: int cnt = 0, new_component = 1;
808: while (*p) {
809: if (*p++ == '/')
810: new_component = (*p != '.' || (p[1] != '/' && p[1] != '\0'));
811: else if (new_component) {
812: new_component = 0;
813: cnt++;
814: }
815: }
816: return cnt;
817: }
818:
819: /* Turns multiple adjacent slashes into a single slash (possible exception:
820: * the preserving of two leading slashes at the start), drops all leading or
821: * interior "." elements unless CFN_KEEP_DOT_DIRS is flagged. Will also drop
822: * a trailing '.' after a '/' if CFN_DROP_TRAILING_DOT_DIR is flagged, removes
823: * a trailing slash (perhaps after removing the aforementioned dot) unless
824: * CFN_KEEP_TRAILING_SLASH is flagged, and will also collapse ".." elements
825: * (except at the start) if CFN_COLLAPSE_DOT_DOT_DIRS is flagged. If the
826: * resulting name would be empty, returns ".". */
827: unsigned int clean_fname(char *name, int flags)
828: {
829: char *limit = name - 1, *t = name, *f = name;
830: int anchored;
831:
832: if (!name)
833: return 0;
834:
835: if ((anchored = *f == '/') != 0) {
836: *t++ = *f++;
837: #ifdef __CYGWIN__
838: /* If there are exactly 2 slashes at the start, preserve
839: * them. Would break daemon excludes unless the paths are
840: * really treated differently, so used this sparingly. */
841: if (*f == '/' && f[1] != '/')
842: *t++ = *f++;
843: #endif
844: } else if (flags & CFN_KEEP_DOT_DIRS && *f == '.' && f[1] == '/') {
845: *t++ = *f++;
846: *t++ = *f++;
847: }
848: while (*f) {
849: /* discard extra slashes */
850: if (*f == '/') {
851: f++;
852: continue;
853: }
854: if (*f == '.') {
855: /* discard interior "." dirs */
856: if (f[1] == '/' && !(flags & CFN_KEEP_DOT_DIRS)) {
857: f += 2;
858: continue;
859: }
860: if (f[1] == '\0' && flags & CFN_DROP_TRAILING_DOT_DIR)
861: break;
862: /* collapse ".." dirs */
863: if (flags & CFN_COLLAPSE_DOT_DOT_DIRS
864: && f[1] == '.' && (f[2] == '/' || !f[2])) {
865: char *s = t - 1;
866: if (s == name && anchored) {
867: f += 2;
868: continue;
869: }
870: while (s > limit && *--s != '/') {}
871: if (s != t - 1 && (s < name || *s == '/')) {
872: t = s + 1;
873: f += 2;
874: continue;
875: }
876: limit = t + 2;
877: }
878: }
879: while (*f && (*t++ = *f++) != '/') {}
880: }
881:
882: if (t > name+anchored && t[-1] == '/' && !(flags & CFN_KEEP_TRAILING_SLASH))
883: t--;
884: if (t == name)
885: *t++ = '.';
886: *t = '\0';
887:
888: return t - name;
889: }
890:
891: /* Make path appear as if a chroot had occurred. This handles a leading
892: * "/" (either removing it or expanding it) and any leading or embedded
893: * ".." components that attempt to escape past the module's top dir.
894: *
895: * If dest is NULL, a buffer is allocated to hold the result. It is legal
896: * to call with the dest and the path (p) pointing to the same buffer, but
897: * rootdir will be ignored to avoid expansion of the string.
898: *
899: * The rootdir string contains a value to use in place of a leading slash.
900: * Specify NULL to get the default of "module_dir".
901: *
902: * The depth var is a count of how many '..'s to allow at the start of the
903: * path.
904: *
905: * We also clean the path in a manner similar to clean_fname() but with a
906: * few differences:
907: *
908: * Turns multiple adjacent slashes into a single slash, gets rid of "." dir
909: * elements (INCLUDING a trailing dot dir), PRESERVES a trailing slash, and
910: * ALWAYS collapses ".." elements (except for those at the start of the
911: * string up to "depth" deep). If the resulting name would be empty,
912: * change it into a ".". */
913: char *sanitize_path(char *dest, const char *p, const char *rootdir, int depth,
914: int flags)
915: {
916: char *start, *sanp;
917: int rlen = 0, drop_dot_dirs = !relative_paths || !(flags & SP_KEEP_DOT_DIRS);
918:
919: if (dest != p) {
920: int plen = strlen(p);
921: if (*p == '/') {
922: if (!rootdir)
923: rootdir = module_dir;
924: rlen = strlen(rootdir);
925: depth = 0;
926: p++;
927: }
928: if (dest) {
929: if (rlen + plen + 1 >= MAXPATHLEN)
930: return NULL;
931: } else if (!(dest = new_array(char, rlen + plen + 1)))
932: out_of_memory("sanitize_path");
933: if (rlen) {
934: memcpy(dest, rootdir, rlen);
935: if (rlen > 1)
936: dest[rlen++] = '/';
937: }
938: }
939:
940: if (drop_dot_dirs) {
941: while (*p == '.' && p[1] == '/')
942: p += 2;
943: }
944:
945: start = sanp = dest + rlen;
946: /* This loop iterates once per filename component in p, pointing at
947: * the start of the name (past any prior slash) for each iteration. */
948: while (*p) {
949: /* discard leading or extra slashes */
950: if (*p == '/') {
951: p++;
952: continue;
953: }
954: if (drop_dot_dirs) {
955: if (*p == '.' && (p[1] == '/' || p[1] == '\0')) {
956: /* skip "." component */
957: p++;
958: continue;
959: }
960: }
961: if (*p == '.' && p[1] == '.' && (p[2] == '/' || p[2] == '\0')) {
962: /* ".." component followed by slash or end */
963: if (depth <= 0 || sanp != start) {
964: p += 2;
965: if (sanp != start) {
966: /* back up sanp one level */
967: --sanp; /* now pointing at slash */
968: while (sanp > start && sanp[-1] != '/')
969: sanp--;
970: }
971: continue;
972: }
973: /* allow depth levels of .. at the beginning */
974: depth--;
975: /* move the virtual beginning to leave the .. alone */
976: start = sanp + 3;
977: }
978: /* copy one component through next slash */
979: while (*p && (*sanp++ = *p++) != '/') {}
980: }
981: if (sanp == dest) {
982: /* ended up with nothing, so put in "." component */
983: *sanp++ = '.';
984: }
985: *sanp = '\0';
986:
987: return dest;
988: }
989:
990: /* Like chdir(), but it keeps track of the current directory (in the
991: * global "curr_dir"), and ensures that the path size doesn't overflow.
992: * Also cleans the path using the clean_fname() function. */
993: int change_dir(const char *dir, int set_path_only)
994: {
995: static int initialised;
996: unsigned int len;
997:
998: if (!initialised) {
999: initialised = 1;
1000: if (getcwd(curr_dir, sizeof curr_dir - 1) == NULL) {
1001: rsyserr(FERROR, errno, "getcwd()");
1002: exit_cleanup(RERR_FILESELECT);
1003: }
1004: curr_dir_len = strlen(curr_dir);
1005: }
1006:
1007: if (!dir) /* this call was probably just to initialize */
1008: return 0;
1009:
1010: len = strlen(dir);
1011: if (len == 1 && *dir == '.')
1012: return 1;
1013:
1014: if (*dir == '/') {
1015: if (len >= sizeof curr_dir) {
1016: errno = ENAMETOOLONG;
1017: return 0;
1018: }
1019: if (!set_path_only && chdir(dir))
1020: return 0;
1021: memcpy(curr_dir, dir, len + 1);
1022: } else {
1023: if (curr_dir_len + 1 + len >= sizeof curr_dir) {
1024: errno = ENAMETOOLONG;
1025: return 0;
1026: }
1027: if (!(curr_dir_len && curr_dir[curr_dir_len-1] == '/'))
1028: curr_dir[curr_dir_len++] = '/';
1029: memcpy(curr_dir + curr_dir_len, dir, len + 1);
1030:
1031: if (!set_path_only && chdir(curr_dir)) {
1032: curr_dir[curr_dir_len] = '\0';
1033: return 0;
1034: }
1035: }
1036:
1037: curr_dir_len = clean_fname(curr_dir, CFN_COLLAPSE_DOT_DOT_DIRS);
1038: if (sanitize_paths) {
1039: if (module_dirlen > curr_dir_len)
1040: module_dirlen = curr_dir_len;
1041: curr_dir_depth = count_dir_elements(curr_dir + module_dirlen);
1042: }
1043:
1044: if (verbose >= 5 && !set_path_only)
1045: rprintf(FINFO, "[%s] change_dir(%s)\n", who_am_i(), curr_dir);
1046:
1047: return 1;
1048: }
1049:
1050: /* This will make a relative path absolute and clean it up via clean_fname().
1051: * Returns the string, which might be newly allocated, or NULL on error. */
1052: char *normalize_path(char *path, BOOL force_newbuf, unsigned int *len_ptr)
1053: {
1054: unsigned int len;
1055:
1056: if (*path != '/') { /* Make path absolute. */
1057: int len = strlen(path);
1058: if (curr_dir_len + 1 + len >= sizeof curr_dir)
1059: return NULL;
1060: curr_dir[curr_dir_len] = '/';
1061: memcpy(curr_dir + curr_dir_len + 1, path, len + 1);
1062: if (!(path = strdup(curr_dir)))
1063: out_of_memory("normalize_path");
1064: curr_dir[curr_dir_len] = '\0';
1065: } else if (force_newbuf) {
1066: if (!(path = strdup(path)))
1067: out_of_memory("normalize_path");
1068: }
1069:
1070: len = clean_fname(path, CFN_COLLAPSE_DOT_DOT_DIRS | CFN_DROP_TRAILING_DOT_DIR);
1071:
1072: if (len_ptr)
1073: *len_ptr = len;
1074:
1075: return path;
1076: }
1077:
1078: /**
1079: * Return a quoted string with the full pathname of the indicated filename.
1080: * The string " (in MODNAME)" may also be appended. The returned pointer
1081: * remains valid until the next time full_fname() is called.
1082: **/
1083: char *full_fname(const char *fn)
1084: {
1085: static char *result = NULL;
1086: char *m1, *m2, *m3;
1087: char *p1, *p2;
1088:
1089: if (result)
1090: free(result);
1091:
1092: if (*fn == '/')
1093: p1 = p2 = "";
1094: else {
1095: p1 = curr_dir + module_dirlen;
1096: for (p2 = p1; *p2 == '/'; p2++) {}
1097: if (*p2)
1098: p2 = "/";
1099: }
1100: if (module_id >= 0) {
1101: m1 = " (in ";
1102: m2 = lp_name(module_id);
1103: m3 = ")";
1104: } else
1105: m1 = m2 = m3 = "";
1106:
1107: if (asprintf(&result, "\"%s%s%s\"%s%s%s", p1, p2, fn, m1, m2, m3) <= 0)
1108: out_of_memory("full_fname");
1109:
1110: return result;
1111: }
1112:
1113: static char partial_fname[MAXPATHLEN];
1114:
1115: char *partial_dir_fname(const char *fname)
1116: {
1117: char *t = partial_fname;
1118: int sz = sizeof partial_fname;
1119: const char *fn;
1120:
1121: if ((fn = strrchr(fname, '/')) != NULL) {
1122: fn++;
1123: if (*partial_dir != '/') {
1124: int len = fn - fname;
1125: strncpy(t, fname, len); /* safe */
1126: t += len;
1127: sz -= len;
1128: }
1129: } else
1130: fn = fname;
1131: if ((int)pathjoin(t, sz, partial_dir, fn) >= sz)
1132: return NULL;
1133: if (daemon_filter_list.head) {
1134: t = strrchr(partial_fname, '/');
1135: *t = '\0';
1136: if (check_filter(&daemon_filter_list, FLOG, partial_fname, 1) < 0)
1137: return NULL;
1138: *t = '/';
1139: if (check_filter(&daemon_filter_list, FLOG, partial_fname, 0) < 0)
1140: return NULL;
1141: }
1142:
1143: return partial_fname;
1144: }
1145:
1146: /* If no --partial-dir option was specified, we don't need to do anything
1147: * (the partial-dir is essentially '.'), so just return success. */
1148: int handle_partial_dir(const char *fname, int create)
1149: {
1150: char *fn, *dir;
1151:
1152: if (fname != partial_fname)
1153: return 1;
1154: if (!create && *partial_dir == '/')
1155: return 1;
1156: if (!(fn = strrchr(partial_fname, '/')))
1157: return 1;
1158:
1159: *fn = '\0';
1160: dir = partial_fname;
1161: if (create) {
1162: STRUCT_STAT st;
1163: int statret = do_lstat(dir, &st);
1164: if (statret == 0 && !S_ISDIR(st.st_mode)) {
1165: if (do_unlink(dir) < 0) {
1166: *fn = '/';
1167: return 0;
1168: }
1169: statret = -1;
1170: }
1.1.1.1.2.1! misho 1171: if (statret < 0 && do_mkdir_path(dir, 0700) < 0) {
1.1 misho 1172: *fn = '/';
1173: return 0;
1174: }
1175: } else
1176: do_rmdir(dir);
1177: *fn = '/';
1178:
1179: return 1;
1180: }
1181:
1.1.1.1.2.1! misho 1182: /* We need to supply our own strcmp function for file list comparisons
! 1183: * to ensure that signed/unsigned usage is consistent between machines. */
! 1184: int u_strcmp(const char *p1, const char *p2)
! 1185: {
! 1186: for ( ; *p1; p1++, p2++) {
! 1187: if (*p1 != *p2)
! 1188: break;
! 1189: }
! 1190:
! 1191: return (int)*(uchar*)p1 - (int)*(uchar*)p2;
! 1192: }
! 1193:
! 1194: /* We need a memcmp function compares unsigned-byte values. */
! 1195: int u_memcmp(const void *p1, const void *p2, size_t len)
! 1196: {
! 1197: const uchar *u1 = p1;
! 1198: const uchar *u2 = p2;
! 1199:
! 1200: while (len--) {
! 1201: if (*u1 != *u2)
! 1202: return (int)*u1 - (int)*u2;
! 1203: }
! 1204:
! 1205: return 0;
! 1206: }
! 1207:
1.1 misho 1208: /* Determine if a symlink points outside the current directory tree.
1209: * This is considered "unsafe" because e.g. when mirroring somebody
1210: * else's machine it might allow them to establish a symlink to
1211: * /etc/passwd, and then read it through a web server.
1212: *
1213: * Returns 1 if unsafe, 0 if safe.
1214: *
1215: * Null symlinks and absolute symlinks are always unsafe.
1216: *
1217: * Basically here we are concerned with symlinks whose target contains
1218: * "..", because this might cause us to walk back up out of the
1219: * transferred directory. We are not allowed to go back up and
1220: * reenter.
1221: *
1222: * "dest" is the target of the symlink in question.
1223: *
1224: * "src" is the top source directory currently applicable at the level
1225: * of the referenced symlink. This is usually the symlink's full path
1226: * (including its name), as referenced from the root of the transfer. */
1227: int unsafe_symlink(const char *dest, const char *src)
1228: {
1229: const char *name, *slash;
1230: int depth = 0;
1231:
1232: /* all absolute and null symlinks are unsafe */
1233: if (!dest || !*dest || *dest == '/')
1234: return 1;
1235:
1236: /* find out what our safety margin is */
1237: for (name = src; (slash = strchr(name, '/')) != 0; name = slash+1) {
1238: /* ".." segment starts the count over. "." segment is ignored. */
1239: if (*name == '.' && (name[1] == '/' || (name[1] == '.' && name[2] == '/'))) {
1240: if (name[1] == '.')
1241: depth = 0;
1242: } else
1243: depth++;
1244: while (slash[1] == '/') slash++; /* just in case src isn't clean */
1245: }
1246: if (*name == '.' && name[1] == '.' && name[2] == '\0')
1247: depth = 0;
1248:
1249: for (name = dest; (slash = strchr(name, '/')) != 0; name = slash+1) {
1250: if (*name == '.' && (name[1] == '/' || (name[1] == '.' && name[2] == '/'))) {
1251: if (name[1] == '.') {
1252: /* if at any point we go outside the current directory
1253: then stop - it is unsafe */
1254: if (--depth < 0)
1255: return 1;
1256: }
1257: } else
1258: depth++;
1259: while (slash[1] == '/') slash++;
1260: }
1261: if (*name == '.' && name[1] == '.' && name[2] == '\0')
1262: depth--;
1263:
1264: return depth < 0;
1265: }
1266:
1267: #define HUMANIFY(mult) \
1268: do { \
1269: if (num >= mult || num <= -mult) { \
1270: double dnum = (double)num / mult; \
1271: char units; \
1272: if (num < 0) \
1273: dnum = -dnum; \
1274: if (dnum < mult) \
1275: units = 'K'; \
1276: else if ((dnum /= mult) < mult) \
1277: units = 'M'; \
1278: else { \
1279: dnum /= mult; \
1280: units = 'G'; \
1281: } \
1282: if (num < 0) \
1283: dnum = -dnum; \
1284: snprintf(bufs[n], sizeof bufs[0], "%.2f%c", dnum, units); \
1285: return bufs[n]; \
1286: } \
1287: } while (0)
1288:
1289: /* Return the int64 number as a string. If the --human-readable option was
1290: * specified, we may output the number in K, M, or G units. We can return
1291: * up to 4 buffers at a time. */
1292: char *human_num(int64 num)
1293: {
1294: static char bufs[4][128]; /* more than enough room */
1295: static unsigned int n;
1296: char *s;
1297: int negated;
1298:
1299: n = (n + 1) % (sizeof bufs / sizeof bufs[0]);
1300:
1301: if (human_readable) {
1302: if (human_readable == 1)
1303: HUMANIFY(1000);
1304: else
1305: HUMANIFY(1024);
1306: }
1307:
1308: s = bufs[n] + sizeof bufs[0] - 1;
1309: *s = '\0';
1310:
1311: if (!num)
1312: *--s = '0';
1313: if (num < 0) {
1314: /* A maximum-size negated number can't fit as a positive,
1315: * so do one digit in negated form to start us off. */
1316: *--s = (char)(-(num % 10)) + '0';
1317: num = -(num / 10);
1318: negated = 1;
1319: } else
1320: negated = 0;
1321:
1322: while (num) {
1323: *--s = (char)(num % 10) + '0';
1324: num /= 10;
1325: }
1326:
1327: if (negated)
1328: *--s = '-';
1329:
1330: return s;
1331: }
1332:
1333: /* Return the double number as a string. If the --human-readable option was
1334: * specified, we may output the number in K, M, or G units. We use a buffer
1335: * from human_num() to return our result. */
1336: char *human_dnum(double dnum, int decimal_digits)
1337: {
1338: char *buf = human_num(dnum);
1339: int len = strlen(buf);
1340: if (isDigit(buf + len - 1)) {
1341: /* There's extra room in buf prior to the start of the num. */
1342: buf -= decimal_digits + 2;
1343: snprintf(buf, len + decimal_digits + 3, "%.*f", decimal_digits, dnum);
1344: }
1345: return buf;
1346: }
1347:
1348: /* Return the date and time as a string. Some callers tweak returned buf. */
1349: char *timestring(time_t t)
1350: {
1351: static char TimeBuf[200];
1352: struct tm *tm = localtime(&t);
1353: char *p;
1354:
1355: #ifdef HAVE_STRFTIME
1356: strftime(TimeBuf, sizeof TimeBuf - 1, "%Y/%m/%d %H:%M:%S", tm);
1357: #else
1358: strlcpy(TimeBuf, asctime(tm), sizeof TimeBuf);
1359: #endif
1360:
1361: if ((p = strchr(TimeBuf, '\n')) != NULL)
1362: *p = '\0';
1363:
1364: return TimeBuf;
1365: }
1366:
1367: /**
1368: * Sleep for a specified number of milliseconds.
1369: *
1370: * Always returns TRUE. (In the future it might return FALSE if
1371: * interrupted.)
1372: **/
1373: int msleep(int t)
1374: {
1375: int tdiff = 0;
1376: struct timeval tval, t1, t2;
1377:
1378: gettimeofday(&t1, NULL);
1379:
1380: while (tdiff < t) {
1381: tval.tv_sec = (t-tdiff)/1000;
1382: tval.tv_usec = 1000*((t-tdiff)%1000);
1383:
1384: errno = 0;
1385: select(0,NULL,NULL, NULL, &tval);
1386:
1387: gettimeofday(&t2, NULL);
1388: tdiff = (t2.tv_sec - t1.tv_sec)*1000 +
1389: (t2.tv_usec - t1.tv_usec)/1000;
1390: }
1391:
1392: return True;
1393: }
1394:
1395: /* Determine if two time_t values are equivalent (either exact, or in
1396: * the modification timestamp window established by --modify-window).
1397: *
1398: * @retval 0 if the times should be treated as the same
1399: *
1400: * @retval +1 if the first is later
1401: *
1402: * @retval -1 if the 2nd is later
1403: **/
1404: int cmp_time(time_t file1, time_t file2)
1405: {
1406: if (file2 > file1) {
1407: if (file2 - file1 <= modify_window)
1408: return 0;
1409: return -1;
1410: }
1411: if (file1 - file2 <= modify_window)
1412: return 0;
1413: return 1;
1414: }
1415:
1416:
1417: #ifdef __INSURE__XX
1418: #include <dlfcn.h>
1419:
1420: /**
1421: This routine is a trick to immediately catch errors when debugging
1422: with insure. A xterm with a gdb is popped up when insure catches
1423: a error. It is Linux specific.
1424: **/
1425: int _Insure_trap_error(int a1, int a2, int a3, int a4, int a5, int a6)
1426: {
1427: static int (*fn)();
1428: int ret;
1429: char *cmd;
1430:
1431: asprintf(&cmd, "/usr/X11R6/bin/xterm -display :0 -T Panic -n Panic -e /bin/sh -c 'cat /tmp/ierrs.*.%d ; gdb /proc/%d/exe %d'",
1432: getpid(), getpid(), getpid());
1433:
1434: if (!fn) {
1435: static void *h;
1436: h = dlopen("/usr/local/parasoft/insure++lite/lib.linux2/libinsure.so", RTLD_LAZY);
1437: fn = dlsym(h, "_Insure_trap_error");
1438: }
1439:
1440: ret = fn(a1, a2, a3, a4, a5, a6);
1441:
1442: system(cmd);
1443:
1444: free(cmd);
1445:
1446: return ret;
1447: }
1448: #endif
1449:
1450: #define MALLOC_MAX 0x40000000
1451:
1452: void *_new_array(unsigned long num, unsigned int size, int use_calloc)
1453: {
1454: if (num >= MALLOC_MAX/size)
1455: return NULL;
1456: return use_calloc ? calloc(num, size) : malloc(num * size);
1457: }
1458:
1459: void *_realloc_array(void *ptr, unsigned int size, size_t num)
1460: {
1461: if (num >= MALLOC_MAX/size)
1462: return NULL;
1463: if (!ptr)
1464: return malloc(size * num);
1465: return realloc(ptr, size * num);
1466: }
1467:
1468: /* Take a filename and filename length and return the most significant
1469: * filename suffix we can find. This ignores suffixes such as "~",
1470: * ".bak", ".orig", ".~1~", etc. */
1471: const char *find_filename_suffix(const char *fn, int fn_len, int *len_ptr)
1472: {
1473: const char *suf, *s;
1474: BOOL had_tilde;
1475: int s_len;
1476:
1477: /* One or more dots at the start aren't a suffix. */
1478: while (fn_len && *fn == '.') fn++, fn_len--;
1479:
1480: /* Ignore the ~ in a "foo~" filename. */
1481: if (fn_len > 1 && fn[fn_len-1] == '~')
1482: fn_len--, had_tilde = True;
1483: else
1484: had_tilde = False;
1485:
1486: /* Assume we don't find an suffix. */
1487: suf = "";
1488: *len_ptr = 0;
1489:
1490: /* Find the last significant suffix. */
1491: for (s = fn + fn_len; fn_len > 1; ) {
1492: while (*--s != '.' && s != fn) {}
1493: if (s == fn)
1494: break;
1495: s_len = fn_len - (s - fn);
1496: fn_len = s - fn;
1497: if (s_len == 4) {
1498: if (strcmp(s+1, "bak") == 0
1499: || strcmp(s+1, "old") == 0)
1500: continue;
1501: } else if (s_len == 5) {
1502: if (strcmp(s+1, "orig") == 0)
1503: continue;
1504: } else if (s_len > 2 && had_tilde
1505: && s[1] == '~' && isDigit(s + 2))
1506: continue;
1507: *len_ptr = s_len;
1508: suf = s;
1509: if (s_len == 1)
1510: break;
1511: /* Determine if the suffix is all digits. */
1512: for (s++, s_len--; s_len > 0; s++, s_len--) {
1513: if (!isDigit(s))
1514: return suf;
1515: }
1516: /* An all-digit suffix may not be that signficant. */
1517: s = suf;
1518: }
1519:
1520: return suf;
1521: }
1522:
1523: /* This is an implementation of the Levenshtein distance algorithm. It
1524: * was implemented to avoid needing a two-dimensional matrix (to save
1525: * memory). It was also tweaked to try to factor in the ASCII distance
1526: * between changed characters as a minor distance quantity. The normal
1527: * Levenshtein units of distance (each signifying a single change between
1528: * the two strings) are defined as a "UNIT". */
1529:
1530: #define UNIT (1 << 16)
1531:
1532: uint32 fuzzy_distance(const char *s1, int len1, const char *s2, int len2)
1533: {
1534: uint32 a[MAXPATHLEN], diag, above, left, diag_inc, above_inc, left_inc;
1535: int32 cost;
1536: int i1, i2;
1537:
1538: if (!len1 || !len2) {
1539: if (!len1) {
1540: s1 = s2;
1541: len1 = len2;
1542: }
1543: for (i1 = 0, cost = 0; i1 < len1; i1++)
1544: cost += s1[i1];
1545: return (int32)len1 * UNIT + cost;
1546: }
1547:
1548: for (i2 = 0; i2 < len2; i2++)
1549: a[i2] = (i2+1) * UNIT;
1550:
1551: for (i1 = 0; i1 < len1; i1++) {
1552: diag = i1 * UNIT;
1553: above = (i1+1) * UNIT;
1554: for (i2 = 0; i2 < len2; i2++) {
1555: left = a[i2];
1556: if ((cost = *((uchar*)s1+i1) - *((uchar*)s2+i2)) != 0) {
1557: if (cost < 0)
1558: cost = UNIT - cost;
1559: else
1560: cost = UNIT + cost;
1561: }
1562: diag_inc = diag + cost;
1563: left_inc = left + UNIT + *((uchar*)s1+i1);
1564: above_inc = above + UNIT + *((uchar*)s2+i2);
1565: a[i2] = above = left < above
1566: ? (left_inc < diag_inc ? left_inc : diag_inc)
1567: : (above_inc < diag_inc ? above_inc : diag_inc);
1568: diag = left;
1569: }
1570: }
1571:
1572: return a[len2-1];
1573: }
1574:
1575: #define BB_SLOT_SIZE (16*1024) /* Desired size in bytes */
1576: #define BB_PER_SLOT_BITS (BB_SLOT_SIZE * 8) /* Number of bits per slot */
1577: #define BB_PER_SLOT_INTS (BB_SLOT_SIZE / 4) /* Number of int32s per slot */
1578:
1579: struct bitbag {
1580: uint32 **bits;
1581: int slot_cnt;
1582: };
1583:
1584: struct bitbag *bitbag_create(int max_ndx)
1585: {
1586: struct bitbag *bb = new(struct bitbag);
1587: bb->slot_cnt = (max_ndx + BB_PER_SLOT_BITS - 1) / BB_PER_SLOT_BITS;
1588:
1589: if (!(bb->bits = (uint32**)calloc(bb->slot_cnt, sizeof (uint32*))))
1590: out_of_memory("bitbag_create");
1591:
1592: return bb;
1593: }
1594:
1595: void bitbag_set_bit(struct bitbag *bb, int ndx)
1596: {
1597: int slot = ndx / BB_PER_SLOT_BITS;
1598: ndx %= BB_PER_SLOT_BITS;
1599:
1600: if (!bb->bits[slot]) {
1601: if (!(bb->bits[slot] = (uint32*)calloc(BB_PER_SLOT_INTS, 4)))
1602: out_of_memory("bitbag_set_bit");
1603: }
1604:
1605: bb->bits[slot][ndx/32] |= 1u << (ndx % 32);
1606: }
1607:
1608: #if 0 /* not needed yet */
1609: void bitbag_clear_bit(struct bitbag *bb, int ndx)
1610: {
1611: int slot = ndx / BB_PER_SLOT_BITS;
1612: ndx %= BB_PER_SLOT_BITS;
1613:
1614: if (!bb->bits[slot])
1615: return;
1616:
1617: bb->bits[slot][ndx/32] &= ~(1u << (ndx % 32));
1618: }
1619:
1620: int bitbag_check_bit(struct bitbag *bb, int ndx)
1621: {
1622: int slot = ndx / BB_PER_SLOT_BITS;
1623: ndx %= BB_PER_SLOT_BITS;
1624:
1625: if (!bb->bits[slot])
1626: return 0;
1627:
1628: return bb->bits[slot][ndx/32] & (1u << (ndx % 32)) ? 1 : 0;
1629: }
1630: #endif
1631:
1632: /* Call this with -1 to start checking from 0. Returns -1 at the end. */
1633: int bitbag_next_bit(struct bitbag *bb, int after)
1634: {
1635: uint32 bits, mask;
1636: int i, ndx = after + 1;
1637: int slot = ndx / BB_PER_SLOT_BITS;
1638: ndx %= BB_PER_SLOT_BITS;
1639:
1640: mask = (1u << (ndx % 32)) - 1;
1641: for (i = ndx / 32; slot < bb->slot_cnt; slot++, i = mask = 0) {
1642: if (!bb->bits[slot])
1643: continue;
1644: for ( ; i < BB_PER_SLOT_INTS; i++, mask = 0) {
1645: if (!(bits = bb->bits[slot][i] & ~mask))
1646: continue;
1647: /* The xor magic figures out the lowest enabled bit in
1648: * bits, and the switch quickly computes log2(bit). */
1649: switch (bits ^ (bits & (bits-1))) {
1650: #define LOG2(n) case 1u << n: return slot*BB_PER_SLOT_BITS + i*32 + n
1651: LOG2(0); LOG2(1); LOG2(2); LOG2(3);
1652: LOG2(4); LOG2(5); LOG2(6); LOG2(7);
1653: LOG2(8); LOG2(9); LOG2(10); LOG2(11);
1654: LOG2(12); LOG2(13); LOG2(14); LOG2(15);
1655: LOG2(16); LOG2(17); LOG2(18); LOG2(19);
1656: LOG2(20); LOG2(21); LOG2(22); LOG2(23);
1657: LOG2(24); LOG2(25); LOG2(26); LOG2(27);
1658: LOG2(28); LOG2(29); LOG2(30); LOG2(31);
1659: }
1660: return -1; /* impossible... */
1661: }
1662: }
1663:
1664: return -1;
1665: }
1666:
1667: void flist_ndx_push(flist_ndx_list *lp, int ndx)
1668: {
1669: struct flist_ndx_item *item;
1670:
1671: if (!(item = new(struct flist_ndx_item)))
1672: out_of_memory("flist_ndx_push");
1673: item->next = NULL;
1674: item->ndx = ndx;
1675: if (lp->tail)
1676: lp->tail->next = item;
1677: else
1678: lp->head = item;
1679: lp->tail = item;
1680: }
1681:
1682: int flist_ndx_pop(flist_ndx_list *lp)
1683: {
1684: struct flist_ndx_item *next;
1685: int ndx;
1686:
1687: if (!lp->head)
1688: return -1;
1689:
1690: ndx = lp->head->ndx;
1691: next = lp->head->next;
1692: free(lp->head);
1693: lp->head = next;
1694: if (!next)
1695: lp->tail = NULL;
1696:
1697: return ndx;
1698: }
1699:
1700: void *expand_item_list(item_list *lp, size_t item_size,
1701: const char *desc, int incr)
1702: {
1703: /* First time through, 0 <= 0, so list is expanded. */
1704: if (lp->malloced <= lp->count) {
1705: void *new_ptr;
1706: size_t new_size = lp->malloced;
1707: if (incr < 0)
1708: new_size += -incr; /* increase slowly */
1709: else if (new_size < (size_t)incr)
1710: new_size += incr;
1711: else
1712: new_size *= 2;
1713: if (new_size < lp->malloced)
1714: overflow_exit("expand_item_list");
1715: /* Using _realloc_array() lets us pass the size, not a type. */
1716: new_ptr = _realloc_array(lp->items, item_size, new_size);
1717: if (verbose >= 4) {
1718: rprintf(FINFO, "[%s] expand %s to %.0f bytes, did%s move\n",
1719: who_am_i(), desc, (double)new_size * item_size,
1720: new_ptr == lp->items ? " not" : "");
1721: }
1722: if (!new_ptr)
1723: out_of_memory("expand_item_list");
1724:
1725: lp->items = new_ptr;
1726: lp->malloced = new_size;
1727: }
1728: return (char*)lp->items + (lp->count++ * item_size);
1729: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>