e2fsprogs-1.41.14/0000755031104000366760000000000011504506437011637 5ustar tytsoe2fsprogs-1.41.14/util/0000755031104000366760000000000011501420246012602 5ustar tytsoe2fsprogs-1.41.14/util/subst.conf.in0000644031104000366760000000077711240667356015250 0ustar tytsoAWK @AWK@ SED @SED@ ET_DIR @ET_DIR@ SS_DIR @SS_DIR@ E2FSPROGS_MONTH @E2FSPROGS_MONTH@ E2FSPROGS_YEAR @E2FSPROGS_YEAR@ E2FSPROGS_VERSION @E2FSPROGS_VERSION@ SIZEOF_LONG_LONG @SIZEOF_LONG_LONG@ SIZEOF_LONG @SIZEOF_LONG@ SIZEOF_INT @SIZEOF_INT@ SIZEOF_SHORT @SIZEOF_SHORT@ datarootdir @datarootdir@ datadir @datadir@ root_sysconfdir @root_sysconfdir@ $root_prefix @root_prefix@ $prefix @prefix@ # Enable the documentation for the journal device mke2fs, tune2fs, and # e2fsck's man page JDEV e2fsprogs-1.41.14/util/Makefile.in0000644031104000366760000000214111240667356014664 0ustar tytso# # Standard e2fsprogs prologue.... # srcdir = @srcdir@ top_srcdir = @top_srcdir@ VPATH = @srcdir@ top_builddir = .. my_dir = util INSTALL = @INSTALL@ SRCS = $(srcdir)/subst.c @MCONFIG@ .c.o: $(E) " CC $<" $(Q) $(BUILD_CC) -c $(BUILD_CFLAGS) $< -o $@ PROGS= subst all:: $(PROGS) gen-tarball subst: subst.o $(E) " LD $@" $(Q) $(BUILD_CC) $(BUILD_LDFLAGS) -o subst subst.o copy_sparse: copy_sparse.o $(E) " LD $@" $(Q) $(BUILD_CC) $(BUILD_LDFLAGS) -o copy_sparse copy_sparse.o gen-tarball: $(srcdir)/gen-tarball.in $(top_builddir)/config.status $(E) " CONFIG.STATUS $@" $(Q) cd $(top_builddir); CONFIG_FILES=util/gen-tarball ./config.status $(Q) chmod +x gen-tarball tarballs: gen-tarball sh gen-tarball debian sh gen-tarball all sh gen-tarball subset clean: $(RM) -f $(PROGS) \#* *.s *.o *.a *~ core *.tar.gz gen-tarball \ copy-sparse mostlyclean: clean distclean: clean $(RM) -f .depend Makefile $(srcdir)/TAGS $(srcdir)/Makefile.in.old # +++ Dependency line eater +++ # # Makefile dependencies follow. This must be the last section in # the Makefile.in file # subst.o: $(srcdir)/subst.c e2fsprogs-1.41.14/util/subset.exclude0000644031104000366760000000047711240667356015511 0ustar tytso.git .hg .hgignore .pc patches build build.static rpm.log TODO powerquest .exclude-subset po/stamp-cat-id po/cat-id-tbl.c Meta e2fsck ext2ed debugfs misc tests resize contrib po include debian lib/e2p lib/evms lib/ext2fs ABOUT-NLS README INSTALL INSTALL.dllbin INSTALL.elfbin RELEASE-NOTES e2fsprogs.lsm e2fsprogs.spec e2fsprogs-1.41.14/util/all.exclude0000644031104000366760000000021311240667356014740 0ustar tytso.git .hg .hgignore .pc patches README.subset build build.static rpm.log TODO powerquest .exclude-file po/stamp-cat-id po/cat-id-tbl.c Meta e2fsprogs-1.41.14/util/copy_sparse.c0000644031104000366760000001043411240667356015316 0ustar tytso/* * copy_sparse.c -- copy a very large sparse files efficiently * (requires root privileges) * * Copyright 2003, 2004 by Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% */ #ifndef __linux__ #include #include int main(void) { fputs("This program is only supported on Linux!\n", stderr); exit(EXIT_FAILURE); } #else #define _LARGEFILE64_SOURCE #include #include #include #include #include #include #include #ifdef HAVE_GETOPT_H #include #else extern char *optarg; extern int optind; #endif #include #include #include #include #include int verbose = 0; #define FIBMAP _IO(0x00,1) /* bmap access */ #define FIGETBSZ _IO(0x00,2) /* get the block size used for bmap */ static unsigned long get_bmap(int fd, unsigned long block) { int ret; unsigned long b; b = block; ret = ioctl(fd, FIBMAP, &b); if (ret < 0) { if (errno == EPERM) { fprintf(stderr, "No permission to use FIBMAP ioctl; must have root privileges\n"); exit(1); } perror("FIBMAP"); } return b; } static int full_read(int fd, char *buf, size_t count) { int got, total = 0; int pass = 0; while (count > 0) { got = read(fd, buf, count); if (got == -1) { if ((errno == EINTR) || (errno == EAGAIN)) continue; return total ? total : -1; } if (got == 0) { if (pass++ >= 3) return total; continue; } pass = 0; buf += got; total += got; count -= got; } return total; } static void copy_sparse_file(const char *src, const char *dest) { struct stat64 fileinfo; long lb, i, fd, ofd, bs, block, numblocks; ssize_t got, got2; off64_t offset = 0, should_be; char *buf; if (verbose) printf("Copying sparse file from %s to %s\n", src, dest); if (strcmp(src, "-")) { if (stat64(src, &fileinfo) < 0) { perror("stat"); exit(1); } if (!S_ISREG(fileinfo.st_mode)) { printf("%s: Not a regular file\n", src); exit(1); } fd = open(src, O_RDONLY | O_LARGEFILE); if (fd < 0) { perror("open"); exit(1); } if (ioctl(fd, FIGETBSZ, &bs) < 0) { perror("FIGETBSZ"); close(fd); exit(1); } if (bs < 0) { printf("%s: Invalid block size: %ld\n", src, bs); exit(1); } if (verbose) printf("Blocksize of file %s is %ld\n", src, bs); numblocks = (fileinfo.st_size + (bs-1)) / bs; if (verbose) printf("File size of %s is %lld (%ld blocks)\n", src, (long long) fileinfo.st_size, numblocks); } else { fd = 0; bs = 1024; } ofd = open(dest, O_WRONLY|O_CREAT|O_TRUNC|O_LARGEFILE, 0777); if (ofd < 0) { perror(dest); exit(1); } buf = malloc(bs); if (!buf) { fprintf(stderr, "Couldn't allocate buffer"); exit(1); } for (lb = 0; !fd || lb < numblocks; lb++) { if (fd) { block = get_bmap(fd, lb); if (!block) continue; should_be = ((off64_t) lb) * bs; if (offset != should_be) { if (verbose) printf("Seeking to %lld\n", should_be); if (lseek64(fd, should_be, SEEK_SET) == (off_t) -1) { perror("lseek src"); exit(1); } if (lseek64(ofd, should_be, SEEK_SET) == (off_t) -1) { perror("lseek dest"); exit(1); } offset = should_be; } } got = full_read(fd, buf, bs); if (fd == 0 && got == 0) break; if (got == bs) { for (i=0; i < bs; i++) if (buf[i]) break; if (i == bs) { lseek(ofd, bs, SEEK_CUR); offset += bs; continue; } } got2 = write(ofd, buf, got); if (got != got2) { printf("short write\n"); exit(1); } offset += got; } offset = fileinfo.st_size; if (fstat64(ofd, &fileinfo) < 0) { perror("fstat"); exit(1); } if (fileinfo.st_size != offset) { lseek64(ofd, offset-1, SEEK_CUR); buf[0] = 0; write(ofd, buf, 1); } close(fd); close(ofd); } static void usage(const char *progname) { fprintf(stderr, "Usage: %s [-v] source_file destination_file\n", progname); exit(1); } int main(int argc, char**argv) { int c; while ((c = getopt(argc, argv, "v")) != EOF) switch (c) { case 'v': verbose++; break; default: usage(argv[0]); break; } if (optind+2 != argc) usage(argv[0]); copy_sparse_file(argv[optind], argv[optind+1]); return 0; } #endif e2fsprogs-1.41.14/util/subst.c0000644031104000366760000001716011240667356014132 0ustar tytso/* * subst.c --- substitution program * * Subst is used as a quicky program to do @ substitutions * */ #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_GETOPT_H #include #else extern char *optarg; extern int optind; #endif struct subst_entry { char *name; char *value; struct subst_entry *next; }; struct subst_entry *subst_table = 0; static int add_subst(char *name, char *value) { struct subst_entry *ent = 0; int retval; retval = ENOMEM; ent = (struct subst_entry *) malloc(sizeof(struct subst_entry)); if (!ent) goto fail; ent->name = (char *) malloc(strlen(name)+1); if (!ent->name) goto fail; ent->value = (char *) malloc(strlen(value)+1); if (!ent->value) goto fail; strcpy(ent->name, name); strcpy(ent->value, value); ent->next = subst_table; subst_table = ent; return 0; fail: if (ent) { free(ent->name); free(ent->value); free(ent); } return retval; } static struct subst_entry *fetch_subst_entry(char *name) { struct subst_entry *ent; for (ent = subst_table; ent; ent = ent->next) { if (strcmp(name, ent->name) == 0) break; } return ent; } /* * Given the starting and ending position of the replacement name, * check to see if it is valid, and pull it out if it is. */ static char *get_subst_symbol(const char *begin, size_t len, char prefix) { static char replace_name[128]; char *cp, *start; start = replace_name; if (prefix) *start++ = prefix; if (len > sizeof(replace_name)-2) return NULL; memcpy(start, begin, len); start[len] = 0; /* * The substitution variable must all be in the of [0-9A-Za-z_]. * If it isn't, this must be an invalid symbol name. */ for (cp = start; *cp; cp++) { if (!(*cp >= 'a' && *cp <= 'z') && !(*cp >= 'A' && *cp <= 'Z') && !(*cp >= '0' && *cp <= '9') && !(*cp == '_')) return NULL; } return (replace_name); } static void replace_string(char *begin, char *end, char *newstr) { int replace_len, len; replace_len = strlen(newstr); len = end - begin; if (replace_len == 0) memmove(begin, end+1, strlen(end)+1); else if (replace_len != len+1) memmove(end+(replace_len-len-1), end, strlen(end)+1); memcpy(begin, newstr, replace_len); } static void substitute_line(char *line) { char *ptr, *name_ptr, *end_ptr; struct subst_entry *ent; char *replace_name; size_t len; /* * Expand all @FOO@ substitutions */ ptr = line; while (ptr) { name_ptr = strchr(ptr, '@'); if (!name_ptr) break; /* No more */ if (*(++name_ptr) == '@') { /* * Handle tytso@@mit.edu --> tytso@mit.edu */ memmove(name_ptr-1, name_ptr, strlen(name_ptr)+1); ptr = name_ptr+1; continue; } end_ptr = strchr(name_ptr, '@'); if (!end_ptr) break; len = end_ptr - name_ptr; replace_name = get_subst_symbol(name_ptr, len, 0); if (!replace_name) { ptr = name_ptr; continue; } ent = fetch_subst_entry(replace_name); if (!ent) { fprintf(stderr, "Unfound expansion: '%s'\n", replace_name); ptr = end_ptr + 1; continue; } #if 0 fprintf(stderr, "Replace name = '%s' with '%s'\n", replace_name, ent->value); #endif ptr = name_ptr-1; replace_string(ptr, end_ptr, ent->value); if ((ent->value[0] == '@') && (strlen(replace_name) == strlen(ent->value)-2) && !strncmp(replace_name, ent->value+1, strlen(ent->value)-2)) /* avoid an infinite loop */ ptr += strlen(ent->value); } /* * Now do a second pass to expand ${FOO} */ ptr = line; while (ptr) { name_ptr = strchr(ptr, '$'); if (!name_ptr) break; /* No more */ if (*(++name_ptr) != '{') { ptr = name_ptr; continue; } name_ptr++; end_ptr = strchr(name_ptr, '}'); if (!end_ptr) break; len = end_ptr - name_ptr; replace_name = get_subst_symbol(name_ptr, len, '$'); if (!replace_name) { ptr = name_ptr; continue; } ent = fetch_subst_entry(replace_name); if (!ent) { ptr = end_ptr + 1; continue; } #if 0 fprintf(stderr, "Replace name = '%s' with '%s'\n", replace_name, ent->value); #endif ptr = name_ptr-2; replace_string(ptr, end_ptr, ent->value); } } static void parse_config_file(FILE *f) { char line[2048]; char *cp, *ptr; while (!feof(f)) { memset(line, 0, sizeof(line)); if (fgets(line, sizeof(line), f) == NULL) break; /* * Strip newlines and comments. */ cp = strchr(line, '\n'); if (cp) *cp = 0; cp = strchr(line, '#'); if (cp) *cp = 0; /* * Skip trailing and leading whitespace */ for (cp = line + strlen(line) - 1; cp >= line; cp--) { if (*cp == ' ' || *cp == '\t') *cp = 0; else break; } cp = line; while (*cp && isspace(*cp)) cp++; ptr = cp; /* * Skip empty lines */ if (*ptr == 0) continue; /* * Ignore future extensions */ if (*ptr == '@') continue; /* * Parse substitutions */ for (cp = ptr; *cp; cp++) if (isspace(*cp)) break; *cp = 0; for (cp++; *cp; cp++) if (!isspace(*cp)) break; #if 0 printf("Substitute: '%s' for '%s'\n", ptr, cp ? cp : ""); #endif add_subst(ptr, cp); } } /* * Return 0 if the files are different, 1 if the files are the same. */ static int compare_file(const char *outfn, const char *newfn) { FILE *old_f, *new_f; char oldbuf[2048], newbuf[2048], *oldcp, *newcp; int retval; old_f = fopen(outfn, "r"); if (!old_f) return 0; new_f = fopen(newfn, "r"); if (!new_f) { fclose(old_f); return 0; } while (1) { oldcp = fgets(oldbuf, sizeof(oldbuf), old_f); newcp = fgets(newbuf, sizeof(newbuf), new_f); if (!oldcp && !newcp) { retval = 1; break; } if (!oldcp || !newcp || strcmp(oldbuf, newbuf)) { retval = 0; break; } } fclose(old_f); fclose(new_f); return retval; } int main(int argc, char **argv) { char line[2048]; int c; FILE *in, *out; char *outfn = NULL, *newfn = NULL; int verbose = 0; int adjust_timestamp = 0; struct stat stbuf; struct utimbuf ut; while ((c = getopt (argc, argv, "f:tv")) != EOF) { switch (c) { case 'f': in = fopen(optarg, "r"); if (!in) { perror(optarg); exit(1); } parse_config_file(in); fclose(in); break; case 't': adjust_timestamp++; break; case 'v': verbose++; break; default: fprintf(stderr, "%s: [-f config-file] [file]\n", argv[0]); break; } } if (optind < argc) { in = fopen(argv[optind], "r"); if (!in) { perror(argv[optind]); exit(1); } optind++; } else in = stdin; if (optind < argc) { outfn = argv[optind]; newfn = (char *) malloc(strlen(outfn)+20); if (!newfn) { fprintf(stderr, "Memory error! Exiting.\n"); exit(1); } strcpy(newfn, outfn); strcat(newfn, ".new"); out = fopen(newfn, "w"); if (!out) { perror(newfn); exit(1); } } else { out = stdout; outfn = 0; } while (!feof(in)) { if (fgets(line, sizeof(line), in) == NULL) break; substitute_line(line); fputs(line, out); } fclose(in); fclose(out); if (outfn) { struct stat st; if (compare_file(outfn, newfn)) { if (verbose) printf("No change, keeping %s.\n", outfn); if (adjust_timestamp) { if (stat(outfn, &stbuf) == 0) { if (verbose) printf("Updating modtime for %s\n", outfn); ut.actime = stbuf.st_atime; ut.modtime = time(0); if (utime(outfn, &ut) < 0) perror("utime"); } } unlink(newfn); } else { if (verbose) printf("Creating or replacing %s.\n", outfn); rename(newfn, outfn); } /* set read-only to alert user it is a generated file */ if (stat(outfn, &st) == 0) chmod(outfn, st.st_mode & ~0222); } return (0); } e2fsprogs-1.41.14/util/gen-tarball.in0000644031104000366760000000317611240667356015350 0ustar tytso#!/bin/sh # # This script is used to generate the distribution tarball # srcdir=@srcdir@ top_srcdir=@top_srcdir@ top_dir=`cd $top_srcdir; pwd` base_ver=`echo @E2FSPROGS_VERSION@ | sed -e 's/-WIP//' -e 's/pre-//' -e 's/-PLUS//'` base_e2fsprogs=`basename $top_dir` exclude=/tmp/exclude GZIP=gzip # # This hack is needed because texi2dvi blows up horribly if there are # any '~' chracters in the directory pathname. So we kludge around it by # using a non-standard directory name for WIP releases. dpkg-source # complains, but life goes on. # deb_pkgver=`echo @E2FSPROGS_PKGVER@ | sed -e 's/~/-/g'` case $1 in debian|ubuntu) SRCROOT="e2fsprogs-$deb_pkgver" rename_tarball="e2fsprogs_@E2FSPROGS_PKGVER@.orig.tar.gz" list=all ;; subset) SRCROOT="e2fsprogs-libs-$base_ver" list=subset ;; all|*) SRCROOT="e2fsprogs-$base_ver" list=all ;; esac mv ../e2fsprogs.spec $top_srcdir/e2fsprogs.spec (cd $top_srcdir/.. ; find $base_e2fsprogs \( -name \*~ -o -name \*.orig \ -o -name CVS -o -name \*.rej -o -name Makefile.pq \ -o -name TAGS -o -name \*.old -o -name SCCS \ -o -name changed-files -o -name .#\* -o -name \*.tar.gz \ -o -name autom4te.cache \) \ -print) | sed -e "s/^$base_e2fsprogs/$SRCROOT/" > $exclude sed -e "s;^;$SRCROOT/;" < $srcdir/$list.exclude >> $exclude (cd $top_srcdir/.. ; rm -f $SRCROOT ; ln -sf $base_e2fsprogs $SRCROOT) (cd $top_srcdir/.. ; tar -c -h -v -f - -X $exclude $SRCROOT) \ | $GZIP -9 -c > $SRCROOT.tar.gz $GZIP -l $SRCROOT.tar.gz (cd $top_srcdir/.. ; rm -f $SRCROOT) mv $top_srcdir/e2fsprogs.spec ../e2fsprogs.spec if test -n "$rename_tarball"; then mv $SRCROOT.tar.gz $rename_tarball fi e2fsprogs-1.41.14/util/gcc-wall-cleanup0000644031104000366760000000116111240667356015661 0ustar tytso#!/bin/sed -f # # This script filters out gcc-wall crud that we're not interested in seeing. # /^cc /d /^kcc /d /^gcc /d /does not support `long long'/d /forbids long long integer constants/d /does not support the `ll' length modifier/d /does not support the `ll' printf length modifier/d /ANSI C forbids long long integer constants/d /traditional C rejects string concatenation/d /integer constant is unsigned in ANSI C, signed with -traditional/d /At top level:/d /In file included from/d /In function `.*':/d /zero-length format string/d /warning: missing initializer/d /warning: (near initialization for/d /^[ ]*from/d e2fsprogs-1.41.14/util/libecho.c0000644031104000366760000000241311240667356014372 0ustar tytso/* * libecho.c * * For each argument on the command line, echo it. Should expand * DOS wildcards correctly. * * Syntax: libecho [-p prefix] list... */ #include #include #include void echo_files(char *, char *); int main(int argc, char *argv[]) { int i; char *prefix; prefix = ""; if (argc < 2) { fprintf(stderr, "Usage: libecho [-p prefix] list...\n"); return 1; } for (i = 1 ; i < argc ; i++) if (!stricmp(argv[i], "-p")) prefix = argv[++i]; else echo_files(prefix, argv[i]); return 0; } void echo_files(char *prefix, char *f) { long ff; struct _finddata_t fdt; char *slash; char filepath[256]; /* * We're unix based quite a bit here. Look for normal slashes and * make them reverse slashes. */ while((slash = strrchr(f, '/')) != NULL) *slash = '\\'; strcpy(filepath, f); slash = strrchr(filepath, '\\'); if (slash) { slash++; *slash = 0; } else { filepath[0] = '\0'; } ff = _findfirst(f, &fdt); if (ff < 0) { printf("%s%s\n", prefix, f); return; } printf("%s%s%s\n", prefix, filepath, fdt.name); for (;;) { if (_findnext(ff, &fdt) < 0) break; printf("%s%s%s\n", prefix, filepath, fdt.name); } _findclose(ff); } e2fsprogs-1.41.14/.gitignore0000644031104000366760000000432211270650332013622 0ustar tytsoautom4te.cache build build.static FILES ^core *~ .pc patches Makefile *.o MCONFIG asm_types.h config.log config.status debugfs/debug_cmds.c debugfs/debugfs debugfs/debugfs.8 doc/libext2fs.aux doc/libext2fs.cp doc/libext2fs.dvi doc/libext2fs.fn doc/libext2fs.fns doc/libext2fs.info doc/libext2fs.ky doc/libext2fs.log doc/libext2fs.pg doc/libext2fs.toc doc/libext2fs.tp doc/libext2fs.vr e2fsck/crc32table.h e2fsck/e2fsck e2fsck/e2fsck.8 e2fsck/e2fsck.conf.5 e2fsck/e2fsck.shared e2fsck/e2fsck.static e2fsck/gen_crc32table e2fsck/prof_err.c e2fsck/prof_err.h e2fsprogs.spec lib/blkid/blkid.pc lib/blkid/blkid_types.h lib/blkid/libblkid.3 lib/blkid/libblkid.a lib/blkid/subdirs lib/e2p/e2p.pc lib/e2p/libe2p.a lib/e2p/subdirs lib/et/com_err.pc lib/et/compile_et lib/et/libcom_err.a lib/et/subdirs lib/ext2fs/ext2_err.c lib/ext2fs/ext2_err.et lib/ext2fs/ext2_err.h lib/ext2fs/ext2_types.h lib/ext2fs/ext2fs.pc lib/ext2fs/libext2fs.a lib/ext2fs/subdirs lib/libblkid.a lib/libcom_err.a lib/libe2p.a lib/libext2fs.a lib/libss.a lib/libuuid.a lib/ss/libss.a lib/ss/mk_cmds lib/ss/ss.pc lib/ss/ss_err.c lib/ss/ss_err.h lib/ss/std_rqs.c lib/ss/subdirs lib/uuid/libuuid.a lib/uuid/subdirs lib/uuid/tst_uuid lib/uuid/uuid.3 lib/uuid/uuid.pc lib/uuid/uuid_clear.3 lib/uuid/uuid_compare.3 lib/uuid/uuid_copy.3 lib/uuid/uuid_generate.3 lib/uuid/uuid_is_null.3 lib/uuid/uuid_parse.3 lib/uuid/uuid_time lib/uuid/uuid_time.3 lib/uuid/uuid_types.h lib/uuid/uuid_unparse.3 misc/badblocks misc/badblocks.8 misc/blkid misc/blkid.8 misc/chattr misc/chattr.1 misc/default_profile.c misc/dumpe2fs misc/dumpe2fs.8 misc/e2freefrag misc/e2freefrag.8 misc/e2image misc/e2image.8 misc/e2initrd_helper misc/e2label.8 misc/e2undo misc/e2undo.8 misc/e4defrag misc/e4defrag.8 misc/filefrag misc/filefrag.8 misc/findfs.8 misc/fsck misc/fsck.8 misc/logsave misc/logsave.8 misc/lsattr misc/lsattr.1 misc/mke2fs misc/mke2fs.8 misc/mke2fs.conf.5 misc/mklost+found misc/mklost+found.8 misc/prof_err.c misc/prof_err.h misc/tune2fs misc/tune2fs.8 misc/uuidd misc/uuidd.8 misc/uuidgen misc/uuidgen.1 po/Makefile.in po/POTFILES public_config.h resize/resize2fs resize/resize2fs.8 resize/test_extent tests/progs/test_icount tests/progs/test_icount_cmds.c util/gen-tarball util/subst util/subst.conf Meta e2fsprogs-1.41.14/aclocal.m40000644031104000366760000032121011240667355013502 0ustar tytso# codeset.m4 serial AM1 (gettext-0.10.40) dnl Copyright (C) 2000-2002 Free Software Foundation, Inc. dnl This file is free software, distributed under the terms of the GNU dnl General Public License. As a special exception to the GNU General dnl Public License, this file may be distributed as part of a program dnl that contains a configuration script generated by Autoconf, under dnl the same distribution terms as the rest of that program. dnl From Bruno Haible. AC_DEFUN([AM_LANGINFO_CODESET], [ AC_CACHE_CHECK([for nl_langinfo and CODESET], am_cv_langinfo_codeset, [AC_TRY_LINK([#include ], [char* cs = nl_langinfo(CODESET);], am_cv_langinfo_codeset=yes, am_cv_langinfo_codeset=no) ]) if test $am_cv_langinfo_codeset = yes; then AC_DEFINE(HAVE_LANGINFO_CODESET, 1, [Define if you have and nl_langinfo(CODESET).]) fi ]) # gettext.m4 serial 28 (gettext-0.13) dnl Copyright (C) 1995-2003 Free Software Foundation, Inc. dnl This file is free software, distributed under the terms of the GNU dnl General Public License. As a special exception to the GNU General dnl Public License, this file may be distributed as part of a program dnl that contains a configuration script generated by Autoconf, under dnl the same distribution terms as the rest of that program. dnl dnl This file can can be used in projects which are not available under dnl the GNU General Public License or the GNU Library General Public dnl License but which still want to provide support for the GNU gettext dnl functionality. dnl Please note that the actual code of the GNU gettext library is covered dnl by the GNU Library General Public License, and the rest of the GNU dnl gettext package package is covered by the GNU General Public License. dnl They are *not* in the public domain. dnl Authors: dnl Ulrich Drepper , 1995-2000. dnl Bruno Haible , 2000-2003. dnl Macro to add for using GNU gettext. dnl Usage: AM_GNU_GETTEXT([INTLSYMBOL], [NEEDSYMBOL], [INTLDIR]). dnl INTLSYMBOL can be one of 'external', 'no-libtool', 'use-libtool'. The dnl default (if it is not specified or empty) is 'no-libtool'. dnl INTLSYMBOL should be 'external' for packages with no intl directory, dnl and 'no-libtool' or 'use-libtool' for packages with an intl directory. dnl If INTLSYMBOL is 'use-libtool', then a libtool library dnl $(top_builddir)/intl/libintl.la will be created (shared and/or static, dnl depending on --{enable,disable}-{shared,static} and on the presence of dnl AM-DISABLE-SHARED). If INTLSYMBOL is 'no-libtool', a static library dnl $(top_builddir)/intl/libintl.a will be created. dnl If NEEDSYMBOL is specified and is 'need-ngettext', then GNU gettext dnl implementations (in libc or libintl) without the ngettext() function dnl will be ignored. If NEEDSYMBOL is specified and is dnl 'need-formatstring-macros', then GNU gettext implementations that don't dnl support the ISO C 99 formatstring macros will be ignored. dnl INTLDIR is used to find the intl libraries. If empty, dnl the value `$(top_builddir)/intl/' is used. dnl dnl The result of the configuration is one of three cases: dnl 1) GNU gettext, as included in the intl subdirectory, will be compiled dnl and used. dnl Catalog format: GNU --> install in $(datadir) dnl Catalog extension: .mo after installation, .gmo in source tree dnl 2) GNU gettext has been found in the system's C library. dnl Catalog format: GNU --> install in $(datadir) dnl Catalog extension: .mo after installation, .gmo in source tree dnl 3) No internationalization, always use English msgid. dnl Catalog format: none dnl Catalog extension: none dnl If INTLSYMBOL is 'external', only cases 2 and 3 can occur. dnl The use of .gmo is historical (it was needed to avoid overwriting the dnl GNU format catalogs when building on a platform with an X/Open gettext), dnl but we keep it in order not to force irrelevant filename changes on the dnl maintainers. dnl AC_DEFUN([AM_GNU_GETTEXT], [ dnl Argument checking. ifelse([$1], [], , [ifelse([$1], [external], , [ifelse([$1], [no-libtool], , [ifelse([$1], [use-libtool], , [errprint([ERROR: invalid first argument to AM_GNU_GETTEXT ])])])])]) ifelse([$2], [], , [ifelse([$2], [need-ngettext], , [ifelse([$2], [need-formatstring-macros], , [errprint([ERROR: invalid second argument to AM_GNU_GETTEXT ])])])]) define(gt_included_intl, ifelse([$1], [external], [no], [yes])) define(gt_libtool_suffix_prefix, ifelse([$1], [use-libtool], [l], [])) AC_REQUIRE([AM_PO_SUBDIRS])dnl ifelse(gt_included_intl, yes, [ AC_REQUIRE([AM_INTL_SUBDIR])dnl ]) dnl Prerequisites of AC_LIB_LINKFLAGS_BODY. AC_REQUIRE([AC_LIB_PREPARE_PREFIX]) AC_REQUIRE([AC_LIB_RPATH]) dnl Sometimes libintl requires libiconv, so first search for libiconv. dnl Ideally we would do this search only after the dnl if test "$USE_NLS" = "yes"; then dnl if test "$gt_cv_func_gnugettext_libc" != "yes"; then dnl tests. But if configure.in invokes AM_ICONV after AM_GNU_GETTEXT dnl the configure script would need to contain the same shell code dnl again, outside any 'if'. There are two solutions: dnl - Invoke AM_ICONV_LINKFLAGS_BODY here, outside any 'if'. dnl - Control the expansions in more detail using AC_PROVIDE_IFELSE. dnl Since AC_PROVIDE_IFELSE is only in autoconf >= 2.52 and not dnl documented, we avoid it. ifelse(gt_included_intl, yes, , [ AC_REQUIRE([AM_ICONV_LINKFLAGS_BODY]) ]) dnl Set USE_NLS. AM_NLS ifelse(gt_included_intl, yes, [ BUILD_INCLUDED_LIBINTL=no USE_INCLUDED_LIBINTL=no ]) LIBINTL= LTLIBINTL= POSUB= dnl If we use NLS figure out what method if test "$USE_NLS" = "yes"; then gt_use_preinstalled_gnugettext=no ifelse(gt_included_intl, yes, [ AC_MSG_CHECKING([whether included gettext is requested]) AC_ARG_WITH(included-gettext, [ --with-included-gettext use the GNU gettext library included here], nls_cv_force_use_gnu_gettext=$withval, nls_cv_force_use_gnu_gettext=no) AC_MSG_RESULT($nls_cv_force_use_gnu_gettext) nls_cv_use_gnu_gettext="$nls_cv_force_use_gnu_gettext" if test "$nls_cv_force_use_gnu_gettext" != "yes"; then ]) dnl User does not insist on using GNU NLS library. Figure out what dnl to use. If GNU gettext is available we use this. Else we have dnl to fall back to GNU NLS library. dnl Add a version number to the cache macros. define([gt_api_version], ifelse([$2], [need-formatstring-macros], 3, ifelse([$2], [need-ngettext], 2, 1))) define([gt_cv_func_gnugettext_libc], [gt_cv_func_gnugettext]gt_api_version[_libc]) define([gt_cv_func_gnugettext_libintl], [gt_cv_func_gnugettext]gt_api_version[_libintl]) AC_CACHE_CHECK([for GNU gettext in libc], gt_cv_func_gnugettext_libc, [AC_TRY_LINK([#include ]ifelse([$2], [need-formatstring-macros], [#ifndef __GNU_GETTEXT_SUPPORTED_REVISION #define __GNU_GETTEXT_SUPPORTED_REVISION(major) ((major) == 0 ? 0 : -1) #endif changequote(,)dnl typedef int array [2 * (__GNU_GETTEXT_SUPPORTED_REVISION(0) >= 1) - 1]; changequote([,])dnl ], [])[extern int _nl_msg_cat_cntr; extern int *_nl_domain_bindings;], [bindtextdomain ("", ""); return (int) gettext ("")]ifelse([$2], [need-ngettext], [ + (int) ngettext ("", "", 0)], [])[ + _nl_msg_cat_cntr + *_nl_domain_bindings], gt_cv_func_gnugettext_libc=yes, gt_cv_func_gnugettext_libc=no)]) if test "$gt_cv_func_gnugettext_libc" != "yes"; then dnl Sometimes libintl requires libiconv, so first search for libiconv. ifelse(gt_included_intl, yes, , [ AM_ICONV_LINK ]) dnl Search for libintl and define LIBINTL, LTLIBINTL and INCINTL dnl accordingly. Don't use AC_LIB_LINKFLAGS_BODY([intl],[iconv]) dnl because that would add "-liconv" to LIBINTL and LTLIBINTL dnl even if libiconv doesn't exist. AC_LIB_LINKFLAGS_BODY([intl]) AC_CACHE_CHECK([for GNU gettext in libintl], gt_cv_func_gnugettext_libintl, [gt_save_CPPFLAGS="$CPPFLAGS" CPPFLAGS="$CPPFLAGS $INCINTL" gt_save_LIBS="$LIBS" LIBS="$LIBS $LIBINTL" dnl Now see whether libintl exists and does not depend on libiconv. AC_TRY_LINK([#include ]ifelse([$2], [need-formatstring-macros], [#ifndef __GNU_GETTEXT_SUPPORTED_REVISION #define __GNU_GETTEXT_SUPPORTED_REVISION(major) ((major) == 0 ? 0 : -1) #endif changequote(,)dnl typedef int array [2 * (__GNU_GETTEXT_SUPPORTED_REVISION(0) >= 1) - 1]; changequote([,])dnl ], [])[extern int _nl_msg_cat_cntr; extern #ifdef __cplusplus "C" #endif const char *_nl_expand_alias ();], [bindtextdomain ("", ""); return (int) gettext ("")]ifelse([$2], [need-ngettext], [ + (int) ngettext ("", "", 0)], [])[ + _nl_msg_cat_cntr + *_nl_expand_alias (0)], gt_cv_func_gnugettext_libintl=yes, gt_cv_func_gnugettext_libintl=no) dnl Now see whether libintl exists and depends on libiconv. if test "$gt_cv_func_gnugettext_libintl" != yes && test -n "$LIBICONV"; then LIBS="$LIBS $LIBICONV" AC_TRY_LINK([#include ]ifelse([$2], [need-formatstring-macros], [#ifndef __GNU_GETTEXT_SUPPORTED_REVISION #define __GNU_GETTEXT_SUPPORTED_REVISION(major) ((major) == 0 ? 0 : -1) #endif changequote(,)dnl typedef int array [2 * (__GNU_GETTEXT_SUPPORTED_REVISION(0) >= 1) - 1]; changequote([,])dnl ], [])[extern int _nl_msg_cat_cntr; extern #ifdef __cplusplus "C" #endif const char *_nl_expand_alias ();], [bindtextdomain ("", ""); return (int) gettext ("")]ifelse([$2], [need-ngettext], [ + (int) ngettext ("", "", 0)], [])[ + _nl_msg_cat_cntr + *_nl_expand_alias (0)], [LIBINTL="$LIBINTL $LIBICONV" LTLIBINTL="$LTLIBINTL $LTLIBICONV" gt_cv_func_gnugettext_libintl=yes ]) fi CPPFLAGS="$gt_save_CPPFLAGS" LIBS="$gt_save_LIBS"]) fi dnl If an already present or preinstalled GNU gettext() is found, dnl use it. But if this macro is used in GNU gettext, and GNU dnl gettext is already preinstalled in libintl, we update this dnl libintl. (Cf. the install rule in intl/Makefile.in.) if test "$gt_cv_func_gnugettext_libc" = "yes" \ || { test "$gt_cv_func_gnugettext_libintl" = "yes" \ && test "$PACKAGE" != gettext-runtime \ && test "$PACKAGE" != gettext-tools; }; then gt_use_preinstalled_gnugettext=yes else dnl Reset the values set by searching for libintl. LIBINTL= LTLIBINTL= INCINTL= fi ifelse(gt_included_intl, yes, [ if test "$gt_use_preinstalled_gnugettext" != "yes"; then dnl GNU gettext is not found in the C library. dnl Fall back on included GNU gettext library. nls_cv_use_gnu_gettext=yes fi fi if test "$nls_cv_use_gnu_gettext" = "yes"; then dnl Mark actions used to generate GNU NLS library. BUILD_INCLUDED_LIBINTL=yes USE_INCLUDED_LIBINTL=yes LIBINTL="ifelse([$3],[],\${top_builddir}/intl,[$3])/libintl.[]gt_libtool_suffix_prefix[]a $LIBICONV" LTLIBINTL="ifelse([$3],[],\${top_builddir}/intl,[$3])/libintl.[]gt_libtool_suffix_prefix[]a $LTLIBICONV" LIBS=`echo " $LIBS " | sed -e 's/ -lintl / /' -e 's/^ //' -e 's/ $//'` fi if test "$gt_use_preinstalled_gnugettext" = "yes" \ || test "$nls_cv_use_gnu_gettext" = "yes"; then dnl Mark actions to use GNU gettext tools. CATOBJEXT=.gmo fi ]) if test "$gt_use_preinstalled_gnugettext" = "yes" \ || test "$nls_cv_use_gnu_gettext" = "yes"; then AC_DEFINE(ENABLE_NLS, 1, [Define to 1 if translation of program messages to the user's native language is requested.]) else USE_NLS=no fi fi AC_MSG_CHECKING([whether to use NLS]) AC_MSG_RESULT([$USE_NLS]) if test "$USE_NLS" = "yes"; then AC_MSG_CHECKING([where the gettext function comes from]) if test "$gt_use_preinstalled_gnugettext" = "yes"; then if test "$gt_cv_func_gnugettext_libintl" = "yes"; then gt_source="external libintl" else gt_source="libc" fi else gt_source="included intl directory" fi AC_MSG_RESULT([$gt_source]) fi if test "$USE_NLS" = "yes"; then if test "$gt_use_preinstalled_gnugettext" = "yes"; then if test "$gt_cv_func_gnugettext_libintl" = "yes"; then AC_MSG_CHECKING([how to link with libintl]) AC_MSG_RESULT([$LIBINTL]) AC_LIB_APPENDTOVAR([CPPFLAGS], [$INCINTL]) fi dnl For backward compatibility. Some packages may be using this. AC_DEFINE(HAVE_GETTEXT, 1, [Define if the GNU gettext() function is already present or preinstalled.]) AC_DEFINE(HAVE_DCGETTEXT, 1, [Define if the GNU dcgettext() function is already present or preinstalled.]) fi dnl We need to process the po/ directory. POSUB=po fi ifelse(gt_included_intl, yes, [ dnl If this is used in GNU gettext we have to set BUILD_INCLUDED_LIBINTL dnl to 'yes' because some of the testsuite requires it. if test "$PACKAGE" = gettext-runtime || test "$PACKAGE" = gettext-tools; then BUILD_INCLUDED_LIBINTL=yes fi dnl Make all variables we use known to autoconf. AC_SUBST(BUILD_INCLUDED_LIBINTL) AC_SUBST(USE_INCLUDED_LIBINTL) AC_SUBST(CATOBJEXT) dnl For backward compatibility. Some configure.ins may be using this. nls_cv_header_intl= nls_cv_header_libgt= dnl For backward compatibility. Some Makefiles may be using this. DATADIRNAME=share AC_SUBST(DATADIRNAME) dnl For backward compatibility. Some Makefiles may be using this. INSTOBJEXT=.mo AC_SUBST(INSTOBJEXT) dnl For backward compatibility. Some Makefiles may be using this. GENCAT=gencat AC_SUBST(GENCAT) dnl For backward compatibility. Some Makefiles may be using this. if test "$USE_INCLUDED_LIBINTL" = yes; then INTLOBJS="\$(GETTOBJS)" fi AC_SUBST(INTLOBJS) dnl Enable libtool support if the surrounding package wishes it. INTL_LIBTOOL_SUFFIX_PREFIX=gt_libtool_suffix_prefix AC_SUBST(INTL_LIBTOOL_SUFFIX_PREFIX) ]) dnl For backward compatibility. Some Makefiles may be using this. INTLLIBS="$LIBINTL" AC_SUBST(INTLLIBS) dnl Make all documented variables known to autoconf. AC_SUBST(LIBINTL) AC_SUBST(LTLIBINTL) AC_SUBST(POSUB) ]) dnl Checks for all prerequisites of the intl subdirectory, dnl except for INTL_LIBTOOL_SUFFIX_PREFIX (and possibly LIBTOOL), INTLOBJS, dnl USE_INCLUDED_LIBINTL, BUILD_INCLUDED_LIBINTL. AC_DEFUN([AM_INTL_SUBDIR], [ AC_REQUIRE([AC_PROG_INSTALL])dnl AC_REQUIRE([AM_MKINSTALLDIRS])dnl AC_REQUIRE([AC_PROG_CC])dnl AC_REQUIRE([AC_CANONICAL_HOST])dnl AC_REQUIRE([AC_PROG_RANLIB])dnl AC_REQUIRE([AC_ISC_POSIX])dnl AC_REQUIRE([AC_HEADER_STDC])dnl AC_REQUIRE([AC_C_CONST])dnl AC_REQUIRE([bh_C_SIGNED])dnl AC_REQUIRE([AC_C_INLINE])dnl AC_REQUIRE([AC_TYPE_OFF_T])dnl AC_REQUIRE([AC_TYPE_SIZE_T])dnl AC_REQUIRE([jm_AC_TYPE_LONG_LONG])dnl AC_REQUIRE([gt_TYPE_LONGDOUBLE])dnl AC_REQUIRE([gt_TYPE_WCHAR_T])dnl AC_REQUIRE([gt_TYPE_WINT_T])dnl AC_REQUIRE([jm_AC_HEADER_INTTYPES_H]) AC_REQUIRE([jm_AC_HEADER_STDINT_H]) AC_REQUIRE([gt_TYPE_INTMAX_T]) AC_REQUIRE([gt_PRINTF_POSIX]) AC_REQUIRE([AC_FUNC_ALLOCA])dnl AC_REQUIRE([AC_FUNC_MMAP])dnl AC_REQUIRE([jm_GLIBC21])dnl AC_REQUIRE([gt_INTDIV0])dnl AC_REQUIRE([jm_AC_TYPE_UINTMAX_T])dnl AC_REQUIRE([gt_HEADER_INTTYPES_H])dnl AC_REQUIRE([gt_INTTYPES_PRI])dnl AC_REQUIRE([gl_XSIZE])dnl AC_CHECK_TYPE([ptrdiff_t], , [AC_DEFINE([ptrdiff_t], [long], [Define as the type of the result of subtracting two pointers, if the system doesn't define it.]) ]) AC_CHECK_HEADERS([argz.h limits.h locale.h nl_types.h malloc.h stddef.h \ stdlib.h string.h unistd.h sys/param.h]) AC_CHECK_FUNCS([asprintf fwprintf getcwd getegid geteuid getgid getuid \ mempcpy munmap putenv setenv setlocale snprintf stpcpy strcasecmp strdup \ strtoul tsearch wcslen __argz_count __argz_stringify __argz_next \ __fsetlocking]) dnl Use the _snprintf function only if it is declared (because on NetBSD it dnl is defined as a weak alias of snprintf; we prefer to use the latter). gt_CHECK_DECL(_snprintf, [#include ]) gt_CHECK_DECL(_snwprintf, [#include ]) dnl Use the *_unlocked functions only if they are declared. dnl (because some of them were defined without being declared in Solaris dnl 2.5.1 but were removed in Solaris 2.6, whereas we want binaries built dnl on Solaris 2.5.1 to run on Solaris 2.6). dnl Don't use AC_CHECK_DECLS because it isn't supported in autoconf-2.13. gt_CHECK_DECL(feof_unlocked, [#include ]) gt_CHECK_DECL(fgets_unlocked, [#include ]) gt_CHECK_DECL(getc_unlocked, [#include ]) case $gt_cv_func_printf_posix in *yes) HAVE_POSIX_PRINTF=1 ;; *) HAVE_POSIX_PRINTF=0 ;; esac AC_SUBST([HAVE_POSIX_PRINTF]) if test "$ac_cv_func_asprintf" = yes; then HAVE_ASPRINTF=1 else HAVE_ASPRINTF=0 fi AC_SUBST([HAVE_ASPRINTF]) if test "$ac_cv_func_snprintf" = yes; then HAVE_SNPRINTF=1 else HAVE_SNPRINTF=0 fi AC_SUBST([HAVE_SNPRINTF]) if test "$ac_cv_func_wprintf" = yes; then HAVE_WPRINTF=1 else HAVE_WPRINTF=0 fi AC_SUBST([HAVE_WPRINTF]) AM_ICONV AM_LANGINFO_CODESET if test $ac_cv_header_locale_h = yes; then AM_LC_MESSAGES fi dnl intl/plural.c is generated from intl/plural.y. It requires bison, dnl because plural.y uses bison specific features. It requires at least dnl bison-1.26 because earlier versions generate a plural.c that doesn't dnl compile. dnl bison is only needed for the maintainer (who touches plural.y). But in dnl order to avoid separate Makefiles or --enable-maintainer-mode, we put dnl the rule in general Makefile. Now, some people carelessly touch the dnl files or have a broken "make" program, hence the plural.c rule will dnl sometimes fire. To avoid an error, defines BISON to ":" if it is not dnl present or too old. AC_CHECK_PROGS([INTLBISON], [bison]) if test -z "$INTLBISON"; then ac_verc_fail=yes else dnl Found it, now check the version. AC_MSG_CHECKING([version of bison]) changequote(<<,>>)dnl ac_prog_version=`$INTLBISON --version 2>&1 | sed -n 's/^.*GNU Bison.* \([0-9]*\.[0-9.]*\).*$/\1/p'` case $ac_prog_version in '') ac_prog_version="v. ?.??, bad"; ac_verc_fail=yes;; 1.2[6-9]* | 1.[3-9][0-9]* | [2-9].*) changequote([,])dnl ac_prog_version="$ac_prog_version, ok"; ac_verc_fail=no;; *) ac_prog_version="$ac_prog_version, bad"; ac_verc_fail=yes;; esac AC_MSG_RESULT([$ac_prog_version]) fi if test $ac_verc_fail = yes; then INTLBISON=: fi ]) dnl gt_CHECK_DECL(FUNC, INCLUDES) dnl Check whether a function is declared. AC_DEFUN([gt_CHECK_DECL], [ AC_CACHE_CHECK([whether $1 is declared], ac_cv_have_decl_$1, [AC_TRY_COMPILE([$2], [ #ifndef $1 char *p = (char *) $1; #endif ], ac_cv_have_decl_$1=yes, ac_cv_have_decl_$1=no)]) if test $ac_cv_have_decl_$1 = yes; then gt_value=1 else gt_value=0 fi AC_DEFINE_UNQUOTED([HAVE_DECL_]translit($1, [a-z], [A-Z]), [$gt_value], [Define to 1 if you have the declaration of `$1', and to 0 if you don't.]) ]) dnl Usage: AM_GNU_GETTEXT_VERSION([gettext-version]) AC_DEFUN([AM_GNU_GETTEXT_VERSION], []) # glibc21.m4 serial 2 (fileutils-4.1.3, gettext-0.10.40) dnl Copyright (C) 2000-2002 Free Software Foundation, Inc. dnl This file is free software, distributed under the terms of the GNU dnl General Public License. As a special exception to the GNU General dnl Public License, this file may be distributed as part of a program dnl that contains a configuration script generated by Autoconf, under dnl the same distribution terms as the rest of that program. # Test for the GNU C Library, version 2.1 or newer. # From Bruno Haible. AC_DEFUN([jm_GLIBC21], [ AC_CACHE_CHECK(whether we are using the GNU C Library 2.1 or newer, ac_cv_gnu_library_2_1, [AC_EGREP_CPP([Lucky GNU user], [ #include #ifdef __GNU_LIBRARY__ #if (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 1) || (__GLIBC__ > 2) Lucky GNU user #endif #endif ], ac_cv_gnu_library_2_1=yes, ac_cv_gnu_library_2_1=no) ] ) AC_SUBST(GLIBC21) GLIBC21="$ac_cv_gnu_library_2_1" ] ) # iconv.m4 serial AM4 (gettext-0.11.3) dnl Copyright (C) 2000-2002 Free Software Foundation, Inc. dnl This file is free software, distributed under the terms of the GNU dnl General Public License. As a special exception to the GNU General dnl Public License, this file may be distributed as part of a program dnl that contains a configuration script generated by Autoconf, under dnl the same distribution terms as the rest of that program. dnl From Bruno Haible. AC_DEFUN([AM_ICONV_LINKFLAGS_BODY], [ dnl Prerequisites of AC_LIB_LINKFLAGS_BODY. AC_REQUIRE([AC_LIB_PREPARE_PREFIX]) AC_REQUIRE([AC_LIB_RPATH]) dnl Search for libiconv and define LIBICONV, LTLIBICONV and INCICONV dnl accordingly. AC_LIB_LINKFLAGS_BODY([iconv]) ]) AC_DEFUN([AM_ICONV_LINK], [ dnl Some systems have iconv in libc, some have it in libiconv (OSF/1 and dnl those with the standalone portable GNU libiconv installed). dnl Search for libiconv and define LIBICONV, LTLIBICONV and INCICONV dnl accordingly. AC_REQUIRE([AM_ICONV_LINKFLAGS_BODY]) dnl Add $INCICONV to CPPFLAGS before performing the following checks, dnl because if the user has installed libiconv and not disabled its use dnl via --without-libiconv-prefix, he wants to use it. The first dnl AC_TRY_LINK will then fail, the second AC_TRY_LINK will succeed. am_save_CPPFLAGS="$CPPFLAGS" AC_LIB_APPENDTOVAR([CPPFLAGS], [$INCICONV]) AC_CACHE_CHECK(for iconv, am_cv_func_iconv, [ am_cv_func_iconv="no, consider installing GNU libiconv" am_cv_lib_iconv=no AC_TRY_LINK([#include #include ], [iconv_t cd = iconv_open("",""); iconv(cd,NULL,NULL,NULL,NULL); iconv_close(cd);], am_cv_func_iconv=yes) if test "$am_cv_func_iconv" != yes; then am_save_LIBS="$LIBS" LIBS="$LIBS $LIBICONV" AC_TRY_LINK([#include #include ], [iconv_t cd = iconv_open("",""); iconv(cd,NULL,NULL,NULL,NULL); iconv_close(cd);], am_cv_lib_iconv=yes am_cv_func_iconv=yes) LIBS="$am_save_LIBS" fi ]) if test "$am_cv_func_iconv" = yes; then AC_DEFINE(HAVE_ICONV, 1, [Define if you have the iconv() function.]) fi if test "$am_cv_lib_iconv" = yes; then AC_MSG_CHECKING([how to link with libiconv]) AC_MSG_RESULT([$LIBICONV]) else dnl If $LIBICONV didn't lead to a usable library, we don't need $INCICONV dnl either. CPPFLAGS="$am_save_CPPFLAGS" LIBICONV= LTLIBICONV= fi AC_SUBST(LIBICONV) AC_SUBST(LTLIBICONV) ]) AC_DEFUN([AM_ICONV], [ AM_ICONV_LINK if test "$am_cv_func_iconv" = yes; then AC_MSG_CHECKING([for iconv declaration]) AC_CACHE_VAL(am_cv_proto_iconv, [ AC_TRY_COMPILE([ #include #include extern #ifdef __cplusplus "C" #endif #if defined(__STDC__) || defined(__cplusplus) size_t iconv (iconv_t cd, char * *inbuf, size_t *inbytesleft, char * *outbuf, size_t *outbytesleft); #else size_t iconv(); #endif ], [], am_cv_proto_iconv_arg1="", am_cv_proto_iconv_arg1="const") am_cv_proto_iconv="extern size_t iconv (iconv_t cd, $am_cv_proto_iconv_arg1 char * *inbuf, size_t *inbytesleft, char * *outbuf, size_t *outbytesleft);"]) am_cv_proto_iconv=`echo "[$]am_cv_proto_iconv" | tr -s ' ' | sed -e 's/( /(/'` AC_MSG_RESULT([$]{ac_t:- }[$]am_cv_proto_iconv) AC_DEFINE_UNQUOTED(ICONV_CONST, $am_cv_proto_iconv_arg1, [Define as const if the declaration of iconv() needs const.]) fi ]) # intdiv0.m4 serial 1 (gettext-0.11.3) dnl Copyright (C) 2002 Free Software Foundation, Inc. dnl This file is free software, distributed under the terms of the GNU dnl General Public License. As a special exception to the GNU General dnl Public License, this file may be distributed as part of a program dnl that contains a configuration script generated by Autoconf, under dnl the same distribution terms as the rest of that program. dnl From Bruno Haible. AC_DEFUN([gt_INTDIV0], [ AC_REQUIRE([AC_PROG_CC])dnl AC_REQUIRE([AC_CANONICAL_HOST])dnl AC_CACHE_CHECK([whether integer division by zero raises SIGFPE], gt_cv_int_divbyzero_sigfpe, [ AC_TRY_RUN([ #include #include static void #ifdef __cplusplus sigfpe_handler (int sig) #else sigfpe_handler (sig) int sig; #endif { /* Exit with code 0 if SIGFPE, with code 1 if any other signal. */ exit (sig != SIGFPE); } int x = 1; int y = 0; int z; int nan; int main () { signal (SIGFPE, sigfpe_handler); /* IRIX and AIX (when "xlc -qcheck" is used) yield signal SIGTRAP. */ #if (defined (__sgi) || defined (_AIX)) && defined (SIGTRAP) signal (SIGTRAP, sigfpe_handler); #endif /* Linux/SPARC yields signal SIGILL. */ #if defined (__sparc__) && defined (__linux__) signal (SIGILL, sigfpe_handler); #endif z = x / y; nan = y / y; exit (1); } ], gt_cv_int_divbyzero_sigfpe=yes, gt_cv_int_divbyzero_sigfpe=no, [ # Guess based on the CPU. case "$host_cpu" in alpha* | i[34567]86 | m68k | s390*) gt_cv_int_divbyzero_sigfpe="guessing yes";; *) gt_cv_int_divbyzero_sigfpe="guessing no";; esac ]) ]) case "$gt_cv_int_divbyzero_sigfpe" in *yes) value=1;; *) value=0;; esac AC_DEFINE_UNQUOTED(INTDIV0_RAISES_SIGFPE, $value, [Define if integer division by zero raises signal SIGFPE.]) ]) # intmax.m4 serial 1 (gettext-0.12) dnl Copyright (C) 2002-2003 Free Software Foundation, Inc. dnl This file is free software, distributed under the terms of the GNU dnl General Public License. As a special exception to the GNU General dnl Public License, this file may be distributed as part of a program dnl that contains a configuration script generated by Autoconf, under dnl the same distribution terms as the rest of that program. dnl From Bruno Haible. dnl Test whether the system has the 'intmax_t' type, but don't attempt to dnl find a replacement if it is lacking. AC_DEFUN([gt_TYPE_INTMAX_T], [ AC_REQUIRE([jm_AC_HEADER_INTTYPES_H]) AC_REQUIRE([jm_AC_HEADER_STDINT_H]) AC_CACHE_CHECK(for intmax_t, gt_cv_c_intmax_t, [AC_TRY_COMPILE([ #include #include #if HAVE_STDINT_H_WITH_UINTMAX #include #endif #if HAVE_INTTYPES_H_WITH_UINTMAX #include #endif ], [intmax_t x = -1;], gt_cv_c_intmax_t=yes, gt_cv_c_intmax_t=no)]) if test $gt_cv_c_intmax_t = yes; then AC_DEFINE(HAVE_INTMAX_T, 1, [Define if you have the 'intmax_t' type in or .]) fi ]) # inttypes.m4 serial 1 (gettext-0.11.4) dnl Copyright (C) 1997-2002 Free Software Foundation, Inc. dnl This file is free software, distributed under the terms of the GNU dnl General Public License. As a special exception to the GNU General dnl Public License, this file may be distributed as part of a program dnl that contains a configuration script generated by Autoconf, under dnl the same distribution terms as the rest of that program. dnl From Paul Eggert. # Define HAVE_INTTYPES_H if exists and doesn't clash with # . AC_DEFUN([gt_HEADER_INTTYPES_H], [ AC_CACHE_CHECK([for inttypes.h], gt_cv_header_inttypes_h, [ AC_TRY_COMPILE( [#include #include ], [], gt_cv_header_inttypes_h=yes, gt_cv_header_inttypes_h=no) ]) if test $gt_cv_header_inttypes_h = yes; then AC_DEFINE_UNQUOTED(HAVE_INTTYPES_H, 1, [Define if exists and doesn't clash with .]) fi ]) # inttypes_h.m4 serial 5 (gettext-0.12) dnl Copyright (C) 1997-2003 Free Software Foundation, Inc. dnl This file is free software, distributed under the terms of the GNU dnl General Public License. As a special exception to the GNU General dnl Public License, this file may be distributed as part of a program dnl that contains a configuration script generated by Autoconf, under dnl the same distribution terms as the rest of that program. dnl From Paul Eggert. # Define HAVE_INTTYPES_H_WITH_UINTMAX if exists, # doesn't clash with , and declares uintmax_t. AC_DEFUN([jm_AC_HEADER_INTTYPES_H], [ AC_CACHE_CHECK([for inttypes.h], jm_ac_cv_header_inttypes_h, [AC_TRY_COMPILE( [#include #include ], [uintmax_t i = (uintmax_t) -1;], jm_ac_cv_header_inttypes_h=yes, jm_ac_cv_header_inttypes_h=no)]) if test $jm_ac_cv_header_inttypes_h = yes; then AC_DEFINE_UNQUOTED(HAVE_INTTYPES_H_WITH_UINTMAX, 1, [Define if exists, doesn't clash with , and declares uintmax_t. ]) fi ]) # inttypes-pri.m4 serial 1 (gettext-0.11.4) dnl Copyright (C) 1997-2002 Free Software Foundation, Inc. dnl This file is free software, distributed under the terms of the GNU dnl General Public License. As a special exception to the GNU General dnl Public License, this file may be distributed as part of a program dnl that contains a configuration script generated by Autoconf, under dnl the same distribution terms as the rest of that program. dnl From Bruno Haible. # Define PRI_MACROS_BROKEN if exists and defines the PRI* # macros to non-string values. This is the case on AIX 4.3.3. AC_DEFUN([gt_INTTYPES_PRI], [ AC_REQUIRE([gt_HEADER_INTTYPES_H]) if test $gt_cv_header_inttypes_h = yes; then AC_CACHE_CHECK([whether the inttypes.h PRIxNN macros are broken], gt_cv_inttypes_pri_broken, [ AC_TRY_COMPILE([#include #ifdef PRId32 char *p = PRId32; #endif ], [], gt_cv_inttypes_pri_broken=no, gt_cv_inttypes_pri_broken=yes) ]) fi if test "$gt_cv_inttypes_pri_broken" = yes; then AC_DEFINE_UNQUOTED(PRI_MACROS_BROKEN, 1, [Define if exists and defines unusable PRI* macros.]) fi ]) # isc-posix.m4 serial 2 (gettext-0.11.2) dnl Copyright (C) 1995-2002 Free Software Foundation, Inc. dnl This file is free software, distributed under the terms of the GNU dnl General Public License. As a special exception to the GNU General dnl Public License, this file may be distributed as part of a program dnl that contains a configuration script generated by Autoconf, under dnl the same distribution terms as the rest of that program. # This file is not needed with autoconf-2.53 and newer. Remove it in 2005. # This test replaces the one in autoconf. # Currently this macro should have the same name as the autoconf macro # because gettext's gettext.m4 (distributed in the automake package) # still uses it. Otherwise, the use in gettext.m4 makes autoheader # give these diagnostics: # configure.in:556: AC_TRY_COMPILE was called before AC_ISC_POSIX # configure.in:556: AC_TRY_RUN was called before AC_ISC_POSIX undefine([AC_ISC_POSIX]) AC_DEFUN([AC_ISC_POSIX], [ dnl This test replaces the obsolescent AC_ISC_POSIX kludge. AC_CHECK_LIB(cposix, strerror, [LIBS="$LIBS -lcposix"]) ] ) # lcmessage.m4 serial 3 (gettext-0.11.3) dnl Copyright (C) 1995-2002 Free Software Foundation, Inc. dnl This file is free software, distributed under the terms of the GNU dnl General Public License. As a special exception to the GNU General dnl Public License, this file may be distributed as part of a program dnl that contains a configuration script generated by Autoconf, under dnl the same distribution terms as the rest of that program. dnl dnl This file can can be used in projects which are not available under dnl the GNU General Public License or the GNU Library General Public dnl License but which still want to provide support for the GNU gettext dnl functionality. dnl Please note that the actual code of the GNU gettext library is covered dnl by the GNU Library General Public License, and the rest of the GNU dnl gettext package package is covered by the GNU General Public License. dnl They are *not* in the public domain. dnl Authors: dnl Ulrich Drepper , 1995. # Check whether LC_MESSAGES is available in . AC_DEFUN([AM_LC_MESSAGES], [ AC_CACHE_CHECK([for LC_MESSAGES], am_cv_val_LC_MESSAGES, [AC_TRY_LINK([#include ], [return LC_MESSAGES], am_cv_val_LC_MESSAGES=yes, am_cv_val_LC_MESSAGES=no)]) if test $am_cv_val_LC_MESSAGES = yes; then AC_DEFINE(HAVE_LC_MESSAGES, 1, [Define if your file defines LC_MESSAGES.]) fi ]) # lib-ld.m4 serial 3 (gettext-0.13) dnl Copyright (C) 1996-2003 Free Software Foundation, Inc. dnl This file is free software, distributed under the terms of the GNU dnl General Public License. As a special exception to the GNU General dnl Public License, this file may be distributed as part of a program dnl that contains a configuration script generated by Autoconf, under dnl the same distribution terms as the rest of that program. dnl Subroutines of libtool.m4, dnl with replacements s/AC_/AC_LIB/ and s/lt_cv/acl_cv/ to avoid collision dnl with libtool.m4. dnl From libtool-1.4. Sets the variable with_gnu_ld to yes or no. AC_DEFUN([AC_LIB_PROG_LD_GNU], [AC_CACHE_CHECK([if the linker ($LD) is GNU ld], acl_cv_prog_gnu_ld, [# I'd rather use --version here, but apparently some GNU ld's only accept -v. case `$LD -v 2>&1 conf$$.sh echo "exit 0" >>conf$$.sh chmod +x conf$$.sh if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then PATH_SEPARATOR=';' else PATH_SEPARATOR=: fi rm -f conf$$.sh fi ac_prog=ld if test "$GCC" = yes; then # Check if gcc -print-prog-name=ld gives a path. AC_MSG_CHECKING([for ld used by GCC]) case $host in *-*-mingw*) # gcc leaves a trailing carriage return which upsets mingw ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; *) ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; esac case $ac_prog in # Accept absolute paths. [[\\/]* | [A-Za-z]:[\\/]*)] [re_direlt='/[^/][^/]*/\.\./'] # Canonicalize the path of ld ac_prog=`echo $ac_prog| sed 's%\\\\%/%g'` while echo $ac_prog | grep "$re_direlt" > /dev/null 2>&1; do ac_prog=`echo $ac_prog| sed "s%$re_direlt%/%"` done test -z "$LD" && LD="$ac_prog" ;; "") # If it fails, then pretend we aren't using GCC. ac_prog=ld ;; *) # If it is relative, then search for the first ld in PATH. with_gnu_ld=unknown ;; esac elif test "$with_gnu_ld" = yes; then AC_MSG_CHECKING([for GNU ld]) else AC_MSG_CHECKING([for non-GNU ld]) fi AC_CACHE_VAL(acl_cv_path_LD, [if test -z "$LD"; then IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS="${IFS}${PATH_SEPARATOR-:}" for ac_dir in $PATH; do test -z "$ac_dir" && ac_dir=. if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then acl_cv_path_LD="$ac_dir/$ac_prog" # Check to see if the program is GNU ld. I'd rather use --version, # but apparently some GNU ld's only accept -v. # Break only if it was the GNU/non-GNU ld that we prefer. case `"$acl_cv_path_LD" -v 2>&1 < /dev/null` in *GNU* | *'with BFD'*) test "$with_gnu_ld" != no && break ;; *) test "$with_gnu_ld" != yes && break ;; esac fi done IFS="$ac_save_ifs" else acl_cv_path_LD="$LD" # Let the user override the test with a path. fi]) LD="$acl_cv_path_LD" if test -n "$LD"; then AC_MSG_RESULT($LD) else AC_MSG_RESULT(no) fi test -z "$LD" && AC_MSG_ERROR([no acceptable ld found in \$PATH]) AC_LIB_PROG_LD_GNU ]) # lib-link.m4 serial 4 (gettext-0.12) dnl Copyright (C) 2001-2003 Free Software Foundation, Inc. dnl This file is free software, distributed under the terms of the GNU dnl General Public License. As a special exception to the GNU General dnl Public License, this file may be distributed as part of a program dnl that contains a configuration script generated by Autoconf, under dnl the same distribution terms as the rest of that program. dnl From Bruno Haible. dnl AC_LIB_LINKFLAGS(name [, dependencies]) searches for libname and dnl the libraries corresponding to explicit and implicit dependencies. dnl Sets and AC_SUBSTs the LIB${NAME} and LTLIB${NAME} variables and dnl augments the CPPFLAGS variable. AC_DEFUN([AC_LIB_LINKFLAGS], [ AC_REQUIRE([AC_LIB_PREPARE_PREFIX]) AC_REQUIRE([AC_LIB_RPATH]) define([Name],[translit([$1],[./-], [___])]) define([NAME],[translit([$1],[abcdefghijklmnopqrstuvwxyz./-], [ABCDEFGHIJKLMNOPQRSTUVWXYZ___])]) AC_CACHE_CHECK([how to link with lib[]$1], [ac_cv_lib[]Name[]_libs], [ AC_LIB_LINKFLAGS_BODY([$1], [$2]) ac_cv_lib[]Name[]_libs="$LIB[]NAME" ac_cv_lib[]Name[]_ltlibs="$LTLIB[]NAME" ac_cv_lib[]Name[]_cppflags="$INC[]NAME" ]) LIB[]NAME="$ac_cv_lib[]Name[]_libs" LTLIB[]NAME="$ac_cv_lib[]Name[]_ltlibs" INC[]NAME="$ac_cv_lib[]Name[]_cppflags" AC_LIB_APPENDTOVAR([CPPFLAGS], [$INC]NAME) AC_SUBST([LIB]NAME) AC_SUBST([LTLIB]NAME) dnl Also set HAVE_LIB[]NAME so that AC_LIB_HAVE_LINKFLAGS can reuse the dnl results of this search when this library appears as a dependency. HAVE_LIB[]NAME=yes undefine([Name]) undefine([NAME]) ]) dnl AC_LIB_HAVE_LINKFLAGS(name, dependencies, includes, testcode) dnl searches for libname and the libraries corresponding to explicit and dnl implicit dependencies, together with the specified include files and dnl the ability to compile and link the specified testcode. If found, it dnl sets and AC_SUBSTs HAVE_LIB${NAME}=yes and the LIB${NAME} and dnl LTLIB${NAME} variables and augments the CPPFLAGS variable, and dnl #defines HAVE_LIB${NAME} to 1. Otherwise, it sets and AC_SUBSTs dnl HAVE_LIB${NAME}=no and LIB${NAME} and LTLIB${NAME} to empty. AC_DEFUN([AC_LIB_HAVE_LINKFLAGS], [ AC_REQUIRE([AC_LIB_PREPARE_PREFIX]) AC_REQUIRE([AC_LIB_RPATH]) define([Name],[translit([$1],[./-], [___])]) define([NAME],[translit([$1],[abcdefghijklmnopqrstuvwxyz./-], [ABCDEFGHIJKLMNOPQRSTUVWXYZ___])]) dnl Search for lib[]Name and define LIB[]NAME, LTLIB[]NAME and INC[]NAME dnl accordingly. AC_LIB_LINKFLAGS_BODY([$1], [$2]) dnl Add $INC[]NAME to CPPFLAGS before performing the following checks, dnl because if the user has installed lib[]Name and not disabled its use dnl via --without-lib[]Name-prefix, he wants to use it. ac_save_CPPFLAGS="$CPPFLAGS" AC_LIB_APPENDTOVAR([CPPFLAGS], [$INC]NAME) AC_CACHE_CHECK([for lib[]$1], [ac_cv_lib[]Name], [ ac_save_LIBS="$LIBS" LIBS="$LIBS $LIB[]NAME" AC_TRY_LINK([$3], [$4], [ac_cv_lib[]Name=yes], [ac_cv_lib[]Name=no]) LIBS="$ac_save_LIBS" ]) if test "$ac_cv_lib[]Name" = yes; then HAVE_LIB[]NAME=yes AC_DEFINE([HAVE_LIB]NAME, 1, [Define if you have the $1 library.]) AC_MSG_CHECKING([how to link with lib[]$1]) AC_MSG_RESULT([$LIB[]NAME]) else HAVE_LIB[]NAME=no dnl If $LIB[]NAME didn't lead to a usable library, we don't need dnl $INC[]NAME either. CPPFLAGS="$ac_save_CPPFLAGS" LIB[]NAME= LTLIB[]NAME= fi AC_SUBST([HAVE_LIB]NAME) AC_SUBST([LIB]NAME) AC_SUBST([LTLIB]NAME) undefine([Name]) undefine([NAME]) ]) dnl Determine the platform dependent parameters needed to use rpath: dnl libext, shlibext, hardcode_libdir_flag_spec, hardcode_libdir_separator, dnl hardcode_direct, hardcode_minus_L. AC_DEFUN([AC_LIB_RPATH], [ AC_REQUIRE([AC_PROG_CC]) dnl we use $CC, $GCC, $LDFLAGS AC_REQUIRE([AC_LIB_PROG_LD]) dnl we use $LD, $with_gnu_ld AC_REQUIRE([AC_CANONICAL_HOST]) dnl we use $host AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT]) dnl we use $ac_aux_dir AC_CACHE_CHECK([for shared library run path origin], acl_cv_rpath, [ CC="$CC" GCC="$GCC" LDFLAGS="$LDFLAGS" LD="$LD" with_gnu_ld="$with_gnu_ld" \ ${CONFIG_SHELL-/bin/sh} "$ac_aux_dir/config.rpath" "$host" > conftest.sh . ./conftest.sh rm -f ./conftest.sh acl_cv_rpath=done ]) wl="$acl_cv_wl" libext="$acl_cv_libext" shlibext="$acl_cv_shlibext" hardcode_libdir_flag_spec="$acl_cv_hardcode_libdir_flag_spec" hardcode_libdir_separator="$acl_cv_hardcode_libdir_separator" hardcode_direct="$acl_cv_hardcode_direct" hardcode_minus_L="$acl_cv_hardcode_minus_L" dnl Determine whether the user wants rpath handling at all. AC_ARG_ENABLE(rpath, [ --disable-rpath do not hardcode runtime library paths], :, enable_rpath=yes) ]) dnl AC_LIB_LINKFLAGS_BODY(name [, dependencies]) searches for libname and dnl the libraries corresponding to explicit and implicit dependencies. dnl Sets the LIB${NAME}, LTLIB${NAME} and INC${NAME} variables. AC_DEFUN([AC_LIB_LINKFLAGS_BODY], [ define([NAME],[translit([$1],[abcdefghijklmnopqrstuvwxyz./-], [ABCDEFGHIJKLMNOPQRSTUVWXYZ___])]) dnl By default, look in $includedir and $libdir. use_additional=yes AC_LIB_WITH_FINAL_PREFIX([ eval additional_includedir=\"$includedir\" eval additional_libdir=\"$libdir\" ]) AC_LIB_ARG_WITH([lib$1-prefix], [ --with-lib$1-prefix[=DIR] search for lib$1 in DIR/include and DIR/lib --without-lib$1-prefix don't search for lib$1 in includedir and libdir], [ if test "X$withval" = "Xno"; then use_additional=no else if test "X$withval" = "X"; then AC_LIB_WITH_FINAL_PREFIX([ eval additional_includedir=\"$includedir\" eval additional_libdir=\"$libdir\" ]) else additional_includedir="$withval/include" additional_libdir="$withval/lib" fi fi ]) dnl Search the library and its dependencies in $additional_libdir and dnl $LDFLAGS. Using breadth-first-seach. LIB[]NAME= LTLIB[]NAME= INC[]NAME= rpathdirs= ltrpathdirs= names_already_handled= names_next_round='$1 $2' while test -n "$names_next_round"; do names_this_round="$names_next_round" names_next_round= for name in $names_this_round; do already_handled= for n in $names_already_handled; do if test "$n" = "$name"; then already_handled=yes break fi done if test -z "$already_handled"; then names_already_handled="$names_already_handled $name" dnl See if it was already located by an earlier AC_LIB_LINKFLAGS dnl or AC_LIB_HAVE_LINKFLAGS call. uppername=`echo "$name" | sed -e 'y|abcdefghijklmnopqrstuvwxyz./-|ABCDEFGHIJKLMNOPQRSTUVWXYZ___|'` eval value=\"\$HAVE_LIB$uppername\" if test -n "$value"; then if test "$value" = yes; then eval value=\"\$LIB$uppername\" test -z "$value" || LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$value" eval value=\"\$LTLIB$uppername\" test -z "$value" || LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }$value" else dnl An earlier call to AC_LIB_HAVE_LINKFLAGS has determined dnl that this library doesn't exist. So just drop it. : fi else dnl Search the library lib$name in $additional_libdir and $LDFLAGS dnl and the already constructed $LIBNAME/$LTLIBNAME. found_dir= found_la= found_so= found_a= if test $use_additional = yes; then if test -n "$shlibext" && test -f "$additional_libdir/lib$name.$shlibext"; then found_dir="$additional_libdir" found_so="$additional_libdir/lib$name.$shlibext" if test -f "$additional_libdir/lib$name.la"; then found_la="$additional_libdir/lib$name.la" fi else if test -f "$additional_libdir/lib$name.$libext"; then found_dir="$additional_libdir" found_a="$additional_libdir/lib$name.$libext" if test -f "$additional_libdir/lib$name.la"; then found_la="$additional_libdir/lib$name.la" fi fi fi fi if test "X$found_dir" = "X"; then for x in $LDFLAGS $LTLIB[]NAME; do AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) case "$x" in -L*) dir=`echo "X$x" | sed -e 's/^X-L//'` if test -n "$shlibext" && test -f "$dir/lib$name.$shlibext"; then found_dir="$dir" found_so="$dir/lib$name.$shlibext" if test -f "$dir/lib$name.la"; then found_la="$dir/lib$name.la" fi else if test -f "$dir/lib$name.$libext"; then found_dir="$dir" found_a="$dir/lib$name.$libext" if test -f "$dir/lib$name.la"; then found_la="$dir/lib$name.la" fi fi fi ;; esac if test "X$found_dir" != "X"; then break fi done fi if test "X$found_dir" != "X"; then dnl Found the library. LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-L$found_dir -l$name" if test "X$found_so" != "X"; then dnl Linking with a shared library. We attempt to hardcode its dnl directory into the executable's runpath, unless it's the dnl standard /usr/lib. if test "$enable_rpath" = no || test "X$found_dir" = "X/usr/lib"; then dnl No hardcoding is needed. LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so" else dnl Use an explicit option to hardcode DIR into the resulting dnl binary. dnl Potentially add DIR to ltrpathdirs. dnl The ltrpathdirs will be appended to $LTLIBNAME at the end. haveit= for x in $ltrpathdirs; do if test "X$x" = "X$found_dir"; then haveit=yes break fi done if test -z "$haveit"; then ltrpathdirs="$ltrpathdirs $found_dir" fi dnl The hardcoding into $LIBNAME is system dependent. if test "$hardcode_direct" = yes; then dnl Using DIR/libNAME.so during linking hardcodes DIR into the dnl resulting binary. LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so" else if test -n "$hardcode_libdir_flag_spec" && test "$hardcode_minus_L" = no; then dnl Use an explicit option to hardcode DIR into the resulting dnl binary. LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so" dnl Potentially add DIR to rpathdirs. dnl The rpathdirs will be appended to $LIBNAME at the end. haveit= for x in $rpathdirs; do if test "X$x" = "X$found_dir"; then haveit=yes break fi done if test -z "$haveit"; then rpathdirs="$rpathdirs $found_dir" fi else dnl Rely on "-L$found_dir". dnl But don't add it if it's already contained in the LDFLAGS dnl or the already constructed $LIBNAME haveit= for x in $LDFLAGS $LIB[]NAME; do AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) if test "X$x" = "X-L$found_dir"; then haveit=yes break fi done if test -z "$haveit"; then LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-L$found_dir" fi if test "$hardcode_minus_L" != no; then dnl FIXME: Not sure whether we should use dnl "-L$found_dir -l$name" or "-L$found_dir $found_so" dnl here. LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_so" else dnl We cannot use $hardcode_runpath_var and LD_RUN_PATH dnl here, because this doesn't fit in flags passed to the dnl compiler. So give up. No hardcoding. This affects only dnl very old systems. dnl FIXME: Not sure whether we should use dnl "-L$found_dir -l$name" or "-L$found_dir $found_so" dnl here. LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-l$name" fi fi fi fi else if test "X$found_a" != "X"; then dnl Linking with a static library. LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$found_a" else dnl We shouldn't come here, but anyway it's good to have a dnl fallback. LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-L$found_dir -l$name" fi fi dnl Assume the include files are nearby. additional_includedir= case "$found_dir" in */lib | */lib/) basedir=`echo "X$found_dir" | sed -e 's,^X,,' -e 's,/lib/*$,,'` additional_includedir="$basedir/include" ;; esac if test "X$additional_includedir" != "X"; then dnl Potentially add $additional_includedir to $INCNAME. dnl But don't add it dnl 1. if it's the standard /usr/include, dnl 2. if it's /usr/local/include and we are using GCC on Linux, dnl 3. if it's already present in $CPPFLAGS or the already dnl constructed $INCNAME, dnl 4. if it doesn't exist as a directory. if test "X$additional_includedir" != "X/usr/include"; then haveit= if test "X$additional_includedir" = "X/usr/local/include"; then if test -n "$GCC"; then case $host_os in linux*) haveit=yes;; esac fi fi if test -z "$haveit"; then for x in $CPPFLAGS $INC[]NAME; do AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) if test "X$x" = "X-I$additional_includedir"; then haveit=yes break fi done if test -z "$haveit"; then if test -d "$additional_includedir"; then dnl Really add $additional_includedir to $INCNAME. INC[]NAME="${INC[]NAME}${INC[]NAME:+ }-I$additional_includedir" fi fi fi fi fi dnl Look for dependencies. if test -n "$found_la"; then dnl Read the .la file. It defines the variables dnl dlname, library_names, old_library, dependency_libs, current, dnl age, revision, installed, dlopen, dlpreopen, libdir. save_libdir="$libdir" case "$found_la" in */* | *\\*) . "$found_la" ;; *) . "./$found_la" ;; esac libdir="$save_libdir" dnl We use only dependency_libs. for dep in $dependency_libs; do case "$dep" in -L*) additional_libdir=`echo "X$dep" | sed -e 's/^X-L//'` dnl Potentially add $additional_libdir to $LIBNAME and $LTLIBNAME. dnl But don't add it dnl 1. if it's the standard /usr/lib, dnl 2. if it's /usr/local/lib and we are using GCC on Linux, dnl 3. if it's already present in $LDFLAGS or the already dnl constructed $LIBNAME, dnl 4. if it doesn't exist as a directory. if test "X$additional_libdir" != "X/usr/lib"; then haveit= if test "X$additional_libdir" = "X/usr/local/lib"; then if test -n "$GCC"; then case $host_os in linux*) haveit=yes;; esac fi fi if test -z "$haveit"; then haveit= for x in $LDFLAGS $LIB[]NAME; do AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) if test "X$x" = "X-L$additional_libdir"; then haveit=yes break fi done if test -z "$haveit"; then if test -d "$additional_libdir"; then dnl Really add $additional_libdir to $LIBNAME. LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-L$additional_libdir" fi fi haveit= for x in $LDFLAGS $LTLIB[]NAME; do AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) if test "X$x" = "X-L$additional_libdir"; then haveit=yes break fi done if test -z "$haveit"; then if test -d "$additional_libdir"; then dnl Really add $additional_libdir to $LTLIBNAME. LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-L$additional_libdir" fi fi fi fi ;; -R*) dir=`echo "X$dep" | sed -e 's/^X-R//'` if test "$enable_rpath" != no; then dnl Potentially add DIR to rpathdirs. dnl The rpathdirs will be appended to $LIBNAME at the end. haveit= for x in $rpathdirs; do if test "X$x" = "X$dir"; then haveit=yes break fi done if test -z "$haveit"; then rpathdirs="$rpathdirs $dir" fi dnl Potentially add DIR to ltrpathdirs. dnl The ltrpathdirs will be appended to $LTLIBNAME at the end. haveit= for x in $ltrpathdirs; do if test "X$x" = "X$dir"; then haveit=yes break fi done if test -z "$haveit"; then ltrpathdirs="$ltrpathdirs $dir" fi fi ;; -l*) dnl Handle this in the next round. names_next_round="$names_next_round "`echo "X$dep" | sed -e 's/^X-l//'` ;; *.la) dnl Handle this in the next round. Throw away the .la's dnl directory; it is already contained in a preceding -L dnl option. names_next_round="$names_next_round "`echo "X$dep" | sed -e 's,^X.*/,,' -e 's,^lib,,' -e 's,\.la$,,'` ;; *) dnl Most likely an immediate library name. LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$dep" LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }$dep" ;; esac done fi else dnl Didn't find the library; assume it is in the system directories dnl known to the linker and runtime loader. (All the system dnl directories known to the linker should also be known to the dnl runtime loader, otherwise the system is severely misconfigured.) LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }-l$name" LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-l$name" fi fi fi done done if test "X$rpathdirs" != "X"; then if test -n "$hardcode_libdir_separator"; then dnl Weird platform: only the last -rpath option counts, the user must dnl pass all path elements in one option. We can arrange that for a dnl single library, but not when more than one $LIBNAMEs are used. alldirs= for found_dir in $rpathdirs; do alldirs="${alldirs}${alldirs:+$hardcode_libdir_separator}$found_dir" done dnl Note: hardcode_libdir_flag_spec uses $libdir and $wl. acl_save_libdir="$libdir" libdir="$alldirs" eval flag=\"$hardcode_libdir_flag_spec\" libdir="$acl_save_libdir" LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$flag" else dnl The -rpath options are cumulative. for found_dir in $rpathdirs; do acl_save_libdir="$libdir" libdir="$found_dir" eval flag=\"$hardcode_libdir_flag_spec\" libdir="$acl_save_libdir" LIB[]NAME="${LIB[]NAME}${LIB[]NAME:+ }$flag" done fi fi if test "X$ltrpathdirs" != "X"; then dnl When using libtool, the option that works for both libraries and dnl executables is -R. The -R options are cumulative. for found_dir in $ltrpathdirs; do LTLIB[]NAME="${LTLIB[]NAME}${LTLIB[]NAME:+ }-R$found_dir" done fi ]) dnl AC_LIB_APPENDTOVAR(VAR, CONTENTS) appends the elements of CONTENTS to VAR, dnl unless already present in VAR. dnl Works only for CPPFLAGS, not for LIB* variables because that sometimes dnl contains two or three consecutive elements that belong together. AC_DEFUN([AC_LIB_APPENDTOVAR], [ for element in [$2]; do haveit= for x in $[$1]; do AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) if test "X$x" = "X$element"; then haveit=yes break fi done if test -z "$haveit"; then [$1]="${[$1]}${[$1]:+ }$element" fi done ]) # lib-prefix.m4 serial 3 (gettext-0.13) dnl Copyright (C) 2001-2003 Free Software Foundation, Inc. dnl This file is free software, distributed under the terms of the GNU dnl General Public License. As a special exception to the GNU General dnl Public License, this file may be distributed as part of a program dnl that contains a configuration script generated by Autoconf, under dnl the same distribution terms as the rest of that program. dnl From Bruno Haible. dnl AC_LIB_ARG_WITH is synonymous to AC_ARG_WITH in autoconf-2.13, and dnl similar to AC_ARG_WITH in autoconf 2.52...2.57 except that is doesn't dnl require excessive bracketing. ifdef([AC_HELP_STRING], [AC_DEFUN([AC_LIB_ARG_WITH], [AC_ARG_WITH([$1],[[$2]],[$3],[$4])])], [AC_DEFUN([AC_][LIB_ARG_WITH], [AC_ARG_WITH([$1],[$2],[$3],[$4])])]) dnl AC_LIB_PREFIX adds to the CPPFLAGS and LDFLAGS the flags that are needed dnl to access previously installed libraries. The basic assumption is that dnl a user will want packages to use other packages he previously installed dnl with the same --prefix option. dnl This macro is not needed if only AC_LIB_LINKFLAGS is used to locate dnl libraries, but is otherwise very convenient. AC_DEFUN([AC_LIB_PREFIX], [ AC_BEFORE([$0], [AC_LIB_LINKFLAGS]) AC_REQUIRE([AC_PROG_CC]) AC_REQUIRE([AC_CANONICAL_HOST]) AC_REQUIRE([AC_LIB_PREPARE_PREFIX]) dnl By default, look in $includedir and $libdir. use_additional=yes AC_LIB_WITH_FINAL_PREFIX([ eval additional_includedir=\"$includedir\" eval additional_libdir=\"$libdir\" ]) AC_LIB_ARG_WITH([lib-prefix], [ --with-lib-prefix[=DIR] search for libraries in DIR/include and DIR/lib --without-lib-prefix don't search for libraries in includedir and libdir], [ if test "X$withval" = "Xno"; then use_additional=no else if test "X$withval" = "X"; then AC_LIB_WITH_FINAL_PREFIX([ eval additional_includedir=\"$includedir\" eval additional_libdir=\"$libdir\" ]) else additional_includedir="$withval/include" additional_libdir="$withval/lib" fi fi ]) if test $use_additional = yes; then dnl Potentially add $additional_includedir to $CPPFLAGS. dnl But don't add it dnl 1. if it's the standard /usr/include, dnl 2. if it's already present in $CPPFLAGS, dnl 3. if it's /usr/local/include and we are using GCC on Linux, dnl 4. if it doesn't exist as a directory. if test "X$additional_includedir" != "X/usr/include"; then haveit= for x in $CPPFLAGS; do AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) if test "X$x" = "X-I$additional_includedir"; then haveit=yes break fi done if test -z "$haveit"; then if test "X$additional_includedir" = "X/usr/local/include"; then if test -n "$GCC"; then case $host_os in linux*) haveit=yes;; esac fi fi if test -z "$haveit"; then if test -d "$additional_includedir"; then dnl Really add $additional_includedir to $CPPFLAGS. CPPFLAGS="${CPPFLAGS}${CPPFLAGS:+ }-I$additional_includedir" fi fi fi fi dnl Potentially add $additional_libdir to $LDFLAGS. dnl But don't add it dnl 1. if it's the standard /usr/lib, dnl 2. if it's already present in $LDFLAGS, dnl 3. if it's /usr/local/lib and we are using GCC on Linux, dnl 4. if it doesn't exist as a directory. if test "X$additional_libdir" != "X/usr/lib"; then haveit= for x in $LDFLAGS; do AC_LIB_WITH_FINAL_PREFIX([eval x=\"$x\"]) if test "X$x" = "X-L$additional_libdir"; then haveit=yes break fi done if test -z "$haveit"; then if test "X$additional_libdir" = "X/usr/local/lib"; then if test -n "$GCC"; then case $host_os in linux*) haveit=yes;; esac fi fi if test -z "$haveit"; then if test -d "$additional_libdir"; then dnl Really add $additional_libdir to $LDFLAGS. LDFLAGS="${LDFLAGS}${LDFLAGS:+ }-L$additional_libdir" fi fi fi fi fi ]) dnl AC_LIB_PREPARE_PREFIX creates variables acl_final_prefix, dnl acl_final_exec_prefix, containing the values to which $prefix and dnl $exec_prefix will expand at the end of the configure script. AC_DEFUN([AC_LIB_PREPARE_PREFIX], [ dnl Unfortunately, prefix and exec_prefix get only finally determined dnl at the end of configure. if test "X$prefix" = "XNONE"; then acl_final_prefix="$ac_default_prefix" else acl_final_prefix="$prefix" fi if test "X$exec_prefix" = "XNONE"; then acl_final_exec_prefix='${prefix}' else acl_final_exec_prefix="$exec_prefix" fi acl_save_prefix="$prefix" prefix="$acl_final_prefix" eval acl_final_exec_prefix=\"$acl_final_exec_prefix\" prefix="$acl_save_prefix" ]) dnl AC_LIB_WITH_FINAL_PREFIX([statement]) evaluates statement, with the dnl variables prefix and exec_prefix bound to the values they will have dnl at the end of the configure script. AC_DEFUN([AC_LIB_WITH_FINAL_PREFIX], [ acl_save_prefix="$prefix" prefix="$acl_final_prefix" acl_save_exec_prefix="$exec_prefix" exec_prefix="$acl_final_exec_prefix" $1 exec_prefix="$acl_save_exec_prefix" prefix="$acl_save_prefix" ]) # longdouble.m4 serial 1 (gettext-0.12) dnl Copyright (C) 2002-2003 Free Software Foundation, Inc. dnl This file is free software, distributed under the terms of the GNU dnl General Public License. As a special exception to the GNU General dnl Public License, this file may be distributed as part of a program dnl that contains a configuration script generated by Autoconf, under dnl the same distribution terms as the rest of that program. dnl From Bruno Haible. dnl Test whether the compiler supports the 'long double' type. dnl Prerequisite: AC_PROG_CC AC_DEFUN([gt_TYPE_LONGDOUBLE], [ AC_CACHE_CHECK([for long double], gt_cv_c_long_double, [if test "$GCC" = yes; then gt_cv_c_long_double=yes else AC_TRY_COMPILE([ /* The Stardent Vistra knows sizeof(long double), but does not support it. */ long double foo = 0.0; /* On Ultrix 4.3 cc, long double is 4 and double is 8. */ int array [2*(sizeof(long double) >= sizeof(double)) - 1]; ], , gt_cv_c_long_double=yes, gt_cv_c_long_double=no) fi]) if test $gt_cv_c_long_double = yes; then AC_DEFINE(HAVE_LONG_DOUBLE, 1, [Define if you have the 'long double' type.]) fi ]) # longlong.m4 serial 4 dnl Copyright (C) 1999-2003 Free Software Foundation, Inc. dnl This file is free software, distributed under the terms of the GNU dnl General Public License. As a special exception to the GNU General dnl Public License, this file may be distributed as part of a program dnl that contains a configuration script generated by Autoconf, under dnl the same distribution terms as the rest of that program. dnl From Paul Eggert. # Define HAVE_LONG_LONG if 'long long' works. AC_DEFUN([jm_AC_TYPE_LONG_LONG], [ AC_CACHE_CHECK([for long long], ac_cv_type_long_long, [AC_TRY_LINK([long long ll = 1LL; int i = 63;], [long long llmax = (long long) -1; return ll << i | ll >> i | llmax / ll | llmax % ll;], ac_cv_type_long_long=yes, ac_cv_type_long_long=no)]) if test $ac_cv_type_long_long = yes; then AC_DEFINE(HAVE_LONG_LONG, 1, [Define if you have the 'long long' type.]) fi ]) # nls.m4 serial 1 (gettext-0.12) dnl Copyright (C) 1995-2003 Free Software Foundation, Inc. dnl This file is free software, distributed under the terms of the GNU dnl General Public License. As a special exception to the GNU General dnl Public License, this file may be distributed as part of a program dnl that contains a configuration script generated by Autoconf, under dnl the same distribution terms as the rest of that program. dnl dnl This file can can be used in projects which are not available under dnl the GNU General Public License or the GNU Library General Public dnl License but which still want to provide support for the GNU gettext dnl functionality. dnl Please note that the actual code of the GNU gettext library is covered dnl by the GNU Library General Public License, and the rest of the GNU dnl gettext package package is covered by the GNU General Public License. dnl They are *not* in the public domain. dnl Authors: dnl Ulrich Drepper , 1995-2000. dnl Bruno Haible , 2000-2003. AC_DEFUN([AM_NLS], [ AC_MSG_CHECKING([whether NLS is requested]) dnl Default is enabled NLS AC_ARG_ENABLE(nls, [ --disable-nls do not use Native Language Support], USE_NLS=$enableval, USE_NLS=yes) AC_MSG_RESULT($USE_NLS) AC_SUBST(USE_NLS) ]) AC_DEFUN([AM_MKINSTALLDIRS], [ dnl If the AC_CONFIG_AUX_DIR macro for autoconf is used we possibly dnl find the mkinstalldirs script in another subdir but $(top_srcdir). dnl Try to locate it. MKINSTALLDIRS= if test -n "$ac_aux_dir"; then case "$ac_aux_dir" in /*) MKINSTALLDIRS="$ac_aux_dir/mkinstalldirs" ;; *) MKINSTALLDIRS="\$(top_builddir)/$ac_aux_dir/mkinstalldirs" ;; esac fi if test -z "$MKINSTALLDIRS"; then MKINSTALLDIRS="\$(top_srcdir)/mkinstalldirs" fi AC_SUBST(MKINSTALLDIRS) ]) # po.m4 serial 3 (gettext-0.14) dnl Copyright (C) 1995-2003 Free Software Foundation, Inc. dnl This file is free software, distributed under the terms of the GNU dnl General Public License. As a special exception to the GNU General dnl Public License, this file may be distributed as part of a program dnl that contains a configuration script generated by Autoconf, under dnl the same distribution terms as the rest of that program. dnl dnl This file can can be used in projects which are not available under dnl the GNU General Public License or the GNU Library General Public dnl License but which still want to provide support for the GNU gettext dnl functionality. dnl Please note that the actual code of the GNU gettext library is covered dnl by the GNU Library General Public License, and the rest of the GNU dnl gettext package package is covered by the GNU General Public License. dnl They are *not* in the public domain. dnl Authors: dnl Ulrich Drepper , 1995-2000. dnl Bruno Haible , 2000-2003. dnl Checks for all prerequisites of the po subdirectory. AC_DEFUN([AM_PO_SUBDIRS], [ AC_REQUIRE([AC_PROG_MAKE_SET])dnl AC_REQUIRE([AC_PROG_INSTALL])dnl AC_REQUIRE([AM_MKINSTALLDIRS])dnl AC_REQUIRE([AM_NLS])dnl dnl Perform the following tests also if --disable-nls has been given, dnl because they are needed for "make dist" to work. dnl Search for GNU msgfmt in the PATH. dnl The first test excludes Solaris msgfmt and early GNU msgfmt versions. dnl The second test excludes FreeBSD msgfmt. AM_PATH_PROG_WITH_TEST(MSGFMT, msgfmt, [$ac_dir/$ac_word --statistics /dev/null >/dev/null 2>&1 && (if $ac_dir/$ac_word --statistics /dev/null 2>&1 >/dev/null | grep usage >/dev/null; then exit 1; else exit 0; fi)], :) AC_PATH_PROG(GMSGFMT, gmsgfmt, $MSGFMT) dnl Search for GNU xgettext 0.12 or newer in the PATH. dnl The first test excludes Solaris xgettext and early GNU xgettext versions. dnl The second test excludes FreeBSD xgettext. AM_PATH_PROG_WITH_TEST(XGETTEXT, xgettext, [$ac_dir/$ac_word --omit-header --copyright-holder= --msgid-bugs-address= /dev/null >/dev/null 2>&1 && (if $ac_dir/$ac_word --omit-header --copyright-holder= --msgid-bugs-address= /dev/null 2>&1 >/dev/null | grep usage >/dev/null; then exit 1; else exit 0; fi)], :) dnl Remove leftover from FreeBSD xgettext call. rm -f messages.po dnl Search for GNU msgmerge 0.11 or newer in the PATH. AM_PATH_PROG_WITH_TEST(MSGMERGE, msgmerge, [$ac_dir/$ac_word --update -q /dev/null /dev/null >/dev/null 2>&1], :) dnl This could go away some day; the PATH_PROG_WITH_TEST already does it. dnl Test whether we really found GNU msgfmt. if test "$GMSGFMT" != ":"; then dnl If it is no GNU msgfmt we define it as : so that the dnl Makefiles still can work. if $GMSGFMT --statistics /dev/null >/dev/null 2>&1 && (if $GMSGFMT --statistics /dev/null 2>&1 >/dev/null | grep usage >/dev/null; then exit 1; else exit 0; fi); then : ; else GMSGFMT=`echo "$GMSGFMT" | sed -e 's,^.*/,,'` AC_MSG_RESULT( [found $GMSGFMT program is not GNU msgfmt; ignore it]) GMSGFMT=":" fi fi dnl This could go away some day; the PATH_PROG_WITH_TEST already does it. dnl Test whether we really found GNU xgettext. if test "$XGETTEXT" != ":"; then dnl If it is no GNU xgettext we define it as : so that the dnl Makefiles still can work. if $XGETTEXT --omit-header --copyright-holder= --msgid-bugs-address= /dev/null >/dev/null 2>&1 && (if $XGETTEXT --omit-header --copyright-holder= --msgid-bugs-address= /dev/null 2>&1 >/dev/null | grep usage >/dev/null; then exit 1; else exit 0; fi); then : ; else AC_MSG_RESULT( [found xgettext program is not GNU xgettext; ignore it]) XGETTEXT=":" fi dnl Remove leftover from FreeBSD xgettext call. rm -f messages.po fi AC_OUTPUT_COMMANDS([ for ac_file in $CONFIG_FILES; do # Support "outfile[:infile[:infile...]]" case "$ac_file" in *:*) ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;; esac # PO directories have a Makefile.in generated from Makefile.in.in. case "$ac_file" in */Makefile.in) # Adjust a relative srcdir. ac_dir=`echo "$ac_file"|sed 's%/[^/][^/]*$%%'` ac_dir_suffix="/`echo "$ac_dir"|sed 's%^\./%%'`" ac_dots=`echo "$ac_dir_suffix"|sed 's%/[^/]*%../%g'` # In autoconf-2.13 it is called $ac_given_srcdir. # In autoconf-2.50 it is called $srcdir. test -n "$ac_given_srcdir" || ac_given_srcdir="$srcdir" case "$ac_given_srcdir" in .) top_srcdir=`echo $ac_dots|sed 's%/$%%'` ;; /*) top_srcdir="$ac_given_srcdir" ;; *) top_srcdir="$ac_dots$ac_given_srcdir" ;; esac if test -f "$ac_given_srcdir/$ac_dir/POTFILES.in"; then rm -f "$ac_dir/POTFILES" test -n "$as_me" && echo "$as_me: creating $ac_dir/POTFILES" || echo "creating $ac_dir/POTFILES" cat "$ac_given_srcdir/$ac_dir/POTFILES.in" | sed -e "/^#/d" -e "/^[ ]*\$/d" -e "s,.*, $top_srcdir/& \\\\," | sed -e "\$s/\(.*\) \\\\/\1/" > "$ac_dir/POTFILES" POMAKEFILEDEPS="POTFILES.in" # ALL_LINGUAS, POFILES, UPDATEPOFILES, DUMMYPOFILES, GMOFILES depend # on $ac_dir but don't depend on user-specified configuration # parameters. if test -f "$ac_given_srcdir/$ac_dir/LINGUAS"; then # The LINGUAS file contains the set of available languages. if test -n "$OBSOLETE_ALL_LINGUAS"; then test -n "$as_me" && echo "$as_me: setting ALL_LINGUAS in configure.in is obsolete" || echo "setting ALL_LINGUAS in configure.in is obsolete" fi ALL_LINGUAS_=`sed -e "/^#/d" "$ac_given_srcdir/$ac_dir/LINGUAS"` # Hide the ALL_LINGUAS assigment from automake. eval 'ALL_LINGUAS''=$ALL_LINGUAS_' POMAKEFILEDEPS="$POMAKEFILEDEPS LINGUAS" else # The set of available languages was given in configure.in. eval 'ALL_LINGUAS''=$OBSOLETE_ALL_LINGUAS' fi # Compute POFILES # as $(foreach lang, $(ALL_LINGUAS), $(srcdir)/$(lang).po) # Compute UPDATEPOFILES # as $(foreach lang, $(ALL_LINGUAS), $(lang).po-update) # Compute DUMMYPOFILES # as $(foreach lang, $(ALL_LINGUAS), $(lang).nop) # Compute GMOFILES # as $(foreach lang, $(ALL_LINGUAS), $(srcdir)/$(lang).gmo) case "$ac_given_srcdir" in .) srcdirpre= ;; *) srcdirpre='$(srcdir)/' ;; esac POFILES= UPDATEPOFILES= DUMMYPOFILES= GMOFILES= for lang in $ALL_LINGUAS; do POFILES="$POFILES $srcdirpre$lang.po" UPDATEPOFILES="$UPDATEPOFILES $lang.po-update" DUMMYPOFILES="$DUMMYPOFILES $lang.nop" GMOFILES="$GMOFILES $srcdirpre$lang.gmo" done # CATALOGS depends on both $ac_dir and the user's LINGUAS # environment variable. INST_LINGUAS= if test -n "$ALL_LINGUAS"; then for presentlang in $ALL_LINGUAS; do useit=no if test "%UNSET%" != "$LINGUAS"; then desiredlanguages="$LINGUAS" else desiredlanguages="$ALL_LINGUAS" fi for desiredlang in $desiredlanguages; do # Use the presentlang catalog if desiredlang is # a. equal to presentlang, or # b. a variant of presentlang (because in this case, # presentlang can be used as a fallback for messages # which are not translated in the desiredlang catalog). case "$desiredlang" in "$presentlang"*) useit=yes;; esac done if test $useit = yes; then INST_LINGUAS="$INST_LINGUAS $presentlang" fi done fi CATALOGS= if test -n "$INST_LINGUAS"; then for lang in $INST_LINGUAS; do CATALOGS="$CATALOGS $lang.gmo" done fi test -n "$as_me" && echo "$as_me: creating $ac_dir/Makefile" || echo "creating $ac_dir/Makefile" sed -e "/^POTFILES =/r $ac_dir/POTFILES" -e "/^# Makevars/r $ac_given_srcdir/$ac_dir/Makevars" -e "s|@POFILES@|$POFILES|g" -e "s|@UPDATEPOFILES@|$UPDATEPOFILES|g" -e "s|@DUMMYPOFILES@|$DUMMYPOFILES|g" -e "s|@GMOFILES@|$GMOFILES|g" -e "s|@CATALOGS@|$CATALOGS|g" -e "s|@POMAKEFILEDEPS@|$POMAKEFILEDEPS|g" "$ac_dir/Makefile.in" > "$ac_dir/Makefile" for f in "$ac_given_srcdir/$ac_dir"/Rules-*; do if test -f "$f"; then case "$f" in *.orig | *.bak | *~) ;; *) cat "$f" >> "$ac_dir/Makefile" ;; esac fi done fi ;; esac done], [# Capture the value of obsolete ALL_LINGUAS because we need it to compute # POFILES, UPDATEPOFILES, DUMMYPOFILES, GMOFILES, CATALOGS. But hide it # from automake. eval 'OBSOLETE_ALL_LINGUAS''="$ALL_LINGUAS"' # Capture the value of LINGUAS because we need it to compute CATALOGS. LINGUAS="${LINGUAS-%UNSET%}" ]) ]) dnl Postprocesses a Makefile in a directory containing PO files. AC_DEFUN([AM_POSTPROCESS_PO_MAKEFILE], [ # When this code is run, in config.status, two variables have already been # set: # - OBSOLETE_ALL_LINGUAS is the value of LINGUAS set in configure.in, # - LINGUAS is the value of the environment variable LINGUAS at configure # time. changequote(,)dnl # Adjust a relative srcdir. ac_dir=`echo "$ac_file"|sed 's%/[^/][^/]*$%%'` ac_dir_suffix="/`echo "$ac_dir"|sed 's%^\./%%'`" ac_dots=`echo "$ac_dir_suffix"|sed 's%/[^/]*%../%g'` # In autoconf-2.13 it is called $ac_given_srcdir. # In autoconf-2.50 it is called $srcdir. test -n "$ac_given_srcdir" || ac_given_srcdir="$srcdir" case "$ac_given_srcdir" in .) top_srcdir=`echo $ac_dots|sed 's%/$%%'` ;; /*) top_srcdir="$ac_given_srcdir" ;; *) top_srcdir="$ac_dots$ac_given_srcdir" ;; esac # Find a way to echo strings without interpreting backslash. if test "X`(echo '\t') 2>/dev/null`" = 'X\t'; then gt_echo='echo' else if test "X`(printf '%s\n' '\t') 2>/dev/null`" = 'X\t'; then gt_echo='printf %s\n' else echo_func () { cat < "$ac_file.tmp" if grep -l '@TCLCATALOGS@' "$ac_file" > /dev/null; then # Add dependencies that cannot be formulated as a simple suffix rule. for lang in $ALL_LINGUAS; do frobbedlang=`echo $lang | sed -e 's/\..*$//' -e 'y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/'` cat >> "$ac_file.tmp" < /dev/null; then # Add dependencies that cannot be formulated as a simple suffix rule. for lang in $ALL_LINGUAS; do frobbedlang=`echo $lang | sed -e 's/_/-/g'` cat >> "$ac_file.tmp" <> "$ac_file.tmp" < #include /* The string "%2$d %1$d", with dollar characters protected from the shell's dollar expansion (possibly an autoconf bug). */ static char format[] = { '%', '2', '$', 'd', ' ', '%', '1', '$', 'd', '\0' }; static char buf[100]; int main () { sprintf (buf, format, 33, 55); return (strcmp (buf, "55 33") != 0); }], gt_cv_func_printf_posix=yes, gt_cv_func_printf_posix=no, [ AC_EGREP_CPP(notposix, [ #if defined __NetBSD__ || defined _MSC_VER || defined __MINGW32__ || defined __CYGWIN__ notposix #endif ], gt_cv_func_printf_posix="guessing no", gt_cv_func_printf_posix="guessing yes") ]) ]) case $gt_cv_func_printf_posix in *yes) AC_DEFINE(HAVE_POSIX_PRINTF, 1, [Define if your printf() function supports format strings with positions.]) ;; esac ]) # progtest.m4 serial 3 (gettext-0.12) dnl Copyright (C) 1996-2003 Free Software Foundation, Inc. dnl This file is free software, distributed under the terms of the GNU dnl General Public License. As a special exception to the GNU General dnl Public License, this file may be distributed as part of a program dnl that contains a configuration script generated by Autoconf, under dnl the same distribution terms as the rest of that program. dnl dnl This file can can be used in projects which are not available under dnl the GNU General Public License or the GNU Library General Public dnl License but which still want to provide support for the GNU gettext dnl functionality. dnl Please note that the actual code of the GNU gettext library is covered dnl by the GNU Library General Public License, and the rest of the GNU dnl gettext package package is covered by the GNU General Public License. dnl They are *not* in the public domain. dnl Authors: dnl Ulrich Drepper , 1996. # Search path for a program which passes the given test. dnl AM_PATH_PROG_WITH_TEST(VARIABLE, PROG-TO-CHECK-FOR, dnl TEST-PERFORMED-ON-FOUND_PROGRAM [, VALUE-IF-NOT-FOUND [, PATH]]) AC_DEFUN([AM_PATH_PROG_WITH_TEST], [ # Prepare PATH_SEPARATOR. # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then echo "#! /bin/sh" >conf$$.sh echo "exit 0" >>conf$$.sh chmod +x conf$$.sh if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then PATH_SEPARATOR=';' else PATH_SEPARATOR=: fi rm -f conf$$.sh fi # Find out how to test for executable files. Don't use a zero-byte file, # as systems may use methods other than mode bits to determine executability. cat >conf$$.file <<_ASEOF #! /bin/sh exit 0 _ASEOF chmod +x conf$$.file if test -x conf$$.file >/dev/null 2>&1; then ac_executable_p="test -x" else ac_executable_p="test -f" fi rm -f conf$$.file # Extract the first word of "$2", so it can be a program name with args. set dummy $2; ac_word=[$]2 AC_MSG_CHECKING([for $ac_word]) AC_CACHE_VAL(ac_cv_path_$1, [case "[$]$1" in [[\\/]]* | ?:[[\\/]]*) ac_cv_path_$1="[$]$1" # Let the user override the test with a path. ;; *) ac_save_IFS="$IFS"; IFS=$PATH_SEPARATOR for ac_dir in ifelse([$5], , $PATH, [$5]); do IFS="$ac_save_IFS" test -z "$ac_dir" && ac_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if $ac_executable_p "$ac_dir/$ac_word$ac_exec_ext"; then if [$3]; then ac_cv_path_$1="$ac_dir/$ac_word$ac_exec_ext" break 2 fi fi done done IFS="$ac_save_IFS" dnl If no 4th arg is given, leave the cache variable unset, dnl so AC_PATH_PROGS will keep looking. ifelse([$4], , , [ test -z "[$]ac_cv_path_$1" && ac_cv_path_$1="$4" ])dnl ;; esac])dnl $1="$ac_cv_path_$1" if test ifelse([$4], , [-n "[$]$1"], ["[$]$1" != "$4"]); then AC_MSG_RESULT([$]$1) else AC_MSG_RESULT(no) fi AC_SUBST($1)dnl ]) # signed.m4 serial 1 (gettext-0.10.40) dnl Copyright (C) 2001-2002 Free Software Foundation, Inc. dnl This file is free software, distributed under the terms of the GNU dnl General Public License. As a special exception to the GNU General dnl Public License, this file may be distributed as part of a program dnl that contains a configuration script generated by Autoconf, under dnl the same distribution terms as the rest of that program. dnl From Bruno Haible. AC_DEFUN([bh_C_SIGNED], [ AC_CACHE_CHECK([for signed], bh_cv_c_signed, [AC_TRY_COMPILE(, [signed char x;], bh_cv_c_signed=yes, bh_cv_c_signed=no)]) if test $bh_cv_c_signed = no; then AC_DEFINE(signed, , [Define to empty if the C compiler doesn't support this keyword.]) fi ]) # size_max.m4 serial 2 dnl Copyright (C) 2003 Free Software Foundation, Inc. dnl This file is free software, distributed under the terms of the GNU dnl General Public License. As a special exception to the GNU General dnl Public License, this file may be distributed as part of a program dnl that contains a configuration script generated by Autoconf, under dnl the same distribution terms as the rest of that program. dnl From Bruno Haible. AC_DEFUN([gl_SIZE_MAX], [ AC_CHECK_HEADERS(stdint.h) dnl First test whether the system already has SIZE_MAX. AC_MSG_CHECKING([for SIZE_MAX]) result= AC_EGREP_CPP([Found it], [ #include #if HAVE_STDINT_H #include #endif #ifdef SIZE_MAX Found it #endif ], result=yes) if test -z "$result"; then dnl Define it ourselves. Here we assume that the type 'size_t' is not wider dnl than the type 'unsigned long'. dnl The _AC_COMPUTE_INT macro works up to LONG_MAX, since it uses 'expr', dnl which is guaranteed to work from LONG_MIN to LONG_MAX. _AC_COMPUTE_INT([~(size_t)0 / 10], res_hi, [#include ], result=?) _AC_COMPUTE_INT([~(size_t)0 % 10], res_lo, [#include ], result=?) _AC_COMPUTE_INT([sizeof (size_t) <= sizeof (unsigned int)], fits_in_uint, [#include ], result=?) if test "$fits_in_uint" = 1; then dnl Even though SIZE_MAX fits in an unsigned int, it must be of type dnl 'unsigned long' if the type 'size_t' is the same as 'unsigned long'. AC_TRY_COMPILE([#include extern size_t foo; extern unsigned long foo; ], [], fits_in_uint=0) fi if test -z "$result"; then if test "$fits_in_uint" = 1; then result="$res_hi$res_lo"U else result="$res_hi$res_lo"UL fi else dnl Shouldn't happen, but who knows... result='~(size_t)0' fi fi AC_MSG_RESULT([$result]) if test "$result" != yes; then AC_DEFINE_UNQUOTED([SIZE_MAX], [$result], [Define as the maximum value of type 'size_t', if the system doesn't define it.]) fi ]) # stdint_h.m4 serial 3 (gettext-0.12) dnl Copyright (C) 1997-2003 Free Software Foundation, Inc. dnl This file is free software, distributed under the terms of the GNU dnl General Public License. As a special exception to the GNU General dnl Public License, this file may be distributed as part of a program dnl that contains a configuration script generated by Autoconf, under dnl the same distribution terms as the rest of that program. dnl From Paul Eggert. # Define HAVE_STDINT_H_WITH_UINTMAX if exists, # doesn't clash with , and declares uintmax_t. AC_DEFUN([jm_AC_HEADER_STDINT_H], [ AC_CACHE_CHECK([for stdint.h], jm_ac_cv_header_stdint_h, [AC_TRY_COMPILE( [#include #include ], [uintmax_t i = (uintmax_t) -1;], jm_ac_cv_header_stdint_h=yes, jm_ac_cv_header_stdint_h=no)]) if test $jm_ac_cv_header_stdint_h = yes; then AC_DEFINE_UNQUOTED(HAVE_STDINT_H_WITH_UINTMAX, 1, [Define if exists, doesn't clash with , and declares uintmax_t. ]) fi ]) # uintmax_t.m4 serial 7 (gettext-0.12) dnl Copyright (C) 1997-2003 Free Software Foundation, Inc. dnl This file is free software, distributed under the terms of the GNU dnl General Public License. As a special exception to the GNU General dnl Public License, this file may be distributed as part of a program dnl that contains a configuration script generated by Autoconf, under dnl the same distribution terms as the rest of that program. dnl From Paul Eggert. AC_PREREQ(2.13) # Define uintmax_t to 'unsigned long' or 'unsigned long long' # if it is not already defined in or . AC_DEFUN([jm_AC_TYPE_UINTMAX_T], [ AC_REQUIRE([jm_AC_HEADER_INTTYPES_H]) AC_REQUIRE([jm_AC_HEADER_STDINT_H]) if test $jm_ac_cv_header_inttypes_h = no && test $jm_ac_cv_header_stdint_h = no; then AC_REQUIRE([jm_AC_TYPE_UNSIGNED_LONG_LONG]) test $ac_cv_type_unsigned_long_long = yes \ && ac_type='unsigned long long' \ || ac_type='unsigned long' AC_DEFINE_UNQUOTED(uintmax_t, $ac_type, [Define to unsigned long or unsigned long long if and don't define.]) else AC_DEFINE(HAVE_UINTMAX_T, 1, [Define if you have the 'uintmax_t' type in or .]) fi ]) # ulonglong.m4 serial 3 dnl Copyright (C) 1999-2003 Free Software Foundation, Inc. dnl This file is free software, distributed under the terms of the GNU dnl General Public License. As a special exception to the GNU General dnl Public License, this file may be distributed as part of a program dnl that contains a configuration script generated by Autoconf, under dnl the same distribution terms as the rest of that program. dnl From Paul Eggert. # Define HAVE_UNSIGNED_LONG_LONG if 'unsigned long long' works. AC_DEFUN([jm_AC_TYPE_UNSIGNED_LONG_LONG], [ AC_CACHE_CHECK([for unsigned long long], ac_cv_type_unsigned_long_long, [AC_TRY_LINK([unsigned long long ull = 1ULL; int i = 63;], [unsigned long long ullmax = (unsigned long long) -1; return ull << i | ull >> i | ullmax / ull | ullmax % ull;], ac_cv_type_unsigned_long_long=yes, ac_cv_type_unsigned_long_long=no)]) if test $ac_cv_type_unsigned_long_long = yes; then AC_DEFINE(HAVE_UNSIGNED_LONG_LONG, 1, [Define if you have the 'unsigned long long' type.]) fi ]) # wchar_t.m4 serial 1 (gettext-0.12) dnl Copyright (C) 2002-2003 Free Software Foundation, Inc. dnl This file is free software, distributed under the terms of the GNU dnl General Public License. As a special exception to the GNU General dnl Public License, this file may be distributed as part of a program dnl that contains a configuration script generated by Autoconf, under dnl the same distribution terms as the rest of that program. dnl From Bruno Haible. dnl Test whether has the 'wchar_t' type. dnl Prerequisite: AC_PROG_CC AC_DEFUN([gt_TYPE_WCHAR_T], [ AC_CACHE_CHECK([for wchar_t], gt_cv_c_wchar_t, [AC_TRY_COMPILE([#include wchar_t foo = (wchar_t)'\0';], , gt_cv_c_wchar_t=yes, gt_cv_c_wchar_t=no)]) if test $gt_cv_c_wchar_t = yes; then AC_DEFINE(HAVE_WCHAR_T, 1, [Define if you have the 'wchar_t' type.]) fi ]) # wint_t.m4 serial 1 (gettext-0.12) dnl Copyright (C) 2003 Free Software Foundation, Inc. dnl This file is free software, distributed under the terms of the GNU dnl General Public License. As a special exception to the GNU General dnl Public License, this file may be distributed as part of a program dnl that contains a configuration script generated by Autoconf, under dnl the same distribution terms as the rest of that program. dnl From Bruno Haible. dnl Test whether has the 'wint_t' type. dnl Prerequisite: AC_PROG_CC AC_DEFUN([gt_TYPE_WINT_T], [ AC_CACHE_CHECK([for wint_t], gt_cv_c_wint_t, [AC_TRY_COMPILE([#include wint_t foo = (wchar_t)'\0';], , gt_cv_c_wint_t=yes, gt_cv_c_wint_t=no)]) if test $gt_cv_c_wint_t = yes; then AC_DEFINE(HAVE_WINT_T, 1, [Define if you have the 'wint_t' type.]) fi ]) # xsize.m4 serial 2 dnl Copyright (C) 2003 Free Software Foundation, Inc. dnl This file is free software, distributed under the terms of the GNU dnl General Public License. As a special exception to the GNU General dnl Public License, this file may be distributed as part of a program dnl that contains a configuration script generated by Autoconf, under dnl the same distribution terms as the rest of that program. AC_DEFUN([gl_XSIZE], [ dnl Prerequisites of lib/xsize.h. AC_REQUIRE([gl_SIZE_MAX]) AC_CHECK_HEADERS(stdint.h) ]) # from http://autoconf-archive.cryp.to/ax_tls.html # # This was licensed under the GPL with the following exception: # # As a special exception, the respective Autoconf Macro's copyright # owner gives unlimited permission to copy, distribute and modify the # configure scripts that are the output of Autoconf when processing # the Macro. You need not follow the terms of the GNU General Public # License when using or distributing such scripts, even though # portions of the text of the Macro appear in them. The GNU General # Public License (GPL) does govern all other use of the material that # constitutes the Autoconf Macro. # # This special exception to the GPL applies to versions of the # Autoconf Macro released by the Autoconf Macro Archive. When you make # and distribute a modified version of the Autoconf Macro, you may # extend this special exception to the GPL to apply to your modified # version as well. # AC_DEFUN([AX_TLS], [ AC_MSG_CHECKING(for thread local storage (TLS) class) AC_CACHE_VAL(ac_cv_tls, [ ax_tls_keywords="__thread __declspec(thread) none" for ax_tls_keyword in $ax_tls_keywords; do case $ax_tls_keyword in none) ac_cv_tls=none ; break ;; *) AC_TRY_COMPILE( [#include static void foo(void) { static ] $ax_tls_keyword [ int bar; exit(1); }], [], [ac_cv_tls=$ax_tls_keyword ; break], ac_cv_tls=none ) esac done ]) if test "$ac_cv_tls" != "none"; then dnl AC_DEFINE([TLS], [], [If the compiler supports a TLS storage class define it to that here]) AC_DEFINE_UNQUOTED([TLS], $ac_cv_tls, [If the compiler supports a TLS storage class define it to that here]) fi AC_MSG_RESULT($ac_cv_tls) ]) # Excerpted from pkg.m4 - Macros to locate and utilise pkg-config # # Copyright © 2004 Scott James Remnant . # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # PKG_PROG_PKG_CONFIG([MIN-VERSION]) # ---------------------------------- AC_DEFUN([PKG_PROG_PKG_CONFIG], [m4_pattern_forbid([^_?PKG_[A-Z_]+$]) m4_pattern_allow([^PKG_CONFIG(_PATH)?$]) AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility])dnl if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then AC_PATH_TOOL([PKG_CONFIG], [pkg-config]) fi if test -n "$PKG_CONFIG"; then _pkg_min_version=m4_default([$1], [0.9.0]) AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version]) if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) PKG_CONFIG="" fi fi[]dnl ])# PKG_PROG_PKG_CONFIG # =========================================================================== # http://www.nongnu.org/autoconf-archive/check_gnu_make.html # =========================================================================== # # SYNOPSIS # # CHECK_GNU_MAKE() # # DESCRIPTION # # This macro searches for a GNU version of make. If a match is found, the # makefile variable `ifGNUmake' is set to the empty string, otherwise it # is set to "#". This is useful for including a special features in a # Makefile, which cannot be handled by other versions of make. The # variable _cv_gnu_make_command is set to the command to invoke GNU make # if it exists, the empty string otherwise. # # Here is an example of its use: # # Makefile.in might contain: # # # A failsafe way of putting a dependency rule into a makefile # $(DEPEND): # $(CC) -MM $(srcdir)/*.c > $(DEPEND) # # @ifGNUmake@ ifeq ($(DEPEND),$(wildcard $(DEPEND))) # @ifGNUmake@ include $(DEPEND) # @ifGNUmake@ endif # # Then configure.in would normally contain: # # CHECK_GNU_MAKE() # AC_OUTPUT(Makefile) # # Then perhaps to cause gnu make to override any other make, we could do # something like this (note that GNU make always looks for GNUmakefile # first): # # if ! test x$_cv_gnu_make_command = x ; then # mv Makefile GNUmakefile # echo .DEFAULT: > Makefile ; # echo \ $_cv_gnu_make_command \$@ >> Makefile; # fi # # Then, if any (well almost any) other make is called, and GNU make also # exists, then the other make wraps the GNU make. # # LICENSE # # Copyright (c) 2008 John Darrington # # Copying and distribution of this file, with or without modification, are # permitted in any medium without royalty provided the copyright notice # and this notice are preserved. # # Note: Modified by Ted Ts'o to add @ifNotGNUMake@ AC_DEFUN( [CHECK_GNU_MAKE], [ AC_CACHE_CHECK( for GNU make,_cv_gnu_make_command, _cv_gnu_make_command='' ; dnl Search all the common names for GNU make for a in "$MAKE" make gmake gnumake ; do if test -z "$a" ; then continue ; fi ; if ( sh -c "$a --version" 2> /dev/null | grep GNU 2>&1 > /dev/null ) ; then _cv_gnu_make_command=$a ; break; fi done ; ) ; dnl If there was a GNU version, then set @ifGNUmake@ to the empty string, '#' otherwise if test "x$_cv_gnu_make_command" != "x" ; then ifGNUmake='' ; ifNotGNUmake='#' ; else ifGNUmake='#' ; ifNotGNUmake='' ; AC_MSG_RESULT("Not found"); fi AC_SUBST(ifGNUmake) AC_SUBST(ifNotGNUmake) ] ) e2fsprogs-1.41.14/lib/0000755031104000366760000000000011405316370012400 5ustar tytsoe2fsprogs-1.41.14/lib/Makefile.checker0000644031104000366760000000063011405316370015442 0ustar tytsoall:: $(LIBRARY)_chk.a real-subdirs:: Makefile $(E) " MKDIR checker" $(Q) mkdir -p checker clean:: $(RM) -rf checker $(LIBRARY)_chk.a: $(OBJS) $(E) " GEN_CHECKER_LIB $@" $(Q) (test -r $@ && $(RM) -f $@.bak && $(MV) $@ $@.bak) $(Q) (cd checker; $(ARGEN) $@ $(OBJS)) -$(Q) $(RANLIB) $@ $(Q) $(RM) -f ../$@ $(Q) (cd .. ; $(LN) $(LINK_BUILD_FLAGS) \ `echo $(my_dir) | sed -e 's;lib/;;'`/$@ $@) e2fsprogs-1.41.14/lib/Makefile.bsd-lib0000644031104000366760000000203611405316370015354 0ustar tytso# # This is a Makefile stub which handles the creation of BSD shared # libraries. # # In order to use this stub, the following makefile variables must be defined. # # BSDLIB_VERSION = 1.0 # BSDLIB_IMAGE = libce # BSDLIB_MYDIR = et # BSDLIB_INSTALL_DIR = $(SHLIBDIR) # all:: image real-subdirs:: Makefile @echo " MKDIR pic" @mkdir -p pic BSD_LIB = $(BSDLIB_IMAGE).so.$(BSDLIB_VERSION) BSDLIB_PIC_FLAG = -fpic image: $(BSD_LIB) $(BSD_LIB): $(OBJS) (cd pic; ld -Bshareable -o $(BSD_LIB) $(OBJS)) $(MV) pic/$(BSD_LIB) . $(RM) -f ../$(BSD_LIB) (cd ..; $(LN) $(LINK_BUILD_FLAGS) \ `echo $(my_dir) | sed -e 's;lib/;;'`/$(BSD_LIB) $(BSD_LIB)) install-shlibs install:: $(BSD_LIB) @echo " INSTALL_PROGRAM $(BSDLIB_INSTALL_DIR)/$(BSD_LIB)" @$(INSTALL_PROGRAM) $(BSD_LIB) \ $(DESTDIR)$(BSDLIB_INSTALL_DIR)/$(BSD_LIB) @-$(LDCONFIG) install-strip: install install-shlibs-strip: install-shlibs uninstall-shlibs uninstall:: $(RM) -f $(DESTDIR)$(BSDLIB_INSTALL_DIR)/$(BSD_LIB) clean:: $(RM) -rf pic $(RM) -f $(BSD_LIB) $(RM) -f ../$(BSD_LIB) e2fsprogs-1.41.14/lib/Makefile.library0000644031104000366760000000073711405316370015512 0ustar tytsoall:: subdirs $(LIBRARY).a install-shlibs:: uninstall-shlibs:: real-subdirs:: subdirs:: Makefile $(Q) $(MAKE) -s real-subdirs $(Q) touch subdirs clean:: $(Q) $(RM) -f subdirs $(LIBRARY).a: $(OBJS) $(E) " GEN_LIB $@" $(Q) (if test -r $@; then $(RM) -f $@.bak && $(MV) $@ $@.bak; fi) $(Q) $(ARGEN) $@ $(OBJS) -@$(RANLIB) $@ $(Q) $(RM) -f ../$@ $(Q) (cd ..; $(LN) $(LINK_BUILD_FLAGS) \ `echo $(my_dir) | sed -e 's;lib/;;'`/$@ $@) $(LIB)/$(LIBRARY).a: $(LIBRARY).a e2fsprogs-1.41.14/lib/Makefile.darwin-lib0000644031104000366760000000247411405316370016076 0ustar tytso# # This is a Makefile stub which handles the creation of Darwin BSD shared # libraries. # # In order to use this stub, the following makefile variables must be defined. # # BSDLIB_VERSION = 1.0 # BSDLIB_IMAGE = libce # BSDLIB_MYDIR = et # BSDLIB_INSTALL_DIR = $(SHLIBDIR) # all:: image real-subdirs:: Makefile $(E) " MKDIR pic" $(Q) mkdir -p pic BSD_LIB = $(BSDLIB_IMAGE).$(BSDLIB_VERSION).dylib BSDLIB_PIC_FLAG = -fPIC image: $(BSD_LIB) $(BSD_LIB): $(OBJS) $(E) " GEN_BSD_SOLIB $(BSD_LIB)" $(Q) (cd pic; $(CC) -dynamiclib -compatibility_version 1.0 -current_version $(BSDLIB_VERSION) \ -flat_namespace -undefined warning -o $(BSD_LIB) $(OBJS)) $(Q) $(MV) pic/$(BSD_LIB) . $(Q) $(RM) -f ../$(BSD_LIB) $(Q) (cd ..; $(LN) $(LINK_BUILD_FLAGS) \ `echo $(my_dir) | sed -e 's;lib/;;'`/$(BSD_LIB) $(BSD_LIB)) $(Q) (cd ..; $(LN) $(LINK_BUILD_FLAGS) $(BSD_LIB) $(BSDLIB_IMAGE).dylib) install-shlibs install:: $(BSD_LIB) $(E) " INSTALL_PROGRAM $(BSDLIB_INSTALL_DIR)/$(BSD_LIB)" $(Q) $(INSTALL_PROGRAM) $(BSD_LIB) \ $(DESTDIR)$(BSDLIB_INSTALL_DIR)/$(BSD_LIB) -$(Q) $(LDCONFIG) install-strip: install install-shlibs-strip: install-shlibs uninstall-shlibs uninstall:: $(RM) -f $(DESTDIR)$(BSDLIB_INSTALL_DIR)/$(BSD_LIB) clean:: $(RM) -rf pic $(RM) -f $(BSD_LIB) $(RM) -f ../$(BSD_LIB) $(RM) -f ../$(BSDLIB_IMAGE).dylib e2fsprogs-1.41.14/lib/ext2fs/0000755031104000366760000000000011504443543013616 5ustar tytsoe2fsprogs-1.41.14/lib/ext2fs/dirblock.c0000644031104000116100000000537211504417000016216 0ustar tytsoeng/* * dirblock.c --- directory block routines. * * Copyright (C) 1995, 1996 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #include #if HAVE_UNISTD_H #include #endif #include #include #include "ext2_fs.h" #include "ext2fs.h" errcode_t ext2fs_read_dir_block2(ext2_filsys fs, blk_t block, void *buf, int flags EXT2FS_ATTR((unused))) { errcode_t retval; char *p, *end; struct ext2_dir_entry *dirent; unsigned int name_len, rec_len; retval = io_channel_read_blk(fs->io, block, 1, buf); if (retval) return retval; p = (char *) buf; end = (char *) buf + fs->blocksize; while (p < end-8) { dirent = (struct ext2_dir_entry *) p; #ifdef WORDS_BIGENDIAN dirent->inode = ext2fs_swab32(dirent->inode); dirent->rec_len = ext2fs_swab16(dirent->rec_len); dirent->name_len = ext2fs_swab16(dirent->name_len); #endif name_len = dirent->name_len; #ifdef WORDS_BIGENDIAN if (flags & EXT2_DIRBLOCK_V2_STRUCT) dirent->name_len = ext2fs_swab16(dirent->name_len); #endif if ((retval = ext2fs_get_rec_len(fs, dirent, &rec_len)) != 0) return retval; if ((rec_len < 8) || (rec_len % 4)) { rec_len = 8; retval = EXT2_ET_DIR_CORRUPTED; } else if (((name_len & 0xFF) + 8) > rec_len) retval = EXT2_ET_DIR_CORRUPTED; p += rec_len; } return retval; } errcode_t ext2fs_read_dir_block(ext2_filsys fs, blk_t block, void *buf) { return ext2fs_read_dir_block2(fs, block, buf, 0); } errcode_t ext2fs_write_dir_block2(ext2_filsys fs, blk_t block, void *inbuf, int flags EXT2FS_ATTR((unused))) { #ifdef WORDS_BIGENDIAN errcode_t retval; char *p, *end; char *buf = 0; unsigned int rec_len; struct ext2_dir_entry *dirent; retval = ext2fs_get_mem(fs->blocksize, &buf); if (retval) return retval; memcpy(buf, inbuf, fs->blocksize); p = buf; end = buf + fs->blocksize; while (p < end) { dirent = (struct ext2_dir_entry *) p; if ((retval = ext2fs_get_rec_len(fs, dirent, &rec_len)) != 0) return retval; if ((rec_len < 8) || (rec_len % 4)) { ext2fs_free_mem(&buf); return (EXT2_ET_DIR_CORRUPTED); } p += rec_len; dirent->inode = ext2fs_swab32(dirent->inode); dirent->rec_len = ext2fs_swab16(dirent->rec_len); dirent->name_len = ext2fs_swab16(dirent->name_len); if (flags & EXT2_DIRBLOCK_V2_STRUCT) dirent->name_len = ext2fs_swab16(dirent->name_len); } retval = io_channel_write_blk(fs->io, block, 1, buf); ext2fs_free_mem(&buf); return retval; #else return io_channel_write_blk(fs->io, block, 1, (char *) inbuf); #endif } errcode_t ext2fs_write_dir_block(ext2_filsys fs, blk_t block, void *inbuf) { return ext2fs_write_dir_block2(fs, block, inbuf, 0); } e2fsprogs-1.41.14/lib/ext2fs/io_manager.c0000644031104000116100000000424011504417000016517 0ustar tytsoeng/* * io_manager.c --- the I/O manager abstraction */ #include #include #if HAVE_UNISTD_H #include #endif #include #include #if HAVE_SYS_STAT_H #include #endif #if HAVE_SYS_TYPES_H #include #endif #include "ext2_fs.h" #include "ext2fs.h" errcode_t io_channel_set_options(io_channel channel, const char *opts) { errcode_t retval = 0; char *next, *ptr, *options, *arg; EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); if (!opts) return 0; if (!channel->manager->set_option) return EXT2_ET_INVALID_ARGUMENT; options = malloc(strlen(opts)+1); if (!options) return EXT2_ET_NO_MEMORY; strcpy(options, opts); ptr = options; while (ptr && *ptr) { next = strchr(ptr, '&'); if (next) *next++ = 0; arg = strchr(ptr, '='); if (arg) *arg++ = 0; retval = (channel->manager->set_option)(channel, ptr, arg); if (retval) break; ptr = next; } free(options); return retval; } errcode_t io_channel_write_byte(io_channel channel, unsigned long offset, int count, const void *data) { EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); if (channel->manager->write_byte) return channel->manager->write_byte(channel, offset, count, data); return EXT2_ET_UNIMPLEMENTED; } errcode_t io_channel_read_blk64(io_channel channel, unsigned long long block, int count, void *data) { EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); if (channel->manager->read_blk64) return (channel->manager->read_blk64)(channel, block, count, data); if ((block >> 32) != 0) return EXT2_ET_IO_CHANNEL_NO_SUPPORT_64; return (channel->manager->read_blk)(channel, (unsigned long) block, count, data); } errcode_t io_channel_write_blk64(io_channel channel, unsigned long long block, int count, const void *data) { EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); if (channel->manager->write_blk64) return (channel->manager->write_blk64)(channel, block, count, data); if ((block >> 32) != 0) return EXT2_ET_IO_CHANNEL_NO_SUPPORT_64; return (channel->manager->write_blk)(channel, (unsigned long) block, count, data); } e2fsprogs-1.41.14/lib/ext2fs/test_io.c0000644031104000116100000003350111504417000016066 0ustar tytsoeng/* * test_io.c --- This is the Test I/O interface. * * Copyright (C) 1996 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #include #include #if HAVE_SYS_STAT_H #include #endif #if HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_SYS_PRCTL_H #include #else #define PR_GET_DUMPABLE 3 #endif #if (!defined(HAVE_PRCTL) && defined(linux)) #include #endif #include "ext2_fs.h" #include "ext2fs.h" /* * For checking structure magic numbers... */ #define EXT2_CHECK_MAGIC(struct, code) \ if ((struct)->magic != (code)) return (code) struct test_private_data { int magic; io_channel real; int flags; FILE *outfile; unsigned long block; int read_abort_count, write_abort_count; void (*read_blk)(unsigned long block, int count, errcode_t err); void (*write_blk)(unsigned long block, int count, errcode_t err); void (*set_blksize)(int blksize, errcode_t err); void (*write_byte)(unsigned long block, int count, errcode_t err); void (*read_blk64)(unsigned long long block, int count, errcode_t err); void (*write_blk64)(unsigned long long block, int count, errcode_t err); }; static errcode_t test_open(const char *name, int flags, io_channel *channel); static errcode_t test_close(io_channel channel); static errcode_t test_set_blksize(io_channel channel, int blksize); static errcode_t test_read_blk(io_channel channel, unsigned long block, int count, void *data); static errcode_t test_write_blk(io_channel channel, unsigned long block, int count, const void *data); static errcode_t test_read_blk64(io_channel channel, unsigned long long block, int count, void *data); static errcode_t test_write_blk64(io_channel channel, unsigned long long block, int count, const void *data); static errcode_t test_flush(io_channel channel); static errcode_t test_write_byte(io_channel channel, unsigned long offset, int count, const void *buf); static errcode_t test_set_option(io_channel channel, const char *option, const char *arg); static errcode_t test_get_stats(io_channel channel, io_stats *stats); static struct struct_io_manager struct_test_manager = { EXT2_ET_MAGIC_IO_MANAGER, "Test I/O Manager", test_open, test_close, test_set_blksize, test_read_blk, test_write_blk, test_flush, test_write_byte, test_set_option, test_get_stats, test_read_blk64, test_write_blk64, }; io_manager test_io_manager = &struct_test_manager; /* * These global variable can be set by the test program as * necessary *before* calling test_open */ io_manager test_io_backing_manager = 0; void (*test_io_cb_read_blk) (unsigned long block, int count, errcode_t err) = 0; void (*test_io_cb_write_blk) (unsigned long block, int count, errcode_t err) = 0; void (*test_io_cb_read_blk64) (unsigned long long block, int count, errcode_t err) = 0; void (*test_io_cb_write_blk64) (unsigned long long block, int count, errcode_t err) = 0; void (*test_io_cb_set_blksize) (int blksize, errcode_t err) = 0; void (*test_io_cb_write_byte) (unsigned long block, int count, errcode_t err) = 0; /* * Test flags */ #define TEST_FLAG_READ 0x01 #define TEST_FLAG_WRITE 0x02 #define TEST_FLAG_SET_BLKSIZE 0x04 #define TEST_FLAG_FLUSH 0x08 #define TEST_FLAG_DUMP 0x10 #define TEST_FLAG_SET_OPTION 0x20 static void test_dump_block(io_channel channel, struct test_private_data *data, unsigned long block, const void *buf) { const unsigned char *cp; FILE *f = data->outfile; int i; unsigned long cksum = 0; for (i=0, cp = buf; i < channel->block_size; i++, cp++) { cksum += *cp; } fprintf(f, "Contents of block %lu, checksum %08lu: \n", block, cksum); for (i=0, cp = buf; i < channel->block_size; i++, cp++) { if ((i % 16) == 0) fprintf(f, "%04x: ", i); fprintf(f, "%02x%c", *cp, ((i % 16) == 15) ? '\n' : ' '); } } static void test_abort(io_channel channel, unsigned long block) { struct test_private_data *data; FILE *f; data = (struct test_private_data *) channel->private_data; f = data->outfile; test_flush(channel); fprintf(f, "Aborting due to I/O to block %lu\n", block); fflush(f); abort(); } static char *safe_getenv(const char *arg) { if ((getuid() != geteuid()) || (getgid() != getegid())) return NULL; #if HAVE_PRCTL if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) == 0) return NULL; #else #if (defined(linux) && defined(SYS_prctl)) if (syscall(SYS_prctl, PR_GET_DUMPABLE, 0, 0, 0, 0) == 0) return NULL; #endif #endif #ifdef HAVE___SECURE_GETENV return __secure_getenv(arg); #else return getenv(arg); #endif } static errcode_t test_open(const char *name, int flags, io_channel *channel) { io_channel io = NULL; struct test_private_data *data = NULL; errcode_t retval; char *value; if (name == 0) return EXT2_ET_BAD_DEVICE_NAME; retval = ext2fs_get_mem(sizeof(struct struct_io_channel), &io); if (retval) return retval; memset(io, 0, sizeof(struct struct_io_channel)); io->magic = EXT2_ET_MAGIC_IO_CHANNEL; retval = ext2fs_get_mem(sizeof(struct test_private_data), &data); if (retval) { retval = EXT2_ET_NO_MEMORY; goto cleanup; } io->manager = test_io_manager; retval = ext2fs_get_mem(strlen(name)+1, &io->name); if (retval) goto cleanup; strcpy(io->name, name); io->private_data = data; io->block_size = 1024; io->read_error = 0; io->write_error = 0; io->refcount = 1; memset(data, 0, sizeof(struct test_private_data)); data->magic = EXT2_ET_MAGIC_TEST_IO_CHANNEL; if (test_io_backing_manager) { retval = test_io_backing_manager->open(name, flags, &data->real); if (retval) goto cleanup; } else data->real = 0; data->read_blk = test_io_cb_read_blk; data->write_blk = test_io_cb_write_blk; data->set_blksize = test_io_cb_set_blksize; data->write_byte = test_io_cb_write_byte; data->read_blk64 = test_io_cb_read_blk64; data->write_blk64 = test_io_cb_write_blk64; data->outfile = NULL; if ((value = safe_getenv("TEST_IO_LOGFILE")) != NULL) data->outfile = fopen(value, "w"); if (!data->outfile) data->outfile = stderr; data->flags = 0; if ((value = safe_getenv("TEST_IO_FLAGS")) != NULL) data->flags = strtoul(value, NULL, 0); data->block = 0; if ((value = safe_getenv("TEST_IO_BLOCK")) != NULL) data->block = strtoul(value, NULL, 0); data->read_abort_count = 0; if ((value = safe_getenv("TEST_IO_READ_ABORT")) != NULL) data->read_abort_count = strtoul(value, NULL, 0); data->write_abort_count = 0; if ((value = safe_getenv("TEST_IO_WRITE_ABORT")) != NULL) data->write_abort_count = strtoul(value, NULL, 0); *channel = io; return 0; cleanup: if (io) ext2fs_free_mem(&io); if (data) ext2fs_free_mem(&data); return retval; } static errcode_t test_close(io_channel channel) { struct test_private_data *data; errcode_t retval = 0; EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); data = (struct test_private_data *) channel->private_data; EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL); if (--channel->refcount > 0) return 0; if (data->real) retval = io_channel_close(data->real); if (data->outfile && data->outfile != stderr) fclose(data->outfile); ext2fs_free_mem(&channel->private_data); if (channel->name) ext2fs_free_mem(&channel->name); ext2fs_free_mem(&channel); return retval; } static errcode_t test_set_blksize(io_channel channel, int blksize) { struct test_private_data *data; errcode_t retval = 0; EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); data = (struct test_private_data *) channel->private_data; EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL); if (data->real) retval = io_channel_set_blksize(data->real, blksize); if (data->set_blksize) data->set_blksize(blksize, retval); if (data->flags & TEST_FLAG_SET_BLKSIZE) fprintf(data->outfile, "Test_io: set_blksize(%d) returned %s\n", blksize, retval ? error_message(retval) : "OK"); channel->block_size = blksize; return retval; } static errcode_t test_read_blk(io_channel channel, unsigned long block, int count, void *buf) { struct test_private_data *data; errcode_t retval = 0; EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); data = (struct test_private_data *) channel->private_data; EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL); if (data->real) retval = io_channel_read_blk(data->real, block, count, buf); if (data->read_blk) data->read_blk(block, count, retval); if (data->flags & TEST_FLAG_READ) fprintf(data->outfile, "Test_io: read_blk(%lu, %d) returned %s\n", block, count, retval ? error_message(retval) : "OK"); if (data->block && data->block == block) { if (data->flags & TEST_FLAG_DUMP) test_dump_block(channel, data, block, buf); if (--data->read_abort_count == 0) test_abort(channel, block); } return retval; } static errcode_t test_write_blk(io_channel channel, unsigned long block, int count, const void *buf) { struct test_private_data *data; errcode_t retval = 0; EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); data = (struct test_private_data *) channel->private_data; EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL); if (data->real) retval = io_channel_write_blk(data->real, block, count, buf); if (data->write_blk) data->write_blk(block, count, retval); if (data->flags & TEST_FLAG_WRITE) fprintf(data->outfile, "Test_io: write_blk(%lu, %d) returned %s\n", block, count, retval ? error_message(retval) : "OK"); if (data->block && data->block == block) { if (data->flags & TEST_FLAG_DUMP) test_dump_block(channel, data, block, buf); if (--data->write_abort_count == 0) test_abort(channel, block); } return retval; } static errcode_t test_read_blk64(io_channel channel, unsigned long long block, int count, void *buf) { struct test_private_data *data; errcode_t retval = 0; EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); data = (struct test_private_data *) channel->private_data; EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL); if (data->real) retval = io_channel_read_blk64(data->real, block, count, buf); if (data->read_blk64) data->read_blk64(block, count, retval); if (data->flags & TEST_FLAG_READ) fprintf(data->outfile, "Test_io: read_blk64(%llu, %d) returned %s\n", block, count, retval ? error_message(retval) : "OK"); if (data->block && data->block == block) { if (data->flags & TEST_FLAG_DUMP) test_dump_block(channel, data, block, buf); if (--data->read_abort_count == 0) test_abort(channel, block); } return retval; } static errcode_t test_write_blk64(io_channel channel, unsigned long long block, int count, const void *buf) { struct test_private_data *data; errcode_t retval = 0; EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); data = (struct test_private_data *) channel->private_data; EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL); if (data->real) retval = io_channel_write_blk64(data->real, block, count, buf); if (data->write_blk64) data->write_blk64(block, count, retval); if (data->flags & TEST_FLAG_WRITE) fprintf(data->outfile, "Test_io: write_blk64(%llu, %d) returned %s\n", block, count, retval ? error_message(retval) : "OK"); if (data->block && data->block == block) { if (data->flags & TEST_FLAG_DUMP) test_dump_block(channel, data, block, buf); if (--data->write_abort_count == 0) test_abort(channel, block); } return retval; } static errcode_t test_write_byte(io_channel channel, unsigned long offset, int count, const void *buf) { struct test_private_data *data; errcode_t retval = 0; EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); data = (struct test_private_data *) channel->private_data; EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL); if (data->real && data->real->manager->write_byte) retval = io_channel_write_byte(data->real, offset, count, buf); if (data->write_byte) data->write_byte(offset, count, retval); if (data->flags & TEST_FLAG_WRITE) fprintf(data->outfile, "Test_io: write_byte(%lu, %d) returned %s\n", offset, count, retval ? error_message(retval) : "OK"); return retval; } /* * Flush data buffers to disk. */ static errcode_t test_flush(io_channel channel) { struct test_private_data *data; errcode_t retval = 0; EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); data = (struct test_private_data *) channel->private_data; EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL); if (data->real) retval = io_channel_flush(data->real); if (data->flags & TEST_FLAG_FLUSH) fprintf(data->outfile, "Test_io: flush() returned %s\n", retval ? error_message(retval) : "OK"); return retval; } static errcode_t test_set_option(io_channel channel, const char *option, const char *arg) { struct test_private_data *data; errcode_t retval = 0; EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); data = (struct test_private_data *) channel->private_data; EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL); if (data->flags & TEST_FLAG_SET_OPTION) fprintf(data->outfile, "Test_io: set_option(%s, %s) ", option, arg); if (data->real && data->real->manager->set_option) { retval = (data->real->manager->set_option)(data->real, option, arg); if (data->flags & TEST_FLAG_SET_OPTION) fprintf(data->outfile, "returned %s\n", retval ? error_message(retval) : "OK"); } else { if (data->flags & TEST_FLAG_SET_OPTION) fprintf(data->outfile, "not implemented\n"); } return retval; } static errcode_t test_get_stats(io_channel channel, io_stats *stats) { struct test_private_data *data; errcode_t retval = 0; EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); data = (struct test_private_data *) channel->private_data; EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_TEST_IO_CHANNEL); if (data->real && data->real->manager->get_stats) { retval = (data->real->manager->get_stats)(data->real, stats); } return retval; } e2fsprogs-1.41.14/lib/ext2fs/ismounted.c0000644031104000366760000002266711405316370016003 0ustar tytso/* * ismounted.c --- Check to see if the filesystem was mounted * * Copyright (C) 1995,1996,1997,1998,1999,2000 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #include #if HAVE_UNISTD_H #include #endif #if HAVE_ERRNO_H #include #endif #include #ifdef HAVE_LINUX_FD_H #include #endif #ifdef HAVE_MNTENT_H #include #endif #ifdef HAVE_GETMNTINFO #include #include #include #endif /* HAVE_GETMNTINFO */ #include #include #include "ext2_fs.h" #include "ext2fs.h" #ifdef HAVE_MNTENT_H /* * Helper function which checks a file in /etc/mtab format to see if a * filesystem is mounted. Returns an error if the file doesn't exist * or can't be opened. */ static errcode_t check_mntent_file(const char *mtab_file, const char *file, int *mount_flags, char *mtpt, int mtlen) { struct mntent *mnt; struct stat st_buf; errcode_t retval = 0; dev_t file_dev=0, file_rdev=0; ino_t file_ino=0; FILE *f; int fd; *mount_flags = 0; if ((f = setmntent (mtab_file, "r")) == NULL) return (errno == ENOENT ? EXT2_NO_MTAB_FILE : errno); if (stat(file, &st_buf) == 0) { if (S_ISBLK(st_buf.st_mode)) { #ifndef __GNU__ /* The GNU hurd is broken with respect to stat devices */ file_rdev = st_buf.st_rdev; #endif /* __GNU__ */ } else { file_dev = st_buf.st_dev; file_ino = st_buf.st_ino; } } while ((mnt = getmntent (f)) != NULL) { if (mnt->mnt_fsname[0] != '/') continue; if (strcmp(file, mnt->mnt_fsname) == 0) break; if (stat(mnt->mnt_fsname, &st_buf) == 0) { if (S_ISBLK(st_buf.st_mode)) { #ifndef __GNU__ if (file_rdev && (file_rdev == st_buf.st_rdev)) break; #endif /* __GNU__ */ } else { if (file_dev && ((file_dev == st_buf.st_dev) && (file_ino == st_buf.st_ino))) break; } } } if (mnt == 0) { #ifndef __GNU__ /* The GNU hurd is broken with respect to stat devices */ /* * Do an extra check to see if this is the root device. We * can't trust /etc/mtab, and /proc/mounts will only list * /dev/root for the root filesystem. Argh. Instead we * check if the given device has the same major/minor number * as the device that the root directory is on. */ if (file_rdev && stat("/", &st_buf) == 0) { if (st_buf.st_dev == file_rdev) { *mount_flags = EXT2_MF_MOUNTED; if (mtpt) strncpy(mtpt, "/", mtlen); goto is_root; } } #endif /* __GNU__ */ goto errout; } #ifndef __GNU__ /* The GNU hurd is deficient; what else is new? */ /* Validate the entry in case /etc/mtab is out of date */ /* * We need to be paranoid, because some broken distributions * (read: Slackware) don't initialize /etc/mtab before checking * all of the non-root filesystems on the disk. */ if (stat(mnt->mnt_dir, &st_buf) < 0) { retval = errno; if (retval == ENOENT) { #ifdef DEBUG printf("Bogus entry in %s! (%s does not exist)\n", mtab_file, mnt->mnt_dir); #endif /* DEBUG */ retval = 0; } goto errout; } if (file_rdev && (st_buf.st_dev != file_rdev)) { #ifdef DEBUG printf("Bogus entry in %s! (%s not mounted on %s)\n", mtab_file, file, mnt->mnt_dir); #endif /* DEBUG */ goto errout; } #endif /* __GNU__ */ *mount_flags = EXT2_MF_MOUNTED; #ifdef MNTOPT_RO /* Check to see if the ro option is set */ if (hasmntopt(mnt, MNTOPT_RO)) *mount_flags |= EXT2_MF_READONLY; #endif if (mtpt) strncpy(mtpt, mnt->mnt_dir, mtlen); /* * Check to see if we're referring to the root filesystem. * If so, do a manual check to see if we can open /etc/mtab * read/write, since if the root is mounted read/only, the * contents of /etc/mtab may not be accurate. */ if (!strcmp(mnt->mnt_dir, "/")) { is_root: #define TEST_FILE "/.ismount-test-file" *mount_flags |= EXT2_MF_ISROOT; fd = open(TEST_FILE, O_RDWR|O_CREAT, 0600); if (fd < 0) { if (errno == EROFS) *mount_flags |= EXT2_MF_READONLY; } else close(fd); (void) unlink(TEST_FILE); } retval = 0; errout: endmntent (f); return retval; } static errcode_t check_mntent(const char *file, int *mount_flags, char *mtpt, int mtlen) { errcode_t retval; #ifdef DEBUG retval = check_mntent_file("/tmp/mtab", file, mount_flags, mtpt, mtlen); if (retval == 0) return 0; #endif /* DEBUG */ #ifdef __linux__ retval = check_mntent_file("/proc/mounts", file, mount_flags, mtpt, mtlen); if (retval == 0 && (*mount_flags != 0)) return 0; #endif /* __linux__ */ #if defined(MOUNTED) || defined(_PATH_MOUNTED) #ifndef MOUNTED #define MOUNTED _PATH_MOUNTED #endif /* MOUNTED */ retval = check_mntent_file(MOUNTED, file, mount_flags, mtpt, mtlen); return retval; #else *mount_flags = 0; return 0; #endif /* defined(MOUNTED) || defined(_PATH_MOUNTED) */ } #else #if defined(HAVE_GETMNTINFO) static errcode_t check_getmntinfo(const char *file, int *mount_flags, char *mtpt, int mtlen) { struct statfs *mp; int len, n; const char *s1; char *s2; n = getmntinfo(&mp, MNT_NOWAIT); if (n == 0) return errno; len = sizeof(_PATH_DEV) - 1; s1 = file; if (strncmp(_PATH_DEV, s1, len) == 0) s1 += len; *mount_flags = 0; while (--n >= 0) { s2 = mp->f_mntfromname; if (strncmp(_PATH_DEV, s2, len) == 0) { s2 += len - 1; *s2 = 'r'; } if (strcmp(s1, s2) == 0 || strcmp(s1, &s2[1]) == 0) { *mount_flags = EXT2_MF_MOUNTED; break; } ++mp; } if (mtpt) strncpy(mtpt, mp->f_mntonname, mtlen); return 0; } #endif /* HAVE_GETMNTINFO */ #endif /* HAVE_MNTENT_H */ /* * Check to see if we're dealing with the swap device. */ static int is_swap_device(const char *file) { FILE *f; char buf[1024], *cp; dev_t file_dev; struct stat st_buf; int ret = 0; file_dev = 0; #ifndef __GNU__ /* The GNU hurd is broken with respect to stat devices */ if ((stat(file, &st_buf) == 0) && S_ISBLK(st_buf.st_mode)) file_dev = st_buf.st_rdev; #endif /* __GNU__ */ if (!(f = fopen("/proc/swaps", "r"))) return 0; /* Skip the first line */ if (!fgets(buf, sizeof(buf), f)) goto leave; if (*buf && strncmp(buf, "Filename\t", 9)) /* Linux <=2.6.19 contained a bug in the /proc/swaps * code where the header would not be displayed */ goto valid_first_line; while (fgets(buf, sizeof(buf), f)) { valid_first_line: if ((cp = strchr(buf, ' ')) != NULL) *cp = 0; if ((cp = strchr(buf, '\t')) != NULL) *cp = 0; if (strcmp(buf, file) == 0) { ret++; break; } #ifndef __GNU__ if (file_dev && (stat(buf, &st_buf) == 0) && S_ISBLK(st_buf.st_mode) && file_dev == st_buf.st_rdev) { ret++; break; } #endif /* __GNU__ */ } leave: fclose(f); return ret; } /* * ext2fs_check_mount_point() fills determines if the device is * mounted or otherwise busy, and fills in mount_flags with one or * more of the following flags: EXT2_MF_MOUNTED, EXT2_MF_ISROOT, * EXT2_MF_READONLY, EXT2_MF_SWAP, and EXT2_MF_BUSY. If mtpt is * non-NULL, the directory where the device is mounted is copied to * where mtpt is pointing, up to mtlen characters. */ #ifdef __TURBOC__ #pragma argsused #endif errcode_t ext2fs_check_mount_point(const char *device, int *mount_flags, char *mtpt, int mtlen) { struct stat st_buf; errcode_t retval = 0; int fd; if (is_swap_device(device)) { *mount_flags = EXT2_MF_MOUNTED | EXT2_MF_SWAP; strncpy(mtpt, "", mtlen); } else { #ifdef HAVE_MNTENT_H retval = check_mntent(device, mount_flags, mtpt, mtlen); #else #ifdef HAVE_GETMNTINFO retval = check_getmntinfo(device, mount_flags, mtpt, mtlen); #else #ifdef __GNUC__ #warning "Can't use getmntent or getmntinfo to check for mounted filesystems!" #endif *mount_flags = 0; #endif /* HAVE_GETMNTINFO */ #endif /* HAVE_MNTENT_H */ } if (retval) return retval; #ifdef __linux__ /* This only works on Linux 2.6+ systems */ if ((stat(device, &st_buf) != 0) || !S_ISBLK(st_buf.st_mode)) return 0; fd = open(device, O_RDONLY | O_EXCL); if (fd < 0) { if (errno == EBUSY) *mount_flags |= EXT2_MF_BUSY; } else close(fd); #endif return 0; } /* * ext2fs_check_if_mounted() sets the mount_flags EXT2_MF_MOUNTED, * EXT2_MF_READONLY, and EXT2_MF_ROOT * */ errcode_t ext2fs_check_if_mounted(const char *file, int *mount_flags) { return ext2fs_check_mount_point(file, mount_flags, NULL, 0); } #ifdef DEBUG int main(int argc, char **argv) { int retval, mount_flags; char mntpt[80]; if (argc < 2) { fprintf(stderr, "Usage: %s device\n", argv[0]); exit(1); } add_error_table(&et_ext2_error_table); mntpt[0] = 0; retval = ext2fs_check_mount_point(argv[1], &mount_flags, mntpt, sizeof(mntpt)); if (retval) { com_err(argv[0], retval, "while calling ext2fs_check_if_mounted"); exit(1); } printf("Device %s reports flags %02x\n", argv[1], mount_flags); if (mount_flags & EXT2_MF_BUSY) printf("\t%s is apparently in use.\n", argv[1]); if (mount_flags & EXT2_MF_MOUNTED) printf("\t%s is mounted.\n", argv[1]); if (mount_flags & EXT2_MF_SWAP) printf("\t%s is a swap device.\n", argv[1]); if (mount_flags & EXT2_MF_READONLY) printf("\t%s is read-only.\n", argv[1]); if (mount_flags & EXT2_MF_ISROOT) printf("\t%s is the root filesystem.\n", argv[1]); if (mntpt[0]) printf("\t%s is mounted on %s.\n", argv[1], mntpt); exit(0); } #endif /* DEBUG */ e2fsprogs-1.41.14/lib/ext2fs/openfs.c0000644031104000116100000002560611504417000015721 0ustar tytsoeng/* * openfs.c --- open an ext2 filesystem * * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #include #include #if HAVE_SYS_STAT_H #include #endif #if HAVE_SYS_TYPES_H #include #endif #include "ext2_fs.h" #include "ext2fs.h" #include "e2image.h" blk_t ext2fs_descriptor_block_loc(ext2_filsys fs, blk_t group_block, dgrp_t i) { int bg; int has_super = 0; int ret_blk; if (!(fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) || (i < fs->super->s_first_meta_bg)) return (group_block + i + 1); bg = EXT2_DESC_PER_BLOCK(fs->super) * i; if (ext2fs_bg_has_super(fs, bg)) has_super = 1; ret_blk = ext2fs_group_first_block(fs, bg) + has_super; /* * If group_block is not the normal value, we're trying to use * the backup group descriptors and superblock --- so use the * alternate location of the second block group in the * metablock group. Ideally we should be testing each bg * descriptor block individually for correctness, but we don't * have the infrastructure in place to do that. */ if (group_block != fs->super->s_first_data_block && ((ret_blk + fs->super->s_blocks_per_group) < fs->super->s_blocks_count)) ret_blk += fs->super->s_blocks_per_group; return ret_blk; } errcode_t ext2fs_open(const char *name, int flags, int superblock, unsigned int block_size, io_manager manager, ext2_filsys *ret_fs) { return ext2fs_open2(name, 0, flags, superblock, block_size, manager, ret_fs); } /* * Note: if superblock is non-zero, block-size must also be non-zero. * Superblock and block_size can be zero to use the default size. * * Valid flags for ext2fs_open() * * EXT2_FLAG_RW - Open the filesystem for read/write. * EXT2_FLAG_FORCE - Open the filesystem even if some of the * features aren't supported. * EXT2_FLAG_JOURNAL_DEV_OK - Open an ext3 journal device */ errcode_t ext2fs_open2(const char *name, const char *io_options, int flags, int superblock, unsigned int block_size, io_manager manager, ext2_filsys *ret_fs) { ext2_filsys fs; errcode_t retval; unsigned long i, first_meta_bg; __u32 features; int groups_per_block, blocks_per_group, io_flags; blk_t group_block, blk; char *dest, *cp; #ifdef WORDS_BIGENDIAN struct ext2_group_desc *gdp; int j; #endif EXT2_CHECK_MAGIC(manager, EXT2_ET_MAGIC_IO_MANAGER); retval = ext2fs_get_mem(sizeof(struct struct_ext2_filsys), &fs); if (retval) return retval; memset(fs, 0, sizeof(struct struct_ext2_filsys)); fs->magic = EXT2_ET_MAGIC_EXT2FS_FILSYS; fs->flags = flags; /* don't overwrite sb backups unless flag is explicitly cleared */ fs->flags |= EXT2_FLAG_MASTER_SB_ONLY; fs->umask = 022; retval = ext2fs_get_mem(strlen(name)+1, &fs->device_name); if (retval) goto cleanup; strcpy(fs->device_name, name); cp = strchr(fs->device_name, '?'); if (!io_options && cp) { *cp++ = 0; io_options = cp; } io_flags = 0; if (flags & EXT2_FLAG_RW) io_flags |= IO_FLAG_RW; if (flags & EXT2_FLAG_EXCLUSIVE) io_flags |= IO_FLAG_EXCLUSIVE; if (flags & EXT2_FLAG_DIRECT_IO) io_flags |= IO_FLAG_DIRECT_IO; retval = manager->open(fs->device_name, io_flags, &fs->io); if (retval) goto cleanup; if (io_options && (retval = io_channel_set_options(fs->io, io_options))) goto cleanup; fs->image_io = fs->io; fs->io->app_data = fs; retval = ext2fs_get_memalign(SUPERBLOCK_SIZE, 512, &fs->super); if (retval) goto cleanup; if (flags & EXT2_FLAG_IMAGE_FILE) { retval = ext2fs_get_mem(sizeof(struct ext2_image_hdr), &fs->image_header); if (retval) goto cleanup; retval = io_channel_read_blk(fs->io, 0, -(int)sizeof(struct ext2_image_hdr), fs->image_header); if (retval) goto cleanup; if (fs->image_header->magic_number != EXT2_ET_MAGIC_E2IMAGE) return EXT2_ET_MAGIC_E2IMAGE; superblock = 1; block_size = fs->image_header->fs_blocksize; } /* * If the user specifies a specific block # for the * superblock, then he/she must also specify the block size! * Otherwise, read the master superblock located at offset * SUPERBLOCK_OFFSET from the start of the partition. * * Note: we only save a backup copy of the superblock if we * are reading the superblock from the primary superblock location. */ if (superblock) { if (!block_size) { retval = EXT2_ET_INVALID_ARGUMENT; goto cleanup; } io_channel_set_blksize(fs->io, block_size); group_block = superblock; fs->orig_super = 0; } else { io_channel_set_blksize(fs->io, SUPERBLOCK_OFFSET); superblock = 1; group_block = 0; retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &fs->orig_super); if (retval) goto cleanup; } retval = io_channel_read_blk(fs->io, superblock, -SUPERBLOCK_SIZE, fs->super); if (retval) goto cleanup; if (fs->orig_super) memcpy(fs->orig_super, fs->super, SUPERBLOCK_SIZE); #ifdef WORDS_BIGENDIAN fs->flags |= EXT2_FLAG_SWAP_BYTES; ext2fs_swap_super(fs->super); #else if (fs->flags & EXT2_FLAG_SWAP_BYTES) { retval = EXT2_ET_UNIMPLEMENTED; goto cleanup; } #endif if (fs->super->s_magic != EXT2_SUPER_MAGIC) { retval = EXT2_ET_BAD_MAGIC; goto cleanup; } if (fs->super->s_rev_level > EXT2_LIB_CURRENT_REV) { retval = EXT2_ET_REV_TOO_HIGH; goto cleanup; } /* * Check for feature set incompatibility */ if (!(flags & EXT2_FLAG_FORCE)) { features = fs->super->s_feature_incompat; #ifdef EXT2_LIB_SOFTSUPP_INCOMPAT if (flags & EXT2_FLAG_SOFTSUPP_FEATURES) features &= !EXT2_LIB_SOFTSUPP_INCOMPAT; #endif if (features & ~EXT2_LIB_FEATURE_INCOMPAT_SUPP) { retval = EXT2_ET_UNSUPP_FEATURE; goto cleanup; } features = fs->super->s_feature_ro_compat; #ifdef EXT2_LIB_SOFTSUPP_RO_COMPAT if (flags & EXT2_FLAG_SOFTSUPP_FEATURES) features &= !EXT2_LIB_SOFTSUPP_RO_COMPAT; #endif if ((flags & EXT2_FLAG_RW) && (features & ~EXT2_LIB_FEATURE_RO_COMPAT_SUPP)) { retval = EXT2_ET_RO_UNSUPP_FEATURE; goto cleanup; } if (!(flags & EXT2_FLAG_JOURNAL_DEV_OK) && (fs->super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)) { retval = EXT2_ET_UNSUPP_FEATURE; goto cleanup; } } if ((fs->super->s_log_block_size + EXT2_MIN_BLOCK_LOG_SIZE) > EXT2_MAX_BLOCK_LOG_SIZE) { retval = EXT2_ET_CORRUPT_SUPERBLOCK; goto cleanup; } fs->blocksize = EXT2_BLOCK_SIZE(fs->super); if (EXT2_INODE_SIZE(fs->super) < EXT2_GOOD_OLD_INODE_SIZE) { retval = EXT2_ET_CORRUPT_SUPERBLOCK; goto cleanup; } fs->fragsize = EXT2_FRAG_SIZE(fs->super); fs->inode_blocks_per_group = ((EXT2_INODES_PER_GROUP(fs->super) * EXT2_INODE_SIZE(fs->super) + EXT2_BLOCK_SIZE(fs->super) - 1) / EXT2_BLOCK_SIZE(fs->super)); if (block_size) { if (block_size != fs->blocksize) { retval = EXT2_ET_UNEXPECTED_BLOCK_SIZE; goto cleanup; } } /* * Set the blocksize to the filesystem's blocksize. */ io_channel_set_blksize(fs->io, fs->blocksize); /* * If this is an external journal device, don't try to read * the group descriptors, because they're not there. */ if (fs->super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) { fs->group_desc_count = 0; *ret_fs = fs; return 0; } if (EXT2_INODES_PER_GROUP(fs->super) == 0) { retval = EXT2_ET_CORRUPT_SUPERBLOCK; goto cleanup; } /* * Read group descriptors */ blocks_per_group = EXT2_BLOCKS_PER_GROUP(fs->super); if (blocks_per_group == 0 || blocks_per_group > EXT2_MAX_BLOCKS_PER_GROUP(fs->super) || fs->inode_blocks_per_group > EXT2_MAX_INODES_PER_GROUP(fs->super) || EXT2_DESC_PER_BLOCK(fs->super) == 0 || fs->super->s_first_data_block >= fs->super->s_blocks_count) { retval = EXT2_ET_CORRUPT_SUPERBLOCK; goto cleanup; } fs->group_desc_count = ext2fs_div_ceil(fs->super->s_blocks_count - fs->super->s_first_data_block, blocks_per_group); if (fs->group_desc_count * EXT2_INODES_PER_GROUP(fs->super) != fs->super->s_inodes_count) { retval = EXT2_ET_CORRUPT_SUPERBLOCK; goto cleanup; } fs->desc_blocks = ext2fs_div_ceil(fs->group_desc_count, EXT2_DESC_PER_BLOCK(fs->super)); retval = ext2fs_get_array(fs->desc_blocks, fs->blocksize, &fs->group_desc); if (retval) goto cleanup; if (!group_block) group_block = fs->super->s_first_data_block; dest = (char *) fs->group_desc; groups_per_block = EXT2_DESC_PER_BLOCK(fs->super); if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) first_meta_bg = fs->super->s_first_meta_bg; else first_meta_bg = fs->desc_blocks; if (first_meta_bg) { retval = io_channel_read_blk(fs->io, group_block+1, first_meta_bg, dest); if (retval) goto cleanup; #ifdef WORDS_BIGENDIAN gdp = (struct ext2_group_desc *) dest; for (j=0; j < groups_per_block*first_meta_bg; j++) ext2fs_swap_group_desc(gdp++); #endif dest += fs->blocksize*first_meta_bg; } for (i=first_meta_bg ; i < fs->desc_blocks; i++) { blk = ext2fs_descriptor_block_loc(fs, group_block, i); retval = io_channel_read_blk(fs->io, blk, 1, dest); if (retval) goto cleanup; #ifdef WORDS_BIGENDIAN gdp = (struct ext2_group_desc *) dest; for (j=0; j < groups_per_block; j++) ext2fs_swap_group_desc(gdp++); #endif dest += fs->blocksize; } fs->stride = fs->super->s_raid_stride; /* * If recovery is from backup superblock, Clear _UNININT flags & * reset bg_itable_unused to zero */ if (superblock > 1 && EXT2_HAS_RO_COMPAT_FEATURE(fs->super, EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) { struct ext2_group_desc *gd; for (i = 0, gd = fs->group_desc; i < fs->group_desc_count; i++, gd++) { gd->bg_flags &= ~EXT2_BG_BLOCK_UNINIT; gd->bg_flags &= ~EXT2_BG_INODE_UNINIT; gd->bg_itable_unused = 0; } ext2fs_mark_super_dirty(fs); } fs->flags &= ~EXT2_FLAG_NOFREE_ON_ERROR; *ret_fs = fs; return 0; cleanup: if (flags & EXT2_FLAG_NOFREE_ON_ERROR) *ret_fs = fs; else ext2fs_free(fs); return retval; } /* * Set/get the filesystem data I/O channel. * * These functions are only valid if EXT2_FLAG_IMAGE_FILE is true. */ errcode_t ext2fs_get_data_io(ext2_filsys fs, io_channel *old_io) { if ((fs->flags & EXT2_FLAG_IMAGE_FILE) == 0) return EXT2_ET_NOT_IMAGE_FILE; if (old_io) { *old_io = (fs->image_io == fs->io) ? 0 : fs->io; } return 0; } errcode_t ext2fs_set_data_io(ext2_filsys fs, io_channel new_io) { if ((fs->flags & EXT2_FLAG_IMAGE_FILE) == 0) return EXT2_ET_NOT_IMAGE_FILE; fs->io = new_io ? new_io : fs->image_io; return 0; } errcode_t ext2fs_rewrite_to_io(ext2_filsys fs, io_channel new_io) { if ((fs->flags & EXT2_FLAG_IMAGE_FILE) == 0) return EXT2_ET_NOT_IMAGE_FILE; fs->io = fs->image_io = new_io; fs->flags |= EXT2_FLAG_DIRTY | EXT2_FLAG_RW | EXT2_FLAG_BB_DIRTY | EXT2_FLAG_IB_DIRTY; fs->flags &= ~EXT2_FLAG_IMAGE_FILE; return 0; } e2fsprogs-1.41.14/lib/ext2fs/jfs_user.h0000644031104000366760000000017311374366235015617 0ustar tytso#ifndef _JFS_USER_H #define _JFS_USER_H typedef unsigned short kdev_t; #include "kernel-jbd.h" #endif /* _JFS_USER_H */ e2fsprogs-1.41.14/lib/ext2fs/undo_io.c0000644031104000116100000003417111504417000016060 0ustar tytsoeng/* * undo_io.c --- This is the undo io manager that copies the old data that * copies the old data being overwritten into a tdb database * * Copyright IBM Corporation, 2007 * Author Aneesh Kumar K.V * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #define _LARGEFILE_SOURCE #define _LARGEFILE64_SOURCE #include #include #if HAVE_UNISTD_H #include #endif #if HAVE_ERRNO_H #include #endif #include #include #ifdef __linux__ #include #endif #if HAVE_SYS_STAT_H #include #endif #if HAVE_SYS_TYPES_H #include #endif #if HAVE_SYS_RESOURCE_H #include #endif #include "tdb.h" #include "ext2_fs.h" #include "ext2fs.h" #ifdef __GNUC__ #define ATTR(x) __attribute__(x) #else #define ATTR(x) #endif /* * For checking structure magic numbers... */ #define EXT2_CHECK_MAGIC(struct, code) \ if ((struct)->magic != (code)) return (code) struct undo_private_data { int magic; TDB_CONTEXT *tdb; char *tdb_file; /* The backing io channel */ io_channel real; int tdb_data_size; int tdb_written; /* to support offset in unix I/O manager */ ext2_loff_t offset; }; static errcode_t undo_open(const char *name, int flags, io_channel *channel); static errcode_t undo_close(io_channel channel); static errcode_t undo_set_blksize(io_channel channel, int blksize); static errcode_t undo_read_blk(io_channel channel, unsigned long block, int count, void *data); static errcode_t undo_write_blk(io_channel channel, unsigned long block, int count, const void *data); static errcode_t undo_flush(io_channel channel); static errcode_t undo_write_byte(io_channel channel, unsigned long offset, int size, const void *data); static errcode_t undo_set_option(io_channel channel, const char *option, const char *arg); static struct struct_io_manager struct_undo_manager = { EXT2_ET_MAGIC_IO_MANAGER, "Undo I/O Manager", undo_open, undo_close, undo_set_blksize, undo_read_blk, undo_write_blk, undo_flush, undo_write_byte, undo_set_option }; io_manager undo_io_manager = &struct_undo_manager; static io_manager undo_io_backing_manager ; static char *tdb_file; static int actual_size; static unsigned char mtime_key[] = "filesystem MTIME"; static unsigned char blksize_key[] = "filesystem BLKSIZE"; static unsigned char uuid_key[] = "filesystem UUID"; errcode_t set_undo_io_backing_manager(io_manager manager) { /* * We may want to do some validation later */ undo_io_backing_manager = manager; return 0; } errcode_t set_undo_io_backup_file(char *file_name) { tdb_file = strdup(file_name); if (tdb_file == NULL) { return EXT2_ET_NO_MEMORY; } return 0; } static errcode_t write_file_system_identity(io_channel undo_channel, TDB_CONTEXT *tdb) { errcode_t retval; struct ext2_super_block super; TDB_DATA tdb_key, tdb_data; struct undo_private_data *data; io_channel channel; int block_size ; data = (struct undo_private_data *) undo_channel->private_data; channel = data->real; block_size = channel->block_size; io_channel_set_blksize(channel, SUPERBLOCK_OFFSET); retval = io_channel_read_blk(channel, 1, -SUPERBLOCK_SIZE, &super); if (retval) goto err_out; /* Write to tdb file in the file system byte order */ tdb_key.dptr = mtime_key; tdb_key.dsize = sizeof(mtime_key); tdb_data.dptr = (unsigned char *) &(super.s_mtime); tdb_data.dsize = sizeof(super.s_mtime); retval = tdb_store(tdb, tdb_key, tdb_data, TDB_INSERT); if (retval == -1) { retval = EXT2_ET_TDB_SUCCESS + tdb_error(tdb); goto err_out; } tdb_key.dptr = uuid_key; tdb_key.dsize = sizeof(uuid_key); tdb_data.dptr = (unsigned char *)&(super.s_uuid); tdb_data.dsize = sizeof(super.s_uuid); retval = tdb_store(tdb, tdb_key, tdb_data, TDB_INSERT); if (retval == -1) { retval = EXT2_ET_TDB_SUCCESS + tdb_error(tdb); } err_out: io_channel_set_blksize(channel, block_size); return retval; } static errcode_t write_block_size(TDB_CONTEXT *tdb, int block_size) { errcode_t retval; TDB_DATA tdb_key, tdb_data; tdb_key.dptr = blksize_key; tdb_key.dsize = sizeof(blksize_key); tdb_data.dptr = (unsigned char *)&(block_size); tdb_data.dsize = sizeof(block_size); retval = tdb_store(tdb, tdb_key, tdb_data, TDB_INSERT); if (retval == -1) { retval = EXT2_ET_TDB_SUCCESS + tdb_error(tdb); } return retval; } static errcode_t undo_write_tdb(io_channel channel, unsigned long block, int count) { int size, sz; unsigned long block_num, backing_blk_num; errcode_t retval = 0; ext2_loff_t offset; struct undo_private_data *data; TDB_DATA tdb_key, tdb_data; unsigned char *read_ptr; unsigned long end_block; data = (struct undo_private_data *) channel->private_data; if (data->tdb == NULL) { /* * Transaction database not initialized */ return 0; } if (count == 1) size = channel->block_size; else { if (count < 0) size = -count; else size = count * channel->block_size; } /* * Data is stored in tdb database as blocks of tdb_data_size size * This helps in efficient lookup further. * * We divide the disk to blocks of tdb_data_size. */ offset = (block * channel->block_size) + data->offset ; block_num = offset / data->tdb_data_size; end_block = (offset + size) / data->tdb_data_size; tdb_transaction_start(data->tdb); while (block_num <= end_block ) { tdb_key.dptr = (unsigned char *)&block_num; tdb_key.dsize = sizeof(block_num); /* * Check if we have the record already */ if (tdb_exists(data->tdb, tdb_key)) { /* Try the next block */ block_num++; continue; } /* * Read one block using the backing I/O manager * The backing I/O manager block size may be * different from the tdb_data_size. * Also we need to recalcuate the block number with respect * to the backing I/O manager. */ offset = block_num * data->tdb_data_size; backing_blk_num = (offset - data->offset) / channel->block_size; count = data->tdb_data_size + ((offset - data->offset) % channel->block_size); retval = ext2fs_get_mem(count, &read_ptr); if (retval) { tdb_transaction_cancel(data->tdb); return retval; } memset(read_ptr, 0, count); actual_size = 0; if ((count % channel->block_size) == 0) sz = count / channel->block_size; else sz = -count; retval = io_channel_read_blk(data->real, backing_blk_num, sz, read_ptr); if (retval) { if (retval != EXT2_ET_SHORT_READ) { free(read_ptr); tdb_transaction_cancel(data->tdb); return retval; } /* * short read so update the record size * accordingly */ tdb_data.dsize = actual_size; } else { tdb_data.dsize = data->tdb_data_size; } tdb_data.dptr = read_ptr + ((offset - data->offset) % channel->block_size); #ifdef DEBUG printf("Printing with key %ld data %x and size %d\n", block_num, tdb_data.dptr, tdb_data.dsize); #endif if (!data->tdb_written) { data->tdb_written = 1; /* Write the blocksize to tdb file */ retval = write_block_size(data->tdb, data->tdb_data_size); if (retval) { tdb_transaction_cancel(data->tdb); retval = EXT2_ET_TDB_ERR_IO; free(read_ptr); return retval; } } retval = tdb_store(data->tdb, tdb_key, tdb_data, TDB_INSERT); if (retval == -1) { /* * TDB_ERR_EXISTS cannot happen because we * have already verified it doesn't exist */ tdb_transaction_cancel(data->tdb); retval = EXT2_ET_TDB_ERR_IO; free(read_ptr); return retval; } free(read_ptr); /* Next block */ block_num++; } tdb_transaction_commit(data->tdb); return retval; } static errcode_t undo_io_read_error(io_channel channel ATTR((unused)), unsigned long block ATTR((unused)), int count ATTR((unused)), void *data ATTR((unused)), size_t size ATTR((unused)), int actual, errcode_t error ATTR((unused))) { actual_size = actual; return error; } static void undo_err_handler_init(io_channel channel) { channel->read_error = undo_io_read_error; } static errcode_t undo_open(const char *name, int flags, io_channel *channel) { io_channel io = NULL; struct undo_private_data *data = NULL; errcode_t retval; if (name == 0) return EXT2_ET_BAD_DEVICE_NAME; retval = ext2fs_get_mem(sizeof(struct struct_io_channel), &io); if (retval) return retval; memset(io, 0, sizeof(struct struct_io_channel)); io->magic = EXT2_ET_MAGIC_IO_CHANNEL; retval = ext2fs_get_mem(sizeof(struct undo_private_data), &data); if (retval) goto cleanup; io->manager = undo_io_manager; retval = ext2fs_get_mem(strlen(name)+1, &io->name); if (retval) goto cleanup; strcpy(io->name, name); io->private_data = data; io->block_size = 1024; io->read_error = 0; io->write_error = 0; io->refcount = 1; memset(data, 0, sizeof(struct undo_private_data)); data->magic = EXT2_ET_MAGIC_UNIX_IO_CHANNEL; if (undo_io_backing_manager) { retval = undo_io_backing_manager->open(name, flags, &data->real); if (retval) goto cleanup; } else { data->real = 0; } /* setup the tdb file */ data->tdb = tdb_open(tdb_file, 0, TDB_CLEAR_IF_FIRST, O_RDWR | O_CREAT | O_TRUNC | O_EXCL, 0600); if (!data->tdb) { retval = errno; goto cleanup; } /* * setup err handler for read so that we know * when the backing manager fails do short read */ undo_err_handler_init(data->real); *channel = io; return 0; cleanup: if (data->real) io_channel_close(data->real); if (data) ext2fs_free_mem(&data); if (io) ext2fs_free_mem(&io); return retval; } static errcode_t undo_close(io_channel channel) { struct undo_private_data *data; errcode_t retval = 0; EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); data = (struct undo_private_data *) channel->private_data; EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); if (--channel->refcount > 0) return 0; /* Before closing write the file system identity */ retval = write_file_system_identity(channel, data->tdb); if (retval) return retval; if (data->real) retval = io_channel_close(data->real); if (data->tdb) tdb_close(data->tdb); ext2fs_free_mem(&channel->private_data); if (channel->name) ext2fs_free_mem(&channel->name); ext2fs_free_mem(&channel); return retval; } static errcode_t undo_set_blksize(io_channel channel, int blksize) { struct undo_private_data *data; errcode_t retval = 0; EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); data = (struct undo_private_data *) channel->private_data; EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); if (data->real) retval = io_channel_set_blksize(data->real, blksize); /* * Set the block size used for tdb */ if (!data->tdb_data_size) { data->tdb_data_size = blksize; } channel->block_size = blksize; return retval; } static errcode_t undo_read_blk(io_channel channel, unsigned long block, int count, void *buf) { errcode_t retval = 0; struct undo_private_data *data; EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); data = (struct undo_private_data *) channel->private_data; EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); if (data->real) retval = io_channel_read_blk(data->real, block, count, buf); return retval; } static errcode_t undo_write_blk(io_channel channel, unsigned long block, int count, const void *buf) { struct undo_private_data *data; errcode_t retval = 0; EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); data = (struct undo_private_data *) channel->private_data; EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); /* * First write the existing content into database */ retval = undo_write_tdb(channel, block, count); if (retval) return retval; if (data->real) retval = io_channel_write_blk(data->real, block, count, buf); return retval; } static errcode_t undo_write_byte(io_channel channel, unsigned long offset, int size, const void *buf) { struct undo_private_data *data; errcode_t retval = 0; ext2_loff_t location; unsigned long blk_num, count;; EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); data = (struct undo_private_data *) channel->private_data; EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); location = offset + data->offset; blk_num = location/channel->block_size; /* * the size specified may spread across multiple blocks * also make sure we account for the fact that block start * offset for tdb is different from the backing I/O manager * due to possible different block size */ count = (size + (location % channel->block_size) + channel->block_size -1)/channel->block_size; retval = undo_write_tdb(channel, blk_num, count); if (retval) return retval; if (data->real && data->real->manager->write_byte) retval = io_channel_write_byte(data->real, offset, size, buf); return retval; } /* * Flush data buffers to disk. */ static errcode_t undo_flush(io_channel channel) { errcode_t retval = 0; struct undo_private_data *data; EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); data = (struct undo_private_data *) channel->private_data; EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); if (data->real) retval = io_channel_flush(data->real); return retval; } static errcode_t undo_set_option(io_channel channel, const char *option, const char *arg) { errcode_t retval = 0; struct undo_private_data *data; unsigned long tmp; char *end; EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); data = (struct undo_private_data *) channel->private_data; EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); if (!strcmp(option, "tdb_data_size")) { if (!arg) return EXT2_ET_INVALID_ARGUMENT; tmp = strtoul(arg, &end, 0); if (*end) return EXT2_ET_INVALID_ARGUMENT; if (!data->tdb_data_size || !data->tdb_written) { data->tdb_data_size = tmp; } return 0; } /* * Need to support offset option to work with * Unix I/O manager */ if (data->real && data->real->manager->set_option) { retval = data->real->manager->set_option(data->real, option, arg); } if (!retval && !strcmp(option, "offset")) { if (!arg) return EXT2_ET_INVALID_ARGUMENT; tmp = strtoul(arg, &end, 0); if (*end) return EXT2_ET_INVALID_ARGUMENT; data->offset = tmp; } return retval; } e2fsprogs-1.41.14/lib/ext2fs/bitmaps.c0000644031104000116100000001073211504417000016060 0ustar tytsoeng/* * bitmaps.c --- routines to read, write, and manipulate the inode and * block bitmaps. * * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #include #include #if HAVE_SYS_STAT_H #include #endif #if HAVE_SYS_TYPES_H #include #endif #include "ext2_fs.h" #include "ext2fs.h" void ext2fs_free_inode_bitmap(ext2fs_inode_bitmap bitmap) { ext2fs_free_generic_bitmap(bitmap); } void ext2fs_free_block_bitmap(ext2fs_block_bitmap bitmap) { ext2fs_free_generic_bitmap(bitmap); } errcode_t ext2fs_copy_bitmap(ext2fs_generic_bitmap src, ext2fs_generic_bitmap *dest) { return (ext2fs_copy_generic_bitmap(src, dest)); } void ext2fs_set_bitmap_padding(ext2fs_generic_bitmap map) { ext2fs_set_generic_bitmap_padding(map); } errcode_t ext2fs_allocate_inode_bitmap(ext2_filsys fs, const char *descr, ext2fs_inode_bitmap *ret) { __u32 start, end, real_end; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); fs->write_bitmaps = ext2fs_write_bitmaps; start = 1; end = fs->super->s_inodes_count; real_end = (EXT2_INODES_PER_GROUP(fs->super) * fs->group_desc_count); return (ext2fs_make_generic_bitmap(EXT2_ET_MAGIC_INODE_BITMAP, fs, start, end, real_end, descr, 0, ret)); } errcode_t ext2fs_allocate_block_bitmap(ext2_filsys fs, const char *descr, ext2fs_block_bitmap *ret) { __u32 start, end, real_end; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); fs->write_bitmaps = ext2fs_write_bitmaps; start = fs->super->s_first_data_block; end = fs->super->s_blocks_count-1; real_end = (EXT2_BLOCKS_PER_GROUP(fs->super) * fs->group_desc_count)-1 + start; return (ext2fs_make_generic_bitmap(EXT2_ET_MAGIC_BLOCK_BITMAP, fs, start, end, real_end, descr, 0, ret)); } errcode_t ext2fs_fudge_inode_bitmap_end(ext2fs_inode_bitmap bitmap, ext2_ino_t end, ext2_ino_t *oend) { return (ext2fs_fudge_generic_bitmap_end(bitmap, EXT2_ET_MAGIC_INODE_BITMAP, EXT2_ET_FUDGE_INODE_BITMAP_END, end, oend)); } errcode_t ext2fs_fudge_block_bitmap_end(ext2fs_block_bitmap bitmap, blk_t end, blk_t *oend) { return (ext2fs_fudge_generic_bitmap_end(bitmap, EXT2_ET_MAGIC_BLOCK_BITMAP, EXT2_ET_FUDGE_BLOCK_BITMAP_END, end, oend)); } void ext2fs_clear_inode_bitmap(ext2fs_inode_bitmap bitmap) { ext2fs_clear_generic_bitmap(bitmap); } void ext2fs_clear_block_bitmap(ext2fs_block_bitmap bitmap) { ext2fs_clear_generic_bitmap(bitmap); } errcode_t ext2fs_resize_inode_bitmap(__u32 new_end, __u32 new_real_end, ext2fs_inode_bitmap bmap) { return (ext2fs_resize_generic_bitmap(EXT2_ET_MAGIC_INODE_BITMAP, new_end, new_real_end, bmap)); } errcode_t ext2fs_resize_block_bitmap(__u32 new_end, __u32 new_real_end, ext2fs_block_bitmap bmap) { return (ext2fs_resize_generic_bitmap(EXT2_ET_MAGIC_BLOCK_BITMAP, new_end, new_real_end, bmap)); } errcode_t ext2fs_compare_block_bitmap(ext2fs_block_bitmap bm1, ext2fs_block_bitmap bm2) { return (ext2fs_compare_generic_bitmap(EXT2_ET_MAGIC_BLOCK_BITMAP, EXT2_ET_NEQ_BLOCK_BITMAP, bm1, bm2)); } errcode_t ext2fs_compare_inode_bitmap(ext2fs_inode_bitmap bm1, ext2fs_inode_bitmap bm2) { return (ext2fs_compare_generic_bitmap(EXT2_ET_MAGIC_INODE_BITMAP, EXT2_ET_NEQ_INODE_BITMAP, bm1, bm2)); } errcode_t ext2fs_set_inode_bitmap_range(ext2fs_inode_bitmap bmap, ext2_ino_t start, unsigned int num, void *in) { return (ext2fs_set_generic_bitmap_range(bmap, EXT2_ET_MAGIC_INODE_BITMAP, start, num, in)); } errcode_t ext2fs_get_inode_bitmap_range(ext2fs_inode_bitmap bmap, ext2_ino_t start, unsigned int num, void *out) { return (ext2fs_get_generic_bitmap_range(bmap, EXT2_ET_MAGIC_INODE_BITMAP, start, num, out)); } errcode_t ext2fs_set_block_bitmap_range(ext2fs_block_bitmap bmap, blk_t start, unsigned int num, void *in) { return (ext2fs_set_generic_bitmap_range(bmap, EXT2_ET_MAGIC_BLOCK_BITMAP, start, num, in)); } errcode_t ext2fs_get_block_bitmap_range(ext2fs_block_bitmap bmap, blk_t start, unsigned int num, void *out) { return (ext2fs_get_generic_bitmap_range(bmap, EXT2_ET_MAGIC_BLOCK_BITMAP, start, num, out)); } e2fsprogs-1.41.14/lib/ext2fs/ext2_io.h0000644031104000116100000000767411504417000016012 0ustar tytsoeng/* * io.h --- the I/O manager abstraction * * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #ifndef _EXT2FS_EXT2_IO_H #define _EXT2FS_EXT2_IO_H /* * ext2_loff_t is defined here since unix_io.c needs it. */ #if defined(__GNUC__) || defined(HAS_LONG_LONG) typedef long long ext2_loff_t; #else typedef long ext2_loff_t; #endif /* llseek.c */ ext2_loff_t ext2fs_llseek (int, ext2_loff_t, int); typedef struct struct_io_manager *io_manager; typedef struct struct_io_channel *io_channel; typedef struct struct_io_stats *io_stats; #define CHANNEL_FLAGS_WRITETHROUGH 0x01 struct struct_io_channel { errcode_t magic; io_manager manager; char *name; int block_size; errcode_t (*read_error)(io_channel channel, unsigned long block, int count, void *data, size_t size, int actual_bytes_read, errcode_t error); errcode_t (*write_error)(io_channel channel, unsigned long block, int count, const void *data, size_t size, int actual_bytes_written, errcode_t error); int refcount; int flags; long reserved[14]; void *private_data; void *app_data; }; struct struct_io_stats { int num_fields; int reserved; unsigned long long bytes_read; unsigned long long bytes_written; }; struct struct_io_manager { errcode_t magic; const char *name; errcode_t (*open)(const char *name, int flags, io_channel *channel); errcode_t (*close)(io_channel channel); errcode_t (*set_blksize)(io_channel channel, int blksize); errcode_t (*read_blk)(io_channel channel, unsigned long block, int count, void *data); errcode_t (*write_blk)(io_channel channel, unsigned long block, int count, const void *data); errcode_t (*flush)(io_channel channel); errcode_t (*write_byte)(io_channel channel, unsigned long offset, int count, const void *data); errcode_t (*set_option)(io_channel channel, const char *option, const char *arg); errcode_t (*get_stats)(io_channel channel, io_stats *io_stats); errcode_t (*read_blk64)(io_channel channel, unsigned long long block, int count, void *data); errcode_t (*write_blk64)(io_channel channel, unsigned long long block, int count, const void *data); long reserved[16]; }; #define IO_FLAG_RW 0x0001 #define IO_FLAG_EXCLUSIVE 0x0002 #define IO_FLAG_DIRECT_IO 0x0004 /* * Convenience functions.... */ #define io_channel_close(c) ((c)->manager->close((c))) #define io_channel_set_blksize(c,s) ((c)->manager->set_blksize((c),s)) #define io_channel_read_blk(c,b,n,d) ((c)->manager->read_blk((c),b,n,d)) #define io_channel_write_blk(c,b,n,d) ((c)->manager->write_blk((c),b,n,d)) #define io_channel_flush(c) ((c)->manager->flush((c))) #define io_channel_bumpcount(c) ((c)->refcount++) /* io_manager.c */ extern errcode_t io_channel_set_options(io_channel channel, const char *options); extern errcode_t io_channel_write_byte(io_channel channel, unsigned long offset, int count, const void *data); extern errcode_t io_channel_read_blk64(io_channel channel, unsigned long long block, int count, void *data); extern errcode_t io_channel_write_blk64(io_channel channel, unsigned long long block, int count, const void *data); /* unix_io.c */ extern io_manager unix_io_manager; /* undo_io.c */ extern io_manager undo_io_manager; extern errcode_t set_undo_io_backing_manager(io_manager manager); extern errcode_t set_undo_io_backup_file(char *file_name); /* test_io.c */ extern io_manager test_io_manager, test_io_backing_manager; extern void (*test_io_cb_read_blk) (unsigned long block, int count, errcode_t err); extern void (*test_io_cb_write_blk) (unsigned long block, int count, errcode_t err); extern void (*test_io_cb_set_blksize) (int blksize, errcode_t err); #endif /* _EXT2FS_EXT2_IO_H */ e2fsprogs-1.41.14/lib/ext2fs/res_gdt.c0000644031104000116100000001346011504417000016051 0ustar tytsoeng/* * res_gdt.c --- reserve blocks for growing the group descriptor table * during online resizing. * * Copyright (C) 2002 Andreas Dilger * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #include #include #include #include "ext2_fs.h" #include "ext2fs.h" /* * Iterate through the groups which hold BACKUP superblock/GDT copies in an * ext3 filesystem. The counters should be initialized to 1, 5, and 7 before * calling this for the first time. In a sparse filesystem it will be the * sequence of powers of 3, 5, and 7: 1, 3, 5, 7, 9, 25, 27, 49, 81, ... * For a non-sparse filesystem it will be every group: 1, 2, 3, 4, ... */ static unsigned int list_backups(ext2_filsys fs, unsigned int *three, unsigned int *five, unsigned int *seven) { unsigned int *min = three; int mult = 3; unsigned int ret; if (!(fs->super->s_feature_ro_compat & EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)) { ret = *min; *min += 1; return ret; } if (*five < *min) { min = five; mult = 5; } if (*seven < *min) { min = seven; mult = 7; } ret = *min; *min *= mult; return ret; } /* * This code assumes that the reserved blocks have already been marked in-use * during ext2fs_initialize(), so that they are not allocated for other * uses before we can add them to the resize inode (which has to come * after the creation of the inode table). */ errcode_t ext2fs_create_resize_inode(ext2_filsys fs) { errcode_t retval, retval2; struct ext2_super_block *sb; struct ext2_inode inode; __u32 *dindir_buf, *gdt_buf; unsigned long long apb, inode_size; blk_t dindir_blk, rsv_off, gdt_off, gdt_blk; int dindir_dirty = 0, inode_dirty = 0; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); sb = fs->super; retval = ext2fs_get_array(2, fs->blocksize, &dindir_buf); if (retval) goto out_free; gdt_buf = (__u32 *)((char *)dindir_buf + fs->blocksize); retval = ext2fs_read_inode(fs, EXT2_RESIZE_INO, &inode); if (retval) goto out_free; /* Maximum possible file size (we donly use the dindirect blocks) */ apb = EXT2_ADDR_PER_BLOCK(sb); if ((dindir_blk = inode.i_block[EXT2_DIND_BLOCK])) { #ifdef RES_GDT_DEBUG printf("reading GDT dindir %u\n", dindir_blk); #endif retval = ext2fs_read_ind_block(fs, dindir_blk, dindir_buf); if (retval) goto out_inode; } else { blk_t goal = sb->s_first_data_block + fs->desc_blocks + sb->s_reserved_gdt_blocks + 2 + fs->inode_blocks_per_group; retval = ext2fs_alloc_block(fs, goal, 0, &dindir_blk); if (retval) goto out_free; inode.i_mode = LINUX_S_IFREG | 0600; inode.i_links_count = 1; inode.i_block[EXT2_DIND_BLOCK] = dindir_blk; ext2fs_iblk_set(fs, &inode, 1); memset(dindir_buf, 0, fs->blocksize); #ifdef RES_GDT_DEBUG printf("allocated GDT dindir %u\n", dindir_blk); #endif dindir_dirty = inode_dirty = 1; inode_size = apb*apb + apb + EXT2_NDIR_BLOCKS; inode_size *= fs->blocksize; inode.i_size = inode_size & 0xFFFFFFFF; inode.i_size_high = (inode_size >> 32) & 0xFFFFFFFF; if(inode.i_size_high) { sb->s_feature_ro_compat |= EXT2_FEATURE_RO_COMPAT_LARGE_FILE; } inode.i_ctime = fs->now ? fs->now : time(0); } for (rsv_off = 0, gdt_off = fs->desc_blocks, gdt_blk = sb->s_first_data_block + 1 + fs->desc_blocks; rsv_off < sb->s_reserved_gdt_blocks; rsv_off++, gdt_off++, gdt_blk++) { unsigned int three = 1, five = 5, seven = 7; unsigned int grp, last = 0; int gdt_dirty = 0; gdt_off %= apb; if (!dindir_buf[gdt_off]) { /* FIXME XXX XXX blk_t new_blk; retval = ext2fs_new_block(fs, gdt_blk, 0, &new_blk); if (retval) goto out_free; if (new_blk != gdt_blk) { // XXX free block retval = -1; // XXX } */ gdt_dirty = dindir_dirty = inode_dirty = 1; memset(gdt_buf, 0, fs->blocksize); dindir_buf[gdt_off] = gdt_blk; ext2fs_iblk_add_blocks(fs, &inode, 1); #ifdef RES_GDT_DEBUG printf("added primary GDT block %u at %u[%u]\n", gdt_blk, dindir_blk, gdt_off); #endif } else if (dindir_buf[gdt_off] == gdt_blk) { #ifdef RES_GDT_DEBUG printf("reading primary GDT block %u\n", gdt_blk); #endif retval = ext2fs_read_ind_block(fs, gdt_blk, gdt_buf); if (retval) goto out_dindir; } else { #ifdef RES_GDT_DEBUG printf("bad primary GDT %u != %u at %u[%u]\n", dindir_buf[gdt_off], gdt_blk,dindir_blk,gdt_off); #endif retval = EXT2_ET_RESIZE_INODE_CORRUPT; goto out_dindir; } while ((grp = list_backups(fs, &three, &five, &seven)) < fs->group_desc_count) { blk_t expect = gdt_blk + grp * sb->s_blocks_per_group; if (!gdt_buf[last]) { #ifdef RES_GDT_DEBUG printf("added backup GDT %u grp %u@%u[%u]\n", expect, grp, gdt_blk, last); #endif gdt_buf[last] = expect; ext2fs_iblk_add_blocks(fs, &inode, 1); gdt_dirty = inode_dirty = 1; } else if (gdt_buf[last] != expect) { #ifdef RES_GDT_DEBUG printf("bad backup GDT %u != %u at %u[%u]\n", gdt_buf[last], expect, gdt_blk, last); #endif retval = EXT2_ET_RESIZE_INODE_CORRUPT; goto out_dindir; } last++; } if (gdt_dirty) { #ifdef RES_GDT_DEBUG printf("writing primary GDT block %u\n", gdt_blk); #endif retval = ext2fs_write_ind_block(fs, gdt_blk, gdt_buf); if (retval) goto out_dindir; } } out_dindir: if (dindir_dirty) { retval2 = ext2fs_write_ind_block(fs, dindir_blk, dindir_buf); if (!retval) retval = retval2; } out_inode: #ifdef RES_GDT_DEBUG printf("inode.i_blocks = %u, i_size = %u\n", inode.i_blocks, inode.i_size); #endif if (inode_dirty) { inode.i_atime = inode.i_mtime = fs->now ? fs->now : time(0); retval2 = ext2fs_write_new_inode(fs, EXT2_RESIZE_INO, &inode); if (!retval) retval = retval2; } out_free: ext2fs_free_mem(&dindir_buf); return retval; } e2fsprogs-1.41.14/lib/ext2fs/inline.c0000644031104000366760000000123611405316370015237 0ustar tytso/* * inline.c --- Includes the inlined functions defined in the header * files as standalone functions, in case the application program * is compiled with inlining turned off. * * Copyright (C) 1993, 1994 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #include #include #if HAVE_SYS_STAT_H #include #endif #if HAVE_SYS_TYPES_H #include #endif #include "ext2_fs.h" #define INCLUDE_INLINE_FUNCS #include "ext2fs.h" e2fsprogs-1.41.14/lib/ext2fs/inode_io.c0000644031104000116100000001617411504417000016214 0ustar tytsoeng/* * inode_io.c --- This is allows an inode in an ext2 filesystem image * to be accessed via the I/O manager interface. * * Copyright (C) 2002 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #if HAVE_ERRNO_H #include #endif #include #include "ext2_fs.h" #include "ext2fs.h" /* * For checking structure magic numbers... */ #define EXT2_CHECK_MAGIC(struct, code) \ if ((struct)->magic != (code)) return (code) struct inode_private_data { int magic; char name[32]; ext2_file_t file; ext2_filsys fs; ext2_ino_t ino; struct ext2_inode inode; int flags; struct inode_private_data *next; }; #define CHANNEL_HAS_INODE 0x8000 static struct inode_private_data *top_intern; static int ino_unique = 0; static errcode_t inode_open(const char *name, int flags, io_channel *channel); static errcode_t inode_close(io_channel channel); static errcode_t inode_set_blksize(io_channel channel, int blksize); static errcode_t inode_read_blk(io_channel channel, unsigned long block, int count, void *data); static errcode_t inode_write_blk(io_channel channel, unsigned long block, int count, const void *data); static errcode_t inode_flush(io_channel channel); static errcode_t inode_write_byte(io_channel channel, unsigned long offset, int size, const void *data); static errcode_t inode_read_blk64(io_channel channel, unsigned long long block, int count, void *data); static errcode_t inode_write_blk64(io_channel channel, unsigned long long block, int count, const void *data); static struct struct_io_manager struct_inode_manager = { EXT2_ET_MAGIC_IO_MANAGER, "Inode I/O Manager", inode_open, inode_close, inode_set_blksize, inode_read_blk, inode_write_blk, inode_flush, inode_write_byte, NULL, NULL, inode_read_blk64, inode_write_blk64 }; io_manager inode_io_manager = &struct_inode_manager; errcode_t ext2fs_inode_io_intern2(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode, char **name) { struct inode_private_data *data; errcode_t retval; if ((retval = ext2fs_get_mem(sizeof(struct inode_private_data), &data))) return retval; data->magic = EXT2_ET_MAGIC_INODE_IO_CHANNEL; sprintf(data->name, "%u:%d", ino, ino_unique++); data->file = 0; data->fs = fs; data->ino = ino; data->flags = 0; if (inode) { memcpy(&data->inode, inode, sizeof(struct ext2_inode)); data->flags |= CHANNEL_HAS_INODE; } data->next = top_intern; top_intern = data; *name = data->name; return 0; } errcode_t ext2fs_inode_io_intern(ext2_filsys fs, ext2_ino_t ino, char **name) { return ext2fs_inode_io_intern2(fs, ino, NULL, name); } static errcode_t inode_open(const char *name, int flags, io_channel *channel) { io_channel io = NULL; struct inode_private_data *prev, *data = NULL; errcode_t retval; int open_flags; if (name == 0) return EXT2_ET_BAD_DEVICE_NAME; for (data = top_intern, prev = NULL; data; prev = data, data = data->next) if (strcmp(name, data->name) == 0) break; if (!data) return ENOENT; if (prev) prev->next = data->next; else top_intern = data->next; retval = ext2fs_get_mem(sizeof(struct struct_io_channel), &io); if (retval) goto cleanup; memset(io, 0, sizeof(struct struct_io_channel)); io->magic = EXT2_ET_MAGIC_IO_CHANNEL; io->manager = inode_io_manager; retval = ext2fs_get_mem(strlen(name)+1, &io->name); if (retval) goto cleanup; strcpy(io->name, name); io->private_data = data; io->block_size = 1024; io->read_error = 0; io->write_error = 0; io->refcount = 1; open_flags = (flags & IO_FLAG_RW) ? EXT2_FILE_WRITE : 0; retval = ext2fs_file_open2(data->fs, data->ino, (data->flags & CHANNEL_HAS_INODE) ? &data->inode : 0, open_flags, &data->file); if (retval) goto cleanup; *channel = io; return 0; cleanup: if (data) { ext2fs_free_mem(&data); } if (io) ext2fs_free_mem(&io); return retval; } static errcode_t inode_close(io_channel channel) { struct inode_private_data *data; errcode_t retval = 0; EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); data = (struct inode_private_data *) channel->private_data; EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_INODE_IO_CHANNEL); if (--channel->refcount > 0) return 0; retval = ext2fs_file_close(data->file); ext2fs_free_mem(&channel->private_data); if (channel->name) ext2fs_free_mem(&channel->name); ext2fs_free_mem(&channel); return retval; } static errcode_t inode_set_blksize(io_channel channel, int blksize) { struct inode_private_data *data; EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); data = (struct inode_private_data *) channel->private_data; EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_INODE_IO_CHANNEL); channel->block_size = blksize; return 0; } static errcode_t inode_read_blk64(io_channel channel, unsigned long long block, int count, void *buf) { struct inode_private_data *data; errcode_t retval; EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); data = (struct inode_private_data *) channel->private_data; EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_INODE_IO_CHANNEL); if ((retval = ext2fs_file_lseek(data->file, block * channel->block_size, EXT2_SEEK_SET, 0))) return retval; count = (count < 0) ? -count : (count * channel->block_size); return ext2fs_file_read(data->file, buf, count, 0); } static errcode_t inode_read_blk(io_channel channel, unsigned long block, int count, void *buf) { return inode_read_blk64(channel, block, count, buf); } static errcode_t inode_write_blk64(io_channel channel, unsigned long long block, int count, const void *buf) { struct inode_private_data *data; errcode_t retval; EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); data = (struct inode_private_data *) channel->private_data; EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_INODE_IO_CHANNEL); if ((retval = ext2fs_file_lseek(data->file, block * channel->block_size, EXT2_SEEK_SET, 0))) return retval; count = (count < 0) ? -count : (count * channel->block_size); return ext2fs_file_write(data->file, buf, count, 0); } static errcode_t inode_write_blk(io_channel channel, unsigned long block, int count, const void *buf) { return inode_write_blk64(channel, block, count, buf); } static errcode_t inode_write_byte(io_channel channel, unsigned long offset, int size, const void *buf) { struct inode_private_data *data; errcode_t retval = 0; EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); data = (struct inode_private_data *) channel->private_data; EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_INODE_IO_CHANNEL); if ((retval = ext2fs_file_lseek(data->file, offset, EXT2_SEEK_SET, 0))) return retval; return ext2fs_file_write(data->file, buf, size, 0); } /* * Flush data buffers to disk. */ static errcode_t inode_flush(io_channel channel) { struct inode_private_data *data; EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); data = (struct inode_private_data *) channel->private_data; EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_INODE_IO_CHANNEL); return ext2fs_file_flush(data->file); } e2fsprogs-1.41.14/lib/ext2fs/inode.c0000644031104000116100000005154411504417000015525 0ustar tytsoeng/* * inode.c --- utility routines to read and write inodes * * Copyright (C) 1993, 1994, 1995, 1996, 1997 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #if HAVE_ERRNO_H #include #endif #include #if HAVE_SYS_STAT_H #include #endif #if HAVE_SYS_TYPES_H #include #endif #include "ext2_fs.h" #include "ext2fsP.h" #include "e2image.h" struct ext2_struct_inode_scan { errcode_t magic; ext2_filsys fs; ext2_ino_t current_inode; blk_t current_block; dgrp_t current_group; ext2_ino_t inodes_left; blk_t blocks_left; dgrp_t groups_left; blk_t inode_buffer_blocks; char * inode_buffer; int inode_size; char * ptr; int bytes_left; char *temp_buffer; errcode_t (*done_group)(ext2_filsys fs, ext2_inode_scan scan, dgrp_t group, void * priv_data); void * done_group_data; int bad_block_ptr; int scan_flags; int reserved[6]; }; /* * This routine flushes the icache, if it exists. */ errcode_t ext2fs_flush_icache(ext2_filsys fs) { int i; if (!fs->icache) return 0; for (i=0; i < fs->icache->cache_size; i++) fs->icache->cache[i].ino = 0; fs->icache->buffer_blk = 0; return 0; } static errcode_t create_icache(ext2_filsys fs) { errcode_t retval; if (fs->icache) return 0; retval = ext2fs_get_mem(sizeof(struct ext2_inode_cache), &fs->icache); if (retval) return retval; memset(fs->icache, 0, sizeof(struct ext2_inode_cache)); retval = ext2fs_get_mem(fs->blocksize, &fs->icache->buffer); if (retval) { ext2fs_free_mem(&fs->icache); return retval; } fs->icache->buffer_blk = 0; fs->icache->cache_last = -1; fs->icache->cache_size = 4; fs->icache->refcount = 1; retval = ext2fs_get_array(fs->icache->cache_size, sizeof(struct ext2_inode_cache_ent), &fs->icache->cache); if (retval) { ext2fs_free_mem(&fs->icache->buffer); ext2fs_free_mem(&fs->icache); return retval; } ext2fs_flush_icache(fs); return 0; } errcode_t ext2fs_open_inode_scan(ext2_filsys fs, int buffer_blocks, ext2_inode_scan *ret_scan) { ext2_inode_scan scan; errcode_t retval; errcode_t (*save_get_blocks)(ext2_filsys f, ext2_ino_t ino, blk_t *blocks); EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); /* * If fs->badblocks isn't set, then set it --- since the inode * scanning functions require it. */ if (fs->badblocks == 0) { /* * Temporarly save fs->get_blocks and set it to zero, * for compatibility with old e2fsck's. */ save_get_blocks = fs->get_blocks; fs->get_blocks = 0; retval = ext2fs_read_bb_inode(fs, &fs->badblocks); if (retval && fs->badblocks) { ext2fs_badblocks_list_free(fs->badblocks); fs->badblocks = 0; } fs->get_blocks = save_get_blocks; } retval = ext2fs_get_mem(sizeof(struct ext2_struct_inode_scan), &scan); if (retval) return retval; memset(scan, 0, sizeof(struct ext2_struct_inode_scan)); scan->magic = EXT2_ET_MAGIC_INODE_SCAN; scan->fs = fs; scan->inode_size = EXT2_INODE_SIZE(fs->super); scan->bytes_left = 0; scan->current_group = 0; scan->groups_left = fs->group_desc_count - 1; scan->inode_buffer_blocks = buffer_blocks ? buffer_blocks : 8; scan->current_block = scan->fs-> group_desc[scan->current_group].bg_inode_table; scan->inodes_left = EXT2_INODES_PER_GROUP(scan->fs->super); scan->blocks_left = scan->fs->inode_blocks_per_group; if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super, EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) { scan->inodes_left -= fs->group_desc[scan->current_group].bg_itable_unused; scan->blocks_left = (scan->inodes_left + (fs->blocksize / scan->inode_size - 1)) * scan->inode_size / fs->blocksize; } retval = ext2fs_get_memalign(scan->inode_buffer_blocks * fs->blocksize, fs->blocksize, &scan->inode_buffer); scan->done_group = 0; scan->done_group_data = 0; scan->bad_block_ptr = 0; if (retval) { ext2fs_free_mem(&scan); return retval; } retval = ext2fs_get_mem(scan->inode_size, &scan->temp_buffer); if (retval) { ext2fs_free_mem(&scan->inode_buffer); ext2fs_free_mem(&scan); return retval; } if (scan->fs->badblocks && scan->fs->badblocks->num) scan->scan_flags |= EXT2_SF_CHK_BADBLOCKS; if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super, EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) scan->scan_flags |= EXT2_SF_DO_LAZY; *ret_scan = scan; return 0; } void ext2fs_close_inode_scan(ext2_inode_scan scan) { if (!scan || (scan->magic != EXT2_ET_MAGIC_INODE_SCAN)) return; ext2fs_free_mem(&scan->inode_buffer); scan->inode_buffer = NULL; ext2fs_free_mem(&scan->temp_buffer); scan->temp_buffer = NULL; ext2fs_free_mem(&scan); return; } void ext2fs_set_inode_callback(ext2_inode_scan scan, errcode_t (*done_group)(ext2_filsys fs, ext2_inode_scan scan, dgrp_t group, void * priv_data), void *done_group_data) { if (!scan || (scan->magic != EXT2_ET_MAGIC_INODE_SCAN)) return; scan->done_group = done_group; scan->done_group_data = done_group_data; } int ext2fs_inode_scan_flags(ext2_inode_scan scan, int set_flags, int clear_flags) { int old_flags; if (!scan || (scan->magic != EXT2_ET_MAGIC_INODE_SCAN)) return 0; old_flags = scan->scan_flags; scan->scan_flags &= ~clear_flags; scan->scan_flags |= set_flags; return old_flags; } /* * This function is called by ext2fs_get_next_inode when it needs to * get ready to read in a new blockgroup. */ static errcode_t get_next_blockgroup(ext2_inode_scan scan) { ext2_filsys fs = scan->fs; scan->current_group++; scan->groups_left--; scan->current_block =fs->group_desc[scan->current_group].bg_inode_table; scan->current_inode = scan->current_group * EXT2_INODES_PER_GROUP(fs->super); scan->bytes_left = 0; scan->inodes_left = EXT2_INODES_PER_GROUP(fs->super); scan->blocks_left = fs->inode_blocks_per_group; if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super, EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) { scan->inodes_left -= fs->group_desc[scan->current_group].bg_itable_unused; scan->blocks_left = (scan->inodes_left + (fs->blocksize / scan->inode_size - 1)) * scan->inode_size / fs->blocksize; } return 0; } errcode_t ext2fs_inode_scan_goto_blockgroup(ext2_inode_scan scan, int group) { scan->current_group = group - 1; scan->groups_left = scan->fs->group_desc_count - group; return get_next_blockgroup(scan); } /* * This function is called by get_next_blocks() to check for bad * blocks in the inode table. * * This function assumes that badblocks_list->list is sorted in * increasing order. */ static errcode_t check_for_inode_bad_blocks(ext2_inode_scan scan, blk_t *num_blocks) { blk_t blk = scan->current_block; badblocks_list bb = scan->fs->badblocks; /* * If the inode table is missing, then obviously there are no * bad blocks. :-) */ if (blk == 0) return 0; /* * If the current block is greater than the bad block listed * in the bad block list, then advance the pointer until this * is no longer the case. If we run out of bad blocks, then * we don't need to do any more checking! */ while (blk > bb->list[scan->bad_block_ptr]) { if (++scan->bad_block_ptr >= bb->num) { scan->scan_flags &= ~EXT2_SF_CHK_BADBLOCKS; return 0; } } /* * If the current block is equal to the bad block listed in * the bad block list, then handle that one block specially. * (We could try to handle runs of bad blocks, but that * only increases CPU efficiency by a small amount, at the * expense of a huge expense of code complexity, and for an * uncommon case at that.) */ if (blk == bb->list[scan->bad_block_ptr]) { scan->scan_flags |= EXT2_SF_BAD_INODE_BLK; *num_blocks = 1; if (++scan->bad_block_ptr >= bb->num) scan->scan_flags &= ~EXT2_SF_CHK_BADBLOCKS; return 0; } /* * If there is a bad block in the range that we're about to * read in, adjust the number of blocks to read so that we we * don't read in the bad block. (Then the next block to read * will be the bad block, which is handled in the above case.) */ if ((blk + *num_blocks) > bb->list[scan->bad_block_ptr]) *num_blocks = (int) (bb->list[scan->bad_block_ptr] - blk); return 0; } /* * This function is called by ext2fs_get_next_inode when it needs to * read in more blocks from the current blockgroup's inode table. */ static errcode_t get_next_blocks(ext2_inode_scan scan) { blk_t num_blocks; errcode_t retval; /* * Figure out how many blocks to read; we read at most * inode_buffer_blocks, and perhaps less if there aren't that * many blocks left to read. */ num_blocks = scan->inode_buffer_blocks; if (num_blocks > scan->blocks_left) num_blocks = scan->blocks_left; /* * If the past block "read" was a bad block, then mark the * left-over extra bytes as also being bad. */ if (scan->scan_flags & EXT2_SF_BAD_INODE_BLK) { if (scan->bytes_left) scan->scan_flags |= EXT2_SF_BAD_EXTRA_BYTES; scan->scan_flags &= ~EXT2_SF_BAD_INODE_BLK; } /* * Do inode bad block processing, if necessary. */ if (scan->scan_flags & EXT2_SF_CHK_BADBLOCKS) { retval = check_for_inode_bad_blocks(scan, &num_blocks); if (retval) return retval; } if ((scan->scan_flags & EXT2_SF_BAD_INODE_BLK) || (scan->current_block == 0)) { memset(scan->inode_buffer, 0, (size_t) num_blocks * scan->fs->blocksize); } else { retval = io_channel_read_blk(scan->fs->io, scan->current_block, (int) num_blocks, scan->inode_buffer); if (retval) return EXT2_ET_NEXT_INODE_READ; } scan->ptr = scan->inode_buffer; scan->bytes_left = num_blocks * scan->fs->blocksize; scan->blocks_left -= num_blocks; if (scan->current_block) scan->current_block += num_blocks; return 0; } #if 0 /* * Returns 1 if the entire inode_buffer has a non-zero size and * contains all zeros. (Not just deleted inodes, since that means * that part of the inode table was used at one point; we want all * zeros, which means that the inode table is pristine.) */ static inline int is_empty_scan(ext2_inode_scan scan) { int i; if (scan->bytes_left == 0) return 0; for (i=0; i < scan->bytes_left; i++) if (scan->ptr[i]) return 0; return 1; } #endif errcode_t ext2fs_get_next_inode_full(ext2_inode_scan scan, ext2_ino_t *ino, struct ext2_inode *inode, int bufsize) { errcode_t retval; int extra_bytes = 0; EXT2_CHECK_MAGIC(scan, EXT2_ET_MAGIC_INODE_SCAN); /* * Do we need to start reading a new block group? */ if (scan->inodes_left <= 0) { force_new_group: if (scan->done_group) { retval = (scan->done_group) (scan->fs, scan, scan->current_group, scan->done_group_data); if (retval) return retval; } if (scan->groups_left <= 0) { *ino = 0; return 0; } retval = get_next_blockgroup(scan); if (retval) return retval; } /* * These checks are done outside the above if statement so * they can be done for block group #0. */ if ((scan->scan_flags & EXT2_SF_DO_LAZY) && (scan->fs->group_desc[scan->current_group].bg_flags & EXT2_BG_INODE_UNINIT)) goto force_new_group; if (scan->inodes_left == 0) goto force_new_group; if (scan->current_block == 0) { if (scan->scan_flags & EXT2_SF_SKIP_MISSING_ITABLE) { goto force_new_group; } else return EXT2_ET_MISSING_INODE_TABLE; } /* * Have we run out of space in the inode buffer? If so, we * need to read in more blocks. */ if (scan->bytes_left < scan->inode_size) { memcpy(scan->temp_buffer, scan->ptr, scan->bytes_left); extra_bytes = scan->bytes_left; retval = get_next_blocks(scan); if (retval) return retval; #if 0 /* * XXX test Need check for used inode somehow. * (Note: this is hard.) */ if (is_empty_scan(scan)) goto force_new_group; #endif } retval = 0; if (extra_bytes) { memcpy(scan->temp_buffer+extra_bytes, scan->ptr, scan->inode_size - extra_bytes); scan->ptr += scan->inode_size - extra_bytes; scan->bytes_left -= scan->inode_size - extra_bytes; #ifdef WORDS_BIGENDIAN memset(inode, 0, bufsize); ext2fs_swap_inode_full(scan->fs, (struct ext2_inode_large *) inode, (struct ext2_inode_large *) scan->temp_buffer, 0, bufsize); #else *inode = *((struct ext2_inode *) scan->temp_buffer); #endif if (scan->scan_flags & EXT2_SF_BAD_EXTRA_BYTES) retval = EXT2_ET_BAD_BLOCK_IN_INODE_TABLE; scan->scan_flags &= ~EXT2_SF_BAD_EXTRA_BYTES; } else { #ifdef WORDS_BIGENDIAN memset(inode, 0, bufsize); ext2fs_swap_inode_full(scan->fs, (struct ext2_inode_large *) inode, (struct ext2_inode_large *) scan->ptr, 0, bufsize); #else memcpy(inode, scan->ptr, bufsize); #endif scan->ptr += scan->inode_size; scan->bytes_left -= scan->inode_size; if (scan->scan_flags & EXT2_SF_BAD_INODE_BLK) retval = EXT2_ET_BAD_BLOCK_IN_INODE_TABLE; } scan->inodes_left--; scan->current_inode++; *ino = scan->current_inode; return retval; } errcode_t ext2fs_get_next_inode(ext2_inode_scan scan, ext2_ino_t *ino, struct ext2_inode *inode) { return ext2fs_get_next_inode_full(scan, ino, inode, sizeof(struct ext2_inode)); } /* * Functions to read and write a single inode. */ errcode_t ext2fs_read_inode_full(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode * inode, int bufsize) { unsigned long group, block, block_nr, offset; char *ptr; errcode_t retval; int clen, i, inodes_per_block, length; io_channel io; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); /* Check to see if user has an override function */ if (fs->read_inode) { retval = (fs->read_inode)(fs, ino, inode); if (retval != EXT2_ET_CALLBACK_NOTHANDLED) return retval; } if ((ino == 0) || (ino > fs->super->s_inodes_count)) return EXT2_ET_BAD_INODE_NUM; /* Create inode cache if not present */ if (!fs->icache) { retval = create_icache(fs); if (retval) return retval; } /* Check to see if it's in the inode cache */ if (bufsize == sizeof(struct ext2_inode)) { /* only old good inode can be retrieved from the cache */ for (i=0; i < fs->icache->cache_size; i++) { if (fs->icache->cache[i].ino == ino) { *inode = fs->icache->cache[i].inode; return 0; } } } if (fs->flags & EXT2_FLAG_IMAGE_FILE) { inodes_per_block = fs->blocksize / EXT2_INODE_SIZE(fs->super); block_nr = fs->image_header->offset_inode / fs->blocksize; block_nr += (ino - 1) / inodes_per_block; offset = ((ino - 1) % inodes_per_block) * EXT2_INODE_SIZE(fs->super); io = fs->image_io; } else { group = (ino - 1) / EXT2_INODES_PER_GROUP(fs->super); if (group > fs->group_desc_count) return EXT2_ET_BAD_INODE_NUM; offset = ((ino - 1) % EXT2_INODES_PER_GROUP(fs->super)) * EXT2_INODE_SIZE(fs->super); block = offset >> EXT2_BLOCK_SIZE_BITS(fs->super); if (!fs->group_desc[(unsigned)group].bg_inode_table) return EXT2_ET_MISSING_INODE_TABLE; block_nr = fs->group_desc[(unsigned)group].bg_inode_table + block; io = fs->io; } offset &= (EXT2_BLOCK_SIZE(fs->super) - 1); length = EXT2_INODE_SIZE(fs->super); if (bufsize < length) length = bufsize; ptr = (char *) inode; while (length) { clen = length; if ((offset + length) > fs->blocksize) clen = fs->blocksize - offset; if (block_nr != fs->icache->buffer_blk) { retval = io_channel_read_blk(io, block_nr, 1, fs->icache->buffer); if (retval) return retval; fs->icache->buffer_blk = block_nr; } memcpy(ptr, ((char *) fs->icache->buffer) + (unsigned) offset, clen); offset = 0; length -= clen; ptr += clen; block_nr++; } #ifdef WORDS_BIGENDIAN ext2fs_swap_inode_full(fs, (struct ext2_inode_large *) inode, (struct ext2_inode_large *) inode, 0, bufsize); #endif /* Update the inode cache */ fs->icache->cache_last = (fs->icache->cache_last + 1) % fs->icache->cache_size; fs->icache->cache[fs->icache->cache_last].ino = ino; fs->icache->cache[fs->icache->cache_last].inode = *inode; return 0; } errcode_t ext2fs_read_inode(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode * inode) { return ext2fs_read_inode_full(fs, ino, inode, sizeof(struct ext2_inode)); } errcode_t ext2fs_write_inode_full(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode * inode, int bufsize) { unsigned long group, block, block_nr, offset; errcode_t retval = 0; struct ext2_inode_large temp_inode, *w_inode; char *ptr; int clen, i, length; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); /* Check to see if user provided an override function */ if (fs->write_inode) { retval = (fs->write_inode)(fs, ino, inode); if (retval != EXT2_ET_CALLBACK_NOTHANDLED) return retval; } /* Check to see if the inode cache needs to be updated */ if (fs->icache) { for (i=0; i < fs->icache->cache_size; i++) { if (fs->icache->cache[i].ino == ino) { fs->icache->cache[i].inode = *inode; break; } } } else { retval = create_icache(fs); if (retval) return retval; } if (!(fs->flags & EXT2_FLAG_RW)) return EXT2_ET_RO_FILSYS; if ((ino == 0) || (ino > fs->super->s_inodes_count)) return EXT2_ET_BAD_INODE_NUM; length = bufsize; if (length < EXT2_INODE_SIZE(fs->super)) length = EXT2_INODE_SIZE(fs->super); if (length > (int) sizeof(struct ext2_inode_large)) { w_inode = malloc(length); if (!w_inode) return ENOMEM; } else w_inode = &temp_inode; memset(w_inode, 0, length); #ifdef WORDS_BIGENDIAN ext2fs_swap_inode_full(fs, w_inode, (struct ext2_inode_large *) inode, 1, bufsize); #else memcpy(w_inode, inode, bufsize); #endif group = (ino - 1) / EXT2_INODES_PER_GROUP(fs->super); offset = ((ino - 1) % EXT2_INODES_PER_GROUP(fs->super)) * EXT2_INODE_SIZE(fs->super); block = offset >> EXT2_BLOCK_SIZE_BITS(fs->super); if (!fs->group_desc[(unsigned) group].bg_inode_table) { retval = EXT2_ET_MISSING_INODE_TABLE; goto errout; } block_nr = fs->group_desc[(unsigned) group].bg_inode_table + block; offset &= (EXT2_BLOCK_SIZE(fs->super) - 1); length = EXT2_INODE_SIZE(fs->super); if (length > bufsize) length = bufsize; ptr = (char *) w_inode; while (length) { clen = length; if ((offset + length) > fs->blocksize) clen = fs->blocksize - offset; if (fs->icache->buffer_blk != block_nr) { retval = io_channel_read_blk(fs->io, block_nr, 1, fs->icache->buffer); if (retval) goto errout; fs->icache->buffer_blk = block_nr; } memcpy((char *) fs->icache->buffer + (unsigned) offset, ptr, clen); retval = io_channel_write_blk(fs->io, block_nr, 1, fs->icache->buffer); if (retval) goto errout; offset = 0; ptr += clen; length -= clen; block_nr++; } fs->flags |= EXT2_FLAG_CHANGED; errout: if (w_inode && w_inode != &temp_inode) free(w_inode); return retval; } errcode_t ext2fs_write_inode(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode) { return ext2fs_write_inode_full(fs, ino, inode, sizeof(struct ext2_inode)); } /* * This function should be called when writing a new inode. It makes * sure that extra part of large inodes is initialized properly. */ errcode_t ext2fs_write_new_inode(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode) { struct ext2_inode *buf; int size = EXT2_INODE_SIZE(fs->super); struct ext2_inode_large *large_inode; errcode_t retval; __u32 t = fs->now ? fs->now : time(NULL); if (!inode->i_ctime) inode->i_ctime = t; if (!inode->i_mtime) inode->i_mtime = t; if (!inode->i_atime) inode->i_atime = t; if (size == sizeof(struct ext2_inode)) return ext2fs_write_inode_full(fs, ino, inode, sizeof(struct ext2_inode)); buf = malloc(size); if (!buf) return ENOMEM; memset(buf, 0, size); *buf = *inode; large_inode = (struct ext2_inode_large *) buf; large_inode->i_extra_isize = sizeof(struct ext2_inode_large) - EXT2_GOOD_OLD_INODE_SIZE; if (!large_inode->i_crtime) large_inode->i_crtime = t; retval = ext2fs_write_inode_full(fs, ino, buf, size); free(buf); return retval; } errcode_t ext2fs_get_blocks(ext2_filsys fs, ext2_ino_t ino, blk_t *blocks) { struct ext2_inode inode; int i; errcode_t retval; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); if (ino > fs->super->s_inodes_count) return EXT2_ET_BAD_INODE_NUM; if (fs->get_blocks) { if (!(*fs->get_blocks)(fs, ino, blocks)) return 0; } retval = ext2fs_read_inode(fs, ino, &inode); if (retval) return retval; for (i=0; i < EXT2_N_BLOCKS; i++) blocks[i] = inode.i_block[i]; return 0; } errcode_t ext2fs_check_directory(ext2_filsys fs, ext2_ino_t ino) { struct ext2_inode inode; errcode_t retval; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); if (ino > fs->super->s_inodes_count) return EXT2_ET_BAD_INODE_NUM; if (fs->check_directory) { retval = (fs->check_directory)(fs, ino); if (retval != EXT2_ET_CALLBACK_NOTHANDLED) return retval; } retval = ext2fs_read_inode(fs, ino, &inode); if (retval) return retval; if (!LINUX_S_ISDIR(inode.i_mode)) return EXT2_ET_NO_DIRECTORY; return 0; } e2fsprogs-1.41.14/lib/ext2fs/get_pathname.c0000644031104000366760000000654511405316370016425 0ustar tytso/* * get_pathname.c --- do directry/inode -> name translation * * Copyright (C) 1993, 1994, 1995 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ /* * * ext2fs_get_pathname(fs, dir, ino, name) * * This function translates takes two inode numbers into a * string, placing the result in . is the containing * directory inode, and is the inode number itself. If * is zero, then ext2fs_get_pathname will return pathname * of the the directory . * */ #include #include #if HAVE_UNISTD_H #include #endif #include "ext2_fs.h" #include "ext2fs.h" struct get_pathname_struct { ext2_ino_t search_ino; ext2_ino_t parent; char *name; errcode_t errcode; }; #ifdef __TURBOC__ #pragma argsused #endif static int get_pathname_proc(struct ext2_dir_entry *dirent, int offset EXT2FS_ATTR((unused)), int blocksize EXT2FS_ATTR((unused)), char *buf EXT2FS_ATTR((unused)), void *priv_data) { struct get_pathname_struct *gp; errcode_t retval; gp = (struct get_pathname_struct *) priv_data; if (((dirent->name_len & 0xFF) == 2) && !strncmp(dirent->name, "..", 2)) gp->parent = dirent->inode; if (dirent->inode == gp->search_ino) { retval = ext2fs_get_mem((dirent->name_len & 0xFF) + 1, &gp->name); if (retval) { gp->errcode = retval; return DIRENT_ABORT; } strncpy(gp->name, dirent->name, (dirent->name_len & 0xFF)); gp->name[dirent->name_len & 0xFF] = '\0'; return DIRENT_ABORT; } return 0; } static errcode_t ext2fs_get_pathname_int(ext2_filsys fs, ext2_ino_t dir, ext2_ino_t ino, int maxdepth, char *buf, char **name) { struct get_pathname_struct gp; char *parent_name, *ret; errcode_t retval; if (dir == ino) { retval = ext2fs_get_mem(2, name); if (retval) return retval; strcpy(*name, (dir == EXT2_ROOT_INO) ? "/" : "."); return 0; } if (!dir || (maxdepth < 0)) { retval = ext2fs_get_mem(4, name); if (retval) return retval; strcpy(*name, "..."); return 0; } gp.search_ino = ino; gp.parent = 0; gp.name = 0; gp.errcode = 0; retval = ext2fs_dir_iterate(fs, dir, 0, buf, get_pathname_proc, &gp); if (retval) goto cleanup; if (gp.errcode) { retval = gp.errcode; goto cleanup; } retval = ext2fs_get_pathname_int(fs, gp.parent, dir, maxdepth-1, buf, &parent_name); if (retval) goto cleanup; if (!ino) { *name = parent_name; return 0; } if (gp.name) retval = ext2fs_get_mem(strlen(parent_name)+strlen(gp.name)+2, &ret); else retval = ext2fs_get_mem(strlen(parent_name)+5, &ret); if (retval) goto cleanup; ret[0] = 0; if (parent_name[1]) strcat(ret, parent_name); strcat(ret, "/"); if (gp.name) strcat(ret, gp.name); else strcat(ret, "???"); *name = ret; ext2fs_free_mem(&parent_name); retval = 0; cleanup: if (gp.name) ext2fs_free_mem(&gp.name); return retval; } errcode_t ext2fs_get_pathname(ext2_filsys fs, ext2_ino_t dir, ext2_ino_t ino, char **name) { char *buf; errcode_t retval; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); retval = ext2fs_get_mem(fs->blocksize, &buf); if (retval) return retval; if (dir == ino) ino = 0; retval = ext2fs_get_pathname_int(fs, dir, ino, 32, buf, name); ext2fs_free_mem(&buf); return retval; } e2fsprogs-1.41.14/lib/ext2fs/icount.c0000644031104000116100000004724511504417000015733 0ustar tytsoeng/* * icount.c --- an efficient inode count abstraction * * Copyright (C) 1997 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #if HAVE_UNISTD_H #include #endif #include #include #include #include #include #include "ext2_fs.h" #include "ext2fs.h" #include "tdb.h" /* * The data storage strategy used by icount relies on the observation * that most inode counts are either zero (for non-allocated inodes), * one (for most files), and only a few that are two or more * (directories and files that are linked to more than one directory). * * Also, e2fsck tends to load the icount data sequentially. * * So, we use an inode bitmap to indicate which inodes have a count of * one, and then use a sorted list to store the counts for inodes * which are greater than one. * * We also use an optional bitmap to indicate which inodes are already * in the sorted list, to speed up the use of this abstraction by * e2fsck's pass 2. Pass 2 increments inode counts as it finds them, * so this extra bitmap avoids searching the sorted list to see if a * particular inode is on the sorted list already. */ struct ext2_icount_el { ext2_ino_t ino; __u32 count; }; struct ext2_icount { errcode_t magic; ext2fs_inode_bitmap single; ext2fs_inode_bitmap multiple; ext2_ino_t count; ext2_ino_t size; ext2_ino_t num_inodes; ext2_ino_t cursor; struct ext2_icount_el *list; struct ext2_icount_el *last_lookup; char *tdb_fn; TDB_CONTEXT *tdb; }; /* * We now use a 32-bit counter field because it doesn't cost us * anything extra for the in-memory data structure, due to alignment * padding. But there's no point changing the interface if most of * the time we only care if the number is bigger than 65,000 or not. * So use the following translation function to return a 16-bit count. */ #define icount_16_xlate(x) (((x) > 65500) ? 65500 : (x)) void ext2fs_free_icount(ext2_icount_t icount) { if (!icount) return; icount->magic = 0; if (icount->list) ext2fs_free_mem(&icount->list); if (icount->single) ext2fs_free_inode_bitmap(icount->single); if (icount->multiple) ext2fs_free_inode_bitmap(icount->multiple); if (icount->tdb) tdb_close(icount->tdb); if (icount->tdb_fn) { unlink(icount->tdb_fn); free(icount->tdb_fn); } ext2fs_free_mem(&icount); } static errcode_t alloc_icount(ext2_filsys fs, int flags, ext2_icount_t *ret) { ext2_icount_t icount; errcode_t retval; *ret = 0; retval = ext2fs_get_mem(sizeof(struct ext2_icount), &icount); if (retval) return retval; memset(icount, 0, sizeof(struct ext2_icount)); retval = ext2fs_allocate_inode_bitmap(fs, 0, &icount->single); if (retval) goto errout; if (flags & EXT2_ICOUNT_OPT_INCREMENT) { retval = ext2fs_allocate_inode_bitmap(fs, 0, &icount->multiple); if (retval) goto errout; } else icount->multiple = 0; icount->magic = EXT2_ET_MAGIC_ICOUNT; icount->num_inodes = fs->super->s_inodes_count; *ret = icount; return 0; errout: ext2fs_free_icount(icount); return(retval); } struct uuid { __u32 time_low; __u16 time_mid; __u16 time_hi_and_version; __u16 clock_seq; __u8 node[6]; }; static void unpack_uuid(void *in, struct uuid *uu) { __u8 *ptr = in; __u32 tmp; tmp = *ptr++; tmp = (tmp << 8) | *ptr++; tmp = (tmp << 8) | *ptr++; tmp = (tmp << 8) | *ptr++; uu->time_low = tmp; tmp = *ptr++; tmp = (tmp << 8) | *ptr++; uu->time_mid = tmp; tmp = *ptr++; tmp = (tmp << 8) | *ptr++; uu->time_hi_and_version = tmp; tmp = *ptr++; tmp = (tmp << 8) | *ptr++; uu->clock_seq = tmp; memcpy(uu->node, ptr, 6); } static void uuid_unparse(void *uu, char *out) { struct uuid uuid; unpack_uuid(uu, &uuid); sprintf(out, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", uuid.time_low, uuid.time_mid, uuid.time_hi_and_version, uuid.clock_seq >> 8, uuid.clock_seq & 0xFF, uuid.node[0], uuid.node[1], uuid.node[2], uuid.node[3], uuid.node[4], uuid.node[5]); } errcode_t ext2fs_create_icount_tdb(ext2_filsys fs, char *tdb_dir, int flags, ext2_icount_t *ret) { ext2_icount_t icount; errcode_t retval; char *fn, uuid[40]; int fd; retval = alloc_icount(fs, flags, &icount); if (retval) return retval; retval = ext2fs_get_mem(strlen(tdb_dir) + 64, &fn); if (retval) goto errout; uuid_unparse(fs->super->s_uuid, uuid); sprintf(fn, "%s/%s-icount-XXXXXX", tdb_dir, uuid); fd = mkstemp(fn); icount->tdb_fn = fn; icount->tdb = tdb_open(fn, 0, TDB_CLEAR_IF_FIRST, O_RDWR | O_CREAT | O_TRUNC, 0600); if (icount->tdb) { close(fd); *ret = icount; return 0; } retval = errno; close(fd); errout: ext2fs_free_icount(icount); return(retval); } errcode_t ext2fs_create_icount2(ext2_filsys fs, int flags, unsigned int size, ext2_icount_t hint, ext2_icount_t *ret) { ext2_icount_t icount; errcode_t retval; size_t bytes; ext2_ino_t i; if (hint) { EXT2_CHECK_MAGIC(hint, EXT2_ET_MAGIC_ICOUNT); if (hint->size > size) size = (size_t) hint->size; } retval = alloc_icount(fs, flags, &icount); if (retval) return retval; if (size) { icount->size = size; } else { /* * Figure out how many special case inode counts we will * have. We know we will need one for each directory; * we also need to reserve some extra room for file links */ retval = ext2fs_get_num_dirs(fs, &icount->size); if (retval) goto errout; icount->size += fs->super->s_inodes_count / 50; } bytes = (size_t) (icount->size * sizeof(struct ext2_icount_el)); #if 0 printf("Icount allocated %u entries, %d bytes.\n", icount->size, bytes); #endif retval = ext2fs_get_array(icount->size, sizeof(struct ext2_icount_el), &icount->list); if (retval) goto errout; memset(icount->list, 0, bytes); icount->count = 0; icount->cursor = 0; /* * Populate the sorted list with those entries which were * found in the hint icount (since those are ones which will * likely need to be in the sorted list this time around). */ if (hint) { for (i=0; i < hint->count; i++) icount->list[i].ino = hint->list[i].ino; icount->count = hint->count; } *ret = icount; return 0; errout: ext2fs_free_icount(icount); return(retval); } errcode_t ext2fs_create_icount(ext2_filsys fs, int flags, unsigned int size, ext2_icount_t *ret) { return ext2fs_create_icount2(fs, flags, size, 0, ret); } /* * insert_icount_el() --- Insert a new entry into the sorted list at a * specified position. */ static struct ext2_icount_el *insert_icount_el(ext2_icount_t icount, ext2_ino_t ino, int pos) { struct ext2_icount_el *el; errcode_t retval; ext2_ino_t new_size = 0; int num; if (icount->last_lookup && icount->last_lookup->ino == ino) return icount->last_lookup; if (icount->count >= icount->size) { if (icount->count) { new_size = icount->list[(unsigned)icount->count-1].ino; new_size = (ext2_ino_t) (icount->count * ((float) icount->num_inodes / new_size)); } if (new_size < (icount->size + 100)) new_size = icount->size + 100; #if 0 printf("Reallocating icount %u entries...\n", new_size); #endif retval = ext2fs_resize_mem((size_t) icount->size * sizeof(struct ext2_icount_el), (size_t) new_size * sizeof(struct ext2_icount_el), &icount->list); if (retval) return 0; icount->size = new_size; } num = (int) icount->count - pos; if (num < 0) return 0; /* should never happen */ if (num) { memmove(&icount->list[pos+1], &icount->list[pos], sizeof(struct ext2_icount_el) * num); } icount->count++; el = &icount->list[pos]; el->count = 0; el->ino = ino; icount->last_lookup = el; return el; } /* * get_icount_el() --- given an inode number, try to find icount * information in the sorted list. If the create flag is set, * and we can't find an entry, create one in the sorted list. */ static struct ext2_icount_el *get_icount_el(ext2_icount_t icount, ext2_ino_t ino, int create) { float range; int low, high, mid; ext2_ino_t lowval, highval; if (!icount || !icount->list) return 0; if (create && ((icount->count == 0) || (ino > icount->list[(unsigned)icount->count-1].ino))) { return insert_icount_el(icount, ino, (unsigned) icount->count); } if (icount->count == 0) return 0; if (icount->cursor >= icount->count) icount->cursor = 0; if (ino == icount->list[icount->cursor].ino) return &icount->list[icount->cursor++]; #if 0 printf("Non-cursor get_icount_el: %u\n", ino); #endif low = 0; high = (int) icount->count-1; while (low <= high) { #if 0 mid = (low+high)/2; #else if (low == high) mid = low; else { /* Interpolate for efficiency */ lowval = icount->list[low].ino; highval = icount->list[high].ino; if (ino < lowval) range = 0; else if (ino > highval) range = 1; else { range = ((float) (ino - lowval)) / (highval - lowval); if (range > 0.9) range = 0.9; if (range < 0.1) range = 0.1; } mid = low + ((int) (range * (high-low))); } #endif if (ino == icount->list[mid].ino) { icount->cursor = mid+1; return &icount->list[mid]; } if (ino < icount->list[mid].ino) high = mid-1; else low = mid+1; } /* * If we need to create a new entry, it should be right at * low (where high will be left at low-1). */ if (create) return insert_icount_el(icount, ino, low); return 0; } static errcode_t set_inode_count(ext2_icount_t icount, ext2_ino_t ino, __u32 count) { struct ext2_icount_el *el; TDB_DATA key, data; if (icount->tdb) { key.dptr = (unsigned char *) &ino; key.dsize = sizeof(ext2_ino_t); data.dptr = (unsigned char *) &count; data.dsize = sizeof(__u32); if (count) { if (tdb_store(icount->tdb, key, data, TDB_REPLACE)) return tdb_error(icount->tdb) + EXT2_ET_TDB_SUCCESS; } else { if (tdb_delete(icount->tdb, key)) return tdb_error(icount->tdb) + EXT2_ET_TDB_SUCCESS; } return 0; } el = get_icount_el(icount, ino, 1); if (!el) return EXT2_ET_NO_MEMORY; el->count = count; return 0; } static errcode_t get_inode_count(ext2_icount_t icount, ext2_ino_t ino, __u32 *count) { struct ext2_icount_el *el; TDB_DATA key, data; if (icount->tdb) { key.dptr = (unsigned char *) &ino; key.dsize = sizeof(ext2_ino_t); data = tdb_fetch(icount->tdb, key); if (data.dptr == NULL) { *count = 0; return tdb_error(icount->tdb) + EXT2_ET_TDB_SUCCESS; } *count = *((__u32 *) data.dptr); free(data.dptr); return 0; } el = get_icount_el(icount, ino, 0); if (!el) { *count = 0; return ENOENT; } *count = el->count; return 0; } errcode_t ext2fs_icount_validate(ext2_icount_t icount, FILE *out) { errcode_t ret = 0; unsigned int i; const char *bad = "bad icount"; EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT); if (icount->count > icount->size) { fprintf(out, "%s: count > size\n", bad); return EXT2_ET_INVALID_ARGUMENT; } for (i=1; i < icount->count; i++) { if (icount->list[i-1].ino >= icount->list[i].ino) { fprintf(out, "%s: list[%d].ino=%u, list[%d].ino=%u\n", bad, i-1, icount->list[i-1].ino, i, icount->list[i].ino); ret = EXT2_ET_INVALID_ARGUMENT; } } return ret; } errcode_t ext2fs_icount_fetch(ext2_icount_t icount, ext2_ino_t ino, __u16 *ret) { __u32 val; EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT); if (!ino || (ino > icount->num_inodes)) return EXT2_ET_INVALID_ARGUMENT; if (ext2fs_test_inode_bitmap(icount->single, ino)) { *ret = 1; return 0; } if (icount->multiple && !ext2fs_test_inode_bitmap(icount->multiple, ino)) { *ret = 0; return 0; } get_inode_count(icount, ino, &val); *ret = icount_16_xlate(val); return 0; } errcode_t ext2fs_icount_increment(ext2_icount_t icount, ext2_ino_t ino, __u16 *ret) { __u32 curr_value; EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT); if (!ino || (ino > icount->num_inodes)) return EXT2_ET_INVALID_ARGUMENT; if (ext2fs_test_inode_bitmap(icount->single, ino)) { /* * If the existing count is 1, then we know there is * no entry in the list. */ if (set_inode_count(icount, ino, 2)) return EXT2_ET_NO_MEMORY; curr_value = 2; ext2fs_unmark_inode_bitmap(icount->single, ino); } else if (icount->multiple) { /* * The count is either zero or greater than 1; if the * inode is set in icount->multiple, then there should * be an entry in the list, so we need to fix it. */ if (ext2fs_test_inode_bitmap(icount->multiple, ino)) { get_inode_count(icount, ino, &curr_value); curr_value++; if (set_inode_count(icount, ino, curr_value)) return EXT2_ET_NO_MEMORY; } else { /* * The count was zero; mark the single bitmap * and return. */ ext2fs_mark_inode_bitmap(icount->single, ino); if (ret) *ret = 1; return 0; } } else { /* * The count is either zero or greater than 1; try to * find an entry in the list to determine which. */ get_inode_count(icount, ino, &curr_value); curr_value++; if (set_inode_count(icount, ino, curr_value)) return EXT2_ET_NO_MEMORY; } if (icount->multiple) ext2fs_mark_inode_bitmap(icount->multiple, ino); if (ret) *ret = icount_16_xlate(curr_value); return 0; } errcode_t ext2fs_icount_decrement(ext2_icount_t icount, ext2_ino_t ino, __u16 *ret) { __u32 curr_value; if (!ino || (ino > icount->num_inodes)) return EXT2_ET_INVALID_ARGUMENT; EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT); if (ext2fs_test_inode_bitmap(icount->single, ino)) { ext2fs_unmark_inode_bitmap(icount->single, ino); if (icount->multiple) ext2fs_unmark_inode_bitmap(icount->multiple, ino); else { set_inode_count(icount, ino, 0); } if (ret) *ret = 0; return 0; } if (icount->multiple && !ext2fs_test_inode_bitmap(icount->multiple, ino)) return EXT2_ET_INVALID_ARGUMENT; get_inode_count(icount, ino, &curr_value); if (!curr_value) return EXT2_ET_INVALID_ARGUMENT; curr_value--; if (set_inode_count(icount, ino, curr_value)) return EXT2_ET_NO_MEMORY; if (curr_value == 1) ext2fs_mark_inode_bitmap(icount->single, ino); if ((curr_value == 0) && icount->multiple) ext2fs_unmark_inode_bitmap(icount->multiple, ino); if (ret) *ret = icount_16_xlate(curr_value); return 0; } errcode_t ext2fs_icount_store(ext2_icount_t icount, ext2_ino_t ino, __u16 count) { if (!ino || (ino > icount->num_inodes)) return EXT2_ET_INVALID_ARGUMENT; EXT2_CHECK_MAGIC(icount, EXT2_ET_MAGIC_ICOUNT); if (count == 1) { ext2fs_mark_inode_bitmap(icount->single, ino); if (icount->multiple) ext2fs_unmark_inode_bitmap(icount->multiple, ino); return 0; } if (count == 0) { ext2fs_unmark_inode_bitmap(icount->single, ino); if (icount->multiple) { /* * If the icount->multiple bitmap is enabled, * we can just clear both bitmaps and we're done */ ext2fs_unmark_inode_bitmap(icount->multiple, ino); } else set_inode_count(icount, ino, 0); return 0; } if (set_inode_count(icount, ino, count)) return EXT2_ET_NO_MEMORY; ext2fs_unmark_inode_bitmap(icount->single, ino); if (icount->multiple) ext2fs_mark_inode_bitmap(icount->multiple, ino); return 0; } ext2_ino_t ext2fs_get_icount_size(ext2_icount_t icount) { if (!icount || icount->magic != EXT2_ET_MAGIC_ICOUNT) return 0; return icount->size; } #ifdef DEBUG ext2_filsys test_fs; ext2_icount_t icount; #define EXIT 0x00 #define FETCH 0x01 #define STORE 0x02 #define INCREMENT 0x03 #define DECREMENT 0x04 struct test_program { int cmd; ext2_ino_t ino; __u16 arg; __u16 expected; }; struct test_program prog[] = { { STORE, 42, 42, 42 }, { STORE, 1, 1, 1 }, { STORE, 2, 2, 2 }, { STORE, 3, 3, 3 }, { STORE, 10, 1, 1 }, { STORE, 42, 0, 0 }, { INCREMENT, 5, 0, 1 }, { INCREMENT, 5, 0, 2 }, { INCREMENT, 5, 0, 3 }, { INCREMENT, 5, 0, 4 }, { DECREMENT, 5, 0, 3 }, { DECREMENT, 5, 0, 2 }, { DECREMENT, 5, 0, 1 }, { DECREMENT, 5, 0, 0 }, { FETCH, 10, 0, 1 }, { FETCH, 1, 0, 1 }, { FETCH, 2, 0, 2 }, { FETCH, 3, 0, 3 }, { INCREMENT, 1, 0, 2 }, { DECREMENT, 2, 0, 1 }, { DECREMENT, 2, 0, 0 }, { FETCH, 12, 0, 0 }, { EXIT, 0, 0, 0 } }; struct test_program extended[] = { { STORE, 1, 1, 1 }, { STORE, 2, 2, 2 }, { STORE, 3, 3, 3 }, { STORE, 4, 4, 4 }, { STORE, 5, 5, 5 }, { STORE, 6, 1, 1 }, { STORE, 7, 2, 2 }, { STORE, 8, 3, 3 }, { STORE, 9, 4, 4 }, { STORE, 10, 5, 5 }, { STORE, 11, 1, 1 }, { STORE, 12, 2, 2 }, { STORE, 13, 3, 3 }, { STORE, 14, 4, 4 }, { STORE, 15, 5, 5 }, { STORE, 16, 1, 1 }, { STORE, 17, 2, 2 }, { STORE, 18, 3, 3 }, { STORE, 19, 4, 4 }, { STORE, 20, 5, 5 }, { STORE, 21, 1, 1 }, { STORE, 22, 2, 2 }, { STORE, 23, 3, 3 }, { STORE, 24, 4, 4 }, { STORE, 25, 5, 5 }, { STORE, 26, 1, 1 }, { STORE, 27, 2, 2 }, { STORE, 28, 3, 3 }, { STORE, 29, 4, 4 }, { STORE, 30, 5, 5 }, { EXIT, 0, 0, 0 } }; /* * Setup the variables for doing the inode scan test. */ static void setup(void) { errcode_t retval; struct ext2_super_block param; initialize_ext2_error_table(); memset(¶m, 0, sizeof(param)); param.s_blocks_count = 12000; retval = ext2fs_initialize("test fs", 0, ¶m, test_io_manager, &test_fs); if (retval) { com_err("setup", retval, "while initializing filesystem"); exit(1); } retval = ext2fs_allocate_tables(test_fs); if (retval) { com_err("setup", retval, "while allocating tables for test filesystem"); exit(1); } } int run_test(int flags, int size, char *dir, struct test_program *prog) { errcode_t retval; ext2_icount_t icount; struct test_program *pc; __u16 result; int problem = 0; if (dir) { retval = ext2fs_create_icount_tdb(test_fs, dir, flags, &icount); if (retval) { com_err("run_test", retval, "while creating icount using tdb"); exit(1); } } else { retval = ext2fs_create_icount2(test_fs, flags, size, 0, &icount); if (retval) { com_err("run_test", retval, "while creating icount"); exit(1); } } for (pc = prog; pc->cmd != EXIT; pc++) { switch (pc->cmd) { case FETCH: printf("icount_fetch(%u) = ", pc->ino); break; case STORE: retval = ext2fs_icount_store(icount, pc->ino, pc->arg); if (retval) { com_err("run_test", retval, "while calling icount_store"); exit(1); } printf("icount_store(%u, %u) = ", pc->ino, pc->arg); break; case INCREMENT: retval = ext2fs_icount_increment(icount, pc->ino, 0); if (retval) { com_err("run_test", retval, "while calling icount_increment"); exit(1); } printf("icount_increment(%u) = ", pc->ino); break; case DECREMENT: retval = ext2fs_icount_decrement(icount, pc->ino, 0); if (retval) { com_err("run_test", retval, "while calling icount_decrement"); exit(1); } printf("icount_decrement(%u) = ", pc->ino); break; } retval = ext2fs_icount_fetch(icount, pc->ino, &result); if (retval) { com_err("run_test", retval, "while calling icount_fetch"); exit(1); } printf("%u (%s)\n", result, (result == pc->expected) ? "OK" : "NOT OK"); if (result != pc->expected) problem++; } printf("icount size is %u\n", ext2fs_get_icount_size(icount)); retval = ext2fs_icount_validate(icount, stdout); if (retval) { com_err("run_test", retval, "while calling icount_validate"); exit(1); } ext2fs_free_icount(icount); return problem; } int main(int argc, char **argv) { int failed = 0; setup(); printf("Standard icount run:\n"); failed += run_test(0, 0, 0, prog); printf("\nMultiple bitmap test:\n"); failed += run_test(EXT2_ICOUNT_OPT_INCREMENT, 0, 0, prog); printf("\nResizing icount:\n"); failed += run_test(0, 3, 0, extended); printf("\nStandard icount run with tdb:\n"); failed += run_test(0, 0, ".", prog); printf("\nMultiple bitmap test with tdb:\n"); failed += run_test(EXT2_ICOUNT_OPT_INCREMENT, 0, ".", prog); if (failed) printf("FAILED!\n"); return failed; } #endif e2fsprogs-1.41.14/lib/ext2fs/dir_iterate.c0000644031104000116100000001451311504443543016730 0ustar tytsoeng/* * dir_iterate.c --- ext2fs directory iteration operations * * Copyright (C) 1993, 1994, 1994, 1995, 1996, 1997 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #if HAVE_ERRNO_H #include #endif #include "ext2_fs.h" #include "ext2fsP.h" #define EXT4_MAX_REC_LEN ((1<<16)-1) errcode_t ext2fs_get_rec_len(ext2_filsys fs, struct ext2_dir_entry *dirent, unsigned int *rec_len) { unsigned int len = dirent->rec_len; if (fs->blocksize < 65536) *rec_len = len; else if (len == EXT4_MAX_REC_LEN || len == 0) *rec_len = fs->blocksize; else *rec_len = (len & 65532) | ((len & 3) << 16); return 0; } errcode_t ext2fs_set_rec_len(ext2_filsys fs, unsigned int len, struct ext2_dir_entry *dirent) { if ((len > fs->blocksize) || (fs->blocksize > (1 << 18)) || (len & 3)) return EINVAL; if (len < 65536) { dirent->rec_len = len; return 0; } if (len == fs->blocksize) { if (fs->blocksize == 65536) dirent->rec_len = EXT4_MAX_REC_LEN; else dirent->rec_len = 0; } else dirent->rec_len = (len & 65532) | ((len >> 16) & 3); return 0; } /* * This function checks to see whether or not a potential deleted * directory entry looks valid. What we do is check the deleted entry * and each successive entry to make sure that they all look valid and * that the last deleted entry ends at the beginning of the next * undeleted entry. Returns 1 if the deleted entry looks valid, zero * if not valid. */ static int ext2fs_validate_entry(ext2_filsys fs, char *buf, unsigned int offset, unsigned int final_offset) { struct ext2_dir_entry *dirent; unsigned int rec_len; #define DIRENT_MIN_LENGTH 12 while ((offset < final_offset) && (offset <= fs->blocksize - DIRENT_MIN_LENGTH)) { dirent = (struct ext2_dir_entry *)(buf + offset); if (ext2fs_get_rec_len(fs, dirent, &rec_len)) return 0; offset += rec_len; if ((rec_len < 8) || ((rec_len % 4) != 0) || ((((unsigned) dirent->name_len & 0xFF)+8) > rec_len)) return 0; } return (offset == final_offset); } errcode_t ext2fs_dir_iterate2(ext2_filsys fs, ext2_ino_t dir, int flags, char *block_buf, int (*func)(ext2_ino_t dir, int entry, struct ext2_dir_entry *dirent, int offset, int blocksize, char *buf, void *priv_data), void *priv_data) { struct dir_context ctx; errcode_t retval; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); retval = ext2fs_check_directory(fs, dir); if (retval) return retval; ctx.dir = dir; ctx.flags = flags; if (block_buf) ctx.buf = block_buf; else { retval = ext2fs_get_mem(fs->blocksize, &ctx.buf); if (retval) return retval; } ctx.func = func; ctx.priv_data = priv_data; ctx.errcode = 0; retval = ext2fs_block_iterate2(fs, dir, BLOCK_FLAG_READ_ONLY, 0, ext2fs_process_dir_block, &ctx); if (!block_buf) ext2fs_free_mem(&ctx.buf); if (retval) return retval; return ctx.errcode; } struct xlate { int (*func)(struct ext2_dir_entry *dirent, int offset, int blocksize, char *buf, void *priv_data); void *real_private; }; static int xlate_func(ext2_ino_t dir EXT2FS_ATTR((unused)), int entry EXT2FS_ATTR((unused)), struct ext2_dir_entry *dirent, int offset, int blocksize, char *buf, void *priv_data) { struct xlate *xl = (struct xlate *) priv_data; return (*xl->func)(dirent, offset, blocksize, buf, xl->real_private); } extern errcode_t ext2fs_dir_iterate(ext2_filsys fs, ext2_ino_t dir, int flags, char *block_buf, int (*func)(struct ext2_dir_entry *dirent, int offset, int blocksize, char *buf, void *priv_data), void *priv_data) { struct xlate xl; xl.real_private = priv_data; xl.func = func; return ext2fs_dir_iterate2(fs, dir, flags, block_buf, xlate_func, &xl); } /* * Helper function which is private to this module. Used by * ext2fs_dir_iterate() and ext2fs_dblist_dir_iterate() */ int ext2fs_process_dir_block(ext2_filsys fs, blk_t *blocknr, e2_blkcnt_t blockcnt, blk_t ref_block EXT2FS_ATTR((unused)), int ref_offset EXT2FS_ATTR((unused)), void *priv_data) { struct dir_context *ctx = (struct dir_context *) priv_data; unsigned int offset = 0; unsigned int next_real_entry = 0; int ret = 0; int changed = 0; int do_abort = 0; unsigned int rec_len, size; int entry; struct ext2_dir_entry *dirent; if (blockcnt < 0) return 0; entry = blockcnt ? DIRENT_OTHER_FILE : DIRENT_DOT_FILE; ctx->errcode = ext2fs_read_dir_block(fs, *blocknr, ctx->buf); if (ctx->errcode) return BLOCK_ABORT; while (offset < fs->blocksize) { dirent = (struct ext2_dir_entry *) (ctx->buf + offset); if (ext2fs_get_rec_len(fs, dirent, &rec_len)) return BLOCK_ABORT; if (((offset + rec_len) > fs->blocksize) || (rec_len < 8) || ((rec_len % 4) != 0) || ((((unsigned) dirent->name_len & 0xFF)+8) > rec_len)) { ctx->errcode = EXT2_ET_DIR_CORRUPTED; return BLOCK_ABORT; } if (!dirent->inode && !(ctx->flags & DIRENT_FLAG_INCLUDE_EMPTY)) goto next; ret = (ctx->func)(ctx->dir, (next_real_entry > offset) ? DIRENT_DELETED_FILE : entry, dirent, offset, fs->blocksize, ctx->buf, ctx->priv_data); if (entry < DIRENT_OTHER_FILE) entry++; if (ret & DIRENT_CHANGED) { if (ext2fs_get_rec_len(fs, dirent, &rec_len)) return BLOCK_ABORT; changed++; } if (ret & DIRENT_ABORT) { do_abort++; break; } next: if (next_real_entry == offset) next_real_entry += rec_len; if (ctx->flags & DIRENT_FLAG_INCLUDE_REMOVED) { size = ((dirent->name_len & 0xFF) + 11) & ~3; if (rec_len != size) { unsigned int final_offset; final_offset = offset + rec_len; offset += size; while (offset < final_offset && !ext2fs_validate_entry(fs, ctx->buf, offset, final_offset)) offset += 4; continue; } } offset += rec_len; } if (changed) { ctx->errcode = ext2fs_write_dir_block(fs, *blocknr, ctx->buf); if (ctx->errcode) return BLOCK_ABORT; } if (do_abort) return BLOCK_ABORT; return 0; } e2fsprogs-1.41.14/lib/ext2fs/fileio.c0000644031104000116100000001733711504417000015700 0ustar tytsoeng/* * fileio.c --- Simple file I/O routines * * Copyright (C) 1997 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #include "ext2_fs.h" #include "ext2fs.h" struct ext2_file { errcode_t magic; ext2_filsys fs; ext2_ino_t ino; struct ext2_inode inode; int flags; __u64 pos; blk_t blockno; blk_t physblock; char *buf; }; #define BMAP_BUFFER (file->buf + fs->blocksize) errcode_t ext2fs_file_open2(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode, int flags, ext2_file_t *ret) { ext2_file_t file; errcode_t retval; /* * Don't let caller create or open a file for writing if the * filesystem is read-only. */ if ((flags & (EXT2_FILE_WRITE | EXT2_FILE_CREATE)) && !(fs->flags & EXT2_FLAG_RW)) return EXT2_ET_RO_FILSYS; retval = ext2fs_get_mem(sizeof(struct ext2_file), &file); if (retval) return retval; memset(file, 0, sizeof(struct ext2_file)); file->magic = EXT2_ET_MAGIC_EXT2_FILE; file->fs = fs; file->ino = ino; file->flags = flags & EXT2_FILE_MASK; if (inode) { memcpy(&file->inode, inode, sizeof(struct ext2_inode)); } else { retval = ext2fs_read_inode(fs, ino, &file->inode); if (retval) goto fail; } retval = ext2fs_get_array(3, fs->blocksize, &file->buf); if (retval) goto fail; *ret = file; return 0; fail: if (file->buf) ext2fs_free_mem(&file->buf); ext2fs_free_mem(&file); return retval; } errcode_t ext2fs_file_open(ext2_filsys fs, ext2_ino_t ino, int flags, ext2_file_t *ret) { return ext2fs_file_open2(fs, ino, NULL, flags, ret); } /* * This function returns the filesystem handle of a file from the structure */ ext2_filsys ext2fs_file_get_fs(ext2_file_t file) { if (file->magic != EXT2_ET_MAGIC_EXT2_FILE) return 0; return file->fs; } /* * This function flushes the dirty block buffer out to disk if * necessary. */ errcode_t ext2fs_file_flush(ext2_file_t file) { errcode_t retval; ext2_filsys fs; EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE); fs = file->fs; if (!(file->flags & EXT2_FILE_BUF_VALID) || !(file->flags & EXT2_FILE_BUF_DIRTY)) return 0; /* * OK, the physical block hasn't been allocated yet. * Allocate it. */ if (!file->physblock) { retval = ext2fs_bmap(fs, file->ino, &file->inode, BMAP_BUFFER, file->ino ? BMAP_ALLOC : 0, file->blockno, &file->physblock); if (retval) return retval; } retval = io_channel_write_blk(fs->io, file->physblock, 1, file->buf); if (retval) return retval; file->flags &= ~EXT2_FILE_BUF_DIRTY; return retval; } /* * This function synchronizes the file's block buffer and the current * file position, possibly invalidating block buffer if necessary */ static errcode_t sync_buffer_position(ext2_file_t file) { blk_t b; errcode_t retval; b = file->pos / file->fs->blocksize; if (b != file->blockno) { retval = ext2fs_file_flush(file); if (retval) return retval; file->flags &= ~EXT2_FILE_BUF_VALID; } file->blockno = b; return 0; } /* * This function loads the file's block buffer with valid data from * the disk as necessary. * * If dontfill is true, then skip initializing the buffer since we're * going to be replacing its entire contents anyway. If set, then the * function basically only sets file->physblock and EXT2_FILE_BUF_VALID */ #define DONTFILL 1 static errcode_t load_buffer(ext2_file_t file, int dontfill) { ext2_filsys fs = file->fs; errcode_t retval; if (!(file->flags & EXT2_FILE_BUF_VALID)) { retval = ext2fs_bmap(fs, file->ino, &file->inode, BMAP_BUFFER, 0, file->blockno, &file->physblock); if (retval) return retval; if (!dontfill) { if (file->physblock) { retval = io_channel_read_blk(fs->io, file->physblock, 1, file->buf); if (retval) return retval; } else memset(file->buf, 0, fs->blocksize); } file->flags |= EXT2_FILE_BUF_VALID; } return 0; } errcode_t ext2fs_file_close(ext2_file_t file) { errcode_t retval; EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE); retval = ext2fs_file_flush(file); if (file->buf) ext2fs_free_mem(&file->buf); ext2fs_free_mem(&file); return retval; } errcode_t ext2fs_file_read(ext2_file_t file, void *buf, unsigned int wanted, unsigned int *got) { ext2_filsys fs; errcode_t retval = 0; unsigned int start, c, count = 0; __u64 left; char *ptr = (char *) buf; EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE); fs = file->fs; while ((file->pos < EXT2_I_SIZE(&file->inode)) && (wanted > 0)) { retval = sync_buffer_position(file); if (retval) goto fail; retval = load_buffer(file, 0); if (retval) goto fail; start = file->pos % fs->blocksize; c = fs->blocksize - start; if (c > wanted) c = wanted; left = EXT2_I_SIZE(&file->inode) - file->pos ; if (c > left) c = left; memcpy(ptr, file->buf+start, c); file->pos += c; ptr += c; count += c; wanted -= c; } fail: if (got) *got = count; return retval; } errcode_t ext2fs_file_write(ext2_file_t file, const void *buf, unsigned int nbytes, unsigned int *written) { ext2_filsys fs; errcode_t retval = 0; unsigned int start, c, count = 0; const char *ptr = (const char *) buf; EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE); fs = file->fs; if (!(file->flags & EXT2_FILE_WRITE)) return EXT2_ET_FILE_RO; while (nbytes > 0) { retval = sync_buffer_position(file); if (retval) goto fail; start = file->pos % fs->blocksize; c = fs->blocksize - start; if (c > nbytes) c = nbytes; /* * We only need to do a read-modify-update cycle if * we're doing a partial write. */ retval = load_buffer(file, (c == fs->blocksize)); if (retval) goto fail; file->flags |= EXT2_FILE_BUF_DIRTY; memcpy(file->buf+start, ptr, c); file->pos += c; ptr += c; count += c; nbytes -= c; } fail: if (written) *written = count; return retval; } errcode_t ext2fs_file_llseek(ext2_file_t file, __u64 offset, int whence, __u64 *ret_pos) { EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE); if (whence == EXT2_SEEK_SET) file->pos = offset; else if (whence == EXT2_SEEK_CUR) file->pos += offset; else if (whence == EXT2_SEEK_END) file->pos = EXT2_I_SIZE(&file->inode) + offset; else return EXT2_ET_INVALID_ARGUMENT; if (ret_pos) *ret_pos = file->pos; return 0; } errcode_t ext2fs_file_lseek(ext2_file_t file, ext2_off_t offset, int whence, ext2_off_t *ret_pos) { __u64 loffset, ret_loffset; errcode_t retval; loffset = offset; retval = ext2fs_file_llseek(file, loffset, whence, &ret_loffset); if (ret_pos) *ret_pos = (ext2_off_t) ret_loffset; return retval; } /* * This function returns the size of the file, according to the inode */ errcode_t ext2fs_file_get_lsize(ext2_file_t file, __u64 *ret_size) { if (file->magic != EXT2_ET_MAGIC_EXT2_FILE) return EXT2_ET_MAGIC_EXT2_FILE; *ret_size = EXT2_I_SIZE(&file->inode); return 0; } /* * This function returns the size of the file, according to the inode */ ext2_off_t ext2fs_file_get_size(ext2_file_t file) { __u64 size; if (ext2fs_file_get_lsize(file, &size)) return 0; if ((size >> 32) != 0) return 0; return size; } /* * This function sets the size of the file, truncating it if necessary * * XXX still need to call truncate */ errcode_t ext2fs_file_set_size(ext2_file_t file, ext2_off_t size) { errcode_t retval; EXT2_CHECK_MAGIC(file, EXT2_ET_MAGIC_EXT2_FILE); file->inode.i_size = size; file->inode.i_size_high = 0; if (file->ino) { retval = ext2fs_write_inode(file->fs, file->ino, &file->inode); if (retval) return retval; } /* * XXX truncate inode if necessary */ return 0; } e2fsprogs-1.41.14/lib/ext2fs/tst_getsize.c0000644031104000366760000000162411405316370016326 0ustar tytso/* * tst_getsize.c --- this function tests the getsize function * * Copyright (C) 1997 by Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #include #include #include #include #if HAVE_ERRNO_H #include #endif #include "ext2_fs.h" #include "ext2fs.h" int main(int argc, const char *argv[]) { errcode_t retval; blk_t blocks; if (argc < 2) { fprintf(stderr, "%s device\n", argv[0]); exit(1); } add_error_table(&et_ext2_error_table); retval = ext2fs_get_device_size(argv[1], 1024, &blocks); if (retval) { com_err(argv[0], retval, "while getting device size"); exit(1); } printf("%s is device has %u blocks.\n", argv[1], blocks); return 0; } e2fsprogs-1.41.14/lib/ext2fs/finddev.c0000644031104000366760000001021711405316370015377 0ustar tytso/* * finddev.c -- this routine attempts to find a particular device in * /dev * * Copyright (C) 2000 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #include #include #if HAVE_SYS_TYPES_H #include #endif #if HAVE_SYS_STAT_H #include #endif #include #if HAVE_ERRNO_H #include #endif #if HAVE_SYS_MKDEV_H #include #endif #include "ext2_fs.h" #include "ext2fs.h" struct dir_list { char *name; struct dir_list *next; }; /* * This function adds an entry to the directory list */ static void add_to_dirlist(const char *name, struct dir_list **list) { struct dir_list *dp; dp = malloc(sizeof(struct dir_list)); if (!dp) return; dp->name = malloc(strlen(name)+1); if (!dp->name) { free(dp); return; } strcpy(dp->name, name); dp->next = *list; *list = dp; } /* * This function frees a directory list */ static void free_dirlist(struct dir_list **list) { struct dir_list *dp, *next; for (dp = *list; dp; dp = next) { next = dp->next; free(dp->name); free(dp); } *list = 0; } static int scan_dir(char *dirname, dev_t device, struct dir_list **list, char **ret_path) { DIR *dir; struct dirent *dp; char path[1024], *cp; int dirlen; struct stat st; dirlen = strlen(dirname); if ((dir = opendir(dirname)) == NULL) return errno; dp = readdir(dir); while (dp) { if (dirlen + strlen(dp->d_name) + 2 >= sizeof(path)) goto skip_to_next; if (dp->d_name[0] == '.' && ((dp->d_name[1] == 0) || ((dp->d_name[1] == '.') && (dp->d_name[2] == 0)))) goto skip_to_next; sprintf(path, "%s/%s", dirname, dp->d_name); if (stat(path, &st) < 0) goto skip_to_next; if (S_ISDIR(st.st_mode)) add_to_dirlist(path, list); if (S_ISBLK(st.st_mode) && st.st_rdev == device) { cp = malloc(strlen(path)+1); if (!cp) { closedir(dir); return ENOMEM; } strcpy(cp, path); *ret_path = cp; goto success; } skip_to_next: dp = readdir(dir); } success: closedir(dir); return 0; } /* * This function finds the pathname to a block device with a given * device number. It returns a pointer to allocated memory to the * pathname on success, and NULL on failure. */ char *ext2fs_find_block_device(dev_t device) { struct dir_list *list = 0, *new_list = 0; struct dir_list *current; char *ret_path = 0; /* * Add the starting directories to search... */ add_to_dirlist("/devices", &list); add_to_dirlist("/devfs", &list); add_to_dirlist("/dev", &list); while (list) { current = list; list = list->next; #ifdef DEBUG printf("Scanning directory %s\n", current->name); #endif scan_dir(current->name, device, &new_list, &ret_path); free(current->name); free(current); if (ret_path) break; /* * If we're done checking at this level, descend to * the next level of subdirectories. (breadth-first) */ if (list == 0) { list = new_list; new_list = 0; } } free_dirlist(&list); free_dirlist(&new_list); return ret_path; } #ifdef DEBUG int main(int argc, char** argv) { char *devname, *tmp; int major, minor; dev_t device; const char *errmsg = "Couldn't parse %s: %s\n"; if ((argc != 2) && (argc != 3)) { fprintf(stderr, "Usage: %s device_number\n", argv[0]); fprintf(stderr, "\t: %s major minor\n", argv[0]); exit(1); } if (argc == 2) { device = strtoul(argv[1], &tmp, 0); if (*tmp) { fprintf(stderr, errmsg, "device number", argv[1]); exit(1); } } else { major = strtoul(argv[1], &tmp, 0); if (*tmp) { fprintf(stderr, errmsg, "major number", argv[1]); exit(1); } minor = strtoul(argv[2], &tmp, 0); if (*tmp) { fprintf(stderr, errmsg, "minor number", argv[2]); exit(1); } device = makedev(major, minor); printf("Looking for device 0x%04x (%d:%d)\n", device, major, minor); } devname = ext2fs_find_block_device(device); if (devname) { printf("Found device! %s\n", devname); free(devname); } else { printf("Couldn't find device.\n"); } return 0; } #endif e2fsprogs-1.41.14/lib/ext2fs/imager.c0000644031104000116100000002004411504417000015662 0ustar tytsoeng/* * image.c --- writes out the critical parts of the filesystem as a * flat file. * * Copyright (C) 2000 Theodore Ts'o. * * Note: this uses the POSIX IO interfaces, unlike most of the other * functions in this library. So sue me. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #if HAVE_ERRNO_H #include #endif #include #include #if HAVE_SYS_STAT_H #include #endif #if HAVE_SYS_TYPES_H #include #endif #include "ext2_fs.h" #include "ext2fs.h" #ifndef HAVE_TYPE_SSIZE_T typedef int ssize_t; #endif /* * This function returns 1 if the specified block is all zeros */ static int check_zero_block(char *buf, int blocksize) { char *cp = buf; int left = blocksize; while (left > 0) { if (*cp++) return 0; left--; } return 1; } /* * Write the inode table out as a single block. */ #define BUF_BLOCKS 32 errcode_t ext2fs_image_inode_write(ext2_filsys fs, int fd, int flags) { unsigned int group, left, c, d; char *buf, *cp; blk_t blk; ssize_t actual; errcode_t retval; buf = malloc(fs->blocksize * BUF_BLOCKS); if (!buf) return ENOMEM; for (group = 0; group < fs->group_desc_count; group++) { blk = fs->group_desc[(unsigned)group].bg_inode_table; if (!blk) { retval = EXT2_ET_MISSING_INODE_TABLE; goto errout; } left = fs->inode_blocks_per_group; while (left) { c = BUF_BLOCKS; if (c > left) c = left; retval = io_channel_read_blk(fs->io, blk, c, buf); if (retval) goto errout; cp = buf; while (c) { if (!(flags & IMAGER_FLAG_SPARSEWRITE)) { d = c; goto skip_sparse; } /* Skip zero blocks */ if (check_zero_block(cp, fs->blocksize)) { c--; blk++; left--; cp += fs->blocksize; lseek(fd, fs->blocksize, SEEK_CUR); continue; } /* Find non-zero blocks */ for (d=1; d < c; d++) { if (check_zero_block(cp + d*fs->blocksize, fs->blocksize)) break; } skip_sparse: actual = write(fd, cp, fs->blocksize * d); if (actual == -1) { retval = errno; goto errout; } if (actual != (ssize_t) (fs->blocksize * d)) { retval = EXT2_ET_SHORT_WRITE; goto errout; } blk += d; left -= d; cp += fs->blocksize * d; c -= d; } } } retval = 0; errout: free(buf); return retval; } /* * Read in the inode table and stuff it into place */ errcode_t ext2fs_image_inode_read(ext2_filsys fs, int fd, int flags EXT2FS_ATTR((unused))) { unsigned int group, c, left; char *buf; blk_t blk; ssize_t actual; errcode_t retval; buf = malloc(fs->blocksize * BUF_BLOCKS); if (!buf) return ENOMEM; for (group = 0; group < fs->group_desc_count; group++) { blk = fs->group_desc[(unsigned)group].bg_inode_table; if (!blk) { retval = EXT2_ET_MISSING_INODE_TABLE; goto errout; } left = fs->inode_blocks_per_group; while (left) { c = BUF_BLOCKS; if (c > left) c = left; actual = read(fd, buf, fs->blocksize * c); if (actual == -1) { retval = errno; goto errout; } if (actual != (ssize_t) (fs->blocksize * c)) { retval = EXT2_ET_SHORT_READ; goto errout; } retval = io_channel_write_blk(fs->io, blk, c, buf); if (retval) goto errout; blk += c; left -= c; } } retval = ext2fs_flush_icache(fs); errout: free(buf); return retval; } /* * Write out superblock and group descriptors */ errcode_t ext2fs_image_super_write(ext2_filsys fs, int fd, int flags EXT2FS_ATTR((unused))) { char *buf, *cp; ssize_t actual; errcode_t retval; buf = malloc(fs->blocksize); if (!buf) return ENOMEM; /* * Write out the superblock */ memset(buf, 0, fs->blocksize); memcpy(buf, fs->super, SUPERBLOCK_SIZE); actual = write(fd, buf, fs->blocksize); if (actual == -1) { retval = errno; goto errout; } if (actual != (ssize_t) fs->blocksize) { retval = EXT2_ET_SHORT_WRITE; goto errout; } /* * Now write out the block group descriptors */ cp = (char *) fs->group_desc; actual = write(fd, cp, fs->blocksize * fs->desc_blocks); if (actual == -1) { retval = errno; goto errout; } if (actual != (ssize_t) (fs->blocksize * fs->desc_blocks)) { retval = EXT2_ET_SHORT_WRITE; goto errout; } retval = 0; errout: free(buf); return retval; } /* * Read the superblock and group descriptors and overwrite them. */ errcode_t ext2fs_image_super_read(ext2_filsys fs, int fd, int flags EXT2FS_ATTR((unused))) { char *buf; ssize_t actual, size; errcode_t retval; size = fs->blocksize * (fs->group_desc_count + 1); buf = malloc(size); if (!buf) return ENOMEM; /* * Read it all in. */ actual = read(fd, buf, size); if (actual == -1) { retval = errno; goto errout; } if (actual != size) { retval = EXT2_ET_SHORT_READ; goto errout; } /* * Now copy in the superblock and group descriptors */ memcpy(fs->super, buf, SUPERBLOCK_SIZE); memcpy(fs->group_desc, buf + fs->blocksize, fs->blocksize * fs->group_desc_count); retval = 0; errout: free(buf); return retval; } /* * Write the block/inode bitmaps. */ errcode_t ext2fs_image_bitmap_write(ext2_filsys fs, int fd, int flags) { ext2fs_generic_bitmap bmap; errcode_t err, retval; ssize_t actual; __u32 itr, cnt, size; int c, total_size; char buf[1024]; if (flags & IMAGER_FLAG_INODEMAP) { if (!fs->inode_map) { retval = ext2fs_read_inode_bitmap(fs); if (retval) return retval; } bmap = fs->inode_map; err = EXT2_ET_MAGIC_INODE_BITMAP; itr = 1; cnt = EXT2_INODES_PER_GROUP(fs->super) * fs->group_desc_count; size = (EXT2_INODES_PER_GROUP(fs->super) / 8); } else { if (!fs->block_map) { retval = ext2fs_read_block_bitmap(fs); if (retval) return retval; } bmap = fs->block_map; err = EXT2_ET_MAGIC_BLOCK_BITMAP; itr = fs->super->s_first_data_block; cnt = EXT2_BLOCKS_PER_GROUP(fs->super) * fs->group_desc_count; size = EXT2_BLOCKS_PER_GROUP(fs->super) / 8; } total_size = size * fs->group_desc_count; while (cnt > 0) { size = sizeof(buf); if (size > (cnt >> 3)) size = (cnt >> 3); retval = ext2fs_get_generic_bitmap_range(bmap, err, itr, size << 3, buf); if (retval) return retval; actual = write(fd, buf, size); if (actual == -1) return errno; if (actual != (int) size) return EXT2_ET_SHORT_READ; itr += size << 3; cnt -= size << 3; } size = total_size % fs->blocksize; memset(buf, 0, sizeof(buf)); if (size) { size = fs->blocksize - size; while (size) { c = size; if (c > (int) sizeof(buf)) c = sizeof(buf); actual = write(fd, buf, c); if (actual == -1) return errno; if (actual != c) return EXT2_ET_SHORT_WRITE; size -= c; } } return 0; } /* * Read the block/inode bitmaps. */ errcode_t ext2fs_image_bitmap_read(ext2_filsys fs, int fd, int flags) { ext2fs_generic_bitmap bmap; errcode_t err, retval; __u32 itr, cnt; char buf[1024]; unsigned int size; ssize_t actual; if (flags & IMAGER_FLAG_INODEMAP) { if (!fs->inode_map) { retval = ext2fs_read_inode_bitmap(fs); if (retval) return retval; } bmap = fs->inode_map; err = EXT2_ET_MAGIC_INODE_BITMAP; itr = 1; cnt = EXT2_INODES_PER_GROUP(fs->super) * fs->group_desc_count; size = (EXT2_INODES_PER_GROUP(fs->super) / 8); } else { if (!fs->block_map) { retval = ext2fs_read_block_bitmap(fs); if (retval) return retval; } bmap = fs->block_map; err = EXT2_ET_MAGIC_BLOCK_BITMAP; itr = fs->super->s_first_data_block; cnt = EXT2_BLOCKS_PER_GROUP(fs->super) * fs->group_desc_count; size = EXT2_BLOCKS_PER_GROUP(fs->super) / 8; } while (cnt > 0) { size = sizeof(buf); if (size > (cnt >> 3)) size = (cnt >> 3); actual = read(fd, buf, size); if (actual == -1) return errno; if (actual != (int) size) return EXT2_ET_SHORT_READ; retval = ext2fs_set_generic_bitmap_range(bmap, err, itr, size << 3, buf); if (retval) return retval; itr += size << 3; cnt -= size << 3; } return 0; } e2fsprogs-1.41.14/lib/ext2fs/ext2_err.et.in0000644031104000116100000002265611504417000016756 0ustar tytsoeng# # Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. # # %Begin-Header% # This file may be redistributed under the terms of the GNU Public # License. # %End-Header% # error_table ext2 ec EXT2_ET_BASE, "EXT2FS Library version @E2FSPROGS_VERSION@" ec EXT2_ET_MAGIC_EXT2FS_FILSYS, "Wrong magic number for ext2_filsys structure" ec EXT2_ET_MAGIC_BADBLOCKS_LIST, "Wrong magic number for badblocks_list structure" ec EXT2_ET_MAGIC_BADBLOCKS_ITERATE, "Wrong magic number for badblocks_iterate structure" ec EXT2_ET_MAGIC_INODE_SCAN, "Wrong magic number for inode_scan structure" ec EXT2_ET_MAGIC_IO_CHANNEL, "Wrong magic number for io_channel structure" ec EXT2_ET_MAGIC_UNIX_IO_CHANNEL, "Wrong magic number for unix io_channel structure" ec EXT2_ET_MAGIC_IO_MANAGER, "Wrong magic number for io_manager structure" ec EXT2_ET_MAGIC_BLOCK_BITMAP, "Wrong magic number for block_bitmap structure" ec EXT2_ET_MAGIC_INODE_BITMAP, "Wrong magic number for inode_bitmap structure" ec EXT2_ET_MAGIC_GENERIC_BITMAP, "Wrong magic number for generic_bitmap structure" ec EXT2_ET_MAGIC_TEST_IO_CHANNEL, "Wrong magic number for test io_channel structure" ec EXT2_ET_MAGIC_DBLIST, "Wrong magic number for directory block list structure" ec EXT2_ET_MAGIC_ICOUNT, "Wrong magic number for icount structure" ec EXT2_ET_MAGIC_PQ_IO_CHANNEL, "Wrong magic number for Powerquest io_channel structure" ec EXT2_ET_MAGIC_EXT2_FILE, "Wrong magic number for ext2 file structure" ec EXT2_ET_MAGIC_E2IMAGE, "Wrong magic number for Ext2 Image Header" ec EXT2_ET_MAGIC_INODE_IO_CHANNEL, "Wrong magic number for inode io_channel structure" ec EXT2_ET_MAGIC_EXTENT_HANDLE, "Wrong magic number for ext4 extent handle" ec EXT2_ET_BAD_MAGIC, "Bad magic number in super-block" ec EXT2_ET_REV_TOO_HIGH, "Filesystem revision too high" ec EXT2_ET_RO_FILSYS, "Attempt to write to filesystem opened read-only" ec EXT2_ET_GDESC_READ, "Can't read group descriptors" ec EXT2_ET_GDESC_WRITE, "Can't write group descriptors" ec EXT2_ET_GDESC_BAD_BLOCK_MAP, "Corrupt group descriptor: bad block for block bitmap" ec EXT2_ET_GDESC_BAD_INODE_MAP, "Corrupt group descriptor: bad block for inode bitmap" ec EXT2_ET_GDESC_BAD_INODE_TABLE, "Corrupt group descriptor: bad block for inode table" ec EXT2_ET_INODE_BITMAP_WRITE, "Can't write an inode bitmap" ec EXT2_ET_INODE_BITMAP_READ, "Can't read an inode bitmap" ec EXT2_ET_BLOCK_BITMAP_WRITE, "Can't write an block bitmap" ec EXT2_ET_BLOCK_BITMAP_READ, "Can't read an block bitmap" ec EXT2_ET_INODE_TABLE_WRITE, "Can't write an inode table" ec EXT2_ET_INODE_TABLE_READ, "Can't read an inode table" ec EXT2_ET_NEXT_INODE_READ, "Can't read next inode" ec EXT2_ET_UNEXPECTED_BLOCK_SIZE, "Filesystem has unexpected block size" ec EXT2_ET_DIR_CORRUPTED, "EXT2 directory corrupted" ec EXT2_ET_SHORT_READ, "Attempt to read block from filesystem resulted in short read" ec EXT2_ET_SHORT_WRITE, "Attempt to write block from filesystem resulted in short write" ec EXT2_ET_DIR_NO_SPACE, "No free space in the directory" ec EXT2_ET_NO_INODE_BITMAP, "Inode bitmap not loaded" ec EXT2_ET_NO_BLOCK_BITMAP, "Block bitmap not loaded" ec EXT2_ET_BAD_INODE_NUM, "Illegal inode number" ec EXT2_ET_BAD_BLOCK_NUM, "Illegal block number" ec EXT2_ET_EXPAND_DIR_ERR, "Internal error in ext2fs_expand_dir" ec EXT2_ET_TOOSMALL, "Not enough space to build proposed filesystem" ec EXT2_ET_BAD_BLOCK_MARK, "Illegal block number passed to ext2fs_mark_block_bitmap" ec EXT2_ET_BAD_BLOCK_UNMARK, "Illegal block number passed to ext2fs_unmark_block_bitmap" ec EXT2_ET_BAD_BLOCK_TEST, "Illegal block number passed to ext2fs_test_block_bitmap" ec EXT2_ET_BAD_INODE_MARK, "Illegal inode number passed to ext2fs_mark_inode_bitmap" ec EXT2_ET_BAD_INODE_UNMARK, "Illegal inode number passed to ext2fs_unmark_inode_bitmap" ec EXT2_ET_BAD_INODE_TEST, "Illegal inode number passed to ext2fs_test_inode_bitmap" ec EXT2_ET_FUDGE_BLOCK_BITMAP_END, "Attempt to fudge end of block bitmap past the real end" ec EXT2_ET_FUDGE_INODE_BITMAP_END, "Attempt to fudge end of inode bitmap past the real end" ec EXT2_ET_BAD_IND_BLOCK, "Illegal indirect block found" ec EXT2_ET_BAD_DIND_BLOCK, "Illegal doubly indirect block found" ec EXT2_ET_BAD_TIND_BLOCK, "Illegal triply indirect block found" ec EXT2_ET_NEQ_BLOCK_BITMAP, "Block bitmaps are not the same" ec EXT2_ET_NEQ_INODE_BITMAP, "Inode bitmaps are not the same" ec EXT2_ET_BAD_DEVICE_NAME, "Illegal or malformed device name" ec EXT2_ET_MISSING_INODE_TABLE, "A block group is missing an inode table" ec EXT2_ET_CORRUPT_SUPERBLOCK, "The ext2 superblock is corrupt" ec EXT2_ET_BAD_GENERIC_MARK, "Illegal generic bit number passed to ext2fs_mark_generic_bitmap" ec EXT2_ET_BAD_GENERIC_UNMARK, "Illegal generic bit number passed to ext2fs_unmark_generic_bitmap" ec EXT2_ET_BAD_GENERIC_TEST, "Illegal generic bit number passed to ext2fs_test_generic_bitmap" ec EXT2_ET_SYMLINK_LOOP, "Too many symbolic links encountered." ec EXT2_ET_CALLBACK_NOTHANDLED, "The callback function will not handle this case" ec EXT2_ET_BAD_BLOCK_IN_INODE_TABLE, "The inode is from a bad block in the inode table" ec EXT2_ET_UNSUPP_FEATURE, "Filesystem has unsupported feature(s)" ec EXT2_ET_RO_UNSUPP_FEATURE, "Filesystem has unsupported read-only feature(s)" ec EXT2_ET_LLSEEK_FAILED, "IO Channel failed to seek on read or write" ec EXT2_ET_NO_MEMORY, "Memory allocation failed" ec EXT2_ET_INVALID_ARGUMENT, "Invalid argument passed to ext2 library" ec EXT2_ET_BLOCK_ALLOC_FAIL, "Could not allocate block in ext2 filesystem" ec EXT2_ET_INODE_ALLOC_FAIL, "Could not allocate inode in ext2 filesystem" ec EXT2_ET_NO_DIRECTORY, "Ext2 inode is not a directory" ec EXT2_ET_TOO_MANY_REFS, "Too many references in table" ec EXT2_ET_FILE_NOT_FOUND, "File not found by ext2_lookup" ec EXT2_ET_FILE_RO, "File open read-only" ec EXT2_ET_DB_NOT_FOUND, "Ext2 directory block not found" ec EXT2_ET_DIR_EXISTS, "Ext2 directory already exists" ec EXT2_ET_UNIMPLEMENTED, "Unimplemented ext2 library function" ec EXT2_ET_CANCEL_REQUESTED, "User cancel requested" ec EXT2_ET_FILE_TOO_BIG, "Ext2 file too big" ec EXT2_ET_JOURNAL_NOT_BLOCK, "Supplied journal device not a block device" ec EXT2_ET_NO_JOURNAL_SB, "Journal superblock not found" ec EXT2_ET_JOURNAL_TOO_SMALL, "Journal must be at least 1024 blocks" ec EXT2_ET_JOURNAL_UNSUPP_VERSION, "Unsupported journal version" ec EXT2_ET_LOAD_EXT_JOURNAL, "Error loading external journal" ec EXT2_ET_NO_JOURNAL, "Journal not found" ec EXT2_ET_DIRHASH_UNSUPP, "Directory hash unsupported" ec EXT2_ET_BAD_EA_BLOCK_NUM, "Illegal extended attribute block number" ec EXT2_ET_TOO_MANY_INODES, "Cannot create filesystem with requested number of inodes" ec EXT2_ET_NOT_IMAGE_FILE, "E2image snapshot not in use" ec EXT2_ET_RES_GDT_BLOCKS, "Too many reserved group descriptor blocks" ec EXT2_ET_RESIZE_INODE_CORRUPT, "Resize inode is corrupt" ec EXT2_ET_SET_BMAP_NO_IND, "Missing indirect block not present" ec EXT2_ET_TDB_SUCCESS, "TDB: Success" ec EXT2_ET_TDB_ERR_CORRUPT, "TDB: Corrupt database" ec EXT2_ET_TDB_ERR_IO, "TDB: IO Error" ec EXT2_ET_TDB_ERR_LOCK, "TDB: Locking error" ec EXT2_ET_TDB_ERR_OOM, "TDB: Out of memory" ec EXT2_ET_TDB_ERR_EXISTS, "TDB: Record exists" ec EXT2_ET_TDB_ERR_NOLOCK, "TDB: Lock exists on other keys" ec EXT2_ET_TDB_ERR_EINVAL, "TDB: Invalid parameter" ec EXT2_ET_TDB_ERR_NOEXIST, "TDB: Record does not exist" ec EXT2_ET_TDB_ERR_RDONLY, "TDB: Write not permitted" ec EXT2_ET_DBLIST_EMPTY, "Ext2fs directory block list is empty" ec EXT2_ET_RO_BLOCK_ITERATE, "Attempt to modify a block mapping via a read-only block iterator" ec EXT2_ET_MAGIC_EXTENT_PATH, "Wrong magic number for ext4 extent saved path" ec EXT2_ET_MAGIC_RESERVED_10, "Wrong magic number --- RESERVED_10" ec EXT2_ET_MAGIC_RESERVED_11, "Wrong magic number --- RESERVED_11" ec EXT2_ET_MAGIC_RESERVED_12, "Wrong magic number --- RESERVED_12" ec EXT2_ET_MAGIC_RESERVED_13, "Wrong magic number --- RESERVED_13" ec EXT2_ET_MAGIC_RESERVED_14, "Wrong magic number --- RESERVED_14" ec EXT2_ET_MAGIC_RESERVED_15, "Wrong magic number --- RESERVED_15" ec EXT2_ET_MAGIC_RESERVED_16, "Wrong magic number --- RESERVED_16" ec EXT2_ET_MAGIC_RESERVED_17, "Wrong magic number --- RESERVED_17" ec EXT2_ET_MAGIC_RESERVED_18, "Wrong magic number --- RESERVED_18" ec EXT2_ET_MAGIC_RESERVED_19, "Wrong magic number --- RESERVED_19" ec EXT2_ET_EXTENT_HEADER_BAD, "Corrupt extent header" ec EXT2_ET_EXTENT_INDEX_BAD, "Corrupt extent index" ec EXT2_ET_EXTENT_LEAF_BAD, "Corrupt extent" ec EXT2_ET_EXTENT_NO_SPACE, "No free space in extent map" ec EXT2_ET_INODE_NOT_EXTENT, "Inode does not use extents" ec EXT2_ET_EXTENT_NO_NEXT, "No 'next' extent" ec EXT2_ET_EXTENT_NO_PREV, "No 'previous' extent" ec EXT2_ET_EXTENT_NO_UP, "No 'up' extent" ec EXT2_ET_EXTENT_NO_DOWN, "No 'down' extent" ec EXT2_ET_NO_CURRENT_NODE, "No current node" ec EXT2_ET_OP_NOT_SUPPORTED, "Ext2fs operation not supported" ec EXT2_ET_CANT_INSERT_EXTENT, "No room to insert extent in node" ec EXT2_ET_CANT_SPLIT_EXTENT, "Splitting would result in empty node" ec EXT2_ET_EXTENT_NOT_FOUND, "Extent not found" ec EXT2_ET_EXTENT_NOT_SUPPORTED, "Operation not supported for inodes containing extents" ec EXT2_ET_EXTENT_INVALID_LENGTH, "Extent length is invalid" ec EXT2_ET_IO_CHANNEL_NO_SUPPORT_64, "I/O Channel does not support 64-bit block numbers" ec EXT2_NO_MTAB_FILE, "Can't check if filesystem is mounted due to missing mtab file" end e2fsprogs-1.41.14/lib/ext2fs/ext2_types.h.in0000644031104000366760000000542011240667355016511 0ustar tytso/* * If linux/types.h is already been included, assume it has defined * everything we need. (cross fingers) Other header files may have * also defined the types that we need. */ #if (!defined(_LINUX_TYPES_H) && !defined(_BLKID_TYPES_H) && \ !defined(_EXT2_TYPES_H)) #define _EXT2_TYPES_H @ASM_TYPES_HEADER@ #ifdef __U8_TYPEDEF typedef __U8_TYPEDEF __u8; #else typedef unsigned char __u8; #endif #ifdef __S8_TYPEDEF typedef __S8_TYPEDEF __s8; #else typedef signed char __s8; #endif #ifdef __U16_TYPEDEF typedef __U16_TYPEDEF __u16; #else #if (@SIZEOF_INT@ == 2) typedef unsigned int __u16; #else #if (@SIZEOF_SHORT@ == 2) typedef unsigned short __u16; #else ?==error: undefined 16 bit type #endif /* SIZEOF_SHORT == 2 */ #endif /* SIZEOF_INT == 2 */ #endif /* __U16_TYPEDEF */ #ifdef __S16_TYPEDEF typedef __S16_TYPEDEF __s16; #else #if (@SIZEOF_INT@ == 2) typedef int __s16; #else #if (@SIZEOF_SHORT@ == 2) typedef short __s16; #else ?==error: undefined 16 bit type #endif /* SIZEOF_SHORT == 2 */ #endif /* SIZEOF_INT == 2 */ #endif /* __S16_TYPEDEF */ #ifdef __U32_TYPEDEF typedef __U32_TYPEDEF __u32; #else #if (@SIZEOF_INT@ == 4) typedef unsigned int __u32; #else #if (@SIZEOF_LONG@ == 4) typedef unsigned long __u32; #else #if (@SIZEOF_SHORT@ == 4) typedef unsigned short __u32; #else ?== error: undefined 32 bit type #endif /* SIZEOF_SHORT == 4 */ #endif /* SIZEOF_LONG == 4 */ #endif /* SIZEOF_INT == 4 */ #endif /* __U32_TYPEDEF */ #ifdef __S32_TYPEDEF typedef __S32_TYPEDEF __s32; #else #if (@SIZEOF_INT@ == 4) typedef int __s32; #else #if (@SIZEOF_LONG@ == 4) typedef long __s32; #else #if (@SIZEOF_SHORT@ == 4) typedef short __s32; #else ?== error: undefined 32 bit type #endif /* SIZEOF_SHORT == 4 */ #endif /* SIZEOF_LONG == 4 */ #endif /* SIZEOF_INT == 4 */ #endif /* __S32_TYPEDEF */ #ifdef __U64_TYPEDEF typedef __U64_TYPEDEF __u64; #else #if (@SIZEOF_INT@ == 8) typedef unsigned int __u64; #else #if (@SIZEOF_LONG@ == 8) typedef unsigned long __u64; #else #if (@SIZEOF_LONG_LONG@ == 8) typedef unsigned long long __u64; #endif /* SIZEOF_LONG_LONG == 8 */ #endif /* SIZEOF_LONG == 8 */ #endif /* SIZEOF_INT == 8 */ #endif /* __U64_TYPEDEF */ #ifdef __S64_TYPEDEF typedef __S64_TYPEDEF __s64; #else #if (@SIZEOF_INT@ == 8) typedef int __s64; #else #if (@SIZEOF_LONG@ == 8) typedef long __s64; #else #if (@SIZEOF_LONG_LONG@ == 8) #if defined(__GNUC__) typedef __signed__ long long __s64; #else typedef signed long long __s64; #endif /* __GNUC__ */ #endif /* SIZEOF_LONG_LONG == 8 */ #endif /* SIZEOF_LONG == 8 */ #endif /* SIZEOF_INT == 8 */ #endif /* __S64_TYPEDEF */ #undef __S8_TYPEDEF #undef __U8_TYPEDEF #undef __S16_TYPEDEF #undef __U16_TYPEDEF #undef __S32_TYPEDEF #undef __U32_TYPEDEF #undef __S64_TYPEDEF #undef __U64_TYPEDEF #endif /* _*_TYPES_H */ @PUBLIC_CONFIG_HEADER@ e2fsprogs-1.41.14/lib/ext2fs/i_block.c0000644031104000116100000000420511504417000016021 0ustar tytsoeng/* * i_block.c --- Manage the i_block field for i_blocks * * Copyright (C) 2008 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #include #if HAVE_UNISTD_H #include #endif #include #include #if HAVE_SYS_STAT_H #include #endif #if HAVE_SYS_TYPES_H #include #endif #include #include "ext2_fs.h" #include "ext2fs.h" errcode_t ext2fs_iblk_add_blocks(ext2_filsys fs, struct ext2_inode *inode, blk64_t num_blocks) { unsigned long long b = inode->i_blocks; if (!(fs->super->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_HUGE_FILE) || !(inode->i_flags & EXT4_HUGE_FILE_FL)) num_blocks *= fs->blocksize / 512; b += num_blocks; if (fs->super->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_HUGE_FILE) { b += ((long long) inode->osd2.linux2.l_i_blocks_hi) << 32; inode->osd2.linux2.l_i_blocks_hi = b >> 32; } else if (b > 0xFFFFFFFF) return EOVERFLOW; inode->i_blocks = b & 0xFFFFFFFF; return 0; } errcode_t ext2fs_iblk_sub_blocks(ext2_filsys fs, struct ext2_inode *inode, blk64_t num_blocks) { unsigned long long b = inode->i_blocks; if (!(fs->super->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_HUGE_FILE) || !(inode->i_flags & EXT4_HUGE_FILE_FL)) num_blocks *= fs->blocksize / 512; if (num_blocks > b) return EOVERFLOW; b -= num_blocks; if (fs->super->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_HUGE_FILE) { b += ((long long) inode->osd2.linux2.l_i_blocks_hi) << 32; inode->osd2.linux2.l_i_blocks_hi = b >> 32; } inode->i_blocks = b & 0xFFFFFFFF; return 0; } errcode_t ext2fs_iblk_set(ext2_filsys fs, struct ext2_inode *inode, blk64_t b) { if (!(fs->super->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_HUGE_FILE) || !(inode->i_flags & EXT4_HUGE_FILE_FL)) b *= fs->blocksize / 512; inode->i_blocks = b & 0xFFFFFFFF; if (fs->super->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_HUGE_FILE) inode->osd2.linux2.l_i_blocks_hi = b >> 32; else if (b >> 32) return EOVERFLOW; return 0; } e2fsprogs-1.41.14/lib/ext2fs/bb_inode.c0000644031104000116100000001417211504417000016164 0ustar tytsoeng/* * bb_inode.c --- routines to update the bad block inode. * * WARNING: This routine modifies a lot of state in the filesystem; if * this routine returns an error, the bad block inode may be in an * inconsistent state. * * Copyright (C) 1994, 1995 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #include #include #if HAVE_SYS_STAT_H #include #endif #if HAVE_SYS_TYPES_H #include #endif #include "ext2_fs.h" #include "ext2fs.h" struct set_badblock_record { ext2_badblocks_iterate bb_iter; int bad_block_count; blk_t *ind_blocks; int max_ind_blocks; int ind_blocks_size; int ind_blocks_ptr; char *block_buf; errcode_t err; }; static int set_bad_block_proc(ext2_filsys fs, blk_t *block_nr, e2_blkcnt_t blockcnt, blk_t ref_block, int ref_offset, void *priv_data); static int clear_bad_block_proc(ext2_filsys fs, blk_t *block_nr, e2_blkcnt_t blockcnt, blk_t ref_block, int ref_offset, void *priv_data); /* * Given a bad blocks bitmap, update the bad blocks inode to reflect * the map. */ errcode_t ext2fs_update_bb_inode(ext2_filsys fs, ext2_badblocks_list bb_list) { errcode_t retval; struct set_badblock_record rec; struct ext2_inode inode; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); if (!fs->block_map) return EXT2_ET_NO_BLOCK_BITMAP; rec.bad_block_count = 0; rec.ind_blocks_size = rec.ind_blocks_ptr = 0; rec.max_ind_blocks = 10; retval = ext2fs_get_array(rec.max_ind_blocks, sizeof(blk_t), &rec.ind_blocks); if (retval) return retval; memset(rec.ind_blocks, 0, rec.max_ind_blocks * sizeof(blk_t)); retval = ext2fs_get_mem(fs->blocksize, &rec.block_buf); if (retval) goto cleanup; memset(rec.block_buf, 0, fs->blocksize); rec.err = 0; /* * First clear the old bad blocks (while saving the indirect blocks) */ retval = ext2fs_block_iterate2(fs, EXT2_BAD_INO, BLOCK_FLAG_DEPTH_TRAVERSE, 0, clear_bad_block_proc, &rec); if (retval) goto cleanup; if (rec.err) { retval = rec.err; goto cleanup; } /* * Now set the bad blocks! * * First, mark the bad blocks as used. This prevents a bad * block from being used as an indirecto block for the bad * block inode (!). */ if (bb_list) { retval = ext2fs_badblocks_list_iterate_begin(bb_list, &rec.bb_iter); if (retval) goto cleanup; retval = ext2fs_block_iterate2(fs, EXT2_BAD_INO, BLOCK_FLAG_APPEND, 0, set_bad_block_proc, &rec); ext2fs_badblocks_list_iterate_end(rec.bb_iter); if (retval) goto cleanup; if (rec.err) { retval = rec.err; goto cleanup; } } /* * Update the bad block inode's mod time and block count * field. */ retval = ext2fs_read_inode(fs, EXT2_BAD_INO, &inode); if (retval) goto cleanup; inode.i_atime = inode.i_mtime = fs->now ? fs->now : time(0); if (!inode.i_ctime) inode.i_ctime = fs->now ? fs->now : time(0); ext2fs_iblk_set(fs, &inode, rec.bad_block_count); inode.i_size = rec.bad_block_count * fs->blocksize; retval = ext2fs_write_inode(fs, EXT2_BAD_INO, &inode); if (retval) goto cleanup; cleanup: ext2fs_free_mem(&rec.ind_blocks); ext2fs_free_mem(&rec.block_buf); return retval; } /* * Helper function for update_bb_inode() * * Clear the bad blocks in the bad block inode, while saving the * indirect blocks. */ #ifdef __TURBOC__ #pragma argsused #endif static int clear_bad_block_proc(ext2_filsys fs, blk_t *block_nr, e2_blkcnt_t blockcnt, blk_t ref_block EXT2FS_ATTR((unused)), int ref_offset EXT2FS_ATTR((unused)), void *priv_data) { struct set_badblock_record *rec = (struct set_badblock_record *) priv_data; errcode_t retval; unsigned long old_size; if (!*block_nr) return 0; /* * If the block number is outrageous, clear it and ignore it. */ if (*block_nr >= fs->super->s_blocks_count || *block_nr < fs->super->s_first_data_block) { *block_nr = 0; return BLOCK_CHANGED; } if (blockcnt < 0) { if (rec->ind_blocks_size >= rec->max_ind_blocks) { old_size = rec->max_ind_blocks * sizeof(blk_t); rec->max_ind_blocks += 10; retval = ext2fs_resize_mem(old_size, rec->max_ind_blocks * sizeof(blk_t), &rec->ind_blocks); if (retval) { rec->max_ind_blocks -= 10; rec->err = retval; return BLOCK_ABORT; } } rec->ind_blocks[rec->ind_blocks_size++] = *block_nr; } /* * Mark the block as unused, and update accounting information */ ext2fs_block_alloc_stats(fs, *block_nr, -1); *block_nr = 0; return BLOCK_CHANGED; } /* * Helper function for update_bb_inode() * * Set the block list in the bad block inode, using the supplied bitmap. */ #ifdef __TURBOC__ #pragma argsused #endif static int set_bad_block_proc(ext2_filsys fs, blk_t *block_nr, e2_blkcnt_t blockcnt, blk_t ref_block EXT2FS_ATTR((unused)), int ref_offset EXT2FS_ATTR((unused)), void *priv_data) { struct set_badblock_record *rec = (struct set_badblock_record *) priv_data; errcode_t retval; blk_t blk; if (blockcnt >= 0) { /* * Get the next bad block. */ if (!ext2fs_badblocks_list_iterate(rec->bb_iter, &blk)) return BLOCK_ABORT; rec->bad_block_count++; } else { /* * An indirect block; fetch a block from the * previously used indirect block list. The block * most be not marked as used; if so, get another one. * If we run out of reserved indirect blocks, allocate * a new one. */ retry: if (rec->ind_blocks_ptr < rec->ind_blocks_size) { blk = rec->ind_blocks[rec->ind_blocks_ptr++]; if (ext2fs_test_block_bitmap(fs->block_map, blk)) goto retry; } else { retval = ext2fs_new_block(fs, 0, 0, &blk); if (retval) { rec->err = retval; return BLOCK_ABORT; } } retval = io_channel_write_blk(fs->io, blk, 1, rec->block_buf); if (retval) { rec->err = retval; return BLOCK_ABORT; } } /* * Update block counts */ ext2fs_block_alloc_stats(fs, blk, +1); *block_nr = blk; return BLOCK_CHANGED; } e2fsprogs-1.41.14/lib/ext2fs/ext2fs.pc.in0000644031104000366760000000035511240667355015773 0ustar tytsoprefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: ext2fs Description: Ext2fs library Version: @E2FSPROGS_VERSION@ Requires.private: com_err Cflags: -I${includedir}/ext2fs Libs: -L${libdir} -lext2fs e2fsprogs-1.41.14/lib/ext2fs/Makefile.in0000644031104000116100000007474511504417000016340 0ustar tytsoengsrcdir = @srcdir@ top_srcdir = @top_srcdir@ VPATH = @srcdir@ top_builddir = ../.. my_dir = lib/ext2fs INSTALL = @INSTALL@ @MCONFIG@ @DEBUGFS_CMT@DEBUGFS_LIB_OBJS = bb_compat.o inode_io.o write_bb_file.o MK_CMDS= _SS_DIR_OVERRIDE=../ss ../ss/mk_cmds @RESIZER_CMT@RESIZE_LIB_OBJS = dupfs.o @TEST_IO_CMT@TEST_IO_LIB_OBJS = test_io.o @IMAGER_CMT@E2IMAGE_LIB_OBJS = imager.o OBJS= $(DEBUGFS_LIB_OBJS) $(RESIZE_LIB_OBJS) $(E2IMAGE_LIB_OBJS) \ $(TEST_IO_LIB_OBJS) \ ext2_err.o \ alloc.o \ alloc_sb.o \ alloc_stats.o \ alloc_tables.o \ badblocks.o \ bb_inode.o \ bitmaps.o \ bitops.o \ block.o \ bmap.o \ check_desc.o \ closefs.o \ crc16.o \ csum.o \ dblist.o \ dblist_dir.o \ dirblock.o \ dirhash.o \ dir_iterate.o \ expanddir.o \ ext_attr.o \ extent.o \ fileio.o \ finddev.o \ flushb.o \ freefs.o \ gen_bitmap.o \ get_pathname.o \ getsize.o \ getsectsize.o \ i_block.o \ icount.o \ ind_block.o \ initialize.o \ inline.o \ inode.o \ io_manager.o \ ismounted.o \ link.o \ llseek.o \ lookup.o \ mkdir.o \ mkjournal.o \ namei.o \ native.o \ newdir.o \ openfs.o \ read_bb.o \ read_bb_file.o \ res_gdt.o \ rw_bitmaps.o \ swapfs.o \ tdb.o \ undo_io.o \ unix_io.o \ unlink.o \ valid_blk.o \ version.o SRCS= ext2_err.c \ $(srcdir)/alloc.c \ $(srcdir)/alloc_sb.c \ $(srcdir)/alloc_stats.c \ $(srcdir)/alloc_tables.c \ $(srcdir)/badblocks.c \ $(srcdir)/bb_compat.c \ $(srcdir)/bb_inode.c \ $(srcdir)/bitmaps.c \ $(srcdir)/bitops.c \ $(srcdir)/block.c \ $(srcdir)/bmap.c \ $(srcdir)/check_desc.c \ $(srcdir)/closefs.c \ $(srcdir)/crc16.c \ $(srcdir)/csum.c \ $(srcdir)/dblist.c \ $(srcdir)/dblist_dir.c \ $(srcdir)/dirblock.c \ $(srcdir)/dirhash.c \ $(srcdir)/dir_iterate.c \ $(srcdir)/dupfs.c \ $(srcdir)/expanddir.c \ $(srcdir)/ext_attr.c \ $(srcdir)/extent.c \ $(srcdir)/fileio.c \ $(srcdir)/finddev.c \ $(srcdir)/flushb.c \ $(srcdir)/freefs.c \ $(srcdir)/gen_bitmap.c \ $(srcdir)/get_pathname.c \ $(srcdir)/getsize.c \ $(srcdir)/getsectsize.c \ $(srcdir)/i_block.c \ $(srcdir)/icount.c \ $(srcdir)/ind_block.c \ $(srcdir)/initialize.c \ $(srcdir)/inline.c \ $(srcdir)/inode.c \ $(srcdir)/inode_io.c \ $(srcdir)/imager.c \ $(srcdir)/io_manager.c \ $(srcdir)/ismounted.c \ $(srcdir)/link.c \ $(srcdir)/llseek.c \ $(srcdir)/lookup.c \ $(srcdir)/mkdir.c \ $(srcdir)/mkjournal.c \ $(srcdir)/namei.c \ $(srcdir)/native.c \ $(srcdir)/newdir.c \ $(srcdir)/openfs.c \ $(srcdir)/read_bb.c \ $(srcdir)/read_bb_file.c \ $(srcdir)/res_gdt.c \ $(srcdir)/rw_bitmaps.c \ $(srcdir)/swapfs.c \ $(srcdir)/tdb.c \ $(srcdir)/test_io.c \ $(srcdir)/tst_badblocks.c \ $(srcdir)/tst_bitops.c \ $(srcdir)/tst_byteswap.c \ $(srcdir)/tst_getsize.c \ $(srcdir)/tst_iscan.c \ $(srcdir)/undo_io.c \ $(srcdir)/unix_io.c \ $(srcdir)/unlink.c \ $(srcdir)/valid_blk.c \ $(srcdir)/version.c \ $(srcdir)/write_bb_file.c HFILES= bitops.h ext2fs.h ext2_io.h ext2_fs.h ext2_ext_attr.h ext3_extents.h \ tdb.h HFILES_IN= ext2_err.h ext2_types.h LIBRARY= libext2fs LIBDIR= ext2fs ELF_VERSION = 2.4 ELF_SO_VERSION = 2 ELF_IMAGE = libext2fs ELF_MYDIR = ext2fs ELF_INSTALL_DIR = $(root_libdir) ELF_OTHER_LIBS = -L../.. -lcom_err BSDLIB_VERSION = 2.1 BSDLIB_IMAGE = libext2fs BSDLIB_MYDIR = ext2fs BSDLIB_INSTALL_DIR = $(root_libdir) @MAKEFILE_LIBRARY@ @MAKEFILE_ELF@ @MAKEFILE_BSDLIB@ @MAKEFILE_PROFILE@ @MAKEFILE_CHECKER@ all:: ext2fs.pc .c.o: $(E) " CC $<" $(Q) $(CC) $(ALL_CFLAGS) -c $< -o $@ @PROFILE_CMT@ $(Q) $(CC) $(ALL_CFLAGS) -g -pg -o profiled/$*.o -c $< @CHECKER_CMT@ $(Q) $(CC) $(ALL_CFLAGS) -checker -g -o checker/$*.o -c $< @ELF_CMT@ $(Q) $(CC) $(ALL_CFLAGS) -fPIC -o elfshared/$*.o -c $< @BSDLIB_CMT@ $(Q) $(CC) $(ALL_CFLAGS) $(BSDLIB_PIC_FLAG) -o pic/$*.o -c $< COMPILE_ET=../et/compile_et --build-tree DISTFILES= Makefile *.c *.h image ext2_err.et: $(DEP_SUBSTITUTE) $(srcdir)/ext2_err.et.in $(E) " SUBST $@" $(Q) $(SUBSTITUTE) $(srcdir)/ext2_err.et.in ext2_err.et ext2_err.c ext2_err.h: ext2_err.et $(E) " COMPILE_ET ext2_err.et" $(Q) $(COMPILE_ET) ext2_err.et ext2fs.pc: $(srcdir)/ext2fs.pc.in $(top_builddir)/config.status $(E) " CONFIG.STATUS $@" $(Q) cd $(top_builddir); CONFIG_FILES=lib/ext2fs/ext2fs.pc ./config.status tst_badblocks: tst_badblocks.o $(STATIC_LIBEXT2FS) $(DEPLIBCOM_ERR) $(E) " LD $@" $(Q) $(CC) -o tst_badblocks tst_badblocks.o $(STATIC_LIBEXT2FS) \ $(LIBCOM_ERR) tst_icount: $(srcdir)/icount.c $(STATIC_LIBEXT2FS) $(DEPLIBCOM_ERR) $(E) " LD $@" $(Q) $(CC) -o tst_icount $(srcdir)/icount.c -DDEBUG $(ALL_CFLAGS) \ $(STATIC_LIBEXT2FS) $(LIBCOM_ERR) tst_iscan: tst_iscan.o $(STATIC_LIBEXT2FS) $(DEPLIBCOM_ERR) $(E) " LD $@" $(Q) $(CC) -o tst_iscan tst_iscan.o $(STATIC_LIBEXT2FS) $(LIBCOM_ERR) tst_getsize: tst_getsize.o $(STATIC_LIBEXT2FS) $(DEPLIBCOM_ERR) $(E) " LD $@" $(Q) $(CC) -o tst_getsize tst_getsize.o $(STATIC_LIBEXT2FS) \ $(LIBCOM_ERR) tst_ismounted: $(srcdir)/ismounted.c $(STATIC_LIBEXT2FS) $(DEPLIBCOM_ERR) $(E) " LD $@" $(Q) $(CC) -o tst_ismounted $(srcdir)/ismounted.c \ $(STATIC_LIBEXT2FS) -DDEBUG $(ALL_CFLAGS) \ $(LIBCOM_ERR) tst_byteswap: tst_byteswap.o $(STATIC_LIBEXT2FS) $(DEPLIBCOM_ERR) $(E) " LD $@" $(Q) $(CC) -o tst_byteswap tst_byteswap.o $(STATIC_LIBEXT2FS) \ $(LIBCOM_ERR) tst_bitops: tst_bitops.o $(STATIC_LIBEXT2FS) $(DEPLIBCOM_ERR) $(E) " LD $@" $(Q) $(CC) -o tst_bitops tst_bitops.o $(ALL_CFLAGS) \ $(STATIC_LIBEXT2FS) $(LIBCOM_ERR) tst_getsectsize: tst_getsectsize.o $(STATIC_LIBEXT2FS) $(DEPLIBCOM_ERR) $(E) " LD $@" $(Q) $(CC) -o tst_sectgetsize tst_getsectsize.o \ $(STATIC_LIBEXT2FS) $(LIBCOM_ERR) tst_types.o: $(srcdir)/tst_types.c ext2_types.h tst_types: tst_types.o ext2_types.h $(E) " LD $@" $(Q) $(CC) -o tst_types tst_types.o tst_super_size.o: $(srcdir)/tst_super_size.c $(srcdir)/ext2_fs.h tst_super_size: tst_super_size.o $(E) " LD $@" $(Q) $(CC) -o tst_super_size tst_super_size.o ext2_tdbtool: tdbtool.o $(E) " LD $@" $(Q) $(CC) -o ext2_tdbtool tdbtool.o tdb.o extent_dbg.c: $(srcdir)/extent_dbg.ct $(E) " MK_CMDS $<" $(Q) $(MK_CMDS) $(srcdir)/extent_dbg.ct debug_cmds.c debug_cmds.h: $(top_srcdir)/debugfs/debug_cmds.ct $(E) " MK_CMDS $<@" $(Q) $(MK_CMDS) $(top_srcdir)/debugfs/debug_cmds.ct DEBUG_OBJS= debug_cmds.o debugfs.o util.o ncheck.o icheck.o ls.o \ lsdel.o dump.o set_fields.o logdump.o htree.o unused.o debugfs.o: $(top_srcdir)/debugfs/debugfs.c $(E) " CC $<" $(Q) $(CC) $(ALL_CFLAGS) -c $< -o $@ util.o: $(top_srcdir)/debugfs/util.c $(E) " CC $<" $(Q) $(CC) $(ALL_CFLAGS) -c $< -o $@ ncheck.o: $(top_srcdir)/debugfs/ncheck.c $(E) " CC $<" $(Q) $(CC) $(ALL_CFLAGS) -c $< -o $@ icheck.o: $(top_srcdir)/debugfs/icheck.c $(E) " CC $<" $(Q) $(CC) $(ALL_CFLAGS) -c $< -o $@ ls.o: $(top_srcdir)/debugfs/ls.c $(E) " CC $<" $(Q) $(CC) $(ALL_CFLAGS) -c $< -o $@ lsdel.o: $(top_srcdir)/debugfs/lsdel.c $(E) " CC $<" $(Q) $(CC) $(ALL_CFLAGS) -c $< -o $@ dump.o: $(top_srcdir)/debugfs/dump.c $(E) " CC $<" $(Q) $(CC) $(ALL_CFLAGS) -c $< -o $@ set_fields.o: $(top_srcdir)/debugfs/set_fields.c $(E) " CC $<" $(Q) $(CC) $(ALL_CFLAGS) -c $< -o $@ logdump.o: $(top_srcdir)/debugfs/logdump.c $(E) " CC $<" $(Q) $(CC) $(ALL_CFLAGS) -c $< -o $@ htree.o: $(top_srcdir)/debugfs/htree.c $(E) " CC $<" $(Q) $(CC) $(ALL_CFLAGS) -c $< -o $@ unused.o: $(top_srcdir)/debugfs/unused.c $(E) " CC $<" $(Q) $(CC) $(ALL_CFLAGS) -c $< -o $@ tst_extents: $(srcdir)/extent.c extent_dbg.c $(DEBUG_OBJS) $(DEPLIBSS) \ $(LIBE2P) $(DEPLIBUUID) $(DEPLIBBLKID) $(DEPLIBCOM_ERR) $(E) " LD $@" $(Q) $(CC) -o tst_extents $(srcdir)/extent.c extent_dbg.c \ $(ALL_CFLAGS) -DDEBUG $(DEBUG_OBJS) $(LIBSS) $(LIBE2P) \ $(STATIC_LIBEXT2FS) $(LIBBLKID) $(LIBUUID) $(LIBCOM_ERR) \ -I $(top_srcdir)/debugfs tst_csum: csum.c $(STATIC_LIBEXT2FS) $(DEPLIBCOM_ERR) \ $(top_srcdir)/lib/e2p/e2p.h $(E) " LD $@" $(Q) $(CC) -o tst_csum $(srcdir)/csum.c -DDEBUG \ $(ALL_CFLAGS) $(STATIC_LIBEXT2FS) $(LIBCOM_ERR) $(LIBE2P) mkjournal: mkjournal.c $(STATIC_LIBEXT2FS) $(DEPLIBCOM_ERR) $(E) " LD $@" $(Q) $(CC) -o mkjournal $(srcdir)/mkjournal.c -DDEBUG $(STATIC_LIBEXT2FS) $(LIBCOM_ERR) $(ALL_CFLAGS) check:: tst_bitops tst_badblocks tst_iscan tst_types tst_icount tst_super_size tst_types tst_csum LD_LIBRARY_PATH=$(LIB) DYLD_LIBRARY_PATH=$(LIB) ./tst_bitops LD_LIBRARY_PATH=$(LIB) DYLD_LIBRARY_PATH=$(LIB) ./tst_badblocks LD_LIBRARY_PATH=$(LIB) DYLD_LIBRARY_PATH=$(LIB) ./tst_iscan LD_LIBRARY_PATH=$(LIB) DYLD_LIBRARY_PATH=$(LIB) ./tst_types LD_LIBRARY_PATH=$(LIB) DYLD_LIBRARY_PATH=$(LIB) ./tst_icount LD_LIBRARY_PATH=$(LIB) DYLD_LIBRARY_PATH=$(LIB) ./tst_super_size LD_LIBRARY_PATH=$(LIB) DYLD_LIBRARY_PATH=$(LIB) ./tst_csum installdirs:: $(E) " MKINSTALLDIRS $(libdir) $(includedir)/ext2fs" $(Q) $(MKINSTALLDIRS) $(DESTDIR)$(libdir) \ $(DESTDIR)$(includedir)/ext2fs $(DESTDIR)$(libdir)/pkgconfig install:: all $(HFILES) $(HFILES_IN) installdirs ext2fs.pc $(E) " INSTALL_DATA $(libdir)/libext2fs.a" $(Q) $(INSTALL_DATA) libext2fs.a $(DESTDIR)$(libdir)/libext2fs.a -$(Q) $(RANLIB) $(DESTDIR)$(libdir)/libext2fs.a $(Q) $(CHMOD) $(LIBMODE) $(DESTDIR)$(libdir)/libext2fs.a $(Q) for i in $(HFILES); do \ echo " INSTALL_DATA $(includedir)/ext2fs/$$i"; \ $(INSTALL_DATA) $(srcdir)/$$i $(DESTDIR)$(includedir)/ext2fs/$$i; \ done $(Q) for i in $(HFILES_IN); do \ echo " INSTALL_DATA $(includedir)/ext2fs/$$i"; \ $(INSTALL_DATA) $$i $(DESTDIR)$(includedir)/ext2fs/$$i; \ done $(E) " INSTALL_DATA $(libdir)/pkgconfig/ext2fs.pc" $(Q) $(INSTALL_DATA) ext2fs.pc $(DESTDIR)$(libdir)/pkgconfig/ext2fs.pc uninstall:: $(RM) -f $(DESTDIR)$(libdir)/libext2fs.a \ $(DESTDIR)$(libdir)/pkgconfig/ext2fs.pc $(RM) -rf $(DESTDIR)$(includedir)/ext2fs clean:: $(RM) -f \#* *.s *.o *.a *~ *.bak core profiled/* checker/* \ tst_badblocks tst_iscan ext2_err.et ext2_err.c ext2_err.h \ tst_byteswap tst_ismounted tst_getsize tst_sectgetsize \ tst_bitops tst_types tst_icount tst_super_size tst_csum \ ext2_tdbtool mkjournal debug_cmds.c \ ../libext2fs.a ../libext2fs_p.a ../libext2fs_chk.a mostlyclean:: clean distclean:: clean $(RM) -f .depend ext2_err.c ext2_err.h Makefile ext2fs.pc \ $(srcdir)/TAGS $(srcdir)/Makefile.in.old # # Hack to parallel makes recognize dependencies correctly. # $(top_builddir)/lib/ext2fs/ext2_err.h: ext2_err.h $(OBJS): subdirs # +++ Dependency line eater +++ # # Makefile dependencies follow. This must be the last section in # the Makefile.in file # ext2_err.o: ext2_err.c alloc.o: $(srcdir)/alloc.c $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ $(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h alloc_sb.o: $(srcdir)/alloc_sb.c $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ $(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h alloc_stats.o: $(srcdir)/alloc_stats.c $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ $(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h alloc_tables.o: $(srcdir)/alloc_tables.c $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ $(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h badblocks.o: $(srcdir)/badblocks.c $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fsP.h \ $(srcdir)/ext2fs.h $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h \ $(top_srcdir)/lib/et/com_err.h $(srcdir)/ext2_io.h \ $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/ext2_ext_attr.h \ $(srcdir)/bitops.h bb_compat.o: $(srcdir)/bb_compat.c $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fsP.h \ $(srcdir)/ext2fs.h $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h \ $(top_srcdir)/lib/et/com_err.h $(srcdir)/ext2_io.h \ $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/ext2_ext_attr.h \ $(srcdir)/bitops.h bb_inode.o: $(srcdir)/bb_inode.c $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ $(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h bitmaps.o: $(srcdir)/bitmaps.c $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ $(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h bitops.o: $(srcdir)/bitops.c $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ $(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h block.o: $(srcdir)/block.c $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ $(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h bmap.o: $(srcdir)/bmap.c $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ $(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h check_desc.o: $(srcdir)/check_desc.c $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ $(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h closefs.o: $(srcdir)/closefs.c $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fsP.h \ $(srcdir)/ext2fs.h $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h \ $(top_srcdir)/lib/et/com_err.h $(srcdir)/ext2_io.h \ $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/ext2_ext_attr.h \ $(srcdir)/bitops.h crc16.o: $(srcdir)/crc16.c $(top_builddir)/lib/ext2fs/ext2_types.h \ $(srcdir)/crc16.h csum.o: $(srcdir)/csum.c $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ $(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h $(srcdir)/crc16.h dblist.o: $(srcdir)/dblist.c $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fsP.h \ $(srcdir)/ext2fs.h $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h \ $(top_srcdir)/lib/et/com_err.h $(srcdir)/ext2_io.h \ $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/ext2_ext_attr.h \ $(srcdir)/bitops.h dblist_dir.o: $(srcdir)/dblist_dir.c $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fsP.h \ $(srcdir)/ext2fs.h $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h \ $(top_srcdir)/lib/et/com_err.h $(srcdir)/ext2_io.h \ $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/ext2_ext_attr.h \ $(srcdir)/bitops.h dirblock.o: $(srcdir)/dirblock.c $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ $(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h dirhash.o: $(srcdir)/dirhash.c $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ $(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h dir_iterate.o: $(srcdir)/dir_iterate.c $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fsP.h \ $(srcdir)/ext2fs.h $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h \ $(top_srcdir)/lib/et/com_err.h $(srcdir)/ext2_io.h \ $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/ext2_ext_attr.h \ $(srcdir)/bitops.h dupfs.o: $(srcdir)/dupfs.c $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fsP.h \ $(srcdir)/ext2fs.h $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h \ $(top_srcdir)/lib/et/com_err.h $(srcdir)/ext2_io.h \ $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/ext2_ext_attr.h \ $(srcdir)/bitops.h expanddir.o: $(srcdir)/expanddir.c $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ $(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h ext_attr.o: $(srcdir)/ext_attr.c $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2_ext_attr.h \ $(srcdir)/ext2fs.h $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h \ $(top_srcdir)/lib/et/com_err.h $(srcdir)/ext2_io.h \ $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/ext2_ext_attr.h \ $(srcdir)/bitops.h extent.o: $(srcdir)/extent.c $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fsP.h \ $(srcdir)/ext2fs.h $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h \ $(top_srcdir)/lib/et/com_err.h $(srcdir)/ext2_io.h \ $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/ext2_ext_attr.h \ $(srcdir)/bitops.h $(srcdir)/e2image.h fileio.o: $(srcdir)/fileio.c $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ $(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h finddev.o: $(srcdir)/finddev.c $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ $(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h flushb.o: $(srcdir)/flushb.c $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ $(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h freefs.o: $(srcdir)/freefs.c $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fsP.h \ $(srcdir)/ext2fs.h $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h \ $(top_srcdir)/lib/et/com_err.h $(srcdir)/ext2_io.h \ $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/ext2_ext_attr.h \ $(srcdir)/bitops.h gen_bitmap.o: $(srcdir)/gen_bitmap.c $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ $(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h get_pathname.o: $(srcdir)/get_pathname.c $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ $(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h getsize.o: $(srcdir)/getsize.c $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ $(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h getsectsize.o: $(srcdir)/getsectsize.c $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ $(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h i_block.o: $(srcdir)/i_block.c $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ $(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h icount.o: $(srcdir)/icount.c $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ $(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h $(srcdir)/tdb.h ind_block.o: $(srcdir)/ind_block.c $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ $(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h initialize.o: $(srcdir)/initialize.c $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ $(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h inline.o: $(srcdir)/inline.c $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ $(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h inode.o: $(srcdir)/inode.c $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fsP.h \ $(srcdir)/ext2fs.h $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h \ $(top_srcdir)/lib/et/com_err.h $(srcdir)/ext2_io.h \ $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/ext2_ext_attr.h \ $(srcdir)/bitops.h $(srcdir)/e2image.h inode_io.o: $(srcdir)/inode_io.c $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ $(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h imager.o: $(srcdir)/imager.c $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ $(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h io_manager.o: $(srcdir)/io_manager.c $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ $(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h ismounted.o: $(srcdir)/ismounted.c $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ $(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h link.o: $(srcdir)/link.c $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ $(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h llseek.o: $(srcdir)/llseek.c $(top_srcdir)/lib/et/com_err.h \ $(srcdir)/ext2_io.h lookup.o: $(srcdir)/lookup.c $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ $(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h mkdir.o: $(srcdir)/mkdir.c $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ $(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h mkjournal.o: $(srcdir)/mkjournal.c $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(top_srcdir)/lib/e2p/e2p.h \ $(srcdir)/ext2_fs.h $(srcdir)/ext2fs.h $(srcdir)/ext3_extents.h \ $(top_srcdir)/lib/et/com_err.h $(srcdir)/ext2_io.h \ $(top_builddir)/lib/ext2fs/ext2_err.h $(srcdir)/ext2_ext_attr.h \ $(srcdir)/bitops.h $(srcdir)/jfs_user.h $(srcdir)/kernel-jbd.h \ $(srcdir)/jfs_compat.h $(srcdir)/kernel-list.h namei.o: $(srcdir)/namei.c $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ $(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h native.o: $(srcdir)/native.c $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ $(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h newdir.o: $(srcdir)/newdir.c $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ $(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h openfs.o: $(srcdir)/openfs.c $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ $(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h $(srcdir)/e2image.h read_bb.o: $(srcdir)/read_bb.c $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ $(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h read_bb_file.o: $(srcdir)/read_bb_file.c $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ $(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h res_gdt.o: $(srcdir)/res_gdt.c $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ $(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h rw_bitmaps.o: $(srcdir)/rw_bitmaps.c $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ $(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h $(srcdir)/e2image.h swapfs.o: $(srcdir)/swapfs.c $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ $(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h tdb.o: $(srcdir)/tdb.c $(srcdir)/tdb.h test_io.o: $(srcdir)/test_io.c $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ $(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h tst_badblocks.o: $(srcdir)/tst_badblocks.c $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ $(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h tst_bitops.o: $(srcdir)/tst_bitops.c $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ $(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h tst_byteswap.o: $(srcdir)/tst_byteswap.c $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ $(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h tst_getsize.o: $(srcdir)/tst_getsize.c $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ $(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h tst_iscan.o: $(srcdir)/tst_iscan.c $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ $(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h undo_io.o: $(srcdir)/undo_io.c $(srcdir)/tdb.h $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ $(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h unix_io.o: $(srcdir)/unix_io.c $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ $(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h unlink.o: $(srcdir)/unlink.c $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ $(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h valid_blk.o: $(srcdir)/valid_blk.c $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ $(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h version.o: $(srcdir)/version.c $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ $(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h $(top_srcdir)/version.h write_bb_file.o: $(srcdir)/write_bb_file.c $(srcdir)/ext2_fs.h \ $(top_builddir)/lib/ext2fs/ext2_types.h $(srcdir)/ext2fs.h \ $(srcdir)/ext2_fs.h $(srcdir)/ext3_extents.h $(top_srcdir)/lib/et/com_err.h \ $(srcdir)/ext2_io.h $(top_builddir)/lib/ext2fs/ext2_err.h \ $(srcdir)/ext2_ext_attr.h $(srcdir)/bitops.h e2fsprogs-1.41.14/lib/ext2fs/alloc_stats.c0000644031104000116100000000544011504417000016731 0ustar tytsoeng/* * alloc_stats.c --- Update allocation statistics for ext2fs * * Copyright (C) 2001 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #include #include "ext2_fs.h" #include "ext2fs.h" void ext2fs_inode_alloc_stats2(ext2_filsys fs, ext2_ino_t ino, int inuse, int isdir) { int group = ext2fs_group_of_ino(fs, ino); #ifndef OMIT_COM_ERR if (ino > fs->super->s_inodes_count) { com_err("ext2fs_inode_alloc_stats2", 0, "Illegal inode number: %lu", (unsigned long) ino); return; } #endif if (inuse > 0) ext2fs_mark_inode_bitmap(fs->inode_map, ino); else ext2fs_unmark_inode_bitmap(fs->inode_map, ino); fs->group_desc[group].bg_free_inodes_count -= inuse; if (isdir) fs->group_desc[group].bg_used_dirs_count += inuse; /* We don't strictly need to be clearing the uninit flag if inuse < 0 * (i.e. freeing inodes) but it also means something is bad. */ fs->group_desc[group].bg_flags &= ~EXT2_BG_INODE_UNINIT; if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super, EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) { ext2_ino_t first_unused_inode = fs->super->s_inodes_per_group - fs->group_desc[group].bg_itable_unused + group * fs->super->s_inodes_per_group + 1; if (ino >= first_unused_inode) fs->group_desc[group].bg_itable_unused = group * fs->super->s_inodes_per_group + fs->super->s_inodes_per_group - ino; ext2fs_group_desc_csum_set(fs, group); } fs->super->s_free_inodes_count -= inuse; ext2fs_mark_super_dirty(fs); ext2fs_mark_ib_dirty(fs); } void ext2fs_inode_alloc_stats(ext2_filsys fs, ext2_ino_t ino, int inuse) { ext2fs_inode_alloc_stats2(fs, ino, inuse, 0); } void ext2fs_block_alloc_stats(ext2_filsys fs, blk_t blk, int inuse) { int group = ext2fs_group_of_blk(fs, blk); #ifndef OMIT_COM_ERR if (blk >= fs->super->s_blocks_count) { com_err("ext2fs_block_alloc_stats", 0, "Illegal block number: %lu", (unsigned long) blk); return; } #endif if (inuse > 0) ext2fs_mark_block_bitmap(fs->block_map, blk); else ext2fs_unmark_block_bitmap(fs->block_map, blk); fs->group_desc[group].bg_free_blocks_count -= inuse; fs->group_desc[group].bg_flags &= ~EXT2_BG_BLOCK_UNINIT; ext2fs_group_desc_csum_set(fs, group); fs->super->s_free_blocks_count -= inuse; ext2fs_mark_super_dirty(fs); ext2fs_mark_bb_dirty(fs); if (fs->block_alloc_stats) (fs->block_alloc_stats)(fs, (blk64_t) blk, inuse); } void ext2fs_set_block_alloc_stats_callback(ext2_filsys fs, void (*func)(ext2_filsys fs, blk64_t blk, int inuse), void (**old)(ext2_filsys fs, blk64_t blk, int inuse)) { if (!fs || fs->magic != EXT2_ET_MAGIC_EXT2FS_FILSYS) return; if (old) *old = fs->block_alloc_stats; fs->block_alloc_stats = func; } e2fsprogs-1.41.14/lib/ext2fs/tst_iscan.c0000644031104000116100000001116411504417000016410 0ustar tytsoeng/* * tst_inode.c --- this function tests the inode scan function * * Copyright (C) 1996 by Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #include #include #include #include #if HAVE_ERRNO_H #include #endif #include "ext2_fs.h" #include "ext2fs.h" blk_t test_vec[] = { 8, 12, 24, 34, 43, 44, 100, 0 }; ext2_filsys test_fs; ext2fs_block_bitmap bad_block_map, touched_map; ext2fs_inode_bitmap bad_inode_map; badblocks_list test_badblocks; int first_no_comma = 1; int failed = 0; static void test_read_blk(unsigned long block, int count, errcode_t err) { int i; if (first_no_comma) first_no_comma = 0; else printf(", "); if (count > 1) printf("%lu-%lu", block, block+count-1); else printf("%lu", block); for (i=0; i < count; i++, block++) { if (ext2fs_test_block_bitmap(touched_map, block)) { printf("\nDuplicate block?!? --- %lu\n", block); failed++; first_no_comma = 1; } ext2fs_mark_block_bitmap(touched_map, block); } } /* * Setup the variables for doing the inode scan test. */ static void setup(void) { errcode_t retval; int i; struct ext2_super_block param; initialize_ext2_error_table(); memset(¶m, 0, sizeof(param)); param.s_blocks_count = 12000; test_io_cb_read_blk = test_read_blk; retval = ext2fs_initialize("test fs", 0, ¶m, test_io_manager, &test_fs); if (retval) { com_err("setup", retval, "While initializing filesystem"); exit(1); } retval = ext2fs_allocate_tables(test_fs); if (retval) { com_err("setup", retval, "While allocating tables for test filesystem"); exit(1); } retval = ext2fs_allocate_block_bitmap(test_fs, "bad block map", &bad_block_map); if (retval) { com_err("setup", retval, "While allocating bad_block bitmap"); exit(1); } retval = ext2fs_allocate_block_bitmap(test_fs, "touched map", &touched_map); if (retval) { com_err("setup", retval, "While allocating touched block bitmap"); exit(1); } retval = ext2fs_allocate_inode_bitmap(test_fs, "bad inode map", &bad_inode_map); if (retval) { com_err("setup", retval, "While allocating bad inode bitmap"); exit(1); } retval = ext2fs_badblocks_list_create(&test_badblocks, 5); if (retval) { com_err("setup", retval, "while creating badblocks list"); exit(1); } for (i=0; test_vec[i]; i++) { retval = ext2fs_badblocks_list_add(test_badblocks, test_vec[i]); if (retval) { com_err("setup", retval, "while adding test vector %d", i); exit(1); } ext2fs_mark_block_bitmap(bad_block_map, test_vec[i]); } test_fs->badblocks = test_badblocks; } /* * Iterate using inode_scan */ static void iterate(void) { struct ext2_inode inode; ext2_inode_scan scan; errcode_t retval; ext2_ino_t ino; retval = ext2fs_open_inode_scan(test_fs, 8, &scan); if (retval) { com_err("iterate", retval, "While opening inode scan"); exit(1); } printf("Reading blocks: "); retval = ext2fs_get_next_inode(scan, &ino, &inode); if (retval) { com_err("iterate", retval, "while reading first inode"); exit(1); } while (ino) { retval = ext2fs_get_next_inode(scan, &ino, &inode); if (retval == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE) { ext2fs_mark_inode_bitmap(bad_inode_map, ino); continue; } if (retval) { com_err("iterate", retval, "while getting next inode"); exit(1); } } printf("\n"); ext2fs_close_inode_scan(scan); } /* * Verify the touched map */ static void check_map(void) { int i, j, first=1; unsigned long blk; for (i=0; test_vec[i]; i++) { if (ext2fs_test_block_bitmap(touched_map, test_vec[i])) { printf("Bad block was touched --- %u\n", test_vec[i]); failed++; first_no_comma = 1; } ext2fs_mark_block_bitmap(touched_map, test_vec[i]); } for (i = 0; i < test_fs->group_desc_count; i++) { for (j=0, blk = test_fs->group_desc[i].bg_inode_table; j < test_fs->inode_blocks_per_group; j++, blk++) { if (!ext2fs_test_block_bitmap(touched_map, blk) && !ext2fs_test_block_bitmap(bad_block_map, blk)) { printf("Missing block --- %lu\n", blk); failed++; } } } printf("Bad inodes: "); for (i=1; i <= test_fs->super->s_inodes_count; i++) { if (ext2fs_test_inode_bitmap(bad_inode_map, i)) { if (first) first = 0; else printf(", "); printf("%u", i); } } printf("\n"); } int main(int argc, char **argv) { setup(); iterate(); check_map(); if (!failed) printf("Inode scan tested OK!\n"); return failed; } e2fsprogs-1.41.14/lib/ext2fs/expanddir.c0000644031104000116100000000507211504417000016400 0ustar tytsoeng/* * expand.c --- expand an ext2fs directory * * Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #include "ext2_fs.h" #include "ext2fs.h" struct expand_dir_struct { int done; int newblocks; errcode_t err; }; static int expand_dir_proc(ext2_filsys fs, blk_t *blocknr, e2_blkcnt_t blockcnt, blk_t ref_block EXT2FS_ATTR((unused)), int ref_offset EXT2FS_ATTR((unused)), void *priv_data) { struct expand_dir_struct *es = (struct expand_dir_struct *) priv_data; blk_t new_blk; static blk_t last_blk = 0; char *block; errcode_t retval; if (*blocknr) { last_blk = *blocknr; return 0; } retval = ext2fs_new_block(fs, last_blk, 0, &new_blk); if (retval) { es->err = retval; return BLOCK_ABORT; } if (blockcnt > 0) { retval = ext2fs_new_dir_block(fs, 0, 0, &block); if (retval) { es->err = retval; return BLOCK_ABORT; } es->done = 1; retval = ext2fs_write_dir_block(fs, new_blk, block); } else { retval = ext2fs_get_mem(fs->blocksize, &block); if (retval) { es->err = retval; return BLOCK_ABORT; } memset(block, 0, fs->blocksize); retval = io_channel_write_blk(fs->io, new_blk, 1, block); } if (retval) { es->err = retval; return BLOCK_ABORT; } ext2fs_free_mem(&block); *blocknr = new_blk; ext2fs_block_alloc_stats(fs, new_blk, +1); es->newblocks++; if (es->done) return (BLOCK_CHANGED | BLOCK_ABORT); else return BLOCK_CHANGED; } errcode_t ext2fs_expand_dir(ext2_filsys fs, ext2_ino_t dir) { errcode_t retval; struct expand_dir_struct es; struct ext2_inode inode; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); if (!(fs->flags & EXT2_FLAG_RW)) return EXT2_ET_RO_FILSYS; if (!fs->block_map) return EXT2_ET_NO_BLOCK_BITMAP; retval = ext2fs_check_directory(fs, dir); if (retval) return retval; es.done = 0; es.err = 0; es.newblocks = 0; retval = ext2fs_block_iterate2(fs, dir, BLOCK_FLAG_APPEND, 0, expand_dir_proc, &es); if (es.err) return es.err; if (!es.done) return EXT2_ET_EXPAND_DIR_ERR; /* * Update the size and block count fields in the inode. */ retval = ext2fs_read_inode(fs, dir, &inode); if (retval) return retval; inode.i_size += fs->blocksize; ext2fs_iblk_add_blocks(fs, &inode, es.newblocks); retval = ext2fs_write_inode(fs, dir, &inode); if (retval) return retval; return 0; } e2fsprogs-1.41.14/lib/ext2fs/dblist_dir.c0000644031104000116100000000321611504417000016537 0ustar tytsoeng/* * dblist_dir.c --- iterate by directory entry * * Copyright 1997 by Theodore Ts'o * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #include #if HAVE_UNISTD_H #include #endif #include #include #include "ext2_fs.h" #include "ext2fsP.h" static int db_dir_proc(ext2_filsys fs, struct ext2_db_entry *db_info, void *priv_data); errcode_t ext2fs_dblist_dir_iterate(ext2_dblist dblist, int flags, char *block_buf, int (*func)(ext2_ino_t dir, int entry, struct ext2_dir_entry *dirent, int offset, int blocksize, char *buf, void *priv_data), void *priv_data) { errcode_t retval; struct dir_context ctx; EXT2_CHECK_MAGIC(dblist, EXT2_ET_MAGIC_DBLIST); ctx.dir = 0; ctx.flags = flags; if (block_buf) ctx.buf = block_buf; else { retval = ext2fs_get_mem(dblist->fs->blocksize, &ctx.buf); if (retval) return retval; } ctx.func = func; ctx.priv_data = priv_data; ctx.errcode = 0; retval = ext2fs_dblist_iterate(dblist, db_dir_proc, &ctx); if (!block_buf) ext2fs_free_mem(&ctx.buf); if (retval) return retval; return ctx.errcode; } static int db_dir_proc(ext2_filsys fs, struct ext2_db_entry *db_info, void *priv_data) { struct dir_context *ctx; int ret; ctx = (struct dir_context *) priv_data; ctx->dir = db_info->ino; ctx->errcode = 0; ret = ext2fs_process_dir_block(fs, &db_info->blk, db_info->blockcnt, 0, 0, priv_data); if ((ret & BLOCK_ABORT) && !ctx->errcode) return DBLIST_ABORT; return 0; } e2fsprogs-1.41.14/lib/ext2fs/brel.h0000644031104000366760000000410511405316370014710 0ustar tytso/* * brel.h * * Copyright (C) 1996, 1997 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ struct ext2_block_relocate_entry { blk_t new; __s16 offset; __u16 flags; union { blk_t block_ref; ext2_ino_t inode_ref; } owner; }; #define RELOCATE_TYPE_REF 0x0007 #define RELOCATE_BLOCK_REF 0x0001 #define RELOCATE_INODE_REF 0x0002 typedef struct ext2_block_relocation_table *ext2_brel; struct ext2_block_relocation_table { __u32 magic; char *name; blk_t current; void *priv_data; /* * Add a block relocation entry. */ errcode_t (*put)(ext2_brel brel, blk_t old, struct ext2_block_relocate_entry *ent); /* * Get a block relocation entry. */ errcode_t (*get)(ext2_brel brel, blk_t old, struct ext2_block_relocate_entry *ent); /* * Initialize for iterating over the block relocation entries. */ errcode_t (*start_iter)(ext2_brel brel); /* * The iterator function for the inode relocation entries. * Returns an inode number of 0 when out of entries. */ errcode_t (*next)(ext2_brel brel, blk_t *old, struct ext2_block_relocate_entry *ent); /* * Move the inode relocation table from one block number to * another. */ errcode_t (*move)(ext2_brel brel, blk_t old, blk_t new); /* * Remove a block relocation entry. */ errcode_t (*delete)(ext2_brel brel, blk_t old); /* * Free the block relocation table. */ errcode_t (*free)(ext2_brel brel); }; errcode_t ext2fs_brel_memarray_create(char *name, blk_t max_block, ext2_brel *brel); #define ext2fs_brel_put(brel, old, ent) ((brel)->put((brel), old, ent)) #define ext2fs_brel_get(brel, old, ent) ((brel)->get((brel), old, ent)) #define ext2fs_brel_start_iter(brel) ((brel)->start_iter((brel))) #define ext2fs_brel_next(brel, old, ent) ((brel)->next((brel), old, ent)) #define ext2fs_brel_move(brel, old, new) ((brel)->move((brel), old, new)) #define ext2fs_brel_delete(brel, old) ((brel)->delete((brel), old)) #define ext2fs_brel_free(brel) ((brel)->free((brel))) e2fsprogs-1.41.14/lib/ext2fs/tst_super_size.c0000644031104000116100000000736411447256631017533 0ustar tytsoeng/* * This testing program makes sure superblock size is 1024 bytes long * * Copyright (C) 2007 by Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #include #include #include #include "ext2_fs.h" #define sb_struct ext2_super_block #define sb_struct_name "ext2_super_block" struct sb_struct sb; int verbose = 0; #define offsetof(type, member) __builtin_offsetof (type, member) #define check_field(x) cur_offset = do_field(#x, sizeof(sb.x), \ offsetof(struct sb_struct, x), \ cur_offset) static int do_field(const char *field, size_t size, int offset, int cur_offset) { if (offset != cur_offset) { printf("Warning! Unexpected offset at %s\n", field); exit(1); } printf("%8d %-30s %3u\n", offset, field, (unsigned) size); return offset + size; } void check_superblock_fields() { #if (__GNUC__ >= 4) int cur_offset = 0; printf("%8s %-30s %3s\n", "offset", "field", "size"); check_field(s_inodes_count); check_field(s_blocks_count); check_field(s_r_blocks_count); check_field(s_free_blocks_count); check_field(s_free_inodes_count); check_field(s_first_data_block); check_field(s_log_block_size); check_field(s_log_frag_size); check_field(s_blocks_per_group); check_field(s_frags_per_group); check_field(s_inodes_per_group); check_field(s_mtime); check_field(s_wtime); check_field(s_mnt_count); check_field(s_max_mnt_count); check_field(s_magic); check_field(s_state); check_field(s_errors); check_field(s_minor_rev_level); check_field(s_lastcheck); check_field(s_checkinterval); check_field(s_creator_os); check_field(s_rev_level); check_field(s_def_resuid); check_field(s_def_resgid); check_field(s_first_ino); check_field(s_inode_size); check_field(s_block_group_nr); check_field(s_feature_compat); check_field(s_feature_incompat); check_field(s_feature_ro_compat); check_field(s_uuid); check_field(s_volume_name); check_field(s_last_mounted); check_field(s_algorithm_usage_bitmap); check_field(s_prealloc_blocks); check_field(s_prealloc_dir_blocks); check_field(s_reserved_gdt_blocks); check_field(s_journal_uuid); check_field(s_journal_inum); check_field(s_journal_dev); check_field(s_last_orphan); check_field(s_hash_seed); check_field(s_def_hash_version); check_field(s_jnl_backup_type); check_field(s_desc_size); check_field(s_default_mount_opts); check_field(s_first_meta_bg); check_field(s_mkfs_time); check_field(s_jnl_blocks); check_field(s_blocks_count_hi); check_field(s_r_blocks_count_hi); check_field(s_free_blocks_hi); check_field(s_min_extra_isize); check_field(s_want_extra_isize); check_field(s_flags); check_field(s_raid_stride); check_field(s_mmp_interval); check_field(s_mmp_block); check_field(s_raid_stripe_width); check_field(s_log_groups_per_flex); check_field(s_reserved_char_pad); check_field(s_reserved_pad); check_field(s_kbytes_written); check_field(s_snapshot_inum); check_field(s_snapshot_id); check_field(s_snapshot_r_blocks_count); check_field(s_snapshot_list); check_field(s_error_count); check_field(s_first_error_time); check_field(s_first_error_ino); check_field(s_first_error_block); check_field(s_first_error_func); check_field(s_first_error_line); check_field(s_last_error_time); check_field(s_last_error_ino); check_field(s_last_error_line); check_field(s_last_error_block); check_field(s_last_error_func); check_field(s_mount_opts); check_field(s_reserved); printf("Ending offset is %d\n\n", cur_offset); #endif } int main(int argc, char **argv) { int s = sizeof(struct sb_struct); check_superblock_fields(); printf("Size of struct %s is %d\n", sb_struct_name, s); if (s != 1024) { exit(1); } exit(0); } e2fsprogs-1.41.14/lib/ext2fs/ext_attr.c0000644031104000116100000000600511504417000016251 0ustar tytsoeng/* * ext_attr.c --- extended attribute blocks * * Copyright (C) 2001 Andreas Gruenbacher, * * Copyright (C) 2002 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #include #if HAVE_UNISTD_H #include #endif #include #include #include "ext2_fs.h" #include "ext2_ext_attr.h" #include "ext2fs.h" #define NAME_HASH_SHIFT 5 #define VALUE_HASH_SHIFT 16 /* * ext2_xattr_hash_entry() * * Compute the hash of an extended attribute. */ __u32 ext2fs_ext_attr_hash_entry(struct ext2_ext_attr_entry *entry, void *data) { __u32 hash = 0; char *name = ((char *) entry) + sizeof(struct ext2_ext_attr_entry); int n; for (n = 0; n < entry->e_name_len; n++) { hash = (hash << NAME_HASH_SHIFT) ^ (hash >> (8*sizeof(hash) - NAME_HASH_SHIFT)) ^ *name++; } /* The hash needs to be calculated on the data in little-endian. */ if (entry->e_value_block == 0 && entry->e_value_size != 0) { __u32 *value = (__u32 *)data; for (n = (entry->e_value_size + EXT2_EXT_ATTR_ROUND) >> EXT2_EXT_ATTR_PAD_BITS; n; n--) { hash = (hash << VALUE_HASH_SHIFT) ^ (hash >> (8*sizeof(hash) - VALUE_HASH_SHIFT)) ^ ext2fs_le32_to_cpu(*value++); } } return hash; } #undef NAME_HASH_SHIFT #undef VALUE_HASH_SHIFT errcode_t ext2fs_read_ext_attr(ext2_filsys fs, blk_t block, void *buf) { errcode_t retval; retval = io_channel_read_blk(fs->io, block, 1, buf); if (retval) return retval; #ifdef WORDS_BIGENDIAN ext2fs_swap_ext_attr(buf, buf, fs->blocksize, 1); #endif return 0; } errcode_t ext2fs_write_ext_attr(ext2_filsys fs, blk_t block, void *inbuf) { errcode_t retval; char *write_buf; char *buf = NULL; #ifdef WORDS_BIGENDIAN retval = ext2fs_get_mem(fs->blocksize, &buf); if (retval) return retval; write_buf = buf; ext2fs_swap_ext_attr(buf, inbuf, fs->blocksize, 1); #else write_buf = (char *) inbuf; #endif retval = io_channel_write_blk(fs->io, block, 1, write_buf); if (buf) ext2fs_free_mem(&buf); if (!retval) ext2fs_mark_changed(fs); return retval; } /* * This function adjusts the reference count of the EA block. */ errcode_t ext2fs_adjust_ea_refcount(ext2_filsys fs, blk_t blk, char *block_buf, int adjust, __u32 *newcount) { errcode_t retval; struct ext2_ext_attr_header *header; char *buf = 0; if ((blk >= fs->super->s_blocks_count) || (blk < fs->super->s_first_data_block)) return EXT2_ET_BAD_EA_BLOCK_NUM; if (!block_buf) { retval = ext2fs_get_mem(fs->blocksize, &buf); if (retval) return retval; block_buf = buf; } retval = ext2fs_read_ext_attr(fs, blk, block_buf); if (retval) goto errout; header = (struct ext2_ext_attr_header *) block_buf; header->h_refcount += adjust; if (newcount) *newcount = header->h_refcount; retval = ext2fs_write_ext_attr(fs, blk, block_buf); if (retval) goto errout; errout: if (buf) ext2fs_free_mem(&buf); return retval; } e2fsprogs-1.41.14/lib/ext2fs/fiemap.h0000644031104000116100000000473211504417000015672 0ustar tytsoeng/* * FS_IOC_FIEMAP ioctl infrastructure. * * Some portions copyright (C) 2007 Cluster File Systems, Inc * * Authors: Mark Fasheh * Kalpak Shah * Andreas Dilger */ #ifndef _LINUX_FIEMAP_H #define _LINUX_FIEMAP_H struct fiemap_extent { __u64 fe_logical; /* logical offset in bytes for the start of * the extent from the beginning of the file */ __u64 fe_physical; /* physical offset in bytes for the start * of the extent from the beginning of the disk */ __u64 fe_length; /* length in bytes for this extent */ __u64 fe_reserved64[2]; __u32 fe_flags; /* FIEMAP_EXTENT_* flags for this extent */ __u32 fe_reserved[3]; }; struct fiemap { __u64 fm_start; /* logical offset (inclusive) at * which to start mapping (in) */ __u64 fm_length; /* logical length of mapping which * userspace wants (in) */ __u32 fm_flags; /* FIEMAP_FLAG_* flags for request (in/out) */ __u32 fm_mapped_extents;/* number of extents that were mapped (out) */ __u32 fm_extent_count; /* size of fm_extents array (in) */ __u32 fm_reserved; struct fiemap_extent fm_extents[0]; /* array of mapped extents (out) */ }; #define FIEMAP_MAX_OFFSET (~0ULL) #define FIEMAP_FLAG_SYNC 0x00000001 /* sync file data before map */ #define FIEMAP_FLAG_XATTR 0x00000002 /* map extended attribute tree */ #define FIEMAP_FLAGS_COMPAT (FIEMAP_FLAG_SYNC | FIEMAP_FLAG_XATTR) #define FIEMAP_EXTENT_LAST 0x00000001 /* Last extent in file. */ #define FIEMAP_EXTENT_UNKNOWN 0x00000002 /* Data location unknown. */ #define FIEMAP_EXTENT_DELALLOC 0x00000004 /* Location still pending. * Sets EXTENT_UNKNOWN. */ #define FIEMAP_EXTENT_ENCODED 0x00000008 /* Data can not be read * while fs is unmounted */ #define FIEMAP_EXTENT_DATA_ENCRYPTED 0x00000080 /* Data is encrypted by fs. * Sets EXTENT_NO_BYPASS. */ #define FIEMAP_EXTENT_NOT_ALIGNED 0x00000100 /* Extent offsets may not be * block aligned. */ #define FIEMAP_EXTENT_DATA_INLINE 0x00000200 /* Data mixed with metadata. * Sets EXTENT_NOT_ALIGNED.*/ #define FIEMAP_EXTENT_DATA_TAIL 0x00000400 /* Multiple files in block. * Sets EXTENT_NOT_ALIGNED.*/ #define FIEMAP_EXTENT_UNWRITTEN 0x00000800 /* Space allocated, but * no data (i.e. zero). */ #define FIEMAP_EXTENT_MERGED 0x00001000 /* File does not natively * support extents. Result * merged for efficiency. */ #endif /* _LINUX_FIEMAP_H */ e2fsprogs-1.41.14/lib/ext2fs/alloc.c0000644031104000116100000001536511504417000015522 0ustar tytsoeng/* * alloc.c --- allocate new inodes, blocks for ext2fs * * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #include #if HAVE_UNISTD_H #include #endif #include #include #if HAVE_SYS_STAT_H #include #endif #if HAVE_SYS_TYPES_H #include #endif #include "ext2_fs.h" #include "ext2fs.h" /* * Check for uninit block bitmaps and deal with them appropriately */ static void check_block_uninit(ext2_filsys fs, ext2fs_block_bitmap map, dgrp_t group) { blk_t i; blk_t blk, super_blk, old_desc_blk, new_desc_blk; int old_desc_blocks; if (!(EXT2_HAS_RO_COMPAT_FEATURE(fs->super, EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) || !(fs->group_desc[group].bg_flags & EXT2_BG_BLOCK_UNINIT)) return; blk = (group * fs->super->s_blocks_per_group) + fs->super->s_first_data_block; ext2fs_super_and_bgd_loc(fs, group, &super_blk, &old_desc_blk, &new_desc_blk, 0); if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) old_desc_blocks = fs->super->s_first_meta_bg; else old_desc_blocks = fs->desc_blocks + fs->super->s_reserved_gdt_blocks; for (i=0; i < fs->super->s_blocks_per_group; i++, blk++) { if ((blk == super_blk) || (old_desc_blk && old_desc_blocks && (blk >= old_desc_blk) && (blk < old_desc_blk + old_desc_blocks)) || (new_desc_blk && (blk == new_desc_blk)) || (blk == fs->group_desc[group].bg_block_bitmap) || (blk == fs->group_desc[group].bg_inode_bitmap) || (blk >= fs->group_desc[group].bg_inode_table && (blk < fs->group_desc[group].bg_inode_table + fs->inode_blocks_per_group))) ext2fs_fast_mark_block_bitmap(map, blk); else ext2fs_fast_unmark_block_bitmap(map, blk); } fs->group_desc[group].bg_flags &= ~EXT2_BG_BLOCK_UNINIT; ext2fs_group_desc_csum_set(fs, group); } /* * Check for uninit inode bitmaps and deal with them appropriately */ static void check_inode_uninit(ext2_filsys fs, ext2fs_inode_bitmap map, dgrp_t group) { ext2_ino_t i, ino; if (!(EXT2_HAS_RO_COMPAT_FEATURE(fs->super, EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) || !(fs->group_desc[group].bg_flags & EXT2_BG_INODE_UNINIT)) return; ino = (group * fs->super->s_inodes_per_group) + 1; for (i=0; i < fs->super->s_inodes_per_group; i++, ino++) ext2fs_fast_unmark_inode_bitmap(map, ino); fs->group_desc[group].bg_flags &= ~EXT2_BG_INODE_UNINIT; check_block_uninit(fs, fs->block_map, group); } /* * Right now, just search forward from the parent directory's block * group to find the next free inode. * * Should have a special policy for directories. */ errcode_t ext2fs_new_inode(ext2_filsys fs, ext2_ino_t dir, int mode EXT2FS_ATTR((unused)), ext2fs_inode_bitmap map, ext2_ino_t *ret) { ext2_ino_t dir_group = 0; ext2_ino_t i; ext2_ino_t start_inode; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); if (!map) map = fs->inode_map; if (!map) return EXT2_ET_NO_INODE_BITMAP; if (dir > 0) dir_group = (dir - 1) / EXT2_INODES_PER_GROUP(fs->super); start_inode = (dir_group * EXT2_INODES_PER_GROUP(fs->super)) + 1; if (start_inode < EXT2_FIRST_INODE(fs->super)) start_inode = EXT2_FIRST_INODE(fs->super); if (start_inode > fs->super->s_inodes_count) return EXT2_ET_INODE_ALLOC_FAIL; i = start_inode; do { if (((i - 1) % EXT2_INODES_PER_GROUP(fs->super)) == 0) check_inode_uninit(fs, map, (i - 1) / EXT2_INODES_PER_GROUP(fs->super)); if (!ext2fs_fast_test_inode_bitmap(map, i)) break; i++; if (i > fs->super->s_inodes_count) i = EXT2_FIRST_INODE(fs->super); } while (i != start_inode); if (ext2fs_test_inode_bitmap(map, i)) return EXT2_ET_INODE_ALLOC_FAIL; *ret = i; return 0; } /* * Stupid algorithm --- we now just search forward starting from the * goal. Should put in a smarter one someday.... */ errcode_t ext2fs_new_block(ext2_filsys fs, blk_t goal, ext2fs_block_bitmap map, blk_t *ret) { blk_t i; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); if (!map) map = fs->block_map; if (!map) return EXT2_ET_NO_BLOCK_BITMAP; if (!goal || (goal >= fs->super->s_blocks_count)) goal = fs->super->s_first_data_block; i = goal; check_block_uninit(fs, map, (i - fs->super->s_first_data_block) / EXT2_BLOCKS_PER_GROUP(fs->super)); do { if (((i - fs->super->s_first_data_block) % EXT2_BLOCKS_PER_GROUP(fs->super)) == 0) check_block_uninit(fs, map, (i - fs->super->s_first_data_block) / EXT2_BLOCKS_PER_GROUP(fs->super)); if (!ext2fs_fast_test_block_bitmap(map, i)) { *ret = i; return 0; } i++; if (i >= fs->super->s_blocks_count) i = fs->super->s_first_data_block; } while (i != goal); return EXT2_ET_BLOCK_ALLOC_FAIL; } /* * This function zeros out the allocated block, and updates all of the * appropriate filesystem records. */ errcode_t ext2fs_alloc_block(ext2_filsys fs, blk_t goal, char *block_buf, blk_t *ret) { errcode_t retval; blk_t block; char *buf = 0; if (!block_buf) { retval = ext2fs_get_mem(fs->blocksize, &buf); if (retval) return retval; block_buf = buf; } memset(block_buf, 0, fs->blocksize); if (fs->get_alloc_block) { blk64_t new; retval = (fs->get_alloc_block)(fs, (blk64_t) goal, &new); if (retval) goto fail; block = (blk_t) new; } else { if (!fs->block_map) { retval = ext2fs_read_block_bitmap(fs); if (retval) goto fail; } retval = ext2fs_new_block(fs, goal, 0, &block); if (retval) goto fail; } retval = io_channel_write_blk(fs->io, block, 1, block_buf); if (retval) goto fail; ext2fs_block_alloc_stats(fs, block, +1); *ret = block; fail: if (buf) ext2fs_free_mem(&buf); return retval; } errcode_t ext2fs_get_free_blocks(ext2_filsys fs, blk_t start, blk_t finish, int num, ext2fs_block_bitmap map, blk_t *ret) { blk_t b = start; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); if (!map) map = fs->block_map; if (!map) return EXT2_ET_NO_BLOCK_BITMAP; if (!b) b = fs->super->s_first_data_block; if (!finish) finish = start; if (!num) num = 1; do { if (b+num-1 > fs->super->s_blocks_count) b = fs->super->s_first_data_block; if (ext2fs_fast_test_block_bitmap_range(map, b, num)) { *ret = b; return 0; } b++; } while (b != finish); return EXT2_ET_BLOCK_ALLOC_FAIL; } void ext2fs_set_alloc_block_callback(ext2_filsys fs, errcode_t (*func)(ext2_filsys fs, blk64_t goal, blk64_t *ret), errcode_t (**old)(ext2_filsys fs, blk64_t goal, blk64_t *ret)) { if (!fs || fs->magic != EXT2_ET_MAGIC_EXT2FS_FILSYS) return; if (old) *old = fs->get_alloc_block; fs->get_alloc_block = func; } e2fsprogs-1.41.14/lib/ext2fs/mkdir.c0000644031104000116100000000537011504417000015531 0ustar tytsoeng/* * mkdir.c --- make a directory in the filesystem * * Copyright (C) 1994, 1995 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #include #include #if HAVE_SYS_STAT_H #include #endif #if HAVE_SYS_TYPES_H #include #endif #include "ext2_fs.h" #include "ext2fs.h" #ifndef EXT2_FT_DIR #define EXT2_FT_DIR 2 #endif errcode_t ext2fs_mkdir(ext2_filsys fs, ext2_ino_t parent, ext2_ino_t inum, const char *name) { errcode_t retval; struct ext2_inode parent_inode, inode; ext2_ino_t ino = inum; ext2_ino_t scratch_ino; blk_t blk; char *block = 0; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); /* * Allocate an inode, if necessary */ if (!ino) { retval = ext2fs_new_inode(fs, parent, LINUX_S_IFDIR | 0755, 0, &ino); if (retval) goto cleanup; } /* * Allocate a data block for the directory */ retval = ext2fs_new_block(fs, 0, 0, &blk); if (retval) goto cleanup; /* * Create a scratch template for the directory */ retval = ext2fs_new_dir_block(fs, ino, parent, &block); if (retval) goto cleanup; /* * Get the parent's inode, if necessary */ if (parent != ino) { retval = ext2fs_read_inode(fs, parent, &parent_inode); if (retval) goto cleanup; } else memset(&parent_inode, 0, sizeof(parent_inode)); /* * Create the inode structure.... */ memset(&inode, 0, sizeof(struct ext2_inode)); inode.i_mode = LINUX_S_IFDIR | (0777 & ~fs->umask); inode.i_uid = inode.i_gid = 0; ext2fs_iblk_set(fs, &inode, 1); inode.i_block[0] = blk; inode.i_links_count = 2; inode.i_size = fs->blocksize; /* * Write out the inode and inode data block */ retval = ext2fs_write_dir_block(fs, blk, block); if (retval) goto cleanup; retval = ext2fs_write_new_inode(fs, ino, &inode); if (retval) goto cleanup; /* * Link the directory into the filesystem hierarchy */ if (name) { retval = ext2fs_lookup(fs, parent, name, strlen(name), 0, &scratch_ino); if (!retval) { retval = EXT2_ET_DIR_EXISTS; name = 0; goto cleanup; } if (retval != EXT2_ET_FILE_NOT_FOUND) goto cleanup; retval = ext2fs_link(fs, parent, name, ino, EXT2_FT_DIR); if (retval) goto cleanup; } /* * Update parent inode's counts */ if (parent != ino) { parent_inode.i_links_count++; retval = ext2fs_write_inode(fs, parent, &parent_inode); if (retval) goto cleanup; } /* * Update accounting.... */ ext2fs_block_alloc_stats(fs, blk, +1); ext2fs_inode_alloc_stats2(fs, ino, +1, 1); cleanup: if (block) ext2fs_free_mem(&block); return retval; } e2fsprogs-1.41.14/lib/ext2fs/gen_bitmap.c0000644031104000116100000002660411504417000016533 0ustar tytsoeng/* * gen_bitmap.c --- Generic (32-bit) bitmap routines * * Copyright (C) 2001 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #include #include #if HAVE_SYS_STAT_H #include #endif #if HAVE_SYS_TYPES_H #include #endif #include "ext2_fs.h" #include "ext2fs.h" struct ext2fs_struct_generic_bitmap { errcode_t magic; ext2_filsys fs; __u32 start, end; __u32 real_end; char * description; char * bitmap; errcode_t base_error_code; __u32 reserved[7]; }; /* * Used by previously inlined function, so we have to export this and * not change the function signature */ void ext2fs_warn_bitmap2(ext2fs_generic_bitmap bitmap, int code, unsigned long arg) { #ifndef OMIT_COM_ERR if (bitmap->description) com_err(0, bitmap->base_error_code+code, "#%lu for %s", arg, bitmap->description); else com_err(0, bitmap->base_error_code + code, "#%lu", arg); #endif } static errcode_t check_magic(ext2fs_generic_bitmap bitmap) { if (!bitmap || !((bitmap->magic == EXT2_ET_MAGIC_GENERIC_BITMAP) || (bitmap->magic == EXT2_ET_MAGIC_INODE_BITMAP) || (bitmap->magic == EXT2_ET_MAGIC_BLOCK_BITMAP))) return EXT2_ET_MAGIC_GENERIC_BITMAP; return 0; } errcode_t ext2fs_make_generic_bitmap(errcode_t magic, ext2_filsys fs, __u32 start, __u32 end, __u32 real_end, const char *descr, char *init_map, ext2fs_generic_bitmap *ret) { ext2fs_generic_bitmap bitmap; errcode_t retval; size_t size; retval = ext2fs_get_mem(sizeof(struct ext2fs_struct_generic_bitmap), &bitmap); if (retval) return retval; bitmap->magic = magic; bitmap->fs = fs; bitmap->start = start; bitmap->end = end; bitmap->real_end = real_end; switch (magic) { case EXT2_ET_MAGIC_INODE_BITMAP: bitmap->base_error_code = EXT2_ET_BAD_INODE_MARK; break; case EXT2_ET_MAGIC_BLOCK_BITMAP: bitmap->base_error_code = EXT2_ET_BAD_BLOCK_MARK; break; default: bitmap->base_error_code = EXT2_ET_BAD_GENERIC_MARK; } if (descr) { retval = ext2fs_get_mem(strlen(descr)+1, &bitmap->description); if (retval) { ext2fs_free_mem(&bitmap); return retval; } strcpy(bitmap->description, descr); } else bitmap->description = 0; size = (size_t) (((bitmap->real_end - bitmap->start) / 8) + 1); /* Round up to allow for the BT x86 instruction */ size = (size + 7) & ~3; retval = ext2fs_get_mem(size, &bitmap->bitmap); if (retval) { ext2fs_free_mem(&bitmap->description); ext2fs_free_mem(&bitmap); return retval; } if (init_map) memcpy(bitmap->bitmap, init_map, size); else memset(bitmap->bitmap, 0, size); *ret = bitmap; return 0; } errcode_t ext2fs_allocate_generic_bitmap(__u32 start, __u32 end, __u32 real_end, const char *descr, ext2fs_generic_bitmap *ret) { return ext2fs_make_generic_bitmap(EXT2_ET_MAGIC_GENERIC_BITMAP, 0, start, end, real_end, descr, 0, ret); } errcode_t ext2fs_copy_generic_bitmap(ext2fs_generic_bitmap src, ext2fs_generic_bitmap *dest) { return (ext2fs_make_generic_bitmap(src->magic, src->fs, src->start, src->end, src->real_end, src->description, src->bitmap, dest)); } void ext2fs_free_generic_bitmap(ext2fs_inode_bitmap bitmap) { if (check_magic(bitmap)) return; bitmap->magic = 0; if (bitmap->description) { ext2fs_free_mem(&bitmap->description); bitmap->description = 0; } if (bitmap->bitmap) { ext2fs_free_mem(&bitmap->bitmap); bitmap->bitmap = 0; } ext2fs_free_mem(&bitmap); } int ext2fs_test_generic_bitmap(ext2fs_generic_bitmap bitmap, blk_t bitno) { if ((bitno < bitmap->start) || (bitno > bitmap->end)) { ext2fs_warn_bitmap2(bitmap, EXT2FS_TEST_ERROR, bitno); return 0; } return ext2fs_test_bit(bitno - bitmap->start, bitmap->bitmap); } int ext2fs_mark_generic_bitmap(ext2fs_generic_bitmap bitmap, __u32 bitno) { if ((bitno < bitmap->start) || (bitno > bitmap->end)) { ext2fs_warn_bitmap2(bitmap, EXT2FS_MARK_ERROR, bitno); return 0; } return ext2fs_set_bit(bitno - bitmap->start, bitmap->bitmap); } int ext2fs_unmark_generic_bitmap(ext2fs_generic_bitmap bitmap, blk_t bitno) { if ((bitno < bitmap->start) || (bitno > bitmap->end)) { ext2fs_warn_bitmap2(bitmap, EXT2FS_UNMARK_ERROR, bitno); return 0; } return ext2fs_clear_bit(bitno - bitmap->start, bitmap->bitmap); } __u32 ext2fs_get_generic_bitmap_start(ext2fs_generic_bitmap bitmap) { return bitmap->start; } __u32 ext2fs_get_generic_bitmap_end(ext2fs_generic_bitmap bitmap) { return bitmap->end; } void ext2fs_clear_generic_bitmap(ext2fs_generic_bitmap bitmap) { if (check_magic(bitmap)) return; memset(bitmap->bitmap, 0, (size_t) (((bitmap->real_end - bitmap->start) / 8) + 1)); } errcode_t ext2fs_fudge_generic_bitmap_end(ext2fs_inode_bitmap bitmap, errcode_t magic, errcode_t neq, ext2_ino_t end, ext2_ino_t *oend) { EXT2_CHECK_MAGIC(bitmap, magic); if (end > bitmap->real_end) return neq; if (oend) *oend = bitmap->end; bitmap->end = end; return 0; } errcode_t ext2fs_resize_generic_bitmap(errcode_t magic, __u32 new_end, __u32 new_real_end, ext2fs_generic_bitmap bmap) { errcode_t retval; size_t size, new_size; __u32 bitno; if (!bmap || (bmap->magic != magic)) return magic; /* * If we're expanding the bitmap, make sure all of the new * parts of the bitmap are zero. */ if (new_end > bmap->end) { bitno = bmap->real_end; if (bitno > new_end) bitno = new_end; for (; bitno > bmap->end; bitno--) ext2fs_clear_bit(bitno - bmap->start, bmap->bitmap); } if (new_real_end == bmap->real_end) { bmap->end = new_end; return 0; } size = ((bmap->real_end - bmap->start) / 8) + 1; new_size = ((new_real_end - bmap->start) / 8) + 1; if (size != new_size) { retval = ext2fs_resize_mem(size, new_size, &bmap->bitmap); if (retval) return retval; } if (new_size > size) memset(bmap->bitmap + size, 0, new_size - size); bmap->end = new_end; bmap->real_end = new_real_end; return 0; } errcode_t ext2fs_compare_generic_bitmap(errcode_t magic, errcode_t neq, ext2fs_generic_bitmap bm1, ext2fs_generic_bitmap bm2) { blk_t i; if (!bm1 || bm1->magic != magic) return magic; if (!bm2 || bm2->magic != magic) return magic; if ((bm1->start != bm2->start) || (bm1->end != bm2->end) || (memcmp(bm1->bitmap, bm2->bitmap, (size_t) (bm1->end - bm1->start)/8))) return neq; for (i = bm1->end - ((bm1->end - bm1->start) % 8); i <= bm1->end; i++) if (ext2fs_fast_test_block_bitmap(bm1, i) != ext2fs_fast_test_block_bitmap(bm2, i)) return neq; return 0; } void ext2fs_set_generic_bitmap_padding(ext2fs_generic_bitmap map) { __u32 i, j; /* Protect loop from wrap-around if map->real_end is maxed */ for (i=map->end+1, j = i - map->start; i <= map->real_end && i > map->end; i++, j++) ext2fs_set_bit(j, map->bitmap); } errcode_t ext2fs_get_generic_bitmap_range(ext2fs_generic_bitmap bmap, errcode_t magic, __u32 start, __u32 num, void *out) { if (!bmap || (bmap->magic != magic)) return magic; if ((start < bmap->start) || (start+num-1 > bmap->real_end)) return EXT2_ET_INVALID_ARGUMENT; memcpy(out, bmap->bitmap + (start >> 3), (num+7) >> 3); return 0; } errcode_t ext2fs_set_generic_bitmap_range(ext2fs_generic_bitmap bmap, errcode_t magic, __u32 start, __u32 num, void *in) { if (!bmap || (bmap->magic != magic)) return magic; if ((start < bmap->start) || (start+num-1 > bmap->real_end)) return EXT2_ET_INVALID_ARGUMENT; memcpy(bmap->bitmap + (start >> 3), in, (num+7) >> 3); return 0; } /* * Compare @mem to zero buffer by 256 bytes. * Return 1 if @mem is zeroed memory, otherwise return 0. */ static int mem_is_zero(const char *mem, size_t len) { static const char zero_buf[256]; while (len >= sizeof(zero_buf)) { if (memcmp(mem, zero_buf, sizeof(zero_buf))) return 0; len -= sizeof(zero_buf); mem += sizeof(zero_buf); } /* Deal with leftover bytes. */ if (len) return !memcmp(mem, zero_buf, len); return 1; } /* * Return true if all of the bits in a specified range are clear */ static int ext2fs_test_clear_generic_bitmap_range(ext2fs_generic_bitmap bitmap, unsigned int start, unsigned int len) { size_t start_byte, len_byte = len >> 3; unsigned int start_bit, len_bit = len % 8; int first_bit = 0; int last_bit = 0; int mark_count = 0; int mark_bit = 0; int i; const char *ADDR = bitmap->bitmap; start -= bitmap->start; start_byte = start >> 3; start_bit = start % 8; if (start_bit != 0) { /* * The compared start block number or start inode number * is not the first bit in a byte. */ mark_count = 8 - start_bit; if (len < 8 - start_bit) { mark_count = (int)len; mark_bit = len + start_bit - 1; } else mark_bit = 7; for (i = mark_count; i > 0; i--, mark_bit--) first_bit |= 1 << mark_bit; /* * Compare blocks or inodes in the first byte. * If there is any marked bit, this function returns 0. */ if (first_bit & ADDR[start_byte]) return 0; else if (len <= 8 - start_bit) return 1; start_byte++; len_bit = (len - mark_count) % 8; len_byte = (len - mark_count) >> 3; } /* * The compared start block number or start inode number is * the first bit in a byte. */ if (len_bit != 0) { /* * The compared end block number or end inode number is * not the last bit in a byte. */ for (mark_bit = len_bit - 1; mark_bit >= 0; mark_bit--) last_bit |= 1 << mark_bit; /* * Compare blocks or inodes in the last byte. * If there is any marked bit, this function returns 0. */ if (last_bit & ADDR[start_byte + len_byte]) return 0; else if (len_byte == 0) return 1; } /* Check whether all bytes are 0 */ return mem_is_zero(ADDR + start_byte, len_byte); } int ext2fs_test_block_bitmap_range(ext2fs_block_bitmap bitmap, blk_t block, int num) { EXT2_CHECK_MAGIC(bitmap, EXT2_ET_MAGIC_BLOCK_BITMAP); if ((block < bitmap->start) || (block+num-1 > bitmap->real_end)) { ext2fs_warn_bitmap(EXT2_ET_BAD_BLOCK_TEST, block, bitmap->description); return 0; } return ext2fs_test_clear_generic_bitmap_range((ext2fs_generic_bitmap) bitmap, block, num); } int ext2fs_test_inode_bitmap_range(ext2fs_inode_bitmap bitmap, ino_t inode, int num) { EXT2_CHECK_MAGIC(bitmap, EXT2_ET_MAGIC_INODE_BITMAP); if ((inode < bitmap->start) || (inode+num-1 > bitmap->real_end)) { ext2fs_warn_bitmap(EXT2_ET_BAD_INODE_TEST, inode, bitmap->description); return 0; } return ext2fs_test_clear_generic_bitmap_range((ext2fs_generic_bitmap) bitmap, inode, num); } void ext2fs_mark_block_bitmap_range(ext2fs_block_bitmap bitmap, blk_t block, int num) { int i; if ((block < bitmap->start) || (block+num-1 > bitmap->end)) { ext2fs_warn_bitmap(EXT2_ET_BAD_BLOCK_MARK, block, bitmap->description); return; } for (i=0; i < num; i++) ext2fs_fast_set_bit(block + i - bitmap->start, bitmap->bitmap); } void ext2fs_unmark_block_bitmap_range(ext2fs_block_bitmap bitmap, blk_t block, int num) { int i; if ((block < bitmap->start) || (block+num-1 > bitmap->end)) { ext2fs_warn_bitmap(EXT2_ET_BAD_BLOCK_UNMARK, block, bitmap->description); return; } for (i=0; i < num; i++) ext2fs_fast_clear_bit(block + i - bitmap->start, bitmap->bitmap); } e2fsprogs-1.41.14/lib/ext2fs/unix_io.c0000644031104000116100000005025511504417000016077 0ustar tytsoeng/* * unix_io.c --- This is the Unix (well, really POSIX) implementation * of the I/O manager. * * Implements a one-block write-through cache. * * Includes support for Windows NT support under Cygwin. * * Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, * 2002 by Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #define _LARGEFILE_SOURCE #define _LARGEFILE64_SOURCE #define _GNU_SOURCE #include #include #if HAVE_UNISTD_H #include #endif #if HAVE_ERRNO_H #include #endif #include #include #ifdef __linux__ #include #endif #ifdef HAVE_SYS_IOCTL_H #include #endif #ifdef HAVE_SYS_MOUNT_H #include #endif #if HAVE_SYS_STAT_H #include #endif #if HAVE_SYS_TYPES_H #include #endif #if HAVE_SYS_RESOURCE_H #include #endif #if defined(__linux__) && defined(_IO) && !defined(BLKROGET) #define BLKROGET _IO(0x12, 94) /* Get read-only status (0 = read_write). */ #endif #if defined(__linux__) && defined(_IO) && !defined(BLKSSZGET) #define BLKSSZGET _IO(0x12,104)/* get block device sector size */ #endif #undef ALIGN_DEBUG #include "ext2_fs.h" #include "ext2fs.h" /* * For checking structure magic numbers... */ #define EXT2_CHECK_MAGIC(struct, code) \ if ((struct)->magic != (code)) return (code) struct unix_cache { char *buf; unsigned long block; int access_time; unsigned dirty:1; unsigned in_use:1; }; #define CACHE_SIZE 8 #define WRITE_DIRECT_SIZE 4 /* Must be smaller than CACHE_SIZE */ #define READ_DIRECT_SIZE 4 /* Should be smaller than CACHE_SIZE */ struct unix_private_data { int magic; int dev; int flags; int align; int access_time; ext2_loff_t offset; struct unix_cache cache[CACHE_SIZE]; void *bounce; struct struct_io_stats io_stats; }; #define IS_ALIGNED(n, align) ((((unsigned long) n) & \ ((unsigned long) ((align)-1))) == 0) static errcode_t unix_open(const char *name, int flags, io_channel *channel); static errcode_t unix_close(io_channel channel); static errcode_t unix_set_blksize(io_channel channel, int blksize); static errcode_t unix_read_blk(io_channel channel, unsigned long block, int count, void *data); static errcode_t unix_write_blk(io_channel channel, unsigned long block, int count, const void *data); static errcode_t unix_flush(io_channel channel); static errcode_t unix_write_byte(io_channel channel, unsigned long offset, int size, const void *data); static errcode_t unix_set_option(io_channel channel, const char *option, const char *arg); static errcode_t unix_get_stats(io_channel channel, io_stats *stats) ; static void reuse_cache(io_channel channel, struct unix_private_data *data, struct unix_cache *cache, unsigned long long block); static errcode_t unix_read_blk64(io_channel channel, unsigned long long block, int count, void *data); static errcode_t unix_write_blk64(io_channel channel, unsigned long long block, int count, const void *data); static struct struct_io_manager struct_unix_manager = { EXT2_ET_MAGIC_IO_MANAGER, "Unix I/O Manager", unix_open, unix_close, unix_set_blksize, unix_read_blk, unix_write_blk, unix_flush, unix_write_byte, unix_set_option, unix_get_stats, unix_read_blk64, unix_write_blk64, }; io_manager unix_io_manager = &struct_unix_manager; static errcode_t unix_get_stats(io_channel channel, io_stats *stats) { errcode_t retval = 0; struct unix_private_data *data; EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); data = (struct unix_private_data *) channel->private_data; EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); if (stats) *stats = &data->io_stats; return retval; } /* * Here are the raw I/O functions */ static errcode_t raw_read_blk(io_channel channel, struct unix_private_data *data, unsigned long long block, int count, void *buf) { errcode_t retval; ssize_t size; ext2_loff_t location; int actual = 0; size = (count < 0) ? -count : count * channel->block_size; data->io_stats.bytes_read += size; location = ((ext2_loff_t) block * channel->block_size) + data->offset; if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) { retval = errno ? errno : EXT2_ET_LLSEEK_FAILED; goto error_out; } if ((data->align == 0) || ((IS_ALIGNED(buf, data->align)) && IS_ALIGNED(size, data->align))) { actual = read(data->dev, buf, size); if (actual != size) { short_read: if (actual < 0) actual = 0; retval = EXT2_ET_SHORT_READ; goto error_out; } return 0; } #ifdef ALIGN_DEBUG printf("raw_read_blk: O_DIRECT fallback: %p %lu\n", buf, (unsigned long) size); #endif /* * The buffer or size which we're trying to read isn't aligned * to the O_DIRECT rules, so we need to do this the hard way... */ while (size > 0) { actual = read(data->dev, data->bounce, channel->block_size); if (actual != channel->block_size) goto short_read; actual = size; if (size > channel->block_size) actual = channel->block_size; memcpy(buf, data->bounce, actual); size -= actual; buf += actual; } return 0; error_out: memset((char *) buf+actual, 0, size-actual); if (channel->read_error) retval = (channel->read_error)(channel, block, count, buf, size, actual, retval); return retval; } static errcode_t raw_write_blk(io_channel channel, struct unix_private_data *data, unsigned long long block, int count, const void *buf) { ssize_t size; ext2_loff_t location; int actual = 0; errcode_t retval; if (count == 1) size = channel->block_size; else { if (count < 0) size = -count; else size = count * channel->block_size; } data->io_stats.bytes_written += size; location = ((ext2_loff_t) block * channel->block_size) + data->offset; if (ext2fs_llseek(data->dev, location, SEEK_SET) != location) { retval = errno ? errno : EXT2_ET_LLSEEK_FAILED; goto error_out; } if ((data->align == 0) || ((IS_ALIGNED(buf, data->align)) && IS_ALIGNED(size, data->align))) { actual = write(data->dev, buf, size); if (actual != size) { short_write: retval = EXT2_ET_SHORT_WRITE; goto error_out; } return 0; } #ifdef ALIGN_DEBUG printf("raw_write_blk: O_DIRECT fallback: %p %lu\n", buf, (unsigned long) size); #endif /* * The buffer or size which we're trying to write isn't aligned * to the O_DIRECT rules, so we need to do this the hard way... */ while (size > 0) { if (size < channel->block_size) { actual = read(data->dev, data->bounce, channel->block_size); if (actual != channel->block_size) { retval = EXT2_ET_SHORT_READ; goto error_out; } } actual = size; if (size > channel->block_size) actual = channel->block_size; memcpy(data->bounce, buf, actual); actual = write(data->dev, data->bounce, channel->block_size); if (actual != channel->block_size) goto short_write; size -= actual; buf += actual; } return 0; error_out: if (channel->write_error) retval = (channel->write_error)(channel, block, count, buf, size, actual, retval); return retval; } /* * Here we implement the cache functions */ /* Allocate the cache buffers */ static errcode_t alloc_cache(io_channel channel, struct unix_private_data *data) { errcode_t retval; struct unix_cache *cache; int i; data->access_time = 0; for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) { cache->block = 0; cache->access_time = 0; cache->dirty = 0; cache->in_use = 0; if (cache->buf) ext2fs_free_mem(&cache->buf); retval = ext2fs_get_memalign(channel->block_size, data->align, &cache->buf); if (retval) return retval; } if (data->align) { if (data->bounce) ext2fs_free_mem(&data->bounce); retval = ext2fs_get_memalign(channel->block_size, data->align, &data->bounce); } return retval; } /* Free the cache buffers */ static void free_cache(struct unix_private_data *data) { struct unix_cache *cache; int i; data->access_time = 0; for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) { cache->block = 0; cache->access_time = 0; cache->dirty = 0; cache->in_use = 0; if (cache->buf) ext2fs_free_mem(&cache->buf); } if (data->bounce) ext2fs_free_mem(&data->bounce); } #ifndef NO_IO_CACHE /* * Try to find a block in the cache. If the block is not found, and * eldest is a non-zero pointer, then fill in eldest with the cache * entry to that should be reused. */ static struct unix_cache *find_cached_block(struct unix_private_data *data, unsigned long long block, struct unix_cache **eldest) { struct unix_cache *cache, *unused_cache, *oldest_cache; int i; unused_cache = oldest_cache = 0; for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) { if (!cache->in_use) { if (!unused_cache) unused_cache = cache; continue; } if (cache->block == block) { cache->access_time = ++data->access_time; return cache; } if (!oldest_cache || (cache->access_time < oldest_cache->access_time)) oldest_cache = cache; } if (eldest) *eldest = (unused_cache) ? unused_cache : oldest_cache; return 0; } /* * Reuse a particular cache entry for another block. */ static void reuse_cache(io_channel channel, struct unix_private_data *data, struct unix_cache *cache, unsigned long long block) { if (cache->dirty && cache->in_use) raw_write_blk(channel, data, cache->block, 1, cache->buf); cache->in_use = 1; cache->dirty = 0; cache->block = block; cache->access_time = ++data->access_time; } /* * Flush all of the blocks in the cache */ static errcode_t flush_cached_blocks(io_channel channel, struct unix_private_data *data, int invalidate) { struct unix_cache *cache; errcode_t retval, retval2; int i; retval2 = 0; for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) { if (!cache->in_use) continue; if (invalidate) cache->in_use = 0; if (!cache->dirty) continue; retval = raw_write_blk(channel, data, cache->block, 1, cache->buf); if (retval) retval2 = retval; else cache->dirty = 0; } return retval2; } #endif /* NO_IO_CACHE */ static errcode_t unix_open(const char *name, int flags, io_channel *channel) { io_channel io = NULL; struct unix_private_data *data = NULL; errcode_t retval; int open_flags; struct stat st; #ifdef __linux__ struct utsname ut; #endif if (name == 0) return EXT2_ET_BAD_DEVICE_NAME; retval = ext2fs_get_mem(sizeof(struct struct_io_channel), &io); if (retval) return retval; memset(io, 0, sizeof(struct struct_io_channel)); io->magic = EXT2_ET_MAGIC_IO_CHANNEL; retval = ext2fs_get_mem(sizeof(struct unix_private_data), &data); if (retval) goto cleanup; io->manager = unix_io_manager; retval = ext2fs_get_mem(strlen(name)+1, &io->name); if (retval) goto cleanup; strcpy(io->name, name); io->private_data = data; io->block_size = 1024; io->read_error = 0; io->write_error = 0; io->refcount = 1; memset(data, 0, sizeof(struct unix_private_data)); data->magic = EXT2_ET_MAGIC_UNIX_IO_CHANNEL; data->io_stats.num_fields = 2; open_flags = (flags & IO_FLAG_RW) ? O_RDWR : O_RDONLY; if (flags & IO_FLAG_EXCLUSIVE) open_flags |= O_EXCL; if (flags & IO_FLAG_DIRECT_IO) open_flags |= O_DIRECT; data->flags = flags; #ifdef HAVE_OPEN64 data->dev = open64(io->name, open_flags); #else data->dev = open(io->name, open_flags); #endif if (data->dev < 0) { retval = errno; goto cleanup; } #ifdef BLKSSZGET if (flags & IO_FLAG_DIRECT_IO) { if (ioctl(data->dev, BLKSSZGET, &data->align) != 0) data->align = io->block_size; } #endif #if defined(__CYGWIN__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) /* * Some operating systems require that the buffers be aligned, * regardless of O_DIRECT */ data->align = 512; #endif if ((retval = alloc_cache(io, data))) goto cleanup; #ifdef BLKROGET if (flags & IO_FLAG_RW) { int error; int readonly = 0; /* Is the block device actually writable? */ error = ioctl(data->dev, BLKROGET, &readonly); if (!error && readonly) { close(data->dev); retval = EPERM; goto cleanup; } } #endif #ifdef __linux__ #undef RLIM_INFINITY #if (defined(__alpha__) || ((defined(__sparc__) || defined(__mips__)) && (SIZEOF_LONG == 4))) #define RLIM_INFINITY ((unsigned long)(~0UL>>1)) #else #define RLIM_INFINITY (~0UL) #endif /* * Work around a bug in 2.4.10-2.4.18 kernels where writes to * block devices are wrongly getting hit by the filesize * limit. This workaround isn't perfect, since it won't work * if glibc wasn't built against 2.2 header files. (Sigh.) * */ if ((flags & IO_FLAG_RW) && (uname(&ut) == 0) && ((ut.release[0] == '2') && (ut.release[1] == '.') && (ut.release[2] == '4') && (ut.release[3] == '.') && (ut.release[4] == '1') && (ut.release[5] >= '0') && (ut.release[5] < '8')) && (fstat(data->dev, &st) == 0) && (S_ISBLK(st.st_mode))) { struct rlimit rlim; rlim.rlim_cur = rlim.rlim_max = (unsigned long) RLIM_INFINITY; setrlimit(RLIMIT_FSIZE, &rlim); getrlimit(RLIMIT_FSIZE, &rlim); if (((unsigned long) rlim.rlim_cur) < ((unsigned long) rlim.rlim_max)) { rlim.rlim_cur = rlim.rlim_max; setrlimit(RLIMIT_FSIZE, &rlim); } } #endif *channel = io; return 0; cleanup: if (data) { free_cache(data); ext2fs_free_mem(&data); } if (io) ext2fs_free_mem(&io); return retval; } static errcode_t unix_close(io_channel channel) { struct unix_private_data *data; errcode_t retval = 0; EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); data = (struct unix_private_data *) channel->private_data; EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); if (--channel->refcount > 0) return 0; #ifndef NO_IO_CACHE retval = flush_cached_blocks(channel, data, 0); #endif if (close(data->dev) < 0) retval = errno; free_cache(data); ext2fs_free_mem(&channel->private_data); if (channel->name) ext2fs_free_mem(&channel->name); ext2fs_free_mem(&channel); return retval; } static errcode_t unix_set_blksize(io_channel channel, int blksize) { struct unix_private_data *data; errcode_t retval; EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); data = (struct unix_private_data *) channel->private_data; EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); if (channel->block_size != blksize) { #ifndef NO_IO_CACHE if ((retval = flush_cached_blocks(channel, data, 0))) return retval; #endif channel->block_size = blksize; free_cache(data); if ((retval = alloc_cache(channel, data))) return retval; } return 0; } static errcode_t unix_read_blk64(io_channel channel, unsigned long long block, int count, void *buf) { struct unix_private_data *data; struct unix_cache *cache, *reuse[READ_DIRECT_SIZE]; errcode_t retval; char *cp; int i, j; EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); data = (struct unix_private_data *) channel->private_data; EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); #ifdef NO_IO_CACHE return raw_read_blk(channel, data, block, count, buf); #else /* * If we're doing an odd-sized read or a very large read, * flush out the cache and then do a direct read. */ if (count < 0 || count > WRITE_DIRECT_SIZE) { if ((retval = flush_cached_blocks(channel, data, 0))) return retval; return raw_read_blk(channel, data, block, count, buf); } cp = buf; while (count > 0) { /* If it's in the cache, use it! */ if ((cache = find_cached_block(data, block, &reuse[0]))) { #ifdef DEBUG printf("Using cached block %lu\n", block); #endif memcpy(cp, cache->buf, channel->block_size); count--; block++; cp += channel->block_size; continue; } if (count == 1) { /* * Special case where we read directly into the * cache buffer; important in the O_DIRECT case */ cache = reuse[0]; reuse_cache(channel, data, cache, block); if ((retval = raw_read_blk(channel, data, block, 1, cache->buf))) { cache->in_use = 0; return retval; } memcpy(cp, cache->buf, channel->block_size); return 0; } /* * Find the number of uncached blocks so we can do a * single read request */ for (i=1; i < count; i++) if (find_cached_block(data, block+i, &reuse[i])) break; #ifdef DEBUG printf("Reading %d blocks starting at %lu\n", i, block); #endif if ((retval = raw_read_blk(channel, data, block, i, cp))) return retval; /* Save the results in the cache */ for (j=0; j < i; j++) { count--; cache = reuse[j]; reuse_cache(channel, data, cache, block++); memcpy(cache->buf, cp, channel->block_size); cp += channel->block_size; } } return 0; #endif /* NO_IO_CACHE */ } static errcode_t unix_read_blk(io_channel channel, unsigned long block, int count, void *buf) { return unix_read_blk64(channel, block, count, buf); } static errcode_t unix_write_blk64(io_channel channel, unsigned long long block, int count, const void *buf) { struct unix_private_data *data; struct unix_cache *cache, *reuse; errcode_t retval = 0; const char *cp; int writethrough; EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); data = (struct unix_private_data *) channel->private_data; EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); #ifdef NO_IO_CACHE return raw_write_blk(channel, data, block, count, buf); #else /* * If we're doing an odd-sized write or a very large write, * flush out the cache completely and then do a direct write. */ if (count < 0 || count > WRITE_DIRECT_SIZE) { if ((retval = flush_cached_blocks(channel, data, 1))) return retval; return raw_write_blk(channel, data, block, count, buf); } /* * For a moderate-sized multi-block write, first force a write * if we're in write-through cache mode, and then fill the * cache with the blocks. */ writethrough = channel->flags & CHANNEL_FLAGS_WRITETHROUGH; if (writethrough) retval = raw_write_blk(channel, data, block, count, buf); cp = buf; while (count > 0) { cache = find_cached_block(data, block, &reuse); if (!cache) { cache = reuse; reuse_cache(channel, data, cache, block); } memcpy(cache->buf, cp, channel->block_size); cache->dirty = !writethrough; count--; block++; cp += channel->block_size; } return retval; #endif /* NO_IO_CACHE */ } static errcode_t unix_write_blk(io_channel channel, unsigned long block, int count, const void *buf) { return unix_write_blk64(channel, block, count, buf); } static errcode_t unix_write_byte(io_channel channel, unsigned long offset, int size, const void *buf) { struct unix_private_data *data; errcode_t retval = 0; ssize_t actual; EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); data = (struct unix_private_data *) channel->private_data; EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); if (data->align != 0) { #ifdef ALIGN_DEBUG printf("unix_write_byte: O_DIRECT fallback\n"); #endif return EXT2_ET_UNIMPLEMENTED; } #ifndef NO_IO_CACHE /* * Flush out the cache completely */ if ((retval = flush_cached_blocks(channel, data, 1))) return retval; #endif if (lseek(data->dev, offset + data->offset, SEEK_SET) < 0) return errno; actual = write(data->dev, buf, size); if (actual != size) return EXT2_ET_SHORT_WRITE; return 0; } /* * Flush data buffers to disk. */ static errcode_t unix_flush(io_channel channel) { struct unix_private_data *data; errcode_t retval = 0; EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); data = (struct unix_private_data *) channel->private_data; EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); #ifndef NO_IO_CACHE retval = flush_cached_blocks(channel, data, 0); #endif fsync(data->dev); return retval; } static errcode_t unix_set_option(io_channel channel, const char *option, const char *arg) { struct unix_private_data *data; unsigned long long tmp; char *end; EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); data = (struct unix_private_data *) channel->private_data; EXT2_CHECK_MAGIC(data, EXT2_ET_MAGIC_UNIX_IO_CHANNEL); if (!strcmp(option, "offset")) { if (!arg) return EXT2_ET_INVALID_ARGUMENT; tmp = strtoull(arg, &end, 0); if (*end) return EXT2_ET_INVALID_ARGUMENT; data->offset = tmp; if (data->offset < 0) return EXT2_ET_INVALID_ARGUMENT; return 0; } return EXT2_ET_INVALID_ARGUMENT; } e2fsprogs-1.41.14/lib/ext2fs/llseek.c0000644031104000366760000000555011405316370015243 0ustar tytso/* * llseek.c -- stub calling the llseek system call * * Copyright (C) 1994, 1995, 1996, 1997 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #define _LARGEFILE_SOURCE #define _LARGEFILE64_SOURCE #if HAVE_SYS_TYPES_H #include #endif #if HAVE_ERRNO_H #include #endif #if HAVE_UNISTD_H #include #endif #ifdef __MSDOS__ #include #endif #include "et/com_err.h" #include "ext2fs/ext2_io.h" #ifdef __linux__ #if defined(HAVE_LSEEK64) && defined(HAVE_LSEEK64_PROTOTYPE) #define my_llseek lseek64 #else #if defined(HAVE_LLSEEK) #include #ifndef HAVE_LLSEEK_PROTOTYPE extern long long llseek (int fd, long long offset, int origin); #endif #define my_llseek llseek #else /* ! HAVE_LLSEEK */ #if SIZEOF_LONG == SIZEOF_LONG_LONG #define llseek lseek #else /* SIZEOF_LONG != SIZEOF_LONG_LONG */ #include #ifndef __NR__llseek #define __NR__llseek 140 #endif #ifndef __i386__ static int _llseek (unsigned int, unsigned long, unsigned long, ext2_loff_t *, unsigned int); static _syscall5(int,_llseek,unsigned int,fd,unsigned long,offset_high, unsigned long, offset_low,ext2_loff_t *,result, unsigned int, origin) #endif static ext2_loff_t my_llseek (int fd, ext2_loff_t offset, int origin) { ext2_loff_t result; int retval; #ifndef __i386__ retval = _llseek(fd, ((unsigned long long) offset) >> 32, #else retval = syscall(__NR__llseek, fd, (unsigned long long) (offset >> 32), #endif ((unsigned long long) offset) & 0xffffffff, &result, origin); return (retval == -1 ? (ext2_loff_t) retval : result); } #endif /* __alpha__ || __ia64__ */ #endif /* HAVE_LLSEEK */ #endif /* defined(HAVE_LSEEK64) && defined(HAVE_LSEEK64_PROTOTYPE) */ ext2_loff_t ext2fs_llseek (int fd, ext2_loff_t offset, int origin) { ext2_loff_t result; static int do_compat = 0; if ((sizeof(off_t) >= sizeof(ext2_loff_t)) || (offset < ((ext2_loff_t) 1 << ((sizeof(off_t)*8) -1)))) return lseek(fd, (off_t) offset, origin); if (do_compat) { errno = EINVAL; return -1; } result = my_llseek (fd, offset, origin); if (result == -1 && errno == ENOSYS) { /* * Just in case this code runs on top of an old kernel * which does not support the llseek system call */ do_compat++; errno = EINVAL; } return result; } #else /* !linux */ #ifndef EINVAL #define EINVAL EXT2_ET_INVALID_ARGUMENT #endif ext2_loff_t ext2fs_llseek (int fd, ext2_loff_t offset, int origin) { #if defined(HAVE_LSEEK64) && defined(HAVE_LSEEK64_PROTOTYPE) return lseek64 (fd, offset, origin); #else if ((sizeof(off_t) < sizeof(ext2_loff_t)) && (offset >= ((ext2_loff_t) 1 << ((sizeof(off_t)*8) -1)))) { errno = EINVAL; return -1; } return lseek (fd, (off_t) offset, origin); #endif } #endif /* linux */ e2fsprogs-1.41.14/lib/ext2fs/ext2_ext_attr.h0000644031104000366760000000477111374366235016603 0ustar tytso/* File: linux/ext2_ext_attr.h On-disk format of extended attributes for the ext2 filesystem. (C) 2000 Andreas Gruenbacher, */ #ifndef _EXT2_EXT_ATTR_H #define _EXT2_EXT_ATTR_H /* Magic value in attribute blocks */ #define EXT2_EXT_ATTR_MAGIC_v1 0xEA010000 #define EXT2_EXT_ATTR_MAGIC 0xEA020000 /* Maximum number of references to one attribute block */ #define EXT2_EXT_ATTR_REFCOUNT_MAX 1024 struct ext2_ext_attr_header { __u32 h_magic; /* magic number for identification */ __u32 h_refcount; /* reference count */ __u32 h_blocks; /* number of disk blocks used */ __u32 h_hash; /* hash value of all attributes */ __u32 h_reserved[4]; /* zero right now */ }; struct ext2_ext_attr_entry { __u8 e_name_len; /* length of name */ __u8 e_name_index; /* attribute name index */ __u16 e_value_offs; /* offset in disk block of value */ __u32 e_value_block; /* disk block attribute is stored on (n/i) */ __u32 e_value_size; /* size of attribute value */ __u32 e_hash; /* hash value of name and value */ #if 0 char e_name[0]; /* attribute name */ #endif }; #define EXT2_EXT_ATTR_PAD_BITS 2 #define EXT2_EXT_ATTR_PAD ((unsigned) 1<e_name_len)) ) #define EXT2_EXT_ATTR_SIZE(size) \ (((size) + EXT2_EXT_ATTR_ROUND) & ~EXT2_EXT_ATTR_ROUND) #define EXT2_EXT_IS_LAST_ENTRY(entry) (*((__u32 *)(entry)) == 0UL) #define EXT2_EXT_ATTR_NAME(entry) \ (((char *) (entry)) + sizeof(struct ext2_ext_attr_entry)) #define EXT2_XATTR_LEN(name_len) \ (((name_len) + EXT2_EXT_ATTR_ROUND + \ sizeof(struct ext2_xattr_entry)) & ~EXT2_EXT_ATTR_ROUND) #define EXT2_XATTR_SIZE(size) \ (((size) + EXT2_EXT_ATTR_ROUND) & ~EXT2_EXT_ATTR_ROUND) #ifdef __KERNEL__ # ifdef CONFIG_EXT2_FS_EXT_ATTR extern int ext2_get_ext_attr(struct inode *, const char *, char *, size_t, int); extern int ext2_set_ext_attr(struct inode *, const char *, char *, size_t, int); extern void ext2_ext_attr_free_inode(struct inode *inode); extern void ext2_ext_attr_put_super(struct super_block *sb); extern int ext2_ext_attr_init(void); extern void ext2_ext_attr_done(void); # else # define ext2_get_ext_attr NULL # define ext2_set_ext_attr NULL # endif #endif /* __KERNEL__ */ #endif /* _EXT2_EXT_ATTR_H */ e2fsprogs-1.41.14/lib/ext2fs/ext2fs.h0000644031104000116100000012546011504417000015646 0ustar tytsoeng/* * ext2fs.h --- ext2fs * * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #ifndef _EXT2FS_EXT2FS_H #define _EXT2FS_EXT2FS_H #ifdef __GNUC__ #define EXT2FS_ATTR(x) __attribute__(x) #else #define EXT2FS_ATTR(x) #endif #ifdef __cplusplus extern "C" { #endif /* * Non-GNU C compilers won't necessarily understand inline */ #if (!defined(__GNUC__) && !defined(__WATCOMC__)) #define NO_INLINE_FUNCS #endif /* * Where the master copy of the superblock is located, and how big * superblocks are supposed to be. We define SUPERBLOCK_SIZE because * the size of the superblock structure is not necessarily trustworthy * (some versions have the padding set up so that the superblock is * 1032 bytes long). */ #define SUPERBLOCK_OFFSET 1024 #define SUPERBLOCK_SIZE 1024 /* * The last ext2fs revision level that this version of the library is * able to support. */ #define EXT2_LIB_CURRENT_REV EXT2_DYNAMIC_REV #ifdef HAVE_SYS_TYPES_H #include #endif #include #include #include #include #if EXT2_FLAT_INCLUDES #include "e2_types.h" #include "ext2_fs.h" #include "ext3_extents.h" #else #include #include #include #endif /* EXT2_FLAT_INCLUDES */ typedef __u32 ext2_ino_t; typedef __u32 blk_t; typedef __u64 blk64_t; typedef __u32 dgrp_t; typedef __u32 ext2_off_t; typedef __s64 e2_blkcnt_t; typedef __u32 ext2_dirhash_t; #if EXT2_FLAT_INCLUDES #include "com_err.h" #include "ext2_io.h" #include "ext2_err.h" #include "ext2_ext_attr.h" #else #include #include #include #include #endif /* * Portability help for Microsoft Visual C++ */ #ifdef _MSC_VER #define EXT2_QSORT_TYPE int __cdecl #else #define EXT2_QSORT_TYPE int #endif typedef struct struct_ext2_filsys *ext2_filsys; #define EXT2FS_MARK_ERROR 0 #define EXT2FS_UNMARK_ERROR 1 #define EXT2FS_TEST_ERROR 2 typedef struct ext2fs_struct_generic_bitmap *ext2fs_generic_bitmap; typedef struct ext2fs_struct_generic_bitmap *ext2fs_inode_bitmap; typedef struct ext2fs_struct_generic_bitmap *ext2fs_block_bitmap; #define EXT2_FIRST_INODE(s) EXT2_FIRST_INO(s) /* * Badblocks list definitions */ typedef struct ext2_struct_u32_list *ext2_badblocks_list; typedef struct ext2_struct_u32_iterate *ext2_badblocks_iterate; typedef struct ext2_struct_u32_list *ext2_u32_list; typedef struct ext2_struct_u32_iterate *ext2_u32_iterate; /* old */ typedef struct ext2_struct_u32_list *badblocks_list; typedef struct ext2_struct_u32_iterate *badblocks_iterate; #define BADBLOCKS_FLAG_DIRTY 1 /* * ext2_dblist structure and abstractions (see dblist.c) */ struct ext2_db_entry { ext2_ino_t ino; blk_t blk; int blockcnt; }; typedef struct ext2_struct_dblist *ext2_dblist; #define DBLIST_ABORT 1 /* * ext2_fileio definitions */ #define EXT2_FILE_WRITE 0x0001 #define EXT2_FILE_CREATE 0x0002 #define EXT2_FILE_MASK 0x00FF #define EXT2_FILE_BUF_DIRTY 0x4000 #define EXT2_FILE_BUF_VALID 0x2000 typedef struct ext2_file *ext2_file_t; #define EXT2_SEEK_SET 0 #define EXT2_SEEK_CUR 1 #define EXT2_SEEK_END 2 /* * Flags for the ext2_filsys structure and for ext2fs_open() */ #define EXT2_FLAG_RW 0x01 #define EXT2_FLAG_CHANGED 0x02 #define EXT2_FLAG_DIRTY 0x04 #define EXT2_FLAG_VALID 0x08 #define EXT2_FLAG_IB_DIRTY 0x10 #define EXT2_FLAG_BB_DIRTY 0x20 #define EXT2_FLAG_SWAP_BYTES 0x40 #define EXT2_FLAG_SWAP_BYTES_READ 0x80 #define EXT2_FLAG_SWAP_BYTES_WRITE 0x100 #define EXT2_FLAG_MASTER_SB_ONLY 0x200 #define EXT2_FLAG_FORCE 0x400 #define EXT2_FLAG_SUPER_ONLY 0x800 #define EXT2_FLAG_JOURNAL_DEV_OK 0x1000 #define EXT2_FLAG_IMAGE_FILE 0x2000 #define EXT2_FLAG_EXCLUSIVE 0x4000 #define EXT2_FLAG_SOFTSUPP_FEATURES 0x8000 #define EXT2_FLAG_NOFREE_ON_ERROR 0x10000 #define EXT2_FLAG_DIRECT_IO 0x80000 /* * Special flag in the ext2 inode i_flag field that means that this is * a new inode. (So that ext2_write_inode() can clear extra fields.) */ #define EXT2_NEW_INODE_FL 0x80000000 /* * Flags for mkjournal * * EXT2_MKJOURNAL_V1_SUPER Make a (deprecated) V1 journal superblock */ #define EXT2_MKJOURNAL_V1_SUPER 0x0000001 struct struct_ext2_filsys { errcode_t magic; io_channel io; int flags; char * device_name; struct ext2_super_block * super; unsigned int blocksize; int fragsize; dgrp_t group_desc_count; unsigned long desc_blocks; struct ext2_group_desc * group_desc; int inode_blocks_per_group; ext2fs_inode_bitmap inode_map; ext2fs_block_bitmap block_map; errcode_t (*get_blocks)(ext2_filsys fs, ext2_ino_t ino, blk_t *blocks); errcode_t (*check_directory)(ext2_filsys fs, ext2_ino_t ino); errcode_t (*write_bitmaps)(ext2_filsys fs); errcode_t (*read_inode)(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode); errcode_t (*write_inode)(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode); ext2_badblocks_list badblocks; ext2_dblist dblist; __u32 stride; /* for mke2fs */ struct ext2_super_block * orig_super; struct ext2_image_hdr * image_header; __u32 umask; time_t now; /* * Reserved for future expansion */ __u32 reserved[7]; /* * Reserved for the use of the calling application. */ void * priv_data; /* * Inode cache */ struct ext2_inode_cache *icache; io_channel image_io; /* * More callback functions */ errcode_t (*get_alloc_block)(ext2_filsys fs, blk64_t goal, blk64_t *ret); void (*block_alloc_stats)(ext2_filsys fs, blk64_t blk, int inuse); }; #if EXT2_FLAT_INCLUDES #include "e2_bitops.h" #else #include #endif /* * Return flags for the block iterator functions */ #define BLOCK_CHANGED 1 #define BLOCK_ABORT 2 #define BLOCK_ERROR 4 /* * Block interate flags * * BLOCK_FLAG_APPEND, or BLOCK_FLAG_HOLE, indicates that the interator * function should be called on blocks where the block number is zero. * This is used by ext2fs_expand_dir() to be able to add a new block * to an inode. It can also be used for programs that want to be able * to deal with files that contain "holes". * * BLOCK_FLAG_DEPTH_TRAVERSE indicates that the iterator function for * the indirect, doubly indirect, etc. blocks should be called after * all of the blocks containined in the indirect blocks are processed. * This is useful if you are going to be deallocating blocks from an * inode. * * BLOCK_FLAG_DATA_ONLY indicates that the iterator function should be * called for data blocks only. * * BLOCK_FLAG_READ_ONLY is a promise by the caller that it will not * modify returned block number. * * BLOCK_FLAG_NO_LARGE is for internal use only. It informs * ext2fs_block_iterate2 that large files won't be accepted. */ #define BLOCK_FLAG_APPEND 1 #define BLOCK_FLAG_HOLE 1 #define BLOCK_FLAG_DEPTH_TRAVERSE 2 #define BLOCK_FLAG_DATA_ONLY 4 #define BLOCK_FLAG_READ_ONLY 8 #define BLOCK_FLAG_NO_LARGE 0x1000 /* * Magic "block count" return values for the block iterator function. */ #define BLOCK_COUNT_IND (-1) #define BLOCK_COUNT_DIND (-2) #define BLOCK_COUNT_TIND (-3) #define BLOCK_COUNT_TRANSLATOR (-4) #if 0 /* * Flags for ext2fs_move_blocks */ #define EXT2_BMOVE_GET_DBLIST 0x0001 #define EXT2_BMOVE_DEBUG 0x0002 #endif /* * Generic (non-filesystem layout specific) extents structure */ #define EXT2_EXTENT_FLAGS_LEAF 0x0001 #define EXT2_EXTENT_FLAGS_UNINIT 0x0002 #define EXT2_EXTENT_FLAGS_SECOND_VISIT 0x0004 struct ext2fs_extent { blk64_t e_pblk; /* first physical block */ blk64_t e_lblk; /* first logical block extent covers */ __u32 e_len; /* number of blocks covered by extent */ __u32 e_flags; /* extent flags */ }; typedef struct ext2_extent_handle *ext2_extent_handle_t; typedef struct ext2_extent_path *ext2_extent_path_t; /* * Flags used by ext2fs_extent_get() */ #define EXT2_EXTENT_CURRENT 0x0000 #define EXT2_EXTENT_MOVE_MASK 0x000F #define EXT2_EXTENT_ROOT 0x0001 #define EXT2_EXTENT_LAST_LEAF 0x0002 #define EXT2_EXTENT_FIRST_SIB 0x0003 #define EXT2_EXTENT_LAST_SIB 0x0004 #define EXT2_EXTENT_NEXT_SIB 0x0005 #define EXT2_EXTENT_PREV_SIB 0x0006 #define EXT2_EXTENT_NEXT_LEAF 0x0007 #define EXT2_EXTENT_PREV_LEAF 0x0008 #define EXT2_EXTENT_NEXT 0x0009 #define EXT2_EXTENT_PREV 0x000A #define EXT2_EXTENT_UP 0x000B #define EXT2_EXTENT_DOWN 0x000C #define EXT2_EXTENT_DOWN_AND_LAST 0x000D /* * Flags used by ext2fs_extent_insert() */ #define EXT2_EXTENT_INSERT_AFTER 0x0001 /* insert after handle loc'n */ #define EXT2_EXTENT_INSERT_NOSPLIT 0x0002 /* insert may not cause split */ /* * Flags used by ext2fs_extent_delete() */ #define EXT2_EXTENT_DELETE_KEEP_EMPTY 0x001 /* keep node if last extnt gone */ /* * Flags used by ext2fs_extent_set_bmap() */ #define EXT2_EXTENT_SET_BMAP_UNINIT 0x0001 /* * Data structure returned by ext2fs_extent_get_info() */ struct ext2_extent_info { int curr_entry; int curr_level; int num_entries; int max_entries; int max_depth; int bytes_avail; blk64_t max_lblk; blk64_t max_pblk; __u32 max_len; __u32 max_uninit_len; }; /* * Flags for directory block reading and writing functions */ #define EXT2_DIRBLOCK_V2_STRUCT 0x0001 /* * Return flags for the directory iterator functions */ #define DIRENT_CHANGED 1 #define DIRENT_ABORT 2 #define DIRENT_ERROR 3 /* * Directory iterator flags */ #define DIRENT_FLAG_INCLUDE_EMPTY 1 #define DIRENT_FLAG_INCLUDE_REMOVED 2 #define DIRENT_DOT_FILE 1 #define DIRENT_DOT_DOT_FILE 2 #define DIRENT_OTHER_FILE 3 #define DIRENT_DELETED_FILE 4 /* * Inode scan definitions */ typedef struct ext2_struct_inode_scan *ext2_inode_scan; /* * ext2fs_scan flags */ #define EXT2_SF_CHK_BADBLOCKS 0x0001 #define EXT2_SF_BAD_INODE_BLK 0x0002 #define EXT2_SF_BAD_EXTRA_BYTES 0x0004 #define EXT2_SF_SKIP_MISSING_ITABLE 0x0008 #define EXT2_SF_DO_LAZY 0x0010 /* * ext2fs_check_if_mounted flags */ #define EXT2_MF_MOUNTED 1 #define EXT2_MF_ISROOT 2 #define EXT2_MF_READONLY 4 #define EXT2_MF_SWAP 8 #define EXT2_MF_BUSY 16 /* * Ext2/linux mode flags. We define them here so that we don't need * to depend on the OS's sys/stat.h, since we may be compiling on a * non-Linux system. */ #define LINUX_S_IFMT 00170000 #define LINUX_S_IFSOCK 0140000 #define LINUX_S_IFLNK 0120000 #define LINUX_S_IFREG 0100000 #define LINUX_S_IFBLK 0060000 #define LINUX_S_IFDIR 0040000 #define LINUX_S_IFCHR 0020000 #define LINUX_S_IFIFO 0010000 #define LINUX_S_ISUID 0004000 #define LINUX_S_ISGID 0002000 #define LINUX_S_ISVTX 0001000 #define LINUX_S_IRWXU 00700 #define LINUX_S_IRUSR 00400 #define LINUX_S_IWUSR 00200 #define LINUX_S_IXUSR 00100 #define LINUX_S_IRWXG 00070 #define LINUX_S_IRGRP 00040 #define LINUX_S_IWGRP 00020 #define LINUX_S_IXGRP 00010 #define LINUX_S_IRWXO 00007 #define LINUX_S_IROTH 00004 #define LINUX_S_IWOTH 00002 #define LINUX_S_IXOTH 00001 #define LINUX_S_ISLNK(m) (((m) & LINUX_S_IFMT) == LINUX_S_IFLNK) #define LINUX_S_ISREG(m) (((m) & LINUX_S_IFMT) == LINUX_S_IFREG) #define LINUX_S_ISDIR(m) (((m) & LINUX_S_IFMT) == LINUX_S_IFDIR) #define LINUX_S_ISCHR(m) (((m) & LINUX_S_IFMT) == LINUX_S_IFCHR) #define LINUX_S_ISBLK(m) (((m) & LINUX_S_IFMT) == LINUX_S_IFBLK) #define LINUX_S_ISFIFO(m) (((m) & LINUX_S_IFMT) == LINUX_S_IFIFO) #define LINUX_S_ISSOCK(m) (((m) & LINUX_S_IFMT) == LINUX_S_IFSOCK) /* * ext2 size of an inode */ #define EXT2_I_SIZE(i) ((i)->i_size | ((__u64) (i)->i_size_high << 32)) /* * ext2_icount_t abstraction */ #define EXT2_ICOUNT_OPT_INCREMENT 0x01 typedef struct ext2_icount *ext2_icount_t; /* * Flags for ext2fs_bmap */ #define BMAP_ALLOC 0x0001 #define BMAP_SET 0x0002 /* * Returned flags from ext2fs_bmap */ #define BMAP_RET_UNINIT 0x0001 /* * Flags for imager.c functions */ #define IMAGER_FLAG_INODEMAP 1 #define IMAGER_FLAG_SPARSEWRITE 2 /* * For checking structure magic numbers... */ #define EXT2_CHECK_MAGIC(struct, code) \ if ((struct)->magic != (code)) return (code) /* * For ext2 compression support */ #define EXT2FS_COMPRESSED_BLKADDR ((blk_t) -1) #define HOLE_BLKADDR(_b) ((_b) == 0 || (_b) == EXT2FS_COMPRESSED_BLKADDR) /* * Features supported by this version of the library */ #define EXT2_LIB_FEATURE_COMPAT_SUPP (EXT2_FEATURE_COMPAT_DIR_PREALLOC|\ EXT2_FEATURE_COMPAT_IMAGIC_INODES|\ EXT3_FEATURE_COMPAT_HAS_JOURNAL|\ EXT2_FEATURE_COMPAT_RESIZE_INODE|\ EXT2_FEATURE_COMPAT_DIR_INDEX|\ EXT2_FEATURE_COMPAT_EXT_ATTR) /* This #ifdef is temporary until compression is fully supported */ #ifdef ENABLE_COMPRESSION #ifndef I_KNOW_THAT_COMPRESSION_IS_EXPERIMENTAL /* If the below warning bugs you, then have `CPPFLAGS=-DI_KNOW_THAT_COMPRESSION_IS_EXPERIMENTAL' in your environment at configure time. */ #warning "Compression support is experimental" #endif #define EXT2_LIB_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE|\ EXT2_FEATURE_INCOMPAT_COMPRESSION|\ EXT3_FEATURE_INCOMPAT_JOURNAL_DEV|\ EXT2_FEATURE_INCOMPAT_META_BG|\ EXT3_FEATURE_INCOMPAT_RECOVER|\ EXT3_FEATURE_INCOMPAT_EXTENTS|\ EXT4_FEATURE_INCOMPAT_FLEX_BG) #else #define EXT2_LIB_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE|\ EXT3_FEATURE_INCOMPAT_JOURNAL_DEV|\ EXT2_FEATURE_INCOMPAT_META_BG|\ EXT3_FEATURE_INCOMPAT_RECOVER|\ EXT3_FEATURE_INCOMPAT_EXTENTS|\ EXT4_FEATURE_INCOMPAT_FLEX_BG) #endif #define EXT2_LIB_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER|\ EXT4_FEATURE_RO_COMPAT_HUGE_FILE|\ EXT2_FEATURE_RO_COMPAT_LARGE_FILE|\ EXT4_FEATURE_RO_COMPAT_DIR_NLINK|\ EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE|\ EXT4_FEATURE_RO_COMPAT_GDT_CSUM) /* * These features are only allowed if EXT2_FLAG_SOFTSUPP_FEATURES is passed * to ext2fs_openfs() */ #define EXT2_LIB_SOFTSUPP_INCOMPAT (0) #define EXT2_LIB_SOFTSUPP_RO_COMPAT (0) /* * function prototypes */ /* alloc.c */ extern errcode_t ext2fs_new_inode(ext2_filsys fs, ext2_ino_t dir, int mode, ext2fs_inode_bitmap map, ext2_ino_t *ret); extern errcode_t ext2fs_new_block(ext2_filsys fs, blk_t goal, ext2fs_block_bitmap map, blk_t *ret); extern errcode_t ext2fs_get_free_blocks(ext2_filsys fs, blk_t start, blk_t finish, int num, ext2fs_block_bitmap map, blk_t *ret); extern errcode_t ext2fs_alloc_block(ext2_filsys fs, blk_t goal, char *block_buf, blk_t *ret); extern void ext2fs_set_alloc_block_callback(ext2_filsys fs, errcode_t (*func)(ext2_filsys fs, blk64_t goal, blk64_t *ret), errcode_t (**old)(ext2_filsys fs, blk64_t goal, blk64_t *ret)); /* alloc_sb.c */ extern int ext2fs_reserve_super_and_bgd(ext2_filsys fs, dgrp_t group, ext2fs_block_bitmap bmap); extern void ext2fs_set_block_alloc_stats_callback(ext2_filsys fs, void (*func)(ext2_filsys fs, blk64_t blk, int inuse), void (**old)(ext2_filsys fs, blk64_t blk, int inuse)); /* alloc_stats.c */ void ext2fs_inode_alloc_stats(ext2_filsys fs, ext2_ino_t ino, int inuse); void ext2fs_inode_alloc_stats2(ext2_filsys fs, ext2_ino_t ino, int inuse, int isdir); void ext2fs_block_alloc_stats(ext2_filsys fs, blk_t blk, int inuse); /* alloc_tables.c */ extern errcode_t ext2fs_allocate_tables(ext2_filsys fs); extern errcode_t ext2fs_allocate_group_table(ext2_filsys fs, dgrp_t group, ext2fs_block_bitmap bmap); /* badblocks.c */ extern errcode_t ext2fs_u32_list_create(ext2_u32_list *ret, int size); extern errcode_t ext2fs_u32_list_add(ext2_u32_list bb, __u32 blk); extern int ext2fs_u32_list_find(ext2_u32_list bb, __u32 blk); extern int ext2fs_u32_list_test(ext2_u32_list bb, blk_t blk); extern errcode_t ext2fs_u32_list_iterate_begin(ext2_u32_list bb, ext2_u32_iterate *ret); extern int ext2fs_u32_list_iterate(ext2_u32_iterate iter, blk_t *blk); extern void ext2fs_u32_list_iterate_end(ext2_u32_iterate iter); extern errcode_t ext2fs_u32_copy(ext2_u32_list src, ext2_u32_list *dest); extern int ext2fs_u32_list_equal(ext2_u32_list bb1, ext2_u32_list bb2); extern errcode_t ext2fs_badblocks_list_create(ext2_badblocks_list *ret, int size); extern errcode_t ext2fs_badblocks_list_add(ext2_badblocks_list bb, blk_t blk); extern int ext2fs_badblocks_list_test(ext2_badblocks_list bb, blk_t blk); extern int ext2fs_u32_list_del(ext2_u32_list bb, __u32 blk); extern void ext2fs_badblocks_list_del(ext2_u32_list bb, __u32 blk); extern errcode_t ext2fs_badblocks_list_iterate_begin(ext2_badblocks_list bb, ext2_badblocks_iterate *ret); extern int ext2fs_badblocks_list_iterate(ext2_badblocks_iterate iter, blk_t *blk); extern void ext2fs_badblocks_list_iterate_end(ext2_badblocks_iterate iter); extern errcode_t ext2fs_badblocks_copy(ext2_badblocks_list src, ext2_badblocks_list *dest); extern int ext2fs_badblocks_equal(ext2_badblocks_list bb1, ext2_badblocks_list bb2); extern int ext2fs_u32_list_count(ext2_u32_list bb); /* bb_compat */ extern errcode_t badblocks_list_create(badblocks_list *ret, int size); extern errcode_t badblocks_list_add(badblocks_list bb, blk_t blk); extern int badblocks_list_test(badblocks_list bb, blk_t blk); extern errcode_t badblocks_list_iterate_begin(badblocks_list bb, badblocks_iterate *ret); extern int badblocks_list_iterate(badblocks_iterate iter, blk_t *blk); extern void badblocks_list_iterate_end(badblocks_iterate iter); extern void badblocks_list_free(badblocks_list bb); /* bb_inode.c */ extern errcode_t ext2fs_update_bb_inode(ext2_filsys fs, ext2_badblocks_list bb_list); /* bitmaps.c */ extern void ext2fs_free_block_bitmap(ext2fs_block_bitmap bitmap); extern void ext2fs_free_inode_bitmap(ext2fs_inode_bitmap bitmap); extern errcode_t ext2fs_copy_bitmap(ext2fs_generic_bitmap src, ext2fs_generic_bitmap *dest); extern errcode_t ext2fs_write_inode_bitmap(ext2_filsys fs); extern errcode_t ext2fs_write_block_bitmap (ext2_filsys fs); extern errcode_t ext2fs_read_inode_bitmap (ext2_filsys fs); extern errcode_t ext2fs_read_block_bitmap(ext2_filsys fs); extern errcode_t ext2fs_allocate_block_bitmap(ext2_filsys fs, const char *descr, ext2fs_block_bitmap *ret); extern errcode_t ext2fs_allocate_inode_bitmap(ext2_filsys fs, const char *descr, ext2fs_inode_bitmap *ret); extern errcode_t ext2fs_fudge_inode_bitmap_end(ext2fs_inode_bitmap bitmap, ext2_ino_t end, ext2_ino_t *oend); extern errcode_t ext2fs_fudge_block_bitmap_end(ext2fs_block_bitmap bitmap, blk_t end, blk_t *oend); extern void ext2fs_clear_inode_bitmap(ext2fs_inode_bitmap bitmap); extern void ext2fs_clear_block_bitmap(ext2fs_block_bitmap bitmap); extern errcode_t ext2fs_read_bitmaps(ext2_filsys fs); extern errcode_t ext2fs_write_bitmaps(ext2_filsys fs); extern errcode_t ext2fs_resize_inode_bitmap(__u32 new_end, __u32 new_real_end, ext2fs_inode_bitmap bmap); extern errcode_t ext2fs_resize_block_bitmap(__u32 new_end, __u32 new_real_end, ext2fs_block_bitmap bmap); extern errcode_t ext2fs_compare_block_bitmap(ext2fs_block_bitmap bm1, ext2fs_block_bitmap bm2); extern errcode_t ext2fs_compare_inode_bitmap(ext2fs_inode_bitmap bm1, ext2fs_inode_bitmap bm2); extern errcode_t ext2fs_set_inode_bitmap_range(ext2fs_inode_bitmap bmap, ext2_ino_t start, unsigned int num, void *in); extern errcode_t ext2fs_get_inode_bitmap_range(ext2fs_inode_bitmap bmap, ext2_ino_t start, unsigned int num, void *out); extern errcode_t ext2fs_set_block_bitmap_range(ext2fs_block_bitmap bmap, blk_t start, unsigned int num, void *in); extern errcode_t ext2fs_get_block_bitmap_range(ext2fs_block_bitmap bmap, blk_t start, unsigned int num, void *out); /* block.c */ extern errcode_t ext2fs_block_iterate(ext2_filsys fs, ext2_ino_t ino, int flags, char *block_buf, int (*func)(ext2_filsys fs, blk_t *blocknr, int blockcnt, void *priv_data), void *priv_data); errcode_t ext2fs_block_iterate2(ext2_filsys fs, ext2_ino_t ino, int flags, char *block_buf, int (*func)(ext2_filsys fs, blk_t *blocknr, e2_blkcnt_t blockcnt, blk_t ref_blk, int ref_offset, void *priv_data), void *priv_data); /* bmap.c */ extern errcode_t ext2fs_bmap(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode, char *block_buf, int bmap_flags, blk_t block, blk_t *phys_blk); extern errcode_t ext2fs_bmap2(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode, char *block_buf, int bmap_flags, blk64_t block, int *ret_flags, blk64_t *phys_blk); #if 0 /* bmove.c */ extern errcode_t ext2fs_move_blocks(ext2_filsys fs, ext2fs_block_bitmap reserve, ext2fs_block_bitmap alloc_map, int flags); #endif /* check_desc.c */ extern errcode_t ext2fs_check_desc(ext2_filsys fs); /* closefs.c */ extern errcode_t ext2fs_close(ext2_filsys fs); extern errcode_t ext2fs_flush(ext2_filsys fs); extern int ext2fs_bg_has_super(ext2_filsys fs, int group_block); extern int ext2fs_super_and_bgd_loc(ext2_filsys fs, dgrp_t group, blk_t *ret_super_blk, blk_t *ret_old_desc_blk, blk_t *ret_new_desc_blk, int *ret_meta_bg); extern void ext2fs_update_dynamic_rev(ext2_filsys fs); /* csum.c */ extern void ext2fs_group_desc_csum_set(ext2_filsys fs, dgrp_t group); extern int ext2fs_group_desc_csum_verify(ext2_filsys fs, dgrp_t group); extern errcode_t ext2fs_set_gdt_csum(ext2_filsys fs); /* dblist.c */ extern errcode_t ext2fs_get_num_dirs(ext2_filsys fs, ext2_ino_t *ret_num_dirs); extern errcode_t ext2fs_init_dblist(ext2_filsys fs, ext2_dblist *ret_dblist); extern errcode_t ext2fs_add_dir_block(ext2_dblist dblist, ext2_ino_t ino, blk_t blk, int blockcnt); extern void ext2fs_dblist_sort(ext2_dblist dblist, EXT2_QSORT_TYPE (*sortfunc)(const void *, const void *)); extern errcode_t ext2fs_dblist_iterate(ext2_dblist dblist, int (*func)(ext2_filsys fs, struct ext2_db_entry *db_info, void *priv_data), void *priv_data); extern errcode_t ext2fs_set_dir_block(ext2_dblist dblist, ext2_ino_t ino, blk_t blk, int blockcnt); extern errcode_t ext2fs_copy_dblist(ext2_dblist src, ext2_dblist *dest); extern int ext2fs_dblist_count(ext2_dblist dblist); extern errcode_t ext2fs_dblist_get_last(ext2_dblist dblist, struct ext2_db_entry **entry); extern errcode_t ext2fs_dblist_drop_last(ext2_dblist dblist); /* dblist_dir.c */ extern errcode_t ext2fs_dblist_dir_iterate(ext2_dblist dblist, int flags, char *block_buf, int (*func)(ext2_ino_t dir, int entry, struct ext2_dir_entry *dirent, int offset, int blocksize, char *buf, void *priv_data), void *priv_data); /* dirblock.c */ extern errcode_t ext2fs_read_dir_block(ext2_filsys fs, blk_t block, void *buf); extern errcode_t ext2fs_read_dir_block2(ext2_filsys fs, blk_t block, void *buf, int flags); extern errcode_t ext2fs_write_dir_block(ext2_filsys fs, blk_t block, void *buf); extern errcode_t ext2fs_write_dir_block2(ext2_filsys fs, blk_t block, void *buf, int flags); /* dirhash.c */ extern errcode_t ext2fs_dirhash(int version, const char *name, int len, const __u32 *seed, ext2_dirhash_t *ret_hash, ext2_dirhash_t *ret_minor_hash); /* dir_iterate.c */ extern errcode_t ext2fs_get_rec_len(ext2_filsys fs, struct ext2_dir_entry *dirent, unsigned int *rec_len); extern errcode_t ext2fs_set_rec_len(ext2_filsys fs, unsigned int len, struct ext2_dir_entry *dirent); extern errcode_t ext2fs_dir_iterate(ext2_filsys fs, ext2_ino_t dir, int flags, char *block_buf, int (*func)(struct ext2_dir_entry *dirent, int offset, int blocksize, char *buf, void *priv_data), void *priv_data); extern errcode_t ext2fs_dir_iterate2(ext2_filsys fs, ext2_ino_t dir, int flags, char *block_buf, int (*func)(ext2_ino_t dir, int entry, struct ext2_dir_entry *dirent, int offset, int blocksize, char *buf, void *priv_data), void *priv_data); /* dupfs.c */ extern errcode_t ext2fs_dup_handle(ext2_filsys src, ext2_filsys *dest); /* expanddir.c */ extern errcode_t ext2fs_expand_dir(ext2_filsys fs, ext2_ino_t dir); /* ext_attr.c */ extern __u32 ext2fs_ext_attr_hash_entry(struct ext2_ext_attr_entry *entry, void *data); extern errcode_t ext2fs_read_ext_attr(ext2_filsys fs, blk_t block, void *buf); extern errcode_t ext2fs_write_ext_attr(ext2_filsys fs, blk_t block, void *buf); extern errcode_t ext2fs_adjust_ea_refcount(ext2_filsys fs, blk_t blk, char *block_buf, int adjust, __u32 *newcount); /* extent.c */ extern errcode_t ext2fs_extent_header_verify(void *ptr, int size); extern errcode_t ext2fs_extent_open(ext2_filsys fs, ext2_ino_t ino, ext2_extent_handle_t *handle); extern errcode_t ext2fs_extent_open2(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode, ext2_extent_handle_t *ret_handle); extern void ext2fs_extent_free(ext2_extent_handle_t handle); extern errcode_t ext2fs_extent_get(ext2_extent_handle_t handle, int flags, struct ext2fs_extent *extent); extern errcode_t ext2fs_extent_replace(ext2_extent_handle_t handle, int flags, struct ext2fs_extent *extent); extern errcode_t ext2fs_extent_insert(ext2_extent_handle_t handle, int flags, struct ext2fs_extent *extent); extern errcode_t ext2fs_extent_set_bmap(ext2_extent_handle_t handle, blk64_t logical, blk64_t physical, int flags); extern errcode_t ext2fs_extent_delete(ext2_extent_handle_t handle, int flags); extern errcode_t ext2fs_extent_get_info(ext2_extent_handle_t handle, struct ext2_extent_info *info); extern errcode_t ext2fs_extent_goto(ext2_extent_handle_t handle, blk64_t blk); /* fileio.c */ extern errcode_t ext2fs_file_open2(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode, int flags, ext2_file_t *ret); extern errcode_t ext2fs_file_open(ext2_filsys fs, ext2_ino_t ino, int flags, ext2_file_t *ret); extern ext2_filsys ext2fs_file_get_fs(ext2_file_t file); extern errcode_t ext2fs_file_close(ext2_file_t file); extern errcode_t ext2fs_file_flush(ext2_file_t file); extern errcode_t ext2fs_file_read(ext2_file_t file, void *buf, unsigned int wanted, unsigned int *got); extern errcode_t ext2fs_file_write(ext2_file_t file, const void *buf, unsigned int nbytes, unsigned int *written); extern errcode_t ext2fs_file_llseek(ext2_file_t file, __u64 offset, int whence, __u64 *ret_pos); extern errcode_t ext2fs_file_lseek(ext2_file_t file, ext2_off_t offset, int whence, ext2_off_t *ret_pos); errcode_t ext2fs_file_get_lsize(ext2_file_t file, __u64 *ret_size); extern ext2_off_t ext2fs_file_get_size(ext2_file_t file); extern errcode_t ext2fs_file_set_size(ext2_file_t file, ext2_off_t size); /* finddev.c */ extern char *ext2fs_find_block_device(dev_t device); /* flushb.c */ extern errcode_t ext2fs_sync_device(int fd, int flushb); /* freefs.c */ extern void ext2fs_free(ext2_filsys fs); extern void ext2fs_free_dblist(ext2_dblist dblist); extern void ext2fs_badblocks_list_free(ext2_badblocks_list bb); extern void ext2fs_u32_list_free(ext2_u32_list bb); /* gen_bitmap.c */ extern void ext2fs_free_generic_bitmap(ext2fs_inode_bitmap bitmap); extern errcode_t ext2fs_make_generic_bitmap(errcode_t magic, ext2_filsys fs, __u32 start, __u32 end, __u32 real_end, const char *descr, char *init_map, ext2fs_generic_bitmap *ret); extern errcode_t ext2fs_allocate_generic_bitmap(__u32 start, __u32 end, __u32 real_end, const char *descr, ext2fs_generic_bitmap *ret); extern errcode_t ext2fs_copy_generic_bitmap(ext2fs_generic_bitmap src, ext2fs_generic_bitmap *dest); extern void ext2fs_clear_generic_bitmap(ext2fs_generic_bitmap bitmap); extern errcode_t ext2fs_fudge_generic_bitmap_end(ext2fs_inode_bitmap bitmap, errcode_t magic, errcode_t neq, ext2_ino_t end, ext2_ino_t *oend); extern void ext2fs_set_generic_bitmap_padding(ext2fs_generic_bitmap map); extern errcode_t ext2fs_resize_generic_bitmap(errcode_t magic, __u32 new_end, __u32 new_real_end, ext2fs_generic_bitmap bmap); extern errcode_t ext2fs_compare_generic_bitmap(errcode_t magic, errcode_t neq, ext2fs_generic_bitmap bm1, ext2fs_generic_bitmap bm2); extern errcode_t ext2fs_get_generic_bitmap_range(ext2fs_generic_bitmap bmap, errcode_t magic, __u32 start, __u32 num, void *out); extern errcode_t ext2fs_set_generic_bitmap_range(ext2fs_generic_bitmap bmap, errcode_t magic, __u32 start, __u32 num, void *in); /* getsize.c */ extern errcode_t ext2fs_get_device_size(const char *file, int blocksize, blk_t *retblocks); extern errcode_t ext2fs_get_device_size2(const char *file, int blocksize, blk64_t *retblocks); /* getsectsize.c */ errcode_t ext2fs_get_device_sectsize(const char *file, int *sectsize); errcode_t ext2fs_get_device_phys_sectsize(const char *file, int *sectsize); /* i_block.c */ errcode_t ext2fs_iblk_add_blocks(ext2_filsys fs, struct ext2_inode *inode, blk64_t num_blocks); errcode_t ext2fs_iblk_sub_blocks(ext2_filsys fs, struct ext2_inode *inode, blk64_t num_blocks); errcode_t ext2fs_iblk_set(ext2_filsys fs, struct ext2_inode *inode, blk64_t b); /* imager.c */ extern errcode_t ext2fs_image_inode_write(ext2_filsys fs, int fd, int flags); extern errcode_t ext2fs_image_inode_read(ext2_filsys fs, int fd, int flags); extern errcode_t ext2fs_image_super_write(ext2_filsys fs, int fd, int flags); extern errcode_t ext2fs_image_super_read(ext2_filsys fs, int fd, int flags); extern errcode_t ext2fs_image_bitmap_write(ext2_filsys fs, int fd, int flags); extern errcode_t ext2fs_image_bitmap_read(ext2_filsys fs, int fd, int flags); /* ind_block.c */ errcode_t ext2fs_read_ind_block(ext2_filsys fs, blk_t blk, void *buf); errcode_t ext2fs_write_ind_block(ext2_filsys fs, blk_t blk, void *buf); /* initialize.c */ extern errcode_t ext2fs_initialize(const char *name, int flags, struct ext2_super_block *param, io_manager manager, ext2_filsys *ret_fs); /* icount.c */ extern void ext2fs_free_icount(ext2_icount_t icount); extern errcode_t ext2fs_create_icount_tdb(ext2_filsys fs, char *tdb_dir, int flags, ext2_icount_t *ret); extern errcode_t ext2fs_create_icount2(ext2_filsys fs, int flags, unsigned int size, ext2_icount_t hint, ext2_icount_t *ret); extern errcode_t ext2fs_create_icount(ext2_filsys fs, int flags, unsigned int size, ext2_icount_t *ret); extern errcode_t ext2fs_icount_fetch(ext2_icount_t icount, ext2_ino_t ino, __u16 *ret); extern errcode_t ext2fs_icount_increment(ext2_icount_t icount, ext2_ino_t ino, __u16 *ret); extern errcode_t ext2fs_icount_decrement(ext2_icount_t icount, ext2_ino_t ino, __u16 *ret); extern errcode_t ext2fs_icount_store(ext2_icount_t icount, ext2_ino_t ino, __u16 count); extern ext2_ino_t ext2fs_get_icount_size(ext2_icount_t icount); errcode_t ext2fs_icount_validate(ext2_icount_t icount, FILE *); /* inode.c */ extern errcode_t ext2fs_flush_icache(ext2_filsys fs); extern errcode_t ext2fs_get_next_inode_full(ext2_inode_scan scan, ext2_ino_t *ino, struct ext2_inode *inode, int bufsize); extern errcode_t ext2fs_open_inode_scan(ext2_filsys fs, int buffer_blocks, ext2_inode_scan *ret_scan); extern void ext2fs_close_inode_scan(ext2_inode_scan scan); extern errcode_t ext2fs_get_next_inode(ext2_inode_scan scan, ext2_ino_t *ino, struct ext2_inode *inode); extern errcode_t ext2fs_inode_scan_goto_blockgroup(ext2_inode_scan scan, int group); extern void ext2fs_set_inode_callback (ext2_inode_scan scan, errcode_t (*done_group)(ext2_filsys fs, ext2_inode_scan scan, dgrp_t group, void * priv_data), void *done_group_data); extern int ext2fs_inode_scan_flags(ext2_inode_scan scan, int set_flags, int clear_flags); extern errcode_t ext2fs_read_inode_full(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode * inode, int bufsize); extern errcode_t ext2fs_read_inode (ext2_filsys fs, ext2_ino_t ino, struct ext2_inode * inode); extern errcode_t ext2fs_write_inode_full(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode * inode, int bufsize); extern errcode_t ext2fs_write_inode(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode * inode); extern errcode_t ext2fs_write_new_inode(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode * inode); extern errcode_t ext2fs_get_blocks(ext2_filsys fs, ext2_ino_t ino, blk_t *blocks); extern errcode_t ext2fs_check_directory(ext2_filsys fs, ext2_ino_t ino); /* inode_io.c */ extern io_manager inode_io_manager; extern errcode_t ext2fs_inode_io_intern(ext2_filsys fs, ext2_ino_t ino, char **name); extern errcode_t ext2fs_inode_io_intern2(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode, char **name); /* ismounted.c */ extern errcode_t ext2fs_check_if_mounted(const char *file, int *mount_flags); extern errcode_t ext2fs_check_mount_point(const char *device, int *mount_flags, char *mtpt, int mtlen); /* namei.c */ extern errcode_t ext2fs_lookup(ext2_filsys fs, ext2_ino_t dir, const char *name, int namelen, char *buf, ext2_ino_t *inode); extern errcode_t ext2fs_namei(ext2_filsys fs, ext2_ino_t root, ext2_ino_t cwd, const char *name, ext2_ino_t *inode); errcode_t ext2fs_namei_follow(ext2_filsys fs, ext2_ino_t root, ext2_ino_t cwd, const char *name, ext2_ino_t *inode); extern errcode_t ext2fs_follow_link(ext2_filsys fs, ext2_ino_t root, ext2_ino_t cwd, ext2_ino_t inode, ext2_ino_t *res_inode); /* native.c */ int ext2fs_native_flag(void); /* newdir.c */ extern errcode_t ext2fs_new_dir_block(ext2_filsys fs, ext2_ino_t dir_ino, ext2_ino_t parent_ino, char **block); /* mkdir.c */ extern errcode_t ext2fs_mkdir(ext2_filsys fs, ext2_ino_t parent, ext2_ino_t inum, const char *name); /* mkjournal.c */ extern errcode_t ext2fs_zero_blocks(ext2_filsys fs, blk_t blk, int num, blk_t *ret_blk, int *ret_count); extern errcode_t ext2fs_create_journal_superblock(ext2_filsys fs, __u32 size, int flags, char **ret_jsb); extern errcode_t ext2fs_add_journal_device(ext2_filsys fs, ext2_filsys journal_dev); extern errcode_t ext2fs_add_journal_inode(ext2_filsys fs, blk_t size, int flags); extern int ext2fs_default_journal_size(__u64 blocks); /* openfs.c */ extern errcode_t ext2fs_open(const char *name, int flags, int superblock, unsigned int block_size, io_manager manager, ext2_filsys *ret_fs); extern errcode_t ext2fs_open2(const char *name, const char *io_options, int flags, int superblock, unsigned int block_size, io_manager manager, ext2_filsys *ret_fs); extern blk_t ext2fs_descriptor_block_loc(ext2_filsys fs, blk_t group_block, dgrp_t i); errcode_t ext2fs_get_data_io(ext2_filsys fs, io_channel *old_io); errcode_t ext2fs_set_data_io(ext2_filsys fs, io_channel new_io); errcode_t ext2fs_rewrite_to_io(ext2_filsys fs, io_channel new_io); /* get_pathname.c */ extern errcode_t ext2fs_get_pathname(ext2_filsys fs, ext2_ino_t dir, ext2_ino_t ino, char **name); /* link.c */ errcode_t ext2fs_link(ext2_filsys fs, ext2_ino_t dir, const char *name, ext2_ino_t ino, int flags); errcode_t ext2fs_unlink(ext2_filsys fs, ext2_ino_t dir, const char *name, ext2_ino_t ino, int flags); /* read_bb.c */ extern errcode_t ext2fs_read_bb_inode(ext2_filsys fs, ext2_badblocks_list *bb_list); /* read_bb_file.c */ extern errcode_t ext2fs_read_bb_FILE2(ext2_filsys fs, FILE *f, ext2_badblocks_list *bb_list, void *priv_data, void (*invalid)(ext2_filsys fs, blk_t blk, char *badstr, void *priv_data)); extern errcode_t ext2fs_read_bb_FILE(ext2_filsys fs, FILE *f, ext2_badblocks_list *bb_list, void (*invalid)(ext2_filsys fs, blk_t blk)); /* res_gdt.c */ extern errcode_t ext2fs_create_resize_inode(ext2_filsys fs); /* swapfs.c */ extern void ext2fs_swap_ext_attr(char *to, char *from, int bufsize, int has_header); extern void ext2fs_swap_ext_attr_header(struct ext2_ext_attr_header *to_header, struct ext2_ext_attr_header *from_hdr); extern void ext2fs_swap_ext_attr_entry(struct ext2_ext_attr_entry *to_entry, struct ext2_ext_attr_entry *from_entry); extern void ext2fs_swap_super(struct ext2_super_block * super); extern void ext2fs_swap_group_desc(struct ext2_group_desc *gdp); extern void ext2fs_swap_inode_full(ext2_filsys fs, struct ext2_inode_large *t, struct ext2_inode_large *f, int hostorder, int bufsize); extern void ext2fs_swap_inode(ext2_filsys fs,struct ext2_inode *t, struct ext2_inode *f, int hostorder); /* valid_blk.c */ extern int ext2fs_inode_has_valid_blocks(struct ext2_inode *inode); /* version.c */ extern int ext2fs_parse_version_string(const char *ver_string); extern int ext2fs_get_library_version(const char **ver_string, const char **date_string); /* write_bb_file.c */ extern errcode_t ext2fs_write_bb_FILE(ext2_badblocks_list bb_list, unsigned int flags, FILE *f); /* inline functions */ extern errcode_t ext2fs_get_mem(unsigned long size, void *ptr); extern errcode_t ext2fs_get_memalign(unsigned long size, unsigned long align, void *ptr); extern errcode_t ext2fs_free_mem(void *ptr); extern errcode_t ext2fs_resize_mem(unsigned long old_size, unsigned long size, void *ptr); extern void ext2fs_mark_super_dirty(ext2_filsys fs); extern void ext2fs_mark_changed(ext2_filsys fs); extern int ext2fs_test_changed(ext2_filsys fs); extern void ext2fs_mark_valid(ext2_filsys fs); extern void ext2fs_unmark_valid(ext2_filsys fs); extern int ext2fs_test_valid(ext2_filsys fs); extern void ext2fs_mark_ib_dirty(ext2_filsys fs); extern void ext2fs_mark_bb_dirty(ext2_filsys fs); extern int ext2fs_test_ib_dirty(ext2_filsys fs); extern int ext2fs_test_bb_dirty(ext2_filsys fs); extern int ext2fs_group_of_blk(ext2_filsys fs, blk_t blk); extern int ext2fs_group_of_ino(ext2_filsys fs, ext2_ino_t ino); extern blk_t ext2fs_group_first_block(ext2_filsys fs, dgrp_t group); extern blk_t ext2fs_group_last_block(ext2_filsys fs, dgrp_t group); extern blk_t ext2fs_inode_data_blocks(ext2_filsys fs, struct ext2_inode *inode); extern unsigned int ext2fs_div_ceil(unsigned int a, unsigned int b); /* * The actual inlined functions definitions themselves... * * If NO_INLINE_FUNCS is defined, then we won't try to do inline * functions at all! */ #if (defined(INCLUDE_INLINE_FUNCS) || !defined(NO_INLINE_FUNCS)) #ifdef INCLUDE_INLINE_FUNCS #define _INLINE_ extern #else #ifdef __GNUC__ #define _INLINE_ extern __inline__ #else /* For Watcom C */ #define _INLINE_ extern inline #endif #endif #ifndef EXT2_CUSTOM_MEMORY_ROUTINES #include /* * Allocate memory */ _INLINE_ errcode_t ext2fs_get_mem(unsigned long size, void *ptr) { void *pp; pp = malloc(size); if (!pp) return EXT2_ET_NO_MEMORY; memcpy(ptr, &pp, sizeof (pp)); return 0; } _INLINE_ errcode_t ext2fs_get_memalign(unsigned long size, unsigned long align, void *ptr) { errcode_t retval; if (align == 0) align = 8; if (retval = posix_memalign((void **) ptr, align, size)) { if (retval == ENOMEM) return EXT2_ET_NO_MEMORY; return retval; } return 0; } _INLINE_ errcode_t ext2fs_get_array(unsigned long count, unsigned long size, void *ptr) { if (count && (-1UL)/countflags |= EXT2_FLAG_DIRTY | EXT2_FLAG_CHANGED; } /* * Mark a filesystem as changed */ _INLINE_ void ext2fs_mark_changed(ext2_filsys fs) { fs->flags |= EXT2_FLAG_CHANGED; } /* * Check to see if a filesystem has changed */ _INLINE_ int ext2fs_test_changed(ext2_filsys fs) { return (fs->flags & EXT2_FLAG_CHANGED); } /* * Mark a filesystem as valid */ _INLINE_ void ext2fs_mark_valid(ext2_filsys fs) { fs->flags |= EXT2_FLAG_VALID; } /* * Mark a filesystem as NOT valid */ _INLINE_ void ext2fs_unmark_valid(ext2_filsys fs) { fs->flags &= ~EXT2_FLAG_VALID; } /* * Check to see if a filesystem is valid */ _INLINE_ int ext2fs_test_valid(ext2_filsys fs) { return (fs->flags & EXT2_FLAG_VALID); } /* * Mark the inode bitmap as dirty */ _INLINE_ void ext2fs_mark_ib_dirty(ext2_filsys fs) { fs->flags |= EXT2_FLAG_IB_DIRTY | EXT2_FLAG_CHANGED; } /* * Mark the block bitmap as dirty */ _INLINE_ void ext2fs_mark_bb_dirty(ext2_filsys fs) { fs->flags |= EXT2_FLAG_BB_DIRTY | EXT2_FLAG_CHANGED; } /* * Check to see if a filesystem's inode bitmap is dirty */ _INLINE_ int ext2fs_test_ib_dirty(ext2_filsys fs) { return (fs->flags & EXT2_FLAG_IB_DIRTY); } /* * Check to see if a filesystem's block bitmap is dirty */ _INLINE_ int ext2fs_test_bb_dirty(ext2_filsys fs) { return (fs->flags & EXT2_FLAG_BB_DIRTY); } /* * Return the group # of a block */ _INLINE_ int ext2fs_group_of_blk(ext2_filsys fs, blk_t blk) { return (blk - fs->super->s_first_data_block) / fs->super->s_blocks_per_group; } /* * Return the group # of an inode number */ _INLINE_ int ext2fs_group_of_ino(ext2_filsys fs, ext2_ino_t ino) { return (ino - 1) / fs->super->s_inodes_per_group; } /* * Return the first block (inclusive) in a group */ _INLINE_ blk_t ext2fs_group_first_block(ext2_filsys fs, dgrp_t group) { return fs->super->s_first_data_block + (group * fs->super->s_blocks_per_group); } /* * Return the last block (inclusive) in a group */ _INLINE_ blk_t ext2fs_group_last_block(ext2_filsys fs, dgrp_t group) { return (group == fs->group_desc_count - 1 ? fs->super->s_blocks_count - 1 : ext2fs_group_first_block(fs, group) + (fs->super->s_blocks_per_group - 1)); } _INLINE_ blk_t ext2fs_inode_data_blocks(ext2_filsys fs, struct ext2_inode *inode) { return inode->i_blocks - (inode->i_file_acl ? fs->blocksize >> 9 : 0); } /* * This is an efficient, overflow safe way of calculating ceil((1.0 * a) / b) */ _INLINE_ unsigned int ext2fs_div_ceil(unsigned int a, unsigned int b) { if (!a) return 0; return ((a - 1) / b) + 1; } #undef _INLINE_ #endif #ifdef __cplusplus } #endif #endif /* _EXT2FS_EXT2FS_H */ e2fsprogs-1.41.14/lib/ext2fs/tdbtool.c0000644031104000366760000003201211374366146015437 0ustar tytso/* Unix SMB/CIFS implementation. Samba database functions Copyright (C) Andrew Tridgell 1999-2000 Copyright (C) Paul `Rusty' Russell 2000 Copyright (C) Jeremy Allison 2000 Copyright (C) Andrew Esh 2001 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "tdb.h" static int do_command(void); const char *cmdname; char *arg1, *arg2; size_t arg1len, arg2len; int bIterate = 0; char *line; TDB_DATA iterate_kbuf; char cmdline[1024]; enum commands { CMD_CREATE_TDB, CMD_OPEN_TDB, CMD_ERASE, CMD_DUMP, CMD_INSERT, CMD_MOVE, CMD_STORE, CMD_SHOW, CMD_KEYS, CMD_HEXKEYS, CMD_DELETE, CMD_LIST_HASH_FREE, CMD_LIST_FREE, CMD_INFO, CMD_FIRST, CMD_NEXT, CMD_SYSTEM, CMD_QUIT, CMD_HELP }; typedef struct { const char *name; enum commands cmd; } COMMAND_TABLE; COMMAND_TABLE cmd_table[] = { {"create", CMD_CREATE_TDB}, {"open", CMD_OPEN_TDB}, {"erase", CMD_ERASE}, {"dump", CMD_DUMP}, {"insert", CMD_INSERT}, {"move", CMD_MOVE}, {"store", CMD_STORE}, {"show", CMD_SHOW}, {"keys", CMD_KEYS}, {"hexkeys", CMD_HEXKEYS}, {"delete", CMD_DELETE}, {"list", CMD_LIST_HASH_FREE}, {"free", CMD_LIST_FREE}, {"info", CMD_INFO}, {"first", CMD_FIRST}, {"1", CMD_FIRST}, {"next", CMD_NEXT}, {"n", CMD_NEXT}, {"quit", CMD_QUIT}, {"q", CMD_QUIT}, {"!", CMD_SYSTEM}, {NULL, CMD_HELP} }; /* a tdb tool for manipulating a tdb database */ static TDB_CONTEXT *tdb; static int print_rec(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state); static int print_key(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state); static int print_hexkey(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state); static void print_asc(const char *buf,int len) { int i; /* We're probably printing ASCII strings so don't try to display the trailing NULL character. */ if (buf[len - 1] == 0) len--; for (i=0;i8) printf(" "); while (n--) printf(" "); n = i%16; if (n > 8) n = 8; print_asc(&buf[i-(i%16)],n); printf(" "); n = (i%16) - n; if (n>0) print_asc(&buf[i-n],n); printf("\n"); } } static void help(void) { printf("\n" "tdbtool: \n" " create dbname : create a database\n" " open dbname : open an existing database\n" " erase : erase the database\n" " dump : dump the database as strings\n" " keys : dump the database keys as strings\n" " hexkeys : dump the database keys as hex values\n" " info : print summary info about the database\n" " insert key data : insert a record\n" " move key file : move a record to a destination tdb\n" " store key data : store a record (replace)\n" " show key : show a record by key\n" " delete key : delete a record by key\n" " list : print the database hash table and freelist\n" " free : print the database freelist\n" " ! command : execute system command\n" " 1 | first : print the first record\n" " n | next : print the next record\n" " q | quit : terminate\n" " \\n : repeat 'next' command\n" "\n"); } static void terror(const char *why) { printf("%s\n", why); } static void create_tdb(const char *tdbname) { if (tdb) tdb_close(tdb); tdb = tdb_open(tdbname, 0, TDB_CLEAR_IF_FIRST, O_RDWR | O_CREAT | O_TRUNC, 0600); if (!tdb) { printf("Could not create %s: %s\n", tdbname, strerror(errno)); } } static void open_tdb(const char *tdbname) { if (tdb) tdb_close(tdb); tdb = tdb_open(tdbname, 0, 0, O_RDWR, 0600); if (!tdb) { printf("Could not open %s: %s\n", tdbname, strerror(errno)); } } static void insert_tdb(char *keyname, size_t keylen, char* data, size_t datalen) { TDB_DATA key, dbuf; if ((keyname == NULL) || (keylen == 0)) { terror("need key"); return; } key.dptr = (unsigned char *)keyname; key.dsize = keylen; dbuf.dptr = (unsigned char *)data; dbuf.dsize = datalen; if (tdb_store(tdb, key, dbuf, TDB_INSERT) == -1) { terror("insert failed"); } } static void store_tdb(char *keyname, size_t keylen, char* data, size_t datalen) { TDB_DATA key, dbuf; if ((keyname == NULL) || (keylen == 0)) { terror("need key"); return; } if ((data == NULL) || (datalen == 0)) { terror("need data"); return; } key.dptr = (unsigned char *)keyname; key.dsize = keylen; dbuf.dptr = (unsigned char *)data; dbuf.dsize = datalen; printf("Storing key:\n"); print_rec(tdb, key, dbuf, NULL); if (tdb_store(tdb, key, dbuf, TDB_REPLACE) == -1) { terror("store failed"); } } static void show_tdb(char *keyname, size_t keylen) { TDB_DATA key, dbuf; if ((keyname == NULL) || (keylen == 0)) { terror("need key"); return; } key.dptr = (unsigned char *)keyname; key.dsize = keylen; dbuf = tdb_fetch(tdb, key); if (!dbuf.dptr) { terror("fetch failed"); return; } print_rec(tdb, key, dbuf, NULL); free( dbuf.dptr ); return; } static void delete_tdb(char *keyname, size_t keylen) { TDB_DATA key; if ((keyname == NULL) || (keylen == 0)) { terror("need key"); return; } key.dptr = (unsigned char *)keyname; key.dsize = keylen; if (tdb_delete(tdb, key) != 0) { terror("delete failed"); } } static void move_rec(char *keyname, size_t keylen, char* tdbname) { TDB_DATA key, dbuf; TDB_CONTEXT *dst_tdb; if ((keyname == NULL) || (keylen == 0)) { terror("need key"); return; } if ( !tdbname ) { terror("need destination tdb name"); return; } key.dptr = (unsigned char *)keyname; key.dsize = keylen; dbuf = tdb_fetch(tdb, key); if (!dbuf.dptr) { terror("fetch failed"); return; } print_rec(tdb, key, dbuf, NULL); dst_tdb = tdb_open(tdbname, 0, 0, O_RDWR, 0600); if ( !dst_tdb ) { terror("unable to open destination tdb"); return; } if ( tdb_store( dst_tdb, key, dbuf, TDB_REPLACE ) == -1 ) { terror("failed to move record"); } else printf("record moved\n"); tdb_close( dst_tdb ); return; } static int print_rec(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state) { printf("\nkey %d bytes\n", (int)key.dsize); print_asc((const char *)key.dptr, key.dsize); printf("\ndata %d bytes\n", (int)dbuf.dsize); print_data((const char *)dbuf.dptr, dbuf.dsize); return 0; } static int print_key(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state) { printf("key %d bytes: ", (int)key.dsize); print_asc((const char *)key.dptr, key.dsize); printf("\n"); return 0; } static int print_hexkey(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state) { printf("key %d bytes\n", (int)key.dsize); print_data((const char *)key.dptr, key.dsize); printf("\n"); return 0; } static int total_bytes; static int traverse_fn(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state) { total_bytes += dbuf.dsize; return 0; } static void info_tdb(void) { int count; total_bytes = 0; if ((count = tdb_traverse(tdb, traverse_fn, NULL)) == -1) printf("Error = %s\n", tdb_errorstr(tdb)); else printf("%d records totalling %d bytes\n", count, total_bytes); } static char *tdb_getline(const char *prompt) { static char thisline[1024]; char *p; fputs(prompt, stdout); thisline[0] = 0; p = fgets(thisline, sizeof(thisline)-1, stdin); if (p) p = strchr(p, '\n'); if (p) *p = 0; return p?thisline:NULL; } static int do_delete_fn(TDB_CONTEXT *the_tdb, TDB_DATA key, TDB_DATA dbuf, void *state) { return tdb_delete(the_tdb, key); } static void first_record(TDB_CONTEXT *the_tdb, TDB_DATA *pkey) { TDB_DATA dbuf; *pkey = tdb_firstkey(the_tdb); dbuf = tdb_fetch(the_tdb, *pkey); if (!dbuf.dptr) terror("fetch failed"); else { print_rec(the_tdb, *pkey, dbuf, NULL); } } static void next_record(TDB_CONTEXT *the_tdb, TDB_DATA *pkey) { TDB_DATA dbuf; *pkey = tdb_nextkey(the_tdb, *pkey); dbuf = tdb_fetch(the_tdb, *pkey); if (!dbuf.dptr) terror("fetch failed"); else print_rec(the_tdb, *pkey, dbuf, NULL); } static int do_command(void) { COMMAND_TABLE *ctp = cmd_table; enum commands mycmd = CMD_HELP; int cmd_len; if (cmdname && strlen(cmdname) == 0) { mycmd = CMD_NEXT; } else { while (ctp->name) { cmd_len = strlen(ctp->name); if (strncmp(ctp->name,cmdname,cmd_len) == 0) { mycmd = ctp->cmd; break; } ctp++; } } switch (mycmd) { case CMD_CREATE_TDB: bIterate = 0; create_tdb(arg1); return 0; case CMD_OPEN_TDB: bIterate = 0; open_tdb(arg1); return 0; case CMD_SYSTEM: /* Shell command */ system(arg1); return 0; case CMD_QUIT: return 1; default: /* all the rest require a open database */ if (!tdb) { bIterate = 0; terror("database not open"); help(); return 0; } switch (mycmd) { case CMD_ERASE: bIterate = 0; tdb_traverse(tdb, do_delete_fn, NULL); return 0; case CMD_DUMP: bIterate = 0; tdb_traverse(tdb, print_rec, NULL); return 0; case CMD_INSERT: bIterate = 0; insert_tdb(arg1, arg1len,arg2,arg2len); return 0; case CMD_MOVE: bIterate = 0; move_rec(arg1,arg1len,arg2); return 0; case CMD_STORE: bIterate = 0; store_tdb(arg1,arg1len,arg2,arg2len); return 0; case CMD_SHOW: bIterate = 0; show_tdb(arg1, arg1len); return 0; case CMD_KEYS: tdb_traverse(tdb, print_key, NULL); return 0; case CMD_HEXKEYS: tdb_traverse(tdb, print_hexkey, NULL); return 0; case CMD_DELETE: bIterate = 0; delete_tdb(arg1,arg1len); return 0; case CMD_LIST_HASH_FREE: tdb_dump_all(tdb); return 0; case CMD_LIST_FREE: tdb_printfreelist(tdb); return 0; case CMD_INFO: info_tdb(); return 0; case CMD_FIRST: bIterate = 1; first_record(tdb, &iterate_kbuf); return 0; case CMD_NEXT: if (bIterate) next_record(tdb, &iterate_kbuf); return 0; case CMD_HELP: help(); return 0; case CMD_CREATE_TDB: case CMD_OPEN_TDB: case CMD_SYSTEM: case CMD_QUIT: /* * unhandled commands. cases included here to avoid compiler * warnings. */ return 0; } } return 0; } static char *convert_string(char *instring, size_t *sizep) { size_t length = 0; char *outp, *inp; char temp[3]; outp = inp = instring; while (*inp) { if (*inp == '\\') { inp++; if (*inp && strchr("0123456789abcdefABCDEF",(int)*inp)) { temp[0] = *inp++; temp[1] = '\0'; if (*inp && strchr("0123456789abcdefABCDEF",(int)*inp)) { temp[1] = *inp++; temp[2] = '\0'; } *outp++ = (char)strtol((const char *)temp,NULL,16); } else { *outp++ = *inp++; } } else { *outp++ = *inp++; } length++; } *sizep = length; return instring; } int main(int argc, char *argv[]) { cmdname = ""; arg1 = NULL; arg1len = 0; arg2 = NULL; arg2len = 0; if (argv[1]) { cmdname = "open"; arg1 = argv[1]; do_command(); cmdname = ""; arg1 = NULL; } switch (argc) { case 1: case 2: /* Interactive mode */ while ((cmdname = tdb_getline("tdb> "))) { arg2 = arg1 = NULL; if ((arg1 = strchr((const char *)cmdname,' ')) != NULL) { arg1++; arg2 = arg1; while (*arg2) { if (*arg2 == ' ') { *arg2++ = '\0'; break; } if ((*arg2++ == '\\') && (*arg2 == ' ')) { arg2++; } } } if (arg1) arg1 = convert_string(arg1,&arg1len); if (arg2) arg2 = convert_string(arg2,&arg2len); if (do_command()) break; } break; case 5: arg2 = convert_string(argv[4],&arg2len); case 4: arg1 = convert_string(argv[3],&arg1len); case 3: cmdname = argv[2]; default: do_command(); break; } if (tdb) tdb_close(tdb); return 0; } e2fsprogs-1.41.14/lib/ext2fs/valid_blk.c0000644031104000116100000000251511504417000016350 0ustar tytsoeng/* * valid_blk.c --- does the inode have valid blocks? * * Copyright 1997 by Theodore Ts'o * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #include #if HAVE_UNISTD_H #include #endif #include #include #include "ext2_fs.h" #include "ext2fs.h" /* * This function returns 1 if the inode's block entries actually * contain block entries. */ int ext2fs_inode_has_valid_blocks(struct ext2_inode *inode) { /* * Only directories, regular files, and some symbolic links * have valid block entries. */ if (!LINUX_S_ISDIR(inode->i_mode) && !LINUX_S_ISREG(inode->i_mode) && !LINUX_S_ISLNK(inode->i_mode)) return 0; /* * If the symbolic link is a "fast symlink", then the symlink * target is stored in the block entries. */ if (LINUX_S_ISLNK (inode->i_mode)) { if (inode->i_file_acl == 0) { /* With no EA block, we can rely on i_blocks */ if (inode->i_blocks == 0) return 0; } else { /* With an EA block, life gets more tricky */ if (inode->i_size >= EXT2_N_BLOCKS*4) return 1; /* definitely using i_block[] */ if (inode->i_size > 4 && inode->i_block[1] == 0) return 1; /* definitely using i_block[] */ return 0; /* Probably a fast symlink */ } } return 1; } e2fsprogs-1.41.14/lib/ext2fs/e2image.h0000644031104000366760000000253111405316370015276 0ustar tytso/* * e2image.h --- header file describing the ext2 image format * * Copyright (C) 2000 Theodore Ts'o. * * Note: this uses the POSIX IO interfaces, unlike most of the other * functions in this library. So sue me. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ struct ext2_image_hdr { __u32 magic_number; /* This must be EXT2_ET_MAGIC_E2IMAGE */ char magic_descriptor[16]; /* "Ext2 Image 1.0", w/ null padding */ char fs_hostname[64];/* Hostname of machine of image */ char fs_netaddr[32]; /* Network address */ __u32 fs_netaddr_type;/* 0 = IPV4, 1 = IPV6, etc. */ __u32 fs_device; /* Device number of image */ char fs_device_name[64]; /* Device name */ char fs_uuid[16]; /* UUID of filesystem */ __u32 fs_blocksize; /* Block size of the filesystem */ __u32 fs_reserved[8]; __u32 image_device; /* Device number of image file */ __u32 image_inode; /* Inode number of image file */ __u32 image_time; /* Time of image creation */ __u32 image_reserved[8]; __u32 offset_super; /* Byte offset of the sb and descriptors */ __u32 offset_inode; /* Byte offset of the inode table */ __u32 offset_inodemap; /* Byte offset of the inode bitmaps */ __u32 offset_blockmap; /* Byte offset of the inode bitmaps */ __u32 offset_reserved[8]; }; e2fsprogs-1.41.14/lib/ext2fs/closefs.c0000644031104000116100000002461311504417000016062 0ustar tytsoeng/* * closefs.c --- close an ext2 filesystem * * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #include #if HAVE_UNISTD_H #include #endif #include #include #include "ext2_fs.h" #include "ext2fsP.h" static int test_root(int a, int b) { if (a == 0) return 1; while (1) { if (a == 1) return 1; if (a % b) return 0; a = a / b; } } int ext2fs_bg_has_super(ext2_filsys fs, int group_block) { if (!(fs->super->s_feature_ro_compat & EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)) return 1; if (test_root(group_block, 3) || (test_root(group_block, 5)) || test_root(group_block, 7)) return 1; return 0; } /* * This function returns the location of the superblock, block group * descriptors for a given block group. It currently returns the * number of free blocks assuming that inode table and allocation * bitmaps will be in the group. This is not necessarily the case * when the flex_bg feature is enabled, so callers should take care! * It was only really intended for use by mke2fs, and even there it's * not that useful. In the future, when we redo this function for * 64-bit block numbers, we should probably return the number of * blocks used by the super block and group descriptors instead. * * See also the comment for ext2fs_reserve_super_and_bgd() */ int ext2fs_super_and_bgd_loc(ext2_filsys fs, dgrp_t group, blk_t *ret_super_blk, blk_t *ret_old_desc_blk, blk_t *ret_new_desc_blk, int *ret_meta_bg) { blk_t group_block, super_blk = 0, old_desc_blk = 0, new_desc_blk = 0; unsigned int meta_bg, meta_bg_size; blk_t numblocks, old_desc_blocks; int has_super; group_block = ext2fs_group_first_block(fs, group); if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) old_desc_blocks = fs->super->s_first_meta_bg; else old_desc_blocks = fs->desc_blocks + fs->super->s_reserved_gdt_blocks; if (group == fs->group_desc_count-1) { numblocks = (fs->super->s_blocks_count - fs->super->s_first_data_block) % fs->super->s_blocks_per_group; if (!numblocks) numblocks = fs->super->s_blocks_per_group; } else numblocks = fs->super->s_blocks_per_group; has_super = ext2fs_bg_has_super(fs, group); if (has_super) { super_blk = group_block; numblocks--; } meta_bg_size = EXT2_DESC_PER_BLOCK(fs->super); meta_bg = group / meta_bg_size; if (!(fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) || (meta_bg < fs->super->s_first_meta_bg)) { if (has_super) { old_desc_blk = group_block + 1; numblocks -= old_desc_blocks; } } else { if (((group % meta_bg_size) == 0) || ((group % meta_bg_size) == 1) || ((group % meta_bg_size) == (meta_bg_size-1))) { if (has_super) has_super = 1; new_desc_blk = group_block + has_super; numblocks--; } } numblocks -= 2 + fs->inode_blocks_per_group; if (ret_super_blk) *ret_super_blk = super_blk; if (ret_old_desc_blk) *ret_old_desc_blk = old_desc_blk; if (ret_new_desc_blk) *ret_new_desc_blk = new_desc_blk; if (ret_meta_bg) *ret_meta_bg = meta_bg; return (numblocks); } /* * This function forces out the primary superblock. We need to only * write out those fields which we have changed, since if the * filesystem is mounted, it may have changed some of the other * fields. * * It takes as input a superblock which has already been byte swapped * (if necessary). * */ static errcode_t write_primary_superblock(ext2_filsys fs, struct ext2_super_block *super) { __u16 *old_super, *new_super; int check_idx, write_idx, size; errcode_t retval; if (!fs->io->manager->write_byte || !fs->orig_super) { fallback: io_channel_set_blksize(fs->io, SUPERBLOCK_OFFSET); retval = io_channel_write_blk(fs->io, 1, -SUPERBLOCK_SIZE, super); io_channel_set_blksize(fs->io, fs->blocksize); return retval; } old_super = (__u16 *) fs->orig_super; new_super = (__u16 *) super; for (check_idx = 0; check_idx < SUPERBLOCK_SIZE/2; check_idx++) { if (old_super[check_idx] == new_super[check_idx]) continue; write_idx = check_idx; for (check_idx++; check_idx < SUPERBLOCK_SIZE/2; check_idx++) if (old_super[check_idx] == new_super[check_idx]) break; size = 2 * (check_idx - write_idx); #if 0 printf("Writing %d bytes starting at %d\n", size, write_idx*2); #endif retval = io_channel_write_byte(fs->io, SUPERBLOCK_OFFSET + (2 * write_idx), size, new_super + write_idx); if (retval == EXT2_ET_UNIMPLEMENTED) goto fallback; if (retval) return retval; } memcpy(fs->orig_super, super, SUPERBLOCK_SIZE); return 0; } /* * Updates the revision to EXT2_DYNAMIC_REV */ void ext2fs_update_dynamic_rev(ext2_filsys fs) { struct ext2_super_block *sb = fs->super; if (sb->s_rev_level > EXT2_GOOD_OLD_REV) return; sb->s_rev_level = EXT2_DYNAMIC_REV; sb->s_first_ino = EXT2_GOOD_OLD_FIRST_INO; sb->s_inode_size = EXT2_GOOD_OLD_INODE_SIZE; /* s_uuid is handled by e2fsck already */ /* other fields should be left alone */ } static errcode_t write_backup_super(ext2_filsys fs, dgrp_t group, blk_t group_block, struct ext2_super_block *super_shadow) { dgrp_t sgrp = group; if (sgrp > ((1 << 16) - 1)) sgrp = (1 << 16) - 1; #ifdef WORDS_BIGENDIAN super_shadow->s_block_group_nr = ext2fs_swab16(sgrp); #else fs->super->s_block_group_nr = sgrp; #endif return io_channel_write_blk(fs->io, group_block, -SUPERBLOCK_SIZE, super_shadow); } errcode_t ext2fs_flush(ext2_filsys fs) { dgrp_t i; errcode_t retval; unsigned long fs_state; __u32 feature_incompat; struct ext2_super_block *super_shadow = 0; struct ext2_group_desc *group_shadow = 0; #ifdef WORDS_BIGENDIAN struct ext2_group_desc *s, *t; dgrp_t j; #endif char *group_ptr; int old_desc_blocks; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); fs_state = fs->super->s_state; feature_incompat = fs->super->s_feature_incompat; fs->super->s_wtime = fs->now ? fs->now : time(NULL); fs->super->s_block_group_nr = 0; #ifdef WORDS_BIGENDIAN retval = EXT2_ET_NO_MEMORY; retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &super_shadow); if (retval) goto errout; retval = ext2fs_get_array(fs->desc_blocks, fs->blocksize, &group_shadow); if (retval) goto errout; memset(group_shadow, 0, (size_t) fs->blocksize * fs->desc_blocks); /* swap the group descriptors */ for (j=0, s=fs->group_desc, t=group_shadow; j < fs->group_desc_count; j++, t++, s++) { *t = *s; ext2fs_swap_group_desc(t); } #else super_shadow = fs->super; group_shadow = fs->group_desc; #endif /* * Set the state of the FS to be non-valid. (The state has * already been backed up earlier, and will be restored after * we write out the backup superblocks.) */ fs->super->s_state &= ~EXT2_VALID_FS; fs->super->s_feature_incompat &= ~EXT3_FEATURE_INCOMPAT_RECOVER; #ifdef WORDS_BIGENDIAN *super_shadow = *fs->super; ext2fs_swap_super(super_shadow); #endif /* * If this is an external journal device, don't write out the * block group descriptors or any of the backup superblocks */ if (fs->super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) goto write_primary_superblock_only; /* * Write out the master group descriptors, and the backup * superblocks and group descriptors. */ group_ptr = (char *) group_shadow; if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) old_desc_blocks = fs->super->s_first_meta_bg; else old_desc_blocks = fs->desc_blocks; for (i = 0; i < fs->group_desc_count; i++) { blk_t super_blk, old_desc_blk, new_desc_blk; int meta_bg; ext2fs_super_and_bgd_loc(fs, i, &super_blk, &old_desc_blk, &new_desc_blk, &meta_bg); if (!(fs->flags & EXT2_FLAG_MASTER_SB_ONLY) &&i && super_blk) { retval = write_backup_super(fs, i, super_blk, super_shadow); if (retval) goto errout; } if (fs->flags & EXT2_FLAG_SUPER_ONLY) continue; if ((old_desc_blk) && (!(fs->flags & EXT2_FLAG_MASTER_SB_ONLY) || (i == 0))) { retval = io_channel_write_blk(fs->io, old_desc_blk, old_desc_blocks, group_ptr); if (retval) goto errout; } if (new_desc_blk) { retval = io_channel_write_blk(fs->io, new_desc_blk, 1, group_ptr + (meta_bg*fs->blocksize)); if (retval) goto errout; } } /* * If the write_bitmaps() function is present, call it to * flush the bitmaps. This is done this way so that a simple * program that doesn't mess with the bitmaps doesn't need to * drag in the bitmaps.c code. */ if (fs->write_bitmaps) { retval = fs->write_bitmaps(fs); if (retval) goto errout; } write_primary_superblock_only: /* * Write out master superblock. This has to be done * separately, since it is located at a fixed location * (SUPERBLOCK_OFFSET). We flush all other pending changes * out to disk first, just to avoid a race condition with an * insy-tinsy window.... */ fs->super->s_block_group_nr = 0; fs->super->s_state = fs_state; fs->super->s_feature_incompat = feature_incompat; #ifdef WORDS_BIGENDIAN *super_shadow = *fs->super; ext2fs_swap_super(super_shadow); #endif retval = io_channel_flush(fs->io); retval = write_primary_superblock(fs, super_shadow); if (retval) goto errout; fs->flags &= ~EXT2_FLAG_DIRTY; retval = io_channel_flush(fs->io); errout: fs->super->s_state = fs_state; #ifdef WORDS_BIGENDIAN if (super_shadow) ext2fs_free_mem(&super_shadow); if (group_shadow) ext2fs_free_mem(&group_shadow); #endif return retval; } errcode_t ext2fs_close(ext2_filsys fs) { errcode_t retval; int meta_blks; io_stats stats = 0; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); if (fs->write_bitmaps) { retval = fs->write_bitmaps(fs); if (retval) return retval; } if (fs->super->s_kbytes_written && fs->io->manager->get_stats) fs->io->manager->get_stats(fs->io, &stats); if (stats && stats->bytes_written && (fs->flags & EXT2_FLAG_RW)) { fs->super->s_kbytes_written += stats->bytes_written >> 10; meta_blks = fs->desc_blocks + 1; if (!(fs->flags & EXT2_FLAG_SUPER_ONLY)) fs->super->s_kbytes_written += meta_blks / (fs->blocksize / 1024); if ((fs->flags & EXT2_FLAG_DIRTY) == 0) fs->flags |= EXT2_FLAG_SUPER_ONLY | EXT2_FLAG_DIRTY; } if (fs->flags & EXT2_FLAG_DIRTY) { retval = ext2fs_flush(fs); if (retval) return retval; } ext2fs_free(fs); return 0; } e2fsprogs-1.41.14/lib/ext2fs/tdb/0000755031104000366760000000000011240667355014375 5ustar tytsoe2fsprogs-1.41.14/lib/ext2fs/tdb/patches/0000755031104000366760000000000011240667355016024 5ustar tytsoe2fsprogs-1.41.14/lib/ext2fs/tdb/patches/static-prototypes0000644031104000366760000001071011240667355021463 0ustar tytsoIndex: tdbsa/tdb.c =================================================================== --- tdbsa.orig/tdb.c +++ tdbsa/tdb.c @@ -251,39 +251,39 @@ struct tdb_context { /* internal prototypes */ -int tdb_munmap(struct tdb_context *tdb); -void tdb_mmap(struct tdb_context *tdb); -int tdb_lock(struct tdb_context *tdb, int list, int ltype); -int tdb_unlock(struct tdb_context *tdb, int list, int ltype); -int tdb_brlock(struct tdb_context *tdb, tdb_off_t offset, int rw_type, int lck_type, int probe, size_t len); -int tdb_transaction_lock(struct tdb_context *tdb, int ltype); -int tdb_transaction_unlock(struct tdb_context *tdb); -int tdb_brlock_upgrade(struct tdb_context *tdb, tdb_off_t offset, size_t len); -int tdb_write_lock_record(struct tdb_context *tdb, tdb_off_t off); -int tdb_write_unlock_record(struct tdb_context *tdb, tdb_off_t off); -int tdb_ofs_read(struct tdb_context *tdb, tdb_off_t offset, tdb_off_t *d); -int tdb_ofs_write(struct tdb_context *tdb, tdb_off_t offset, tdb_off_t *d); -void *tdb_convert(void *buf, u32 size); -int tdb_free(struct tdb_context *tdb, tdb_off_t offset, struct list_struct *rec); -tdb_off_t tdb_allocate(struct tdb_context *tdb, tdb_len_t length, struct list_struct *rec); -int tdb_ofs_read(struct tdb_context *tdb, tdb_off_t offset, tdb_off_t *d); -int tdb_ofs_write(struct tdb_context *tdb, tdb_off_t offset, tdb_off_t *d); -int tdb_lock_record(struct tdb_context *tdb, tdb_off_t off); -int tdb_unlock_record(struct tdb_context *tdb, tdb_off_t off); -int tdb_rec_read(struct tdb_context *tdb, tdb_off_t offset, struct list_struct *rec); -int tdb_rec_write(struct tdb_context *tdb, tdb_off_t offset, struct list_struct *rec); -int tdb_do_delete(struct tdb_context *tdb, tdb_off_t rec_ptr, struct list_struct *rec); -unsigned char *tdb_alloc_read(struct tdb_context *tdb, tdb_off_t offset, tdb_len_t len); -int tdb_parse_data(struct tdb_context *tdb, TDB_DATA key, +static int tdb_munmap(struct tdb_context *tdb); +static void tdb_mmap(struct tdb_context *tdb); +static int tdb_lock(struct tdb_context *tdb, int list, int ltype); +static int tdb_unlock(struct tdb_context *tdb, int list, int ltype); +static int tdb_brlock(struct tdb_context *tdb, tdb_off_t offset, int rw_type, int lck_type, int probe, size_t len); +static int tdb_transaction_lock(struct tdb_context *tdb, int ltype); +static int tdb_transaction_unlock(struct tdb_context *tdb); +static int tdb_brlock_upgrade(struct tdb_context *tdb, tdb_off_t offset, size_t len); +static int tdb_write_lock_record(struct tdb_context *tdb, tdb_off_t off); +static int tdb_write_unlock_record(struct tdb_context *tdb, tdb_off_t off); +static int tdb_ofs_read(struct tdb_context *tdb, tdb_off_t offset, tdb_off_t *d); +static int tdb_ofs_write(struct tdb_context *tdb, tdb_off_t offset, tdb_off_t *d); +static void *tdb_convert(void *buf, u32 size); +static int tdb_free(struct tdb_context *tdb, tdb_off_t offset, struct list_struct *rec); +static tdb_off_t tdb_allocate(struct tdb_context *tdb, tdb_len_t length, struct list_struct *rec); +static int tdb_ofs_read(struct tdb_context *tdb, tdb_off_t offset, tdb_off_t *d); +static int tdb_ofs_write(struct tdb_context *tdb, tdb_off_t offset, tdb_off_t *d); +static int tdb_lock_record(struct tdb_context *tdb, tdb_off_t off); +static int tdb_unlock_record(struct tdb_context *tdb, tdb_off_t off); +static int tdb_rec_read(struct tdb_context *tdb, tdb_off_t offset, struct list_struct *rec); +static int tdb_rec_write(struct tdb_context *tdb, tdb_off_t offset, struct list_struct *rec); +static int tdb_do_delete(struct tdb_context *tdb, tdb_off_t rec_ptr, struct list_struct *rec); +static unsigned char *tdb_alloc_read(struct tdb_context *tdb, tdb_off_t offset, tdb_len_t len); +static int tdb_parse_data(struct tdb_context *tdb, TDB_DATA key, tdb_off_t offset, tdb_len_t len, int (*parser)(TDB_DATA key, TDB_DATA data, void *private_data), void *private_data); -tdb_off_t tdb_find_lock_hash(struct tdb_context *tdb, TDB_DATA key, u32 hash, int locktype, +static tdb_off_t tdb_find_lock_hash(struct tdb_context *tdb, TDB_DATA key, u32 hash, int locktype, struct list_struct *rec); -void tdb_io_init(struct tdb_context *tdb); -int tdb_expand(struct tdb_context *tdb, tdb_off_t size); -int tdb_rec_free_read(struct tdb_context *tdb, tdb_off_t off, +static void tdb_io_init(struct tdb_context *tdb); +static int tdb_expand(struct tdb_context *tdb, tdb_off_t size); +static int tdb_rec_free_read(struct tdb_context *tdb, tdb_off_t off, struct list_struct *rec); e2fsprogs-1.41.14/lib/ext2fs/tdb/patches/copyright0000644031104000366760000000131411240667355017756 0ustar tytsoIndex: tdbsa/tdb.c =================================================================== --- tdbsa.orig/tdb.c +++ tdbsa/tdb.c @@ -4,11 +4,11 @@ Rev: 23371 Last Changed Date: 2007-06-06 20:14:06 -0400 (Wed, 06 Jun 2007) */ /* - Unix SMB/CIFS implementation. + trivial database library - standalone version - trivial database library - private includes - - Copyright (C) Andrew Tridgell 2005 + Copyright (C) Andrew Tridgell 1999-2005 + Copyright (C) Jeremy Allison 2000-2006 + Copyright (C) Paul `Rusty' Russell 2000 ** NOTE! The following LGPL license applies to the tdb ** library. This does NOT imply that all of Samba is released e2fsprogs-1.41.14/lib/ext2fs/tdb/patches/replace-includes0000644031104000366760000000360011240667355021165 0ustar tytsoIndex: tdb/tdb.c =================================================================== --- tdb.orig/tdb.c +++ tdb/tdb.c @@ -29,11 +29,82 @@ Last Changed Date: 2007-06-22 13:36:10 - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "replace.h" -#include "system/filesys.h" -#include "system/time.h" -#include "system/shmem.h" -#include "system/select.h" +#ifdef CONFIG_STAND_ALONE +#define HAVE_MMAP +#define HAVE_STRDUP +#define HAVE_SYS_MMAN_H +#define HAVE_UTIME_H +#define HAVE_UTIME +#endif +#define _XOPEN_SOURCE 500 + +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_SYS_SELECT_H +#include +#endif +#include +#include +#include +#ifdef HAVE_UTIME_H +#include +#endif +#include +#include +#include + +#ifdef HAVE_SYS_MMAN_H +#include +#endif + +#ifndef MAP_FILE +#define MAP_FILE 0 +#endif + +#ifndef MAP_FAILED +#define MAP_FAILED ((void *)-1) +#endif + +#ifndef HAVE_STRDUP +#define strdup rep_strdup +static char *rep_strdup(const char *s) +{ + char *ret; + int length; + if (!s) + return NULL; + + if (!length) + length = strlen(s); + + ret = malloc(length + 1); + if (ret) { + strncpy(ret, s, length); + ret[length] = '\0'; + } + return ret; +} +#endif + +#ifndef PRINTF_ATTRIBUTE +#if (__GNUC__ >= 3) && (__GNUC_MINOR__ >= 1 ) +/** Use gcc attribute to check printf fns. a1 is the 1-based index of + * the parameter containing the format, and a2 the index of the first + * argument. Note that some gcc 2.x versions don't handle this + * properly **/ +#define PRINTF_ATTRIBUTE(a1, a2) __attribute__ ((format (__printf__, a1, a2))) +#else +#define PRINTF_ATTRIBUTE(a1, a2) +#endif +#endif + +typedef int bool; + #include "tdb.h" #ifndef u32 e2fsprogs-1.41.14/lib/ext2fs/tdb/patches/series0000644031104000366760000000013611240667355017241 0ustar tytsocopyright replace-includes static-prototypes static-functions tdbtool-includes ext2tdb-rename e2fsprogs-1.41.14/lib/ext2fs/tdb/patches/ext2tdb-rename0000644031104000366760000000543111240667355020573 0ustar tytsoIndex: tdb/tdb.h =================================================================== --- tdb.orig/tdb.h +++ tdb/tdb.h @@ -76,6 +76,60 @@ typedef struct TDB_DATA { #endif #endif +/* ext2fs tdb renames */ +#define tdb_open ext2fs_tdb_open +#define tdb_open_ex ext2fs_tdb_open_ex +#define tdb_set_max_dead ext2fs_tdb_set_max_dead +#define tdb_reopen ext2fs_tdb_reopen +#define tdb_reopen_all ext2fs_tdb_reopen_all +#define tdb_set_logging_function ext2fs_tdb_set_logging_function +#define tdb_error ext2fs_tdb_error +#define tdb_errorstr ext2fs_tdb_errorstr +#define tdb_fetch ext2fs_tdb_fetch +#define tdb_parse_record ext2fs_tdb_parse_record +#define tdb_delete ext2fs_tdb_delete +#define tdb_store ext2fs_tdb_store +#define tdb_append ext2fs_tdb_append +#define tdb_close ext2fs_tdb_close +#define tdb_firstkey ext2fs_tdb_firstkey +#define tdb_nextkey ext2fs_tdb_nextkey +#define tdb_traverse ext2fs_tdb_traverse +#define tdb_traverse_read ext2fs_tdb_traverse_read +#define tdb_exists ext2fs_tdb_exists +#define tdb_lockall ext2fs_tdb_lockall +#define tdb_unlockall ext2fs_tdb_unlockall +#define tdb_lockall_read ext2fs_tdb_lockall_read +#define tdb_unlockall_read ext2fs_tdb_unlockall_read +#define tdb_name ext2fs_tdb_name +#define tdb_fd ext2fs_tdb_fd +#define tdb_log_fn ext2fs_tdb_log_fn +#define tdb_get_logging_private ext2fs_tdb_get_logging_private +#define tdb_transaction_start ext2fs_tdb_transaction_start +#define tdb_transaction_commit ext2fs_tdb_transaction_commit +#define tdb_transaction_cancel ext2fs_tdb_transaction_cancel +#define tdb_transaction_recover ext2fs_tdb_transaction_recover +#define tdb_get_seqnum ext2fs_tdb_get_seqnum +#define tdb_hash_size ext2fs_tdb_hash_size +#define tdb_map_size ext2fs_tdb_map_size +#define tdb_get_flags ext2fs_tdb_get_flags +#define tdb_chainlock ext2fs_tdb_chainlock +#define tdb_chainunlock ext2fs_tdb_chainunlock +#define tdb_chainlock_read ext2fs_tdb_chainlock_read +#define tdb_chainunlock_read ext2fs_tdb_chainunlock_read +#define tdb_dump_all ext2fs_tdb_dump_all +#define tdb_printfreelist ext2fs_tdb_printfreelist +#define tdb_validate_freelist ext2fs_tdb_validate_freelist +#define tdb_chainlock_mark ext2fs_tdb_chainlock_mark +#define tdb_chainlock_nonblock ext2fs_tdb_chainlock_nonblock +#define tdb_chainlock_unmark ext2fs_tdb_chainlock_unmark +#define tdb_enable_seqnum ext2fs_tdb_enable_seqnum +#define tdb_increment_seqnum_nonblock ext2fs_tdb_increment_seqnum_nonblock +#define tdb_lock_nonblock ext2fs_tdb_lock_nonblock +#define tdb_lockall_mark ext2fs_tdb_lockall_mark +#define tdb_lockall_nonblock ext2fs_tdb_lockall_nonblock +#define tdb_lockall_read_nonblock ext2fs_tdb_lockall_read_nonblock +#define tdb_lockall_unmark ext2fs_tdb_lockall_unmark + /* this is the context structure that is returned from a db open */ typedef struct tdb_context TDB_CONTEXT; e2fsprogs-1.41.14/lib/ext2fs/tdb/patches/tdbtool-includes0000644031104000366760000000124211240667355021221 0ustar tytsoIndex: tdbsa/tdbtool.c =================================================================== --- tdbsa.orig/tdbtool.c +++ tdbsa/tdbtool.c @@ -21,10 +21,21 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include "replace.h" -#include "system/locale.h" -#include "system/time.h" -#include "system/filesys.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + #include "tdb.h" static int do_command(void); e2fsprogs-1.41.14/lib/ext2fs/tdb/patches/static-functions0000644031104000366760000000104111240667355021240 0ustar tytsoIndex: tdbsa/tdb.c =================================================================== --- tdbsa.orig/tdb.c +++ tdbsa/tdb.c @@ -2254,7 +2254,7 @@ int tdb_transaction_recover(struct tdb_c /* file: freelist.c */ /* read a freelist record and check for simple errors */ -int tdb_rec_free_read(struct tdb_context *tdb, tdb_off_t off, struct list_struct *rec) +static int tdb_rec_free_read(struct tdb_context *tdb, tdb_off_t off, struct list_struct *rec) { if (tdb->methods->tdb_read(tdb, off, rec, sizeof(*rec),DOCONV()) == -1) return -1; e2fsprogs-1.41.14/lib/ext2fs/tdb/build-tdb0000755031104000366760000000152111240667355016170 0ustar tytso#!/bin/sh # # This file creates a stand-alone TDB based on a set of sources from # Samba #BASE_DIR=/usr/projects/samba/samba-4.0.0tp4/source/lib/tdb BASE_DIR=/usr/projects/samba/tdb rm -rf .pc FILES="error.c lock.c io.c transaction.c freelist.c \ freelistcheck.c traverse.c dump.c tdb.c open.c" (cd $BASE_DIR/common; svn info ) > .svninfo echo "/*" > tdb.c grep ^URL .svninfo >> tdb.c grep "^Last Changed Rev" .svninfo | sed -e 's/Last Changed //' >> tdb.c grep "^Last Changed Date" .svninfo >> tdb.c echo "*/" >> tdb.c cat $BASE_DIR/common/tdb_private.h >> tdb.c for i in $FILES; do if [ `tail -n 1 tdb.c | wc -c` -gt 1 ]; then printf "\n" >> tdb.c fi echo "/* file: $i */" >> tdb.c sed -e '1,/#include "tdb_private.h"/d' < $BASE_DIR/common/$i >> tdb.c done cp $BASE_DIR/include/tdb.h . cp $BASE_DIR/tools/tdbtool.c . quilt push -a e2fsprogs-1.41.14/lib/ext2fs/block.c0000644031104000116100000003342411504417000015516 0ustar tytsoeng/* * block.c --- iterate over all blocks in an inode * * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #include "ext2_fs.h" #include "ext2fs.h" struct block_context { ext2_filsys fs; int (*func)(ext2_filsys fs, blk_t *blocknr, e2_blkcnt_t bcount, blk_t ref_blk, int ref_offset, void *priv_data); e2_blkcnt_t bcount; int bsize; int flags; errcode_t errcode; char *ind_buf; char *dind_buf; char *tind_buf; void *priv_data; }; #define check_for_ro_violation_return(ctx, ret) \ do { \ if (((ctx)->flags & BLOCK_FLAG_READ_ONLY) && \ ((ret) & BLOCK_CHANGED)) { \ (ctx)->errcode = EXT2_ET_RO_BLOCK_ITERATE; \ ret |= BLOCK_ABORT | BLOCK_ERROR; \ return ret; \ } \ } while (0) #define check_for_ro_violation_goto(ctx, ret, label) \ do { \ if (((ctx)->flags & BLOCK_FLAG_READ_ONLY) && \ ((ret) & BLOCK_CHANGED)) { \ (ctx)->errcode = EXT2_ET_RO_BLOCK_ITERATE; \ ret |= BLOCK_ABORT | BLOCK_ERROR; \ goto label; \ } \ } while (0) static int block_iterate_ind(blk_t *ind_block, blk_t ref_block, int ref_offset, struct block_context *ctx) { int ret = 0, changed = 0; int i, flags, limit, offset; blk_t *block_nr; limit = ctx->fs->blocksize >> 2; if (!(ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) && !(ctx->flags & BLOCK_FLAG_DATA_ONLY)) ret = (*ctx->func)(ctx->fs, ind_block, BLOCK_COUNT_IND, ref_block, ref_offset, ctx->priv_data); check_for_ro_violation_return(ctx, ret); if (!*ind_block || (ret & BLOCK_ABORT)) { ctx->bcount += limit; return ret; } if (*ind_block >= ctx->fs->super->s_blocks_count || *ind_block < ctx->fs->super->s_first_data_block) { ctx->errcode = EXT2_ET_BAD_IND_BLOCK; ret |= BLOCK_ERROR; return ret; } ctx->errcode = ext2fs_read_ind_block(ctx->fs, *ind_block, ctx->ind_buf); if (ctx->errcode) { ret |= BLOCK_ERROR; return ret; } block_nr = (blk_t *) ctx->ind_buf; offset = 0; if (ctx->flags & BLOCK_FLAG_APPEND) { for (i = 0; i < limit; i++, ctx->bcount++, block_nr++) { flags = (*ctx->func)(ctx->fs, block_nr, ctx->bcount, *ind_block, offset, ctx->priv_data); changed |= flags; if (flags & BLOCK_ABORT) { ret |= BLOCK_ABORT; break; } offset += sizeof(blk_t); } } else { for (i = 0; i < limit; i++, ctx->bcount++, block_nr++) { if (*block_nr == 0) goto skip_sparse; flags = (*ctx->func)(ctx->fs, block_nr, ctx->bcount, *ind_block, offset, ctx->priv_data); changed |= flags; if (flags & BLOCK_ABORT) { ret |= BLOCK_ABORT; break; } skip_sparse: offset += sizeof(blk_t); } } check_for_ro_violation_return(ctx, changed); if (changed & BLOCK_CHANGED) { ctx->errcode = ext2fs_write_ind_block(ctx->fs, *ind_block, ctx->ind_buf); if (ctx->errcode) ret |= BLOCK_ERROR | BLOCK_ABORT; } if ((ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) && !(ctx->flags & BLOCK_FLAG_DATA_ONLY) && !(ret & BLOCK_ABORT)) ret |= (*ctx->func)(ctx->fs, ind_block, BLOCK_COUNT_IND, ref_block, ref_offset, ctx->priv_data); check_for_ro_violation_return(ctx, ret); return ret; } static int block_iterate_dind(blk_t *dind_block, blk_t ref_block, int ref_offset, struct block_context *ctx) { int ret = 0, changed = 0; int i, flags, limit, offset; blk_t *block_nr; limit = ctx->fs->blocksize >> 2; if (!(ctx->flags & (BLOCK_FLAG_DEPTH_TRAVERSE | BLOCK_FLAG_DATA_ONLY))) ret = (*ctx->func)(ctx->fs, dind_block, BLOCK_COUNT_DIND, ref_block, ref_offset, ctx->priv_data); check_for_ro_violation_return(ctx, ret); if (!*dind_block || (ret & BLOCK_ABORT)) { ctx->bcount += limit*limit; return ret; } if (*dind_block >= ctx->fs->super->s_blocks_count || *dind_block < ctx->fs->super->s_first_data_block) { ctx->errcode = EXT2_ET_BAD_DIND_BLOCK; ret |= BLOCK_ERROR; return ret; } ctx->errcode = ext2fs_read_ind_block(ctx->fs, *dind_block, ctx->dind_buf); if (ctx->errcode) { ret |= BLOCK_ERROR; return ret; } block_nr = (blk_t *) ctx->dind_buf; offset = 0; if (ctx->flags & BLOCK_FLAG_APPEND) { for (i = 0; i < limit; i++, block_nr++) { flags = block_iterate_ind(block_nr, *dind_block, offset, ctx); changed |= flags; if (flags & (BLOCK_ABORT | BLOCK_ERROR)) { ret |= flags & (BLOCK_ABORT | BLOCK_ERROR); break; } offset += sizeof(blk_t); } } else { for (i = 0; i < limit; i++, block_nr++) { if (*block_nr == 0) { ctx->bcount += limit; continue; } flags = block_iterate_ind(block_nr, *dind_block, offset, ctx); changed |= flags; if (flags & (BLOCK_ABORT | BLOCK_ERROR)) { ret |= flags & (BLOCK_ABORT | BLOCK_ERROR); break; } offset += sizeof(blk_t); } } check_for_ro_violation_return(ctx, changed); if (changed & BLOCK_CHANGED) { ctx->errcode = ext2fs_write_ind_block(ctx->fs, *dind_block, ctx->dind_buf); if (ctx->errcode) ret |= BLOCK_ERROR | BLOCK_ABORT; } if ((ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) && !(ctx->flags & BLOCK_FLAG_DATA_ONLY) && !(ret & BLOCK_ABORT)) ret |= (*ctx->func)(ctx->fs, dind_block, BLOCK_COUNT_DIND, ref_block, ref_offset, ctx->priv_data); check_for_ro_violation_return(ctx, ret); return ret; } static int block_iterate_tind(blk_t *tind_block, blk_t ref_block, int ref_offset, struct block_context *ctx) { int ret = 0, changed = 0; int i, flags, limit, offset; blk_t *block_nr; limit = ctx->fs->blocksize >> 2; if (!(ctx->flags & (BLOCK_FLAG_DEPTH_TRAVERSE | BLOCK_FLAG_DATA_ONLY))) ret = (*ctx->func)(ctx->fs, tind_block, BLOCK_COUNT_TIND, ref_block, ref_offset, ctx->priv_data); check_for_ro_violation_return(ctx, ret); if (!*tind_block || (ret & BLOCK_ABORT)) { ctx->bcount += limit*limit*limit; return ret; } if (*tind_block >= ctx->fs->super->s_blocks_count || *tind_block < ctx->fs->super->s_first_data_block) { ctx->errcode = EXT2_ET_BAD_TIND_BLOCK; ret |= BLOCK_ERROR; return ret; } ctx->errcode = ext2fs_read_ind_block(ctx->fs, *tind_block, ctx->tind_buf); if (ctx->errcode) { ret |= BLOCK_ERROR; return ret; } block_nr = (blk_t *) ctx->tind_buf; offset = 0; if (ctx->flags & BLOCK_FLAG_APPEND) { for (i = 0; i < limit; i++, block_nr++) { flags = block_iterate_dind(block_nr, *tind_block, offset, ctx); changed |= flags; if (flags & (BLOCK_ABORT | BLOCK_ERROR)) { ret |= flags & (BLOCK_ABORT | BLOCK_ERROR); break; } offset += sizeof(blk_t); } } else { for (i = 0; i < limit; i++, block_nr++) { if (*block_nr == 0) { ctx->bcount += limit*limit; continue; } flags = block_iterate_dind(block_nr, *tind_block, offset, ctx); changed |= flags; if (flags & (BLOCK_ABORT | BLOCK_ERROR)) { ret |= flags & (BLOCK_ABORT | BLOCK_ERROR); break; } offset += sizeof(blk_t); } } check_for_ro_violation_return(ctx, changed); if (changed & BLOCK_CHANGED) { ctx->errcode = ext2fs_write_ind_block(ctx->fs, *tind_block, ctx->tind_buf); if (ctx->errcode) ret |= BLOCK_ERROR | BLOCK_ABORT; } if ((ctx->flags & BLOCK_FLAG_DEPTH_TRAVERSE) && !(ctx->flags & BLOCK_FLAG_DATA_ONLY) && !(ret & BLOCK_ABORT)) ret |= (*ctx->func)(ctx->fs, tind_block, BLOCK_COUNT_TIND, ref_block, ref_offset, ctx->priv_data); check_for_ro_violation_return(ctx, ret); return ret; } errcode_t ext2fs_block_iterate2(ext2_filsys fs, ext2_ino_t ino, int flags, char *block_buf, int (*func)(ext2_filsys fs, blk_t *blocknr, e2_blkcnt_t blockcnt, blk_t ref_blk, int ref_offset, void *priv_data), void *priv_data) { int i; int r, ret = 0; struct ext2_inode inode; errcode_t retval; struct block_context ctx; int limit; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); ctx.errcode = ext2fs_read_inode(fs, ino, &inode); if (ctx.errcode) return ctx.errcode; /* * Check to see if we need to limit large files */ if (flags & BLOCK_FLAG_NO_LARGE) { if (!LINUX_S_ISDIR(inode.i_mode) && (inode.i_size_high != 0)) return EXT2_ET_FILE_TOO_BIG; } limit = fs->blocksize >> 2; ctx.fs = fs; ctx.func = func; ctx.priv_data = priv_data; ctx.flags = flags; ctx.bcount = 0; if (block_buf) { ctx.ind_buf = block_buf; } else { retval = ext2fs_get_array(3, fs->blocksize, &ctx.ind_buf); if (retval) return retval; } ctx.dind_buf = ctx.ind_buf + fs->blocksize; ctx.tind_buf = ctx.dind_buf + fs->blocksize; /* * Iterate over the HURD translator block (if present) */ if ((fs->super->s_creator_os == EXT2_OS_HURD) && !(flags & BLOCK_FLAG_DATA_ONLY)) { if (inode.osd1.hurd1.h_i_translator) { ret |= (*ctx.func)(fs, &inode.osd1.hurd1.h_i_translator, BLOCK_COUNT_TRANSLATOR, 0, 0, priv_data); if (ret & BLOCK_ABORT) goto abort_exit; check_for_ro_violation_goto(&ctx, ret, abort_exit); } } if (inode.i_flags & EXT4_EXTENTS_FL) { ext2_extent_handle_t handle; struct ext2fs_extent extent; e2_blkcnt_t blockcnt = 0; blk_t blk, new_blk; int op = EXT2_EXTENT_ROOT; int uninit; unsigned int j; ctx.errcode = ext2fs_extent_open2(fs, ino, &inode, &handle); if (ctx.errcode) goto abort_exit; while (1) { ctx.errcode = ext2fs_extent_get(handle, op, &extent); if (ctx.errcode) { if (ctx.errcode != EXT2_ET_EXTENT_NO_NEXT) break; ctx.errcode = 0; if (!(flags & BLOCK_FLAG_APPEND)) break; next_block_set: blk = 0; r = (*ctx.func)(fs, &blk, blockcnt, 0, 0, priv_data); ret |= r; check_for_ro_violation_goto(&ctx, ret, extent_errout); if (r & BLOCK_CHANGED) { ctx.errcode = ext2fs_extent_set_bmap(handle, (blk64_t) blockcnt++, (blk64_t) blk, 0); if (ctx.errcode || (ret & BLOCK_ABORT)) break; if (blk) goto next_block_set; } break; } op = EXT2_EXTENT_NEXT; blk = extent.e_pblk; if (!(extent.e_flags & EXT2_EXTENT_FLAGS_LEAF)) { if (ctx.flags & BLOCK_FLAG_DATA_ONLY) continue; if ((!(extent.e_flags & EXT2_EXTENT_FLAGS_SECOND_VISIT) && !(ctx.flags & BLOCK_FLAG_DEPTH_TRAVERSE)) || ((extent.e_flags & EXT2_EXTENT_FLAGS_SECOND_VISIT) && (ctx.flags & BLOCK_FLAG_DEPTH_TRAVERSE))) { ret |= (*ctx.func)(fs, &blk, -1, 0, 0, priv_data); if (ret & BLOCK_CHANGED) { extent.e_pblk = blk; ctx.errcode = ext2fs_extent_replace(handle, 0, &extent); if (ctx.errcode) break; } } continue; } uninit = 0; if (extent.e_flags & EXT2_EXTENT_FLAGS_UNINIT) uninit = EXT2_EXTENT_SET_BMAP_UNINIT; for (blockcnt = extent.e_lblk, j = 0; j < extent.e_len; blk++, blockcnt++, j++) { new_blk = blk; r = (*ctx.func)(fs, &new_blk, blockcnt, 0, 0, priv_data); ret |= r; check_for_ro_violation_goto(&ctx, ret, extent_errout); if (r & BLOCK_CHANGED) { ctx.errcode = ext2fs_extent_set_bmap(handle, (blk64_t) blockcnt, (blk64_t) new_blk, uninit); if (ctx.errcode) goto extent_errout; } if (ret & BLOCK_ABORT) break; } } extent_errout: ext2fs_extent_free(handle); ret |= BLOCK_ERROR | BLOCK_ABORT; goto errout; } /* * Iterate over normal data blocks */ for (i = 0; i < EXT2_NDIR_BLOCKS ; i++, ctx.bcount++) { if (inode.i_block[i] || (flags & BLOCK_FLAG_APPEND)) { ret |= (*ctx.func)(fs, &inode.i_block[i], ctx.bcount, 0, i, priv_data); if (ret & BLOCK_ABORT) goto abort_exit; } } check_for_ro_violation_goto(&ctx, ret, abort_exit); if (inode.i_block[EXT2_IND_BLOCK] || (flags & BLOCK_FLAG_APPEND)) { ret |= block_iterate_ind(&inode.i_block[EXT2_IND_BLOCK], 0, EXT2_IND_BLOCK, &ctx); if (ret & BLOCK_ABORT) goto abort_exit; } else ctx.bcount += limit; if (inode.i_block[EXT2_DIND_BLOCK] || (flags & BLOCK_FLAG_APPEND)) { ret |= block_iterate_dind(&inode.i_block[EXT2_DIND_BLOCK], 0, EXT2_DIND_BLOCK, &ctx); if (ret & BLOCK_ABORT) goto abort_exit; } else ctx.bcount += limit * limit; if (inode.i_block[EXT2_TIND_BLOCK] || (flags & BLOCK_FLAG_APPEND)) { ret |= block_iterate_tind(&inode.i_block[EXT2_TIND_BLOCK], 0, EXT2_TIND_BLOCK, &ctx); if (ret & BLOCK_ABORT) goto abort_exit; } abort_exit: if (ret & BLOCK_CHANGED) { retval = ext2fs_write_inode(fs, ino, &inode); if (retval) { ret |= BLOCK_ERROR; ctx.errcode = retval; } } errout: if (!block_buf) ext2fs_free_mem(&ctx.ind_buf); return (ret & BLOCK_ERROR) ? ctx.errcode : 0; } /* * Emulate the old ext2fs_block_iterate function! */ struct xlate { int (*func)(ext2_filsys fs, blk_t *blocknr, int bcount, void *priv_data); void *real_private; }; #ifdef __TURBOC__ #pragma argsused #endif static int xlate_func(ext2_filsys fs, blk_t *blocknr, e2_blkcnt_t blockcnt, blk_t ref_block EXT2FS_ATTR((unused)), int ref_offset EXT2FS_ATTR((unused)), void *priv_data) { struct xlate *xl = (struct xlate *) priv_data; return (*xl->func)(fs, blocknr, (int) blockcnt, xl->real_private); } errcode_t ext2fs_block_iterate(ext2_filsys fs, ext2_ino_t ino, int flags, char *block_buf, int (*func)(ext2_filsys fs, blk_t *blocknr, int blockcnt, void *priv_data), void *priv_data) { struct xlate xl; xl.real_private = priv_data; xl.func = func; return ext2fs_block_iterate2(fs, ino, BLOCK_FLAG_NO_LARGE | flags, block_buf, xlate_func, &xl); } e2fsprogs-1.41.14/lib/ext2fs/jfs_dat.h0000644031104000366760000000300211374366235015403 0ustar tytso/* * jfs_dat.h --- stripped down header file which only contains the JFS * on-disk data structures */ #define JFS_MAGIC_NUMBER 0xc03b3998U /* The first 4 bytes of /dev/random! */ /* * On-disk structures */ /* * Descriptor block types: */ #define JFS_DESCRIPTOR_BLOCK 1 #define JFS_COMMIT_BLOCK 2 #define JFS_SUPERBLOCK 3 /* * Standard header for all descriptor blocks: */ typedef struct journal_header_s { __u32 h_magic; __u32 h_blocktype; __u32 h_sequence; } journal_header_t; /* * The block tag: used to describe a single buffer in the journal */ typedef struct journal_block_tag_s { __u32 t_blocknr; /* The on-disk block number */ __u32 t_flags; /* See below */ } journal_block_tag_t; /* Definitions for the journal tag flags word: */ #define JFS_FLAG_ESCAPE 1 /* on-disk block is escaped */ #define JFS_FLAG_SAME_UUID 2 /* block has same uuid as previous */ #define JFS_FLAG_DELETED 4 /* block deleted by this transaction */ #define JFS_FLAG_LAST_TAG 8 /* last tag in this descriptor block */ /* * The journal superblock */ typedef struct journal_superblock_s { journal_header_t s_header; /* Static information describing the journal */ __u32 s_blocksize; /* journal device blocksize */ __u32 s_maxlen; /* total blocks in journal file */ __u32 s_first; /* first block of log information */ /* Dynamic information describing the current state of the log */ __u32 s_sequence; /* first commit ID expected in log */ __u32 s_start; /* blocknr of start of log */ } journal_superblock_t; e2fsprogs-1.41.14/lib/ext2fs/irel.h0000644031104000366760000000644711405316370014732 0ustar tytso/* * irel.h * * Copyright (C) 1996, 1997 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ struct ext2_inode_reference { blk_t block; __u16 offset; }; struct ext2_inode_relocate_entry { ext2_ino_t new; ext2_ino_t orig; __u16 flags; __u16 max_refs; }; typedef struct ext2_inode_relocation_table *ext2_irel; struct ext2_inode_relocation_table { __u32 magic; char *name; ext2_ino_t current; void *priv_data; /* * Add an inode relocation entry. */ errcode_t (*put)(ext2_irel irel, ext2_ino_t old, struct ext2_inode_relocate_entry *ent); /* * Get an inode relocation entry. */ errcode_t (*get)(ext2_irel irel, ext2_ino_t old, struct ext2_inode_relocate_entry *ent); /* * Get an inode relocation entry by its original inode number */ errcode_t (*get_by_orig)(ext2_irel irel, ext2_ino_t orig, ext2_ino_t *old, struct ext2_inode_relocate_entry *ent); /* * Initialize for iterating over the inode relocation entries. */ errcode_t (*start_iter)(ext2_irel irel); /* * The iterator function for the inode relocation entries. * Returns an inode number of 0 when out of entries. */ errcode_t (*next)(ext2_irel irel, ext2_ino_t *old, struct ext2_inode_relocate_entry *ent); /* * Add an inode reference (i.e., note the fact that a * particular block/offset contains a reference to an inode) */ errcode_t (*add_ref)(ext2_irel irel, ext2_ino_t ino, struct ext2_inode_reference *ref); /* * Initialize for iterating over the inode references for a * particular inode. */ errcode_t (*start_iter_ref)(ext2_irel irel, ext2_ino_t ino); /* * The iterator function for the inode references for an * inode. The references for only one inode can be interator * over at a time, as the iterator state is stored in ext2_irel. */ errcode_t (*next_ref)(ext2_irel irel, struct ext2_inode_reference *ref); /* * Move the inode relocation table from one inode number to * another. Note that the inode references also must move. */ errcode_t (*move)(ext2_irel irel, ext2_ino_t old, ext2_ino_t new); /* * Remove an inode relocation entry, along with all of the * inode references. */ errcode_t (*delete)(ext2_irel irel, ext2_ino_t old); /* * Free the inode relocation table. */ errcode_t (*free)(ext2_irel irel); }; errcode_t ext2fs_irel_memarray_create(char *name, ext2_ino_t max_inode, ext2_irel *irel); #define ext2fs_irel_put(irel, old, ent) ((irel)->put((irel), old, ent)) #define ext2fs_irel_get(irel, old, ent) ((irel)->get((irel), old, ent)) #define ext2fs_irel_get_by_orig(irel, orig, old, ent) \ ((irel)->get_by_orig((irel), orig, old, ent)) #define ext2fs_irel_start_iter(irel) ((irel)->start_iter((irel))) #define ext2fs_irel_next(irel, old, ent) ((irel)->next((irel), old, ent)) #define ext2fs_irel_add_ref(irel, ino, ref) ((irel)->add_ref((irel), ino, ref)) #define ext2fs_irel_start_iter_ref(irel, ino) ((irel)->start_iter_ref((irel), ino)) #define ext2fs_irel_next_ref(irel, ref) ((irel)->next_ref((irel), ref)) #define ext2fs_irel_move(irel, old, new) ((irel)->move((irel), old, new)) #define ext2fs_irel_delete(irel, old) ((irel)->delete((irel), old)) #define ext2fs_irel_free(irel) ((irel)->free((irel))) e2fsprogs-1.41.14/lib/ext2fs/swapfs.c0000644031104000116100000002444211504417000015727 0ustar tytsoeng/* * swapfs.c --- swap ext2 filesystem data structures * * Copyright (C) 1995, 1996, 2002 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #include #if HAVE_UNISTD_H #include #endif #include #include #include "ext2_fs.h" #include "ext2fs.h" #include #ifdef WORDS_BIGENDIAN void ext2fs_swap_super(struct ext2_super_block * sb) { int i; sb->s_inodes_count = ext2fs_swab32(sb->s_inodes_count); sb->s_blocks_count = ext2fs_swab32(sb->s_blocks_count); sb->s_r_blocks_count = ext2fs_swab32(sb->s_r_blocks_count); sb->s_free_blocks_count = ext2fs_swab32(sb->s_free_blocks_count); sb->s_free_inodes_count = ext2fs_swab32(sb->s_free_inodes_count); sb->s_first_data_block = ext2fs_swab32(sb->s_first_data_block); sb->s_log_block_size = ext2fs_swab32(sb->s_log_block_size); sb->s_log_frag_size = ext2fs_swab32(sb->s_log_frag_size); sb->s_blocks_per_group = ext2fs_swab32(sb->s_blocks_per_group); sb->s_frags_per_group = ext2fs_swab32(sb->s_frags_per_group); sb->s_inodes_per_group = ext2fs_swab32(sb->s_inodes_per_group); sb->s_mtime = ext2fs_swab32(sb->s_mtime); sb->s_wtime = ext2fs_swab32(sb->s_wtime); sb->s_mnt_count = ext2fs_swab16(sb->s_mnt_count); sb->s_max_mnt_count = ext2fs_swab16(sb->s_max_mnt_count); sb->s_magic = ext2fs_swab16(sb->s_magic); sb->s_state = ext2fs_swab16(sb->s_state); sb->s_errors = ext2fs_swab16(sb->s_errors); sb->s_minor_rev_level = ext2fs_swab16(sb->s_minor_rev_level); sb->s_lastcheck = ext2fs_swab32(sb->s_lastcheck); sb->s_checkinterval = ext2fs_swab32(sb->s_checkinterval); sb->s_creator_os = ext2fs_swab32(sb->s_creator_os); sb->s_rev_level = ext2fs_swab32(sb->s_rev_level); sb->s_def_resuid = ext2fs_swab16(sb->s_def_resuid); sb->s_def_resgid = ext2fs_swab16(sb->s_def_resgid); sb->s_first_ino = ext2fs_swab32(sb->s_first_ino); sb->s_inode_size = ext2fs_swab16(sb->s_inode_size); sb->s_block_group_nr = ext2fs_swab16(sb->s_block_group_nr); sb->s_feature_compat = ext2fs_swab32(sb->s_feature_compat); sb->s_feature_incompat = ext2fs_swab32(sb->s_feature_incompat); sb->s_feature_ro_compat = ext2fs_swab32(sb->s_feature_ro_compat); sb->s_algorithm_usage_bitmap = ext2fs_swab32(sb->s_algorithm_usage_bitmap); sb->s_reserved_gdt_blocks = ext2fs_swab16(sb->s_reserved_gdt_blocks); sb->s_journal_inum = ext2fs_swab32(sb->s_journal_inum); sb->s_journal_dev = ext2fs_swab32(sb->s_journal_dev); sb->s_last_orphan = ext2fs_swab32(sb->s_last_orphan); sb->s_desc_size = ext2fs_swab16(sb->s_desc_size); sb->s_default_mount_opts = ext2fs_swab32(sb->s_default_mount_opts); sb->s_first_meta_bg = ext2fs_swab32(sb->s_first_meta_bg); sb->s_mkfs_time = ext2fs_swab32(sb->s_mkfs_time); sb->s_blocks_count_hi = ext2fs_swab32(sb->s_blocks_count_hi); sb->s_r_blocks_count_hi = ext2fs_swab32(sb->s_r_blocks_count_hi); sb->s_free_blocks_hi = ext2fs_swab32(sb->s_free_blocks_hi); sb->s_min_extra_isize = ext2fs_swab16(sb->s_min_extra_isize); sb->s_want_extra_isize = ext2fs_swab16(sb->s_want_extra_isize); sb->s_flags = ext2fs_swab32(sb->s_flags); sb->s_kbytes_written = ext2fs_swab64(sb->s_kbytes_written); sb->s_snapshot_inum = ext2fs_swab32(sb->s_snapshot_inum); sb->s_snapshot_id = ext2fs_swab32(sb->s_snapshot_id); sb->s_snapshot_r_blocks_count = ext2fs_swab64(sb->s_snapshot_r_blocks_count); sb->s_snapshot_list = ext2fs_swab32(sb->s_snapshot_list); for (i=0; i < 4; i++) sb->s_hash_seed[i] = ext2fs_swab32(sb->s_hash_seed[i]); /* if journal backup is for a valid extent-based journal... */ if (!ext2fs_extent_header_verify(sb->s_jnl_blocks, sizeof(sb->s_jnl_blocks))) { /* ... swap only the journal i_size */ sb->s_jnl_blocks[16] = ext2fs_swab32(sb->s_jnl_blocks[16]); /* and the extent data is not swapped on read */ return; } /* direct/indirect journal: swap it all */ for (i=0; i < 17; i++) sb->s_jnl_blocks[i] = ext2fs_swab32(sb->s_jnl_blocks[i]); } void ext2fs_swap_group_desc(struct ext2_group_desc *gdp) { gdp->bg_block_bitmap = ext2fs_swab32(gdp->bg_block_bitmap); gdp->bg_inode_bitmap = ext2fs_swab32(gdp->bg_inode_bitmap); gdp->bg_inode_table = ext2fs_swab32(gdp->bg_inode_table); gdp->bg_free_blocks_count = ext2fs_swab16(gdp->bg_free_blocks_count); gdp->bg_free_inodes_count = ext2fs_swab16(gdp->bg_free_inodes_count); gdp->bg_used_dirs_count = ext2fs_swab16(gdp->bg_used_dirs_count); gdp->bg_flags = ext2fs_swab16(gdp->bg_flags); gdp->bg_itable_unused = ext2fs_swab16(gdp->bg_itable_unused); gdp->bg_checksum = ext2fs_swab16(gdp->bg_checksum); } void ext2fs_swap_ext_attr_header(struct ext2_ext_attr_header *to_header, struct ext2_ext_attr_header *from_header) { int n; to_header->h_magic = ext2fs_swab32(from_header->h_magic); to_header->h_blocks = ext2fs_swab32(from_header->h_blocks); to_header->h_refcount = ext2fs_swab32(from_header->h_refcount); to_header->h_hash = ext2fs_swab32(from_header->h_hash); for (n = 0; n < 4; n++) to_header->h_reserved[n] = ext2fs_swab32(from_header->h_reserved[n]); } void ext2fs_swap_ext_attr_entry(struct ext2_ext_attr_entry *to_entry, struct ext2_ext_attr_entry *from_entry) { to_entry->e_value_offs = ext2fs_swab16(from_entry->e_value_offs); to_entry->e_value_block = ext2fs_swab32(from_entry->e_value_block); to_entry->e_value_size = ext2fs_swab32(from_entry->e_value_size); to_entry->e_hash = ext2fs_swab32(from_entry->e_hash); } void ext2fs_swap_ext_attr(char *to, char *from, int bufsize, int has_header) { struct ext2_ext_attr_header *from_header = (struct ext2_ext_attr_header *)from; struct ext2_ext_attr_header *to_header = (struct ext2_ext_attr_header *)to; struct ext2_ext_attr_entry *from_entry, *to_entry; char *from_end = (char *)from_header + bufsize; if (to_header != from_header) memcpy(to_header, from_header, bufsize); if (has_header) { ext2fs_swap_ext_attr_header(to_header, from_header); from_entry = (struct ext2_ext_attr_entry *)(from_header+1); to_entry = (struct ext2_ext_attr_entry *)(to_header+1); } else { from_entry = (struct ext2_ext_attr_entry *)from_header; to_entry = (struct ext2_ext_attr_entry *)to_header; } while ((char *)from_entry < from_end && *(__u32 *)from_entry) { ext2fs_swap_ext_attr_entry(to_entry, from_entry); from_entry = EXT2_EXT_ATTR_NEXT(from_entry); to_entry = EXT2_EXT_ATTR_NEXT(to_entry); } } void ext2fs_swap_inode_full(ext2_filsys fs, struct ext2_inode_large *t, struct ext2_inode_large *f, int hostorder, int bufsize) { unsigned i, has_data_blocks, extra_isize, attr_magic; int has_extents = 0; int islnk = 0; __u32 *eaf, *eat; if (hostorder && LINUX_S_ISLNK(f->i_mode)) islnk = 1; t->i_mode = ext2fs_swab16(f->i_mode); if (!hostorder && LINUX_S_ISLNK(t->i_mode)) islnk = 1; t->i_uid = ext2fs_swab16(f->i_uid); t->i_size = ext2fs_swab32(f->i_size); t->i_atime = ext2fs_swab32(f->i_atime); t->i_ctime = ext2fs_swab32(f->i_ctime); t->i_mtime = ext2fs_swab32(f->i_mtime); t->i_dtime = ext2fs_swab32(f->i_dtime); t->i_gid = ext2fs_swab16(f->i_gid); t->i_links_count = ext2fs_swab16(f->i_links_count); t->i_file_acl = ext2fs_swab32(f->i_file_acl); if (hostorder) has_data_blocks = ext2fs_inode_data_blocks(fs, (struct ext2_inode *) f); t->i_blocks = ext2fs_swab32(f->i_blocks); if (!hostorder) has_data_blocks = ext2fs_inode_data_blocks(fs, (struct ext2_inode *) t); if (hostorder && (f->i_flags & EXT4_EXTENTS_FL)) has_extents = 1; t->i_flags = ext2fs_swab32(f->i_flags); if (!hostorder && (t->i_flags & EXT4_EXTENTS_FL)) has_extents = 1; t->i_dir_acl = ext2fs_swab32(f->i_dir_acl); /* extent data are swapped on access, not here */ if (!has_extents && (!islnk || has_data_blocks)) { for (i = 0; i < EXT2_N_BLOCKS; i++) t->i_block[i] = ext2fs_swab32(f->i_block[i]); } else if (t != f) { for (i = 0; i < EXT2_N_BLOCKS; i++) t->i_block[i] = f->i_block[i]; } t->i_generation = ext2fs_swab32(f->i_generation); t->i_faddr = ext2fs_swab32(f->i_faddr); switch (fs->super->s_creator_os) { case EXT2_OS_LINUX: t->osd1.linux1.l_i_version = ext2fs_swab32(f->osd1.linux1.l_i_version); t->osd2.linux2.l_i_blocks_hi = ext2fs_swab16(f->osd2.linux2.l_i_blocks_hi); t->osd2.linux2.l_i_file_acl_high = ext2fs_swab16(f->osd2.linux2.l_i_file_acl_high); t->osd2.linux2.l_i_uid_high = ext2fs_swab16 (f->osd2.linux2.l_i_uid_high); t->osd2.linux2.l_i_gid_high = ext2fs_swab16 (f->osd2.linux2.l_i_gid_high); t->osd2.linux2.l_i_reserved2 = ext2fs_swab32(f->osd2.linux2.l_i_reserved2); break; case EXT2_OS_HURD: t->osd1.hurd1.h_i_translator = ext2fs_swab32 (f->osd1.hurd1.h_i_translator); t->osd2.hurd2.h_i_frag = f->osd2.hurd2.h_i_frag; t->osd2.hurd2.h_i_fsize = f->osd2.hurd2.h_i_fsize; t->osd2.hurd2.h_i_mode_high = ext2fs_swab16 (f->osd2.hurd2.h_i_mode_high); t->osd2.hurd2.h_i_uid_high = ext2fs_swab16 (f->osd2.hurd2.h_i_uid_high); t->osd2.hurd2.h_i_gid_high = ext2fs_swab16 (f->osd2.hurd2.h_i_gid_high); t->osd2.hurd2.h_i_author = ext2fs_swab32 (f->osd2.hurd2.h_i_author); break; default: break; } if (bufsize < (int) (sizeof(struct ext2_inode) + sizeof(__u16))) return; /* no i_extra_isize field */ if (hostorder) extra_isize = f->i_extra_isize; t->i_extra_isize = ext2fs_swab16(f->i_extra_isize); if (!hostorder) extra_isize = t->i_extra_isize; if (extra_isize > EXT2_INODE_SIZE(fs->super) - sizeof(struct ext2_inode)) { /* this is error case: i_extra_size is too large */ return; } i = sizeof(struct ext2_inode) + extra_isize + sizeof(__u32); if (bufsize < (int) i) return; /* no space for EA magic */ eaf = (__u32 *) (((char *) f) + sizeof(struct ext2_inode) + extra_isize); attr_magic = *eaf; if (!hostorder) attr_magic = ext2fs_swab32(attr_magic); if (attr_magic != EXT2_EXT_ATTR_MAGIC) return; /* it seems no magic here */ eat = (__u32 *) (((char *) t) + sizeof(struct ext2_inode) + extra_isize); *eat = ext2fs_swab32(*eaf); /* convert EA(s) */ ext2fs_swap_ext_attr((char *) (eat + 1), (char *) (eaf + 1), bufsize - sizeof(struct ext2_inode) - extra_isize - sizeof(__u32), 0); } void ext2fs_swap_inode(ext2_filsys fs, struct ext2_inode *t, struct ext2_inode *f, int hostorder) { ext2fs_swap_inode_full(fs, (struct ext2_inode_large *) t, (struct ext2_inode_large *) f, hostorder, sizeof(struct ext2_inode)); } #endif e2fsprogs-1.41.14/lib/ext2fs/alloc_tables.c0000644031104000116100000001452111504417000017045 0ustar tytsoeng/* * alloc_tables.c --- Allocate tables for a newly initialized * filesystem. Used by mke2fs when initializing a filesystem * * Copyright (C) 1996 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #include #include #if HAVE_SYS_STAT_H #include #endif #if HAVE_SYS_TYPES_H #include #endif #include "ext2_fs.h" #include "ext2fs.h" /* * This routine searches for free blocks that can allocate a full * group of bitmaps or inode tables for a flexbg group. Returns the * block number with a correct offset were the bitmaps and inode * tables can be allocated continously and in order. */ static blk_t flexbg_offset(ext2_filsys fs, dgrp_t group, blk_t start_blk, ext2fs_block_bitmap bmap, int offset, int size, int elem_size) { int flexbg, flexbg_size; blk_t last_blk, first_free = 0; dgrp_t last_grp; flexbg_size = 1 << fs->super->s_log_groups_per_flex; flexbg = group / flexbg_size; if (size > (int) (fs->super->s_blocks_per_group / 8)) size = (int) fs->super->s_blocks_per_group / 8; if (offset) offset -= 1; /* * Don't do a long search if the previous block * search is still valid. */ if (start_blk && group % flexbg_size) { if (ext2fs_test_block_bitmap_range(bmap, start_blk + elem_size, size)) return start_blk + elem_size; } start_blk = ext2fs_group_first_block(fs, flexbg_size * flexbg); last_grp = group | (flexbg_size - 1); if (last_grp > fs->group_desc_count) last_grp = fs->group_desc_count; last_blk = ext2fs_group_last_block(fs, last_grp); /* Find the first available block */ if (ext2fs_get_free_blocks(fs, start_blk, last_blk, 1, bmap, &first_free)) return first_free; if (ext2fs_get_free_blocks(fs, first_free + offset, last_blk, size, bmap, &first_free)) return first_free; return first_free; } errcode_t ext2fs_allocate_group_table(ext2_filsys fs, dgrp_t group, ext2fs_block_bitmap bmap) { errcode_t retval; blk_t group_blk, start_blk, last_blk, new_blk, blk; dgrp_t last_grp = 0; int j, rem_grps = 0, flexbg_size = 0; group_blk = ext2fs_group_first_block(fs, group); last_blk = ext2fs_group_last_block(fs, group); if (!bmap) bmap = fs->block_map; if (EXT2_HAS_INCOMPAT_FEATURE(fs->super, EXT4_FEATURE_INCOMPAT_FLEX_BG) && fs->super->s_log_groups_per_flex) { flexbg_size = 1 << fs->super->s_log_groups_per_flex; last_grp = group | (flexbg_size - 1); rem_grps = last_grp - group; if (last_grp > fs->group_desc_count) last_grp = fs->group_desc_count; } /* * Allocate the block and inode bitmaps, if necessary */ if (fs->stride) { retval = ext2fs_get_free_blocks(fs, group_blk, last_blk, 1, bmap, &start_blk); if (retval) return retval; start_blk += fs->inode_blocks_per_group; start_blk += ((fs->stride * group) % (last_blk - start_blk + 1)); if (start_blk >= last_blk) start_blk = group_blk; } else start_blk = group_blk; if (flexbg_size) { blk_t prev_block = 0; if (group && fs->group_desc[group-1].bg_block_bitmap) prev_block = fs->group_desc[group-1].bg_block_bitmap; start_blk = flexbg_offset(fs, group, prev_block, bmap, 0, rem_grps, 1); last_blk = ext2fs_group_last_block(fs, last_grp); } if (!fs->group_desc[group].bg_block_bitmap) { retval = ext2fs_get_free_blocks(fs, start_blk, last_blk, 1, bmap, &new_blk); if (retval == EXT2_ET_BLOCK_ALLOC_FAIL) retval = ext2fs_get_free_blocks(fs, group_blk, last_blk, 1, bmap, &new_blk); if (retval) return retval; ext2fs_mark_block_bitmap(bmap, new_blk); fs->group_desc[group].bg_block_bitmap = new_blk; if (flexbg_size) { dgrp_t gr = ext2fs_group_of_blk(fs, new_blk); fs->group_desc[gr].bg_free_blocks_count--; fs->super->s_free_blocks_count--; fs->group_desc[gr].bg_flags &= ~EXT2_BG_BLOCK_UNINIT; ext2fs_group_desc_csum_set(fs, gr); } } if (flexbg_size) { blk_t prev_block = 0; if (group && fs->group_desc[group-1].bg_inode_bitmap) prev_block = fs->group_desc[group-1].bg_inode_bitmap; start_blk = flexbg_offset(fs, group, prev_block, bmap, flexbg_size, rem_grps, 1); last_blk = ext2fs_group_last_block(fs, last_grp); } if (!fs->group_desc[group].bg_inode_bitmap) { retval = ext2fs_get_free_blocks(fs, start_blk, last_blk, 1, bmap, &new_blk); if (retval == EXT2_ET_BLOCK_ALLOC_FAIL) retval = ext2fs_get_free_blocks(fs, group_blk, last_blk, 1, bmap, &new_blk); if (retval) return retval; ext2fs_mark_block_bitmap(bmap, new_blk); fs->group_desc[group].bg_inode_bitmap = new_blk; if (flexbg_size) { dgrp_t gr = ext2fs_group_of_blk(fs, new_blk); fs->group_desc[gr].bg_free_blocks_count--; fs->super->s_free_blocks_count--; fs->group_desc[gr].bg_flags &= ~EXT2_BG_BLOCK_UNINIT; ext2fs_group_desc_csum_set(fs, gr); } } /* * Allocate the inode table */ if (flexbg_size) { blk_t prev_block = 0; if (group && fs->group_desc[group-1].bg_inode_table) prev_block = fs->group_desc[group-1].bg_inode_table; if (last_grp == fs->group_desc_count) rem_grps = last_grp - group; group_blk = flexbg_offset(fs, group, prev_block, bmap, flexbg_size * 2, fs->inode_blocks_per_group * rem_grps, fs->inode_blocks_per_group); last_blk = ext2fs_group_last_block(fs, last_grp); } if (!fs->group_desc[group].bg_inode_table) { retval = ext2fs_get_free_blocks(fs, group_blk, last_blk, fs->inode_blocks_per_group, bmap, &new_blk); if (retval) return retval; for (j=0, blk = new_blk; j < fs->inode_blocks_per_group; j++, blk++) { ext2fs_mark_block_bitmap(bmap, blk); if (flexbg_size) { dgrp_t gr = ext2fs_group_of_blk(fs, blk); fs->group_desc[gr].bg_free_blocks_count--; fs->super->s_free_blocks_count--; fs->group_desc[gr].bg_flags &= ~EXT2_BG_BLOCK_UNINIT; ext2fs_group_desc_csum_set(fs, gr); } } fs->group_desc[group].bg_inode_table = new_blk; } ext2fs_group_desc_csum_set(fs, group); return 0; } errcode_t ext2fs_allocate_tables(ext2_filsys fs) { errcode_t retval; dgrp_t i; for (i = 0; i < fs->group_desc_count; i++) { retval = ext2fs_allocate_group_table(fs, i, fs->block_map); if (retval) return retval; } return 0; } e2fsprogs-1.41.14/lib/ext2fs/crc16.h0000644031104000366760000000127411374366235014720 0ustar tytso/* * crc16.h - CRC-16 routine * * Implements the standard CRC-16: * Width 16 * Poly 0x8005 (x16 + x15 + x2 + 1) * Init 0 * * Copyright (c) 2005 Ben Gardner * * This source code is licensed under the GNU General Public License, * Version 2. See the file COPYING for more details. */ #ifndef __CRC16_H #define __CRC16_H /* for an unknown reason, PPC treats __u16 as signed and keeps doing sign * extension on the value. Instead, use only the low 16 bits of an * unsigned int for holding the CRC value to avoid this. */ typedef unsigned int crc16_t; extern crc16_t ext2fs_crc16(crc16_t crc, const void *buffer, unsigned int len); #endif /* __CRC16_H */ e2fsprogs-1.41.14/lib/ext2fs/namei.c0000644031104000116100000001163111504417000015511 0ustar tytsoeng/* * namei.c --- ext2fs directory lookup operations * * Copyright (C) 1993, 1994, 1994, 1995 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif /* #define NAMEI_DEBUG */ #include "ext2_fs.h" #include "ext2fs.h" static errcode_t open_namei(ext2_filsys fs, ext2_ino_t root, ext2_ino_t base, const char *pathname, size_t pathlen, int follow, int link_count, char *buf, ext2_ino_t *res_inode); static errcode_t follow_link(ext2_filsys fs, ext2_ino_t root, ext2_ino_t dir, ext2_ino_t inode, int link_count, char *buf, ext2_ino_t *res_inode) { char *pathname; char *buffer = 0; errcode_t retval; struct ext2_inode ei; #ifdef NAMEI_DEBUG printf("follow_link: root=%lu, dir=%lu, inode=%lu, lc=%d\n", root, dir, inode, link_count); #endif retval = ext2fs_read_inode (fs, inode, &ei); if (retval) return retval; if (!LINUX_S_ISLNK (ei.i_mode)) { *res_inode = inode; return 0; } if (link_count++ > 5) { return EXT2_ET_SYMLINK_LOOP; } if (ext2fs_inode_data_blocks(fs,&ei)) { retval = ext2fs_get_mem(fs->blocksize, &buffer); if (retval) return retval; retval = io_channel_read_blk(fs->io, ei.i_block[0], 1, buffer); if (retval) { ext2fs_free_mem(&buffer); return retval; } pathname = buffer; } else pathname = (char *)&(ei.i_block[0]); retval = open_namei(fs, root, dir, pathname, ei.i_size, 1, link_count, buf, res_inode); if (buffer) ext2fs_free_mem(&buffer); return retval; } /* * This routine interprets a pathname in the context of the current * directory and the root directory, and returns the inode of the * containing directory, and a pointer to the filename of the file * (pointing into the pathname) and the length of the filename. */ static errcode_t dir_namei(ext2_filsys fs, ext2_ino_t root, ext2_ino_t dir, const char *pathname, int pathlen, int link_count, char *buf, const char **name, int *namelen, ext2_ino_t *res_inode) { char c; const char *thisname; int len; ext2_ino_t inode; errcode_t retval; if ((c = *pathname) == '/') { dir = root; pathname++; pathlen--; } while (1) { thisname = pathname; for (len=0; --pathlen >= 0;len++) { c = *(pathname++); if (c == '/') break; } if (pathlen < 0) break; retval = ext2fs_lookup (fs, dir, thisname, len, buf, &inode); if (retval) return retval; retval = follow_link (fs, root, dir, inode, link_count, buf, &dir); if (retval) return retval; } *name = thisname; *namelen = len; *res_inode = dir; return 0; } static errcode_t open_namei(ext2_filsys fs, ext2_ino_t root, ext2_ino_t base, const char *pathname, size_t pathlen, int follow, int link_count, char *buf, ext2_ino_t *res_inode) { const char *base_name; int namelen; ext2_ino_t dir, inode; errcode_t retval; #ifdef NAMEI_DEBUG printf("open_namei: root=%lu, dir=%lu, path=%*s, lc=%d\n", root, base, pathlen, pathname, link_count); #endif retval = dir_namei(fs, root, base, pathname, pathlen, link_count, buf, &base_name, &namelen, &dir); if (retval) return retval; if (!namelen) { /* special case: '/usr/' etc */ *res_inode=dir; return 0; } retval = ext2fs_lookup (fs, dir, base_name, namelen, buf, &inode); if (retval) return retval; if (follow) { retval = follow_link(fs, root, dir, inode, link_count, buf, &inode); if (retval) return retval; } #ifdef NAMEI_DEBUG printf("open_namei: (link_count=%d) returns %lu\n", link_count, inode); #endif *res_inode = inode; return 0; } errcode_t ext2fs_namei(ext2_filsys fs, ext2_ino_t root, ext2_ino_t cwd, const char *name, ext2_ino_t *inode) { char *buf; errcode_t retval; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); retval = ext2fs_get_mem(fs->blocksize, &buf); if (retval) return retval; retval = open_namei(fs, root, cwd, name, strlen(name), 0, 0, buf, inode); ext2fs_free_mem(&buf); return retval; } errcode_t ext2fs_namei_follow(ext2_filsys fs, ext2_ino_t root, ext2_ino_t cwd, const char *name, ext2_ino_t *inode) { char *buf; errcode_t retval; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); retval = ext2fs_get_mem(fs->blocksize, &buf); if (retval) return retval; retval = open_namei(fs, root, cwd, name, strlen(name), 1, 0, buf, inode); ext2fs_free_mem(&buf); return retval; } errcode_t ext2fs_follow_link(ext2_filsys fs, ext2_ino_t root, ext2_ino_t cwd, ext2_ino_t inode, ext2_ino_t *res_inode) { char *buf; errcode_t retval; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); retval = ext2fs_get_mem(fs->blocksize, &buf); if (retval) return retval; retval = follow_link(fs, root, cwd, inode, 0, buf, res_inode); ext2fs_free_mem(&buf); return retval; } e2fsprogs-1.41.14/lib/ext2fs/bitops.c0000644031104000116100000000332411504417000015720 0ustar tytsoeng/* * bitops.c --- Bitmap frobbing code. See bitops.h for the inlined * routines. * * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #include #if HAVE_SYS_TYPES_H #include #endif #include "ext2_fs.h" #include "ext2fs.h" #ifndef _EXT2_HAVE_ASM_BITOPS_ /* * For the benefit of those who are trying to port Linux to another * architecture, here are some C-language equivalents. You should * recode these in the native assmebly language, if at all possible. * * C language equivalents written by Theodore Ts'o, 9/26/92. * Modified by Pete A. Zaitcev 7/14/95 to be portable to big endian * systems, as well as non-32 bit systems. */ int ext2fs_set_bit(unsigned int nr,void * addr) { int mask, retval; unsigned char *ADDR = (unsigned char *) addr; ADDR += nr >> 3; mask = 1 << (nr & 0x07); retval = mask & *ADDR; *ADDR |= mask; return retval; } int ext2fs_clear_bit(unsigned int nr, void * addr) { int mask, retval; unsigned char *ADDR = (unsigned char *) addr; ADDR += nr >> 3; mask = 1 << (nr & 0x07); retval = mask & *ADDR; *ADDR &= ~mask; return retval; } int ext2fs_test_bit(unsigned int nr, const void * addr) { int mask; const unsigned char *ADDR = (const unsigned char *) addr; ADDR += nr >> 3; mask = 1 << (nr & 0x07); return (mask & *ADDR); } #endif /* !_EXT2_HAVE_ASM_BITOPS_ */ void ext2fs_warn_bitmap(errcode_t errcode, unsigned long arg, const char *description) { #ifndef OMIT_COM_ERR if (description) com_err(0, errcode, "#%lu for %s", arg, description); else com_err(0, errcode, "#%lu", arg); #endif } e2fsprogs-1.41.14/lib/ext2fs/tst_badblocks.c0000644031104000116100000001626211504417000017243 0ustar tytsoeng/* * This testing program makes sure the badblocks implementation works. * * Copyright (C) 1996 by Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #include #include #include #include #if HAVE_ERRNO_H #include #endif #include "ext2_fs.h" #include "ext2fs.h" #define ADD_BLK 0x0001 #define DEL_BLK 0x0002 blk_t test1[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0 }; blk_t test2[] = { 11, 10, 9, 8, 7, 6, 5, 4, 3, 3, 2, 1 }; blk_t test3[] = { 3, 1, 4, 5, 9, 2, 7, 10, 5, 6, 10, 8, 0 }; blk_t test4[] = { 20, 50, 12, 17, 13, 2, 66, 23, 56, 0 }; blk_t test4a[] = { 20, 1, 50, 1, 3, 0, 17, 1, 18, 0, 16, 0, 11, 0, 12, 1, 13, 1, 14, 0, 80, 0, 45, 0, 66, 1, 0 }; blk_t test5[] = { 31, 20, 17, 51, 23, 1, 56, 57, 0 }; blk_t test5a[] = { 50, ADD_BLK, 51, DEL_BLK, 57, DEL_BLK, 66, ADD_BLK, 31, DEL_BLK, 12, ADD_BLK, 2, ADD_BLK, 13, ADD_BLK, 1, DEL_BLK, 0 }; static int test_fail = 0; static int test_expected_fail = 0; static errcode_t create_test_list(blk_t *vec, badblocks_list *ret) { errcode_t retval; badblocks_list bb; int i; retval = ext2fs_badblocks_list_create(&bb, 5); if (retval) { com_err("create_test_list", retval, "while creating list"); return retval; } for (i=0; vec[i]; i++) { retval = ext2fs_badblocks_list_add(bb, vec[i]); if (retval) { com_err("create_test_list", retval, "while adding test vector %d", i); ext2fs_badblocks_list_free(bb); return retval; } } *ret = bb; return 0; } static void print_list(badblocks_list bb, int verify) { errcode_t retval; badblocks_iterate iter; blk_t blk; int i, ok; retval = ext2fs_badblocks_list_iterate_begin(bb, &iter); if (retval) { com_err("print_list", retval, "while setting up iterator"); return; } ok = i = 1; while (ext2fs_badblocks_list_iterate(iter, &blk)) { printf("%u ", blk); if (i++ != blk) ok = 0; } ext2fs_badblocks_list_iterate_end(iter); if (verify) { if (ok) printf("--- OK"); else { printf("--- NOT OK"); test_fail++; } } } static void validate_test_seq(badblocks_list bb, blk_t *vec) { int i, match, ok; for (i = 0; vec[i]; i += 2) { match = ext2fs_badblocks_list_test(bb, vec[i]); if (match == vec[i+1]) ok = 1; else { ok = 0; test_fail++; } printf("\tblock %u is %s --- %s\n", vec[i], match ? "present" : "absent", ok ? "OK" : "NOT OK"); } } static void do_test_seq(badblocks_list bb, blk_t *vec) { int i, match; for (i = 0; vec[i]; i += 2) { switch (vec[i+1]) { case ADD_BLK: ext2fs_badblocks_list_add(bb, vec[i]); match = ext2fs_badblocks_list_test(bb, vec[i]); printf("Adding block %u --- now %s\n", vec[i], match ? "present" : "absent"); if (!match) { printf("FAILURE!\n"); test_fail++; } break; case DEL_BLK: ext2fs_badblocks_list_del(bb, vec[i]); match = ext2fs_badblocks_list_test(bb, vec[i]); printf("Removing block %u --- now %s\n", vec[i], ext2fs_badblocks_list_test(bb, vec[i]) ? "present" : "absent"); if (match) { printf("FAILURE!\n"); test_fail++; } break; } } } int file_test(badblocks_list bb) { badblocks_list new_bb = 0; errcode_t retval; FILE *f; f = tmpfile(); if (!f) { fprintf(stderr, "Error opening temp file: %s\n", error_message(errno)); return 1; } retval = ext2fs_write_bb_FILE(bb, 0, f); if (retval) { com_err("file_test", retval, "while writing bad blocks"); return 1; } rewind(f); retval = ext2fs_read_bb_FILE2(0, f, &new_bb, 0, 0); if (retval) { com_err("file_test", retval, "while reading bad blocks"); return 1; } fclose(f); if (ext2fs_badblocks_equal(bb, new_bb)) { printf("Block bitmap matched after reading and writing.\n"); } else { printf("Block bitmap NOT matched.\n"); test_fail++; } return 0; } static void invalid_proc(ext2_filsys fs, blk_t blk) { if (blk == 34500) { printf("Expected invalid block\n"); test_expected_fail++; } else { printf("Invalid block #: %u\n", blk); test_fail++; } } int file_test_invalid(badblocks_list bb) { badblocks_list new_bb = 0; errcode_t retval; ext2_filsys fs; FILE *f; fs = malloc(sizeof(struct struct_ext2_filsys)); memset(fs, 0, sizeof(struct struct_ext2_filsys)); fs->magic = EXT2_ET_MAGIC_EXT2FS_FILSYS; fs->super = malloc(SUPERBLOCK_SIZE); memset(fs->super, 0, SUPERBLOCK_SIZE); fs->super->s_first_data_block = 1; fs->super->s_blocks_count = 100; f = tmpfile(); if (!f) { fprintf(stderr, "Error opening temp file: %s\n", error_message(errno)); return 1; } retval = ext2fs_write_bb_FILE(bb, 0, f); if (retval) { com_err("file_test", retval, "while writing bad blocks"); return 1; } fprintf(f, "34500\n"); rewind(f); test_expected_fail = 0; retval = ext2fs_read_bb_FILE(fs, f, &new_bb, invalid_proc); if (retval) { com_err("file_test", retval, "while reading bad blocks"); return 1; } fclose(f); if (!test_expected_fail) { printf("Expected test failure didn't happen!\n"); test_fail++; } if (ext2fs_badblocks_equal(bb, new_bb)) { printf("Block bitmap matched after reading and writing.\n"); } else { printf("Block bitmap NOT matched.\n"); test_fail++; } return 0; } int main(int argc, char **argv) { badblocks_list bb1, bb2, bb3, bb4, bb5; int equal; errcode_t retval; add_error_table(&et_ext2_error_table); bb1 = bb2 = bb3 = bb4 = bb5 = 0; printf("test1: "); retval = create_test_list(test1, &bb1); if (retval == 0) print_list(bb1, 1); printf("\n"); printf("test2: "); retval = create_test_list(test2, &bb2); if (retval == 0) print_list(bb2, 1); printf("\n"); printf("test3: "); retval = create_test_list(test3, &bb3); if (retval == 0) print_list(bb3, 1); printf("\n"); printf("test4: "); retval = create_test_list(test4, &bb4); if (retval == 0) { print_list(bb4, 0); printf("\n"); validate_test_seq(bb4, test4a); } printf("\n"); printf("test5: "); retval = create_test_list(test5, &bb5); if (retval == 0) { print_list(bb5, 0); printf("\n"); do_test_seq(bb5, test5a); printf("After test5 sequence: "); print_list(bb5, 0); printf("\n"); } printf("\n"); if (bb1 && bb2 && bb3 && bb4 && bb5) { printf("Comparison tests:\n"); equal = ext2fs_badblocks_equal(bb1, bb2); printf("bb1 and bb2 are %sequal.\n", equal ? "" : "NOT "); if (equal) test_fail++; equal = ext2fs_badblocks_equal(bb1, bb3); printf("bb1 and bb3 are %sequal.\n", equal ? "" : "NOT "); if (!equal) test_fail++; equal = ext2fs_badblocks_equal(bb1, bb4); printf("bb1 and bb4 are %sequal.\n", equal ? "" : "NOT "); if (equal) test_fail++; equal = ext2fs_badblocks_equal(bb4, bb5); printf("bb4 and bb5 are %sequal.\n", equal ? "" : "NOT "); if (!equal) test_fail++; printf("\n"); } file_test(bb4); file_test_invalid(bb4); if (test_fail == 0) printf("ext2fs library badblocks tests checks out OK!\n"); if (bb1) ext2fs_badblocks_list_free(bb1); if (bb2) ext2fs_badblocks_list_free(bb2); if (bb3) ext2fs_badblocks_list_free(bb3); if (bb4) ext2fs_badblocks_list_free(bb4); return test_fail; } e2fsprogs-1.41.14/lib/ext2fs/nt_io.c0000644031104000366760000006110311405316370015070 0ustar tytso/* * nt_io.c --- This is the Nt I/O interface to the I/O manager. * * Implements a one-block write-through cache. * * Copyright (C) 1993, 1994, 1995 Theodore Ts'o. * Copyright (C) 1998 Andrey Shedel (andreys@ns.cr.cyco.com) * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #ifdef HAVE_CONFIG_H #include "config.h" #endif // // I need some warnings to disable... // #pragma warning(disable:4514) // unreferenced inline function has been removed #pragma warning(push,4) #pragma warning(disable:4201) // nonstandard extension used : nameless struct/union) #pragma warning(disable:4214) // nonstandard extension used : bit field types other than int #pragma warning(disable:4115) // named type definition in parentheses #include #include #include #pragma warning(pop) // // Some native APIs. // NTSYSAPI ULONG NTAPI RtlNtStatusToDosError( IN NTSTATUS Status ); NTSYSAPI NTSTATUS NTAPI NtClose( IN HANDLE Handle ); NTSYSAPI NTSTATUS NTAPI NtOpenFile( OUT PHANDLE FileHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, OUT PIO_STATUS_BLOCK IoStatusBlock, IN ULONG ShareAccess, IN ULONG OpenOptions ); NTSYSAPI NTSTATUS NTAPI NtFlushBuffersFile( IN HANDLE FileHandle, OUT PIO_STATUS_BLOCK IoStatusBlock ); NTSYSAPI NTSTATUS NTAPI NtReadFile( IN HANDLE FileHandle, IN HANDLE Event OPTIONAL, IN PIO_APC_ROUTINE ApcRoutine OPTIONAL, IN PVOID ApcContext OPTIONAL, OUT PIO_STATUS_BLOCK IoStatusBlock, OUT PVOID Buffer, IN ULONG Length, IN PLARGE_INTEGER ByteOffset OPTIONAL, IN PULONG Key OPTIONAL ); NTSYSAPI NTSTATUS NTAPI NtWriteFile( IN HANDLE FileHandle, IN HANDLE Event OPTIONAL, IN PIO_APC_ROUTINE ApcRoutine OPTIONAL, IN PVOID ApcContext OPTIONAL, OUT PIO_STATUS_BLOCK IoStatusBlock, IN PVOID Buffer, IN ULONG Length, IN PLARGE_INTEGER ByteOffset OPTIONAL, IN PULONG Key OPTIONAL ); NTSYSAPI NTSTATUS NTAPI NtDeviceIoControlFile( IN HANDLE FileHandle, IN HANDLE Event OPTIONAL, IN PIO_APC_ROUTINE ApcRoutine OPTIONAL, IN PVOID ApcContext OPTIONAL, OUT PIO_STATUS_BLOCK IoStatusBlock, IN ULONG IoControlCode, IN PVOID InputBuffer OPTIONAL, IN ULONG InputBufferLength, OUT PVOID OutputBuffer OPTIONAL, IN ULONG OutputBufferLength ); NTSYSAPI NTSTATUS NTAPI NtFsControlFile( IN HANDLE FileHandle, IN HANDLE Event OPTIONAL, IN PIO_APC_ROUTINE ApcRoutine OPTIONAL, IN PVOID ApcContext OPTIONAL, OUT PIO_STATUS_BLOCK IoStatusBlock, IN ULONG IoControlCode, IN PVOID InputBuffer OPTIONAL, IN ULONG InputBufferLength, OUT PVOID OutputBuffer OPTIONAL, IN ULONG OutputBufferLength ); NTSYSAPI NTSTATUS NTAPI NtDelayExecution( IN BOOLEAN Alertable, IN PLARGE_INTEGER Interval ); #define FSCTL_LOCK_VOLUME CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 6, METHOD_BUFFERED, FILE_ANY_ACCESS) #define FSCTL_UNLOCK_VOLUME CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 7, METHOD_BUFFERED, FILE_ANY_ACCESS) #define FSCTL_DISMOUNT_VOLUME CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 8, METHOD_BUFFERED, FILE_ANY_ACCESS) #define FSCTL_IS_VOLUME_MOUNTED CTL_CODE(FILE_DEVICE_FILE_SYSTEM,10, METHOD_BUFFERED, FILE_ANY_ACCESS) // // useful macros // #define BooleanFlagOn(Flags,SingleFlag) ((BOOLEAN)((((Flags) & (SingleFlag)) != 0))) // // Include Win32 error codes. // #include // // standard stuff // #include #include #include #include #include #include #include "ext2_fs.h" #include #include "et/com_err.h" #include "ext2fs/ext2fs.h" #include "ext2fs/ext2_err.h" // // For checking structure magic numbers... // #define EXT2_CHECK_MAGIC(struct, code) \ if ((struct)->magic != (code)) return (code) #define EXT2_ET_MAGIC_NT_IO_CHANNEL 0x10ed // // Private data block // typedef struct _NT_PRIVATE_DATA { int magic; HANDLE Handle; int Flags; PCHAR Buffer; __u32 BufferBlockNumber; ULONG BufferSize; BOOLEAN OpenedReadonly; BOOLEAN Written; }NT_PRIVATE_DATA, *PNT_PRIVATE_DATA; // // Standard interface prototypes // static errcode_t nt_open(const char *name, int flags, io_channel *channel); static errcode_t nt_close(io_channel channel); static errcode_t nt_set_blksize(io_channel channel, int blksize); static errcode_t nt_read_blk(io_channel channel, unsigned long block, int count, void *data); static errcode_t nt_write_blk(io_channel channel, unsigned long block, int count, const void *data); static errcode_t nt_flush(io_channel channel); static struct struct_io_manager struct_nt_manager = { EXT2_ET_MAGIC_IO_MANAGER, "NT I/O Manager", nt_open, nt_close, nt_set_blksize, nt_read_blk, nt_write_blk, nt_flush }; // // function to get API // io_manager nt_io_manager() { return &struct_nt_manager; } // // This is a code to convert Win32 errors to unix errno // typedef struct { ULONG WinError; int errnocode; }ERROR_ENTRY; static ERROR_ENTRY ErrorTable[] = { { ERROR_INVALID_FUNCTION, EINVAL }, { ERROR_FILE_NOT_FOUND, ENOENT }, { ERROR_PATH_NOT_FOUND, ENOENT }, { ERROR_TOO_MANY_OPEN_FILES, EMFILE }, { ERROR_ACCESS_DENIED, EACCES }, { ERROR_INVALID_HANDLE, EBADF }, { ERROR_ARENA_TRASHED, ENOMEM }, { ERROR_NOT_ENOUGH_MEMORY, ENOMEM }, { ERROR_INVALID_BLOCK, ENOMEM }, { ERROR_BAD_ENVIRONMENT, E2BIG }, { ERROR_BAD_FORMAT, ENOEXEC }, { ERROR_INVALID_ACCESS, EINVAL }, { ERROR_INVALID_DATA, EINVAL }, { ERROR_INVALID_DRIVE, ENOENT }, { ERROR_CURRENT_DIRECTORY, EACCES }, { ERROR_NOT_SAME_DEVICE, EXDEV }, { ERROR_NO_MORE_FILES, ENOENT }, { ERROR_LOCK_VIOLATION, EACCES }, { ERROR_BAD_NETPATH, ENOENT }, { ERROR_NETWORK_ACCESS_DENIED, EACCES }, { ERROR_BAD_NET_NAME, ENOENT }, { ERROR_FILE_EXISTS, EEXIST }, { ERROR_CANNOT_MAKE, EACCES }, { ERROR_FAIL_I24, EACCES }, { ERROR_INVALID_PARAMETER, EINVAL }, { ERROR_NO_PROC_SLOTS, EAGAIN }, { ERROR_DRIVE_LOCKED, EACCES }, { ERROR_BROKEN_PIPE, EPIPE }, { ERROR_DISK_FULL, ENOSPC }, { ERROR_INVALID_TARGET_HANDLE, EBADF }, { ERROR_INVALID_HANDLE, EINVAL }, { ERROR_WAIT_NO_CHILDREN, ECHILD }, { ERROR_CHILD_NOT_COMPLETE, ECHILD }, { ERROR_DIRECT_ACCESS_HANDLE, EBADF }, { ERROR_NEGATIVE_SEEK, EINVAL }, { ERROR_SEEK_ON_DEVICE, EACCES }, { ERROR_DIR_NOT_EMPTY, ENOTEMPTY }, { ERROR_NOT_LOCKED, EACCES }, { ERROR_BAD_PATHNAME, ENOENT }, { ERROR_MAX_THRDS_REACHED, EAGAIN }, { ERROR_LOCK_FAILED, EACCES }, { ERROR_ALREADY_EXISTS, EEXIST }, { ERROR_FILENAME_EXCED_RANGE, ENOENT }, { ERROR_NESTING_NOT_ALLOWED, EAGAIN }, { ERROR_NOT_ENOUGH_QUOTA, ENOMEM } }; static unsigned _MapDosError ( IN ULONG WinError ) { int i; // // Lookup // for (i = 0; i < (sizeof(ErrorTable)/sizeof(ErrorTable[0])); ++i) { if (WinError == ErrorTable[i].WinError) { return ErrorTable[i].errnocode; } } // // not in table. Check ranges // if ((WinError >= ERROR_WRITE_PROTECT) && (WinError <= ERROR_SHARING_BUFFER_EXCEEDED)) { return EACCES; } else if ((WinError >= ERROR_INVALID_STARTING_CODESEG) && (WinError <= ERROR_INFLOOP_IN_RELOC_CHAIN)) { return ENOEXEC; } else { return EINVAL; } } // // Function to map NT status to dos error. // static __inline unsigned _MapNtStatus( IN NTSTATUS Status ) { return _MapDosError(RtlNtStatusToDosError(Status)); } // // Helper functions to make things easyer // static NTSTATUS _OpenNtName( IN PCSTR Name, IN BOOLEAN Readonly, OUT PHANDLE Handle, OUT PBOOLEAN OpenedReadonly OPTIONAL ) { UNICODE_STRING UnicodeString; ANSI_STRING AnsiString; WCHAR Buffer[512]; NTSTATUS Status; OBJECT_ATTRIBUTES ObjectAttributes; IO_STATUS_BLOCK IoStatusBlock; // // Make Unicode name from inlut string // UnicodeString.Buffer = &Buffer[0]; UnicodeString.Length = 0; UnicodeString.MaximumLength = sizeof(Buffer); // in bytes!!! RtlInitAnsiString(&AnsiString, Name); Status = RtlAnsiStringToUnicodeString(&UnicodeString, &AnsiString, FALSE); if(!NT_SUCCESS(Status)) { return Status; // Unpappable character? } // // Initialize object // InitializeObjectAttributes(&ObjectAttributes, &UnicodeString, OBJ_CASE_INSENSITIVE, NULL, NULL ); // // Try to open it in initial mode // if(ARGUMENT_PRESENT(OpenedReadonly)) { *OpenedReadonly = Readonly; } Status = NtOpenFile(Handle, SYNCHRONIZE | FILE_READ_DATA | (Readonly ? 0 : FILE_WRITE_DATA), &ObjectAttributes, &IoStatusBlock, FILE_SHARE_WRITE | FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_NONALERT); if(!NT_SUCCESS(Status)) { // // Maybe was just mounted? wait 0.5 sec and retry. // LARGE_INTEGER Interval; Interval.QuadPart = -5000000; // 0.5 sec. from now NtDelayExecution(FALSE, &Interval); Status = NtOpenFile(Handle, SYNCHRONIZE | FILE_READ_DATA | (Readonly ? 0 : FILE_WRITE_DATA), &ObjectAttributes, &IoStatusBlock, FILE_SHARE_WRITE | FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_NONALERT); // // Try to satisfy mode // if((STATUS_ACCESS_DENIED == Status) && !Readonly) { if(ARGUMENT_PRESENT(OpenedReadonly)) { *OpenedReadonly = TRUE; } Status = NtOpenFile(Handle, SYNCHRONIZE | FILE_READ_DATA, &ObjectAttributes, &IoStatusBlock, FILE_SHARE_WRITE | FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_NONALERT); } } // // done // return Status; } static NTSTATUS _OpenDriveLetter( IN CHAR Letter, IN BOOLEAN ReadOnly, OUT PHANDLE Handle, OUT PBOOLEAN OpenedReadonly OPTIONAL ) { CHAR Buffer[100]; sprintf(Buffer, "\\DosDevices\\%c:", Letter); return _OpenNtName(Buffer, ReadOnly, Handle, OpenedReadonly); } // // Flush device // static __inline NTSTATUS _FlushDrive( IN HANDLE Handle ) { IO_STATUS_BLOCK IoStatusBlock; return NtFlushBuffersFile(Handle, &IoStatusBlock); } // // lock drive // static __inline NTSTATUS _LockDrive( IN HANDLE Handle ) { IO_STATUS_BLOCK IoStatusBlock; return NtFsControlFile(Handle, 0, 0, 0, &IoStatusBlock, FSCTL_LOCK_VOLUME, 0, 0, 0, 0); } // // unlock drive // static __inline NTSTATUS _UnlockDrive( IN HANDLE Handle ) { IO_STATUS_BLOCK IoStatusBlock; return NtFsControlFile(Handle, 0, 0, 0, &IoStatusBlock, FSCTL_UNLOCK_VOLUME, 0, 0, 0, 0); } static __inline NTSTATUS _DismountDrive( IN HANDLE Handle ) { IO_STATUS_BLOCK IoStatusBlock; return NtFsControlFile(Handle, 0, 0, 0, &IoStatusBlock, FSCTL_DISMOUNT_VOLUME, 0, 0, 0, 0); } // // is mounted // static __inline BOOLEAN _IsMounted( IN HANDLE Handle ) { IO_STATUS_BLOCK IoStatusBlock; NTSTATUS Status; Status = NtFsControlFile(Handle, 0, 0, 0, &IoStatusBlock, FSCTL_IS_VOLUME_MOUNTED, 0, 0, 0, 0); return (BOOLEAN)(STATUS_SUCCESS == Status); } static __inline NTSTATUS _CloseDisk( IN HANDLE Handle ) { return NtClose(Handle); } // // Make NT name from any recognized name // static PCSTR _NormalizeDeviceName( IN PCSTR Device, IN PSTR NormalizedDeviceNameBuffer ) { int PartitionNumber = -1; UCHAR DiskNumber; PSTR p; // // Do not try to parse NT name // if('\\' == *Device) return Device; // // Strip leading '/dev/' if any // if(('/' == *(Device)) && ('d' == *(Device + 1)) && ('e' == *(Device + 2)) && ('v' == *(Device + 3)) && ('/' == *(Device + 4))) { Device += 5; } if('\0' == *Device) { return NULL; } // // forms: hda[n], fd[n] // if('d' != *(Device + 1)) { return NULL; } if('h' == *Device) { if((*(Device + 2) < 'a') || (*(Device + 2) > ('a' + 9)) || ((*(Device + 3) != '\0') && ((*(Device + 4) != '\0') || ((*(Device + 3) < '0') || (*(Device + 3) > '9')) ) ) ) { return NULL; } DiskNumber = (UCHAR)(*(Device + 2) - 'a'); if(*(Device + 3) != '\0') { PartitionNumber = (*(Device + 3) - '0'); } } else if('f' == *Device) { // // 3-d letted should be a digit. // if((*(Device + 3) != '\0') || (*(Device + 2) < '0') || (*(Device + 2) > '9')) { return NULL; } DiskNumber = (UCHAR)(*(Device + 2) - '0'); } else { // // invalid prefix // return NULL; } // // Prefix // strcpy(NormalizedDeviceNameBuffer, "\\Device\\"); // // Media name // switch(*Device) { case 'f': strcat(NormalizedDeviceNameBuffer, "Floppy0"); break; case 'h': strcat(NormalizedDeviceNameBuffer, "Harddisk0"); break; } p = NormalizedDeviceNameBuffer + strlen(NormalizedDeviceNameBuffer) - 1; *p = (CHAR)(*p + DiskNumber); // // Partition nr. // if(PartitionNumber >= 0) { strcat(NormalizedDeviceNameBuffer, "\\Partition0"); p = NormalizedDeviceNameBuffer + strlen(NormalizedDeviceNameBuffer) - 1; *p = (CHAR)(*p + PartitionNumber); } return NormalizedDeviceNameBuffer; } static VOID _GetDeviceSize( IN HANDLE h, OUT unsigned __int64 *FsSize ) { PARTITION_INFORMATION pi; DISK_GEOMETRY gi; NTSTATUS Status; IO_STATUS_BLOCK IoStatusBlock; // // Zero it // *FsSize = 0; // // Call driver // RtlZeroMemory(&pi, sizeof(PARTITION_INFORMATION)); Status = NtDeviceIoControlFile( h, NULL, NULL, NULL, &IoStatusBlock, IOCTL_DISK_GET_PARTITION_INFO, &pi, sizeof(PARTITION_INFORMATION), &pi, sizeof(PARTITION_INFORMATION)); if(NT_SUCCESS(Status)) { *FsSize = pi.PartitionLength.QuadPart; } else if(STATUS_INVALID_DEVICE_REQUEST == Status) { // // No partitions: get device info. // RtlZeroMemory(&gi, sizeof(DISK_GEOMETRY)); Status = NtDeviceIoControlFile( h, NULL, NULL, NULL, &IoStatusBlock, IOCTL_DISK_GET_DRIVE_GEOMETRY, &gi, sizeof(DISK_GEOMETRY), &gi, sizeof(DISK_GEOMETRY)); if(NT_SUCCESS(Status)) { *FsSize = gi.BytesPerSector * gi.SectorsPerTrack * gi.TracksPerCylinder * gi.Cylinders.QuadPart; } } } // // Open device by name. // static BOOLEAN _Ext2OpenDevice( IN PCSTR Name, IN BOOLEAN ReadOnly, OUT PHANDLE Handle, OUT PBOOLEAN OpenedReadonly OPTIONAL, OUT unsigned *Errno OPTIONAL ) { CHAR NormalizedDeviceName[512]; NTSTATUS Status; if(NULL == Name) { // // Set not found // if(ARGUMENT_PRESENT(Errno)) *Errno = ENOENT; return FALSE; } if((((*Name) | 0x20) >= 'a') && (((*Name) | 0x20) <= 'z') && (':' == *(Name + 1)) && ('\0' == *(Name + 2))) { Status = _OpenDriveLetter(*Name, ReadOnly, Handle, OpenedReadonly); } else { // // Make name // Name = _NormalizeDeviceName(Name, NormalizedDeviceName); if(NULL == Name) { // // Set not found // if(ARGUMENT_PRESENT(Errno)) *Errno = ENOENT; return FALSE; } // // Try to open it // Status = _OpenNtName(Name, ReadOnly, Handle, OpenedReadonly); } if(!NT_SUCCESS(Status)) { if(ARGUMENT_PRESENT(Errno)) *Errno = _MapNtStatus(Status); return FALSE; } return TRUE; } // // Raw block io. Sets dos errno // static BOOLEAN _BlockIo( IN HANDLE Handle, IN LARGE_INTEGER Offset, IN ULONG Bytes, IN OUT PCHAR Buffer, IN BOOLEAN Read, OUT unsigned* Errno ) { IO_STATUS_BLOCK IoStatusBlock; NTSTATUS Status; // // Should be aligned // ASSERT(0 == (Bytes % 512)); ASSERT(0 == (Offset.LowPart % 512)); // // perform io // if(Read) { Status = NtReadFile(Handle, NULL, NULL, NULL, &IoStatusBlock, Buffer, Bytes, &Offset, NULL); } else { Status = NtWriteFile(Handle, NULL, NULL, NULL, &IoStatusBlock, Buffer, Bytes, &Offset, NULL); } // // translate error // if(NT_SUCCESS(Status)) { *Errno = 0; return TRUE; } *Errno = _MapNtStatus(Status); return FALSE; } __inline BOOLEAN _RawWrite( IN HANDLE Handle, IN LARGE_INTEGER Offset, IN ULONG Bytes, OUT const CHAR* Buffer, OUT unsigned* Errno ) { return _BlockIo(Handle, Offset, Bytes, (PCHAR)Buffer, FALSE, Errno); } __inline BOOLEAN _RawRead( IN HANDLE Handle, IN LARGE_INTEGER Offset, IN ULONG Bytes, IN PCHAR Buffer, OUT unsigned* Errno ) { return _BlockIo(Handle, Offset, Bytes, Buffer, TRUE, Errno); } __inline BOOLEAN _SetPartType( IN HANDLE Handle, IN UCHAR Type ) { IO_STATUS_BLOCK IoStatusBlock; return STATUS_SUCCESS == NtDeviceIoControlFile( Handle, NULL, NULL, NULL, &IoStatusBlock, IOCTL_DISK_SET_PARTITION_INFO, &Type, sizeof(Type), NULL, 0); } //--------------------- interface part // // Interface functions. // Is_mounted is set to 1 if the device is mounted, 0 otherwise // errcode_t ext2fs_check_if_mounted(const char *file, int *mount_flags) { HANDLE h; BOOLEAN Readonly; *mount_flags = 0; if(!_Ext2OpenDevice(file, TRUE, &h, &Readonly, NULL)) { return 0; } __try{ *mount_flags &= _IsMounted(h) ? EXT2_MF_MOUNTED : 0; } __finally{ _CloseDisk(h); } return 0; } // // Returns the number of blocks in a partition // static __int64 FsSize = 0; static char knowndevice[1024] = ""; errcode_t ext2fs_get_device_size(const char *file, int blocksize, blk_t *retblocks) { HANDLE h; BOOLEAN Readonly; if((0 == FsSize) || (0 != strcmp(knowndevice, file))) { if(!_Ext2OpenDevice(file, TRUE, &h, &Readonly, NULL)) { return 0; } __try{ // // Get size // _GetDeviceSize(h, &FsSize); strcpy(knowndevice, file); } __finally{ _CloseDisk(h); } } *retblocks = (blk_t)(unsigned __int64)(FsSize / blocksize); UNREFERENCED_PARAMETER(file); return 0; } // // Table elements // static errcode_t nt_open(const char *name, int flags, io_channel *channel) { io_channel io = NULL; PNT_PRIVATE_DATA NtData = NULL; errcode_t Errno = 0; // // Check name // if (NULL == name) { return EXT2_ET_BAD_DEVICE_NAME; } __try{ // // Allocate channel handle // io = (io_channel) malloc(sizeof(struct struct_io_channel)); if (NULL == io) { Errno = ENOMEM; __leave; } RtlZeroMemory(io, sizeof(struct struct_io_channel)); io->magic = EXT2_ET_MAGIC_IO_CHANNEL; NtData = (PNT_PRIVATE_DATA)malloc(sizeof(NT_PRIVATE_DATA)); if (NULL == NtData) { Errno = ENOMEM; __leave; } io->manager = nt_io_manager(); io->name = malloc(strlen(name) + 1); if (NULL == io->name) { Errno = ENOMEM; __leave; } strcpy(io->name, name); io->private_data = NtData; io->block_size = 1024; io->read_error = 0; io->write_error = 0; io->refcount = 1; // // Initialize data // RtlZeroMemory(NtData, sizeof(NT_PRIVATE_DATA)); NtData->magic = EXT2_ET_MAGIC_NT_IO_CHANNEL; NtData->BufferBlockNumber = 0xffffffff; NtData->BufferSize = 1024; NtData->Buffer = malloc(NtData->BufferSize); if (NULL == NtData->Buffer) { Errno = ENOMEM; __leave; } // // Open it // if(!_Ext2OpenDevice(name, (BOOLEAN)!BooleanFlagOn(flags, EXT2_FLAG_RW), &NtData->Handle, &NtData->OpenedReadonly, &Errno)) { __leave; } // // get size // _GetDeviceSize(NtData->Handle, &FsSize); strcpy(knowndevice, name); // // Lock/dismount // if(!NT_SUCCESS(_LockDrive(NtData->Handle)) /*|| !NT_SUCCESS(_DismountDrive(NtData->Handle))*/) { NtData->OpenedReadonly = TRUE; } // // Done // *channel = io; } __finally{ if(0 != Errno) { // // Cleanup // if (NULL != io) { free(io->name); free(io); } if (NULL != NtData) { if(NULL != NtData->Handle) { _UnlockDrive(NtData->Handle); _CloseDisk(NtData->Handle); } free(NtData->Buffer); free(NtData); } } } return Errno; } // // Close api // static errcode_t nt_close(io_channel channel) { PNT_PRIVATE_DATA NtData = NULL; if(NULL == channel) { return 0; } EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); NtData = (PNT_PRIVATE_DATA) channel->private_data; EXT2_CHECK_MAGIC(NtData, EXT2_ET_MAGIC_NT_IO_CHANNEL); if (--channel->refcount > 0) { return 0; } free(channel->name); free(channel); if (NULL != NtData) { if(NULL != NtData->Handle) { _DismountDrive(NtData->Handle); _UnlockDrive(NtData->Handle); _CloseDisk(NtData->Handle); } free(NtData->Buffer); free(NtData); } return 0; } // // set block size // static errcode_t nt_set_blksize(io_channel channel, int blksize) { PNT_PRIVATE_DATA NtData = NULL; EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); NtData = (PNT_PRIVATE_DATA) channel->private_data; EXT2_CHECK_MAGIC(NtData, EXT2_ET_MAGIC_NT_IO_CHANNEL); if (channel->block_size != blksize) { channel->block_size = blksize; free(NtData->Buffer); NtData->BufferBlockNumber = 0xffffffff; NtData->BufferSize = channel->block_size; ASSERT(0 == (NtData->BufferSize % 512)); NtData->Buffer = malloc(NtData->BufferSize); if (NULL == NtData->Buffer) { return ENOMEM; } } return 0; } // // read block // static errcode_t nt_read_blk(io_channel channel, unsigned long block, int count, void *buf) { PVOID BufferToRead; ULONG SizeToRead; ULONG Size; LARGE_INTEGER Offset; PNT_PRIVATE_DATA NtData = NULL; unsigned Errno = 0; EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); NtData = (PNT_PRIVATE_DATA) channel->private_data; EXT2_CHECK_MAGIC(NtData, EXT2_ET_MAGIC_NT_IO_CHANNEL); // // If it's in the cache, use it! // if ((1 == count) && (block == NtData->BufferBlockNumber) && (NtData->BufferBlockNumber != 0xffffffff)) { memcpy(buf, NtData->Buffer, channel->block_size); return 0; } Size = (count < 0) ? (ULONG)(-count) : (ULONG)(count * channel->block_size); Offset.QuadPart = block * channel->block_size; // // If not fit to the block // if(Size <= NtData->BufferSize) { // // Update the cache // NtData->BufferBlockNumber = block; BufferToRead = NtData->Buffer; SizeToRead = NtData->BufferSize; } else { SizeToRead = Size; BufferToRead = buf; ASSERT(0 == (SizeToRead % channel->block_size)); } if(!_RawRead(NtData->Handle, Offset, SizeToRead, BufferToRead, &Errno)) { if (channel->read_error) { return (channel->read_error)(channel, block, count, buf, Size, 0, Errno); } else { return Errno; } } if(BufferToRead != buf) { ASSERT(Size <= SizeToRead); memcpy(buf, BufferToRead, Size); } return 0; } // // write block // static errcode_t nt_write_blk(io_channel channel, unsigned long block, int count, const void *buf) { ULONG SizeToWrite; LARGE_INTEGER Offset; PNT_PRIVATE_DATA NtData = NULL; unsigned Errno = 0; EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); NtData = (PNT_PRIVATE_DATA) channel->private_data; EXT2_CHECK_MAGIC(NtData, EXT2_ET_MAGIC_NT_IO_CHANNEL); if(NtData->OpenedReadonly) { return EACCES; } if (count == 1) { SizeToWrite = channel->block_size; } else { NtData->BufferBlockNumber = 0xffffffff; if (count < 0) { SizeToWrite = (ULONG)(-count); } else { SizeToWrite = (ULONG)(count * channel->block_size); } } ASSERT(0 == (SizeToWrite % 512)); Offset.QuadPart = block * channel->block_size; if(!_RawWrite(NtData->Handle, Offset, SizeToWrite, buf, &Errno)) { if (channel->write_error) { return (channel->write_error)(channel, block, count, buf, SizeToWrite, 0, Errno); } else { return Errno; } } // // Stash a copy. // if(SizeToWrite >= NtData->BufferSize) { NtData->BufferBlockNumber = block; memcpy(NtData->Buffer, buf, NtData->BufferSize); } NtData->Written = TRUE; return 0; } // // Flush data buffers to disk. Since we are currently using a // write-through cache, this is a no-op. // static errcode_t nt_flush(io_channel channel) { PNT_PRIVATE_DATA NtData = NULL; EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); NtData = (PNT_PRIVATE_DATA) channel->private_data; EXT2_CHECK_MAGIC(NtData, EXT2_ET_MAGIC_NT_IO_CHANNEL); if(NtData->OpenedReadonly) { return 0; // EACCESS; } // // Flush file buffers. // _FlushDrive(NtData->Handle); // // Test and correct partition type. // if(NtData->Written) { _SetPartType(NtData->Handle, 0x83); } return 0; } e2fsprogs-1.41.14/lib/ext2fs/kernel-list.h0000644031104000366760000000471111374366235016232 0ustar tytso#ifndef _LINUX_LIST_H #define _LINUX_LIST_H /* * Simple doubly linked list implementation. * * Some of the internal functions ("__xxx") are useful when * manipulating whole lists rather than single entries, as * sometimes we already know the next/prev entries and we can * generate better code by using them directly rather than * using the generic single-entry routines. */ struct list_head { struct list_head *next, *prev; }; #define LIST_HEAD_INIT(name) { &(name), &(name) } #define LIST_HEAD(name) \ struct list_head name = { &name, &name } #define INIT_LIST_HEAD(ptr) do { \ (ptr)->next = (ptr); (ptr)->prev = (ptr); \ } while (0) #if (!defined(__GNUC__) && !defined(__WATCOMC__)) #define __inline__ #endif /* * Insert a new entry between two known consecutive entries. * * This is only for internal list manipulation where we know * the prev/next entries already! */ static __inline__ void __list_add(struct list_head * new, struct list_head * prev, struct list_head * next) { next->prev = new; new->next = next; new->prev = prev; prev->next = new; } /* * Insert a new entry after the specified head.. */ static __inline__ void list_add(struct list_head *new, struct list_head *head) { __list_add(new, head, head->next); } /* * Insert a new entry at the tail */ static __inline__ void list_add_tail(struct list_head *new, struct list_head *head) { __list_add(new, head->prev, head); } /* * Delete a list entry by making the prev/next entries * point to each other. * * This is only for internal list manipulation where we know * the prev/next entries already! */ static __inline__ void __list_del(struct list_head * prev, struct list_head * next) { next->prev = prev; prev->next = next; } static __inline__ void list_del(struct list_head *entry) { __list_del(entry->prev, entry->next); } static __inline__ int list_empty(struct list_head *head) { return head->next == head; } /* * Splice in "list" into "head" */ static __inline__ void list_splice(struct list_head *list, struct list_head *head) { struct list_head *first = list->next; if (first != list) { struct list_head *last = list->prev; struct list_head *at = head->next; first->prev = head; head->next = first; last->next = at; at->prev = last; } } #define list_entry(ptr, type, member) \ ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) #define list_for_each(pos, head) \ for (pos = (head)->next; pos != (head); pos = pos->next) #endif e2fsprogs-1.41.14/lib/ext2fs/link.c0000644031104000366760000000711111405316370014714 0ustar tytso/* * link.c --- create links in a ext2fs directory * * Copyright (C) 1993, 1994 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #include "ext2_fs.h" #include "ext2fs.h" struct link_struct { ext2_filsys fs; const char *name; int namelen; ext2_ino_t inode; int flags; int done; unsigned int blocksize; errcode_t err; struct ext2_super_block *sb; }; static int link_proc(struct ext2_dir_entry *dirent, int offset, int blocksize, char *buf, void *priv_data) { struct link_struct *ls = (struct link_struct *) priv_data; struct ext2_dir_entry *next; unsigned int rec_len, min_rec_len, curr_rec_len; int ret = 0; rec_len = EXT2_DIR_REC_LEN(ls->namelen); ls->err = ext2fs_get_rec_len(ls->fs, dirent, &curr_rec_len); if (ls->err) return DIRENT_ABORT; /* * See if the following directory entry (if any) is unused; * if so, absorb it into this one. */ next = (struct ext2_dir_entry *) (buf + offset + curr_rec_len); if ((offset + curr_rec_len < blocksize - 8) && (next->inode == 0) && (offset + curr_rec_len + next->rec_len <= blocksize)) { curr_rec_len += next->rec_len; ls->err = ext2fs_set_rec_len(ls->fs, curr_rec_len, dirent); if (ls->err) return DIRENT_ABORT; ret = DIRENT_CHANGED; } /* * If the directory entry is used, see if we can split the * directory entry to make room for the new name. If so, * truncate it and return. */ if (dirent->inode) { min_rec_len = EXT2_DIR_REC_LEN(dirent->name_len & 0xFF); if (curr_rec_len < (min_rec_len + rec_len)) return ret; rec_len = curr_rec_len - min_rec_len; ls->err = ext2fs_set_rec_len(ls->fs, min_rec_len, dirent); if (ls->err) return DIRENT_ABORT; next = (struct ext2_dir_entry *) (buf + offset + dirent->rec_len); next->inode = 0; next->name_len = 0; ls->err = ext2fs_set_rec_len(ls->fs, rec_len, next); if (ls->err) return DIRENT_ABORT; return DIRENT_CHANGED; } /* * If we get this far, then the directory entry is not used. * See if we can fit the request entry in. If so, do it. */ if (curr_rec_len < rec_len) return ret; dirent->inode = ls->inode; dirent->name_len = ls->namelen; strncpy(dirent->name, ls->name, ls->namelen); if (ls->sb->s_feature_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE) dirent->name_len |= (ls->flags & 0x7) << 8; ls->done++; return DIRENT_ABORT|DIRENT_CHANGED; } /* * Note: the low 3 bits of the flags field are used as the directory * entry filetype. */ #ifdef __TURBOC__ #pragma argsused #endif errcode_t ext2fs_link(ext2_filsys fs, ext2_ino_t dir, const char *name, ext2_ino_t ino, int flags) { errcode_t retval; struct link_struct ls; struct ext2_inode inode; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); if (!(fs->flags & EXT2_FLAG_RW)) return EXT2_ET_RO_FILSYS; ls.fs = fs; ls.name = name; ls.namelen = name ? strlen(name) : 0; ls.inode = ino; ls.flags = flags; ls.done = 0; ls.sb = fs->super; ls.blocksize = fs->blocksize; ls.err = 0; retval = ext2fs_dir_iterate(fs, dir, DIRENT_FLAG_INCLUDE_EMPTY, 0, link_proc, &ls); if (retval) return retval; if (ls.err) return ls.err; if (!ls.done) return EXT2_ET_DIR_NO_SPACE; if ((retval = ext2fs_read_inode(fs, dir, &inode)) != 0) return retval; if (inode.i_flags & EXT2_INDEX_FL) { inode.i_flags &= ~EXT2_INDEX_FL; if ((retval = ext2fs_write_inode(fs, dir, &inode)) != 0) return retval; } return 0; } e2fsprogs-1.41.14/lib/ext2fs/version.c0000644031104000366760000000206611405316370015450 0ustar tytso/* * version.c --- Return the version of the ext2 library * * Copyright (C) 1997 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #if HAVE_UNISTD_H #include #endif #include #include #include #include "ext2_fs.h" #include "ext2fs.h" #include "../../version.h" static const char *lib_version = E2FSPROGS_VERSION; static const char *lib_date = E2FSPROGS_DATE; int ext2fs_parse_version_string(const char *ver_string) { const char *cp; int version = 0, dot_count = 0; for (cp = ver_string; *cp; cp++) { if (*cp == '.') { if (dot_count++) break; else continue; } if (!isdigit(*cp)) break; version = (version * 10) + (*cp - '0'); } return version; } int ext2fs_get_library_version(const char **ver_string, const char **date_string) { if (ver_string) *ver_string = lib_version; if (date_string) *date_string = lib_date; return ext2fs_parse_version_string(lib_version); } e2fsprogs-1.41.14/lib/ext2fs/check_desc.c0000644031104000116100000000500311504417000016467 0ustar tytsoeng/* * check_desc.c --- Check the group descriptors of an ext2 filesystem * * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #include #include #if HAVE_SYS_STAT_H #include #endif #if HAVE_SYS_TYPES_H #include #endif #include "ext2_fs.h" #include "ext2fs.h" /* * This routine sanity checks the group descriptors */ errcode_t ext2fs_check_desc(ext2_filsys fs) { ext2fs_block_bitmap bmap; errcode_t retval; dgrp_t i; blk_t first_block = fs->super->s_first_data_block; blk_t last_block = fs->super->s_blocks_count-1; blk_t blk, b; int j; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); retval = ext2fs_allocate_block_bitmap(fs, "check_desc map", &bmap); if (retval) return retval; for (i = 0; i < fs->group_desc_count; i++) ext2fs_reserve_super_and_bgd(fs, i, bmap); for (i = 0; i < fs->group_desc_count; i++) { if (!EXT2_HAS_INCOMPAT_FEATURE(fs->super, EXT4_FEATURE_INCOMPAT_FLEX_BG)) { first_block = ext2fs_group_first_block(fs, i); last_block = ext2fs_group_last_block(fs, i); if (i == (fs->group_desc_count - 1)) last_block = fs->super->s_blocks_count-1; } /* * Check to make sure the block bitmap for group is sane */ blk = fs->group_desc[i].bg_block_bitmap; if (blk < first_block || blk > last_block || ext2fs_test_block_bitmap(bmap, blk)) { retval = EXT2_ET_GDESC_BAD_BLOCK_MAP; goto errout; } ext2fs_mark_block_bitmap(bmap, blk); /* * Check to make sure the inode bitmap for group is sane */ blk = fs->group_desc[i].bg_inode_bitmap; if (blk < first_block || blk > last_block || ext2fs_test_block_bitmap(bmap, blk)) { retval = EXT2_ET_GDESC_BAD_INODE_MAP; goto errout; } ext2fs_mark_block_bitmap(bmap, blk); /* * Check to make sure the inode table for group is sane */ blk = fs->group_desc[i].bg_inode_table; if (blk < first_block || ((blk + fs->inode_blocks_per_group - 1) > last_block)) { retval = EXT2_ET_GDESC_BAD_INODE_TABLE; goto errout; } for (j = 0, b = blk; j < fs->inode_blocks_per_group; j++, b++) { if (ext2fs_test_block_bitmap(bmap, b)) { retval = EXT2_ET_GDESC_BAD_INODE_TABLE; goto errout; } ext2fs_mark_block_bitmap(bmap, b); } } errout: ext2fs_free_block_bitmap(bmap); return retval; } e2fsprogs-1.41.14/lib/ext2fs/rw_bitmaps.c0000644031104000116100000002104511504417000016567 0ustar tytsoeng/* * rw_bitmaps.c --- routines to read and write the inode and block bitmaps. * * Copyright (C) 1993, 1994, 1994, 1996 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #include #include #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_SYS_TYPES_H #include #endif #include "ext2_fs.h" #include "ext2fs.h" #include "e2image.h" static errcode_t write_bitmaps(ext2_filsys fs, int do_inode, int do_block) { dgrp_t i; unsigned int j; int block_nbytes, inode_nbytes; unsigned int nbits; errcode_t retval; char *block_buf, *inode_buf; int csum_flag = 0; blk_t blk; blk_t blk_itr = fs->super->s_first_data_block; ext2_ino_t ino_itr = 1; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); if (!(fs->flags & EXT2_FLAG_RW)) return EXT2_ET_RO_FILSYS; if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super, EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) csum_flag = 1; inode_nbytes = block_nbytes = 0; if (do_block) { block_nbytes = EXT2_BLOCKS_PER_GROUP(fs->super) / 8; retval = ext2fs_get_memalign(fs->blocksize, fs->blocksize, &block_buf); if (retval) return retval; memset(block_buf, 0xff, fs->blocksize); } if (do_inode) { inode_nbytes = (size_t) ((EXT2_INODES_PER_GROUP(fs->super)+7) / 8); retval = ext2fs_get_memalign(fs->blocksize, fs->blocksize, &inode_buf); if (retval) return retval; memset(inode_buf, 0xff, fs->blocksize); } for (i = 0; i < fs->group_desc_count; i++) { if (!do_block) goto skip_block_bitmap; if (csum_flag && fs->group_desc[i].bg_flags & EXT2_BG_BLOCK_UNINIT) goto skip_this_block_bitmap; retval = ext2fs_get_block_bitmap_range(fs->block_map, blk_itr, block_nbytes << 3, block_buf); if (retval) return retval; if (i == fs->group_desc_count - 1) { /* Force bitmap padding for the last group */ nbits = ((fs->super->s_blocks_count - fs->super->s_first_data_block) % EXT2_BLOCKS_PER_GROUP(fs->super)); if (nbits) for (j = nbits; j < fs->blocksize * 8; j++) ext2fs_set_bit(j, block_buf); } blk = fs->group_desc[i].bg_block_bitmap; if (blk) { retval = io_channel_write_blk(fs->io, blk, 1, block_buf); if (retval) return EXT2_ET_BLOCK_BITMAP_WRITE; } skip_this_block_bitmap: blk_itr += block_nbytes << 3; skip_block_bitmap: if (!do_inode) continue; if (csum_flag && fs->group_desc[i].bg_flags & EXT2_BG_INODE_UNINIT) goto skip_this_inode_bitmap; retval = ext2fs_get_inode_bitmap_range(fs->inode_map, ino_itr, inode_nbytes << 3, inode_buf); if (retval) return retval; blk = fs->group_desc[i].bg_inode_bitmap; if (blk) { retval = io_channel_write_blk(fs->io, blk, 1, inode_buf); if (retval) return EXT2_ET_INODE_BITMAP_WRITE; } skip_this_inode_bitmap: ino_itr += inode_nbytes << 3; } if (do_block) { fs->flags &= ~EXT2_FLAG_BB_DIRTY; ext2fs_free_mem(&block_buf); } if (do_inode) { fs->flags &= ~EXT2_FLAG_IB_DIRTY; ext2fs_free_mem(&inode_buf); } return 0; } static errcode_t read_bitmaps(ext2_filsys fs, int do_inode, int do_block) { dgrp_t i; char *block_bitmap = 0, *inode_bitmap = 0; char *buf; errcode_t retval; int block_nbytes = EXT2_BLOCKS_PER_GROUP(fs->super) / 8; int inode_nbytes = EXT2_INODES_PER_GROUP(fs->super) / 8; int csum_flag = 0; int do_image = fs->flags & EXT2_FLAG_IMAGE_FILE; unsigned int cnt; blk_t blk; blk_t blk_itr = fs->super->s_first_data_block; blk_t blk_cnt; ext2_ino_t ino_itr = 1; ext2_ino_t ino_cnt; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); fs->write_bitmaps = ext2fs_write_bitmaps; if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super, EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) csum_flag = 1; retval = ext2fs_get_mem(strlen(fs->device_name) + 80, &buf); if (retval) return retval; if (do_block) { if (fs->block_map) ext2fs_free_block_bitmap(fs->block_map); strcpy(buf, "block bitmap for "); strcat(buf, fs->device_name); retval = ext2fs_allocate_block_bitmap(fs, buf, &fs->block_map); if (retval) goto cleanup; if (do_image) retval = ext2fs_get_mem(fs->blocksize, &block_bitmap); else retval = ext2fs_get_memalign((unsigned) block_nbytes, fs->blocksize, &block_bitmap); if (retval) goto cleanup; } else block_nbytes = 0; if (do_inode) { if (fs->inode_map) ext2fs_free_inode_bitmap(fs->inode_map); strcpy(buf, "inode bitmap for "); strcat(buf, fs->device_name); retval = ext2fs_allocate_inode_bitmap(fs, buf, &fs->inode_map); if (retval) goto cleanup; retval = ext2fs_get_mem(do_image ? fs->blocksize : (unsigned) inode_nbytes, &inode_bitmap); if (retval) goto cleanup; } else inode_nbytes = 0; ext2fs_free_mem(&buf); if (fs->flags & EXT2_FLAG_IMAGE_FILE) { blk = (fs->image_header->offset_inodemap / fs->blocksize); ino_cnt = fs->super->s_inodes_count; while (inode_nbytes > 0) { retval = io_channel_read_blk(fs->image_io, blk++, 1, inode_bitmap); if (retval) goto cleanup; cnt = fs->blocksize << 3; if (cnt > ino_cnt) cnt = ino_cnt; retval = ext2fs_set_inode_bitmap_range(fs->inode_map, ino_itr, cnt, inode_bitmap); if (retval) goto cleanup; ino_itr += fs->blocksize << 3; ino_cnt -= fs->blocksize << 3; inode_nbytes -= fs->blocksize; } blk = (fs->image_header->offset_blockmap / fs->blocksize); blk_cnt = EXT2_BLOCKS_PER_GROUP(fs->super) * fs->group_desc_count; while (block_nbytes > 0) { retval = io_channel_read_blk(fs->image_io, blk++, 1, block_bitmap); if (retval) goto cleanup; cnt = fs->blocksize << 3; if (cnt > blk_cnt) cnt = blk_cnt; retval = ext2fs_set_block_bitmap_range(fs->block_map, blk_itr, cnt, block_bitmap); if (retval) goto cleanup; blk_itr += fs->blocksize << 3; blk_cnt -= fs->blocksize << 3; block_nbytes -= fs->blocksize; } goto success_cleanup; } for (i = 0; i < fs->group_desc_count; i++) { if (block_bitmap) { blk = fs->group_desc[i].bg_block_bitmap; if (csum_flag && fs->group_desc[i].bg_flags & EXT2_BG_BLOCK_UNINIT && ext2fs_group_desc_csum_verify(fs, i)) blk = 0; if (blk) { retval = io_channel_read_blk(fs->io, blk, -block_nbytes, block_bitmap); if (retval) { retval = EXT2_ET_BLOCK_BITMAP_READ; goto cleanup; } } else memset(block_bitmap, 0, block_nbytes); cnt = block_nbytes << 3; retval = ext2fs_set_block_bitmap_range(fs->block_map, blk_itr, cnt, block_bitmap); if (retval) goto cleanup; blk_itr += block_nbytes << 3; } if (inode_bitmap) { blk = fs->group_desc[i].bg_inode_bitmap; if (csum_flag && fs->group_desc[i].bg_flags & EXT2_BG_INODE_UNINIT && ext2fs_group_desc_csum_verify(fs, i)) blk = 0; if (blk) { retval = io_channel_read_blk(fs->io, blk, -inode_nbytes, inode_bitmap); if (retval) { retval = EXT2_ET_INODE_BITMAP_READ; goto cleanup; } } else memset(inode_bitmap, 0, inode_nbytes); cnt = inode_nbytes << 3; retval = ext2fs_set_inode_bitmap_range(fs->inode_map, ino_itr, cnt, inode_bitmap); if (retval) goto cleanup; ino_itr += inode_nbytes << 3; } } success_cleanup: if (inode_bitmap) ext2fs_free_mem(&inode_bitmap); if (block_bitmap) ext2fs_free_mem(&block_bitmap); return 0; cleanup: if (do_block) { ext2fs_free_mem(&fs->block_map); fs->block_map = 0; } if (do_inode) { ext2fs_free_mem(&fs->inode_map); fs->inode_map = 0; } if (inode_bitmap) ext2fs_free_mem(&inode_bitmap); if (block_bitmap) ext2fs_free_mem(&block_bitmap); if (buf) ext2fs_free_mem(&buf); return retval; } errcode_t ext2fs_read_inode_bitmap(ext2_filsys fs) { return read_bitmaps(fs, 1, 0); } errcode_t ext2fs_read_block_bitmap(ext2_filsys fs) { return read_bitmaps(fs, 0, 1); } errcode_t ext2fs_write_inode_bitmap(ext2_filsys fs) { return write_bitmaps(fs, 1, 0); } errcode_t ext2fs_write_block_bitmap (ext2_filsys fs) { return write_bitmaps(fs, 0, 1); } errcode_t ext2fs_read_bitmaps(ext2_filsys fs) { if (fs->inode_map && fs->block_map) return 0; return read_bitmaps(fs, !fs->inode_map, !fs->block_map); } errcode_t ext2fs_write_bitmaps(ext2_filsys fs) { int do_inode = fs->inode_map && ext2fs_test_ib_dirty(fs); int do_block = fs->block_map && ext2fs_test_bb_dirty(fs); if (!do_inode && !do_block) return 0; return write_bitmaps(fs, do_inode, do_block); } e2fsprogs-1.41.14/lib/ext2fs/dirhash.c0000644031104000366760000001366411405316370015413 0ustar tytso/* * dirhash.c -- Calculate the hash of a directory entry * * Copyright (c) 2001 Daniel Phillips * * Copyright (c) 2002 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #include #include #include "ext2_fs.h" #include "ext2fs.h" /* * Keyed 32-bit hash function using TEA in a Davis-Meyer function * H0 = Key * Hi = E Mi(Hi-1) + Hi-1 * * (see Applied Cryptography, 2nd edition, p448). * * Jeremy Fitzhardinge 1998 * * This code is made available under the terms of the GPL */ #define DELTA 0x9E3779B9 static void TEA_transform(__u32 buf[4], __u32 const in[]) { __u32 sum = 0; __u32 b0 = buf[0], b1 = buf[1]; __u32 a = in[0], b = in[1], c = in[2], d = in[3]; int n = 16; do { sum += DELTA; b0 += ((b1 << 4)+a) ^ (b1+sum) ^ ((b1 >> 5)+b); b1 += ((b0 << 4)+c) ^ (b0+sum) ^ ((b0 >> 5)+d); } while(--n); buf[0] += b0; buf[1] += b1; } /* F, G and H are basic MD4 functions: selection, majority, parity */ #define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) #define G(x, y, z) (((x) & (y)) + (((x) ^ (y)) & (z))) #define H(x, y, z) ((x) ^ (y) ^ (z)) /* * The generic round function. The application is so specific that * we don't bother protecting all the arguments with parens, as is generally * good macro practice, in favor of extra legibility. * Rotation is separate from addition to prevent recomputation */ #define ROUND(f, a, b, c, d, x, s) \ (a += f(b, c, d) + x, a = (a << s) | (a >> (32-s))) #define K1 0 #define K2 013240474631UL #define K3 015666365641UL /* * Basic cut-down MD4 transform. Returns only 32 bits of result. */ static void halfMD4Transform (__u32 buf[4], __u32 const in[]) { __u32 a = buf[0], b = buf[1], c = buf[2], d = buf[3]; /* Round 1 */ ROUND(F, a, b, c, d, in[0] + K1, 3); ROUND(F, d, a, b, c, in[1] + K1, 7); ROUND(F, c, d, a, b, in[2] + K1, 11); ROUND(F, b, c, d, a, in[3] + K1, 19); ROUND(F, a, b, c, d, in[4] + K1, 3); ROUND(F, d, a, b, c, in[5] + K1, 7); ROUND(F, c, d, a, b, in[6] + K1, 11); ROUND(F, b, c, d, a, in[7] + K1, 19); /* Round 2 */ ROUND(G, a, b, c, d, in[1] + K2, 3); ROUND(G, d, a, b, c, in[3] + K2, 5); ROUND(G, c, d, a, b, in[5] + K2, 9); ROUND(G, b, c, d, a, in[7] + K2, 13); ROUND(G, a, b, c, d, in[0] + K2, 3); ROUND(G, d, a, b, c, in[2] + K2, 5); ROUND(G, c, d, a, b, in[4] + K2, 9); ROUND(G, b, c, d, a, in[6] + K2, 13); /* Round 3 */ ROUND(H, a, b, c, d, in[3] + K3, 3); ROUND(H, d, a, b, c, in[7] + K3, 9); ROUND(H, c, d, a, b, in[2] + K3, 11); ROUND(H, b, c, d, a, in[6] + K3, 15); ROUND(H, a, b, c, d, in[1] + K3, 3); ROUND(H, d, a, b, c, in[5] + K3, 9); ROUND(H, c, d, a, b, in[0] + K3, 11); ROUND(H, b, c, d, a, in[4] + K3, 15); buf[0] += a; buf[1] += b; buf[2] += c; buf[3] += d; } #undef ROUND #undef F #undef G #undef H #undef K1 #undef K2 #undef K3 /* The old legacy hash */ static ext2_dirhash_t dx_hack_hash (const char *name, int len, int unsigned_flag) { __u32 hash, hash0 = 0x12a3fe2d, hash1 = 0x37abe8f9; const unsigned char *ucp = (const unsigned char *) name; const signed char *scp = (const signed char *) name; int c; while (len--) { if (unsigned_flag) c = (int) *ucp++; else c = (int) *scp++; hash = hash1 + (hash0 ^ (c * 7152373)); if (hash & 0x80000000) hash -= 0x7fffffff; hash1 = hash0; hash0 = hash; } return (hash0 << 1); } static void str2hashbuf(const char *msg, int len, __u32 *buf, int num, int unsigned_flag) { __u32 pad, val; int i, c; const unsigned char *ucp = (const unsigned char *) msg; const signed char *scp = (const signed char *) msg; pad = (__u32)len | ((__u32)len << 8); pad |= pad << 16; val = pad; if (len > num*4) len = num * 4; for (i=0; i < len; i++) { if ((i % 4) == 0) val = pad; if (unsigned_flag) c = (int) ucp[i]; else c = (int) scp[i]; val = c + (val << 8); if ((i % 4) == 3) { *buf++ = val; val = pad; num--; } } if (--num >= 0) *buf++ = val; while (--num >= 0) *buf++ = pad; } /* * Returns the hash of a filename. If len is 0 and name is NULL, then * this function can be used to test whether or not a hash version is * supported. * * The seed is an 4 longword (32 bits) "secret" which can be used to * uniquify a hash. If the seed is all zero's, then some default seed * may be used. * * A particular hash version specifies whether or not the seed is * represented, and whether or not the returned hash is 32 bits or 64 * bits. 32 bit hashes will return 0 for the minor hash. */ errcode_t ext2fs_dirhash(int version, const char *name, int len, const __u32 *seed, ext2_dirhash_t *ret_hash, ext2_dirhash_t *ret_minor_hash) { __u32 hash; __u32 minor_hash = 0; const char *p; int i; __u32 in[8], buf[4]; int unsigned_flag = 0; /* Initialize the default seed for the hash checksum functions */ buf[0] = 0x67452301; buf[1] = 0xefcdab89; buf[2] = 0x98badcfe; buf[3] = 0x10325476; /* Check to see if the seed is all zero's */ if (seed) { for (i=0; i < 4; i++) { if (seed[i]) break; } if (i < 4) memcpy(buf, seed, sizeof(buf)); } switch (version) { case EXT2_HASH_LEGACY_UNSIGNED: unsigned_flag++; case EXT2_HASH_LEGACY: hash = dx_hack_hash(name, len, unsigned_flag); break; case EXT2_HASH_HALF_MD4_UNSIGNED: unsigned_flag++; case EXT2_HASH_HALF_MD4: p = name; while (len > 0) { str2hashbuf(p, len, in, 8, unsigned_flag); halfMD4Transform(buf, in); len -= 32; p += 32; } minor_hash = buf[2]; hash = buf[1]; break; case EXT2_HASH_TEA_UNSIGNED: unsigned_flag++; case EXT2_HASH_TEA: p = name; while (len > 0) { str2hashbuf(p, len, in, 4, unsigned_flag); TEA_transform(buf, in); len -= 16; p += 16; } hash = buf[0]; minor_hash = buf[1]; break; default: *ret_hash = 0; return EXT2_ET_DIRHASH_UNSUPP; } *ret_hash = hash & ~1; if (ret_minor_hash) *ret_minor_hash = minor_hash; return 0; } e2fsprogs-1.41.14/lib/ext2fs/dosio.c0000644031104000366760000002430711405316370015102 0ustar tytso/* * dosio.c -- Disk I/O module for the ext2fs/DOS library. * * Copyright (c) 1997 by Theodore Ts'o. * * Copyright (c) 1997 Mark Habersack * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #include #include #include #include #include #ifdef HAVE_ERRNO_H #include #endif #include #include "utils.h" #include "dosio.h" #include "et/com_err.h" #include "ext2_err.h" #include "ext2fs/io.h" /* * Some helper macros */ #define LINUX_EXT2FS 0x83 #define LINUX_SWAP 0x82 #define WRITE_ERR(_msg_) write(2, _msg_, strlen(_msg_)) #define WRITE_ERR_S(_msg_) write(2, _msg_, sizeof(_msg_)) /* * Exported variables */ unsigned long _dio_error; unsigned long _dio_hw_error; /* * Array of all opened partitions */ static PARTITION **partitions = NULL; static unsigned short npart = 0; /* Number of mapped partitions */ static PARTITION *active = NULL; /* * I/O Manager routine prototypes */ static errcode_t dos_open(const char *dev, int flags, io_channel *channel); static errcode_t dos_close(io_channel channel); static errcode_t dos_set_blksize(io_channel channel, int blksize); static errcode_t dos_read_blk(io_channel channel, unsigned long block, int count, void *buf); static errcode_t dos_write_blk(io_channel channel, unsigned long block, int count, const void *buf); static errcode_t dos_flush(io_channel channel); static struct struct_io_manager struct_dos_manager = { EXT2_ET_MAGIC_IO_MANAGER, "DOS I/O Manager", dos_open, dos_close, dos_set_blksize, dos_read_blk, dos_write_blk, dos_flush }; io_manager dos_io_manager = &struct_dos_manager; /* * Macro taken from unix_io.c */ /* * For checking structure magic numbers... */ #define EXT2_CHECK_MAGIC(struct, code) \ if ((struct)->magic != (code)) return (code) /* * Calculates a CHS address of a sector from its LBA * offset for the given partition. */ static void lba2chs(unsigned long lba_addr, CHS *chs, PARTITION *part) { unsigned long abss; chs->offset = lba_addr & 0x000001FF; abss = (lba_addr >> 9) + part->start; chs->cyl = abss / (part->sects * part->heads); chs->head = (abss / part->sects) % part->heads; chs->sector = (abss % part->sects) + 1; } #ifdef __TURBOC__ #pragma argsused #endif /* * Scans the passed partition table looking for *pno partition * that has LINUX_EXT2FS type. * * TODO: * For partition numbers >5 Linux uses DOS extended partitions - * dive into them an return an appropriate entry. Also dive into * extended partitions when scanning for a first Linux/ext2fs. */ static PTABLE_ENTRY *scan_partition_table(PTABLE_ENTRY *pentry, unsigned short phys, unsigned char *pno) { unsigned i; if(*pno != 0xFF && *pno >= 5) return NULL; /* We don't support extended partitions for now */ if(*pno != 0xFF) { if(pentry[*pno].type == LINUX_EXT2FS) return &pentry[*pno]; else { if(!pentry[*pno].type) *pno = 0xFE; else if(pentry[*pno].type == LINUX_SWAP) *pno = 0xFD; return NULL; } } for(i = 0; i < 4; i++) if(pentry[i].type == LINUX_EXT2FS) { *pno = i; return &pentry[i]; } return NULL; } /* * Allocate libext2fs structures associated with I/O manager */ static io_channel alloc_io_channel(PARTITION *part) { io_channel ioch; ioch = (io_channel)malloc(sizeof(struct struct_io_channel)); if (!ioch) return NULL; memset(ioch, 0, sizeof(struct struct_io_channel)); ioch->magic = EXT2_ET_MAGIC_IO_CHANNEL; ioch->manager = dos_io_manager; ioch->name = (char *)malloc(strlen(part->dev)+1); if (!ioch->name) { free(ioch); return NULL; } strcpy(ioch->name, part->dev); ioch->private_data = part; ioch->block_size = 1024; /* The smallest ext2fs block size */ ioch->read_error = 0; ioch->write_error = 0; return ioch; } #ifdef __TURBOC__ #pragma argsused #endif /* * Open the 'name' partition, initialize all information structures * we need to keep and create libext2fs I/O manager. */ static errcode_t dos_open(const char *dev, int flags, io_channel *channel) { unsigned char *tmp, sec[512]; PARTITION *part; PTABLE_ENTRY *pent; PARTITION **newparts; if(!dev) { _dio_error = ERR_BADDEV; return EXT2_ET_BAD_DEVICE_NAME; } /* * First check whether the dev name is OK */ tmp = (unsigned char*)strrchr(dev, '/'); if(!tmp) { _dio_error = ERR_BADDEV; return EXT2_ET_BAD_DEVICE_NAME; } *tmp = 0; if(strcmp(dev, "/dev")) { _dio_error = ERR_BADDEV; return EXT2_ET_BAD_DEVICE_NAME; } *tmp++ = '/'; /* * Check whether the partition data is already in cache */ part = (PARTITION*)malloc(sizeof(PARTITION)); if (!part) return ENOMEM; { int i = 0; for(;i < npart; i++) if(!strcmp(partitions[i]->dev, dev)) { /* Found it! Make it the active one */ active = partitions[i]; *channel = alloc_io_channel(active); if (!*channel) return ENOMEM; return 0; } } /* * Drive number & optionally partn number */ switch(tmp[0]) { case 'h': case 's': part->phys = 0x80; part->phys += toupper(tmp[2]) - 'A'; /* * Do we have the partition number? */ if(tmp[3]) part->pno = isdigit((int)tmp[3]) ? tmp[3] - '0' - 1: 0; else part->pno = 0xFF; break; case 'f': if(tmp[2]) part->phys = isdigit((int)tmp[2]) ? tmp[2] - '0' : 0; else part->phys = 0x00; /* We'll assume /dev/fd0 */ break; default: _dio_error = ERR_BADDEV; return ENODEV; } if(part->phys < 0x80) { /* We don't support floppies for now */ _dio_error = ERR_NOTSUPP; return EINVAL; } part->dev = strdup(dev); /* * Get drive's geometry */ _dio_hw_error = biosdisk(DISK_GET_GEOMETRY, part->phys, 0, /* head */ 0, /* cylinder */ 1, /* sector */ 1, /* just one sector */ sec); if(!HW_OK()) { _dio_error = ERR_HARDWARE; free(part->dev); free(part); return EFAULT; } /* * Calculate the geometry */ part->cyls = (unsigned short)(((sec[0] >> 6) << 8) + sec[1] + 1); part->heads = sec[3] + 1; part->sects = sec[0] & 0x3F; /* * Now that we know all we need, let's look for the partition */ _dio_hw_error = biosdisk(DISK_READ, part->phys, 0, 0, 1, 1, sec); if(!HW_OK()) { _dio_error = ERR_HARDWARE; free(part->dev); free(part); return EFAULT; } pent = (PTABLE_ENTRY*)&sec[0x1BE]; pent = scan_partition_table(pent, part->phys, &part->pno); if(!pent) { _dio_error = part->pno == 0xFE ? ERR_EMPTYPART : part->pno == 0xFD ? ERR_LINUXSWAP : ERR_NOTEXT2FS; free(part->dev); free(part); return ENODEV; } /* * Calculate the remaining figures */ { unsigned long fsec, fhead, fcyl; fsec = (unsigned long)(pent->start_sec & 0x3F); fhead = (unsigned long)pent->start_head; fcyl = ((pent->start_sec >> 6) << 8) + pent->start_cyl; part->start = fsec + fhead * part->sects + fcyl * (part->heads * part->sects) - 1; part->len = pent->size; } /* * Add the partition to the table */ newparts = (PARTITION**)realloc(partitions, sizeof(PARTITION) * npart); if (!newparts) { free(part); return ENOMEM; } partitions = newparts; partitions[npart++] = active = part; /* * Now alloc all libe2fs structures */ *channel = alloc_io_channel(active); if (!*channel) return ENOMEM; return 0; } static errcode_t dos_close(io_channel channel) { free(channel->name); free(channel); return 0; } static errcode_t dos_set_blksize(io_channel channel, int blksize) { channel->block_size = blksize; return 0; } static errcode_t dos_read_blk(io_channel channel, unsigned long block, int count, void *buf) { PARTITION *part; size_t size; ext2_loff_t loc; CHS chs; EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); part = (PARTITION*)channel->private_data; size = (size_t)((count < 0) ? -count : count * channel->block_size); loc = (ext2_loff_t) block * channel->block_size; lba2chs(loc, &chs, part); /* * Potential bug here: * If DJGPP is used then reads of >18 sectors will fail! * Have to rewrite biosdisk. */ _dio_hw_error = biosdisk(DISK_READ, part->phys, chs.head, chs.cyl, chs.sector, size < 512 ? 1 : size/512, buf); if(!HW_OK()) { _dio_error = ERR_HARDWARE; return EFAULT; } return 0; } static errcode_t dos_write_blk(io_channel channel, unsigned long block, int count, const void *buf) { PARTITION *part; size_t size; ext2_loff_t loc; CHS chs; EXT2_CHECK_MAGIC(channel, EXT2_ET_MAGIC_IO_CHANNEL); part = (PARTITION*)channel->private_data; if(count == 1) size = (size_t)channel->block_size; else { if (count < 0) size = (size_t)-count; else size = (size_t)(count * channel->block_size); } loc = (ext2_loff_t)block * channel->block_size; lba2chs(loc, &chs, part); _dio_hw_error = biosdisk(DISK_WRITE, part->phys, chs.head, chs.cyl, chs.sector, size < 512 ? 1 : size/512, (void*)buf); if(!HW_OK()) { _dio_error = ERR_HARDWARE; return EFAULT; } return 0; } #ifdef __TURBOC__ #pragma argsused #endif static errcode_t dos_flush(io_channel channel) { /* * No buffers, no flush... */ return 0; } e2fsprogs-1.41.14/lib/ext2fs/ext2_fs.h0000644031104000116100000006670111504417000016007 0ustar tytsoeng/* * linux/include/linux/ext2_fs.h * * Copyright (C) 1992, 1993, 1994, 1995 * Remy Card (card@masi.ibp.fr) * Laboratoire MASI - Institut Blaise Pascal * Universite Pierre et Marie Curie (Paris VI) * * from * * linux/include/linux/minix_fs.h * * Copyright (C) 1991, 1992 Linus Torvalds */ #ifndef _LINUX_EXT2_FS_H #define _LINUX_EXT2_FS_H #include /* Changed from linux/types.h */ /* * The second extended filesystem constants/structures */ /* * Define EXT2FS_DEBUG to produce debug messages */ #undef EXT2FS_DEBUG /* * Define EXT2_PREALLOCATE to preallocate data blocks for expanding files */ #define EXT2_PREALLOCATE #define EXT2_DEFAULT_PREALLOC_BLOCKS 8 /* * The second extended file system version */ #define EXT2FS_DATE "95/08/09" #define EXT2FS_VERSION "0.5b" /* * Special inode numbers */ #define EXT2_BAD_INO 1 /* Bad blocks inode */ #define EXT2_ROOT_INO 2 /* Root inode */ #define EXT2_ACL_IDX_INO 3 /* ACL inode */ #define EXT2_ACL_DATA_INO 4 /* ACL inode */ #define EXT2_BOOT_LOADER_INO 5 /* Boot loader inode */ #define EXT2_UNDEL_DIR_INO 6 /* Undelete directory inode */ #define EXT2_RESIZE_INO 7 /* Reserved group descriptors inode */ #define EXT2_JOURNAL_INO 8 /* Journal inode */ #define EXT2_EXCLUDE_INO 9 /* The "exclude" inode, for snapshots */ /* First non-reserved inode for old ext2 filesystems */ #define EXT2_GOOD_OLD_FIRST_INO 11 /* * The second extended file system magic number */ #define EXT2_SUPER_MAGIC 0xEF53 #ifdef __KERNEL__ #define EXT2_SB(sb) (&((sb)->u.ext2_sb)) #else /* Assume that user mode programs are passing in an ext2fs superblock, not * a kernel struct super_block. This will allow us to call the feature-test * macros from user land. */ #define EXT2_SB(sb) (sb) #endif /* * Maximal count of links to a file */ #define EXT2_LINK_MAX 65000 /* * Macro-instructions used to manage several block sizes */ #define EXT2_MIN_BLOCK_LOG_SIZE 10 /* 1024 */ #define EXT2_MAX_BLOCK_LOG_SIZE 16 /* 65536 */ #define EXT2_MIN_BLOCK_SIZE (1 << EXT2_MIN_BLOCK_LOG_SIZE) #define EXT2_MAX_BLOCK_SIZE (1 << EXT2_MAX_BLOCK_LOG_SIZE) #ifdef __KERNEL__ #define EXT2_BLOCK_SIZE(s) ((s)->s_blocksize) #define EXT2_BLOCK_SIZE_BITS(s) ((s)->s_blocksize_bits) #define EXT2_ADDR_PER_BLOCK_BITS(s) (EXT2_SB(s)->addr_per_block_bits) #define EXT2_INODE_SIZE(s) (EXT2_SB(s)->s_inode_size) #define EXT2_FIRST_INO(s) (EXT2_SB(s)->s_first_ino) #else #define EXT2_BLOCK_SIZE(s) (EXT2_MIN_BLOCK_SIZE << (s)->s_log_block_size) #define EXT2_BLOCK_SIZE_BITS(s) ((s)->s_log_block_size + 10) #define EXT2_INODE_SIZE(s) (((s)->s_rev_level == EXT2_GOOD_OLD_REV) ? \ EXT2_GOOD_OLD_INODE_SIZE : (s)->s_inode_size) #define EXT2_FIRST_INO(s) (((s)->s_rev_level == EXT2_GOOD_OLD_REV) ? \ EXT2_GOOD_OLD_FIRST_INO : (s)->s_first_ino) #endif #define EXT2_ADDR_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof(__u32)) /* * Macro-instructions used to manage fragments */ #define EXT2_MIN_FRAG_SIZE EXT2_MIN_BLOCK_SIZE #define EXT2_MAX_FRAG_SIZE EXT2_MAX_BLOCK_SIZE #define EXT2_MIN_FRAG_LOG_SIZE EXT2_MIN_BLOCK_LOG_SIZE #ifdef __KERNEL__ # define EXT2_FRAG_SIZE(s) (EXT2_SB(s)->s_frag_size) # define EXT2_FRAGS_PER_BLOCK(s) (EXT2_SB(s)->s_frags_per_block) #else # define EXT2_FRAG_SIZE(s) (EXT2_MIN_FRAG_SIZE << (s)->s_log_frag_size) # define EXT2_FRAGS_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / EXT2_FRAG_SIZE(s)) #endif /* * ACL structures */ struct ext2_acl_header /* Header of Access Control Lists */ { __u32 aclh_size; __u32 aclh_file_count; __u32 aclh_acle_count; __u32 aclh_first_acle; }; struct ext2_acl_entry /* Access Control List Entry */ { __u32 acle_size; __u16 acle_perms; /* Access permissions */ __u16 acle_type; /* Type of entry */ __u16 acle_tag; /* User or group identity */ __u16 acle_pad1; __u32 acle_next; /* Pointer on next entry for the */ /* same inode or on next free entry */ }; /* * Structure of a blocks group descriptor */ struct ext2_group_desc { __u32 bg_block_bitmap; /* Blocks bitmap block */ __u32 bg_inode_bitmap; /* Inodes bitmap block */ __u32 bg_inode_table; /* Inodes table block */ __u16 bg_free_blocks_count; /* Free blocks count */ __u16 bg_free_inodes_count; /* Free inodes count */ __u16 bg_used_dirs_count; /* Directories count */ __u16 bg_flags; __u32 bg_reserved[2]; __u16 bg_itable_unused; /* Unused inodes count */ __u16 bg_checksum; /* crc16(s_uuid+grouo_num+group_desc)*/ }; struct ext4_group_desc { __u32 bg_block_bitmap; /* Blocks bitmap block */ __u32 bg_inode_bitmap; /* Inodes bitmap block */ __u32 bg_inode_table; /* Inodes table block */ __u16 bg_free_blocks_count; /* Free blocks count */ __u16 bg_free_inodes_count; /* Free inodes count */ __u16 bg_used_dirs_count; /* Directories count */ __u16 bg_flags; __u32 bg_reserved[2]; __u16 bg_itable_unused; /* Unused inodes count */ __u16 bg_checksum; /* crc16(s_uuid+grouo_num+group_desc)*/ __u32 bg_block_bitmap_hi; /* Blocks bitmap block MSB */ __u32 bg_inode_bitmap_hi; /* Inodes bitmap block MSB */ __u32 bg_inode_table_hi; /* Inodes table block MSB */ __u16 bg_free_blocks_count_hi;/* Free blocks count MSB */ __u16 bg_free_inodes_count_hi;/* Free inodes count MSB */ __u16 bg_used_dirs_count_hi; /* Directories count MSB */ __u16 bg_pad; __u32 bg_reserved2[3]; }; #define EXT2_BG_INODE_UNINIT 0x0001 /* Inode table/bitmap not initialized */ #define EXT2_BG_BLOCK_UNINIT 0x0002 /* Block bitmap not initialized */ #define EXT2_BG_INODE_ZEROED 0x0004 /* On-disk itable initialized to zero */ /* * Data structures used by the directory indexing feature * * Note: all of the multibyte integer fields are little endian. */ /* * Note: dx_root_info is laid out so that if it should somehow get * overlaid by a dirent the two low bits of the hash version will be * zero. Therefore, the hash version mod 4 should never be 0. * Sincerely, the paranoia department. */ struct ext2_dx_root_info { __u32 reserved_zero; __u8 hash_version; /* 0 now, 1 at release */ __u8 info_length; /* 8 */ __u8 indirect_levels; __u8 unused_flags; }; #define EXT2_HASH_LEGACY 0 #define EXT2_HASH_HALF_MD4 1 #define EXT2_HASH_TEA 2 #define EXT2_HASH_LEGACY_UNSIGNED 3 /* reserved for userspace lib */ #define EXT2_HASH_HALF_MD4_UNSIGNED 4 /* reserved for userspace lib */ #define EXT2_HASH_TEA_UNSIGNED 5 /* reserved for userspace lib */ #define EXT2_HASH_FLAG_INCOMPAT 0x1 struct ext2_dx_entry { __u32 hash; __u32 block; }; struct ext2_dx_countlimit { __u16 limit; __u16 count; }; /* * Macro-instructions used to manage group descriptors */ #define EXT2_MIN_DESC_SIZE 32 #define EXT2_MIN_DESC_SIZE_64BIT 64 #define EXT2_MAX_DESC_SIZE EXT2_MIN_BLOCK_SIZE #define EXT2_DESC_SIZE(s) \ ((EXT2_SB(s)->s_feature_incompat & EXT4_FEATURE_INCOMPAT_64BIT) ? \ (s)->s_desc_size : EXT2_MIN_DESC_SIZE) #define EXT2_BLOCKS_PER_GROUP(s) (EXT2_SB(s)->s_blocks_per_group) #define EXT2_INODES_PER_GROUP(s) (EXT2_SB(s)->s_inodes_per_group) #define EXT2_INODES_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s)/EXT2_INODE_SIZE(s)) /* limits imposed by 16-bit value gd_free_{blocks,inode}_count */ #define EXT2_MAX_BLOCKS_PER_GROUP(s) ((1 << 16) - 8) #define EXT2_MAX_INODES_PER_GROUP(s) ((1 << 16) - EXT2_INODES_PER_BLOCK(s)) #ifdef __KERNEL__ #define EXT2_DESC_PER_BLOCK(s) (EXT2_SB(s)->s_desc_per_block) #define EXT2_DESC_PER_BLOCK_BITS(s) (EXT2_SB(s)->s_desc_per_block_bits) #else #define EXT2_DESC_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / EXT2_DESC_SIZE(s)) #endif /* * Constants relative to the data blocks */ #define EXT2_NDIR_BLOCKS 12 #define EXT2_IND_BLOCK EXT2_NDIR_BLOCKS #define EXT2_DIND_BLOCK (EXT2_IND_BLOCK + 1) #define EXT2_TIND_BLOCK (EXT2_DIND_BLOCK + 1) #define EXT2_N_BLOCKS (EXT2_TIND_BLOCK + 1) /* * Inode flags */ #define EXT2_SECRM_FL 0x00000001 /* Secure deletion */ #define EXT2_UNRM_FL 0x00000002 /* Undelete */ #define EXT2_COMPR_FL 0x00000004 /* Compress file */ #define EXT2_SYNC_FL 0x00000008 /* Synchronous updates */ #define EXT2_IMMUTABLE_FL 0x00000010 /* Immutable file */ #define EXT2_APPEND_FL 0x00000020 /* writes to file may only append */ #define EXT2_NODUMP_FL 0x00000040 /* do not dump file */ #define EXT2_NOATIME_FL 0x00000080 /* do not update atime */ /* Reserved for compression usage... */ #define EXT2_DIRTY_FL 0x00000100 #define EXT2_COMPRBLK_FL 0x00000200 /* One or more compressed clusters */ #define EXT2_NOCOMPR_FL 0x00000400 /* Access raw compressed data */ #define EXT2_ECOMPR_FL 0x00000800 /* Compression error */ /* End compression flags --- maybe not all used */ #define EXT2_BTREE_FL 0x00001000 /* btree format dir */ #define EXT2_INDEX_FL 0x00001000 /* hash-indexed directory */ #define EXT2_IMAGIC_FL 0x00002000 #define EXT3_JOURNAL_DATA_FL 0x00004000 /* file data should be journaled */ #define EXT2_NOTAIL_FL 0x00008000 /* file tail should not be merged */ #define EXT2_DIRSYNC_FL 0x00010000 /* Synchronous directory modifications */ #define EXT2_TOPDIR_FL 0x00020000 /* Top of directory hierarchies*/ #define EXT4_HUGE_FILE_FL 0x00040000 /* Set to each huge file */ #define EXT4_EXTENTS_FL 0x00080000 /* Inode uses extents */ #define EXT4_EA_INODE_FL 0x00200000 /* Inode used for large EA */ #define EXT4_EOFBLOCKS_FL 0x00400000 /* Blocks allocated beyond EOF */ #define EXT4_SNAPFILE_FL 0x01000000 /* Inode is a snapshot */ #define EXT4_SNAPFILE_DELETED_FL 0x04000000 /* Snapshot is being deleted */ #define EXT4_SNAPFILE_SHRUNK_FL 0x08000000 /* Snapshot shrink has completed */ #define EXT2_RESERVED_FL 0x80000000 /* reserved for ext2 lib */ #define EXT2_FL_USER_VISIBLE 0x004BDFFF /* User visible flags */ #define EXT2_FL_USER_MODIFIABLE 0x004B80FF /* User modifiable flags */ /* * ioctl commands */ /* Used for online resize */ struct ext2_new_group_input { __u32 group; /* Group number for this data */ __u32 block_bitmap; /* Absolute block number of block bitmap */ __u32 inode_bitmap; /* Absolute block number of inode bitmap */ __u32 inode_table; /* Absolute block number of inode table start */ __u32 blocks_count; /* Total number of blocks in this group */ __u16 reserved_blocks; /* Number of reserved blocks in this group */ __u16 unused; /* Number of reserved GDT blocks in group */ }; struct ext4_new_group_input { __u32 group; /* Group number for this data */ __u64 block_bitmap; /* Absolute block number of block bitmap */ __u64 inode_bitmap; /* Absolute block number of inode bitmap */ __u64 inode_table; /* Absolute block number of inode table start */ __u32 blocks_count; /* Total number of blocks in this group */ __u16 reserved_blocks; /* Number of reserved blocks in this group */ __u16 unused; }; #ifdef __GNU__ /* Needed for the Hurd */ #define _IOT_ext2_new_group_input _IOT (_IOTS(__u32), 5, _IOTS(__u16), 2, 0, 0) #endif #define EXT2_IOC_GETFLAGS _IOR('f', 1, long) #define EXT2_IOC_SETFLAGS _IOW('f', 2, long) #define EXT2_IOC_GETVERSION _IOR('v', 1, long) #define EXT2_IOC_SETVERSION _IOW('v', 2, long) #define EXT2_IOC_GETVERSION_NEW _IOR('f', 3, long) #define EXT2_IOC_SETVERSION_NEW _IOW('f', 4, long) #define EXT2_IOC_GROUP_EXTEND _IOW('f', 7, unsigned long) #define EXT2_IOC_GROUP_ADD _IOW('f', 8,struct ext2_new_group_input) #define EXT4_IOC_GROUP_ADD _IOW('f', 8,struct ext4_new_group_input) /* * Structure of an inode on the disk */ struct ext2_inode { __u16 i_mode; /* File mode */ __u16 i_uid; /* Low 16 bits of Owner Uid */ __u32 i_size; /* Size in bytes */ __u32 i_atime; /* Access time */ __u32 i_ctime; /* Inode change time */ __u32 i_mtime; /* Modification time */ __u32 i_dtime; /* Deletion Time */ __u16 i_gid; /* Low 16 bits of Group Id */ __u16 i_links_count; /* Links count */ __u32 i_blocks; /* Blocks count */ __u32 i_flags; /* File flags */ union { struct { __u32 l_i_version; /* was l_i_reserved1 */ } linux1; struct { __u32 h_i_translator; } hurd1; } osd1; /* OS dependent 1 */ __u32 i_block[EXT2_N_BLOCKS];/* Pointers to blocks */ __u32 i_generation; /* File version (for NFS) */ __u32 i_file_acl; /* File ACL */ __u32 i_dir_acl; /* Directory ACL */ __u32 i_faddr; /* Fragment address */ union { struct { __u16 l_i_blocks_hi; __u16 l_i_file_acl_high; __u16 l_i_uid_high; /* these 2 fields */ __u16 l_i_gid_high; /* were reserved2[0] */ __u32 l_i_reserved2; } linux2; struct { __u8 h_i_frag; /* Fragment number */ __u8 h_i_fsize; /* Fragment size */ __u16 h_i_mode_high; __u16 h_i_uid_high; __u16 h_i_gid_high; __u32 h_i_author; } hurd2; } osd2; /* OS dependent 2 */ }; /* * Permanent part of an large inode on the disk */ struct ext2_inode_large { __u16 i_mode; /* File mode */ __u16 i_uid; /* Low 16 bits of Owner Uid */ __u32 i_size; /* Size in bytes */ __u32 i_atime; /* Access time */ __u32 i_ctime; /* Inode Change time */ __u32 i_mtime; /* Modification time */ __u32 i_dtime; /* Deletion Time */ __u16 i_gid; /* Low 16 bits of Group Id */ __u16 i_links_count; /* Links count */ __u32 i_blocks; /* Blocks count */ __u32 i_flags; /* File flags */ union { struct { __u32 l_i_version; /* was l_i_reserved1 */ } linux1; struct { __u32 h_i_translator; } hurd1; } osd1; /* OS dependent 1 */ __u32 i_block[EXT2_N_BLOCKS];/* Pointers to blocks */ __u32 i_generation; /* File version (for NFS) */ __u32 i_file_acl; /* File ACL */ __u32 i_dir_acl; /* Directory ACL */ __u32 i_faddr; /* Fragment address */ union { struct { __u16 l_i_blocks_hi; __u16 l_i_file_acl_high; __u16 l_i_uid_high; /* these 2 fields */ __u16 l_i_gid_high; /* were reserved2[0] */ __u32 l_i_reserved2; } linux2; struct { __u8 h_i_frag; /* Fragment number */ __u8 h_i_fsize; /* Fragment size */ __u16 h_i_mode_high; __u16 h_i_uid_high; __u16 h_i_gid_high; __u32 h_i_author; } hurd2; } osd2; /* OS dependent 2 */ __u16 i_extra_isize; __u16 i_pad1; __u32 i_ctime_extra; /* extra Change time (nsec << 2 | epoch) */ __u32 i_mtime_extra; /* extra Modification time (nsec << 2 | epoch) */ __u32 i_atime_extra; /* extra Access time (nsec << 2 | epoch) */ __u32 i_crtime; /* File creation time */ __u32 i_crtime_extra; /* extra File creation time (nsec << 2 | epoch)*/ __u32 i_version_hi; /* high 32 bits for 64-bit version */ }; #define i_size_high i_dir_acl #if defined(__KERNEL__) || defined(__linux__) #define i_reserved1 osd1.linux1.l_i_reserved1 #define i_frag osd2.linux2.l_i_frag #define i_fsize osd2.linux2.l_i_fsize #define i_uid_low i_uid #define i_gid_low i_gid #define i_uid_high osd2.linux2.l_i_uid_high #define i_gid_high osd2.linux2.l_i_gid_high #define i_reserved2 osd2.linux2.l_i_reserved2 #else #if defined(__GNU__) #define i_translator osd1.hurd1.h_i_translator #define i_frag osd2.hurd2.h_i_frag; #define i_fsize osd2.hurd2.h_i_fsize; #define i_uid_high osd2.hurd2.h_i_uid_high #define i_gid_high osd2.hurd2.h_i_gid_high #define i_author osd2.hurd2.h_i_author #endif /* __GNU__ */ #endif /* defined(__KERNEL__) || defined(__linux__) */ #define inode_uid(inode) ((inode).i_uid | (inode).osd2.linux2.l_i_uid_high << 16) #define inode_gid(inode) ((inode).i_gid | (inode).osd2.linux2.l_i_gid_high << 16) #define ext2fs_set_i_uid_high(inode,x) ((inode).osd2.linux2.l_i_uid_high = (x)) #define ext2fs_set_i_gid_high(inode,x) ((inode).osd2.linux2.l_i_gid_high = (x)) /* * File system states */ #define EXT2_VALID_FS 0x0001 /* Unmounted cleanly */ #define EXT2_ERROR_FS 0x0002 /* Errors detected */ #define EXT3_ORPHAN_FS 0x0004 /* Orphans being recovered */ /* * Misc. filesystem flags */ #define EXT2_FLAGS_SIGNED_HASH 0x0001 /* Signed dirhash in use */ #define EXT2_FLAGS_UNSIGNED_HASH 0x0002 /* Unsigned dirhash in use */ #define EXT2_FLAGS_TEST_FILESYS 0x0004 /* OK for use on development code */ #define EXT2_FLAGS_IS_SNAPSHOT 0x0010 /* This is a snapshot image */ #define EXT2_FLAGS_FIX_SNAPSHOT 0x0020 /* Snapshot inodes corrupted */ #define EXT2_FLAGS_FIX_EXCLUDE 0x0040 /* Exclude bitmaps corrupted */ /* * Mount flags */ #define EXT2_MOUNT_CHECK 0x0001 /* Do mount-time checks */ #define EXT2_MOUNT_GRPID 0x0004 /* Create files with directory's group */ #define EXT2_MOUNT_DEBUG 0x0008 /* Some debugging messages */ #define EXT2_MOUNT_ERRORS_CONT 0x0010 /* Continue on errors */ #define EXT2_MOUNT_ERRORS_RO 0x0020 /* Remount fs ro on errors */ #define EXT2_MOUNT_ERRORS_PANIC 0x0040 /* Panic on errors */ #define EXT2_MOUNT_MINIX_DF 0x0080 /* Mimics the Minix statfs */ #define EXT2_MOUNT_NO_UID32 0x0200 /* Disable 32-bit UIDs */ #define clear_opt(o, opt) o &= ~EXT2_MOUNT_##opt #define set_opt(o, opt) o |= EXT2_MOUNT_##opt #define test_opt(sb, opt) (EXT2_SB(sb)->s_mount_opt & \ EXT2_MOUNT_##opt) /* * Maximal mount counts between two filesystem checks */ #define EXT2_DFL_MAX_MNT_COUNT 20 /* Allow 20 mounts */ #define EXT2_DFL_CHECKINTERVAL 0 /* Don't use interval check */ /* * Behaviour when detecting errors */ #define EXT2_ERRORS_CONTINUE 1 /* Continue execution */ #define EXT2_ERRORS_RO 2 /* Remount fs read-only */ #define EXT2_ERRORS_PANIC 3 /* Panic */ #define EXT2_ERRORS_DEFAULT EXT2_ERRORS_CONTINUE #if (__GNUC__ >= 4) #define ext4_offsetof(TYPE,MEMBER) __builtin_offsetof(TYPE,MEMBER) #else #define ext4_offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) #endif /* * Structure of the super block */ struct ext2_super_block { __u32 s_inodes_count; /* Inodes count */ __u32 s_blocks_count; /* Blocks count */ __u32 s_r_blocks_count; /* Reserved blocks count */ __u32 s_free_blocks_count; /* Free blocks count */ __u32 s_free_inodes_count; /* Free inodes count */ __u32 s_first_data_block; /* First Data Block */ __u32 s_log_block_size; /* Block size */ __s32 s_log_frag_size; /* Fragment size */ __u32 s_blocks_per_group; /* # Blocks per group */ __u32 s_frags_per_group; /* # Fragments per group */ __u32 s_inodes_per_group; /* # Inodes per group */ __u32 s_mtime; /* Mount time */ __u32 s_wtime; /* Write time */ __u16 s_mnt_count; /* Mount count */ __s16 s_max_mnt_count; /* Maximal mount count */ __u16 s_magic; /* Magic signature */ __u16 s_state; /* File system state */ __u16 s_errors; /* Behaviour when detecting errors */ __u16 s_minor_rev_level; /* minor revision level */ __u32 s_lastcheck; /* time of last check */ __u32 s_checkinterval; /* max. time between checks */ __u32 s_creator_os; /* OS */ __u32 s_rev_level; /* Revision level */ __u16 s_def_resuid; /* Default uid for reserved blocks */ __u16 s_def_resgid; /* Default gid for reserved blocks */ /* * These fields are for EXT2_DYNAMIC_REV superblocks only. * * Note: the difference between the compatible feature set and * the incompatible feature set is that if there is a bit set * in the incompatible feature set that the kernel doesn't * know about, it should refuse to mount the filesystem. * * e2fsck's requirements are more strict; if it doesn't know * about a feature in either the compatible or incompatible * feature set, it must abort and not try to meddle with * things it doesn't understand... */ __u32 s_first_ino; /* First non-reserved inode */ __u16 s_inode_size; /* size of inode structure */ __u16 s_block_group_nr; /* block group # of this superblock */ __u32 s_feature_compat; /* compatible feature set */ __u32 s_feature_incompat; /* incompatible feature set */ __u32 s_feature_ro_compat; /* readonly-compatible feature set */ __u8 s_uuid[16]; /* 128-bit uuid for volume */ char s_volume_name[16]; /* volume name */ char s_last_mounted[64]; /* directory where last mounted */ __u32 s_algorithm_usage_bitmap; /* For compression */ /* * Performance hints. Directory preallocation should only * happen if the EXT2_FEATURE_COMPAT_DIR_PREALLOC flag is on. */ __u8 s_prealloc_blocks; /* Nr of blocks to try to preallocate*/ __u8 s_prealloc_dir_blocks; /* Nr to preallocate for dirs */ __u16 s_reserved_gdt_blocks; /* Per group table for online growth */ /* * Journaling support valid if EXT2_FEATURE_COMPAT_HAS_JOURNAL set. */ __u8 s_journal_uuid[16]; /* uuid of journal superblock */ __u32 s_journal_inum; /* inode number of journal file */ __u32 s_journal_dev; /* device number of journal file */ __u32 s_last_orphan; /* start of list of inodes to delete */ __u32 s_hash_seed[4]; /* HTREE hash seed */ __u8 s_def_hash_version; /* Default hash version to use */ __u8 s_jnl_backup_type; /* Default type of journal backup */ __u16 s_desc_size; /* Group desc. size: INCOMPAT_64BIT */ __u32 s_default_mount_opts; __u32 s_first_meta_bg; /* First metablock group */ __u32 s_mkfs_time; /* When the filesystem was created */ __u32 s_jnl_blocks[17]; /* Backup of the journal inode */ __u32 s_blocks_count_hi; /* Blocks count high 32bits */ __u32 s_r_blocks_count_hi; /* Reserved blocks count high 32 bits*/ __u32 s_free_blocks_hi; /* Free blocks count */ __u16 s_min_extra_isize; /* All inodes have at least # bytes */ __u16 s_want_extra_isize; /* New inodes should reserve # bytes */ __u32 s_flags; /* Miscellaneous flags */ __u16 s_raid_stride; /* RAID stride */ __u16 s_mmp_interval; /* # seconds to wait in MMP checking */ __u64 s_mmp_block; /* Block for multi-mount protection */ __u32 s_raid_stripe_width; /* blocks on all data disks (N*stride)*/ __u8 s_log_groups_per_flex; /* FLEX_BG group size */ __u8 s_reserved_char_pad; __u16 s_reserved_pad; /* Padding to next 32bits */ __u64 s_kbytes_written; /* nr of lifetime kilobytes written */ __u32 s_snapshot_inum; /* Inode number of active snapshot */ __u32 s_snapshot_id; /* sequential ID of active snapshot */ __u64 s_snapshot_r_blocks_count; /* reserved blocks for active snapshot's future use */ __u32 s_snapshot_list; /* inode number of the head of the on-disk snapshot list */ #define EXT4_S_ERR_START ext4_offsetof(struct ext2_super_block, s_error_count) __u32 s_error_count; /* number of fs errors */ __u32 s_first_error_time; /* first time an error happened */ __u32 s_first_error_ino; /* inode involved in first error */ __u64 s_first_error_block; /* block involved of first error */ __u8 s_first_error_func[32]; /* function where the error happened */ __u32 s_first_error_line; /* line number where error happened */ __u32 s_last_error_time; /* most recent time of an error */ __u32 s_last_error_ino; /* inode involved in last error */ __u32 s_last_error_line; /* line number where error happened */ __u64 s_last_error_block; /* block involved of last error */ __u8 s_last_error_func[32]; /* function where the error happened */ #define EXT4_S_ERR_END ext4_offsetof(struct ext2_super_block, s_mount_opts) __u8 s_mount_opts[64]; __u32 s_reserved[112]; /* Padding to the end of the block */ }; #define EXT4_S_ERR_LEN (EXT4_S_ERR_END - EXT4_S_ERR_START) /* * Codes for operating systems */ #define EXT2_OS_LINUX 0 #define EXT2_OS_HURD 1 #define EXT2_OBSO_OS_MASIX 2 #define EXT2_OS_FREEBSD 3 #define EXT2_OS_LITES 4 /* * Revision levels */ #define EXT2_GOOD_OLD_REV 0 /* The good old (original) format */ #define EXT2_DYNAMIC_REV 1 /* V2 format w/ dynamic inode sizes */ #define EXT2_CURRENT_REV EXT2_GOOD_OLD_REV #define EXT2_MAX_SUPP_REV EXT2_DYNAMIC_REV #define EXT2_GOOD_OLD_INODE_SIZE 128 /* * Journal inode backup types */ #define EXT3_JNL_BACKUP_BLOCKS 1 /* * Feature set definitions */ #define EXT2_HAS_COMPAT_FEATURE(sb,mask) \ ( EXT2_SB(sb)->s_feature_compat & (mask) ) #define EXT2_HAS_RO_COMPAT_FEATURE(sb,mask) \ ( EXT2_SB(sb)->s_feature_ro_compat & (mask) ) #define EXT2_HAS_INCOMPAT_FEATURE(sb,mask) \ ( EXT2_SB(sb)->s_feature_incompat & (mask) ) #define EXT2_FEATURE_COMPAT_DIR_PREALLOC 0x0001 #define EXT2_FEATURE_COMPAT_IMAGIC_INODES 0x0002 #define EXT3_FEATURE_COMPAT_HAS_JOURNAL 0x0004 #define EXT2_FEATURE_COMPAT_EXT_ATTR 0x0008 #define EXT2_FEATURE_COMPAT_RESIZE_INODE 0x0010 #define EXT2_FEATURE_COMPAT_DIR_INDEX 0x0020 #define EXT2_FEATURE_COMPAT_LAZY_BG 0x0040 #define EXT2_FEATURE_COMPAT_EXCLUDE_INODE 0x0080 #define EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER 0x0001 #define EXT2_FEATURE_RO_COMPAT_LARGE_FILE 0x0002 /* #define EXT2_FEATURE_RO_COMPAT_BTREE_DIR 0x0004 not used */ #define EXT4_FEATURE_RO_COMPAT_HUGE_FILE 0x0008 #define EXT4_FEATURE_RO_COMPAT_GDT_CSUM 0x0010 #define EXT4_FEATURE_RO_COMPAT_DIR_NLINK 0x0020 #define EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE 0x0040 #define EXT4_FEATURE_RO_COMPAT_HAS_SNAPSHOT 0x0080 #define EXT2_FEATURE_INCOMPAT_COMPRESSION 0x0001 #define EXT2_FEATURE_INCOMPAT_FILETYPE 0x0002 #define EXT3_FEATURE_INCOMPAT_RECOVER 0x0004 /* Needs recovery */ #define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV 0x0008 /* Journal device */ #define EXT2_FEATURE_INCOMPAT_META_BG 0x0010 #define EXT3_FEATURE_INCOMPAT_EXTENTS 0x0040 #define EXT4_FEATURE_INCOMPAT_64BIT 0x0080 #define EXT4_FEATURE_INCOMPAT_MMP 0x0100 #define EXT4_FEATURE_INCOMPAT_FLEX_BG 0x0200 #define EXT4_FEATURE_INCOMPAT_EA_INODE 0x0400 #define EXT4_FEATURE_INCOMPAT_DIRDATA 0x1000 #define EXT2_FEATURE_COMPAT_SUPP 0 #define EXT2_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE) #define EXT2_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \ EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \ EXT4_FEATURE_RO_COMPAT_DIR_NLINK| \ EXT2_FEATURE_RO_COMPAT_BTREE_DIR) /* * Default values for user and/or group using reserved blocks */ #define EXT2_DEF_RESUID 0 #define EXT2_DEF_RESGID 0 /* * Default mount options */ #define EXT2_DEFM_DEBUG 0x0001 #define EXT2_DEFM_BSDGROUPS 0x0002 #define EXT2_DEFM_XATTR_USER 0x0004 #define EXT2_DEFM_ACL 0x0008 #define EXT2_DEFM_UID16 0x0010 #define EXT3_DEFM_JMODE 0x0060 #define EXT3_DEFM_JMODE_DATA 0x0020 #define EXT3_DEFM_JMODE_ORDERED 0x0040 #define EXT3_DEFM_JMODE_WBACK 0x0060 #define EXT4_DEFM_NOBARRIER 0x0100 #define EXT4_DEFM_BLOCK_VALIDITY 0x0200 #define EXT4_DEFM_DISCARD 0x0400 #define EXT4_DEFM_NODELALLOC 0x0800 /* * Structure of a directory entry */ #define EXT2_NAME_LEN 255 struct ext2_dir_entry { __u32 inode; /* Inode number */ __u16 rec_len; /* Directory entry length */ __u16 name_len; /* Name length */ char name[EXT2_NAME_LEN]; /* File name */ }; /* * The new version of the directory entry. Since EXT2 structures are * stored in intel byte order, and the name_len field could never be * bigger than 255 chars, it's safe to reclaim the extra byte for the * file_type field. */ struct ext2_dir_entry_2 { __u32 inode; /* Inode number */ __u16 rec_len; /* Directory entry length */ __u8 name_len; /* Name length */ __u8 file_type; char name[EXT2_NAME_LEN]; /* File name */ }; /* * Ext2 directory file types. Only the low 3 bits are used. The * other bits are reserved for now. */ #define EXT2_FT_UNKNOWN 0 #define EXT2_FT_REG_FILE 1 #define EXT2_FT_DIR 2 #define EXT2_FT_CHRDEV 3 #define EXT2_FT_BLKDEV 4 #define EXT2_FT_FIFO 5 #define EXT2_FT_SOCK 6 #define EXT2_FT_SYMLINK 7 #define EXT2_FT_MAX 8 /* * EXT2_DIR_PAD defines the directory entries boundaries * * NOTE: It must be a multiple of 4 */ #define EXT2_DIR_PAD 4 #define EXT2_DIR_ROUND (EXT2_DIR_PAD - 1) #define EXT2_DIR_REC_LEN(name_len) (((name_len) + 8 + EXT2_DIR_ROUND) & \ ~EXT2_DIR_ROUND) /* * This structure will be used for multiple mount protection. It will be * written into the block number saved in the s_mmp_block field in the * superblock. */ #define EXT2_MMP_MAGIC 0x004D4D50 /* ASCII for MMP */ #define EXT2_MMP_CLEAN 0xFF4D4D50 /* Value of mmp_seq for clean unmount */ #define EXT2_MMP_FSCK_ON 0xE24D4D50 /* Value of mmp_seq when being fscked */ struct mmp_struct { __u32 mmp_magic; __u32 mmp_seq; __u64 mmp_time; char mmp_nodename[64]; char mmp_bdevname[32]; __u16 mmp_interval; __u16 mmp_pad1; __u32 mmp_pad2; }; /* * Interval in number of seconds to update the MMP sequence number. */ #define EXT2_MMP_DEF_INTERVAL 5 #endif /* _LINUX_EXT2_FS_H */ e2fsprogs-1.41.14/lib/ext2fs/lookup.c0000644031104000366760000000264211405316370015274 0ustar tytso/* * lookup.c --- ext2fs directory lookup operations * * Copyright (C) 1993, 1994, 1994, 1995 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #include "ext2_fs.h" #include "ext2fs.h" struct lookup_struct { const char *name; int len; ext2_ino_t *inode; int found; }; #ifdef __TURBOC__ #pragma argsused #endif static int lookup_proc(struct ext2_dir_entry *dirent, int offset EXT2FS_ATTR((unused)), int blocksize EXT2FS_ATTR((unused)), char *buf EXT2FS_ATTR((unused)), void *priv_data) { struct lookup_struct *ls = (struct lookup_struct *) priv_data; if (ls->len != (dirent->name_len & 0xFF)) return 0; if (strncmp(ls->name, dirent->name, (dirent->name_len & 0xFF))) return 0; *ls->inode = dirent->inode; ls->found++; return DIRENT_ABORT; } errcode_t ext2fs_lookup(ext2_filsys fs, ext2_ino_t dir, const char *name, int namelen, char *buf, ext2_ino_t *inode) { errcode_t retval; struct lookup_struct ls; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); ls.name = name; ls.len = namelen; ls.inode = inode; ls.found = 0; retval = ext2fs_dir_iterate(fs, dir, 0, buf, lookup_proc, &ls); if (retval) return retval; return (ls.found) ? 0 : EXT2_ET_FILE_NOT_FOUND; } e2fsprogs-1.41.14/lib/ext2fs/read_bb_file.c0000644031104000116100000000414211504417000016774 0ustar tytsoeng/* * read_bb_file.c --- read a list of bad blocks from a FILE * * * Copyright (C) 1994, 1995, 2000 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #include #include #if HAVE_SYS_STAT_H #include #endif #if HAVE_SYS_TYPES_H #include #endif #include "ext2_fs.h" #include "ext2fs.h" /* * Reads a list of bad blocks from a FILE * */ errcode_t ext2fs_read_bb_FILE2(ext2_filsys fs, FILE *f, ext2_badblocks_list *bb_list, void *priv_data, void (*invalid)(ext2_filsys fs, blk_t blk, char *badstr, void *priv_data)) { errcode_t retval; blk_t blockno; int count; char buf[128]; if (fs) EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); if (!*bb_list) { retval = ext2fs_badblocks_list_create(bb_list, 10); if (retval) return retval; } while (!feof (f)) { if (fgets(buf, sizeof(buf), f) == NULL) break; count = sscanf(buf, "%u", &blockno); if (count <= 0) continue; if (fs && ((blockno < fs->super->s_first_data_block) || (blockno >= fs->super->s_blocks_count))) { if (invalid) (invalid)(fs, blockno, buf, priv_data); continue; } retval = ext2fs_badblocks_list_add(*bb_list, blockno); if (retval) return retval; } return 0; } struct compat_struct { void (*invalid)(ext2_filsys, blk_t); }; static void call_compat_invalid(ext2_filsys fs, blk_t blk, char *badstr EXT2FS_ATTR((unused)), void *priv_data) { struct compat_struct *st; st = (struct compat_struct *) priv_data; if (st->invalid) (st->invalid)(fs, blk); } /* * Reads a list of bad blocks from a FILE * */ errcode_t ext2fs_read_bb_FILE(ext2_filsys fs, FILE *f, ext2_badblocks_list *bb_list, void (*invalid)(ext2_filsys fs, blk_t blk)) { struct compat_struct st; st.invalid = invalid; return ext2fs_read_bb_FILE2(fs, f, bb_list, &st, call_compat_invalid); } e2fsprogs-1.41.14/lib/ext2fs/extent_dbg.ct0000644031104000366760000000273711240667355016310 0ustar tytso# # Copyright (C) 1993 Theodore Ts'o. This file may be redistributed # under the terms of the GNU Public License. # command_table extent_cmds; request do_inode, "Open an inode", inode; request do_current_node, "Current extent node", current_node, current; request do_root_node, "Goto root extent", root_node, root; request do_last_leaf, "Goto last leaf", last_leaf; request do_first_sib, "Goto first sibling", first_sibling, first_sib; request do_last_sib, "Goto last sibling", last_sibling, last_sib; request do_next_sib, "Goto next sibling", next_sibling, next_sib, ns; request do_prev_sib, "Goto previous sibling", prev_sibling, prev_sib, ps; request do_next_leaf, "Goto next leaf", next_leaf, nl; request do_prev_leaf, "Goto previous leaf", prev_leaf, pl; request do_next, "Goto next node", next, n; request do_prev, "Goto previous node", previous, prev, p; request do_up, "Up node", up_node, up, u; request do_down, "Down node", down_node, down, d; request do_delete_node, "Delete node", delete_node, delete; request do_insert_node, "Insert node", insert_node, insert; request do_split_node, "Split node", split_node, split; request do_set_bmap, "Set block mapping", set_bmap; request do_replace_node, "Insert node", replace_node, replace; request do_print_all, "Iterate over all nodes and print them", print_all, all; request do_goto_block, "Goto extent containing specified block", goto_block, goto; request do_info, "Print extent info", info; end; e2fsprogs-1.41.14/lib/ext2fs/tst_getsectsize.c0000644031104000366760000000214011405316370017177 0ustar tytso/* * tst_getsize.c --- this function tests the getsize function * * Copyright (C) 1997 by Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #include #include #include #include #if HAVE_ERRNO_H #include #endif #include "ext2_fs.h" #include "ext2fs.h" int main(int argc, char **argv) { int lsectsize, psectsize; int retval; if (argc < 2) { fprintf(stderr, "Usage: %s device\n", argv[0]); exit(1); } retval = ext2fs_get_device_sectsize(argv[1], &lsectsize); if (retval) { com_err(argv[0], retval, "while calling ext2fs_get_device_sectsize"); exit(1); } retval = ext2fs_get_device_phys_sectsize(argv[1], &psectsize); if (retval) { com_err(argv[0], retval, "while calling ext2fs_get_device_phys_sectsize"); exit(1); } printf("Device %s has logical/physical sector size of %d/%d.\n", argv[1], lsectsize, psectsize); exit(0); } e2fsprogs-1.41.14/lib/ext2fs/tdb.h0000644031104000366760000002116311374366235014552 0ustar tytso#ifndef __TDB_H__ #define __TDB_H__ /* Unix SMB/CIFS implementation. trivial database library Copyright (C) Andrew Tridgell 1999-2004 ** NOTE! The following LGPL license applies to the tdb ** library. This does NOT imply that all of Samba is released ** under the LGPL This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifdef __cplusplus extern "C" { #endif /* flags to tdb_store() */ #define TDB_REPLACE 1 #define TDB_INSERT 2 #define TDB_MODIFY 3 /* flags for tdb_open() */ #define TDB_DEFAULT 0 /* just a readability place holder */ #define TDB_CLEAR_IF_FIRST 1 #define TDB_INTERNAL 2 /* don't store on disk */ #define TDB_NOLOCK 4 /* don't do any locking */ #define TDB_NOMMAP 8 /* don't use mmap */ #define TDB_CONVERT 16 /* convert endian (internal use) */ #define TDB_BIGENDIAN 32 /* header is big-endian (internal use) */ #define TDB_NOSYNC 64 /* don't use synchronous transactions */ #define TDB_SEQNUM 128 /* maintain a sequence number */ #define TDB_ERRCODE(code, ret) ((tdb->ecode = (code)), ret) /* error codes */ enum TDB_ERROR {TDB_SUCCESS=0, TDB_ERR_CORRUPT, TDB_ERR_IO, TDB_ERR_LOCK, TDB_ERR_OOM, TDB_ERR_EXISTS, TDB_ERR_NOLOCK, TDB_ERR_LOCK_TIMEOUT, TDB_ERR_NOEXIST, TDB_ERR_EINVAL, TDB_ERR_RDONLY}; /* debugging uses one of the following levels */ enum tdb_debug_level {TDB_DEBUG_FATAL = 0, TDB_DEBUG_ERROR, TDB_DEBUG_WARNING, TDB_DEBUG_TRACE}; typedef struct TDB_DATA { unsigned char *dptr; size_t dsize; } TDB_DATA; #ifndef PRINTF_ATTRIBUTE #if (__GNUC__ >= 3) /** Use gcc attribute to check printf fns. a1 is the 1-based index of * the parameter containing the format, and a2 the index of the first * argument. Note that some gcc 2.x versions don't handle this * properly **/ #define PRINTF_ATTRIBUTE(a1, a2) __attribute__ ((format (__printf__, a1, a2))) #else #define PRINTF_ATTRIBUTE(a1, a2) #endif #endif /* ext2fs tdb renames */ #define tdb_open ext2fs_tdb_open #define tdb_open_ex ext2fs_tdb_open_ex #define tdb_set_max_dead ext2fs_tdb_set_max_dead #define tdb_reopen ext2fs_tdb_reopen #define tdb_reopen_all ext2fs_tdb_reopen_all #define tdb_set_logging_function ext2fs_tdb_set_logging_function #define tdb_error ext2fs_tdb_error #define tdb_errorstr ext2fs_tdb_errorstr #define tdb_fetch ext2fs_tdb_fetch #define tdb_parse_record ext2fs_tdb_parse_record #define tdb_delete ext2fs_tdb_delete #define tdb_store ext2fs_tdb_store #define tdb_append ext2fs_tdb_append #define tdb_close ext2fs_tdb_close #define tdb_firstkey ext2fs_tdb_firstkey #define tdb_nextkey ext2fs_tdb_nextkey #define tdb_traverse ext2fs_tdb_traverse #define tdb_traverse_read ext2fs_tdb_traverse_read #define tdb_exists ext2fs_tdb_exists #define tdb_lockall ext2fs_tdb_lockall #define tdb_unlockall ext2fs_tdb_unlockall #define tdb_lockall_read ext2fs_tdb_lockall_read #define tdb_unlockall_read ext2fs_tdb_unlockall_read #define tdb_name ext2fs_tdb_name #define tdb_fd ext2fs_tdb_fd #define tdb_log_fn ext2fs_tdb_log_fn #define tdb_get_logging_private ext2fs_tdb_get_logging_private #define tdb_transaction_start ext2fs_tdb_transaction_start #define tdb_transaction_commit ext2fs_tdb_transaction_commit #define tdb_transaction_cancel ext2fs_tdb_transaction_cancel #define tdb_transaction_recover ext2fs_tdb_transaction_recover #define tdb_get_seqnum ext2fs_tdb_get_seqnum #define tdb_hash_size ext2fs_tdb_hash_size #define tdb_map_size ext2fs_tdb_map_size #define tdb_get_flags ext2fs_tdb_get_flags #define tdb_chainlock ext2fs_tdb_chainlock #define tdb_chainunlock ext2fs_tdb_chainunlock #define tdb_chainlock_read ext2fs_tdb_chainlock_read #define tdb_chainunlock_read ext2fs_tdb_chainunlock_read #define tdb_dump_all ext2fs_tdb_dump_all #define tdb_printfreelist ext2fs_tdb_printfreelist #define tdb_validate_freelist ext2fs_tdb_validate_freelist #define tdb_chainlock_mark ext2fs_tdb_chainlock_mark #define tdb_chainlock_nonblock ext2fs_tdb_chainlock_nonblock #define tdb_chainlock_unmark ext2fs_tdb_chainlock_unmark #define tdb_enable_seqnum ext2fs_tdb_enable_seqnum #define tdb_increment_seqnum_nonblock ext2fs_tdb_increment_seqnum_nonblock #define tdb_lock_nonblock ext2fs_tdb_lock_nonblock #define tdb_lockall_mark ext2fs_tdb_lockall_mark #define tdb_lockall_nonblock ext2fs_tdb_lockall_nonblock #define tdb_lockall_read_nonblock ext2fs_tdb_lockall_read_nonblock #define tdb_lockall_unmark ext2fs_tdb_lockall_unmark /* this is the context structure that is returned from a db open */ typedef struct tdb_context TDB_CONTEXT; typedef int (*tdb_traverse_func)(struct tdb_context *, TDB_DATA, TDB_DATA, void *); typedef void (*tdb_log_func)(struct tdb_context *, enum tdb_debug_level, const char *, ...) PRINTF_ATTRIBUTE(3, 4); typedef unsigned int (*tdb_hash_func)(TDB_DATA *key); struct tdb_logging_context { tdb_log_func log_fn; void *log_private; }; struct tdb_context *tdb_open(const char *name, int hash_size, int tdb_flags, int open_flags, mode_t mode); struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags, int open_flags, mode_t mode, const struct tdb_logging_context *log_ctx, tdb_hash_func hash_fn); void tdb_set_max_dead(struct tdb_context *tdb, int max_dead); int tdb_reopen(struct tdb_context *tdb); int tdb_reopen_all(int parent_longlived); void tdb_set_logging_function(struct tdb_context *tdb, const struct tdb_logging_context *log_ctx); enum TDB_ERROR tdb_error(struct tdb_context *tdb); const char *tdb_errorstr(struct tdb_context *tdb); TDB_DATA tdb_fetch(struct tdb_context *tdb, TDB_DATA key); int tdb_parse_record(struct tdb_context *tdb, TDB_DATA key, int (*parser)(TDB_DATA key, TDB_DATA data, void *private_data), void *private_data); int tdb_delete(struct tdb_context *tdb, TDB_DATA key); int tdb_store(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf, int flag); int tdb_append(struct tdb_context *tdb, TDB_DATA key, TDB_DATA new_dbuf); int tdb_close(struct tdb_context *tdb); TDB_DATA tdb_firstkey(struct tdb_context *tdb); TDB_DATA tdb_nextkey(struct tdb_context *tdb, TDB_DATA key); int tdb_traverse(struct tdb_context *tdb, tdb_traverse_func fn, void *); int tdb_traverse_read(struct tdb_context *tdb, tdb_traverse_func fn, void *); int tdb_exists(struct tdb_context *tdb, TDB_DATA key); int tdb_lockall(struct tdb_context *tdb); int tdb_lockall_nonblock(struct tdb_context *tdb); int tdb_unlockall(struct tdb_context *tdb); int tdb_lockall_read(struct tdb_context *tdb); int tdb_lockall_read_nonblock(struct tdb_context *tdb); int tdb_unlockall_read(struct tdb_context *tdb); int tdb_lockall_mark(struct tdb_context *tdb); int tdb_lockall_unmark(struct tdb_context *tdb); const char *tdb_name(struct tdb_context *tdb); int tdb_fd(struct tdb_context *tdb); tdb_log_func tdb_log_fn(struct tdb_context *tdb); void *tdb_get_logging_private(struct tdb_context *tdb); int tdb_transaction_start(struct tdb_context *tdb); int tdb_transaction_commit(struct tdb_context *tdb); int tdb_transaction_cancel(struct tdb_context *tdb); int tdb_transaction_recover(struct tdb_context *tdb); int tdb_get_seqnum(struct tdb_context *tdb); int tdb_hash_size(struct tdb_context *tdb); size_t tdb_map_size(struct tdb_context *tdb); int tdb_get_flags(struct tdb_context *tdb); void tdb_enable_seqnum(struct tdb_context *tdb); void tdb_increment_seqnum_nonblock(struct tdb_context *tdb); /* Low level locking functions: use with care */ int tdb_chainlock(struct tdb_context *tdb, TDB_DATA key); int tdb_chainlock_nonblock(struct tdb_context *tdb, TDB_DATA key); int tdb_chainunlock(struct tdb_context *tdb, TDB_DATA key); int tdb_chainlock_read(struct tdb_context *tdb, TDB_DATA key); int tdb_chainunlock_read(struct tdb_context *tdb, TDB_DATA key); int tdb_chainlock_mark(struct tdb_context *tdb, TDB_DATA key); int tdb_chainlock_unmark(struct tdb_context *tdb, TDB_DATA key); /* Debug functions. Not used in production. */ void tdb_dump_all(struct tdb_context *tdb); int tdb_printfreelist(struct tdb_context *tdb); int tdb_validate_freelist(struct tdb_context *tdb, int *pnum_entries); extern TDB_DATA tdb_null; #ifdef __cplusplus } #endif #endif /* tdb.h */ e2fsprogs-1.41.14/lib/ext2fs/dosio.h0000644031104000366760000001111411405316370015077 0ustar tytso/* * v1.0 * * Disk I/O include file for the ext2fs/DOS library. * * Copyright (c) 1997 Mark Habersack * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #ifndef __diskio_h #define __diskio_h #ifdef __TURBOC__ #ifndef __LARGE__ # error "ext2fs/DOS library requires LARGE model!" #endif #endif #ifdef __TURBOC__ #include "msdos.h" #endif /* * A helper structure used in LBA => CHS conversion */ typedef struct { unsigned short cyl; /* Cylinder (or track) */ unsigned short head; unsigned short sector; unsigned short offset; /* Offset of byte within the sector */ } CHS; /* * All partition data we need is here */ typedef struct { char *dev; /* _Linux_ device name (like "/dev/hda1") */ unsigned char phys; /* Physical DOS drive number */ unsigned long start; /* LBA address of partition start */ unsigned long len; /* length of partition in sectors */ unsigned char pno; /* Partition number (read from *dev) */ /* This partition's drive geometry */ unsigned short cyls; unsigned short heads; unsigned short sects; } PARTITION; /* * PC partition table entry format */ #ifdef __DJGPP__ #pragma pack(1) #endif typedef struct { unsigned char active; unsigned char start_head; unsigned char start_sec; unsigned char start_cyl; unsigned char type; unsigned char end_head; unsigned char end_sec; unsigned char end_cyl; unsigned long first_sec_rel; unsigned long size; } PTABLE_ENTRY; #ifdef __DJGPP__ #pragma pack() #endif /* * INT 0x13 operation codes */ #define DISK_READ 0x02 #define DISK_WRITE 0x03 #define DISK_GET_GEOMETRY 0x08 #define DISK_READY 0x10 /* * Errors to put in _dio_error */ #define ERR_BADDEV 0x00000001L #define ERR_HARDWARE 0x00000002L #define ERR_NOTSUPP 0x00000003L #define ERR_NOTEXT2FS 0x00000004L #define ERR_EMPTYPART 0x00000005L #define ERR_LINUXSWAP 0x00000006L /* * Functions in diskio.c */ /* * Variable contains last module's error */ extern unsigned long _dio_error; /* * This one contains last hardware error (if _dio_error == ERR_HARDWARE) */ extern unsigned long _dio_hw_error; /* * Macros to check for disk hardware errors */ #define HW_OK() ((unsigned char)_dio_hw_error == 0x00) #define HW_BAD_CMD() ((unsigned char)_dio_hw_error == 0x01) #define HW_NO_ADDR_MARK() ((unsigned char)_dio_hw_error == 0x02) #define HW_WRITE_PROT() ((unsigned char)_dio_hw_error == 0x03) #define HW_NO_SECTOR() ((unsigned char)_dio_hw_error == 0x04) #define HW_RESET_FAIL() ((unsigned char)_dio_hw_error == 0x05) #define HW_DISK_CHANGED() ((unsigned char)_dio_hw_error == 0x06) #define HW_DRIVE_FAIL() ((unsigned char)_dio_hw_error == 0x07) #define HW_DMA_OVERRUN() ((unsigned char)_dio_hw_error == 0x08) #define HW_DMA_BOUNDARY() ((unsigned char)_dio_hw_error == 0x09) #define HW_BAD_SECTOR() ((unsigned char)_dio_hw_error == 0x0A) #define HW_BAD_TRACK() ((unsigned char)_dio_hw_error == 0x0B) #define HW_UNSUPP_TRACK() ((unsigned char)_dio_hw_error == 0x0C) #define HW_BAD_CRC_ECC() ((unsigned char)_dio_hw_error == 0x10) #define HW_CRC_ECC_CORR() ((unsigned char)_dio_hw_error == 0x11) #define HW_CONTR_FAIL() ((unsigned char)_dio_hw_error == 0x20) #define HW_SEEK_FAIL() ((unsigned char)_dio_hw_error == 0x40) #define HW_ATTACH_FAIL() ((unsigned char)_dio_hw_error == 0x80) #define HW_DRIVE_NREADY() ((unsigned char)_dio_hw_error == 0xAA) #define HW_UNDEF_ERROR() ((unsigned char)_dio_hw_error == 0xBB) #define HW_WRITE_FAULT() ((unsigned char)_dio_hw_error == 0xCC) #define HW_STATUS_ERROR() ((unsigned char)_dio_hw_error == 0xE0) #define HW_SENSE_FAIL() ((unsigned char)_dio_hw_error == 0xFF) /* * Open the specified partition. * String 'dev' must have a format: * * /dev/{sd|hd|fd}[X] * * where, * * only one of the option in curly braces can be used and X is an optional * partition number for the given device. If X is not specified, function * scans the drive's partition table in search for the first Linux ext2fs * partition (signature 0x83). Along the way it dives into every extended * partition encountered. * Scan ends if either (a) there are no more used partition entries, or * (b) there is no Xth partition. * * Routine returns 0 on success and !=0 otherwise. */ int open_partition(char *dev); #endif /* __diskio_h */ e2fsprogs-1.41.14/lib/ext2fs/kernel-jbd.h0000644031104000116100000007111311504417000016443 0ustar tytsoeng/* * linux/include/linux/jbd.h * * Written by Stephen C. Tweedie * * Copyright 1998-2000 Red Hat, Inc --- All Rights Reserved * * This file is part of the Linux kernel and is made available under * the terms of the GNU General Public License, version 2, or at your * option, any later version, incorporated herein by reference. * * Definitions for transaction data structures for the buffer cache * filesystem journaling support. */ #ifndef _LINUX_JBD_H #define _LINUX_JBD_H #if defined(CONFIG_JBD) || defined(CONFIG_JBD_MODULE) || !defined(__KERNEL__) /* Allow this file to be included directly into e2fsprogs */ #ifndef __KERNEL__ #include "jfs_compat.h" #define JFS_DEBUG #define jfs_debug jbd_debug #else #include #include #include #endif #ifndef __GNUC__ #define __FUNCTION__ "" #endif #define journal_oom_retry 1 #ifdef __STDC__ #ifdef CONFIG_JBD_DEBUG /* * Define JBD_EXPENSIVE_CHECKING to enable more expensive internal * consistency checks. By default we don't do this unless * CONFIG_JBD_DEBUG is on. */ #define JBD_EXPENSIVE_CHECKING extern int journal_enable_debug; #define jbd_debug(n, f, a...) \ do { \ if ((n) <= journal_enable_debug) { \ printk (KERN_DEBUG "(%s, %d): %s: ", \ __FILE__, __LINE__, __FUNCTION__); \ printk (f, ## a); \ } \ } while (0) #else #ifdef __GNUC__ #define jbd_debug(f, a...) /**/ #else #define jbd_debug(f, ...) /**/ #endif #endif #else #define jbd_debug(x) /* AIX doesn't do STDC */ #endif extern void * __jbd_kmalloc (char *where, size_t size, int flags, int retry); #define jbd_kmalloc(size, flags) \ __jbd_kmalloc(__FUNCTION__, (size), (flags), journal_oom_retry) #define jbd_rep_kmalloc(size, flags) \ __jbd_kmalloc(__FUNCTION__, (size), (flags), 1) #define JFS_MIN_JOURNAL_BLOCKS 1024 #ifdef __KERNEL__ typedef struct handle_s handle_t; /* Atomic operation type */ typedef struct journal_s journal_t; /* Journal control structure */ #endif /* * Internal structures used by the logging mechanism: */ #define JFS_MAGIC_NUMBER 0xc03b3998U /* The first 4 bytes of /dev/random! */ /* * On-disk structures */ /* * Descriptor block types: */ #define JFS_DESCRIPTOR_BLOCK 1 #define JFS_COMMIT_BLOCK 2 #define JFS_SUPERBLOCK_V1 3 #define JFS_SUPERBLOCK_V2 4 #define JFS_REVOKE_BLOCK 5 /* * Standard header for all descriptor blocks: */ typedef struct journal_header_s { __u32 h_magic; __u32 h_blocktype; __u32 h_sequence; } journal_header_t; /* * Checksum types. */ #define JBD2_CRC32_CHKSUM 1 #define JBD2_MD5_CHKSUM 2 #define JBD2_SHA1_CHKSUM 3 #define JBD2_CRC32_CHKSUM_SIZE 4 #define JBD2_CHECKSUM_BYTES (32 / sizeof(__u32)) /* * Commit block header for storing transactional checksums: */ struct commit_header { __u32 h_magic; __u32 h_blocktype; __u32 h_sequence; unsigned char h_chksum_type; unsigned char h_chksum_size; unsigned char h_padding[2]; __u32 h_chksum[JBD2_CHECKSUM_BYTES]; __u64 h_commit_sec; __u32 h_commit_nsec; }; /* * The block tag: used to describe a single buffer in the journal */ typedef struct journal_block_tag_s { __u32 t_blocknr; /* The on-disk block number */ __u32 t_flags; /* See below */ __u32 t_blocknr_high; /* most-significant high 32bits. */ } journal_block_tag_t; #define JBD_TAG_SIZE64 (sizeof(journal_block_tag_t)) #define JBD_TAG_SIZE32 (8) /* * The revoke descriptor: used on disk to describe a series of blocks to * be revoked from the log */ typedef struct journal_revoke_header_s { journal_header_t r_header; int r_count; /* Count of bytes used in the block */ } journal_revoke_header_t; /* Definitions for the journal tag flags word: */ #define JFS_FLAG_ESCAPE 1 /* on-disk block is escaped */ #define JFS_FLAG_SAME_UUID 2 /* block has same uuid as previous */ #define JFS_FLAG_DELETED 4 /* block deleted by this transaction */ #define JFS_FLAG_LAST_TAG 8 /* last tag in this descriptor block */ /* * The journal superblock. All fields are in big-endian byte order. */ typedef struct journal_superblock_s { /* 0x0000 */ journal_header_t s_header; /* 0x000C */ /* Static information describing the journal */ __u32 s_blocksize; /* journal device blocksize */ __u32 s_maxlen; /* total blocks in journal file */ __u32 s_first; /* first block of log information */ /* 0x0018 */ /* Dynamic information describing the current state of the log */ __u32 s_sequence; /* first commit ID expected in log */ __u32 s_start; /* blocknr of start of log */ /* 0x0020 */ /* Error value, as set by journal_abort(). */ __s32 s_errno; /* 0x0024 */ /* Remaining fields are only valid in a version-2 superblock */ __u32 s_feature_compat; /* compatible feature set */ __u32 s_feature_incompat; /* incompatible feature set */ __u32 s_feature_ro_compat; /* readonly-compatible feature set */ /* 0x0030 */ __u8 s_uuid[16]; /* 128-bit uuid for journal */ /* 0x0040 */ __u32 s_nr_users; /* Nr of filesystems sharing log */ __u32 s_dynsuper; /* Blocknr of dynamic superblock copy*/ /* 0x0048 */ __u32 s_max_transaction; /* Limit of journal blocks per trans.*/ __u32 s_max_trans_data; /* Limit of data blocks per trans. */ /* 0x0050 */ __u32 s_padding[44]; /* 0x0100 */ __u8 s_users[16*48]; /* ids of all fs'es sharing the log */ /* 0x0400 */ } journal_superblock_t; #define JFS_HAS_COMPAT_FEATURE(j,mask) \ ((j)->j_format_version >= 2 && \ ((j)->j_superblock->s_feature_compat & cpu_to_be32((mask)))) #define JFS_HAS_RO_COMPAT_FEATURE(j,mask) \ ((j)->j_format_version >= 2 && \ ((j)->j_superblock->s_feature_ro_compat & cpu_to_be32((mask)))) #define JFS_HAS_INCOMPAT_FEATURE(j,mask) \ ((j)->j_format_version >= 2 && \ ((j)->j_superblock->s_feature_incompat & cpu_to_be32((mask)))) #define JFS_FEATURE_COMPAT_CHECKSUM 0x00000001 #define JFS_FEATURE_INCOMPAT_REVOKE 0x00000001 #define JFS_FEATURE_INCOMPAT_REVOKE 0x00000001 #define JFS_FEATURE_INCOMPAT_64BIT 0x00000002 #define JFS_FEATURE_INCOMPAT_ASYNC_COMMIT 0x00000004 /* Features known to this kernel version: */ #define JFS_KNOWN_COMPAT_FEATURES 0 #define JFS_KNOWN_ROCOMPAT_FEATURES 0 #define JFS_KNOWN_INCOMPAT_FEATURES (JFS_FEATURE_INCOMPAT_REVOKE|\ JFS_FEATURE_INCOMPAT_ASYNC_COMMIT) #ifdef __KERNEL__ #include #include #define JBD_ASSERTIONS #ifdef JBD_ASSERTIONS #define J_ASSERT(assert) \ do { \ if (!(assert)) { \ printk (KERN_EMERG \ "Assertion failure in %s() at %s:%d: \"%s\"\n", \ __FUNCTION__, __FILE__, __LINE__, # assert); \ BUG(); \ } \ } while (0) #if defined(CONFIG_BUFFER_DEBUG) void buffer_assertion_failure(struct buffer_head *bh); #define J_ASSERT_BH(bh, expr) \ do { \ if (!(expr)) \ buffer_assertion_failure(bh); \ J_ASSERT(expr); \ } while (0) #define J_ASSERT_JH(jh, expr) J_ASSERT_BH(jh2bh(jh), expr) #else #define J_ASSERT_BH(bh, expr) J_ASSERT(expr) #define J_ASSERT_JH(jh, expr) J_ASSERT(expr) #endif #else #define J_ASSERT(assert) #endif /* JBD_ASSERTIONS */ enum jbd_state_bits { BH_JWrite = BH_PrivateStart, /* 1 if being written to log (@@@ DEBUGGING) */ BH_Freed, /* 1 if buffer has been freed (truncated) */ BH_Revoked, /* 1 if buffer has been revoked from the log */ BH_RevokeValid, /* 1 if buffer revoked flag is valid */ BH_JBDDirty, /* 1 if buffer is dirty but journaled */ }; /* Return true if the buffer is one which JBD is managing */ static inline int buffer_jbd(struct buffer_head *bh) { return __buffer_state(bh, JBD); } static inline struct buffer_head *jh2bh(struct journal_head *jh) { return jh->b_bh; } static inline struct journal_head *bh2jh(struct buffer_head *bh) { return bh->b_private; } struct jbd_revoke_table_s; /* The handle_t type represents a single atomic update being performed * by some process. All filesystem modifications made by the process go * through this handle. Recursive operations (such as quota operations) * are gathered into a single update. * * The buffer credits field is used to account for journaled buffers * being modified by the running process. To ensure that there is * enough log space for all outstanding operations, we need to limit the * number of outstanding buffers possible at any time. When the * operation completes, any buffer credits not used are credited back to * the transaction, so that at all times we know how many buffers the * outstanding updates on a transaction might possibly touch. */ struct handle_s { /* Which compound transaction is this update a part of? */ transaction_t * h_transaction; /* Number of remaining buffers we are allowed to dirty: */ int h_buffer_credits; /* Reference count on this handle */ int h_ref; /* Field for caller's use to track errors through large fs operations */ int h_err; /* Flags */ unsigned int h_sync: 1; /* sync-on-close */ unsigned int h_jdata: 1; /* force data journaling */ unsigned int h_aborted: 1; /* fatal error on handle */ }; /* The transaction_t type is the guts of the journaling mechanism. It * tracks a compound transaction through its various states: * * RUNNING: accepting new updates * LOCKED: Updates still running but we don't accept new ones * RUNDOWN: Updates are tidying up but have finished requesting * new buffers to modify (state not used for now) * FLUSH: All updates complete, but we are still writing to disk * COMMIT: All data on disk, writing commit record * FINISHED: We still have to keep the transaction for checkpointing. * * The transaction keeps track of all of the buffers modified by a * running transaction, and all of the buffers committed but not yet * flushed to home for finished transactions. */ struct transaction_s { /* Pointer to the journal for this transaction. */ journal_t * t_journal; /* Sequence number for this transaction */ tid_t t_tid; /* Transaction's current state */ enum { T_RUNNING, T_LOCKED, T_RUNDOWN, T_FLUSH, T_COMMIT, T_FINISHED } t_state; /* Where in the log does this transaction's commit start? */ unsigned long t_log_start; /* Doubly-linked circular list of all inodes owned by this transaction */ /* AKPM: unused */ struct inode * t_ilist; /* Number of buffers on the t_buffers list */ int t_nr_buffers; /* Doubly-linked circular list of all buffers reserved but not yet modified by this transaction */ struct journal_head * t_reserved_list; /* Doubly-linked circular list of all metadata buffers owned by this transaction */ struct journal_head * t_buffers; /* * Doubly-linked circular list of all data buffers still to be * flushed before this transaction can be committed. * Protected by journal_datalist_lock. */ struct journal_head * t_sync_datalist; /* * Doubly-linked circular list of all writepage data buffers * still to be written before this transaction can be committed. * Protected by journal_datalist_lock. */ struct journal_head * t_async_datalist; /* Doubly-linked circular list of all forget buffers (superceded buffers which we can un-checkpoint once this transaction commits) */ struct journal_head * t_forget; /* * Doubly-linked circular list of all buffers still to be * flushed before this transaction can be checkpointed. */ /* Protected by journal_datalist_lock */ struct journal_head * t_checkpoint_list; /* Doubly-linked circular list of temporary buffers currently undergoing IO in the log */ struct journal_head * t_iobuf_list; /* Doubly-linked circular list of metadata buffers being shadowed by log IO. The IO buffers on the iobuf list and the shadow buffers on this list match each other one for one at all times. */ struct journal_head * t_shadow_list; /* Doubly-linked circular list of control buffers being written to the log. */ struct journal_head * t_log_list; /* Number of outstanding updates running on this transaction */ int t_updates; /* Number of buffers reserved for use by all handles in this * transaction handle but not yet modified. */ int t_outstanding_credits; /* * Forward and backward links for the circular list of all * transactions awaiting checkpoint. */ /* Protected by journal_datalist_lock */ transaction_t *t_cpnext, *t_cpprev; /* When will the transaction expire (become due for commit), in * jiffies ? */ unsigned long t_expires; /* How many handles used this transaction? */ int t_handle_count; }; /* The journal_t maintains all of the journaling state information for a * single filesystem. It is linked to from the fs superblock structure. * * We use the journal_t to keep track of all outstanding transaction * activity on the filesystem, and to manage the state of the log * writing process. */ struct journal_s { /* General journaling state flags */ unsigned long j_flags; /* Is there an outstanding uncleared error on the journal (from * a prior abort)? */ int j_errno; /* The superblock buffer */ struct buffer_head * j_sb_buffer; journal_superblock_t * j_superblock; /* Version of the superblock format */ int j_format_version; /* Number of processes waiting to create a barrier lock */ int j_barrier_count; /* The barrier lock itself */ struct semaphore j_barrier; /* Transactions: The current running transaction... */ transaction_t * j_running_transaction; /* ... the transaction we are pushing to disk ... */ transaction_t * j_committing_transaction; /* ... and a linked circular list of all transactions waiting * for checkpointing. */ /* Protected by journal_datalist_lock */ transaction_t * j_checkpoint_transactions; /* Wait queue for waiting for a locked transaction to start committing, or for a barrier lock to be released */ wait_queue_head_t j_wait_transaction_locked; /* Wait queue for waiting for checkpointing to complete */ wait_queue_head_t j_wait_logspace; /* Wait queue for waiting for commit to complete */ wait_queue_head_t j_wait_done_commit; /* Wait queue to trigger checkpointing */ wait_queue_head_t j_wait_checkpoint; /* Wait queue to trigger commit */ wait_queue_head_t j_wait_commit; /* Wait queue to wait for updates to complete */ wait_queue_head_t j_wait_updates; /* Semaphore for locking against concurrent checkpoints */ struct semaphore j_checkpoint_sem; /* The main journal lock, used by lock_journal() */ struct semaphore j_sem; /* Journal head: identifies the first unused block in the journal. */ unsigned long j_head; /* Journal tail: identifies the oldest still-used block in the * journal. */ unsigned long j_tail; /* Journal free: how many free blocks are there in the journal? */ unsigned long j_free; /* Journal start and end: the block numbers of the first usable * block and one beyond the last usable block in the journal. */ unsigned long j_first, j_last; /* Device, blocksize and starting block offset for the location * where we store the journal. */ kdev_t j_dev; int j_blocksize; unsigned int j_blk_offset; /* Device which holds the client fs. For internal journal this * will be equal to j_dev. */ kdev_t j_fs_dev; /* Total maximum capacity of the journal region on disk. */ unsigned int j_maxlen; /* Optional inode where we store the journal. If present, all * journal block numbers are mapped into this inode via * bmap(). */ struct inode * j_inode; /* Sequence number of the oldest transaction in the log */ tid_t j_tail_sequence; /* Sequence number of the next transaction to grant */ tid_t j_transaction_sequence; /* Sequence number of the most recently committed transaction */ tid_t j_commit_sequence; /* Sequence number of the most recent transaction wanting commit */ tid_t j_commit_request; /* Journal uuid: identifies the object (filesystem, LVM volume * etc) backed by this journal. This will eventually be * replaced by an array of uuids, allowing us to index multiple * devices within a single journal and to perform atomic updates * across them. */ __u8 j_uuid[16]; /* Pointer to the current commit thread for this journal */ struct task_struct * j_task; /* Maximum number of metadata buffers to allow in a single * compound commit transaction */ int j_max_transaction_buffers; /* What is the maximum transaction lifetime before we begin a * commit? */ unsigned long j_commit_interval; /* The timer used to wakeup the commit thread: */ struct timer_list * j_commit_timer; int j_commit_timer_active; /* Link all journals together - system-wide */ struct list_head j_all_journals; /* The revoke table: maintains the list of revoked blocks in the current transaction. */ struct jbd_revoke_table_s *j_revoke; /* Failed journal commit ID */ unsigned int j_failed_commit; }; /* * Journal flag definitions */ #define JFS_UNMOUNT 0x001 /* Journal thread is being destroyed */ #define JFS_ABORT 0x002 /* Journaling has been aborted for errors. */ #define JFS_ACK_ERR 0x004 /* The errno in the sb has been acked */ #define JFS_FLUSHED 0x008 /* The journal superblock has been flushed */ #define JFS_LOADED 0x010 /* The journal superblock has been loaded */ /* * Function declarations for the journaling transaction and buffer * management */ /* Filing buffers */ extern void __journal_unfile_buffer(struct journal_head *); extern void journal_unfile_buffer(struct journal_head *); extern void __journal_refile_buffer(struct journal_head *); extern void journal_refile_buffer(struct journal_head *); extern void __journal_file_buffer(struct journal_head *, transaction_t *, int); extern void __journal_free_buffer(struct journal_head *bh); extern void journal_file_buffer(struct journal_head *, transaction_t *, int); extern void __journal_clean_data_list(transaction_t *transaction); /* Log buffer allocation */ extern struct journal_head * journal_get_descriptor_buffer(journal_t *); extern unsigned long journal_next_log_block(journal_t *); /* Commit management */ extern void journal_commit_transaction(journal_t *); /* Checkpoint list management */ int __journal_clean_checkpoint_list(journal_t *journal); extern void journal_remove_checkpoint(struct journal_head *); extern void __journal_remove_checkpoint(struct journal_head *); extern void journal_insert_checkpoint(struct journal_head *, transaction_t *); extern void __journal_insert_checkpoint(struct journal_head *,transaction_t *); /* Buffer IO */ extern int journal_write_metadata_buffer(transaction_t *transaction, struct journal_head *jh_in, struct journal_head **jh_out, int blocknr); /* Transaction locking */ extern void __wait_on_journal (journal_t *); /* * Journal locking. * * We need to lock the journal during transaction state changes so that * nobody ever tries to take a handle on the running transaction while * we are in the middle of moving it to the commit phase. * * Note that the locking is completely interrupt unsafe. We never touch * journal structures from interrupts. * * In 2.2, the BKL was required for lock_journal. This is no longer * the case. */ static inline void lock_journal(journal_t *journal) { down(&journal->j_sem); } /* This returns zero if we acquired the semaphore */ static inline int try_lock_journal(journal_t * journal) { return down_trylock(&journal->j_sem); } static inline void unlock_journal(journal_t * journal) { up(&journal->j_sem); } static inline handle_t *journal_current_handle(void) { return current->journal_info; } /* The journaling code user interface: * * Create and destroy handles * Register buffer modifications against the current transaction. */ extern handle_t *journal_start(journal_t *, int nblocks); extern handle_t *journal_try_start(journal_t *, int nblocks); extern int journal_restart (handle_t *, int nblocks); extern int journal_extend (handle_t *, int nblocks); extern int journal_get_write_access (handle_t *, struct buffer_head *); extern int journal_get_create_access (handle_t *, struct buffer_head *); extern int journal_get_undo_access (handle_t *, struct buffer_head *); extern int journal_dirty_data (handle_t *, struct buffer_head *, int async); extern int journal_dirty_metadata (handle_t *, struct buffer_head *); extern void journal_release_buffer (handle_t *, struct buffer_head *); extern void journal_forget (handle_t *, struct buffer_head *); extern void journal_sync_buffer (struct buffer_head *); extern int journal_flushpage(journal_t *, struct page *, unsigned long); extern int journal_try_to_free_buffers(journal_t *, struct page *, int); extern int journal_stop(handle_t *); extern int journal_flush (journal_t *); extern void journal_lock_updates (journal_t *); extern void journal_unlock_updates (journal_t *); extern journal_t * journal_init_dev(kdev_t dev, kdev_t fs_dev, int start, int len, int bsize); extern journal_t * journal_init_inode (struct inode *); extern int journal_update_format (journal_t *); extern int journal_check_used_features (journal_t *, unsigned long, unsigned long, unsigned long); extern int journal_check_available_features (journal_t *, unsigned long, unsigned long, unsigned long); extern int journal_set_features (journal_t *, unsigned long, unsigned long, unsigned long); extern int journal_create (journal_t *); extern int journal_load (journal_t *journal); extern void journal_destroy (journal_t *); extern int journal_recover (journal_t *journal); extern int journal_wipe (journal_t *, int); extern int journal_skip_recovery (journal_t *); extern void journal_update_superblock (journal_t *, int); extern void __journal_abort (journal_t *); extern void journal_abort (journal_t *, int); extern int journal_errno (journal_t *); extern void journal_ack_err (journal_t *); extern int journal_clear_err (journal_t *); extern unsigned long journal_bmap(journal_t *journal, unsigned long blocknr); extern int journal_force_commit(journal_t *journal); /* * journal_head management */ extern struct journal_head *journal_add_journal_head(struct buffer_head *bh); extern void journal_remove_journal_head(struct buffer_head *bh); extern void __journal_remove_journal_head(struct buffer_head *bh); extern void journal_unlock_journal_head(struct journal_head *jh); /* Primary revoke support */ #define JOURNAL_REVOKE_DEFAULT_HASH 256 extern int journal_init_revoke(journal_t *, int); extern void journal_destroy_revoke_caches(void); extern int journal_init_revoke_caches(void); extern void journal_destroy_revoke(journal_t *); extern int journal_revoke (handle_t *, unsigned long, struct buffer_head *); extern int journal_cancel_revoke(handle_t *, struct journal_head *); extern void journal_write_revoke_records(journal_t *, transaction_t *); /* Recovery revoke support */ extern int journal_set_revoke(journal_t *, unsigned long, tid_t); extern int journal_test_revoke(journal_t *, unsigned long, tid_t); extern void journal_clear_revoke(journal_t *); extern void journal_brelse_array(struct buffer_head *b[], int n); /* The log thread user interface: * * Request space in the current transaction, and force transaction commit * transitions on demand. */ extern int log_space_left (journal_t *); /* Called with journal locked */ extern tid_t log_start_commit (journal_t *, transaction_t *); extern void log_wait_commit (journal_t *, tid_t); extern int log_do_checkpoint (journal_t *, int); extern void log_wait_for_space(journal_t *, int nblocks); extern void __journal_drop_transaction(journal_t *, transaction_t *); extern int cleanup_journal_tail(journal_t *); /* Reduce journal memory usage by flushing */ extern void shrink_journal_memory(void); /* Debugging code only: */ #define jbd_ENOSYS() \ do { \ printk (KERN_ERR "JBD unimplemented function " __FUNCTION__); \ current->state = TASK_UNINTERRUPTIBLE; \ schedule(); \ } while (1) /* * is_journal_abort * * Simple test wrapper function to test the JFS_ABORT state flag. This * bit, when set, indicates that we have had a fatal error somewhere, * either inside the journaling layer or indicated to us by the client * (eg. ext3), and that we and should not commit any further * transactions. */ static inline int is_journal_aborted(journal_t *journal) { return journal->j_flags & JFS_ABORT; } static inline int is_handle_aborted(handle_t *handle) { if (handle->h_aborted) return 1; return is_journal_aborted(handle->h_transaction->t_journal); } static inline void journal_abort_handle(handle_t *handle) { handle->h_aborted = 1; } /* Not all architectures define BUG() */ #ifndef BUG #define BUG() do { \ printk("kernel BUG at %s:%d!\n", __FILE__, __LINE__); \ * ((char *) 0) = 0; \ } while (0) #endif /* BUG */ #else extern int journal_recover (journal_t *journal); extern int journal_skip_recovery (journal_t *); /* Primary revoke support */ extern int journal_init_revoke(journal_t *, int); extern void journal_destroy_revoke_caches(void); extern int journal_init_revoke_caches(void); /* Recovery revoke support */ extern int journal_set_revoke(journal_t *, unsigned long, tid_t); extern int journal_test_revoke(journal_t *, unsigned long, tid_t); extern void journal_clear_revoke(journal_t *); extern void journal_brelse_array(struct buffer_head *b[], int n); extern void journal_destroy_revoke(journal_t *); #endif /* __KERNEL__ */ static inline int tid_gt(tid_t x, tid_t y) EXT2FS_ATTR((unused)); static inline int tid_geq(tid_t x, tid_t y) EXT2FS_ATTR((unused)); /* Comparison functions for transaction IDs: perform comparisons using * modulo arithmetic so that they work over sequence number wraps. */ static inline int tid_gt(tid_t x, tid_t y) { int difference = (x - y); return (difference > 0); } static inline int tid_geq(tid_t x, tid_t y) { int difference = (x - y); return (difference >= 0); } extern int journal_blocks_per_page(struct inode *inode); /* * Definitions which augment the buffer_head layer */ /* journaling buffer types */ #define BJ_None 0 /* Not journaled */ #define BJ_SyncData 1 /* Normal data: flush before commit */ #define BJ_AsyncData 2 /* writepage data: wait on it before commit */ #define BJ_Metadata 3 /* Normal journaled metadata */ #define BJ_Forget 4 /* Buffer superceded by this transaction */ #define BJ_IO 5 /* Buffer is for temporary IO use */ #define BJ_Shadow 6 /* Buffer contents being shadowed to the log */ #define BJ_LogCtl 7 /* Buffer contains log descriptors */ #define BJ_Reserved 8 /* Buffer is reserved for access by journal */ #define BJ_Types 9 extern int jbd_blocks_per_page(struct inode *inode); #ifdef __KERNEL__ extern spinlock_t jh_splice_lock; /* * Once `expr1' has been found true, take jh_splice_lock * and then reevaluate everything. */ #define SPLICE_LOCK(expr1, expr2) \ ({ \ int ret = (expr1); \ if (ret) { \ spin_lock(&jh_splice_lock); \ ret = (expr1) && (expr2); \ spin_unlock(&jh_splice_lock); \ } \ ret; \ }) /* * A number of buffer state predicates. They test for * buffer_jbd() because they are used in core kernel code. * * These will be racy on SMP unless we're *sure* that the * buffer won't be detached from the journalling system * in parallel. */ /* Return true if the buffer is on journal list `list' */ static inline int buffer_jlist_eq(struct buffer_head *bh, int list) { return SPLICE_LOCK(buffer_jbd(bh), bh2jh(bh)->b_jlist == list); } /* Return true if this bufer is dirty wrt the journal */ static inline int buffer_jdirty(struct buffer_head *bh) { return buffer_jbd(bh) && __buffer_state(bh, JBDDirty); } /* Return true if it's a data buffer which journalling is managing */ static inline int buffer_jbd_data(struct buffer_head *bh) { return SPLICE_LOCK(buffer_jbd(bh), bh2jh(bh)->b_jlist == BJ_SyncData || bh2jh(bh)->b_jlist == BJ_AsyncData); } #ifdef CONFIG_SMP #define assert_spin_locked(lock) J_ASSERT(spin_is_locked(lock)) #else #define assert_spin_locked(lock) do {} while(0) #endif #define buffer_trace_init(bh) do {} while (0) #define print_buffer_fields(bh) do {} while (0) #define print_buffer_trace(bh) do {} while (0) #define BUFFER_TRACE(bh, info) do {} while (0) #define BUFFER_TRACE2(bh, bh2, info) do {} while (0) #define JBUFFER_TRACE(jh, info) do {} while (0) #endif /* __KERNEL__ */ #endif /* CONFIG_JBD || CONFIG_JBD_MODULE || !__KERNEL__ */ /* * Compatibility no-ops which allow the kernel to compile without CONFIG_JBD * go here. */ #if defined(__KERNEL__) && !(defined(CONFIG_JBD) || defined(CONFIG_JBD_MODULE)) #define J_ASSERT(expr) do {} while (0) #define J_ASSERT_BH(bh, expr) do {} while (0) #define buffer_jbd(bh) 0 #define buffer_jlist_eq(bh, val) 0 #define journal_buffer_journal_lru(bh) 0 #endif /* defined(__KERNEL__) && !defined(CONFIG_JBD) */ #endif /* _LINUX_JBD_H */ e2fsprogs-1.41.14/lib/ext2fs/ind_block.c0000644031104000366760000000256411405316370015712 0ustar tytso/* * ind_block.c --- indirect block I/O routines * * Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, * 2001, 2002, 2003, 2004, 2005 by Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #include "ext2_fs.h" #include "ext2fs.h" errcode_t ext2fs_read_ind_block(ext2_filsys fs, blk_t blk, void *buf) { errcode_t retval; #ifdef WORDS_BIGENDIAN blk_t *block_nr; int i; int limit = fs->blocksize >> 2; #endif if ((fs->flags & EXT2_FLAG_IMAGE_FILE) && (fs->io != fs->image_io)) memset(buf, 0, fs->blocksize); else { retval = io_channel_read_blk(fs->io, blk, 1, buf); if (retval) return retval; } #ifdef WORDS_BIGENDIAN block_nr = (blk_t *) buf; for (i = 0; i < limit; i++, block_nr++) *block_nr = ext2fs_swab32(*block_nr); #endif return 0; } errcode_t ext2fs_write_ind_block(ext2_filsys fs, blk_t blk, void *buf) { #ifdef WORDS_BIGENDIAN blk_t *block_nr; int i; int limit = fs->blocksize >> 2; #endif if (fs->flags & EXT2_FLAG_IMAGE_FILE) return 0; #ifdef WORDS_BIGENDIAN block_nr = (blk_t *) buf; for (i = 0; i < limit; i++, block_nr++) *block_nr = ext2fs_swab32(*block_nr); #endif return io_channel_write_blk(fs->io, blk, 1, buf); } e2fsprogs-1.41.14/lib/ext2fs/extent.c0000644031104000116100000014022211504417000015726 0ustar tytsoeng/* * extent.c --- routines to implement extents support * * Copyright (C) 2007 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #if HAVE_ERRNO_H #include #endif #if HAVE_SYS_STAT_H #include #endif #if HAVE_SYS_TYPES_H #include #endif #include "ext2_fs.h" #include "ext2fsP.h" #include "e2image.h" /* * Definitions to be dropped in lib/ext2fs/ext2fs.h */ /* * Private definitions */ struct extent_path { char *buf; int entries; int max_entries; int left; int visit_num; int flags; blk64_t end_blk; void *curr; }; struct ext2_extent_handle { errcode_t magic; ext2_filsys fs; ext2_ino_t ino; struct ext2_inode *inode; int type; int level; int max_depth; struct extent_path *path; }; struct ext2_extent_path { errcode_t magic; int leaf_height; blk64_t lblk; }; /* * Useful Debugging stuff */ #ifdef DEBUG static void dbg_show_header(struct ext3_extent_header *eh) { printf("header: magic=%x entries=%u max=%u depth=%u generation=%u\n", ext2fs_le16_to_cpu(eh->eh_magic), ext2fs_le16_to_cpu(eh->eh_entries), ext2fs_le16_to_cpu(eh->eh_max), ext2fs_le16_to_cpu(eh->eh_depth), ext2fs_le32_to_cpu(eh->eh_generation)); } static void dbg_show_index(struct ext3_extent_idx *ix) { printf("index: block=%u leaf=%u leaf_hi=%u unused=%u\n", ext2fs_le32_to_cpu(ix->ei_block), ext2fs_le32_to_cpu(ix->ei_leaf), ext2fs_le16_to_cpu(ix->ei_leaf_hi), ext2fs_le16_to_cpu(ix->ei_unused)); } static void dbg_show_extent(struct ext3_extent *ex) { printf("extent: block=%u-%u len=%u start=%u start_hi=%u\n", ext2fs_le32_to_cpu(ex->ee_block), ext2fs_le32_to_cpu(ex->ee_block) + ext2fs_le16_to_cpu(ex->ee_len) - 1, ext2fs_le16_to_cpu(ex->ee_len), ext2fs_le32_to_cpu(ex->ee_start), ext2fs_le16_to_cpu(ex->ee_start_hi)); } static void dbg_print_extent(char *desc, struct ext2fs_extent *extent) { if (desc) printf("%s: ", desc); printf("extent: lblk %llu--%llu, len %u, pblk %llu, flags: ", extent->e_lblk, extent->e_lblk + extent->e_len - 1, extent->e_len, extent->e_pblk); if (extent->e_flags & EXT2_EXTENT_FLAGS_LEAF) fputs("LEAF ", stdout); if (extent->e_flags & EXT2_EXTENT_FLAGS_UNINIT) fputs("UNINIT ", stdout); if (extent->e_flags & EXT2_EXTENT_FLAGS_SECOND_VISIT) fputs("2ND_VISIT ", stdout); if (!extent->e_flags) fputs("(none)", stdout); fputc('\n', stdout); } #else #define dbg_show_header(eh) do { } while (0) #define dbg_show_index(ix) do { } while (0) #define dbg_show_extent(ex) do { } while (0) #define dbg_print_extent(desc, ex) do { } while (0) #endif /* * Verify the extent header as being sane */ errcode_t ext2fs_extent_header_verify(void *ptr, int size) { int eh_max, entry_size; struct ext3_extent_header *eh = ptr; dbg_show_header(eh); if (ext2fs_le16_to_cpu(eh->eh_magic) != EXT3_EXT_MAGIC) return EXT2_ET_EXTENT_HEADER_BAD; if (ext2fs_le16_to_cpu(eh->eh_entries) > ext2fs_le16_to_cpu(eh->eh_max)) return EXT2_ET_EXTENT_HEADER_BAD; if (eh->eh_depth == 0) entry_size = sizeof(struct ext3_extent); else entry_size = sizeof(struct ext3_extent_idx); eh_max = (size - sizeof(*eh)) / entry_size; /* Allow two extent-sized items at the end of the block, for * ext4_extent_tail with checksum in the future. */ if ((ext2fs_le16_to_cpu(eh->eh_max) > eh_max) || (ext2fs_le16_to_cpu(eh->eh_max) < (eh_max - 2))) return EXT2_ET_EXTENT_HEADER_BAD; return 0; } /* * Begin functions to handle an inode's extent information */ extern void ext2fs_extent_free(ext2_extent_handle_t handle) { int i; if (!handle) return; if (handle->inode) ext2fs_free_mem(&handle->inode); if (handle->path) { for (i=1; i <= handle->max_depth; i++) { if (handle->path[i].buf) ext2fs_free_mem(&handle->path[i].buf); } ext2fs_free_mem(&handle->path); } ext2fs_free_mem(&handle); } extern errcode_t ext2fs_extent_open(ext2_filsys fs, ext2_ino_t ino, ext2_extent_handle_t *ret_handle) { return ext2fs_extent_open2(fs, ino, NULL, ret_handle); } extern errcode_t ext2fs_extent_open2(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode, ext2_extent_handle_t *ret_handle) { struct ext2_extent_handle *handle; errcode_t retval; int i; struct ext3_extent_header *eh; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); if (!inode) if ((ino == 0) || (ino > fs->super->s_inodes_count)) return EXT2_ET_BAD_INODE_NUM; retval = ext2fs_get_mem(sizeof(struct ext2_extent_handle), &handle); if (retval) return retval; memset(handle, 0, sizeof(struct ext2_extent_handle)); retval = ext2fs_get_mem(sizeof(struct ext2_inode), &handle->inode); if (retval) goto errout; handle->ino = ino; handle->fs = fs; if (inode) { memcpy(handle->inode, inode, sizeof(struct ext2_inode)); } else { retval = ext2fs_read_inode(fs, ino, handle->inode); if (retval) goto errout; } eh = (struct ext3_extent_header *) &handle->inode->i_block[0]; for (i=0; i < EXT2_N_BLOCKS; i++) if (handle->inode->i_block[i]) break; if (i >= EXT2_N_BLOCKS) { eh->eh_magic = ext2fs_cpu_to_le16(EXT3_EXT_MAGIC); eh->eh_depth = 0; eh->eh_entries = 0; i = (sizeof(handle->inode->i_block) - sizeof(*eh)) / sizeof(struct ext3_extent); eh->eh_max = ext2fs_cpu_to_le16(i); handle->inode->i_flags |= EXT4_EXTENTS_FL; } if (!(handle->inode->i_flags & EXT4_EXTENTS_FL)) { retval = EXT2_ET_INODE_NOT_EXTENT; goto errout; } retval = ext2fs_extent_header_verify(eh, sizeof(handle->inode->i_block)); if (retval) goto errout; handle->max_depth = ext2fs_le16_to_cpu(eh->eh_depth); handle->type = ext2fs_le16_to_cpu(eh->eh_magic); retval = ext2fs_get_mem(((handle->max_depth+1) * sizeof(struct extent_path)), &handle->path); memset(handle->path, 0, (handle->max_depth+1) * sizeof(struct extent_path)); handle->path[0].buf = (char *) handle->inode->i_block; handle->path[0].left = handle->path[0].entries = ext2fs_le16_to_cpu(eh->eh_entries); handle->path[0].max_entries = ext2fs_le16_to_cpu(eh->eh_max); handle->path[0].curr = 0; handle->path[0].end_blk = ((((__u64) handle->inode->i_size_high << 32) + handle->inode->i_size + (fs->blocksize - 1)) >> EXT2_BLOCK_SIZE_BITS(fs->super)); handle->path[0].visit_num = 1; handle->level = 0; handle->magic = EXT2_ET_MAGIC_EXTENT_HANDLE; *ret_handle = handle; return 0; errout: ext2fs_extent_free(handle); return retval; } /* * This function is responsible for (optionally) moving through the * extent tree and then returning the current extent */ errcode_t ext2fs_extent_get(ext2_extent_handle_t handle, int flags, struct ext2fs_extent *extent) { struct extent_path *path, *newpath; struct ext3_extent_header *eh; struct ext3_extent_idx *ix = 0; struct ext3_extent *ex; errcode_t retval; blk_t blk; blk64_t end_blk; int orig_op, op; EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EXTENT_HANDLE); if (!handle->path) return EXT2_ET_NO_CURRENT_NODE; orig_op = op = flags & EXT2_EXTENT_MOVE_MASK; retry: path = handle->path + handle->level; if ((orig_op == EXT2_EXTENT_NEXT) || (orig_op == EXT2_EXTENT_NEXT_LEAF)) { if (handle->level < handle->max_depth) { /* interior node */ if (path->visit_num == 0) { path->visit_num++; op = EXT2_EXTENT_DOWN; } else if (path->left > 0) op = EXT2_EXTENT_NEXT_SIB; else if (handle->level > 0) op = EXT2_EXTENT_UP; else return EXT2_ET_EXTENT_NO_NEXT; } else { /* leaf node */ if (path->left > 0) op = EXT2_EXTENT_NEXT_SIB; else if (handle->level > 0) op = EXT2_EXTENT_UP; else return EXT2_ET_EXTENT_NO_NEXT; } if (op != EXT2_EXTENT_NEXT_SIB) { #ifdef DEBUG_GET_EXTENT printf("<<<< OP = %s\n", (op == EXT2_EXTENT_DOWN) ? "down" : ((op == EXT2_EXTENT_UP) ? "up" : "unknown")); #endif } } if ((orig_op == EXT2_EXTENT_PREV) || (orig_op == EXT2_EXTENT_PREV_LEAF)) { if (handle->level < handle->max_depth) { /* interior node */ if (path->visit_num > 0 ) { /* path->visit_num = 0; */ op = EXT2_EXTENT_DOWN_AND_LAST; } else if (path->left < path->entries-1) op = EXT2_EXTENT_PREV_SIB; else if (handle->level > 0) op = EXT2_EXTENT_UP; else return EXT2_ET_EXTENT_NO_PREV; } else { /* leaf node */ if (path->left < path->entries-1) op = EXT2_EXTENT_PREV_SIB; else if (handle->level > 0) op = EXT2_EXTENT_UP; else return EXT2_ET_EXTENT_NO_PREV; } if (op != EXT2_EXTENT_PREV_SIB) { #ifdef DEBUG_GET_EXTENT printf("<<<< OP = %s\n", (op == EXT2_EXTENT_DOWN_AND_LAST) ? "down/last" : ((op == EXT2_EXTENT_UP) ? "up" : "unknown")); #endif } } if (orig_op == EXT2_EXTENT_LAST_LEAF) { if ((handle->level < handle->max_depth) && (path->left == 0)) op = EXT2_EXTENT_DOWN; else op = EXT2_EXTENT_LAST_SIB; #ifdef DEBUG_GET_EXTENT printf("<<<< OP = %s\n", (op == EXT2_EXTENT_DOWN) ? "down" : "last_sib"); #endif } switch (op) { case EXT2_EXTENT_CURRENT: ix = path->curr; break; case EXT2_EXTENT_ROOT: handle->level = 0; path = handle->path + handle->level; case EXT2_EXTENT_FIRST_SIB: path->left = path->entries; path->curr = 0; case EXT2_EXTENT_NEXT_SIB: if (path->left <= 0) return EXT2_ET_EXTENT_NO_NEXT; if (path->curr) { ix = path->curr; ix++; } else { eh = (struct ext3_extent_header *) path->buf; ix = EXT_FIRST_INDEX(eh); } path->left--; path->curr = ix; path->visit_num = 0; break; case EXT2_EXTENT_PREV_SIB: if (!path->curr || path->left+1 >= path->entries) return EXT2_ET_EXTENT_NO_PREV; ix = path->curr; ix--; path->curr = ix; path->left++; if (handle->level < handle->max_depth) path->visit_num = 1; break; case EXT2_EXTENT_LAST_SIB: eh = (struct ext3_extent_header *) path->buf; path->curr = EXT_LAST_EXTENT(eh); ix = path->curr; path->left = 0; path->visit_num = 0; break; case EXT2_EXTENT_UP: if (handle->level <= 0) return EXT2_ET_EXTENT_NO_UP; handle->level--; path--; ix = path->curr; if ((orig_op == EXT2_EXTENT_PREV) || (orig_op == EXT2_EXTENT_PREV_LEAF)) path->visit_num = 0; break; case EXT2_EXTENT_DOWN: case EXT2_EXTENT_DOWN_AND_LAST: if (!path->curr ||(handle->level >= handle->max_depth)) return EXT2_ET_EXTENT_NO_DOWN; ix = path->curr; newpath = path + 1; if (!newpath->buf) { retval = ext2fs_get_mem(handle->fs->blocksize, &newpath->buf); if (retval) return retval; } blk = ext2fs_le32_to_cpu(ix->ei_leaf) + ((__u64) ext2fs_le16_to_cpu(ix->ei_leaf_hi) << 32); if ((handle->fs->flags & EXT2_FLAG_IMAGE_FILE) && (handle->fs->io != handle->fs->image_io)) memset(newpath->buf, 0, handle->fs->blocksize); else { retval = io_channel_read_blk(handle->fs->io, blk, 1, newpath->buf); if (retval) return retval; } handle->level++; eh = (struct ext3_extent_header *) newpath->buf; retval = ext2fs_extent_header_verify(eh, handle->fs->blocksize); if (retval) { handle->level--; return retval; } newpath->left = newpath->entries = ext2fs_le16_to_cpu(eh->eh_entries); newpath->max_entries = ext2fs_le16_to_cpu(eh->eh_max); if (path->left > 0) { ix++; newpath->end_blk = ext2fs_le32_to_cpu(ix->ei_block); } else newpath->end_blk = path->end_blk; path = newpath; if (op == EXT2_EXTENT_DOWN) { ix = EXT_FIRST_INDEX((struct ext3_extent_header *) eh); path->curr = ix; path->left = path->entries - 1; path->visit_num = 0; } else { ix = EXT_LAST_INDEX((struct ext3_extent_header *) eh); path->curr = ix; path->left = 0; if (handle->level < handle->max_depth) path->visit_num = 1; } #ifdef DEBUG_GET_EXTENT printf("Down to level %d/%d, end_blk=%llu\n", handle->level, handle->max_depth, path->end_blk); #endif break; default: return EXT2_ET_OP_NOT_SUPPORTED; } if (!ix) return EXT2_ET_NO_CURRENT_NODE; extent->e_flags = 0; #ifdef DEBUG_GET_EXTENT printf("(Left %d)\n", path->left); #endif if (handle->level == handle->max_depth) { ex = (struct ext3_extent *) ix; extent->e_pblk = ext2fs_le32_to_cpu(ex->ee_start) + ((__u64) ext2fs_le16_to_cpu(ex->ee_start_hi) << 32); extent->e_lblk = ext2fs_le32_to_cpu(ex->ee_block); extent->e_len = ext2fs_le16_to_cpu(ex->ee_len); extent->e_flags |= EXT2_EXTENT_FLAGS_LEAF; if (extent->e_len > EXT_INIT_MAX_LEN) { extent->e_len -= EXT_INIT_MAX_LEN; extent->e_flags |= EXT2_EXTENT_FLAGS_UNINIT; } } else { extent->e_pblk = ext2fs_le32_to_cpu(ix->ei_leaf) + ((__u64) ext2fs_le16_to_cpu(ix->ei_leaf_hi) << 32); extent->e_lblk = ext2fs_le32_to_cpu(ix->ei_block); if (path->left > 0) { ix++; end_blk = ext2fs_le32_to_cpu(ix->ei_block); } else end_blk = path->end_blk; extent->e_len = end_blk - extent->e_lblk; } if (path->visit_num) extent->e_flags |= EXT2_EXTENT_FLAGS_SECOND_VISIT; if (((orig_op == EXT2_EXTENT_NEXT_LEAF) || (orig_op == EXT2_EXTENT_PREV_LEAF)) && (handle->level != handle->max_depth)) goto retry; if ((orig_op == EXT2_EXTENT_LAST_LEAF) && ((handle->level != handle->max_depth) || (path->left != 0))) goto retry; return 0; } static errcode_t update_path(ext2_extent_handle_t handle) { blk64_t blk; errcode_t retval; struct ext3_extent_idx *ix; if (handle->level == 0) { retval = ext2fs_write_inode(handle->fs, handle->ino, handle->inode); } else { ix = handle->path[handle->level - 1].curr; blk = ext2fs_le32_to_cpu(ix->ei_leaf) + ((__u64) ext2fs_le16_to_cpu(ix->ei_leaf_hi) << 32); retval = io_channel_write_blk(handle->fs->io, blk, 1, handle->path[handle->level].buf); } return retval; } #if 0 errcode_t ext2fs_extent_save_path(ext2_extent_handle_t handle, ext2_extent_path_t *ret_path) { ext2_extent_path_t save_path; struct ext2fs_extent extent; struct ext2_extent_info info; errcode_t retval; retval = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT, &extent); if (retval) return retval; retval = ext2fs_extent_get_info(handle, &info); if (retval) return retval; retval = ext2fs_get_mem(sizeof(struct ext2_extent_path), &save_path); if (retval) return retval; memset(save_path, 0, sizeof(struct ext2_extent_path)); save_path->magic = EXT2_ET_MAGIC_EXTENT_PATH; save_path->leaf_height = info.max_depth - info.curr_level - 1; save_path->lblk = extent.e_lblk; *ret_path = save_path; return 0; } errcode_t ext2fs_extent_free_path(ext2_extent_path_t path) { EXT2_CHECK_MAGIC(path, EXT2_ET_MAGIC_EXTENT_PATH); ext2fs_free_mem(&path); return 0; } #endif /* * Go to the node at leaf_level which contains logical block blk. * * leaf_level is height from the leaf node level, i.e. * leaf_level 0 is at leaf node, leaf_level 1 is 1 above etc. * * If "blk" has no mapping (hole) then handle is left at last * extent before blk. */ static errcode_t extent_goto(ext2_extent_handle_t handle, int leaf_level, blk64_t blk) { struct ext2fs_extent extent; errcode_t retval; retval = ext2fs_extent_get(handle, EXT2_EXTENT_ROOT, &extent); if (retval) { if (retval == EXT2_ET_EXTENT_NO_NEXT) retval = EXT2_ET_EXTENT_NOT_FOUND; return retval; } if (leaf_level > handle->max_depth) { #ifdef DEBUG printf("leaf level %d greater than tree depth %d\n", leaf_level, handle->max_depth); #endif return EXT2_ET_OP_NOT_SUPPORTED; } #ifdef DEBUG printf("goto extent ino %u, level %d, %llu\n", handle->ino, leaf_level, blk); #endif #ifdef DEBUG_GOTO_EXTENTS dbg_print_extent("root", &extent); #endif while (1) { if (handle->max_depth - handle->level == leaf_level) { /* block is in this &extent */ if ((blk >= extent.e_lblk) && (blk < extent.e_lblk + extent.e_len)) return 0; if (blk < extent.e_lblk) { retval = ext2fs_extent_get(handle, EXT2_EXTENT_PREV_SIB, &extent); return EXT2_ET_EXTENT_NOT_FOUND; } retval = ext2fs_extent_get(handle, EXT2_EXTENT_NEXT_SIB, &extent); if (retval == EXT2_ET_EXTENT_NO_NEXT) return EXT2_ET_EXTENT_NOT_FOUND; if (retval) return retval; continue; } retval = ext2fs_extent_get(handle, EXT2_EXTENT_NEXT_SIB, &extent); if (retval == EXT2_ET_EXTENT_NO_NEXT) goto go_down; if (retval) return retval; #ifdef DEBUG_GOTO_EXTENTS dbg_print_extent("next", &extent); #endif if (blk == extent.e_lblk) goto go_down; if (blk > extent.e_lblk) continue; retval = ext2fs_extent_get(handle, EXT2_EXTENT_PREV_SIB, &extent); if (retval) return retval; #ifdef DEBUG_GOTO_EXTENTS dbg_print_extent("prev", &extent); #endif go_down: retval = ext2fs_extent_get(handle, EXT2_EXTENT_DOWN, &extent); if (retval) return retval; #ifdef DEBUG_GOTO_EXTENTS dbg_print_extent("down", &extent); #endif } } errcode_t ext2fs_extent_goto(ext2_extent_handle_t handle, blk64_t blk) { return extent_goto(handle, 0, blk); } /* * Traverse back up to root fixing parents of current node as needed. * * If we changed start of first entry in a node, fix parent index start * and so on. * * Safe to call for any position in node; if not at the first entry, * will simply return. */ static errcode_t ext2fs_extent_fix_parents(ext2_extent_handle_t handle) { int retval = 0; blk64_t start; struct extent_path *path; struct ext2fs_extent extent; EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EXTENT_HANDLE); if (!(handle->fs->flags & EXT2_FLAG_RW)) return EXT2_ET_RO_FILSYS; if (!handle->path) return EXT2_ET_NO_CURRENT_NODE; path = handle->path + handle->level; if (!path->curr) return EXT2_ET_NO_CURRENT_NODE; retval = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT, &extent); if (retval) goto done; /* modified node's start block */ start = extent.e_lblk; /* traverse up until index not first, or startblk matches, or top */ while (handle->level > 0 && (path->left == path->entries - 1)) { retval = ext2fs_extent_get(handle, EXT2_EXTENT_UP, &extent); if (retval) goto done; if (extent.e_lblk == start) break; path = handle->path + handle->level; extent.e_len += (extent.e_lblk - start); extent.e_lblk = start; retval = ext2fs_extent_replace(handle, 0, &extent); if (retval) goto done; update_path(handle); } /* put handle back to where we started */ retval = ext2fs_extent_goto(handle, start); done: return retval; } errcode_t ext2fs_extent_replace(ext2_extent_handle_t handle, int flags EXT2FS_ATTR((unused)), struct ext2fs_extent *extent) { struct extent_path *path; struct ext3_extent_idx *ix; struct ext3_extent *ex; EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EXTENT_HANDLE); if (!(handle->fs->flags & EXT2_FLAG_RW)) return EXT2_ET_RO_FILSYS; if (!handle->path) return EXT2_ET_NO_CURRENT_NODE; path = handle->path + handle->level; if (!path->curr) return EXT2_ET_NO_CURRENT_NODE; #ifdef DEBUG printf("extent replace: %u ", handle->ino); dbg_print_extent(0, extent); #endif if (handle->level == handle->max_depth) { ex = path->curr; ex->ee_block = ext2fs_cpu_to_le32(extent->e_lblk); ex->ee_start = ext2fs_cpu_to_le32(extent->e_pblk & 0xFFFFFFFF); ex->ee_start_hi = ext2fs_cpu_to_le16(extent->e_pblk >> 32); if (extent->e_flags & EXT2_EXTENT_FLAGS_UNINIT) { if (extent->e_len > EXT_UNINIT_MAX_LEN) return EXT2_ET_EXTENT_INVALID_LENGTH; ex->ee_len = ext2fs_cpu_to_le16(extent->e_len + EXT_INIT_MAX_LEN); } else { if (extent->e_len > EXT_INIT_MAX_LEN) return EXT2_ET_EXTENT_INVALID_LENGTH; ex->ee_len = ext2fs_cpu_to_le16(extent->e_len); } } else { ix = path->curr; ix->ei_leaf = ext2fs_cpu_to_le32(extent->e_pblk & 0xFFFFFFFF); ix->ei_leaf_hi = ext2fs_cpu_to_le16(extent->e_pblk >> 32); ix->ei_block = ext2fs_cpu_to_le32(extent->e_lblk); ix->ei_unused = 0; } update_path(handle); return 0; } /* * allocate a new block, move half the current node to it, and update parent * * handle will be left pointing at original record. */ static errcode_t extent_node_split(ext2_extent_handle_t handle) { errcode_t retval = 0; blk_t new_node_pblk; blk64_t new_node_start; blk64_t orig_lblk; blk64_t goal_blk = 0; int orig_height; char *block_buf = NULL; struct ext2fs_extent extent; struct extent_path *path, *newpath = 0; struct ext3_extent_header *eh, *neweh; int tocopy; int new_root = 0; struct ext2_extent_info info; /* basic sanity */ EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EXTENT_HANDLE); if (!(handle->fs->flags & EXT2_FLAG_RW)) return EXT2_ET_RO_FILSYS; if (!handle->path) return EXT2_ET_NO_CURRENT_NODE; #ifdef DEBUG printf("splitting node at level %d\n", handle->level); #endif retval = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT, &extent); if (retval) goto done; retval = ext2fs_extent_get_info(handle, &info); if (retval) goto done; /* save the position we were originally splitting... */ orig_height = info.max_depth - info.curr_level; orig_lblk = extent.e_lblk; /* Is there room in the parent for a new entry? */ if (handle->level && (handle->path[handle->level - 1].entries >= handle->path[handle->level - 1].max_entries)) { #ifdef DEBUG printf("parent level %d full; splitting it too\n", handle->level - 1); #endif /* split the parent */ retval = ext2fs_extent_get(handle, EXT2_EXTENT_UP, &extent); if (retval) goto done; goal_blk = extent.e_pblk; retval = extent_node_split(handle); if (retval) goto done; /* get handle back to our original split position */ retval = extent_goto(handle, orig_height, orig_lblk); if (retval) goto done; } /* At this point, parent should have room for this split */ path = handle->path + handle->level; if (!path->curr) return EXT2_ET_NO_CURRENT_NODE; /* extent header of the current node we'll split */ eh = (struct ext3_extent_header *)path->buf; /* splitting root level means moving them all out */ if (handle->level == 0) { new_root = 1; tocopy = ext2fs_le16_to_cpu(eh->eh_entries); retval = ext2fs_get_mem(((handle->max_depth+2) * sizeof(struct extent_path)), &newpath); if (retval) goto done; memset(newpath, 0, ((handle->max_depth+2) * sizeof(struct extent_path))); } else { tocopy = ext2fs_le16_to_cpu(eh->eh_entries) / 2; } #ifdef DEBUG printf("will copy out %d of %d entries at level %d\n", tocopy, ext2fs_le16_to_cpu(eh->eh_entries), handle->level); #endif if (!tocopy) { #ifdef DEBUG printf("Nothing to copy to new block!\n"); #endif retval = EXT2_ET_CANT_SPLIT_EXTENT; goto done; } /* first we need a new block, or can do nothing. */ block_buf = malloc(handle->fs->blocksize); if (!block_buf) { retval = ENOMEM; goto done; } if (!goal_blk) { dgrp_t group = ext2fs_group_of_ino(handle->fs, handle->ino); __u8 log_flex = handle->fs->super->s_log_groups_per_flex; if (log_flex) group = group & ~((1 << (log_flex)) - 1); goal_blk = (group * handle->fs->super->s_blocks_per_group) + handle->fs->super->s_first_data_block; } retval = ext2fs_alloc_block(handle->fs, (blk_t) goal_blk, block_buf, &new_node_pblk); if (retval) goto done; #ifdef DEBUG printf("will copy to new node at block %lu\n", (unsigned long) new_node_pblk); #endif /* Copy data into new block buffer */ /* First the header for the new block... */ neweh = (struct ext3_extent_header *) block_buf; memcpy(neweh, eh, sizeof(struct ext3_extent_header)); neweh->eh_entries = ext2fs_cpu_to_le16(tocopy); neweh->eh_max = ext2fs_cpu_to_le16((handle->fs->blocksize - sizeof(struct ext3_extent_header)) / sizeof(struct ext3_extent)); /* then the entries for the new block... */ memcpy(EXT_FIRST_INDEX(neweh), EXT_FIRST_INDEX(eh) + (ext2fs_le16_to_cpu(eh->eh_entries) - tocopy), sizeof(struct ext3_extent_idx) * tocopy); new_node_start = ext2fs_le32_to_cpu(EXT_FIRST_INDEX(neweh)->ei_block); /* ...and write the new node block out to disk. */ retval = io_channel_write_blk(handle->fs->io, new_node_pblk, 1, block_buf); if (retval) goto done; /* OK! we've created the new node; now adjust the tree */ /* current path now has fewer active entries, we copied some out */ if (handle->level == 0) { memcpy(newpath, path, sizeof(struct extent_path) * (handle->max_depth+1)); handle->path = newpath; newpath = path; path = handle->path; path->entries = 1; path->left = path->max_entries - 1; handle->max_depth++; eh->eh_depth = ext2fs_cpu_to_le16(handle->max_depth); } else { path->entries -= tocopy; path->left -= tocopy; } eh->eh_entries = ext2fs_cpu_to_le16(path->entries); /* this writes out the node, incl. the modified header */ retval = update_path(handle); if (retval) goto done; /* now go up and insert/replace index for new node we created */ if (new_root) { retval = ext2fs_extent_get(handle, EXT2_EXTENT_FIRST_SIB, &extent); if (retval) goto done; extent.e_lblk = new_node_start; extent.e_pblk = new_node_pblk; extent.e_len = handle->path[0].end_blk - extent.e_lblk; retval = ext2fs_extent_replace(handle, 0, &extent); if (retval) goto done; } else { __u32 new_node_length; retval = ext2fs_extent_get(handle, EXT2_EXTENT_UP, &extent); /* will insert after this one; it's length is shorter now */ new_node_length = new_node_start - extent.e_lblk; extent.e_len -= new_node_length; retval = ext2fs_extent_replace(handle, 0, &extent); if (retval) goto done; /* now set up the new extent and insert it */ extent.e_lblk = new_node_start; extent.e_pblk = new_node_pblk; extent.e_len = new_node_length; retval = ext2fs_extent_insert(handle, EXT2_EXTENT_INSERT_AFTER, &extent); if (retval) goto done; } /* get handle back to our original position */ retval = extent_goto(handle, orig_height, orig_lblk); if (retval) goto done; /* new node hooked in, so update inode block count (do this here?) */ handle->inode->i_blocks += handle->fs->blocksize / 512; retval = ext2fs_write_inode(handle->fs, handle->ino, handle->inode); if (retval) goto done; done: if (newpath) ext2fs_free_mem(&newpath); free(block_buf); return retval; } errcode_t ext2fs_extent_insert(ext2_extent_handle_t handle, int flags, struct ext2fs_extent *extent) { struct extent_path *path; struct ext3_extent_idx *ix; struct ext3_extent_header *eh; errcode_t retval; EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EXTENT_HANDLE); if (!(handle->fs->flags & EXT2_FLAG_RW)) return EXT2_ET_RO_FILSYS; if (!handle->path) return EXT2_ET_NO_CURRENT_NODE; #ifdef DEBUG printf("extent insert: %u ", handle->ino); dbg_print_extent(0, extent); #endif path = handle->path + handle->level; if (path->entries >= path->max_entries) { if (flags & EXT2_EXTENT_INSERT_NOSPLIT) { return EXT2_ET_CANT_INSERT_EXTENT; } else { #ifdef DEBUG printf("node full (level %d) - splitting\n", handle->level); #endif retval = extent_node_split(handle); if (retval) return retval; path = handle->path + handle->level; } } eh = (struct ext3_extent_header *) path->buf; if (path->curr) { ix = path->curr; if (flags & EXT2_EXTENT_INSERT_AFTER) { ix++; path->left--; } } else ix = EXT_FIRST_INDEX(eh); path->curr = ix; if (path->left >= 0) memmove(ix + 1, ix, (path->left+1) * sizeof(struct ext3_extent_idx)); path->left++; path->entries++; eh = (struct ext3_extent_header *) path->buf; eh->eh_entries = ext2fs_cpu_to_le16(path->entries); retval = ext2fs_extent_replace(handle, 0, extent); if (retval) goto errout; retval = update_path(handle); if (retval) goto errout; return 0; errout: ext2fs_extent_delete(handle, 0); return retval; } /* * Sets the physical block for a logical file block in the extent tree. * * May: map unmapped, unmap mapped, or remap mapped blocks. * * Mapping an unmapped block adds a single-block extent. * * Unmapping first or last block modifies extent in-place * - But may need to fix parent's starts too in first-block case * * Mapping any unmapped block requires adding a (single-block) extent * and inserting into proper point in tree. * * Modifying (unmapping or remapping) a block in the middle * of an extent requires splitting the extent. * - Remapping case requires new single-block extent. * * Remapping first or last block adds an extent. * * We really need extent adding to be smart about merging. */ errcode_t ext2fs_extent_set_bmap(ext2_extent_handle_t handle, blk64_t logical, blk64_t physical, int flags) { errcode_t ec, retval = 0; int mapped = 1; /* logical is mapped? */ int orig_height; int extent_uninit = 0; int prev_uninit = 0; int next_uninit = 0; int new_uninit = 0; int max_len = EXT_INIT_MAX_LEN; int has_prev, has_next; blk64_t orig_lblk; struct extent_path *path; struct ext2fs_extent extent, next_extent, prev_extent; struct ext2fs_extent newextent; struct ext2_extent_info info; EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EXTENT_HANDLE); #ifdef DEBUG printf("set_bmap ino %u log %lld phys %lld flags %d\n", handle->ino, logical, physical, flags); #endif if (!(handle->fs->flags & EXT2_FLAG_RW)) return EXT2_ET_RO_FILSYS; if (!handle->path) return EXT2_ET_NO_CURRENT_NODE; path = handle->path + handle->level; if (flags & EXT2_EXTENT_SET_BMAP_UNINIT) { new_uninit = 1; max_len = EXT_UNINIT_MAX_LEN; } /* if (re)mapping, set up new extent to insert */ if (physical) { newextent.e_len = 1; newextent.e_pblk = physical; newextent.e_lblk = logical; newextent.e_flags = EXT2_EXTENT_FLAGS_LEAF; if (new_uninit) newextent.e_flags |= EXT2_EXTENT_FLAGS_UNINIT; } /* special case if the extent tree is completely empty */ if ((handle->max_depth == 0) && (path->entries == 0)) { retval = ext2fs_extent_insert(handle, 0, &newextent); return retval; } /* save our original location in the extent tree */ if ((retval = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT, &extent))) { if (retval != EXT2_ET_NO_CURRENT_NODE) return retval; memset(&extent, 0, sizeof(extent)); } if ((retval = ext2fs_extent_get_info(handle, &info))) return retval; orig_height = info.max_depth - info.curr_level; orig_lblk = extent.e_lblk; /* go to the logical spot we want to (re/un)map */ retval = ext2fs_extent_goto(handle, logical); if (retval) { if (retval == EXT2_ET_EXTENT_NOT_FOUND) { retval = 0; mapped = 0; if (!physical) { #ifdef DEBUG printf("block %llu already unmapped\n", logical); #endif goto done; } } else goto done; } /* * This may be the extent *before* the requested logical, * if it's currently unmapped. * * Get the previous and next leaf extents, if they are present. */ retval = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT, &extent); if (retval) goto done; if (extent.e_flags & EXT2_EXTENT_FLAGS_UNINIT) extent_uninit = 1; retval = ext2fs_extent_get(handle, EXT2_EXTENT_NEXT_LEAF, &next_extent); if (retval) { has_next = 0; if (retval != EXT2_ET_EXTENT_NO_NEXT) goto done; } else { dbg_print_extent("set_bmap: next_extent", &next_extent); has_next = 1; if (next_extent.e_flags & EXT2_EXTENT_FLAGS_UNINIT) next_uninit = 1; } retval = ext2fs_extent_goto(handle, logical); if (retval && retval != EXT2_ET_EXTENT_NOT_FOUND) goto done; retval = ext2fs_extent_get(handle, EXT2_EXTENT_PREV_LEAF, &prev_extent); if (retval) { has_prev = 0; if (retval != EXT2_ET_EXTENT_NO_PREV) goto done; } else { has_prev = 1; dbg_print_extent("set_bmap: prev_extent", &prev_extent); if (prev_extent.e_flags & EXT2_EXTENT_FLAGS_UNINIT) prev_uninit = 1; } retval = ext2fs_extent_goto(handle, logical); if (retval && retval != EXT2_ET_EXTENT_NOT_FOUND) goto done; /* check if already pointing to the requested physical */ if (mapped && (new_uninit == extent_uninit) && (extent.e_pblk + (logical - extent.e_lblk) == physical)) { #ifdef DEBUG printf("physical block (at %llu) unchanged\n", logical); #endif goto done; } if (!mapped) { #ifdef DEBUG printf("mapping unmapped logical block %llu\n", logical); #endif if ((logical == extent.e_lblk + extent.e_len) && (physical == extent.e_pblk + extent.e_len) && (new_uninit == extent_uninit) && ((int) extent.e_len < max_len-1)) { extent.e_len++; retval = ext2fs_extent_replace(handle, 0, &extent); } else if ((logical == extent.e_lblk - 1) && (physical == extent.e_pblk - 1) && (new_uninit == extent_uninit) && ((int) extent.e_len < max_len - 1)) { extent.e_len++; extent.e_lblk--; extent.e_pblk--; retval = ext2fs_extent_replace(handle, 0, &extent); } else if (has_next && (logical == next_extent.e_lblk - 1) && (physical == next_extent.e_pblk - 1) && (new_uninit == next_uninit) && ((int) next_extent.e_len < max_len - 1)) { retval = ext2fs_extent_get(handle, EXT2_EXTENT_NEXT_LEAF, &next_extent); if (retval) goto done; next_extent.e_len++; next_extent.e_lblk--; next_extent.e_pblk--; retval = ext2fs_extent_replace(handle, 0, &next_extent); } else if (logical < extent.e_lblk) retval = ext2fs_extent_insert(handle, 0, &newextent); else retval = ext2fs_extent_insert(handle, EXT2_EXTENT_INSERT_AFTER, &newextent); if (retval) goto done; retval = ext2fs_extent_fix_parents(handle); if (retval) goto done; } else if ((logical == extent.e_lblk) && (extent.e_len == 1)) { #ifdef DEBUG printf("(re/un)mapping only block in extent\n"); #endif if (physical) { retval = ext2fs_extent_replace(handle, 0, &newextent); } else { retval = ext2fs_extent_delete(handle, 0); if (retval) goto done; ec = ext2fs_extent_fix_parents(handle); if (ec != EXT2_ET_NO_CURRENT_NODE) retval = ec; } if (retval) goto done; } else if (logical == extent.e_lblk + extent.e_len - 1) { #ifdef DEBUG printf("(re/un)mapping last block in extent\n"); #endif if (physical) { if (has_next && (logical == (next_extent.e_lblk - 1)) && (physical == (next_extent.e_pblk - 1)) && (new_uninit == next_uninit) && ((int) next_extent.e_len < max_len - 1)) { retval = ext2fs_extent_get(handle, EXT2_EXTENT_NEXT_LEAF, &next_extent); if (retval) goto done; next_extent.e_len++; next_extent.e_lblk--; next_extent.e_pblk--; retval = ext2fs_extent_replace(handle, 0, &next_extent); if (retval) goto done; } else retval = ext2fs_extent_insert(handle, EXT2_EXTENT_INSERT_AFTER, &newextent); if (retval) goto done; /* Now pointing at inserted extent; move back to prev */ retval = ext2fs_extent_get(handle, EXT2_EXTENT_PREV_LEAF, &extent); if (retval) goto done; } extent.e_len--; retval = ext2fs_extent_replace(handle, 0, &extent); if (retval) goto done; } else if (logical == extent.e_lblk) { #ifdef DEBUG printf("(re/un)mapping first block in extent\n"); #endif if (physical) { if (has_prev && (logical == (prev_extent.e_lblk + prev_extent.e_len)) && (physical == (prev_extent.e_pblk + prev_extent.e_len)) && (new_uninit == prev_uninit) && ((int) prev_extent.e_len < max_len-1)) { retval = ext2fs_extent_get(handle, EXT2_EXTENT_PREV_LEAF, &prev_extent); if (retval) goto done; prev_extent.e_len++; retval = ext2fs_extent_replace(handle, 0, &prev_extent); } else retval = ext2fs_extent_insert(handle, 0, &newextent); if (retval) goto done; retval = ext2fs_extent_get(handle, EXT2_EXTENT_NEXT_LEAF, &extent); if (retval) goto done; } extent.e_pblk++; extent.e_lblk++; extent.e_len--; retval = ext2fs_extent_replace(handle, 0, &extent); if (retval) goto done; } else { __u32 orig_length; #ifdef DEBUG printf("(re/un)mapping in middle of extent\n"); #endif /* need to split this extent; later */ orig_length = extent.e_len; /* shorten pre-split extent */ extent.e_len = (logical - extent.e_lblk); retval = ext2fs_extent_replace(handle, 0, &extent); if (retval) goto done; /* insert our new extent, if any */ if (physical) { /* insert new extent after current */ retval = ext2fs_extent_insert(handle, EXT2_EXTENT_INSERT_AFTER, &newextent); if (retval) goto done; } /* add post-split extent */ extent.e_pblk += extent.e_len + 1; extent.e_lblk += extent.e_len + 1; extent.e_len = orig_length - extent.e_len - 1; retval = ext2fs_extent_insert(handle, EXT2_EXTENT_INSERT_AFTER, &extent); if (retval) goto done; } done: /* get handle back to its position */ if (orig_height > handle->max_depth) orig_height = handle->max_depth; /* In case we shortened the tree */ extent_goto(handle, orig_height, orig_lblk); return retval; } errcode_t ext2fs_extent_delete(ext2_extent_handle_t handle, int flags) { struct extent_path *path; char *cp; struct ext3_extent_header *eh; errcode_t retval = 0; EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EXTENT_HANDLE); if (!(handle->fs->flags & EXT2_FLAG_RW)) return EXT2_ET_RO_FILSYS; if (!handle->path) return EXT2_ET_NO_CURRENT_NODE; #ifdef DEBUG { struct ext2fs_extent extent; retval = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT, &extent); if (retval == 0) { printf("extent delete %u ", handle->ino); dbg_print_extent(0, &extent); } } #endif path = handle->path + handle->level; if (!path->curr) return EXT2_ET_NO_CURRENT_NODE; cp = path->curr; if (path->left) { memmove(cp, cp + sizeof(struct ext3_extent_idx), path->left * sizeof(struct ext3_extent_idx)); path->left--; } else { struct ext3_extent_idx *ix = path->curr; ix--; path->curr = ix; } if (--path->entries == 0) path->curr = 0; /* if non-root node has no entries left, remove it & parent ptr to it */ if (path->entries == 0 && handle->level) { if (!(flags & EXT2_EXTENT_DELETE_KEEP_EMPTY)) { struct ext2fs_extent extent; retval = ext2fs_extent_get(handle, EXT2_EXTENT_UP, &extent); if (retval) return retval; retval = ext2fs_extent_delete(handle, flags); handle->inode->i_blocks -= handle->fs->blocksize / 512; retval = ext2fs_write_inode(handle->fs, handle->ino, handle->inode); ext2fs_block_alloc_stats(handle->fs, extent.e_pblk, -1); } } else { eh = (struct ext3_extent_header *) path->buf; eh->eh_entries = ext2fs_cpu_to_le16(path->entries); if ((path->entries == 0) && (handle->level == 0)) eh->eh_depth = handle->max_depth = 0; retval = update_path(handle); } return retval; } errcode_t ext2fs_extent_get_info(ext2_extent_handle_t handle, struct ext2_extent_info *info) { struct extent_path *path; EXT2_CHECK_MAGIC(handle, EXT2_ET_MAGIC_EXTENT_HANDLE); memset(info, 0, sizeof(struct ext2_extent_info)); path = handle->path + handle->level; if (path) { if (path->curr) info->curr_entry = ((char *) path->curr - path->buf) / sizeof(struct ext3_extent_idx); else info->curr_entry = 0; info->num_entries = path->entries; info->max_entries = path->max_entries; info->bytes_avail = (path->max_entries - path->entries) * sizeof(struct ext3_extent); } info->curr_level = handle->level; info->max_depth = handle->max_depth; info->max_lblk = ((__u64) 1 << 32) - 1; info->max_pblk = ((__u64) 1 << 48) - 1; info->max_len = (1UL << 15); info->max_uninit_len = (1UL << 15) - 1; return 0; } #ifdef DEBUG #include "ss/ss.h" #include "debugfs.h" /* * Hook in new commands into debugfs */ const char *debug_prog_name = "tst_extents"; extern ss_request_table extent_cmds; ss_request_table *extra_cmds = &extent_cmds; ext2_ino_t current_ino = 0; ext2_extent_handle_t current_handle; int common_extent_args_process(int argc, char *argv[], int min_argc, int max_argc, const char *cmd, const char *usage, int flags) { if (common_args_process(argc, argv, min_argc, max_argc, cmd, usage, flags)) return 1; if (!current_handle) { com_err(cmd, 0, "Extent handle not open"); return 1; } return 0; } void do_inode(int argc, char *argv[]) { ext2_ino_t inode; int i; struct ext3_extent_header *eh; errcode_t retval; if (check_fs_open(argv[0])) return; if (argc == 1) { if (current_ino) printf("Current inode is %d\n", current_ino); else printf("No current inode\n"); return; } if (common_inode_args_process(argc, argv, &inode, 0)) { return; } current_ino = 0; retval = ext2fs_extent_open(current_fs, inode, ¤t_handle); if (retval) { com_err(argv[1], retval, "while opening extent handle"); return; } current_ino = inode; printf("Loaded inode %d\n", current_ino); return; } void generic_goto_node(char *cmd_name, int op) { struct ext2fs_extent extent; errcode_t retval; if (check_fs_open(cmd_name)) return; if (!current_handle) { com_err(cmd_name, 0, "Extent handle not open"); return; } retval = ext2fs_extent_get(current_handle, op, &extent); if (retval) { com_err(cmd_name, retval, 0); return; } dbg_print_extent(0, &extent); } void do_current_node(int argc, char *argv[]) { generic_goto_node(argv[0], EXT2_EXTENT_CURRENT); } void do_root_node(int argc, char *argv[]) { generic_goto_node(argv[0], EXT2_EXTENT_ROOT); } void do_last_leaf(int argc, char *argv[]) { generic_goto_node(argv[0], EXT2_EXTENT_LAST_LEAF); } void do_first_sib(int argc, char *argv[]) { generic_goto_node(argv[0], EXT2_EXTENT_FIRST_SIB); } void do_last_sib(int argc, char *argv[]) { generic_goto_node(argv[0], EXT2_EXTENT_LAST_SIB); } void do_next_sib(int argc, char *argv[]) { generic_goto_node(argv[0], EXT2_EXTENT_NEXT_SIB); } void do_prev_sib(int argc, char *argv[]) { generic_goto_node(argv[0], EXT2_EXTENT_PREV_SIB); } void do_next_leaf(int argc, char *argv[]) { generic_goto_node(argv[0], EXT2_EXTENT_NEXT_LEAF); } void do_prev_leaf(int argc, char *argv[]) { generic_goto_node(argv[0], EXT2_EXTENT_PREV_LEAF); } void do_next(int argc, char *argv[]) { generic_goto_node(argv[0], EXT2_EXTENT_NEXT); } void do_prev(int argc, char *argv[]) { generic_goto_node(argv[0], EXT2_EXTENT_PREV); } void do_up(int argc, char *argv[]) { generic_goto_node(argv[0], EXT2_EXTENT_UP); } void do_down(int argc, char *argv[]) { generic_goto_node(argv[0], EXT2_EXTENT_DOWN); } void do_delete_node(int argc, char *argv[]) { errcode_t retval; int err; if (common_extent_args_process(argc, argv, 1, 1, "delete_node", "", CHECK_FS_RW | CHECK_FS_BITMAPS)) return; retval = ext2fs_extent_delete(current_handle, 0); if (retval) { com_err(argv[0], retval, 0); return; } if (current_handle->path && current_handle->path[0].curr) do_current_node(argc, argv); } void do_replace_node(int argc, char *argv[]) { const char *usage = "[--uninit] "; errcode_t retval; struct ext2fs_extent extent; int err; if (common_extent_args_process(argc, argv, 3, 5, "replace_node", usage, CHECK_FS_RW | CHECK_FS_BITMAPS)) return; extent.e_flags = 0; if (!strcmp(argv[1], "--uninit")) { argc--; argv++; extent.e_flags |= EXT2_EXTENT_FLAGS_UNINIT; } if (argc != 4) { fprintf(stderr, "Usage: %s %s\n", argv[0], usage); return; } extent.e_lblk = parse_ulong(argv[1], argv[0], "logical block", &err); if (err) return; extent.e_len = parse_ulong(argv[2], argv[0], "logical block", &err); if (err) return; extent.e_pblk = parse_ulong(argv[3], argv[0], "logical block", &err); if (err) return; retval = ext2fs_extent_replace(current_handle, 0, &extent); if (retval) { com_err(argv[0], retval, 0); return; } do_current_node(argc, argv); } void do_split_node(int argc, char *argv[]) { errcode_t retval; struct ext2fs_extent extent; int err; if (common_extent_args_process(argc, argv, 1, 1, "split_node", "", CHECK_FS_RW | CHECK_FS_BITMAPS)) return; retval = extent_node_split(current_handle); if (retval) { com_err(argv[0], retval, 0); return; } do_current_node(argc, argv); } void do_insert_node(int argc, char *argv[]) { const char *usage = "[--after] [--uninit] "; errcode_t retval; struct ext2fs_extent extent; char *cmd; int err; int flags = 0; if (common_extent_args_process(argc, argv, 3, 6, "insert_node", usage, CHECK_FS_RW | CHECK_FS_BITMAPS)) return; cmd = argv[0]; extent.e_flags = 0; while (argc > 2) { if (!strcmp(argv[1], "--after")) { argc--; argv++; flags |= EXT2_EXTENT_INSERT_AFTER; continue; } if (!strcmp(argv[1], "--uninit")) { argc--; argv++; extent.e_flags |= EXT2_EXTENT_FLAGS_UNINIT; continue; } break; } if (argc != 4) { fprintf(stderr, "usage: %s %s\n", cmd, usage); return; } extent.e_lblk = parse_ulong(argv[1], cmd, "logical block", &err); if (err) return; extent.e_len = parse_ulong(argv[2], cmd, "length", &err); if (err) return; extent.e_pblk = parse_ulong(argv[3], cmd, "pysical block", &err); if (err) return; retval = ext2fs_extent_insert(current_handle, flags, &extent); if (retval) { com_err(cmd, retval, 0); return; } do_current_node(argc, argv); } void do_set_bmap(int argc, char **argv) { const char *usage = "[--uninit] "; errcode_t retval; blk_t logical; blk_t physical; char *cmd = argv[0]; int flags = 0; int err; if (common_extent_args_process(argc, argv, 3, 5, "set_bmap", usage, CHECK_FS_RW | CHECK_FS_BITMAPS)) return; if (argc > 2 && !strcmp(argv[1], "--uninit")) { argc--; argv++; flags |= EXT2_EXTENT_SET_BMAP_UNINIT; } if (argc != 3) { fprintf(stderr, "Usage: %s %s\n", cmd, usage); return; } logical = parse_ulong(argv[1], cmd, "logical block", &err); if (err) return; physical = parse_ulong(argv[2], cmd, "physical block", &err); if (err) return; retval = ext2fs_extent_set_bmap(current_handle, logical, (blk64_t) physical, flags); if (retval) { com_err(cmd, retval, 0); return; } if (current_handle->path && current_handle->path[0].curr) do_current_node(argc, argv); } void do_print_all(int argc, char **argv) { const char *usage = "[--leaf-only|--reverse|--reverse-leaf]"; struct ext2fs_extent extent; errcode_t retval; errcode_t end_err = EXT2_ET_EXTENT_NO_NEXT; int op = EXT2_EXTENT_NEXT; int first_op = EXT2_EXTENT_ROOT; if (common_extent_args_process(argc, argv, 1, 2, "print_all", usage, 0)) return; if (argc == 2) { if (!strcmp(argv[1], "--leaf-only")) op = EXT2_EXTENT_NEXT_LEAF; else if (!strcmp(argv[1], "--reverse")) { op = EXT2_EXTENT_PREV; first_op = EXT2_EXTENT_LAST_LEAF; end_err = EXT2_ET_EXTENT_NO_PREV; } else if (!strcmp(argv[1], "--reverse-leaf")) { op = EXT2_EXTENT_PREV_LEAF; first_op = EXT2_EXTENT_LAST_LEAF; end_err = EXT2_ET_EXTENT_NO_PREV; } else { fprintf(stderr, "Usage: %s %s\n", argv[0], usage); return; } } retval = ext2fs_extent_get(current_handle, first_op, &extent); if (retval) { com_err(argv[0], retval, 0); return; } dbg_print_extent(0, &extent); while (1) { retval = ext2fs_extent_get(current_handle, op, &extent); if (retval == end_err) break; if (retval) { com_err(argv[0], retval, 0); return; } dbg_print_extent(0, &extent); } } void do_info(int argc, char **argv) { struct ext2fs_extent extent; struct ext2_extent_info info; errcode_t retval; if (common_extent_args_process(argc, argv, 1, 1, "info", "", 0)) return; retval = ext2fs_extent_get_info(current_handle, &info); if (retval) { com_err(argv[0], retval, 0); return; } retval = ext2fs_extent_get(current_handle, EXT2_EXTENT_CURRENT, &extent); if (retval) { com_err(argv[0], retval, 0); return; } dbg_print_extent(0, &extent); printf("Current handle location: %d/%d (max: %d, bytes %d), level %d/%d\n", info.curr_entry, info.num_entries, info.max_entries, info.bytes_avail, info.curr_level, info.max_depth); printf("\tmax lblk: %llu, max pblk: %llu\n", info.max_lblk, info.max_pblk); printf("\tmax_len: %u, max_uninit_len: %u\n", info.max_len, info.max_uninit_len); } void do_goto_block(int argc, char **argv) { struct ext2fs_extent extent; errcode_t retval; int op = EXT2_EXTENT_NEXT_LEAF; blk_t blk; int level = 0; if (common_extent_args_process(argc, argv, 2, 3, "goto_block", "block [level]", 0)) return; if (strtoblk(argv[0], argv[1], &blk)) return; if (argc == 3) if (strtoblk(argv[0], argv[2], &level)) return; retval = extent_goto(current_handle, level, (blk64_t) blk); if (retval) { com_err(argv[0], retval, "while trying to go to block %u, level %d", blk, level); return; } generic_goto_node(argv[0], EXT2_EXTENT_CURRENT); } #endif e2fsprogs-1.41.14/lib/ext2fs/sparse.c0000644031104000366760000000316711405316370015263 0ustar tytso/* * sparse.c --- find the groups in an ext2 filesystem with metadata backups * * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. * Copyright (C) 2002 Andreas Dilger. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #include #include "ext2_fs.h" #include "ext2fsP.h" static int test_root(int a, int b) { if (a == 0) return 1; while (1) { if (a == 1) return 1; if (a % b) return 0; a = a / b; } } int ext2fs_bg_has_super(ext2_filsys fs, int group_block) { if (!(fs->super->s_feature_ro_compat & EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)) return 1; if (test_root(group_block, 3) || (test_root(group_block, 5)) || test_root(group_block, 7)) return 1; return 0; } /* * Iterate through the groups which hold BACKUP superblock/GDT copies in an * ext3 filesystem. The counters should be initialized to 1, 5, and 7 before * calling this for the first time. In a sparse filesystem it will be the * sequence of powers of 3, 5, and 7: 1, 3, 5, 7, 9, 25, 27, 49, 81, ... * For a non-sparse filesystem it will be every group: 1, 2, 3, 4, ... */ unsigned int ext2fs_list_backups(ext2_filsys fs, unsigned int *three, unsigned int *five, unsigned int *seven) { unsigned int *min = three; int mult = 3; unsigned int ret; if (!(fs->super->s_feature_ro_compat & EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)) { ret = *min; *min += 1; return ret; } if (*five < *min) { min = five; mult = 5; } if (*seven < *min) { min = seven; mult = 7; } ret = *min; *min *= mult; return ret; } e2fsprogs-1.41.14/lib/ext2fs/tst_byteswap.c0000644031104000366760000000361111405316370016510 0ustar tytso/* * This testing program makes sure the byteswap functions work * * Copyright (C) 2000 by Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #include #include #include #include #if HAVE_ERRNO_H #include #endif #include "ext2_fs.h" #include "ext2fs.h" __u16 test1[] = { 0x0001, 0x0100, 0x1234, 0x3412, 0xff00, 0x00ff, 0x4000, 0x0040, 0xfeff, 0xfffe, 0x0000, 0x0000 }; __u32 test2[] = { 0x00000001, 0x01000000, 0x80000000, 0x00000080, 0x12345678, 0x78563412, 0xffff0000, 0x0000ffff, 0x00ff0000, 0x0000ff00, 0xff000000, 0x000000ff, 0x00000000, 0x00000000 }; int main(int argc, char **argv) { int i; int errors = 0; printf("Testing ext2fs_swab16\n"); i=0; do { printf("swab16(0x%04x) = 0x%04x\n", test1[i], ext2fs_swab16(test1[i])); if (ext2fs_swab16(test1[i]) != test1[i+1]) { printf("Error!!! %04x != %04x\n", ext2fs_swab16(test1[i]), test1[i+1]); errors++; } if (ext2fs_swab16(test1[i+1]) != test1[i]) { printf("Error!!! %04x != %04x\n", ext2fs_swab16(test1[i+1]), test1[i]); errors++; } i += 2; } while (test1[i] != 0); printf("Testing ext2fs_swab32\n"); i = 0; do { printf("swab32(0x%08x) = 0x%08x\n", test2[i], ext2fs_swab32(test2[i])); if (ext2fs_swab32(test2[i]) != test2[i+1]) { printf("Error!!! %04x != %04x\n", ext2fs_swab32(test2[i]), test2[i+1]); errors++; } if (ext2fs_swab32(test2[i+1]) != test2[i]) { printf("Error!!! %04x != %04x\n", ext2fs_swab32(test2[i+1]), test2[i]); errors++; } i += 2; } while (test2[i] != 0); if (!errors) printf("No errors found in the byteswap implementation!\n"); return errors; } e2fsprogs-1.41.14/lib/ext2fs/jfs_compat.h0000644031104000366760000000262311374366235016126 0ustar tytso #ifndef _JFS_COMPAT_H #define _JFS_COMPAT_H #include "kernel-list.h" #include #ifdef HAVE_NETINET_IN_H #include #endif #define printk printf #define KERN_ERR "" #define KERN_DEBUG "" #define READ 0 #define WRITE 1 #define cpu_to_be32(n) htonl(n) #define be32_to_cpu(n) ntohl(n) typedef unsigned int tid_t; typedef struct journal_s journal_t; struct buffer_head; struct inode; struct journal_s { unsigned long j_flags; int j_errno; struct buffer_head * j_sb_buffer; struct journal_superblock_s *j_superblock; int j_format_version; unsigned long j_head; unsigned long j_tail; unsigned long j_free; unsigned long j_first, j_last; kdev_t j_dev; kdev_t j_fs_dev; int j_blocksize; unsigned int j_blk_offset; unsigned int j_maxlen; struct inode * j_inode; tid_t j_tail_sequence; tid_t j_transaction_sequence; __u8 j_uuid[16]; struct jbd_revoke_table_s *j_revoke; tid_t j_failed_commit; }; #define J_ASSERT(assert) \ do { if (!(assert)) { \ printf ("Assertion failure in %s() at %s line %d: " \ "\"%s\"\n", \ __FUNCTION__, __FILE__, __LINE__, # assert); \ fatal_error(e2fsck_global_ctx, 0); \ } } while (0) #define is_journal_abort(x) 0 #define BUFFER_TRACE(bh, info) do {} while (0) /* Need this so we can compile with configure --enable-gcc-wall */ #ifdef NO_INLINE_FUNCS #define inline #endif #endif /* _JFS_COMPAT_H */ e2fsprogs-1.41.14/lib/ext2fs/initialize.c0000644031104000116100000003246211504417000016566 0ustar tytsoeng/* * initialize.c --- initialize a filesystem handle given superblock * parameters. Used by mke2fs when initializing a filesystem. * * Copyright (C) 1994, 1995, 1996 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #include #include #if HAVE_SYS_STAT_H #include #endif #if HAVE_SYS_TYPES_H #include #endif #include "ext2_fs.h" #include "ext2fs.h" #if defined(__linux__) && defined(EXT2_OS_LINUX) #define CREATOR_OS EXT2_OS_LINUX #else #if defined(__GNU__) && defined(EXT2_OS_HURD) #define CREATOR_OS EXT2_OS_HURD #else #if defined(__FreeBSD__) && defined(EXT2_OS_FREEBSD) #define CREATOR_OS EXT2_OS_FREEBSD #else #if defined(LITES) && defined(EXT2_OS_LITES) #define CREATOR_OS EXT2_OS_LITES #else #define CREATOR_OS EXT2_OS_LINUX /* by default */ #endif /* defined(LITES) && defined(EXT2_OS_LITES) */ #endif /* defined(__FreeBSD__) && defined(EXT2_OS_FREEBSD) */ #endif /* defined(__GNU__) && defined(EXT2_OS_HURD) */ #endif /* defined(__linux__) && defined(EXT2_OS_LINUX) */ /* * Note we override the kernel include file's idea of what the default * check interval (never) should be. It's a good idea to check at * least *occasionally*, specially since servers will never rarely get * to reboot, since Linux is so robust these days. :-) * * 180 days (six months) seems like a good value. */ #ifdef EXT2_DFL_CHECKINTERVAL #undef EXT2_DFL_CHECKINTERVAL #endif #define EXT2_DFL_CHECKINTERVAL (86400L * 180L) /* * Calculate the number of GDT blocks to reserve for online filesystem growth. * The absolute maximum number of GDT blocks we can reserve is determined by * the number of block pointers that can fit into a single block. */ static unsigned int calc_reserved_gdt_blocks(ext2_filsys fs) { struct ext2_super_block *sb = fs->super; unsigned long bpg = sb->s_blocks_per_group; unsigned int gdpb = EXT2_DESC_PER_BLOCK(sb); unsigned long max_blocks = 0xffffffff; unsigned long rsv_groups; unsigned int rsv_gdb; /* We set it at 1024x the current filesystem size, or * the upper block count limit (2^32), whichever is lower. */ if (sb->s_blocks_count < max_blocks / 1024) max_blocks = sb->s_blocks_count * 1024; rsv_groups = ext2fs_div_ceil(max_blocks - sb->s_first_data_block, bpg); rsv_gdb = ext2fs_div_ceil(rsv_groups, gdpb) - fs->desc_blocks; if (rsv_gdb > EXT2_ADDR_PER_BLOCK(sb)) rsv_gdb = EXT2_ADDR_PER_BLOCK(sb); #ifdef RES_GDT_DEBUG printf("max_blocks %lu, rsv_groups = %lu, rsv_gdb = %u\n", max_blocks, rsv_groups, rsv_gdb); #endif return rsv_gdb; } errcode_t ext2fs_initialize(const char *name, int flags, struct ext2_super_block *param, io_manager manager, ext2_filsys *ret_fs) { ext2_filsys fs; errcode_t retval; struct ext2_super_block *super; int frags_per_block; unsigned int rem; unsigned int overhead = 0; unsigned int ipg; dgrp_t i; blk_t numblocks; int rsv_gdt; int csum_flag; int io_flags; char *buf = 0; char c; if (!param || !param->s_blocks_count) return EXT2_ET_INVALID_ARGUMENT; retval = ext2fs_get_mem(sizeof(struct struct_ext2_filsys), &fs); if (retval) return retval; memset(fs, 0, sizeof(struct struct_ext2_filsys)); fs->magic = EXT2_ET_MAGIC_EXT2FS_FILSYS; fs->flags = flags | EXT2_FLAG_RW; fs->umask = 022; #ifdef WORDS_BIGENDIAN fs->flags |= EXT2_FLAG_SWAP_BYTES; #endif io_flags = IO_FLAG_RW; if (flags & EXT2_FLAG_EXCLUSIVE) io_flags |= IO_FLAG_EXCLUSIVE; retval = manager->open(name, io_flags, &fs->io); if (retval) goto cleanup; fs->image_io = fs->io; fs->io->app_data = fs; retval = ext2fs_get_mem(strlen(name)+1, &fs->device_name); if (retval) goto cleanup; strcpy(fs->device_name, name); retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &super); if (retval) goto cleanup; fs->super = super; memset(super, 0, SUPERBLOCK_SIZE); #define set_field(field, default) (super->field = param->field ? \ param->field : (default)) super->s_magic = EXT2_SUPER_MAGIC; super->s_state = EXT2_VALID_FS; set_field(s_log_block_size, 0); /* default blocksize: 1024 bytes */ set_field(s_log_frag_size, 0); /* default fragsize: 1024 bytes */ set_field(s_first_data_block, super->s_log_block_size ? 0 : 1); set_field(s_max_mnt_count, EXT2_DFL_MAX_MNT_COUNT); set_field(s_errors, EXT2_ERRORS_DEFAULT); set_field(s_feature_compat, 0); set_field(s_feature_incompat, 0); set_field(s_feature_ro_compat, 0); set_field(s_first_meta_bg, 0); set_field(s_raid_stride, 0); /* default stride size: 0 */ set_field(s_raid_stripe_width, 0); /* default stripe width: 0 */ set_field(s_log_groups_per_flex, 0); set_field(s_flags, 0); if (super->s_feature_incompat & ~EXT2_LIB_FEATURE_INCOMPAT_SUPP) { retval = EXT2_ET_UNSUPP_FEATURE; goto cleanup; } if (super->s_feature_ro_compat & ~EXT2_LIB_FEATURE_RO_COMPAT_SUPP) { retval = EXT2_ET_RO_UNSUPP_FEATURE; goto cleanup; } set_field(s_rev_level, EXT2_GOOD_OLD_REV); if (super->s_rev_level >= EXT2_DYNAMIC_REV) { set_field(s_first_ino, EXT2_GOOD_OLD_FIRST_INO); set_field(s_inode_size, EXT2_GOOD_OLD_INODE_SIZE); if (super->s_inode_size >= sizeof(struct ext2_inode_large)) { int extra_isize = sizeof(struct ext2_inode_large) - EXT2_GOOD_OLD_INODE_SIZE; set_field(s_min_extra_isize, extra_isize); set_field(s_want_extra_isize, extra_isize); } } else { super->s_first_ino = EXT2_GOOD_OLD_FIRST_INO; super->s_inode_size = EXT2_GOOD_OLD_INODE_SIZE; } set_field(s_checkinterval, EXT2_DFL_CHECKINTERVAL); super->s_mkfs_time = super->s_lastcheck = fs->now ? fs->now : time(NULL); super->s_creator_os = CREATOR_OS; fs->blocksize = EXT2_BLOCK_SIZE(super); fs->fragsize = EXT2_FRAG_SIZE(super); frags_per_block = fs->blocksize / fs->fragsize; /* default: (fs->blocksize*8) blocks/group, up to 2^16 (GDT limit) */ set_field(s_blocks_per_group, fs->blocksize * 8); if (super->s_blocks_per_group > EXT2_MAX_BLOCKS_PER_GROUP(super)) super->s_blocks_per_group = EXT2_MAX_BLOCKS_PER_GROUP(super); super->s_frags_per_group = super->s_blocks_per_group * frags_per_block; super->s_blocks_count = param->s_blocks_count; super->s_r_blocks_count = param->s_r_blocks_count; if (super->s_r_blocks_count >= param->s_blocks_count) { retval = EXT2_ET_INVALID_ARGUMENT; goto cleanup; } /* * If we're creating an external journal device, we don't need * to bother with the rest. */ if (super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) { fs->group_desc_count = 0; ext2fs_mark_super_dirty(fs); *ret_fs = fs; return 0; } retry: fs->group_desc_count = ext2fs_div_ceil(super->s_blocks_count - super->s_first_data_block, EXT2_BLOCKS_PER_GROUP(super)); if (fs->group_desc_count == 0) { retval = EXT2_ET_TOOSMALL; goto cleanup; } fs->desc_blocks = ext2fs_div_ceil(fs->group_desc_count, EXT2_DESC_PER_BLOCK(super)); i = fs->blocksize >= 4096 ? 1 : 4096 / fs->blocksize; set_field(s_inodes_count, super->s_blocks_count / i); /* * Make sure we have at least EXT2_FIRST_INO + 1 inodes, so * that we have enough inodes for the filesystem(!) */ if (super->s_inodes_count < EXT2_FIRST_INODE(super)+1) super->s_inodes_count = EXT2_FIRST_INODE(super)+1; /* * There should be at least as many inodes as the user * requested. Figure out how many inodes per group that * should be. But make sure that we don't allocate more than * one bitmap's worth of inodes each group. */ ipg = ext2fs_div_ceil(super->s_inodes_count, fs->group_desc_count); if (ipg > fs->blocksize * 8) { if (super->s_blocks_per_group >= 256) { /* Try again with slightly different parameters */ super->s_blocks_per_group -= 8; super->s_blocks_count = param->s_blocks_count; super->s_frags_per_group = super->s_blocks_per_group * frags_per_block; goto retry; } else { retval = EXT2_ET_TOO_MANY_INODES; goto cleanup; } } if (ipg > (unsigned) EXT2_MAX_INODES_PER_GROUP(super)) ipg = EXT2_MAX_INODES_PER_GROUP(super); ipg_retry: super->s_inodes_per_group = ipg; /* * Make sure the number of inodes per group completely fills * the inode table blocks in the descriptor. If not, add some * additional inodes/group. Waste not, want not... */ fs->inode_blocks_per_group = (((super->s_inodes_per_group * EXT2_INODE_SIZE(super)) + EXT2_BLOCK_SIZE(super) - 1) / EXT2_BLOCK_SIZE(super)); super->s_inodes_per_group = ((fs->inode_blocks_per_group * EXT2_BLOCK_SIZE(super)) / EXT2_INODE_SIZE(super)); /* * Finally, make sure the number of inodes per group is a * multiple of 8. This is needed to simplify the bitmap * splicing code. */ super->s_inodes_per_group &= ~7; fs->inode_blocks_per_group = (((super->s_inodes_per_group * EXT2_INODE_SIZE(super)) + EXT2_BLOCK_SIZE(super) - 1) / EXT2_BLOCK_SIZE(super)); /* * adjust inode count to reflect the adjusted inodes_per_group */ if ((__u64)super->s_inodes_per_group * fs->group_desc_count > ~0U) { ipg--; goto ipg_retry; } super->s_inodes_count = super->s_inodes_per_group * fs->group_desc_count; super->s_free_inodes_count = super->s_inodes_count; /* * check the number of reserved group descriptor table blocks */ if (super->s_feature_compat & EXT2_FEATURE_COMPAT_RESIZE_INODE) rsv_gdt = calc_reserved_gdt_blocks(fs); else rsv_gdt = 0; set_field(s_reserved_gdt_blocks, rsv_gdt); if (super->s_reserved_gdt_blocks > EXT2_ADDR_PER_BLOCK(super)) { retval = EXT2_ET_RES_GDT_BLOCKS; goto cleanup; } /* * Calculate the maximum number of bookkeeping blocks per * group. It includes the superblock, the block group * descriptors, the block bitmap, the inode bitmap, the inode * table, and the reserved gdt blocks. */ overhead = (int) (3 + fs->inode_blocks_per_group + fs->desc_blocks + super->s_reserved_gdt_blocks); /* This can only happen if the user requested too many inodes */ if (overhead > super->s_blocks_per_group) { retval = EXT2_ET_TOO_MANY_INODES; goto cleanup; } /* * See if the last group is big enough to support the * necessary data structures. If not, we need to get rid of * it. We need to recalculate the overhead for the last block * group, since it might or might not have a superblock * backup. */ overhead = (int) (2 + fs->inode_blocks_per_group); if (ext2fs_bg_has_super(fs, fs->group_desc_count - 1)) overhead += 1 + fs->desc_blocks + super->s_reserved_gdt_blocks; rem = ((super->s_blocks_count - super->s_first_data_block) % super->s_blocks_per_group); if ((fs->group_desc_count == 1) && rem && (rem < overhead)) { retval = EXT2_ET_TOOSMALL; goto cleanup; } if (rem && (rem < overhead+50)) { super->s_blocks_count -= rem; goto retry; } /* * At this point we know how big the filesystem will be. So * we can do any and all allocations that depend on the block * count. */ retval = ext2fs_get_mem(strlen(fs->device_name) + 80, &buf); if (retval) goto cleanup; strcpy(buf, "block bitmap for "); strcat(buf, fs->device_name); retval = ext2fs_allocate_block_bitmap(fs, buf, &fs->block_map); if (retval) goto cleanup; strcpy(buf, "inode bitmap for "); strcat(buf, fs->device_name); retval = ext2fs_allocate_inode_bitmap(fs, buf, &fs->inode_map); if (retval) goto cleanup; ext2fs_free_mem(&buf); retval = ext2fs_get_array(fs->desc_blocks, fs->blocksize, &fs->group_desc); if (retval) goto cleanup; memset(fs->group_desc, 0, (size_t) fs->desc_blocks * fs->blocksize); /* * Reserve the superblock and group descriptors for each * group, and fill in the correct group statistics for group. * Note that although the block bitmap, inode bitmap, and * inode table have not been allocated (and in fact won't be * by this routine), they are accounted for nevertheless. * * If FLEX_BG meta-data grouping is used, only account for the * superblock and group descriptors (the inode tables and * bitmaps will be accounted for when allocated). */ super->s_free_blocks_count = 0; csum_flag = EXT2_HAS_RO_COMPAT_FEATURE(fs->super, EXT4_FEATURE_RO_COMPAT_GDT_CSUM); for (i = 0; i < fs->group_desc_count; i++) { /* * Don't set the BLOCK_UNINIT group for the last group * because the block bitmap needs to be padded. */ if (csum_flag) { if (i != fs->group_desc_count - 1) fs->group_desc[i].bg_flags |= EXT2_BG_BLOCK_UNINIT; fs->group_desc[i].bg_flags |= EXT2_BG_INODE_UNINIT; numblocks = super->s_inodes_per_group; if (i == 0) numblocks -= super->s_first_ino; fs->group_desc[i].bg_itable_unused = numblocks; } numblocks = ext2fs_reserve_super_and_bgd(fs, i, fs->block_map); if (fs->super->s_log_groups_per_flex) numblocks += 2 + fs->inode_blocks_per_group; super->s_free_blocks_count += numblocks; fs->group_desc[i].bg_free_blocks_count = numblocks; fs->group_desc[i].bg_free_inodes_count = fs->super->s_inodes_per_group; fs->group_desc[i].bg_used_dirs_count = 0; ext2fs_group_desc_csum_set(fs, i); } c = (char) 255; if (((int) c) == -1) { super->s_flags |= EXT2_FLAGS_SIGNED_HASH; } else { super->s_flags |= EXT2_FLAGS_UNSIGNED_HASH; } ext2fs_mark_super_dirty(fs); ext2fs_mark_bb_dirty(fs); ext2fs_mark_ib_dirty(fs); io_channel_set_blksize(fs->io, fs->blocksize); *ret_fs = fs; return 0; cleanup: free(buf); ext2fs_free(fs); return retval; } e2fsprogs-1.41.14/lib/ext2fs/bmap.c0000644031104000366760000001750411405316370014705 0ustar tytso/* * bmap.c --- logical to physical block mapping * * Copyright (C) 1997 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #include #include "ext2_fs.h" #include "ext2fs.h" #if defined(__GNUC__) && !defined(NO_INLINE_FUNCS) #define _BMAP_INLINE_ __inline__ #else #define _BMAP_INLINE_ #endif extern errcode_t ext2fs_bmap(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode, char *block_buf, int bmap_flags, blk_t block, blk_t *phys_blk); #define inode_bmap(inode, nr) ((inode)->i_block[(nr)]) static _BMAP_INLINE_ errcode_t block_ind_bmap(ext2_filsys fs, int flags, blk_t ind, char *block_buf, int *blocks_alloc, blk_t nr, blk_t *ret_blk) { errcode_t retval; blk_t b; if (!ind) { if (flags & BMAP_SET) return EXT2_ET_SET_BMAP_NO_IND; *ret_blk = 0; return 0; } retval = io_channel_read_blk(fs->io, ind, 1, block_buf); if (retval) return retval; if (flags & BMAP_SET) { b = *ret_blk; #ifdef WORDS_BIGENDIAN b = ext2fs_swab32(b); #endif ((blk_t *) block_buf)[nr] = b; return io_channel_write_blk(fs->io, ind, 1, block_buf); } b = ((blk_t *) block_buf)[nr]; #ifdef WORDS_BIGENDIAN b = ext2fs_swab32(b); #endif if (!b && (flags & BMAP_ALLOC)) { b = nr ? ((blk_t *) block_buf)[nr-1] : 0; retval = ext2fs_alloc_block(fs, b, block_buf + fs->blocksize, &b); if (retval) return retval; #ifdef WORDS_BIGENDIAN ((blk_t *) block_buf)[nr] = ext2fs_swab32(b); #else ((blk_t *) block_buf)[nr] = b; #endif retval = io_channel_write_blk(fs->io, ind, 1, block_buf); if (retval) return retval; (*blocks_alloc)++; } *ret_blk = b; return 0; } static _BMAP_INLINE_ errcode_t block_dind_bmap(ext2_filsys fs, int flags, blk_t dind, char *block_buf, int *blocks_alloc, blk_t nr, blk_t *ret_blk) { blk_t b; errcode_t retval; blk_t addr_per_block; addr_per_block = (blk_t) fs->blocksize >> 2; retval = block_ind_bmap(fs, flags & ~BMAP_SET, dind, block_buf, blocks_alloc, nr / addr_per_block, &b); if (retval) return retval; retval = block_ind_bmap(fs, flags, b, block_buf, blocks_alloc, nr % addr_per_block, ret_blk); return retval; } static _BMAP_INLINE_ errcode_t block_tind_bmap(ext2_filsys fs, int flags, blk_t tind, char *block_buf, int *blocks_alloc, blk_t nr, blk_t *ret_blk) { blk_t b; errcode_t retval; blk_t addr_per_block; addr_per_block = (blk_t) fs->blocksize >> 2; retval = block_dind_bmap(fs, flags & ~BMAP_SET, tind, block_buf, blocks_alloc, nr / addr_per_block, &b); if (retval) return retval; retval = block_ind_bmap(fs, flags, b, block_buf, blocks_alloc, nr % addr_per_block, ret_blk); return retval; } errcode_t ext2fs_bmap2(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode, char *block_buf, int bmap_flags, blk64_t block, int *ret_flags, blk64_t *phys_blk) { struct ext2_inode inode_buf; ext2_extent_handle_t handle = 0; blk_t addr_per_block; blk_t b, blk32; char *buf = 0; errcode_t retval = 0; int blocks_alloc = 0, inode_dirty = 0; if (!(bmap_flags & BMAP_SET)) *phys_blk = 0; if (ret_flags) *ret_flags = 0; /* Read inode structure if necessary */ if (!inode) { retval = ext2fs_read_inode(fs, ino, &inode_buf); if (retval) return retval; inode = &inode_buf; } addr_per_block = (blk_t) fs->blocksize >> 2; if (inode->i_flags & EXT4_EXTENTS_FL) { struct ext2fs_extent extent; unsigned int offset; retval = ext2fs_extent_open2(fs, ino, inode, &handle); if (retval) goto done; if (bmap_flags & BMAP_SET) { retval = ext2fs_extent_set_bmap(handle, block, *phys_blk, 0); goto done; } retval = ext2fs_extent_goto(handle, block); if (retval) { /* If the extent is not found, return phys_blk = 0 */ if (retval == EXT2_ET_EXTENT_NOT_FOUND) goto got_block; goto done; } retval = ext2fs_extent_get(handle, EXT2_EXTENT_CURRENT, &extent); if (retval) goto done; offset = block - extent.e_lblk; if (block >= extent.e_lblk && (offset <= extent.e_len)) { *phys_blk = extent.e_pblk + offset; if (ret_flags && extent.e_flags & EXT2_EXTENT_FLAGS_UNINIT) *ret_flags |= BMAP_RET_UNINIT; } got_block: if ((*phys_blk == 0) && (bmap_flags & BMAP_ALLOC)) { retval = ext2fs_alloc_block(fs, b, block_buf, &b); if (retval) goto done; retval = ext2fs_extent_set_bmap(handle, block, (blk64_t) b, 0); if (retval) goto done; /* Update inode after setting extent */ retval = ext2fs_read_inode(fs, ino, inode); if (retval) return retval; blocks_alloc++; *phys_blk = b; } retval = 0; goto done; } if (!block_buf) { retval = ext2fs_get_array(2, fs->blocksize, &buf); if (retval) return retval; block_buf = buf; } if (block < EXT2_NDIR_BLOCKS) { if (bmap_flags & BMAP_SET) { b = *phys_blk; inode_bmap(inode, block) = b; inode_dirty++; goto done; } *phys_blk = inode_bmap(inode, block); b = block ? inode_bmap(inode, block-1) : 0; if ((*phys_blk == 0) && (bmap_flags & BMAP_ALLOC)) { retval = ext2fs_alloc_block(fs, b, block_buf, &b); if (retval) goto done; inode_bmap(inode, block) = b; blocks_alloc++; *phys_blk = b; } goto done; } /* Indirect block */ block -= EXT2_NDIR_BLOCKS; blk32 = *phys_blk; if (block < addr_per_block) { b = inode_bmap(inode, EXT2_IND_BLOCK); if (!b) { if (!(bmap_flags & BMAP_ALLOC)) { if (bmap_flags & BMAP_SET) retval = EXT2_ET_SET_BMAP_NO_IND; goto done; } b = inode_bmap(inode, EXT2_IND_BLOCK-1); retval = ext2fs_alloc_block(fs, b, block_buf, &b); if (retval) goto done; inode_bmap(inode, EXT2_IND_BLOCK) = b; blocks_alloc++; } retval = block_ind_bmap(fs, bmap_flags, b, block_buf, &blocks_alloc, block, &blk32); if (retval == 0) *phys_blk = blk32; goto done; } /* Doubly indirect block */ block -= addr_per_block; if (block < addr_per_block * addr_per_block) { b = inode_bmap(inode, EXT2_DIND_BLOCK); if (!b) { if (!(bmap_flags & BMAP_ALLOC)) { if (bmap_flags & BMAP_SET) retval = EXT2_ET_SET_BMAP_NO_IND; goto done; } b = inode_bmap(inode, EXT2_IND_BLOCK); retval = ext2fs_alloc_block(fs, b, block_buf, &b); if (retval) goto done; inode_bmap(inode, EXT2_DIND_BLOCK) = b; blocks_alloc++; } retval = block_dind_bmap(fs, bmap_flags, b, block_buf, &blocks_alloc, block, &blk32); if (retval == 0) *phys_blk = blk32; goto done; } /* Triply indirect block */ block -= addr_per_block * addr_per_block; b = inode_bmap(inode, EXT2_TIND_BLOCK); if (!b) { if (!(bmap_flags & BMAP_ALLOC)) { if (bmap_flags & BMAP_SET) retval = EXT2_ET_SET_BMAP_NO_IND; goto done; } b = inode_bmap(inode, EXT2_DIND_BLOCK); retval = ext2fs_alloc_block(fs, b, block_buf, &b); if (retval) goto done; inode_bmap(inode, EXT2_TIND_BLOCK) = b; blocks_alloc++; } retval = block_tind_bmap(fs, bmap_flags, b, block_buf, &blocks_alloc, block, &blk32); if (retval == 0) *phys_blk = blk32; done: if (buf) ext2fs_free_mem(&buf); if (handle) ext2fs_extent_free(handle); if ((retval == 0) && (blocks_alloc || inode_dirty)) { ext2fs_iblk_add_blocks(fs, inode, blocks_alloc); retval = ext2fs_write_inode(fs, ino, inode); } return retval; } errcode_t ext2fs_bmap(ext2_filsys fs, ext2_ino_t ino, struct ext2_inode *inode, char *block_buf, int bmap_flags, blk_t block, blk_t *phys_blk) { errcode_t ret; blk64_t ret_blk = *phys_blk; ret = ext2fs_bmap2(fs, ino, inode, block_buf, bmap_flags, block, 0, &ret_blk); if (ret) return ret; if (ret_blk >= ((long long) 1 << 32)) return EOVERFLOW; *phys_blk = ret_blk; return 0; } e2fsprogs-1.41.14/lib/ext2fs/dblist.c0000644031104000116100000001406311504417000015703 0ustar tytsoeng/* * dblist.c -- directory block list functions * * Copyright 1997 by Theodore Ts'o * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #include #if HAVE_UNISTD_H #include #endif #include #include #include "ext2_fs.h" #include "ext2fsP.h" static EXT2_QSORT_TYPE dir_block_cmp(const void *a, const void *b); /* * Returns the number of directories in the filesystem as reported by * the group descriptors. Of course, the group descriptors could be * wrong! */ errcode_t ext2fs_get_num_dirs(ext2_filsys fs, ext2_ino_t *ret_num_dirs) { dgrp_t i; ext2_ino_t num_dirs, max_dirs; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); num_dirs = 0; max_dirs = fs->super->s_inodes_per_group; for (i = 0; i < fs->group_desc_count; i++) { if (fs->group_desc[i].bg_used_dirs_count > max_dirs) num_dirs += max_dirs / 8; else num_dirs += fs->group_desc[i].bg_used_dirs_count; } if (num_dirs > fs->super->s_inodes_count) num_dirs = fs->super->s_inodes_count; *ret_num_dirs = num_dirs; return 0; } /* * helper function for making a new directory block list (for * initialize and copy). */ static errcode_t make_dblist(ext2_filsys fs, ext2_ino_t size, ext2_ino_t count, struct ext2_db_entry *list, ext2_dblist *ret_dblist) { ext2_dblist dblist; errcode_t retval; size_t len; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); if ((ret_dblist == 0) && fs->dblist && (fs->dblist->magic == EXT2_ET_MAGIC_DBLIST)) return 0; retval = ext2fs_get_mem(sizeof(struct ext2_struct_dblist), &dblist); if (retval) return retval; memset(dblist, 0, sizeof(struct ext2_struct_dblist)); dblist->magic = EXT2_ET_MAGIC_DBLIST; dblist->fs = fs; if (size) dblist->size = size; else { retval = ext2fs_get_num_dirs(fs, &dblist->size); if (retval) goto cleanup; dblist->size = (dblist->size * 2) + 12; } len = (size_t) sizeof(struct ext2_db_entry) * dblist->size; dblist->count = count; retval = ext2fs_get_array(dblist->size, sizeof(struct ext2_db_entry), &dblist->list); if (retval) goto cleanup; if (list) memcpy(dblist->list, list, len); else memset(dblist->list, 0, len); if (ret_dblist) *ret_dblist = dblist; else fs->dblist = dblist; return 0; cleanup: if (dblist) ext2fs_free_mem(&dblist); return retval; } /* * Initialize a directory block list */ errcode_t ext2fs_init_dblist(ext2_filsys fs, ext2_dblist *ret_dblist) { ext2_dblist dblist; errcode_t retval; retval = make_dblist(fs, 0, 0, 0, &dblist); if (retval) return retval; dblist->sorted = 1; if (ret_dblist) *ret_dblist = dblist; else fs->dblist = dblist; return 0; } /* * Copy a directory block list */ errcode_t ext2fs_copy_dblist(ext2_dblist src, ext2_dblist *dest) { ext2_dblist dblist; errcode_t retval; retval = make_dblist(src->fs, src->size, src->count, src->list, &dblist); if (retval) return retval; dblist->sorted = src->sorted; *dest = dblist; return 0; } /* * Close a directory block list * * (moved to closefs.c) */ /* * Add a directory block to the directory block list */ errcode_t ext2fs_add_dir_block(ext2_dblist dblist, ext2_ino_t ino, blk_t blk, int blockcnt) { struct ext2_db_entry *new_entry; errcode_t retval; unsigned long old_size; EXT2_CHECK_MAGIC(dblist, EXT2_ET_MAGIC_DBLIST); if (dblist->count >= dblist->size) { old_size = dblist->size * sizeof(struct ext2_db_entry); dblist->size += dblist->size > 200 ? dblist->size / 2 : 100; retval = ext2fs_resize_mem(old_size, (size_t) dblist->size * sizeof(struct ext2_db_entry), &dblist->list); if (retval) { dblist->size -= 100; return retval; } } new_entry = dblist->list + ( (int) dblist->count++); new_entry->blk = blk; new_entry->ino = ino; new_entry->blockcnt = blockcnt; dblist->sorted = 0; return 0; } /* * Change the directory block to the directory block list */ errcode_t ext2fs_set_dir_block(ext2_dblist dblist, ext2_ino_t ino, blk_t blk, int blockcnt) { dgrp_t i; EXT2_CHECK_MAGIC(dblist, EXT2_ET_MAGIC_DBLIST); for (i=0; i < dblist->count; i++) { if ((dblist->list[i].ino != ino) || (dblist->list[i].blockcnt != blockcnt)) continue; dblist->list[i].blk = blk; dblist->sorted = 0; return 0; } return EXT2_ET_DB_NOT_FOUND; } void ext2fs_dblist_sort(ext2_dblist dblist, EXT2_QSORT_TYPE (*sortfunc)(const void *, const void *)) { if (!sortfunc) sortfunc = dir_block_cmp; qsort(dblist->list, (size_t) dblist->count, sizeof(struct ext2_db_entry), sortfunc); dblist->sorted = 1; } /* * This function iterates over the directory block list */ errcode_t ext2fs_dblist_iterate(ext2_dblist dblist, int (*func)(ext2_filsys fs, struct ext2_db_entry *db_info, void *priv_data), void *priv_data) { ext2_ino_t i; int ret; EXT2_CHECK_MAGIC(dblist, EXT2_ET_MAGIC_DBLIST); if (!dblist->sorted) ext2fs_dblist_sort(dblist, 0); for (i=0; i < dblist->count; i++) { ret = (*func)(dblist->fs, &dblist->list[(int)i], priv_data); if (ret & DBLIST_ABORT) return 0; } return 0; } static EXT2_QSORT_TYPE dir_block_cmp(const void *a, const void *b) { const struct ext2_db_entry *db_a = (const struct ext2_db_entry *) a; const struct ext2_db_entry *db_b = (const struct ext2_db_entry *) b; if (db_a->blk != db_b->blk) return (int) (db_a->blk - db_b->blk); if (db_a->ino != db_b->ino) return (int) (db_a->ino - db_b->ino); return (int) (db_a->blockcnt - db_b->blockcnt); } int ext2fs_dblist_count(ext2_dblist dblist) { return (int) dblist->count; } errcode_t ext2fs_dblist_get_last(ext2_dblist dblist, struct ext2_db_entry **entry) { EXT2_CHECK_MAGIC(dblist, EXT2_ET_MAGIC_DBLIST); if (dblist->count == 0) return EXT2_ET_DBLIST_EMPTY; if (entry) *entry = dblist->list + ( (int) dblist->count-1); return 0; } errcode_t ext2fs_dblist_drop_last(ext2_dblist dblist) { EXT2_CHECK_MAGIC(dblist, EXT2_ET_MAGIC_DBLIST); if (dblist->count == 0) return EXT2_ET_DBLIST_EMPTY; dblist->count--; return 0; } e2fsprogs-1.41.14/lib/ext2fs/csum.c0000644031104000116100000001545511504417000015377 0ustar tytsoeng/* * csum.c --- checksumming of ext3 structures * * Copyright (C) 2006 Cluster File Systems, Inc. * Copyright (C) 2006, 2007 by Andreas Dilger * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #if HAVE_SYS_TYPES_H #include #endif #include "ext2_fs.h" #include "ext2fs.h" #include "crc16.h" #include #ifndef offsetof #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) #endif #ifdef DEBUG #define STATIC #else #define STATIC static #endif STATIC __u16 ext2fs_group_desc_csum(ext2_filsys fs, dgrp_t group) { __u16 crc = 0; struct ext2_group_desc *desc; desc = &fs->group_desc[group]; if (fs->super->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_GDT_CSUM) { int offset = offsetof(struct ext2_group_desc, bg_checksum); #ifdef WORDS_BIGENDIAN struct ext2_group_desc swabdesc = *desc; /* Have to swab back to little-endian to do the checksum */ ext2fs_swap_group_desc(&swabdesc); desc = &swabdesc; group = ext2fs_swab32(group); #endif crc = ext2fs_crc16(~0, fs->super->s_uuid, sizeof(fs->super->s_uuid)); crc = ext2fs_crc16(crc, &group, sizeof(group)); crc = ext2fs_crc16(crc, desc, offset); offset += sizeof(desc->bg_checksum); /* skip checksum */ assert(offset == sizeof(*desc)); /* for checksum of struct ext4_group_desc do the rest...*/ if (offset < fs->super->s_desc_size) { crc = ext2fs_crc16(crc, (char *)desc + offset, fs->super->s_desc_size - offset); } } return crc; } int ext2fs_group_desc_csum_verify(ext2_filsys fs, dgrp_t group) { if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super, EXT4_FEATURE_RO_COMPAT_GDT_CSUM) && (fs->group_desc[group].bg_checksum != ext2fs_group_desc_csum(fs, group))) return 0; return 1; } void ext2fs_group_desc_csum_set(ext2_filsys fs, dgrp_t group) { if (EXT2_HAS_RO_COMPAT_FEATURE(fs->super, EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) fs->group_desc[group].bg_checksum = ext2fs_group_desc_csum(fs, group); } static __u32 find_last_inode_ingrp(ext2fs_inode_bitmap bitmap, __u32 inodes_per_grp, dgrp_t grp_no) { ext2_ino_t i, start_ino, end_ino; start_ino = grp_no * inodes_per_grp + 1; end_ino = start_ino + inodes_per_grp - 1; for (i = end_ino; i >= start_ino; i--) { if (ext2fs_fast_test_inode_bitmap(bitmap, i)) return i - start_ino + 1; } return inodes_per_grp; } /* update the bitmap flags, set the itable high watermark, and calculate * checksums for the group descriptors */ errcode_t ext2fs_set_gdt_csum(ext2_filsys fs) { struct ext2_super_block *sb = fs->super; struct ext2_group_desc *bg = fs->group_desc; int dirty = 0; dgrp_t i; if (!fs->inode_map) return EXT2_ET_NO_INODE_BITMAP; if (!EXT2_HAS_RO_COMPAT_FEATURE(fs->super, EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) return 0; for (i = 0; i < fs->group_desc_count; i++, bg++) { int old_csum = bg->bg_checksum; int old_unused = bg->bg_itable_unused; int old_flags = bg->bg_flags; if (bg->bg_free_inodes_count == sb->s_inodes_per_group) { bg->bg_flags |= EXT2_BG_INODE_UNINIT; bg->bg_itable_unused = sb->s_inodes_per_group; } else { bg->bg_flags &= ~EXT2_BG_INODE_UNINIT; bg->bg_itable_unused = sb->s_inodes_per_group - find_last_inode_ingrp(fs->inode_map, sb->s_inodes_per_group,i); } ext2fs_group_desc_csum_set(fs, i); if (old_flags != bg->bg_flags) dirty = 1; if (old_unused != bg->bg_itable_unused) dirty = 1; if (old_csum != bg->bg_checksum) dirty = 1; } if (dirty) ext2fs_mark_super_dirty(fs); return 0; } #ifdef DEBUG #include "e2p/e2p.h" void print_csum(const char *msg, ext2_filsys fs, dgrp_t group) { __u16 crc1, crc2, crc3; dgrp_t swabgroup; struct ext2_group_desc *desc = &fs->group_desc[group]; struct ext2_super_block *sb = fs->super; #ifdef WORDS_BIGENDIAN struct ext2_group_desc swabdesc = fs->group_desc[group]; /* Have to swab back to little-endian to do the checksum */ ext2fs_swap_group_desc(&swabdesc); desc = &swabdesc; swabgroup = ext2fs_swab32(group); #else swabgroup = group; #endif crc1 = ext2fs_crc16(~0, sb->s_uuid, sizeof(fs->super->s_uuid)); crc2 = ext2fs_crc16(crc1, &swabgroup, sizeof(swabgroup)); crc3 = ext2fs_crc16(crc2, desc, offsetof(struct ext2_group_desc, bg_checksum)); printf("%s: UUID %s(%04x), grp %u(%04x): %04x=%04x\n", msg, e2p_uuid2str(sb->s_uuid), crc1, group, crc2,crc3, ext2fs_group_desc_csum(fs, group)); } unsigned char sb_uuid[16] = { 0x4f, 0x25, 0xe8, 0xcf, 0xe7, 0x97, 0x48, 0x23, 0xbe, 0xfa, 0xa7, 0x88, 0x4b, 0xae, 0xec, 0xdb }; int main(int argc, char **argv) { struct ext2_super_block param; errcode_t retval; ext2_filsys fs; int i; __u16 csum1, csum2, csum_known = 0xd3a4; memset(¶m, 0, sizeof(param)); param.s_blocks_count = 32768; retval = ext2fs_initialize("test fs", 0, ¶m, test_io_manager, &fs); if (retval) { com_err("setup", retval, "While initializing filesystem"); exit(1); } memcpy(fs->super->s_uuid, sb_uuid, 16); fs->super->s_feature_ro_compat = EXT4_FEATURE_RO_COMPAT_GDT_CSUM; for (i=0; i < fs->group_desc_count; i++) { fs->group_desc[i].bg_block_bitmap = 124; fs->group_desc[i].bg_inode_bitmap = 125; fs->group_desc[i].bg_inode_table = 126; fs->group_desc[i].bg_free_blocks_count = 31119; fs->group_desc[i].bg_free_inodes_count = 15701; fs->group_desc[i].bg_used_dirs_count = 2; fs->group_desc[i].bg_flags = 0; }; csum1 = ext2fs_group_desc_csum(fs, 0); print_csum("csum0000", fs, 0); if (csum1 != csum_known) { printf("checksum for group 0 should be %04x\n", csum_known); exit(1); } csum2 = ext2fs_group_desc_csum(fs, 1); print_csum("csum0001", fs, 1); if (csum1 == csum2) { printf("checksums for different groups shouldn't match\n"); exit(1); } csum2 = ext2fs_group_desc_csum(fs, 2); print_csum("csumffff", fs, 2); if (csum1 == csum2) { printf("checksums for different groups shouldn't match\n"); exit(1); } fs->group_desc[0].bg_checksum = csum1; csum2 = ext2fs_group_desc_csum(fs, 0); print_csum("csum_set", fs, 0); if (csum1 != csum2) { printf("checksums should not depend on checksum field\n"); exit(1); } if (!ext2fs_group_desc_csum_verify(fs, 0)) { printf("checksums should verify against gd_checksum\n"); exit(1); } memset(fs->super->s_uuid, 0x30, sizeof(fs->super->s_uuid)); print_csum("new_uuid", fs, 0); if (ext2fs_group_desc_csum_verify(fs, 0) != 0) { printf("checksums for different filesystems shouldn't match\n"); exit(1); } csum1 = fs->group_desc[0].bg_checksum = ext2fs_group_desc_csum(fs, 0); print_csum("csum_new", fs, 0); fs->group_desc[0].bg_free_blocks_count = 1; csum2 = ext2fs_group_desc_csum(fs, 0); print_csum("csum_blk", fs, 0); if (csum1 == csum2) { printf("checksums for different data shouldn't match\n"); exit(1); } return 0; } #endif e2fsprogs-1.41.14/lib/ext2fs/unlink.c0000644031104000366760000000364311405316370015265 0ustar tytso/* * unlink.c --- delete links in a ext2fs directory * * Copyright (C) 1993, 1994, 1997 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #include "ext2_fs.h" #include "ext2fs.h" struct link_struct { const char *name; int namelen; ext2_ino_t inode; int flags; struct ext2_dir_entry *prev; int done; }; #ifdef __TURBOC__ #pragma argsused #endif static int unlink_proc(struct ext2_dir_entry *dirent, int offset, int blocksize EXT2FS_ATTR((unused)), char *buf EXT2FS_ATTR((unused)), void *priv_data) { struct link_struct *ls = (struct link_struct *) priv_data; struct ext2_dir_entry *prev; prev = ls->prev; ls->prev = dirent; if (ls->name) { if ((dirent->name_len & 0xFF) != ls->namelen) return 0; if (strncmp(ls->name, dirent->name, dirent->name_len & 0xFF)) return 0; } if (ls->inode) { if (dirent->inode != ls->inode) return 0; } else { if (!dirent->inode) return 0; } if (offset) prev->rec_len += dirent->rec_len; else dirent->inode = 0; ls->done++; return DIRENT_ABORT|DIRENT_CHANGED; } #ifdef __TURBOC__ #pragma argsused #endif errcode_t ext2fs_unlink(ext2_filsys fs, ext2_ino_t dir, const char *name, ext2_ino_t ino, int flags EXT2FS_ATTR((unused))) { errcode_t retval; struct link_struct ls; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); if (!name && !ino) return EXT2_ET_INVALID_ARGUMENT; if (!(fs->flags & EXT2_FLAG_RW)) return EXT2_ET_RO_FILSYS; ls.name = name; ls.namelen = name ? strlen(name) : 0; ls.inode = ino; ls.flags = 0; ls.done = 0; ls.prev = 0; retval = ext2fs_dir_iterate(fs, dir, DIRENT_FLAG_INCLUDE_EMPTY, 0, unlink_proc, &ls); if (retval) return retval; return (ls.done) ? 0 : EXT2_ET_DIR_NO_SPACE; } e2fsprogs-1.41.14/lib/ext2fs/brel_ma.c0000644031104000366760000001030111405316370015353 0ustar tytso/* * brel_ma.c * * Copyright (C) 1996, 1997 Theodore Ts'o. * * TODO: rewrite to not use a direct array!!! (Fortunately this * module isn't really used yet.) * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #include #include #include #if HAVE_UNISTD_H #include #endif #if HAVE_ERRNO_H #include #endif #include "ext2_fs.h" #include "ext2fs.h" #include "brel.h" static errcode_t bma_put(ext2_brel brel, blk_t old, struct ext2_block_relocate_entry *ent); static errcode_t bma_get(ext2_brel brel, blk_t old, struct ext2_block_relocate_entry *ent); static errcode_t bma_start_iter(ext2_brel brel); static errcode_t bma_next(ext2_brel brel, blk_t *old, struct ext2_block_relocate_entry *ent); static errcode_t bma_move(ext2_brel brel, blk_t old, blk_t new); static errcode_t bma_delete(ext2_brel brel, blk_t old); static errcode_t bma_free(ext2_brel brel); struct brel_ma { __u32 magic; blk_t max_block; struct ext2_block_relocate_entry *entries; }; errcode_t ext2fs_brel_memarray_create(char *name, blk_t max_block, ext2_brel *new_brel) { ext2_brel brel = 0; errcode_t retval; struct brel_ma *ma = 0; size_t size; *new_brel = 0; /* * Allocate memory structures */ retval = ext2fs_get_mem(sizeof(struct ext2_block_relocation_table), &brel); if (retval) goto errout; memset(brel, 0, sizeof(struct ext2_block_relocation_table)); retval = ext2fs_get_mem(strlen(name)+1, &brel->name); if (retval) goto errout; strcpy(brel->name, name); retval = ext2fs_get_mem(sizeof(struct brel_ma), &ma); if (retval) goto errout; memset(ma, 0, sizeof(struct brel_ma)); brel->priv_data = ma; size = (size_t) (sizeof(struct ext2_block_relocate_entry) * (max_block+1)); retval = ext2fs_get_array(max_block+1, sizeof(struct ext2_block_relocate_entry), &ma->entries); if (retval) goto errout; memset(ma->entries, 0, size); ma->max_block = max_block; /* * Fill in the brel data structure */ brel->put = bma_put; brel->get = bma_get; brel->start_iter = bma_start_iter; brel->next = bma_next; brel->move = bma_move; brel->delete = bma_delete; brel->free = bma_free; *new_brel = brel; return 0; errout: bma_free(brel); return retval; } static errcode_t bma_put(ext2_brel brel, blk_t old, struct ext2_block_relocate_entry *ent) { struct brel_ma *ma; ma = brel->priv_data; if (old > ma->max_block) return EXT2_ET_INVALID_ARGUMENT; ma->entries[(unsigned)old] = *ent; return 0; } static errcode_t bma_get(ext2_brel brel, blk_t old, struct ext2_block_relocate_entry *ent) { struct brel_ma *ma; ma = brel->priv_data; if (old > ma->max_block) return EXT2_ET_INVALID_ARGUMENT; if (ma->entries[(unsigned)old].new == 0) return ENOENT; *ent = ma->entries[old]; return 0; } static errcode_t bma_start_iter(ext2_brel brel) { brel->current = 0; return 0; } static errcode_t bma_next(ext2_brel brel, blk_t *old, struct ext2_block_relocate_entry *ent) { struct brel_ma *ma; ma = brel->priv_data; while (++brel->current < ma->max_block) { if (ma->entries[(unsigned)brel->current].new == 0) continue; *old = brel->current; *ent = ma->entries[(unsigned)brel->current]; return 0; } *old = 0; return 0; } static errcode_t bma_move(ext2_brel brel, blk_t old, blk_t new) { struct brel_ma *ma; ma = brel->priv_data; if ((old > ma->max_block) || (new > ma->max_block)) return EXT2_ET_INVALID_ARGUMENT; if (ma->entries[(unsigned)old].new == 0) return ENOENT; ma->entries[(unsigned)new] = ma->entries[old]; ma->entries[(unsigned)old].new = 0; return 0; } static errcode_t bma_delete(ext2_brel brel, blk_t old) { struct brel_ma *ma; ma = brel->priv_data; if (old > ma->max_block) return EXT2_ET_INVALID_ARGUMENT; if (ma->entries[(unsigned)old].new == 0) return ENOENT; ma->entries[(unsigned)old].new = 0; return 0; } static errcode_t bma_free(ext2_brel brel) { struct brel_ma *ma; if (!brel) return 0; ma = brel->priv_data; if (ma) { if (ma->entries) ext2fs_free_mem(&ma->entries); ext2fs_free_mem(&ma); } if (brel->name) ext2fs_free_mem(&brel->name); ext2fs_free_mem(&brel); return 0; } e2fsprogs-1.41.14/lib/ext2fs/ext3_extents.h0000644031104000366760000000743711405316370016434 0ustar tytso/* * Copyright (c) 2003,2004 Cluster File Systems, Inc, info@clusterfs.com * Written by Alex Tomas * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #ifndef _LINUX_EXT3_EXTENTS #define _LINUX_EXT3_EXTENTS /* * ext3_inode has i_block array (total 60 bytes) * first 4 bytes are used to store: * - tree depth (0 mean there is no tree yet. all extents in the inode) * - number of alive extents in the inode */ /* * this is extent on-disk structure * it's used at the bottom of the tree */ struct ext3_extent { __u32 ee_block; /* first logical block extent covers */ __u16 ee_len; /* number of blocks covered by extent */ __u16 ee_start_hi; /* high 16 bits of physical block */ __u32 ee_start; /* low 32 bigs of physical block */ }; /* * this is index on-disk structure * it's used at all the levels, but the bottom */ struct ext3_extent_idx { __u32 ei_block; /* index covers logical blocks from 'block' */ __u32 ei_leaf; /* pointer to the physical block of the next * * level. leaf or next index could bet here */ __u16 ei_leaf_hi; /* high 16 bits of physical block */ __u16 ei_unused; }; /* * each block (leaves and indexes), even inode-stored has header */ struct ext3_extent_header { __u16 eh_magic; /* probably will support different formats */ __u16 eh_entries; /* number of valid entries */ __u16 eh_max; /* capacity of store in entries */ __u16 eh_depth; /* has tree real underlaying blocks? */ __u32 eh_generation; /* generation of the tree */ }; #define EXT3_EXT_MAGIC 0xf30a /* * array of ext3_ext_path contains path to some extent * creation/lookup routines use it for traversal/splitting/etc * truncate uses it to simulate recursive walking */ struct ext3_ext_path { __u32 p_block; __u16 p_depth; struct ext3_extent *p_ext; struct ext3_extent_idx *p_idx; struct ext3_extent_header *p_hdr; struct buffer_head *p_bh; }; /* * EXT_INIT_MAX_LEN is the maximum number of blocks we can have in an * initialized extent. This is 2^15 and not (2^16 - 1), since we use the * MSB of ee_len field in the extent datastructure to signify if this * particular extent is an initialized extent or an uninitialized (i.e. * preallocated). * EXT_UNINIT_MAX_LEN is the maximum number of blocks we can have in an * uninitialized extent. * If ee_len is <= 0x8000, it is an initialized extent. Otherwise, it is an * uninitialized one. In other words, if MSB of ee_len is set, it is an * uninitialized extent with only one special scenario when ee_len = 0x8000. * In this case we can not have an uninitialized extent of zero length and * thus we make it as a special case of initialized extent with 0x8000 length. * This way we get better extent-to-group alignment for initialized extents. * Hence, the maximum number of blocks we can have in an *initialized* * extent is 2^15 (32768) and in an *uninitialized* extent is 2^15-1 (32767). */ #define EXT_INIT_MAX_LEN (1UL << 15) #define EXT_UNINIT_MAX_LEN (EXT_INIT_MAX_LEN - 1) #define EXT_FIRST_EXTENT(__hdr__) \ ((struct ext3_extent *) (((char *) (__hdr__)) + \ sizeof(struct ext3_extent_header))) #define EXT_FIRST_INDEX(__hdr__) \ ((struct ext3_extent_idx *) (((char *) (__hdr__)) + \ sizeof(struct ext3_extent_header))) #define EXT_HAS_FREE_INDEX(__path__) \ ((__path__)->p_hdr->eh_entries < (__path__)->p_hdr->eh_max) #define EXT_LAST_EXTENT(__hdr__) \ (EXT_FIRST_EXTENT((__hdr__)) + (__hdr__)->eh_entries - 1) #define EXT_LAST_INDEX(__hdr__) \ (EXT_FIRST_INDEX((__hdr__)) + (__hdr__)->eh_entries - 1) #define EXT_MAX_EXTENT(__hdr__) \ (EXT_FIRST_EXTENT((__hdr__)) + (__hdr__)->eh_max - 1) #define EXT_MAX_INDEX(__hdr__) \ (EXT_FIRST_INDEX((__hdr__)) + (__hdr__)->eh_max - 1) #endif /* _LINUX_EXT3_EXTENTS */ e2fsprogs-1.41.14/lib/ext2fs/freefs.c0000644031104000366760000000432211405316370015232 0ustar tytso/* * freefs.c --- free an ext2 filesystem * * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #include #if HAVE_UNISTD_H #include #endif #include "ext2_fs.h" #include "ext2fsP.h" static void ext2fs_free_inode_cache(struct ext2_inode_cache *icache); void ext2fs_free(ext2_filsys fs) { if (!fs || (fs->magic != EXT2_ET_MAGIC_EXT2FS_FILSYS)) return; if (fs->image_io != fs->io) { if (fs->image_io) io_channel_close(fs->image_io); } if (fs->io) { io_channel_close(fs->io); } if (fs->device_name) ext2fs_free_mem(&fs->device_name); if (fs->super) ext2fs_free_mem(&fs->super); if (fs->orig_super) ext2fs_free_mem(&fs->orig_super); if (fs->group_desc) ext2fs_free_mem(&fs->group_desc); if (fs->block_map) ext2fs_free_block_bitmap(fs->block_map); if (fs->inode_map) ext2fs_free_inode_bitmap(fs->inode_map); if (fs->badblocks) ext2fs_badblocks_list_free(fs->badblocks); fs->badblocks = 0; if (fs->dblist) ext2fs_free_dblist(fs->dblist); if (fs->icache) ext2fs_free_inode_cache(fs->icache); fs->magic = 0; ext2fs_free_mem(&fs); } /* * Free the inode cache structure */ static void ext2fs_free_inode_cache(struct ext2_inode_cache *icache) { if (--icache->refcount) return; if (icache->buffer) ext2fs_free_mem(&icache->buffer); if (icache->cache) ext2fs_free_mem(&icache->cache); icache->buffer_blk = 0; ext2fs_free_mem(&icache); } /* * This procedure frees a badblocks list. */ void ext2fs_u32_list_free(ext2_u32_list bb) { if (bb->magic != EXT2_ET_MAGIC_BADBLOCKS_LIST) return; if (bb->list) ext2fs_free_mem(&bb->list); bb->list = 0; ext2fs_free_mem(&bb); } void ext2fs_badblocks_list_free(ext2_badblocks_list bb) { ext2fs_u32_list_free((ext2_u32_list) bb); } /* * Free a directory block list */ void ext2fs_free_dblist(ext2_dblist dblist) { if (!dblist || (dblist->magic != EXT2_ET_MAGIC_DBLIST)) return; if (dblist->list) ext2fs_free_mem(&dblist->list); dblist->list = 0; if (dblist->fs && dblist->fs->dblist == dblist) dblist->fs->dblist = 0; dblist->magic = 0; ext2fs_free_mem(&dblist); } e2fsprogs-1.41.14/lib/ext2fs/bmove.c0000644031104000116100000000713311504417000015532 0ustar tytsoeng/* * bmove.c --- Move blocks around to make way for a particular * filesystem structure. * * Copyright (C) 1997 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #if HAVE_SYS_TYPES_H #include #endif #if HAVE_SYS_TIME_H #include #endif #include "ext2_fs.h" #include "ext2fsP.h" struct process_block_struct { ext2_ino_t ino; struct ext2_inode * inode; ext2fs_block_bitmap reserve; ext2fs_block_bitmap alloc_map; errcode_t error; char *buf; int add_dir; int flags; }; static int process_block(ext2_filsys fs, blk_t *block_nr, e2_blkcnt_t blockcnt, blk_t ref_block, int ref_offset, void *priv_data) { struct process_block_struct *pb; errcode_t retval; int ret; blk_t block, orig; pb = (struct process_block_struct *) priv_data; block = orig = *block_nr; ret = 0; /* * Let's see if this is one which we need to relocate */ if (ext2fs_test_block_bitmap(pb->reserve, block)) { do { if (++block >= fs->super->s_blocks_count) block = fs->super->s_first_data_block; if (block == orig) { pb->error = EXT2_ET_BLOCK_ALLOC_FAIL; return BLOCK_ABORT; } } while (ext2fs_test_block_bitmap(pb->reserve, block) || ext2fs_test_block_bitmap(pb->alloc_map, block)); retval = io_channel_read_blk(fs->io, orig, 1, pb->buf); if (retval) { pb->error = retval; return BLOCK_ABORT; } retval = io_channel_write_blk(fs->io, block, 1, pb->buf); if (retval) { pb->error = retval; return BLOCK_ABORT; } *block_nr = block; ext2fs_mark_block_bitmap(pb->alloc_map, block); ret = BLOCK_CHANGED; if (pb->flags & EXT2_BMOVE_DEBUG) printf("ino=%ld, blockcnt=%lld, %u->%u\n", pb->ino, blockcnt, orig, block); } if (pb->add_dir) { retval = ext2fs_add_dir_block(fs->dblist, pb->ino, block, (int) blockcnt); if (retval) { pb->error = retval; ret |= BLOCK_ABORT; } } return ret; } errcode_t ext2fs_move_blocks(ext2_filsys fs, ext2fs_block_bitmap reserve, ext2fs_block_bitmap alloc_map, int flags) { ext2_ino_t ino; struct ext2_inode inode; errcode_t retval; struct process_block_struct pb; ext2_inode_scan scan; char *block_buf; retval = ext2fs_open_inode_scan(fs, 0, &scan); if (retval) return retval; pb.reserve = reserve; pb.error = 0; pb.alloc_map = alloc_map ? alloc_map : fs->block_map; pb.flags = flags; retval = ext2fs_get_array(4, fs->blocksize, &block_buf); if (retval) return retval; pb.buf = block_buf + fs->blocksize * 3; /* * If GET_DBLIST is set in the flags field, then we should * gather directory block information while we're doing the * block move. */ if (flags & EXT2_BMOVE_GET_DBLIST) { if (fs->dblist) { ext2fs_free_dblist(fs->dblist); fs->dblist = NULL; } retval = ext2fs_init_dblist(fs, 0); if (retval) return retval; } retval = ext2fs_get_next_inode(scan, &ino, &inode); if (retval) return retval; while (ino) { if ((inode.i_links_count == 0) || !ext2fs_inode_has_valid_blocks(&inode)) goto next; pb.ino = ino; pb.inode = &inode; pb.add_dir = (LINUX_S_ISDIR(inode.i_mode) && flags & EXT2_BMOVE_GET_DBLIST); retval = ext2fs_block_iterate2(fs, ino, 0, block_buf, process_block, &pb); if (retval) return retval; if (pb.error) return pb.error; next: retval = ext2fs_get_next_inode(scan, &ino, &inode); if (retval == EXT2_ET_BAD_BLOCK_IN_INODE_TABLE) goto next; } return 0; } e2fsprogs-1.41.14/lib/ext2fs/tdb.c0000644031104000366760000033331111374366146014547 0ustar tytso/* URL: svn://svnanon.samba.org/samba/branches/SAMBA_4_0/source/lib/tdb/common Rev: 23590 Last Changed Date: 2007-06-22 13:36:10 -0400 (Fri, 22 Jun 2007) */ /* trivial database library - standalone version Copyright (C) Andrew Tridgell 1999-2005 Copyright (C) Jeremy Allison 2000-2006 Copyright (C) Paul `Rusty' Russell 2000 ** NOTE! The following LGPL license applies to the tdb ** library. This does NOT imply that all of Samba is released ** under the LGPL This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifdef CONFIG_STAND_ALONE #define HAVE_MMAP #define HAVE_STRDUP #define HAVE_SYS_MMAN_H #define HAVE_UTIME_H #define HAVE_UTIME #endif #define _XOPEN_SOURCE 600 #include #include #include #include #include #include #include #ifdef HAVE_SYS_SELECT_H #include #endif #include #include #include #ifdef HAVE_UTIME_H #include #endif #include #include #include #ifdef HAVE_SYS_MMAN_H #include #endif #ifndef MAP_FILE #define MAP_FILE 0 #endif #ifndef MAP_FAILED #define MAP_FAILED ((void *)-1) #endif #ifndef HAVE_STRDUP #define strdup rep_strdup static char *rep_strdup(const char *s) { char *ret; int length; if (!s) return NULL; if (!length) length = strlen(s); ret = malloc(length + 1); if (ret) { strncpy(ret, s, length); ret[length] = '\0'; } return ret; } #endif #ifndef PRINTF_ATTRIBUTE #if (__GNUC__ >= 3) && (__GNUC_MINOR__ >= 1 ) /** Use gcc attribute to check printf fns. a1 is the 1-based index of * the parameter containing the format, and a2 the index of the first * argument. Note that some gcc 2.x versions don't handle this * properly **/ #define PRINTF_ATTRIBUTE(a1, a2) __attribute__ ((format (__printf__, a1, a2))) #else #define PRINTF_ATTRIBUTE(a1, a2) #endif #endif typedef int bool; #include "tdb.h" #ifndef u32 #define u32 unsigned #endif #ifndef HAVE_GETPAGESIZE #define getpagesize() 0x2000 #endif typedef u32 tdb_len_t; typedef u32 tdb_off_t; #ifndef offsetof #define offsetof(t,f) ((unsigned int)&((t *)0)->f) #endif #define TDB_MAGIC_FOOD "TDB file\n" #define TDB_VERSION (0x26011967 + 6) #define TDB_MAGIC (0x26011999U) #define TDB_FREE_MAGIC (~TDB_MAGIC) #define TDB_DEAD_MAGIC (0xFEE1DEAD) #define TDB_RECOVERY_MAGIC (0xf53bc0e7U) #define TDB_ALIGNMENT 4 #define MIN_REC_SIZE (2*sizeof(struct list_struct) + TDB_ALIGNMENT) #define DEFAULT_HASH_SIZE 131 #define FREELIST_TOP (sizeof(struct tdb_header)) #define TDB_ALIGN(x,a) (((x) + (a)-1) & ~((a)-1)) #define TDB_BYTEREV(x) (((((x)&0xff)<<24)|((x)&0xFF00)<<8)|(((x)>>8)&0xFF00)|((x)>>24)) #define TDB_DEAD(r) ((r)->magic == TDB_DEAD_MAGIC) #define TDB_BAD_MAGIC(r) ((r)->magic != TDB_MAGIC && !TDB_DEAD(r)) #define TDB_HASH_TOP(hash) (FREELIST_TOP + (BUCKET(hash)+1)*sizeof(tdb_off_t)) #define TDB_HASHTABLE_SIZE(tdb) ((tdb->header.hash_size+1)*sizeof(tdb_off_t)) #define TDB_DATA_START(hash_size) TDB_HASH_TOP(hash_size-1) #define TDB_RECOVERY_HEAD offsetof(struct tdb_header, recovery_start) #define TDB_SEQNUM_OFS offsetof(struct tdb_header, sequence_number) #define TDB_PAD_BYTE 0x42 #define TDB_PAD_U32 0x42424242 /* NB assumes there is a local variable called "tdb" that is the * current context, also takes doubly-parenthesized print-style * argument. */ #define TDB_LOG(x) tdb->log.log_fn x /* lock offsets */ #define GLOBAL_LOCK 0 #define ACTIVE_LOCK 4 #define TRANSACTION_LOCK 8 /* free memory if the pointer is valid and zero the pointer */ #ifndef SAFE_FREE #define SAFE_FREE(x) do { if ((x) != NULL) {free(x); (x)=NULL;} } while(0) #endif #define BUCKET(hash) ((hash) % tdb->header.hash_size) #define DOCONV() (tdb->flags & TDB_CONVERT) #define CONVERT(x) (DOCONV() ? tdb_convert(&x, sizeof(x)) : &x) /* the body of the database is made of one list_struct for the free space plus a separate data list for each hash value */ struct list_struct { tdb_off_t next; /* offset of the next record in the list */ tdb_len_t rec_len; /* total byte length of record */ tdb_len_t key_len; /* byte length of key */ tdb_len_t data_len; /* byte length of data */ u32 full_hash; /* the full 32 bit hash of the key */ u32 magic; /* try to catch errors */ /* the following union is implied: union { char record[rec_len]; struct { char key[key_len]; char data[data_len]; } u32 totalsize; (tailer) } */ }; /* this is stored at the front of every database */ struct tdb_header { char magic_food[32]; /* for /etc/magic */ u32 version; /* version of the code */ u32 hash_size; /* number of hash entries */ tdb_off_t rwlocks; /* obsolete - kept to detect old formats */ tdb_off_t recovery_start; /* offset of transaction recovery region */ tdb_off_t sequence_number; /* used when TDB_SEQNUM is set */ tdb_off_t reserved[29]; }; struct tdb_lock_type { int list; u32 count; u32 ltype; }; struct tdb_traverse_lock { struct tdb_traverse_lock *next; u32 off; u32 hash; int lock_rw; }; struct tdb_methods { int (*tdb_read)(struct tdb_context *, tdb_off_t , void *, tdb_len_t , int ); int (*tdb_write)(struct tdb_context *, tdb_off_t, const void *, tdb_len_t); void (*next_hash_chain)(struct tdb_context *, u32 *); int (*tdb_oob)(struct tdb_context *, tdb_off_t , int ); int (*tdb_expand_file)(struct tdb_context *, tdb_off_t , tdb_off_t ); int (*tdb_brlock)(struct tdb_context *, tdb_off_t , int, int, int, size_t); }; struct tdb_context { char *name; /* the name of the database */ void *map_ptr; /* where it is currently mapped */ int fd; /* open file descriptor for the database */ tdb_len_t map_size; /* how much space has been mapped */ int read_only; /* opened read-only */ int traverse_read; /* read-only traversal */ struct tdb_lock_type global_lock; int num_lockrecs; struct tdb_lock_type *lockrecs; /* only real locks, all with count>0 */ enum TDB_ERROR ecode; /* error code for last tdb error */ struct tdb_header header; /* a cached copy of the header */ u32 flags; /* the flags passed to tdb_open */ struct tdb_traverse_lock travlocks; /* current traversal locks */ struct tdb_context *next; /* all tdbs to avoid multiple opens */ dev_t device; /* uniquely identifies this tdb */ ino_t inode; /* uniquely identifies this tdb */ struct tdb_logging_context log; unsigned int (*hash_fn)(TDB_DATA *key); int open_flags; /* flags used in the open - needed by reopen */ unsigned int num_locks; /* number of chain locks held */ const struct tdb_methods *methods; struct tdb_transaction *transaction; int page_size; int max_dead_records; bool have_transaction_lock; }; /* internal prototypes */ static int tdb_munmap(struct tdb_context *tdb); static void tdb_mmap(struct tdb_context *tdb); static int tdb_lock(struct tdb_context *tdb, int list, int ltype); static int tdb_unlock(struct tdb_context *tdb, int list, int ltype); static int tdb_brlock(struct tdb_context *tdb, tdb_off_t offset, int rw_type, int lck_type, int probe, size_t len); static int tdb_transaction_lock(struct tdb_context *tdb, int ltype); static int tdb_transaction_unlock(struct tdb_context *tdb); static int tdb_brlock_upgrade(struct tdb_context *tdb, tdb_off_t offset, size_t len); static int tdb_write_lock_record(struct tdb_context *tdb, tdb_off_t off); static int tdb_write_unlock_record(struct tdb_context *tdb, tdb_off_t off); static int tdb_ofs_read(struct tdb_context *tdb, tdb_off_t offset, tdb_off_t *d); static int tdb_ofs_write(struct tdb_context *tdb, tdb_off_t offset, tdb_off_t *d); static void *tdb_convert(void *buf, u32 size); static int tdb_free(struct tdb_context *tdb, tdb_off_t offset, struct list_struct *rec); static tdb_off_t tdb_allocate(struct tdb_context *tdb, tdb_len_t length, struct list_struct *rec); static int tdb_ofs_read(struct tdb_context *tdb, tdb_off_t offset, tdb_off_t *d); static int tdb_ofs_write(struct tdb_context *tdb, tdb_off_t offset, tdb_off_t *d); static int tdb_lock_record(struct tdb_context *tdb, tdb_off_t off); static int tdb_unlock_record(struct tdb_context *tdb, tdb_off_t off); static int tdb_rec_read(struct tdb_context *tdb, tdb_off_t offset, struct list_struct *rec); static int tdb_rec_write(struct tdb_context *tdb, tdb_off_t offset, struct list_struct *rec); static int tdb_do_delete(struct tdb_context *tdb, tdb_off_t rec_ptr, struct list_struct *rec); static unsigned char *tdb_alloc_read(struct tdb_context *tdb, tdb_off_t offset, tdb_len_t len); static int tdb_parse_data(struct tdb_context *tdb, TDB_DATA key, tdb_off_t offset, tdb_len_t len, int (*parser)(TDB_DATA key, TDB_DATA data, void *private_data), void *private_data); static tdb_off_t tdb_find_lock_hash(struct tdb_context *tdb, TDB_DATA key, u32 hash, int locktype, struct list_struct *rec); static void tdb_io_init(struct tdb_context *tdb); static int tdb_expand(struct tdb_context *tdb, tdb_off_t size); static int tdb_rec_free_read(struct tdb_context *tdb, tdb_off_t off, struct list_struct *rec); /* file: error.c */ enum TDB_ERROR tdb_error(struct tdb_context *tdb) { return tdb->ecode; } static struct tdb_errname { enum TDB_ERROR ecode; const char *estring; } emap[] = { {TDB_SUCCESS, "Success"}, {TDB_ERR_CORRUPT, "Corrupt database"}, {TDB_ERR_IO, "IO Error"}, {TDB_ERR_LOCK, "Locking error"}, {TDB_ERR_OOM, "Out of memory"}, {TDB_ERR_EXISTS, "Record exists"}, {TDB_ERR_NOLOCK, "Lock exists on other keys"}, {TDB_ERR_EINVAL, "Invalid parameter"}, {TDB_ERR_NOEXIST, "Record does not exist"}, {TDB_ERR_RDONLY, "write not permitted"} }; /* Error string for the last tdb error */ const char *tdb_errorstr(struct tdb_context *tdb) { u32 i; for (i = 0; i < sizeof(emap) / sizeof(struct tdb_errname); i++) if (tdb->ecode == emap[i].ecode) return emap[i].estring; return "Invalid error code"; } /* file: lock.c */ #define TDB_MARK_LOCK 0x80000000 /* a byte range locking function - return 0 on success this functions locks/unlocks 1 byte at the specified offset. On error, errno is also set so that errors are passed back properly through tdb_open(). note that a len of zero means lock to end of file */ int tdb_brlock(struct tdb_context *tdb, tdb_off_t offset, int rw_type, int lck_type, int probe, size_t len) { struct flock fl; int ret; if (tdb->flags & TDB_NOLOCK) { return 0; } if ((rw_type == F_WRLCK) && (tdb->read_only || tdb->traverse_read)) { tdb->ecode = TDB_ERR_RDONLY; return -1; } fl.l_type = rw_type; fl.l_whence = SEEK_SET; fl.l_start = offset; fl.l_len = len; fl.l_pid = 0; do { ret = fcntl(tdb->fd,lck_type,&fl); } while (ret == -1 && errno == EINTR); if (ret == -1) { /* Generic lock error. errno set by fcntl. * EAGAIN is an expected return from non-blocking * locks. */ if (!probe && lck_type != F_SETLK) { /* Ensure error code is set for log fun to examine. */ tdb->ecode = TDB_ERR_LOCK; TDB_LOG((tdb, TDB_DEBUG_TRACE,"tdb_brlock failed (fd=%d) at offset %d rw_type=%d lck_type=%d len=%d\n", tdb->fd, offset, rw_type, lck_type, (int)len)); } return TDB_ERRCODE(TDB_ERR_LOCK, -1); } return 0; } /* upgrade a read lock to a write lock. This needs to be handled in a special way as some OSes (such as solaris) have too conservative deadlock detection and claim a deadlock when progress can be made. For those OSes we may loop for a while. */ int tdb_brlock_upgrade(struct tdb_context *tdb, tdb_off_t offset, size_t len) { int count = 1000; while (count--) { struct timeval tv; if (tdb_brlock(tdb, offset, F_WRLCK, F_SETLKW, 1, len) == 0) { return 0; } if (errno != EDEADLK) { break; } /* sleep for as short a time as we can - more portable than usleep() */ tv.tv_sec = 0; tv.tv_usec = 1; select(0, NULL, NULL, NULL, &tv); } TDB_LOG((tdb, TDB_DEBUG_TRACE,"tdb_brlock_upgrade failed at offset %d\n", offset)); return -1; } /* lock a list in the database. list -1 is the alloc list */ static int _tdb_lock(struct tdb_context *tdb, int list, int ltype, int op) { struct tdb_lock_type *new_lck; int i; bool mark_lock = ((ltype & TDB_MARK_LOCK) == TDB_MARK_LOCK); ltype &= ~TDB_MARK_LOCK; /* a global lock allows us to avoid per chain locks */ if (tdb->global_lock.count && (ltype == tdb->global_lock.ltype || ltype == F_RDLCK)) { return 0; } if (tdb->global_lock.count) { return TDB_ERRCODE(TDB_ERR_LOCK, -1); } if (list < -1 || list >= (int)tdb->header.hash_size) { TDB_LOG((tdb, TDB_DEBUG_ERROR,"tdb_lock: invalid list %d for ltype=%d\n", list, ltype)); return -1; } if (tdb->flags & TDB_NOLOCK) return 0; for (i=0; inum_lockrecs; i++) { if (tdb->lockrecs[i].list == list) { if (tdb->lockrecs[i].count == 0) { /* * Can't happen, see tdb_unlock(). It should * be an assert. */ TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_lock: " "lck->count == 0 for list %d", list)); } /* * Just increment the in-memory struct, posix locks * don't stack. */ tdb->lockrecs[i].count++; return 0; } } new_lck = (struct tdb_lock_type *)realloc( tdb->lockrecs, sizeof(*tdb->lockrecs) * (tdb->num_lockrecs+1)); if (new_lck == NULL) { errno = ENOMEM; return -1; } tdb->lockrecs = new_lck; /* Since fcntl locks don't nest, we do a lock for the first one, and simply bump the count for future ones */ if (!mark_lock && tdb->methods->tdb_brlock(tdb,FREELIST_TOP+4*list, ltype, op, 0, 1)) { return -1; } tdb->num_locks++; tdb->lockrecs[tdb->num_lockrecs].list = list; tdb->lockrecs[tdb->num_lockrecs].count = 1; tdb->lockrecs[tdb->num_lockrecs].ltype = ltype; tdb->num_lockrecs += 1; return 0; } /* lock a list in the database. list -1 is the alloc list */ int tdb_lock(struct tdb_context *tdb, int list, int ltype) { int ret; ret = _tdb_lock(tdb, list, ltype, F_SETLKW); if (ret) { TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_lock failed on list %d " "ltype=%d (%s)\n", list, ltype, strerror(errno))); } return ret; } /* lock a list in the database. list -1 is the alloc list. non-blocking lock */ int tdb_lock_nonblock(struct tdb_context *tdb, int list, int ltype) { return _tdb_lock(tdb, list, ltype, F_SETLK); } /* unlock the database: returns void because it's too late for errors. */ /* changed to return int it may be interesting to know there has been an error --simo */ int tdb_unlock(struct tdb_context *tdb, int list, int ltype) { int ret = -1; int i; struct tdb_lock_type *lck = NULL; bool mark_lock = ((ltype & TDB_MARK_LOCK) == TDB_MARK_LOCK); ltype &= ~TDB_MARK_LOCK; /* a global lock allows us to avoid per chain locks */ if (tdb->global_lock.count && (ltype == tdb->global_lock.ltype || ltype == F_RDLCK)) { return 0; } if (tdb->global_lock.count) { return TDB_ERRCODE(TDB_ERR_LOCK, -1); } if (tdb->flags & TDB_NOLOCK) return 0; /* Sanity checks */ if (list < -1 || list >= (int)tdb->header.hash_size) { TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_unlock: list %d invalid (%d)\n", list, tdb->header.hash_size)); return ret; } for (i=0; inum_lockrecs; i++) { if (tdb->lockrecs[i].list == list) { lck = &tdb->lockrecs[i]; break; } } if ((lck == NULL) || (lck->count == 0)) { TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_unlock: count is 0\n")); return -1; } if (lck->count > 1) { lck->count--; return 0; } /* * This lock has count==1 left, so we need to unlock it in the * kernel. We don't bother with decrementing the in-memory array * element, we're about to overwrite it with the last array element * anyway. */ if (mark_lock) { ret = 0; } else { ret = tdb->methods->tdb_brlock(tdb, FREELIST_TOP+4*list, F_UNLCK, F_SETLKW, 0, 1); } tdb->num_locks--; /* * Shrink the array by overwriting the element just unlocked with the * last array element. */ if (tdb->num_lockrecs > 1) { *lck = tdb->lockrecs[tdb->num_lockrecs-1]; } tdb->num_lockrecs -= 1; /* * We don't bother with realloc when the array shrinks, but if we have * a completely idle tdb we should get rid of the locked array. */ if (tdb->num_lockrecs == 0) { SAFE_FREE(tdb->lockrecs); } if (ret) TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_unlock: An error occurred unlocking!\n")); return ret; } /* get the transaction lock */ int tdb_transaction_lock(struct tdb_context *tdb, int ltype) { if (tdb->have_transaction_lock || tdb->global_lock.count) { return 0; } if (tdb->methods->tdb_brlock(tdb, TRANSACTION_LOCK, ltype, F_SETLKW, 0, 1) == -1) { TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_lock: failed to get transaction lock\n")); tdb->ecode = TDB_ERR_LOCK; return -1; } tdb->have_transaction_lock = 1; return 0; } /* release the transaction lock */ int tdb_transaction_unlock(struct tdb_context *tdb) { int ret; if (!tdb->have_transaction_lock) { return 0; } ret = tdb->methods->tdb_brlock(tdb, TRANSACTION_LOCK, F_UNLCK, F_SETLKW, 0, 1); if (ret == 0) { tdb->have_transaction_lock = 0; } return ret; } /* lock/unlock entire database */ static int _tdb_lockall(struct tdb_context *tdb, int ltype, int op) { bool mark_lock = ((ltype & TDB_MARK_LOCK) == TDB_MARK_LOCK); ltype &= ~TDB_MARK_LOCK; /* There are no locks on read-only dbs */ if (tdb->read_only || tdb->traverse_read) return TDB_ERRCODE(TDB_ERR_LOCK, -1); if (tdb->global_lock.count && tdb->global_lock.ltype == ltype) { tdb->global_lock.count++; return 0; } if (tdb->global_lock.count) { /* a global lock of a different type exists */ return TDB_ERRCODE(TDB_ERR_LOCK, -1); } if (tdb->num_locks != 0) { /* can't combine global and chain locks */ return TDB_ERRCODE(TDB_ERR_LOCK, -1); } if (!mark_lock && tdb->methods->tdb_brlock(tdb, FREELIST_TOP, ltype, op, 0, 4*tdb->header.hash_size)) { if (op == F_SETLKW) { TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_lockall failed (%s)\n", strerror(errno))); } return -1; } tdb->global_lock.count = 1; tdb->global_lock.ltype = ltype; return 0; } /* unlock entire db */ static int _tdb_unlockall(struct tdb_context *tdb, int ltype) { bool mark_lock = ((ltype & TDB_MARK_LOCK) == TDB_MARK_LOCK); ltype &= ~TDB_MARK_LOCK; /* There are no locks on read-only dbs */ if (tdb->read_only || tdb->traverse_read) { return TDB_ERRCODE(TDB_ERR_LOCK, -1); } if (tdb->global_lock.ltype != ltype || tdb->global_lock.count == 0) { return TDB_ERRCODE(TDB_ERR_LOCK, -1); } if (tdb->global_lock.count > 1) { tdb->global_lock.count--; return 0; } if (!mark_lock && tdb->methods->tdb_brlock(tdb, FREELIST_TOP, F_UNLCK, F_SETLKW, 0, 4*tdb->header.hash_size)) { TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_unlockall failed (%s)\n", strerror(errno))); return -1; } tdb->global_lock.count = 0; tdb->global_lock.ltype = 0; return 0; } /* lock entire database with write lock */ int tdb_lockall(struct tdb_context *tdb) { return _tdb_lockall(tdb, F_WRLCK, F_SETLKW); } /* lock entire database with write lock - mark only */ int tdb_lockall_mark(struct tdb_context *tdb) { return _tdb_lockall(tdb, F_WRLCK | TDB_MARK_LOCK, F_SETLKW); } /* unlock entire database with write lock - unmark only */ int tdb_lockall_unmark(struct tdb_context *tdb) { return _tdb_unlockall(tdb, F_WRLCK | TDB_MARK_LOCK); } /* lock entire database with write lock - nonblocking varient */ int tdb_lockall_nonblock(struct tdb_context *tdb) { return _tdb_lockall(tdb, F_WRLCK, F_SETLK); } /* unlock entire database with write lock */ int tdb_unlockall(struct tdb_context *tdb) { return _tdb_unlockall(tdb, F_WRLCK); } /* lock entire database with read lock */ int tdb_lockall_read(struct tdb_context *tdb) { return _tdb_lockall(tdb, F_RDLCK, F_SETLKW); } /* lock entire database with read lock - nonblock varient */ int tdb_lockall_read_nonblock(struct tdb_context *tdb) { return _tdb_lockall(tdb, F_RDLCK, F_SETLK); } /* unlock entire database with read lock */ int tdb_unlockall_read(struct tdb_context *tdb) { return _tdb_unlockall(tdb, F_RDLCK); } /* lock/unlock one hash chain. This is meant to be used to reduce contention - it cannot guarantee how many records will be locked */ int tdb_chainlock(struct tdb_context *tdb, TDB_DATA key) { return tdb_lock(tdb, BUCKET(tdb->hash_fn(&key)), F_WRLCK); } /* lock/unlock one hash chain, non-blocking. This is meant to be used to reduce contention - it cannot guarantee how many records will be locked */ int tdb_chainlock_nonblock(struct tdb_context *tdb, TDB_DATA key) { return tdb_lock_nonblock(tdb, BUCKET(tdb->hash_fn(&key)), F_WRLCK); } /* mark a chain as locked without actually locking it. Warning! use with great caution! */ int tdb_chainlock_mark(struct tdb_context *tdb, TDB_DATA key) { return tdb_lock(tdb, BUCKET(tdb->hash_fn(&key)), F_WRLCK | TDB_MARK_LOCK); } /* unmark a chain as locked without actually locking it. Warning! use with great caution! */ int tdb_chainlock_unmark(struct tdb_context *tdb, TDB_DATA key) { return tdb_unlock(tdb, BUCKET(tdb->hash_fn(&key)), F_WRLCK | TDB_MARK_LOCK); } int tdb_chainunlock(struct tdb_context *tdb, TDB_DATA key) { return tdb_unlock(tdb, BUCKET(tdb->hash_fn(&key)), F_WRLCK); } int tdb_chainlock_read(struct tdb_context *tdb, TDB_DATA key) { return tdb_lock(tdb, BUCKET(tdb->hash_fn(&key)), F_RDLCK); } int tdb_chainunlock_read(struct tdb_context *tdb, TDB_DATA key) { return tdb_unlock(tdb, BUCKET(tdb->hash_fn(&key)), F_RDLCK); } /* record lock stops delete underneath */ int tdb_lock_record(struct tdb_context *tdb, tdb_off_t off) { return off ? tdb->methods->tdb_brlock(tdb, off, F_RDLCK, F_SETLKW, 0, 1) : 0; } /* Write locks override our own fcntl readlocks, so check it here. Note this is meant to be F_SETLK, *not* F_SETLKW, as it's not an error to fail to get the lock here. */ int tdb_write_lock_record(struct tdb_context *tdb, tdb_off_t off) { struct tdb_traverse_lock *i; for (i = &tdb->travlocks; i; i = i->next) if (i->off == off) return -1; return tdb->methods->tdb_brlock(tdb, off, F_WRLCK, F_SETLK, 1, 1); } /* Note this is meant to be F_SETLK, *not* F_SETLKW, as it's not an error to fail to get the lock here. */ int tdb_write_unlock_record(struct tdb_context *tdb, tdb_off_t off) { return tdb->methods->tdb_brlock(tdb, off, F_UNLCK, F_SETLK, 0, 1); } /* fcntl locks don't stack: avoid unlocking someone else's */ int tdb_unlock_record(struct tdb_context *tdb, tdb_off_t off) { struct tdb_traverse_lock *i; u32 count = 0; if (off == 0) return 0; for (i = &tdb->travlocks; i; i = i->next) if (i->off == off) count++; return (count == 1 ? tdb->methods->tdb_brlock(tdb, off, F_UNLCK, F_SETLKW, 0, 1) : 0); } /* file: io.c */ /* check for an out of bounds access - if it is out of bounds then see if the database has been expanded by someone else and expand if necessary note that "len" is the minimum length needed for the db */ static int tdb_oob(struct tdb_context *tdb, tdb_off_t len, int probe) { struct stat st; if (len <= tdb->map_size) return 0; if (tdb->flags & TDB_INTERNAL) { if (!probe) { /* Ensure ecode is set for log fn. */ tdb->ecode = TDB_ERR_IO; TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_oob len %d beyond internal malloc size %d\n", (int)len, (int)tdb->map_size)); } return TDB_ERRCODE(TDB_ERR_IO, -1); } if (fstat(tdb->fd, &st) == -1) { return TDB_ERRCODE(TDB_ERR_IO, -1); } if (st.st_size < (size_t)len) { if (!probe) { /* Ensure ecode is set for log fn. */ tdb->ecode = TDB_ERR_IO; TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_oob len %d beyond eof at %d\n", (int)len, (int)st.st_size)); } return TDB_ERRCODE(TDB_ERR_IO, -1); } /* Unmap, update size, remap */ if (tdb_munmap(tdb) == -1) return TDB_ERRCODE(TDB_ERR_IO, -1); tdb->map_size = st.st_size; tdb_mmap(tdb); return 0; } /* write a lump of data at a specified offset */ static int tdb_write(struct tdb_context *tdb, tdb_off_t off, const void *buf, tdb_len_t len) { if (len == 0) { return 0; } if (tdb->read_only || tdb->traverse_read) { tdb->ecode = TDB_ERR_RDONLY; return -1; } if (tdb->methods->tdb_oob(tdb, off + len, 0) != 0) return -1; if (tdb->map_ptr) { memcpy(off + (char *)tdb->map_ptr, buf, len); } else if (pwrite(tdb->fd, buf, len, off) != (ssize_t)len) { /* Ensure ecode is set for log fn. */ tdb->ecode = TDB_ERR_IO; TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_write failed at %d len=%d (%s)\n", off, len, strerror(errno))); return TDB_ERRCODE(TDB_ERR_IO, -1); } return 0; } /* Endian conversion: we only ever deal with 4 byte quantities */ void *tdb_convert(void *buf, u32 size) { u32 i, *p = (u32 *)buf; for (i = 0; i < size / 4; i++) p[i] = TDB_BYTEREV(p[i]); return buf; } /* read a lump of data at a specified offset, maybe convert */ static int tdb_read(struct tdb_context *tdb, tdb_off_t off, void *buf, tdb_len_t len, int cv) { if (tdb->methods->tdb_oob(tdb, off + len, 0) != 0) { return -1; } if (tdb->map_ptr) { memcpy(buf, off + (char *)tdb->map_ptr, len); } else { ssize_t ret = pread(tdb->fd, buf, len, off); if (ret != (ssize_t)len) { /* Ensure ecode is set for log fn. */ tdb->ecode = TDB_ERR_IO; TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_read failed at %d " "len=%d ret=%d (%s) map_size=%d\n", (int)off, (int)len, (int)ret, strerror(errno), (int)tdb->map_size)); return TDB_ERRCODE(TDB_ERR_IO, -1); } } if (cv) { tdb_convert(buf, len); } return 0; } /* do an unlocked scan of the hash table heads to find the next non-zero head. The value will then be confirmed with the lock held */ static void tdb_next_hash_chain(struct tdb_context *tdb, u32 *chain) { u32 h = *chain; if (tdb->map_ptr) { for (;h < tdb->header.hash_size;h++) { if (0 != *(u32 *)(TDB_HASH_TOP(h) + (unsigned char *)tdb->map_ptr)) { break; } } } else { u32 off=0; for (;h < tdb->header.hash_size;h++) { if (tdb_ofs_read(tdb, TDB_HASH_TOP(h), &off) != 0 || off != 0) { break; } } } (*chain) = h; } int tdb_munmap(struct tdb_context *tdb) { if (tdb->flags & TDB_INTERNAL) return 0; #ifdef HAVE_MMAP if (tdb->map_ptr) { int ret = munmap(tdb->map_ptr, tdb->map_size); if (ret != 0) return ret; } #endif tdb->map_ptr = NULL; return 0; } void tdb_mmap(struct tdb_context *tdb) { if (tdb->flags & TDB_INTERNAL) return; #ifdef HAVE_MMAP if (!(tdb->flags & TDB_NOMMAP)) { tdb->map_ptr = mmap(NULL, tdb->map_size, PROT_READ|(tdb->read_only? 0:PROT_WRITE), MAP_SHARED|MAP_FILE, tdb->fd, 0); /* * NB. When mmap fails it returns MAP_FAILED *NOT* NULL !!!! */ if (tdb->map_ptr == MAP_FAILED) { tdb->map_ptr = NULL; TDB_LOG((tdb, TDB_DEBUG_WARNING, "tdb_mmap failed for size %d (%s)\n", tdb->map_size, strerror(errno))); } } else { tdb->map_ptr = NULL; } #else tdb->map_ptr = NULL; #endif } /* expand a file. we prefer to use ftruncate, as that is what posix says to use for mmap expansion */ static int tdb_expand_file(struct tdb_context *tdb, tdb_off_t size, tdb_off_t addition) { char buf[1024]; if (tdb->read_only || tdb->traverse_read) { tdb->ecode = TDB_ERR_RDONLY; return -1; } if (ftruncate(tdb->fd, size+addition) == -1) { char b = 0; if (pwrite(tdb->fd, &b, 1, (size+addition) - 1) != 1) { TDB_LOG((tdb, TDB_DEBUG_FATAL, "expand_file to %d failed (%s)\n", size+addition, strerror(errno))); return -1; } } /* now fill the file with something. This ensures that the file isn't sparse, which would be very bad if we ran out of disk. This must be done with write, not via mmap */ memset(buf, TDB_PAD_BYTE, sizeof(buf)); while (addition) { int n = addition>sizeof(buf)?sizeof(buf):addition; int ret = pwrite(tdb->fd, buf, n, size); if (ret != n) { TDB_LOG((tdb, TDB_DEBUG_FATAL, "expand_file write of %d failed (%s)\n", n, strerror(errno))); return -1; } addition -= n; size += n; } return 0; } /* expand the database at least size bytes by expanding the underlying file and doing the mmap again if necessary */ int tdb_expand(struct tdb_context *tdb, tdb_off_t size) { struct list_struct rec; tdb_off_t offset; if (tdb_lock(tdb, -1, F_WRLCK) == -1) { TDB_LOG((tdb, TDB_DEBUG_ERROR, "lock failed in tdb_expand\n")); return -1; } /* must know about any previous expansions by another process */ tdb->methods->tdb_oob(tdb, tdb->map_size + 1, 1); /* always make room for at least 10 more records, and round the database up to a multiple of the page size */ size = TDB_ALIGN(tdb->map_size + size*10, tdb->page_size) - tdb->map_size; if (!(tdb->flags & TDB_INTERNAL)) tdb_munmap(tdb); /* * We must ensure the file is unmapped before doing this * to ensure consistency with systems like OpenBSD where * writes and mmaps are not consistent. */ /* expand the file itself */ if (!(tdb->flags & TDB_INTERNAL)) { if (tdb->methods->tdb_expand_file(tdb, tdb->map_size, size) != 0) goto fail; } tdb->map_size += size; if (tdb->flags & TDB_INTERNAL) { char *new_map_ptr = (char *)realloc(tdb->map_ptr, tdb->map_size); if (!new_map_ptr) { tdb->map_size -= size; goto fail; } tdb->map_ptr = new_map_ptr; } else { /* * We must ensure the file is remapped before adding the space * to ensure consistency with systems like OpenBSD where * writes and mmaps are not consistent. */ /* We're ok if the mmap fails as we'll fallback to read/write */ tdb_mmap(tdb); } /* form a new freelist record */ memset(&rec,'\0',sizeof(rec)); rec.rec_len = size - sizeof(rec); /* link it into the free list */ offset = tdb->map_size - size; if (tdb_free(tdb, offset, &rec) == -1) goto fail; tdb_unlock(tdb, -1, F_WRLCK); return 0; fail: tdb_unlock(tdb, -1, F_WRLCK); return -1; } /* read/write a tdb_off_t */ int tdb_ofs_read(struct tdb_context *tdb, tdb_off_t offset, tdb_off_t *d) { return tdb->methods->tdb_read(tdb, offset, (char*)d, sizeof(*d), DOCONV()); } int tdb_ofs_write(struct tdb_context *tdb, tdb_off_t offset, tdb_off_t *d) { tdb_off_t off = *d; return tdb->methods->tdb_write(tdb, offset, CONVERT(off), sizeof(*d)); } /* read a lump of data, allocating the space for it */ unsigned char *tdb_alloc_read(struct tdb_context *tdb, tdb_off_t offset, tdb_len_t len) { unsigned char *buf; /* some systems don't like zero length malloc */ if (len == 0) { len = 1; } if (!(buf = (unsigned char *)malloc(len))) { /* Ensure ecode is set for log fn. */ tdb->ecode = TDB_ERR_OOM; TDB_LOG((tdb, TDB_DEBUG_ERROR,"tdb_alloc_read malloc failed len=%d (%s)\n", len, strerror(errno))); return TDB_ERRCODE(TDB_ERR_OOM, buf); } if (tdb->methods->tdb_read(tdb, offset, buf, len, 0) == -1) { SAFE_FREE(buf); return NULL; } return buf; } /* Give a piece of tdb data to a parser */ int tdb_parse_data(struct tdb_context *tdb, TDB_DATA key, tdb_off_t offset, tdb_len_t len, int (*parser)(TDB_DATA key, TDB_DATA data, void *private_data), void *private_data) { TDB_DATA data; int result; data.dsize = len; if ((tdb->transaction == NULL) && (tdb->map_ptr != NULL)) { /* * Optimize by avoiding the malloc/memcpy/free, point the * parser directly at the mmap area. */ if (tdb->methods->tdb_oob(tdb, offset+len, 0) != 0) { return -1; } data.dptr = offset + (unsigned char *)tdb->map_ptr; return parser(key, data, private_data); } if (!(data.dptr = tdb_alloc_read(tdb, offset, len))) { return -1; } result = parser(key, data, private_data); free(data.dptr); return result; } /* read/write a record */ int tdb_rec_read(struct tdb_context *tdb, tdb_off_t offset, struct list_struct *rec) { if (tdb->methods->tdb_read(tdb, offset, rec, sizeof(*rec),DOCONV()) == -1) return -1; if (TDB_BAD_MAGIC(rec)) { /* Ensure ecode is set for log fn. */ tdb->ecode = TDB_ERR_CORRUPT; TDB_LOG((tdb, TDB_DEBUG_FATAL,"tdb_rec_read bad magic 0x%x at offset=%d\n", rec->magic, offset)); return TDB_ERRCODE(TDB_ERR_CORRUPT, -1); } return tdb->methods->tdb_oob(tdb, rec->next+sizeof(*rec), 0); } int tdb_rec_write(struct tdb_context *tdb, tdb_off_t offset, struct list_struct *rec) { struct list_struct r = *rec; return tdb->methods->tdb_write(tdb, offset, CONVERT(r), sizeof(r)); } static const struct tdb_methods io_methods = { tdb_read, tdb_write, tdb_next_hash_chain, tdb_oob, tdb_expand_file, tdb_brlock }; /* initialise the default methods table */ void tdb_io_init(struct tdb_context *tdb) { tdb->methods = &io_methods; } /* file: transaction.c */ /* transaction design: - only allow a single transaction at a time per database. This makes using the transaction API simpler, as otherwise the caller would have to cope with temporary failures in transactions that conflict with other current transactions - keep the transaction recovery information in the same file as the database, using a special 'transaction recovery' record pointed at by the header. This removes the need for extra journal files as used by some other databases - dynamically allocated the transaction recover record, re-using it for subsequent transactions. If a larger record is needed then tdb_free() the old record to place it on the normal tdb freelist before allocating the new record - during transactions, keep a linked list of writes all that have been performed by intercepting all tdb_write() calls. The hooked transaction versions of tdb_read() and tdb_write() check this linked list and try to use the elements of the list in preference to the real database. - don't allow any locks to be held when a transaction starts, otherwise we can end up with deadlock (plus lack of lock nesting in posix locks would mean the lock is lost) - if the caller gains a lock during the transaction but doesn't release it then fail the commit - allow for nested calls to tdb_transaction_start(), re-using the existing transaction record. If the inner transaction is cancelled then a subsequent commit will fail - keep a mirrored copy of the tdb hash chain heads to allow for the fast hash heads scan on traverse, updating the mirrored copy in the transaction version of tdb_write - allow callers to mix transaction and non-transaction use of tdb, although once a transaction is started then an exclusive lock is gained until the transaction is committed or cancelled - the commit stategy involves first saving away all modified data into a linearised buffer in the transaction recovery area, then marking the transaction recovery area with a magic value to indicate a valid recovery record. In total 4 fsync/msync calls are needed per commit to prevent race conditions. It might be possible to reduce this to 3 or even 2 with some more work. - check for a valid recovery record on open of the tdb, while the global lock is held. Automatically recover from the transaction recovery area if needed, then continue with the open as usual. This allows for smooth crash recovery with no administrator intervention. - if TDB_NOSYNC is passed to flags in tdb_open then transactions are still available, but no transaction recovery area is used and no fsync/msync calls are made. */ struct tdb_transaction_el { struct tdb_transaction_el *next, *prev; tdb_off_t offset; tdb_len_t length; unsigned char *data; }; /* hold the context of any current transaction */ struct tdb_transaction { /* we keep a mirrored copy of the tdb hash heads here so tdb_next_hash_chain() can operate efficiently */ u32 *hash_heads; /* the original io methods - used to do IOs to the real db */ const struct tdb_methods *io_methods; /* the list of transaction elements. We use a doubly linked list with a last pointer to allow us to keep the list ordered, with first element at the front of the list. It needs to be doubly linked as the read/write traversals need to be backwards, while the commit needs to be forwards */ struct tdb_transaction_el *elements, *elements_last; /* non-zero when an internal transaction error has occurred. All write operations will then fail until the transaction is ended */ int transaction_error; /* when inside a transaction we need to keep track of any nested tdb_transaction_start() calls, as these are allowed, but don't create a new transaction */ int nesting; /* old file size before transaction */ tdb_len_t old_map_size; }; /* read while in a transaction. We need to check first if the data is in our list of transaction elements, then if not do a real read */ static int transaction_read(struct tdb_context *tdb, tdb_off_t off, void *buf, tdb_len_t len, int cv) { struct tdb_transaction_el *el; /* we need to walk the list backwards to get the most recent data */ for (el=tdb->transaction->elements_last;el;el=el->prev) { tdb_len_t partial; if (off+len <= el->offset) { continue; } if (off >= el->offset + el->length) { continue; } /* an overlapping read - needs to be split into up to 2 reads and a memcpy */ if (off < el->offset) { partial = el->offset - off; if (transaction_read(tdb, off, buf, partial, cv) != 0) { goto fail; } len -= partial; off += partial; buf = (void *)(partial + (char *)buf); } if (off + len <= el->offset + el->length) { partial = len; } else { partial = el->offset + el->length - off; } memcpy(buf, el->data + (off - el->offset), partial); if (cv) { tdb_convert(buf, len); } len -= partial; off += partial; buf = (void *)(partial + (char *)buf); if (len != 0 && transaction_read(tdb, off, buf, len, cv) != 0) { goto fail; } return 0; } /* its not in the transaction elements - do a real read */ return tdb->transaction->io_methods->tdb_read(tdb, off, buf, len, cv); fail: TDB_LOG((tdb, TDB_DEBUG_FATAL, "transaction_read: failed at off=%d len=%d\n", off, len)); tdb->ecode = TDB_ERR_IO; tdb->transaction->transaction_error = 1; return -1; } /* write while in a transaction */ static int transaction_write(struct tdb_context *tdb, tdb_off_t off, const void *buf, tdb_len_t len) { struct tdb_transaction_el *el, *best_el=NULL; if (len == 0) { return 0; } /* if the write is to a hash head, then update the transaction hash heads */ if (len == sizeof(tdb_off_t) && off >= FREELIST_TOP && off < FREELIST_TOP+TDB_HASHTABLE_SIZE(tdb)) { u32 chain = (off-FREELIST_TOP) / sizeof(tdb_off_t); memcpy(&tdb->transaction->hash_heads[chain], buf, len); } /* first see if we can replace an existing entry */ for (el=tdb->transaction->elements_last;el;el=el->prev) { tdb_len_t partial; if (best_el == NULL && off == el->offset+el->length) { best_el = el; } if (off+len <= el->offset) { continue; } if (off >= el->offset + el->length) { continue; } /* an overlapping write - needs to be split into up to 2 writes and a memcpy */ if (off < el->offset) { partial = el->offset - off; if (transaction_write(tdb, off, buf, partial) != 0) { goto fail; } len -= partial; off += partial; buf = (const void *)(partial + (const char *)buf); } if (off + len <= el->offset + el->length) { partial = len; } else { partial = el->offset + el->length - off; } memcpy(el->data + (off - el->offset), buf, partial); len -= partial; off += partial; buf = (const void *)(partial + (const char *)buf); if (len != 0 && transaction_write(tdb, off, buf, len) != 0) { goto fail; } return 0; } /* see if we can append the new entry to an existing entry */ if (best_el && best_el->offset + best_el->length == off && (off+len < tdb->transaction->old_map_size || off > tdb->transaction->old_map_size)) { unsigned char *data = best_el->data; el = best_el; el->data = (unsigned char *)realloc(el->data, el->length + len); if (el->data == NULL) { tdb->ecode = TDB_ERR_OOM; tdb->transaction->transaction_error = 1; el->data = data; return -1; } if (buf) { memcpy(el->data + el->length, buf, len); } else { memset(el->data + el->length, TDB_PAD_BYTE, len); } el->length += len; return 0; } /* add a new entry at the end of the list */ el = (struct tdb_transaction_el *)malloc(sizeof(*el)); if (el == NULL) { tdb->ecode = TDB_ERR_OOM; tdb->transaction->transaction_error = 1; return -1; } el->next = NULL; el->prev = tdb->transaction->elements_last; el->offset = off; el->length = len; el->data = (unsigned char *)malloc(len); if (el->data == NULL) { free(el); tdb->ecode = TDB_ERR_OOM; tdb->transaction->transaction_error = 1; return -1; } if (buf) { memcpy(el->data, buf, len); } else { memset(el->data, TDB_PAD_BYTE, len); } if (el->prev) { el->prev->next = el; } else { tdb->transaction->elements = el; } tdb->transaction->elements_last = el; return 0; fail: TDB_LOG((tdb, TDB_DEBUG_FATAL, "transaction_write: failed at off=%d len=%d\n", off, len)); tdb->ecode = TDB_ERR_IO; tdb->transaction->transaction_error = 1; return -1; } /* accelerated hash chain head search, using the cached hash heads */ static void transaction_next_hash_chain(struct tdb_context *tdb, u32 *chain) { u32 h = *chain; for (;h < tdb->header.hash_size;h++) { /* the +1 takes account of the freelist */ if (0 != tdb->transaction->hash_heads[h+1]) { break; } } (*chain) = h; } /* out of bounds check during a transaction */ static int transaction_oob(struct tdb_context *tdb, tdb_off_t len, int probe) { if (len <= tdb->map_size) { return 0; } return TDB_ERRCODE(TDB_ERR_IO, -1); } /* transaction version of tdb_expand(). */ static int transaction_expand_file(struct tdb_context *tdb, tdb_off_t size, tdb_off_t addition) { /* add a write to the transaction elements, so subsequent reads see the zero data */ if (transaction_write(tdb, size, NULL, addition) != 0) { return -1; } return 0; } /* brlock during a transaction - ignore them */ static int transaction_brlock(struct tdb_context *tdb, tdb_off_t offset, int rw_type, int lck_type, int probe, size_t len) { return 0; } static const struct tdb_methods transaction_methods = { transaction_read, transaction_write, transaction_next_hash_chain, transaction_oob, transaction_expand_file, transaction_brlock }; /* start a tdb transaction. No token is returned, as only a single transaction is allowed to be pending per tdb_context */ int tdb_transaction_start(struct tdb_context *tdb) { /* some sanity checks */ if (tdb->read_only || (tdb->flags & TDB_INTERNAL) || tdb->traverse_read) { TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_start: cannot start a transaction on a read-only or internal db\n")); tdb->ecode = TDB_ERR_EINVAL; return -1; } /* cope with nested tdb_transaction_start() calls */ if (tdb->transaction != NULL) { tdb->transaction->nesting++; TDB_LOG((tdb, TDB_DEBUG_TRACE, "tdb_transaction_start: nesting %d\n", tdb->transaction->nesting)); return 0; } if (tdb->num_locks != 0 || tdb->global_lock.count) { /* the caller must not have any locks when starting a transaction as otherwise we'll be screwed by lack of nested locks in posix */ TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_start: cannot start a transaction with locks held\n")); tdb->ecode = TDB_ERR_LOCK; return -1; } if (tdb->travlocks.next != NULL) { /* you cannot use transactions inside a traverse (although you can use traverse inside a transaction) as otherwise you can end up with deadlock */ TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_start: cannot start a transaction within a traverse\n")); tdb->ecode = TDB_ERR_LOCK; return -1; } tdb->transaction = (struct tdb_transaction *) calloc(sizeof(struct tdb_transaction), 1); if (tdb->transaction == NULL) { tdb->ecode = TDB_ERR_OOM; return -1; } /* get the transaction write lock. This is a blocking lock. As discussed with Volker, there are a number of ways we could make this async, which we will probably do in the future */ if (tdb_transaction_lock(tdb, F_WRLCK) == -1) { SAFE_FREE(tdb->transaction); return -1; } /* get a read lock from the freelist to the end of file. This is upgraded to a write lock during the commit */ if (tdb_brlock(tdb, FREELIST_TOP, F_RDLCK, F_SETLKW, 0, 0) == -1) { TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_start: failed to get hash locks\n")); tdb->ecode = TDB_ERR_LOCK; goto fail; } /* setup a copy of the hash table heads so the hash scan in traverse can be fast */ tdb->transaction->hash_heads = (u32 *) calloc(tdb->header.hash_size+1, sizeof(u32)); if (tdb->transaction->hash_heads == NULL) { tdb->ecode = TDB_ERR_OOM; goto fail; } if (tdb->methods->tdb_read(tdb, FREELIST_TOP, tdb->transaction->hash_heads, TDB_HASHTABLE_SIZE(tdb), 0) != 0) { TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_start: failed to read hash heads\n")); tdb->ecode = TDB_ERR_IO; goto fail; } /* make sure we know about any file expansions already done by anyone else */ tdb->methods->tdb_oob(tdb, tdb->map_size + 1, 1); tdb->transaction->old_map_size = tdb->map_size; /* finally hook the io methods, replacing them with transaction specific methods */ tdb->transaction->io_methods = tdb->methods; tdb->methods = &transaction_methods; /* by calling this transaction write here, we ensure that we don't grow the transaction linked list due to hash table updates */ if (transaction_write(tdb, FREELIST_TOP, tdb->transaction->hash_heads, TDB_HASHTABLE_SIZE(tdb)) != 0) { TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_start: failed to prime hash table\n")); tdb->ecode = TDB_ERR_IO; tdb->methods = tdb->transaction->io_methods; goto fail; } return 0; fail: tdb_brlock(tdb, FREELIST_TOP, F_UNLCK, F_SETLKW, 0, 0); tdb_transaction_unlock(tdb); SAFE_FREE(tdb->transaction->hash_heads); SAFE_FREE(tdb->transaction); return -1; } /* cancel the current transaction */ int tdb_transaction_cancel(struct tdb_context *tdb) { if (tdb->transaction == NULL) { TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_cancel: no transaction\n")); return -1; } if (tdb->transaction->nesting != 0) { tdb->transaction->transaction_error = 1; tdb->transaction->nesting--; return 0; } tdb->map_size = tdb->transaction->old_map_size; /* free all the transaction elements */ while (tdb->transaction->elements) { struct tdb_transaction_el *el = tdb->transaction->elements; tdb->transaction->elements = el->next; free(el->data); free(el); } /* remove any global lock created during the transaction */ if (tdb->global_lock.count != 0) { tdb_brlock(tdb, FREELIST_TOP, F_UNLCK, F_SETLKW, 0, 4*tdb->header.hash_size); tdb->global_lock.count = 0; } /* remove any locks created during the transaction */ if (tdb->num_locks != 0) { int i; for (i=0;inum_lockrecs;i++) { tdb_brlock(tdb,FREELIST_TOP+4*tdb->lockrecs[i].list, F_UNLCK,F_SETLKW, 0, 1); } tdb->num_locks = 0; tdb->num_lockrecs = 0; SAFE_FREE(tdb->lockrecs); } /* restore the normal io methods */ tdb->methods = tdb->transaction->io_methods; tdb_brlock(tdb, FREELIST_TOP, F_UNLCK, F_SETLKW, 0, 0); tdb_transaction_unlock(tdb); SAFE_FREE(tdb->transaction->hash_heads); SAFE_FREE(tdb->transaction); return 0; } /* sync to disk */ static int transaction_sync(struct tdb_context *tdb, tdb_off_t offset, tdb_len_t length) { if (fsync(tdb->fd) != 0) { tdb->ecode = TDB_ERR_IO; TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction: fsync failed\n")); return -1; } #ifdef MS_SYNC if (tdb->map_ptr) { tdb_off_t moffset = offset & ~(tdb->page_size-1); if (msync(moffset + (char *)tdb->map_ptr, length + (offset - moffset), MS_SYNC) != 0) { tdb->ecode = TDB_ERR_IO; TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction: msync failed - %s\n", strerror(errno))); return -1; } } #endif return 0; } /* work out how much space the linearised recovery data will consume */ static tdb_len_t tdb_recovery_size(struct tdb_context *tdb) { struct tdb_transaction_el *el; tdb_len_t recovery_size = 0; recovery_size = sizeof(u32); for (el=tdb->transaction->elements;el;el=el->next) { if (el->offset >= tdb->transaction->old_map_size) { continue; } recovery_size += 2*sizeof(tdb_off_t) + el->length; } return recovery_size; } /* allocate the recovery area, or use an existing recovery area if it is large enough */ static int tdb_recovery_allocate(struct tdb_context *tdb, tdb_len_t *recovery_size, tdb_off_t *recovery_offset, tdb_len_t *recovery_max_size) { struct list_struct rec; const struct tdb_methods *methods = tdb->transaction->io_methods; tdb_off_t recovery_head; if (tdb_ofs_read(tdb, TDB_RECOVERY_HEAD, &recovery_head) == -1) { TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_recovery_allocate: failed to read recovery head\n")); return -1; } rec.rec_len = 0; if (recovery_head != 0 && methods->tdb_read(tdb, recovery_head, &rec, sizeof(rec), DOCONV()) == -1) { TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_recovery_allocate: failed to read recovery record\n")); return -1; } *recovery_size = tdb_recovery_size(tdb); if (recovery_head != 0 && *recovery_size <= rec.rec_len) { /* it fits in the existing area */ *recovery_max_size = rec.rec_len; *recovery_offset = recovery_head; return 0; } /* we need to free up the old recovery area, then allocate a new one at the end of the file. Note that we cannot use tdb_allocate() to allocate the new one as that might return us an area that is being currently used (as of the start of the transaction) */ if (recovery_head != 0) { if (tdb_free(tdb, recovery_head, &rec) == -1) { TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_recovery_allocate: failed to free previous recovery area\n")); return -1; } } /* the tdb_free() call might have increased the recovery size */ *recovery_size = tdb_recovery_size(tdb); /* round up to a multiple of page size */ *recovery_max_size = TDB_ALIGN(sizeof(rec) + *recovery_size, tdb->page_size) - sizeof(rec); *recovery_offset = tdb->map_size; recovery_head = *recovery_offset; if (methods->tdb_expand_file(tdb, tdb->transaction->old_map_size, (tdb->map_size - tdb->transaction->old_map_size) + sizeof(rec) + *recovery_max_size) == -1) { TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_recovery_allocate: failed to create recovery area\n")); return -1; } /* remap the file (if using mmap) */ methods->tdb_oob(tdb, tdb->map_size + 1, 1); /* we have to reset the old map size so that we don't try to expand the file again in the transaction commit, which would destroy the recovery area */ tdb->transaction->old_map_size = tdb->map_size; /* write the recovery header offset and sync - we can sync without a race here as the magic ptr in the recovery record has not been set */ CONVERT(recovery_head); if (methods->tdb_write(tdb, TDB_RECOVERY_HEAD, &recovery_head, sizeof(tdb_off_t)) == -1) { TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_recovery_allocate: failed to write recovery head\n")); return -1; } return 0; } /* setup the recovery data that will be used on a crash during commit */ static int transaction_setup_recovery(struct tdb_context *tdb, tdb_off_t *magic_offset) { struct tdb_transaction_el *el; tdb_len_t recovery_size; unsigned char *data, *p; const struct tdb_methods *methods = tdb->transaction->io_methods; struct list_struct *rec; tdb_off_t recovery_offset, recovery_max_size; tdb_off_t old_map_size = tdb->transaction->old_map_size; u32 magic, tailer; /* check that the recovery area has enough space */ if (tdb_recovery_allocate(tdb, &recovery_size, &recovery_offset, &recovery_max_size) == -1) { return -1; } data = (unsigned char *)malloc(recovery_size + sizeof(*rec)); if (data == NULL) { tdb->ecode = TDB_ERR_OOM; return -1; } rec = (struct list_struct *)data; memset(rec, 0, sizeof(*rec)); rec->magic = 0; rec->data_len = recovery_size; rec->rec_len = recovery_max_size; rec->key_len = old_map_size; CONVERT(rec); /* build the recovery data into a single blob to allow us to do a single large write, which should be more efficient */ p = data + sizeof(*rec); for (el=tdb->transaction->elements;el;el=el->next) { if (el->offset >= old_map_size) { continue; } if (el->offset + el->length > tdb->transaction->old_map_size) { TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_setup_recovery: transaction data over new region boundary\n")); free(data); tdb->ecode = TDB_ERR_CORRUPT; return -1; } memcpy(p, &el->offset, 4); memcpy(p+4, &el->length, 4); if (DOCONV()) { tdb_convert(p, 8); } /* the recovery area contains the old data, not the new data, so we have to call the original tdb_read method to get it */ if (methods->tdb_read(tdb, el->offset, p + 8, el->length, 0) != 0) { free(data); tdb->ecode = TDB_ERR_IO; return -1; } p += 8 + el->length; } /* and the tailer */ tailer = sizeof(*rec) + recovery_max_size; memcpy(p, &tailer, 4); CONVERT(p); /* write the recovery data to the recovery area */ if (methods->tdb_write(tdb, recovery_offset, data, sizeof(*rec) + recovery_size) == -1) { TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_setup_recovery: failed to write recovery data\n")); free(data); tdb->ecode = TDB_ERR_IO; return -1; } /* as we don't have ordered writes, we have to sync the recovery data before we update the magic to indicate that the recovery data is present */ if (transaction_sync(tdb, recovery_offset, sizeof(*rec) + recovery_size) == -1) { free(data); return -1; } free(data); magic = TDB_RECOVERY_MAGIC; CONVERT(magic); *magic_offset = recovery_offset + offsetof(struct list_struct, magic); if (methods->tdb_write(tdb, *magic_offset, &magic, sizeof(magic)) == -1) { TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_setup_recovery: failed to write recovery magic\n")); tdb->ecode = TDB_ERR_IO; return -1; } /* ensure the recovery magic marker is on disk */ if (transaction_sync(tdb, *magic_offset, sizeof(magic)) == -1) { return -1; } return 0; } /* commit the current transaction */ int tdb_transaction_commit(struct tdb_context *tdb) { const struct tdb_methods *methods; tdb_off_t magic_offset = 0; u32 zero = 0; if (tdb->transaction == NULL) { TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_commit: no transaction\n")); return -1; } if (tdb->transaction->transaction_error) { tdb->ecode = TDB_ERR_IO; tdb_transaction_cancel(tdb); TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_commit: transaction error pending\n")); return -1; } if (tdb->transaction->nesting != 0) { tdb->transaction->nesting--; return 0; } /* check for a null transaction */ if (tdb->transaction->elements == NULL) { tdb_transaction_cancel(tdb); return 0; } methods = tdb->transaction->io_methods; /* if there are any locks pending then the caller has not nested their locks properly, so fail the transaction */ if (tdb->num_locks || tdb->global_lock.count) { tdb->ecode = TDB_ERR_LOCK; TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_commit: locks pending on commit\n")); tdb_transaction_cancel(tdb); return -1; } /* upgrade the main transaction lock region to a write lock */ if (tdb_brlock_upgrade(tdb, FREELIST_TOP, 0) == -1) { TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_start: failed to upgrade hash locks\n")); tdb->ecode = TDB_ERR_LOCK; tdb_transaction_cancel(tdb); return -1; } /* get the global lock - this prevents new users attaching to the database during the commit */ if (tdb_brlock(tdb, GLOBAL_LOCK, F_WRLCK, F_SETLKW, 0, 1) == -1) { TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_transaction_commit: failed to get global lock\n")); tdb->ecode = TDB_ERR_LOCK; tdb_transaction_cancel(tdb); return -1; } if (!(tdb->flags & TDB_NOSYNC)) { /* write the recovery data to the end of the file */ if (transaction_setup_recovery(tdb, &magic_offset) == -1) { TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_commit: failed to setup recovery data\n")); tdb_brlock(tdb, GLOBAL_LOCK, F_UNLCK, F_SETLKW, 0, 1); tdb_transaction_cancel(tdb); return -1; } } /* expand the file to the new size if needed */ if (tdb->map_size != tdb->transaction->old_map_size) { if (methods->tdb_expand_file(tdb, tdb->transaction->old_map_size, tdb->map_size - tdb->transaction->old_map_size) == -1) { tdb->ecode = TDB_ERR_IO; TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_commit: expansion failed\n")); tdb_brlock(tdb, GLOBAL_LOCK, F_UNLCK, F_SETLKW, 0, 1); tdb_transaction_cancel(tdb); return -1; } tdb->map_size = tdb->transaction->old_map_size; methods->tdb_oob(tdb, tdb->map_size + 1, 1); } /* perform all the writes */ while (tdb->transaction->elements) { struct tdb_transaction_el *el = tdb->transaction->elements; if (methods->tdb_write(tdb, el->offset, el->data, el->length) == -1) { TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_commit: write failed during commit\n")); /* we've overwritten part of the data and possibly expanded the file, so we need to run the crash recovery code */ tdb->methods = methods; tdb_transaction_recover(tdb); tdb_transaction_cancel(tdb); tdb_brlock(tdb, GLOBAL_LOCK, F_UNLCK, F_SETLKW, 0, 1); TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_commit: write failed\n")); return -1; } tdb->transaction->elements = el->next; free(el->data); free(el); } if (!(tdb->flags & TDB_NOSYNC)) { /* ensure the new data is on disk */ if (transaction_sync(tdb, 0, tdb->map_size) == -1) { return -1; } /* remove the recovery marker */ if (methods->tdb_write(tdb, magic_offset, &zero, 4) == -1) { TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_commit: failed to remove recovery magic\n")); return -1; } /* ensure the recovery marker has been removed on disk */ if (transaction_sync(tdb, magic_offset, 4) == -1) { return -1; } } tdb_brlock(tdb, GLOBAL_LOCK, F_UNLCK, F_SETLKW, 0, 1); /* TODO: maybe write to some dummy hdr field, or write to magic offset without mmap, before the last sync, instead of the utime() call */ /* on some systems (like Linux 2.6.x) changes via mmap/msync don't change the mtime of the file, this means the file may not be backed up (as tdb rounding to block sizes means that file size changes are quite rare too). The following forces mtime changes when a transaction completes */ #ifdef HAVE_UTIME utime(tdb->name, NULL); #endif /* use a transaction cancel to free memory and remove the transaction locks */ tdb_transaction_cancel(tdb); return 0; } /* recover from an aborted transaction. Must be called with exclusive database write access already established (including the global lock to prevent new processes attaching) */ int tdb_transaction_recover(struct tdb_context *tdb) { tdb_off_t recovery_head, recovery_eof; unsigned char *data, *p; u32 zero = 0; struct list_struct rec; /* find the recovery area */ if (tdb_ofs_read(tdb, TDB_RECOVERY_HEAD, &recovery_head) == -1) { TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to read recovery head\n")); tdb->ecode = TDB_ERR_IO; return -1; } if (recovery_head == 0) { /* we have never allocated a recovery record */ return 0; } /* read the recovery record */ if (tdb->methods->tdb_read(tdb, recovery_head, &rec, sizeof(rec), DOCONV()) == -1) { TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to read recovery record\n")); tdb->ecode = TDB_ERR_IO; return -1; } if (rec.magic != TDB_RECOVERY_MAGIC) { /* there is no valid recovery data */ return 0; } if (tdb->read_only) { TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: attempt to recover read only database\n")); tdb->ecode = TDB_ERR_CORRUPT; return -1; } recovery_eof = rec.key_len; data = (unsigned char *)malloc(rec.data_len); if (data == NULL) { TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to allocate recovery data\n")); tdb->ecode = TDB_ERR_OOM; return -1; } /* read the full recovery data */ if (tdb->methods->tdb_read(tdb, recovery_head + sizeof(rec), data, rec.data_len, 0) == -1) { TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to read recovery data\n")); tdb->ecode = TDB_ERR_IO; return -1; } /* recover the file data */ p = data; while (p+8 < data + rec.data_len) { u32 ofs, len; if (DOCONV()) { tdb_convert(p, 8); } memcpy(&ofs, p, 4); memcpy(&len, p+4, 4); if (tdb->methods->tdb_write(tdb, ofs, p+8, len) == -1) { free(data); TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to recover %d bytes at offset %d\n", len, ofs)); tdb->ecode = TDB_ERR_IO; return -1; } p += 8 + len; } free(data); if (transaction_sync(tdb, 0, tdb->map_size) == -1) { TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to sync recovery\n")); tdb->ecode = TDB_ERR_IO; return -1; } /* if the recovery area is after the recovered eof then remove it */ if (recovery_eof <= recovery_head) { if (tdb_ofs_write(tdb, TDB_RECOVERY_HEAD, &zero) == -1) { TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to remove recovery head\n")); tdb->ecode = TDB_ERR_IO; return -1; } } /* remove the recovery magic */ if (tdb_ofs_write(tdb, recovery_head + offsetof(struct list_struct, magic), &zero) == -1) { TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to remove recovery magic\n")); tdb->ecode = TDB_ERR_IO; return -1; } /* reduce the file size to the old size */ tdb_munmap(tdb); if (ftruncate(tdb->fd, recovery_eof) != 0) { TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to reduce to recovery size\n")); tdb->ecode = TDB_ERR_IO; return -1; } tdb->map_size = recovery_eof; tdb_mmap(tdb); if (transaction_sync(tdb, 0, recovery_eof) == -1) { TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_transaction_recover: failed to sync2 recovery\n")); tdb->ecode = TDB_ERR_IO; return -1; } TDB_LOG((tdb, TDB_DEBUG_TRACE, "tdb_transaction_recover: recovered %d byte database\n", recovery_eof)); /* all done */ return 0; } /* file: freelist.c */ /* read a freelist record and check for simple errors */ static int tdb_rec_free_read(struct tdb_context *tdb, tdb_off_t off, struct list_struct *rec) { if (tdb->methods->tdb_read(tdb, off, rec, sizeof(*rec),DOCONV()) == -1) return -1; if (rec->magic == TDB_MAGIC) { /* this happens when a app is showdown while deleting a record - we should not completely fail when this happens */ TDB_LOG((tdb, TDB_DEBUG_WARNING, "tdb_rec_free_read non-free magic 0x%x at offset=%d - fixing\n", rec->magic, off)); rec->magic = TDB_FREE_MAGIC; if (tdb->methods->tdb_write(tdb, off, rec, sizeof(*rec)) == -1) return -1; } if (rec->magic != TDB_FREE_MAGIC) { /* Ensure ecode is set for log fn. */ tdb->ecode = TDB_ERR_CORRUPT; TDB_LOG((tdb, TDB_DEBUG_WARNING, "tdb_rec_free_read bad magic 0x%x at offset=%d\n", rec->magic, off)); return TDB_ERRCODE(TDB_ERR_CORRUPT, -1); } if (tdb->methods->tdb_oob(tdb, rec->next+sizeof(*rec), 0) != 0) return -1; return 0; } /* Remove an element from the freelist. Must have alloc lock. */ static int remove_from_freelist(struct tdb_context *tdb, tdb_off_t off, tdb_off_t next) { tdb_off_t last_ptr, i; /* read in the freelist top */ last_ptr = FREELIST_TOP; while (tdb_ofs_read(tdb, last_ptr, &i) != -1 && i != 0) { if (i == off) { /* We've found it! */ return tdb_ofs_write(tdb, last_ptr, &next); } /* Follow chain (next offset is at start of record) */ last_ptr = i; } TDB_LOG((tdb, TDB_DEBUG_FATAL,"remove_from_freelist: not on list at off=%d\n", off)); return TDB_ERRCODE(TDB_ERR_CORRUPT, -1); } /* update a record tailer (must hold allocation lock) */ static int update_tailer(struct tdb_context *tdb, tdb_off_t offset, const struct list_struct *rec) { tdb_off_t totalsize; /* Offset of tailer from record header */ totalsize = sizeof(*rec) + rec->rec_len; return tdb_ofs_write(tdb, offset + totalsize - sizeof(tdb_off_t), &totalsize); } /* Add an element into the freelist. Merge adjacent records if neccessary. */ int tdb_free(struct tdb_context *tdb, tdb_off_t offset, struct list_struct *rec) { tdb_off_t right, left; /* Allocation and tailer lock */ if (tdb_lock(tdb, -1, F_WRLCK) != 0) return -1; /* set an initial tailer, so if we fail we don't leave a bogus record */ if (update_tailer(tdb, offset, rec) != 0) { TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_free: update_tailer failed!\n")); goto fail; } /* Look right first (I'm an Australian, dammit) */ right = offset + sizeof(*rec) + rec->rec_len; if (right + sizeof(*rec) <= tdb->map_size) { struct list_struct r; if (tdb->methods->tdb_read(tdb, right, &r, sizeof(r), DOCONV()) == -1) { TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_free: right read failed at %u\n", right)); goto left; } /* If it's free, expand to include it. */ if (r.magic == TDB_FREE_MAGIC) { if (remove_from_freelist(tdb, right, r.next) == -1) { TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_free: right free failed at %u\n", right)); goto left; } rec->rec_len += sizeof(r) + r.rec_len; } } left: /* Look left */ left = offset - sizeof(tdb_off_t); if (left > TDB_DATA_START(tdb->header.hash_size)) { struct list_struct l; tdb_off_t leftsize; /* Read in tailer and jump back to header */ if (tdb_ofs_read(tdb, left, &leftsize) == -1) { TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_free: left offset read failed at %u\n", left)); goto update; } /* it could be uninitialised data */ if (leftsize == 0 || leftsize == TDB_PAD_U32) { goto update; } left = offset - leftsize; /* Now read in record */ if (tdb->methods->tdb_read(tdb, left, &l, sizeof(l), DOCONV()) == -1) { TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_free: left read failed at %u (%u)\n", left, leftsize)); goto update; } /* If it's free, expand to include it. */ if (l.magic == TDB_FREE_MAGIC) { if (remove_from_freelist(tdb, left, l.next) == -1) { TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_free: left free failed at %u\n", left)); goto update; } else { offset = left; rec->rec_len += leftsize; } } } update: if (update_tailer(tdb, offset, rec) == -1) { TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_free: update_tailer failed at %u\n", offset)); goto fail; } /* Now, prepend to free list */ rec->magic = TDB_FREE_MAGIC; if (tdb_ofs_read(tdb, FREELIST_TOP, &rec->next) == -1 || tdb_rec_write(tdb, offset, rec) == -1 || tdb_ofs_write(tdb, FREELIST_TOP, &offset) == -1) { TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_free record write failed at offset=%d\n", offset)); goto fail; } /* And we're done. */ tdb_unlock(tdb, -1, F_WRLCK); return 0; fail: tdb_unlock(tdb, -1, F_WRLCK); return -1; } /* the core of tdb_allocate - called when we have decided which free list entry to use */ static tdb_off_t tdb_allocate_ofs(struct tdb_context *tdb, tdb_len_t length, tdb_off_t rec_ptr, struct list_struct *rec, tdb_off_t last_ptr) { struct list_struct newrec; tdb_off_t newrec_ptr; memset(&newrec, '\0', sizeof(newrec)); /* found it - now possibly split it up */ if (rec->rec_len > length + MIN_REC_SIZE) { /* Length of left piece */ length = TDB_ALIGN(length, TDB_ALIGNMENT); /* Right piece to go on free list */ newrec.rec_len = rec->rec_len - (sizeof(*rec) + length); newrec_ptr = rec_ptr + sizeof(*rec) + length; /* And left record is shortened */ rec->rec_len = length; } else { newrec_ptr = 0; } /* Remove allocated record from the free list */ if (tdb_ofs_write(tdb, last_ptr, &rec->next) == -1) { return 0; } /* Update header: do this before we drop alloc lock, otherwise tdb_free() might try to merge with us, thinking we're free. (Thanks Jeremy Allison). */ rec->magic = TDB_MAGIC; if (tdb_rec_write(tdb, rec_ptr, rec) == -1) { return 0; } /* Did we create new block? */ if (newrec_ptr) { /* Update allocated record tailer (we shortened it). */ if (update_tailer(tdb, rec_ptr, rec) == -1) { return 0; } /* Free new record */ if (tdb_free(tdb, newrec_ptr, &newrec) == -1) { return 0; } } /* all done - return the new record offset */ return rec_ptr; } /* allocate some space from the free list. The offset returned points to a unconnected list_struct within the database with room for at least length bytes of total data 0 is returned if the space could not be allocated */ tdb_off_t tdb_allocate(struct tdb_context *tdb, tdb_len_t length, struct list_struct *rec) { tdb_off_t rec_ptr, last_ptr, newrec_ptr; struct { tdb_off_t rec_ptr, last_ptr; tdb_len_t rec_len; } bestfit; if (tdb_lock(tdb, -1, F_WRLCK) == -1) return 0; /* Extra bytes required for tailer */ length += sizeof(tdb_off_t); again: last_ptr = FREELIST_TOP; /* read in the freelist top */ if (tdb_ofs_read(tdb, FREELIST_TOP, &rec_ptr) == -1) goto fail; bestfit.rec_ptr = 0; bestfit.last_ptr = 0; bestfit.rec_len = 0; /* this is a best fit allocation strategy. Originally we used a first fit strategy, but it suffered from massive fragmentation issues when faced with a slowly increasing record size. */ while (rec_ptr) { if (tdb_rec_free_read(tdb, rec_ptr, rec) == -1) { goto fail; } if (rec->rec_len >= length) { if (bestfit.rec_ptr == 0 || rec->rec_len < bestfit.rec_len) { bestfit.rec_len = rec->rec_len; bestfit.rec_ptr = rec_ptr; bestfit.last_ptr = last_ptr; /* consider a fit to be good enough if we aren't wasting more than half the space */ if (bestfit.rec_len < 2*length) { break; } } } /* move to the next record */ last_ptr = rec_ptr; rec_ptr = rec->next; } if (bestfit.rec_ptr != 0) { if (tdb_rec_free_read(tdb, bestfit.rec_ptr, rec) == -1) { goto fail; } newrec_ptr = tdb_allocate_ofs(tdb, length, bestfit.rec_ptr, rec, bestfit.last_ptr); tdb_unlock(tdb, -1, F_WRLCK); return newrec_ptr; } /* we didn't find enough space. See if we can expand the database and if we can then try again */ if (tdb_expand(tdb, length + sizeof(*rec)) == 0) goto again; fail: tdb_unlock(tdb, -1, F_WRLCK); return 0; } /* file: freelistcheck.c */ /* Check the freelist is good and contains no loops. Very memory intensive - only do this as a consistency checker. Heh heh - uses an in memory tdb as the storage for the "seen" record list. For some reason this strikes me as extremely clever as I don't have to write another tree data structure implementation :-). */ static int seen_insert(struct tdb_context *mem_tdb, tdb_off_t rec_ptr) { TDB_DATA key, data; memset(&data, '\0', sizeof(data)); key.dptr = (unsigned char *)&rec_ptr; key.dsize = sizeof(rec_ptr); return tdb_store(mem_tdb, key, data, TDB_INSERT); } int tdb_validate_freelist(struct tdb_context *tdb, int *pnum_entries) { struct tdb_context *mem_tdb = NULL; struct list_struct rec; tdb_off_t rec_ptr, last_ptr; int ret = -1; *pnum_entries = 0; mem_tdb = tdb_open("flval", tdb->header.hash_size, TDB_INTERNAL, O_RDWR, 0600); if (!mem_tdb) { return -1; } if (tdb_lock(tdb, -1, F_WRLCK) == -1) { tdb_close(mem_tdb); return 0; } last_ptr = FREELIST_TOP; /* Store the FREELIST_TOP record. */ if (seen_insert(mem_tdb, last_ptr) == -1) { ret = TDB_ERRCODE(TDB_ERR_CORRUPT, -1); goto fail; } /* read in the freelist top */ if (tdb_ofs_read(tdb, FREELIST_TOP, &rec_ptr) == -1) { goto fail; } while (rec_ptr) { /* If we can't store this record (we've seen it before) then the free list has a loop and must be corrupt. */ if (seen_insert(mem_tdb, rec_ptr)) { ret = TDB_ERRCODE(TDB_ERR_CORRUPT, -1); goto fail; } if (tdb_rec_free_read(tdb, rec_ptr, &rec) == -1) { goto fail; } /* move to the next record */ last_ptr = rec_ptr; rec_ptr = rec.next; *pnum_entries += 1; } ret = 0; fail: tdb_close(mem_tdb); tdb_unlock(tdb, -1, F_WRLCK); return ret; } /* file: traverse.c */ /* Uses traverse lock: 0 = finish, -1 = error, other = record offset */ static int tdb_next_lock(struct tdb_context *tdb, struct tdb_traverse_lock *tlock, struct list_struct *rec) { int want_next = (tlock->off != 0); /* Lock each chain from the start one. */ for (; tlock->hash < tdb->header.hash_size; tlock->hash++) { if (!tlock->off && tlock->hash != 0) { /* this is an optimisation for the common case where the hash chain is empty, which is particularly common for the use of tdb with ldb, where large hashes are used. In that case we spend most of our time in tdb_brlock(), locking empty hash chains. To avoid this, we do an unlocked pre-check to see if the hash chain is empty before starting to look inside it. If it is empty then we can avoid that hash chain. If it isn't empty then we can't believe the value we get back, as we read it without a lock, so instead we get the lock and re-fetch the value below. Notice that not doing this optimisation on the first hash chain is critical. We must guarantee that we have done at least one fcntl lock at the start of a search to guarantee that memory is coherent on SMP systems. If records are added by others during the search then thats OK, and we could possibly miss those with this trick, but we could miss them anyway without this trick, so the semantics don't change. With a non-indexed ldb search this trick gains us a factor of around 80 in speed on a linux 2.6.x system (testing using ldbtest). */ tdb->methods->next_hash_chain(tdb, &tlock->hash); if (tlock->hash == tdb->header.hash_size) { continue; } } if (tdb_lock(tdb, tlock->hash, tlock->lock_rw) == -1) return -1; /* No previous record? Start at top of chain. */ if (!tlock->off) { if (tdb_ofs_read(tdb, TDB_HASH_TOP(tlock->hash), &tlock->off) == -1) goto fail; } else { /* Otherwise unlock the previous record. */ if (tdb_unlock_record(tdb, tlock->off) != 0) goto fail; } if (want_next) { /* We have offset of old record: grab next */ if (tdb_rec_read(tdb, tlock->off, rec) == -1) goto fail; tlock->off = rec->next; } /* Iterate through chain */ while( tlock->off) { tdb_off_t current; if (tdb_rec_read(tdb, tlock->off, rec) == -1) goto fail; /* Detect infinite loops. From "Shlomi Yaakobovich" . */ if (tlock->off == rec->next) { TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_next_lock: loop detected.\n")); goto fail; } if (!TDB_DEAD(rec)) { /* Woohoo: we found one! */ if (tdb_lock_record(tdb, tlock->off) != 0) goto fail; return tlock->off; } /* Try to clean dead ones from old traverses */ current = tlock->off; tlock->off = rec->next; if (!(tdb->read_only || tdb->traverse_read) && tdb_do_delete(tdb, current, rec) != 0) goto fail; } tdb_unlock(tdb, tlock->hash, tlock->lock_rw); want_next = 0; } /* We finished iteration without finding anything */ return TDB_ERRCODE(TDB_SUCCESS, 0); fail: tlock->off = 0; if (tdb_unlock(tdb, tlock->hash, tlock->lock_rw) != 0) TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_next_lock: On error unlock failed!\n")); return -1; } /* traverse the entire database - calling fn(tdb, key, data) on each element. return -1 on error or the record count traversed if fn is NULL then it is not called a non-zero return value from fn() indicates that the traversal should stop */ static int tdb_traverse_internal(struct tdb_context *tdb, tdb_traverse_func fn, void *private_data, struct tdb_traverse_lock *tl) { TDB_DATA key, dbuf; struct list_struct rec; int ret, count = 0; /* This was in the initializaton, above, but the IRIX compiler * did not like it. crh */ tl->next = tdb->travlocks.next; /* fcntl locks don't stack: beware traverse inside traverse */ tdb->travlocks.next = tl; /* tdb_next_lock places locks on the record returned, and its chain */ while ((ret = tdb_next_lock(tdb, tl, &rec)) > 0) { count++; /* now read the full record */ key.dptr = tdb_alloc_read(tdb, tl->off + sizeof(rec), rec.key_len + rec.data_len); if (!key.dptr) { ret = -1; if (tdb_unlock(tdb, tl->hash, tl->lock_rw) != 0) goto out; if (tdb_unlock_record(tdb, tl->off) != 0) TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_traverse: key.dptr == NULL and unlock_record failed!\n")); goto out; } key.dsize = rec.key_len; dbuf.dptr = key.dptr + rec.key_len; dbuf.dsize = rec.data_len; /* Drop chain lock, call out */ if (tdb_unlock(tdb, tl->hash, tl->lock_rw) != 0) { ret = -1; SAFE_FREE(key.dptr); goto out; } if (fn && fn(tdb, key, dbuf, private_data)) { /* They want us to terminate traversal */ ret = count; if (tdb_unlock_record(tdb, tl->off) != 0) { TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_traverse: unlock_record failed!\n"));; ret = -1; } SAFE_FREE(key.dptr); goto out; } SAFE_FREE(key.dptr); } out: tdb->travlocks.next = tl->next; if (ret < 0) return -1; else return count; } /* a write style traverse - temporarily marks the db read only */ int tdb_traverse_read(struct tdb_context *tdb, tdb_traverse_func fn, void *private_data) { struct tdb_traverse_lock tl = { NULL, 0, 0, F_RDLCK }; int ret; /* we need to get a read lock on the transaction lock here to cope with the lock ordering semantics of solaris10 */ if (tdb_transaction_lock(tdb, F_RDLCK)) { return -1; } tdb->traverse_read++; ret = tdb_traverse_internal(tdb, fn, private_data, &tl); tdb->traverse_read--; tdb_transaction_unlock(tdb); return ret; } /* a write style traverse - needs to get the transaction lock to prevent deadlocks */ int tdb_traverse(struct tdb_context *tdb, tdb_traverse_func fn, void *private_data) { struct tdb_traverse_lock tl = { NULL, 0, 0, F_WRLCK }; int ret; if (tdb->read_only || tdb->traverse_read) { return tdb_traverse_read(tdb, fn, private_data); } if (tdb_transaction_lock(tdb, F_WRLCK)) { return -1; } ret = tdb_traverse_internal(tdb, fn, private_data, &tl); tdb_transaction_unlock(tdb); return ret; } /* find the first entry in the database and return its key */ TDB_DATA tdb_firstkey(struct tdb_context *tdb) { TDB_DATA key; struct list_struct rec; /* release any old lock */ if (tdb_unlock_record(tdb, tdb->travlocks.off) != 0) return tdb_null; tdb->travlocks.off = tdb->travlocks.hash = 0; tdb->travlocks.lock_rw = F_RDLCK; /* Grab first record: locks chain and returned record. */ if (tdb_next_lock(tdb, &tdb->travlocks, &rec) <= 0) return tdb_null; /* now read the key */ key.dsize = rec.key_len; key.dptr =tdb_alloc_read(tdb,tdb->travlocks.off+sizeof(rec),key.dsize); /* Unlock the hash chain of the record we just read. */ if (tdb_unlock(tdb, tdb->travlocks.hash, tdb->travlocks.lock_rw) != 0) TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_firstkey: error occurred while tdb_unlocking!\n")); return key; } /* find the next entry in the database, returning its key */ TDB_DATA tdb_nextkey(struct tdb_context *tdb, TDB_DATA oldkey) { u32 oldhash; TDB_DATA key = tdb_null; struct list_struct rec; unsigned char *k = NULL; /* Is locked key the old key? If so, traverse will be reliable. */ if (tdb->travlocks.off) { if (tdb_lock(tdb,tdb->travlocks.hash,tdb->travlocks.lock_rw)) return tdb_null; if (tdb_rec_read(tdb, tdb->travlocks.off, &rec) == -1 || !(k = tdb_alloc_read(tdb,tdb->travlocks.off+sizeof(rec), rec.key_len)) || memcmp(k, oldkey.dptr, oldkey.dsize) != 0) { /* No, it wasn't: unlock it and start from scratch */ if (tdb_unlock_record(tdb, tdb->travlocks.off) != 0) { SAFE_FREE(k); return tdb_null; } if (tdb_unlock(tdb, tdb->travlocks.hash, tdb->travlocks.lock_rw) != 0) { SAFE_FREE(k); return tdb_null; } tdb->travlocks.off = 0; } SAFE_FREE(k); } if (!tdb->travlocks.off) { /* No previous element: do normal find, and lock record */ tdb->travlocks.off = tdb_find_lock_hash(tdb, oldkey, tdb->hash_fn(&oldkey), tdb->travlocks.lock_rw, &rec); if (!tdb->travlocks.off) return tdb_null; tdb->travlocks.hash = BUCKET(rec.full_hash); if (tdb_lock_record(tdb, tdb->travlocks.off) != 0) { TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_nextkey: lock_record failed (%s)!\n", strerror(errno))); return tdb_null; } } oldhash = tdb->travlocks.hash; /* Grab next record: locks chain and returned record, unlocks old record */ if (tdb_next_lock(tdb, &tdb->travlocks, &rec) > 0) { key.dsize = rec.key_len; key.dptr = tdb_alloc_read(tdb, tdb->travlocks.off+sizeof(rec), key.dsize); /* Unlock the chain of this new record */ if (tdb_unlock(tdb, tdb->travlocks.hash, tdb->travlocks.lock_rw) != 0) TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_nextkey: WARNING tdb_unlock failed!\n")); } /* Unlock the chain of old record */ if (tdb_unlock(tdb, BUCKET(oldhash), tdb->travlocks.lock_rw) != 0) TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_nextkey: WARNING tdb_unlock failed!\n")); return key; } /* file: dump.c */ static tdb_off_t tdb_dump_record(struct tdb_context *tdb, int hash, tdb_off_t offset) { struct list_struct rec; tdb_off_t tailer_ofs, tailer; if (tdb->methods->tdb_read(tdb, offset, (char *)&rec, sizeof(rec), DOCONV()) == -1) { printf("ERROR: failed to read record at %u\n", offset); return 0; } printf(" rec: hash=%d offset=0x%08x next=0x%08x rec_len=%d " "key_len=%d data_len=%d full_hash=0x%x magic=0x%x\n", hash, offset, rec.next, rec.rec_len, rec.key_len, rec.data_len, rec.full_hash, rec.magic); tailer_ofs = offset + sizeof(rec) + rec.rec_len - sizeof(tdb_off_t); if (tdb_ofs_read(tdb, tailer_ofs, &tailer) == -1) { printf("ERROR: failed to read tailer at %u\n", tailer_ofs); return rec.next; } if (tailer != rec.rec_len + sizeof(rec)) { printf("ERROR: tailer does not match record! tailer=%u totalsize=%u\n", (unsigned int)tailer, (unsigned int)(rec.rec_len + sizeof(rec))); } return rec.next; } static int tdb_dump_chain(struct tdb_context *tdb, int i) { tdb_off_t rec_ptr, top; top = TDB_HASH_TOP(i); if (tdb_lock(tdb, i, F_WRLCK) != 0) return -1; if (tdb_ofs_read(tdb, top, &rec_ptr) == -1) return tdb_unlock(tdb, i, F_WRLCK); if (rec_ptr) printf("hash=%d\n", i); while (rec_ptr) { rec_ptr = tdb_dump_record(tdb, i, rec_ptr); } return tdb_unlock(tdb, i, F_WRLCK); } void tdb_dump_all(struct tdb_context *tdb) { int i; for (i=0;iheader.hash_size;i++) { tdb_dump_chain(tdb, i); } printf("freelist:\n"); tdb_dump_chain(tdb, -1); } int tdb_printfreelist(struct tdb_context *tdb) { int ret; long total_free = 0; tdb_off_t offset, rec_ptr; struct list_struct rec; if ((ret = tdb_lock(tdb, -1, F_WRLCK)) != 0) return ret; offset = FREELIST_TOP; /* read in the freelist top */ if (tdb_ofs_read(tdb, offset, &rec_ptr) == -1) { tdb_unlock(tdb, -1, F_WRLCK); return 0; } printf("freelist top=[0x%08x]\n", rec_ptr ); while (rec_ptr) { if (tdb->methods->tdb_read(tdb, rec_ptr, (char *)&rec, sizeof(rec), DOCONV()) == -1) { tdb_unlock(tdb, -1, F_WRLCK); return -1; } if (rec.magic != TDB_FREE_MAGIC) { printf("bad magic 0x%08x in free list\n", rec.magic); tdb_unlock(tdb, -1, F_WRLCK); return -1; } printf("entry offset=[0x%08x], rec.rec_len = [0x%08x (%d)] (end = 0x%08x)\n", rec_ptr, rec.rec_len, rec.rec_len, rec_ptr + rec.rec_len); total_free += rec.rec_len; /* move to the next record */ rec_ptr = rec.next; } printf("total rec_len = [0x%08x (%d)]\n", (int)total_free, (int)total_free); return tdb_unlock(tdb, -1, F_WRLCK); } /* file: tdb.c */ TDB_DATA tdb_null; /* non-blocking increment of the tdb sequence number if the tdb has been opened using the TDB_SEQNUM flag */ void tdb_increment_seqnum_nonblock(struct tdb_context *tdb) { tdb_off_t seqnum=0; if (!(tdb->flags & TDB_SEQNUM)) { return; } /* we ignore errors from this, as we have no sane way of dealing with them. */ tdb_ofs_read(tdb, TDB_SEQNUM_OFS, &seqnum); seqnum++; tdb_ofs_write(tdb, TDB_SEQNUM_OFS, &seqnum); } /* increment the tdb sequence number if the tdb has been opened using the TDB_SEQNUM flag */ static void tdb_increment_seqnum(struct tdb_context *tdb) { if (!(tdb->flags & TDB_SEQNUM)) { return; } if (tdb_brlock(tdb, TDB_SEQNUM_OFS, F_WRLCK, F_SETLKW, 1, 1) != 0) { return; } tdb_increment_seqnum_nonblock(tdb); tdb_brlock(tdb, TDB_SEQNUM_OFS, F_UNLCK, F_SETLKW, 1, 1); } static int tdb_key_compare(TDB_DATA key, TDB_DATA data, void *private_data) { return memcmp(data.dptr, key.dptr, data.dsize); } /* Returns 0 on fail. On success, return offset of record, and fills in rec */ static tdb_off_t tdb_find(struct tdb_context *tdb, TDB_DATA key, u32 hash, struct list_struct *r) { tdb_off_t rec_ptr; /* read in the hash top */ if (tdb_ofs_read(tdb, TDB_HASH_TOP(hash), &rec_ptr) == -1) return 0; /* keep looking until we find the right record */ while (rec_ptr) { if (tdb_rec_read(tdb, rec_ptr, r) == -1) return 0; if (!TDB_DEAD(r) && hash==r->full_hash && key.dsize==r->key_len && tdb_parse_data(tdb, key, rec_ptr + sizeof(*r), r->key_len, tdb_key_compare, NULL) == 0) { return rec_ptr; } rec_ptr = r->next; } return TDB_ERRCODE(TDB_ERR_NOEXIST, 0); } /* As tdb_find, but if you succeed, keep the lock */ tdb_off_t tdb_find_lock_hash(struct tdb_context *tdb, TDB_DATA key, u32 hash, int locktype, struct list_struct *rec) { u32 rec_ptr; if (tdb_lock(tdb, BUCKET(hash), locktype) == -1) return 0; if (!(rec_ptr = tdb_find(tdb, key, hash, rec))) tdb_unlock(tdb, BUCKET(hash), locktype); return rec_ptr; } /* update an entry in place - this only works if the new data size is <= the old data size and the key exists. on failure return -1. */ static int tdb_update_hash(struct tdb_context *tdb, TDB_DATA key, u32 hash, TDB_DATA dbuf) { struct list_struct rec; tdb_off_t rec_ptr; /* find entry */ if (!(rec_ptr = tdb_find(tdb, key, hash, &rec))) return -1; /* must be long enough key, data and tailer */ if (rec.rec_len < key.dsize + dbuf.dsize + sizeof(tdb_off_t)) { tdb->ecode = TDB_SUCCESS; /* Not really an error */ return -1; } if (tdb->methods->tdb_write(tdb, rec_ptr + sizeof(rec) + rec.key_len, dbuf.dptr, dbuf.dsize) == -1) return -1; if (dbuf.dsize != rec.data_len) { /* update size */ rec.data_len = dbuf.dsize; return tdb_rec_write(tdb, rec_ptr, &rec); } return 0; } /* find an entry in the database given a key */ /* If an entry doesn't exist tdb_err will be set to * TDB_ERR_NOEXIST. If a key has no data attached * then the TDB_DATA will have zero length but * a non-zero pointer */ TDB_DATA tdb_fetch(struct tdb_context *tdb, TDB_DATA key) { tdb_off_t rec_ptr; struct list_struct rec; TDB_DATA ret; u32 hash; /* find which hash bucket it is in */ hash = tdb->hash_fn(&key); if (!(rec_ptr = tdb_find_lock_hash(tdb,key,hash,F_RDLCK,&rec))) return tdb_null; ret.dptr = tdb_alloc_read(tdb, rec_ptr + sizeof(rec) + rec.key_len, rec.data_len); ret.dsize = rec.data_len; tdb_unlock(tdb, BUCKET(rec.full_hash), F_RDLCK); return ret; } /* * Find an entry in the database and hand the record's data to a parsing * function. The parsing function is executed under the chain read lock, so it * should be fast and should not block on other syscalls. * * DONT CALL OTHER TDB CALLS FROM THE PARSER, THIS MIGHT LEAD TO SEGFAULTS. * * For mmapped tdb's that do not have a transaction open it points the parsing * function directly at the mmap area, it avoids the malloc/memcpy in this * case. If a transaction is open or no mmap is available, it has to do * malloc/read/parse/free. * * This is interesting for all readers of potentially large data structures in * the tdb records, ldb indexes being one example. */ int tdb_parse_record(struct tdb_context *tdb, TDB_DATA key, int (*parser)(TDB_DATA key, TDB_DATA data, void *private_data), void *private_data) { tdb_off_t rec_ptr; struct list_struct rec; int ret; u32 hash; /* find which hash bucket it is in */ hash = tdb->hash_fn(&key); if (!(rec_ptr = tdb_find_lock_hash(tdb,key,hash,F_RDLCK,&rec))) { return TDB_ERRCODE(TDB_ERR_NOEXIST, 0); } ret = tdb_parse_data(tdb, key, rec_ptr + sizeof(rec) + rec.key_len, rec.data_len, parser, private_data); tdb_unlock(tdb, BUCKET(rec.full_hash), F_RDLCK); return ret; } /* check if an entry in the database exists note that 1 is returned if the key is found and 0 is returned if not found this doesn't match the conventions in the rest of this module, but is compatible with gdbm */ static int tdb_exists_hash(struct tdb_context *tdb, TDB_DATA key, u32 hash) { struct list_struct rec; if (tdb_find_lock_hash(tdb, key, hash, F_RDLCK, &rec) == 0) return 0; tdb_unlock(tdb, BUCKET(rec.full_hash), F_RDLCK); return 1; } int tdb_exists(struct tdb_context *tdb, TDB_DATA key) { u32 hash = tdb->hash_fn(&key); return tdb_exists_hash(tdb, key, hash); } /* actually delete an entry in the database given the offset */ int tdb_do_delete(struct tdb_context *tdb, tdb_off_t rec_ptr, struct list_struct*rec) { tdb_off_t last_ptr, i; struct list_struct lastrec; if (tdb->read_only || tdb->traverse_read) return -1; if (tdb_write_lock_record(tdb, rec_ptr) == -1) { /* Someone traversing here: mark it as dead */ rec->magic = TDB_DEAD_MAGIC; return tdb_rec_write(tdb, rec_ptr, rec); } if (tdb_write_unlock_record(tdb, rec_ptr) != 0) return -1; /* find previous record in hash chain */ if (tdb_ofs_read(tdb, TDB_HASH_TOP(rec->full_hash), &i) == -1) return -1; for (last_ptr = 0; i != rec_ptr; last_ptr = i, i = lastrec.next) if (tdb_rec_read(tdb, i, &lastrec) == -1) return -1; /* unlink it: next ptr is at start of record. */ if (last_ptr == 0) last_ptr = TDB_HASH_TOP(rec->full_hash); if (tdb_ofs_write(tdb, last_ptr, &rec->next) == -1) return -1; /* recover the space */ if (tdb_free(tdb, rec_ptr, rec) == -1) return -1; return 0; } static int tdb_count_dead(struct tdb_context *tdb, u32 hash) { int res = 0; tdb_off_t rec_ptr; struct list_struct rec; /* read in the hash top */ if (tdb_ofs_read(tdb, TDB_HASH_TOP(hash), &rec_ptr) == -1) return 0; while (rec_ptr) { if (tdb_rec_read(tdb, rec_ptr, &rec) == -1) return 0; if (rec.magic == TDB_DEAD_MAGIC) { res += 1; } rec_ptr = rec.next; } return res; } /* * Purge all DEAD records from a hash chain */ static int tdb_purge_dead(struct tdb_context *tdb, u32 hash) { int res = -1; struct list_struct rec; tdb_off_t rec_ptr; if (tdb_lock(tdb, -1, F_WRLCK) == -1) { return -1; } /* read in the hash top */ if (tdb_ofs_read(tdb, TDB_HASH_TOP(hash), &rec_ptr) == -1) goto fail; while (rec_ptr) { tdb_off_t next; if (tdb_rec_read(tdb, rec_ptr, &rec) == -1) { goto fail; } next = rec.next; if (rec.magic == TDB_DEAD_MAGIC && tdb_do_delete(tdb, rec_ptr, &rec) == -1) { goto fail; } rec_ptr = next; } res = 0; fail: tdb_unlock(tdb, -1, F_WRLCK); return res; } /* delete an entry in the database given a key */ static int tdb_delete_hash(struct tdb_context *tdb, TDB_DATA key, u32 hash) { tdb_off_t rec_ptr; struct list_struct rec; int ret; if (tdb->max_dead_records != 0) { /* * Allow for some dead records per hash chain, mainly for * tdb's with a very high create/delete rate like locking.tdb. */ if (tdb_lock(tdb, BUCKET(hash), F_WRLCK) == -1) return -1; if (tdb_count_dead(tdb, hash) >= tdb->max_dead_records) { /* * Don't let the per-chain freelist grow too large, * delete all existing dead records */ tdb_purge_dead(tdb, hash); } if (!(rec_ptr = tdb_find(tdb, key, hash, &rec))) { tdb_unlock(tdb, BUCKET(hash), F_WRLCK); return -1; } /* * Just mark the record as dead. */ rec.magic = TDB_DEAD_MAGIC; ret = tdb_rec_write(tdb, rec_ptr, &rec); } else { if (!(rec_ptr = tdb_find_lock_hash(tdb, key, hash, F_WRLCK, &rec))) return -1; ret = tdb_do_delete(tdb, rec_ptr, &rec); } if (ret == 0) { tdb_increment_seqnum(tdb); } if (tdb_unlock(tdb, BUCKET(rec.full_hash), F_WRLCK) != 0) TDB_LOG((tdb, TDB_DEBUG_WARNING, "tdb_delete: WARNING tdb_unlock failed!\n")); return ret; } int tdb_delete(struct tdb_context *tdb, TDB_DATA key) { u32 hash = tdb->hash_fn(&key); return tdb_delete_hash(tdb, key, hash); } /* * See if we have a dead record around with enough space */ static tdb_off_t tdb_find_dead(struct tdb_context *tdb, u32 hash, struct list_struct *r, tdb_len_t length) { tdb_off_t rec_ptr; /* read in the hash top */ if (tdb_ofs_read(tdb, TDB_HASH_TOP(hash), &rec_ptr) == -1) return 0; /* keep looking until we find the right record */ while (rec_ptr) { if (tdb_rec_read(tdb, rec_ptr, r) == -1) return 0; if (TDB_DEAD(r) && r->rec_len >= length) { /* * First fit for simple coding, TODO: change to best * fit */ return rec_ptr; } rec_ptr = r->next; } return 0; } /* store an element in the database, replacing any existing element with the same key return 0 on success, -1 on failure */ int tdb_store(struct tdb_context *tdb, TDB_DATA key, TDB_DATA dbuf, int flag) { struct list_struct rec; u32 hash; tdb_off_t rec_ptr; char *p = NULL; int ret = -1; if (tdb->read_only || tdb->traverse_read) { tdb->ecode = TDB_ERR_RDONLY; return -1; } /* find which hash bucket it is in */ hash = tdb->hash_fn(&key); if (tdb_lock(tdb, BUCKET(hash), F_WRLCK) == -1) return -1; /* check for it existing, on insert. */ if (flag == TDB_INSERT) { if (tdb_exists_hash(tdb, key, hash)) { tdb->ecode = TDB_ERR_EXISTS; goto fail; } } else { /* first try in-place update, on modify or replace. */ if (tdb_update_hash(tdb, key, hash, dbuf) == 0) { goto done; } if (tdb->ecode == TDB_ERR_NOEXIST && flag == TDB_MODIFY) { /* if the record doesn't exist and we are in TDB_MODIFY mode then we should fail the store */ goto fail; } } /* reset the error code potentially set by the tdb_update() */ tdb->ecode = TDB_SUCCESS; /* delete any existing record - if it doesn't exist we don't care. Doing this first reduces fragmentation, and avoids coalescing with `allocated' block before it's updated. */ if (flag != TDB_INSERT) tdb_delete_hash(tdb, key, hash); /* Copy key+value *before* allocating free space in case malloc fails and we are left with a dead spot in the tdb. */ if (!(p = (char *)malloc(key.dsize + dbuf.dsize))) { tdb->ecode = TDB_ERR_OOM; goto fail; } memcpy(p, key.dptr, key.dsize); if (dbuf.dsize) memcpy(p+key.dsize, dbuf.dptr, dbuf.dsize); if (tdb->max_dead_records != 0) { /* * Allow for some dead records per hash chain, look if we can * find one that can hold the new record. We need enough space * for key, data and tailer. If we find one, we don't have to * consult the central freelist. */ rec_ptr = tdb_find_dead( tdb, hash, &rec, key.dsize + dbuf.dsize + sizeof(tdb_off_t)); if (rec_ptr != 0) { rec.key_len = key.dsize; rec.data_len = dbuf.dsize; rec.full_hash = hash; rec.magic = TDB_MAGIC; if (tdb_rec_write(tdb, rec_ptr, &rec) == -1 || tdb->methods->tdb_write( tdb, rec_ptr + sizeof(rec), p, key.dsize + dbuf.dsize) == -1) { goto fail; } goto done; } } /* * We have to allocate some space from the freelist, so this means we * have to lock it. Use the chance to purge all the DEAD records from * the hash chain under the freelist lock. */ if (tdb_lock(tdb, -1, F_WRLCK) == -1) { goto fail; } if ((tdb->max_dead_records != 0) && (tdb_purge_dead(tdb, hash) == -1)) { tdb_unlock(tdb, -1, F_WRLCK); goto fail; } /* we have to allocate some space */ rec_ptr = tdb_allocate(tdb, key.dsize + dbuf.dsize, &rec); tdb_unlock(tdb, -1, F_WRLCK); if (rec_ptr == 0) { goto fail; } /* Read hash top into next ptr */ if (tdb_ofs_read(tdb, TDB_HASH_TOP(hash), &rec.next) == -1) goto fail; rec.key_len = key.dsize; rec.data_len = dbuf.dsize; rec.full_hash = hash; rec.magic = TDB_MAGIC; /* write out and point the top of the hash chain at it */ if (tdb_rec_write(tdb, rec_ptr, &rec) == -1 || tdb->methods->tdb_write(tdb, rec_ptr+sizeof(rec), p, key.dsize+dbuf.dsize)==-1 || tdb_ofs_write(tdb, TDB_HASH_TOP(hash), &rec_ptr) == -1) { /* Need to tdb_unallocate() here */ goto fail; } done: ret = 0; fail: if (ret == 0) { tdb_increment_seqnum(tdb); } SAFE_FREE(p); tdb_unlock(tdb, BUCKET(hash), F_WRLCK); return ret; } /* Append to an entry. Create if not exist. */ int tdb_append(struct tdb_context *tdb, TDB_DATA key, TDB_DATA new_dbuf) { u32 hash; TDB_DATA dbuf; int ret = -1; /* find which hash bucket it is in */ hash = tdb->hash_fn(&key); if (tdb_lock(tdb, BUCKET(hash), F_WRLCK) == -1) return -1; dbuf = tdb_fetch(tdb, key); if (dbuf.dptr == NULL) { dbuf.dptr = (unsigned char *)malloc(new_dbuf.dsize); } else { unsigned char *new_dptr = (unsigned char *)realloc(dbuf.dptr, dbuf.dsize + new_dbuf.dsize); if (new_dptr == NULL) { free(dbuf.dptr); } dbuf.dptr = new_dptr; } if (dbuf.dptr == NULL) { tdb->ecode = TDB_ERR_OOM; goto failed; } memcpy(dbuf.dptr + dbuf.dsize, new_dbuf.dptr, new_dbuf.dsize); dbuf.dsize += new_dbuf.dsize; ret = tdb_store(tdb, key, dbuf, 0); failed: tdb_unlock(tdb, BUCKET(hash), F_WRLCK); SAFE_FREE(dbuf.dptr); return ret; } /* return the name of the current tdb file useful for external logging functions */ const char *tdb_name(struct tdb_context *tdb) { return tdb->name; } /* return the underlying file descriptor being used by tdb, or -1 useful for external routines that want to check the device/inode of the fd */ int tdb_fd(struct tdb_context *tdb) { return tdb->fd; } /* return the current logging function useful for external tdb routines that wish to log tdb errors */ tdb_log_func tdb_log_fn(struct tdb_context *tdb) { return tdb->log.log_fn; } /* get the tdb sequence number. Only makes sense if the writers opened with TDB_SEQNUM set. Note that this sequence number will wrap quite quickly, so it should only be used for a 'has something changed' test, not for code that relies on the count of the number of changes made. If you want a counter then use a tdb record. The aim of this sequence number is to allow for a very lightweight test of a possible tdb change. */ int tdb_get_seqnum(struct tdb_context *tdb) { tdb_off_t seqnum=0; tdb_ofs_read(tdb, TDB_SEQNUM_OFS, &seqnum); return seqnum; } int tdb_hash_size(struct tdb_context *tdb) { return tdb->header.hash_size; } size_t tdb_map_size(struct tdb_context *tdb) { return tdb->map_size; } int tdb_get_flags(struct tdb_context *tdb) { return tdb->flags; } /* enable sequence number handling on an open tdb */ void tdb_enable_seqnum(struct tdb_context *tdb) { tdb->flags |= TDB_SEQNUM; } /* file: open.c */ /* all contexts, to ensure no double-opens (fcntl locks don't nest!) */ static struct tdb_context *tdbs = NULL; /* This is based on the hash algorithm from gdbm */ static unsigned int default_tdb_hash(TDB_DATA *key) { u32 value; /* Used to compute the hash value. */ u32 i; /* Used to cycle through random values. */ /* Set the initial value from the key size. */ for (value = 0x238F13AF * key->dsize, i=0; i < key->dsize; i++) value = (value + (key->dptr[i] << (i*5 % 24))); return (1103515243 * value + 12345); } /* initialise a new database with a specified hash size */ static int tdb_new_database(struct tdb_context *tdb, int hash_size) { struct tdb_header *newdb; int size, ret = -1; /* We make it up in memory, then write it out if not internal */ size = sizeof(struct tdb_header) + (hash_size+1)*sizeof(tdb_off_t); if (!(newdb = (struct tdb_header *)calloc(size, 1))) return TDB_ERRCODE(TDB_ERR_OOM, -1); /* Fill in the header */ newdb->version = TDB_VERSION; newdb->hash_size = hash_size; if (tdb->flags & TDB_INTERNAL) { tdb->map_size = size; tdb->map_ptr = (char *)newdb; memcpy(&tdb->header, newdb, sizeof(tdb->header)); /* Convert the `ondisk' version if asked. */ CONVERT(*newdb); return 0; } if (lseek(tdb->fd, 0, SEEK_SET) == -1) goto fail; if (ftruncate(tdb->fd, 0) == -1) goto fail; /* This creates an endian-converted header, as if read from disk */ CONVERT(*newdb); memcpy(&tdb->header, newdb, sizeof(tdb->header)); /* Don't endian-convert the magic food! */ memcpy(newdb->magic_food, TDB_MAGIC_FOOD, strlen(TDB_MAGIC_FOOD)+1); if (write(tdb->fd, newdb, size) != size) { ret = -1; } else { ret = 0; } fail: SAFE_FREE(newdb); return ret; } static int tdb_already_open(dev_t device, ino_t ino) { struct tdb_context *i; for (i = tdbs; i; i = i->next) { if (i->device == device && i->inode == ino) { return 1; } } return 0; } /* open the database, creating it if necessary The open_flags and mode are passed straight to the open call on the database file. A flags value of O_WRONLY is invalid. The hash size is advisory, use zero for a default value. Return is NULL on error, in which case errno is also set. Don't try to call tdb_error or tdb_errname, just do strerror(errno). @param name may be NULL for internal databases. */ struct tdb_context *tdb_open(const char *name, int hash_size, int tdb_flags, int open_flags, mode_t mode) { return tdb_open_ex(name, hash_size, tdb_flags, open_flags, mode, NULL, NULL); } /* a default logging function */ static void null_log_fn(struct tdb_context *tdb, enum tdb_debug_level level, const char *fmt, ...) PRINTF_ATTRIBUTE(3, 4); static void null_log_fn(struct tdb_context *tdb, enum tdb_debug_level level, const char *fmt, ...) { } struct tdb_context *tdb_open_ex(const char *name, int hash_size, int tdb_flags, int open_flags, mode_t mode, const struct tdb_logging_context *log_ctx, tdb_hash_func hash_fn) { struct tdb_context *tdb; struct stat st; int rev = 0, locked = 0; unsigned char *vp; u32 vertest; if (!(tdb = (struct tdb_context *)calloc(1, sizeof *tdb))) { /* Can't log this */ errno = ENOMEM; goto fail; } tdb_io_init(tdb); tdb->fd = -1; tdb->name = NULL; tdb->map_ptr = NULL; tdb->flags = tdb_flags; tdb->open_flags = open_flags; if (log_ctx) { tdb->log = *log_ctx; } else { tdb->log.log_fn = null_log_fn; tdb->log.log_private = NULL; } tdb->hash_fn = hash_fn ? hash_fn : default_tdb_hash; /* cache the page size */ tdb->page_size = getpagesize(); if (tdb->page_size <= 0) { tdb->page_size = 0x2000; } if ((open_flags & O_ACCMODE) == O_WRONLY) { TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: can't open tdb %s write-only\n", name)); errno = EINVAL; goto fail; } if (hash_size == 0) hash_size = DEFAULT_HASH_SIZE; if ((open_flags & O_ACCMODE) == O_RDONLY) { tdb->read_only = 1; /* read only databases don't do locking or clear if first */ tdb->flags |= TDB_NOLOCK; tdb->flags &= ~TDB_CLEAR_IF_FIRST; } /* internal databases don't mmap or lock, and start off cleared */ if (tdb->flags & TDB_INTERNAL) { tdb->flags |= (TDB_NOLOCK | TDB_NOMMAP); tdb->flags &= ~TDB_CLEAR_IF_FIRST; if (tdb_new_database(tdb, hash_size) != 0) { TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: tdb_new_database failed!")); goto fail; } goto internal; } if ((tdb->fd = open(name, open_flags, mode)) == -1) { TDB_LOG((tdb, TDB_DEBUG_WARNING, "tdb_open_ex: could not open file %s: %s\n", name, strerror(errno))); goto fail; /* errno set by open(2) */ } /* ensure there is only one process initialising at once */ if (tdb->methods->tdb_brlock(tdb, GLOBAL_LOCK, F_WRLCK, F_SETLKW, 0, 1) == -1) { TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: failed to get global lock on %s: %s\n", name, strerror(errno))); goto fail; /* errno set by tdb_brlock */ } /* we need to zero database if we are the only one with it open */ if ((tdb_flags & TDB_CLEAR_IF_FIRST) && (locked = (tdb->methods->tdb_brlock(tdb, ACTIVE_LOCK, F_WRLCK, F_SETLK, 0, 1) == 0))) { open_flags |= O_CREAT; if (ftruncate(tdb->fd, 0) == -1) { TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_open_ex: " "failed to truncate %s: %s\n", name, strerror(errno))); goto fail; /* errno set by ftruncate */ } } if (read(tdb->fd, &tdb->header, sizeof(tdb->header)) != sizeof(tdb->header) || strcmp(tdb->header.magic_food, TDB_MAGIC_FOOD) != 0 || (tdb->header.version != TDB_VERSION && !(rev = (tdb->header.version==TDB_BYTEREV(TDB_VERSION))))) { /* its not a valid database - possibly initialise it */ if (!(open_flags & O_CREAT) || tdb_new_database(tdb, hash_size) == -1) { errno = EIO; /* ie bad format or something */ goto fail; } rev = (tdb->flags & TDB_CONVERT); } vp = (unsigned char *)&tdb->header.version; vertest = (((u32)vp[0]) << 24) | (((u32)vp[1]) << 16) | (((u32)vp[2]) << 8) | (u32)vp[3]; tdb->flags |= (vertest==TDB_VERSION) ? TDB_BIGENDIAN : 0; if (!rev) tdb->flags &= ~TDB_CONVERT; else { tdb->flags |= TDB_CONVERT; tdb_convert(&tdb->header, sizeof(tdb->header)); } if (fstat(tdb->fd, &st) == -1) goto fail; if (tdb->header.rwlocks != 0) { TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: spinlocks no longer supported\n")); goto fail; } /* Is it already in the open list? If so, fail. */ if (tdb_already_open(st.st_dev, st.st_ino)) { TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: " "%s (%d,%d) is already open in this process\n", name, (int)st.st_dev, (int)st.st_ino)); errno = EBUSY; goto fail; } if (!(tdb->name = (char *)strdup(name))) { errno = ENOMEM; goto fail; } tdb->map_size = st.st_size; tdb->device = st.st_dev; tdb->inode = st.st_ino; tdb->max_dead_records = 0; tdb_mmap(tdb); if (locked) { if (tdb->methods->tdb_brlock(tdb, ACTIVE_LOCK, F_UNLCK, F_SETLK, 0, 1) == -1) { TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: " "failed to take ACTIVE_LOCK on %s: %s\n", name, strerror(errno))); goto fail; } } /* We always need to do this if the CLEAR_IF_FIRST flag is set, even if we didn't get the initial exclusive lock as we need to let all other users know we're using it. */ if (tdb_flags & TDB_CLEAR_IF_FIRST) { /* leave this lock in place to indicate it's in use */ if (tdb->methods->tdb_brlock(tdb, ACTIVE_LOCK, F_RDLCK, F_SETLKW, 0, 1) == -1) goto fail; } /* if needed, run recovery */ if (tdb_transaction_recover(tdb) == -1) { goto fail; } internal: /* Internal (memory-only) databases skip all the code above to * do with disk files, and resume here by releasing their * global lock and hooking into the active list. */ if (tdb->methods->tdb_brlock(tdb, GLOBAL_LOCK, F_UNLCK, F_SETLKW, 0, 1) == -1) goto fail; tdb->next = tdbs; tdbs = tdb; return tdb; fail: { int save_errno = errno; if (!tdb) return NULL; if (tdb->map_ptr) { if (tdb->flags & TDB_INTERNAL) SAFE_FREE(tdb->map_ptr); else tdb_munmap(tdb); } SAFE_FREE(tdb->name); if (tdb->fd != -1) if (close(tdb->fd) != 0) TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_open_ex: failed to close tdb->fd on error!\n")); SAFE_FREE(tdb); errno = save_errno; return NULL; } } /* * Set the maximum number of dead records per hash chain */ void tdb_set_max_dead(struct tdb_context *tdb, int max_dead) { tdb->max_dead_records = max_dead; } /** * Close a database. * * @returns -1 for error; 0 for success. **/ int tdb_close(struct tdb_context *tdb) { struct tdb_context **i; int ret = 0; if (tdb->transaction) { tdb_transaction_cancel(tdb); } if (tdb->map_ptr) { if (tdb->flags & TDB_INTERNAL) SAFE_FREE(tdb->map_ptr); else tdb_munmap(tdb); } SAFE_FREE(tdb->name); if (tdb->fd != -1) ret = close(tdb->fd); SAFE_FREE(tdb->lockrecs); /* Remove from contexts list */ for (i = &tdbs; *i; i = &(*i)->next) { if (*i == tdb) { *i = tdb->next; break; } } memset(tdb, 0, sizeof(*tdb)); SAFE_FREE(tdb); return ret; } /* register a loging function */ void tdb_set_logging_function(struct tdb_context *tdb, const struct tdb_logging_context *log_ctx) { tdb->log = *log_ctx; } void *tdb_get_logging_private(struct tdb_context *tdb) { return tdb->log.log_private; } /* reopen a tdb - this can be used after a fork to ensure that we have an independent seek pointer from our parent and to re-establish locks */ int tdb_reopen(struct tdb_context *tdb) { struct stat st; if (tdb->flags & TDB_INTERNAL) { return 0; /* Nothing to do. */ } if (tdb->num_locks != 0 || tdb->global_lock.count) { TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_reopen: reopen not allowed with locks held\n")); goto fail; } if (tdb->transaction != 0) { TDB_LOG((tdb, TDB_DEBUG_ERROR, "tdb_reopen: reopen not allowed inside a transaction\n")); goto fail; } if (tdb_munmap(tdb) != 0) { TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_reopen: munmap failed (%s)\n", strerror(errno))); goto fail; } if (close(tdb->fd) != 0) TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_reopen: WARNING closing tdb->fd failed!\n")); tdb->fd = open(tdb->name, tdb->open_flags & ~(O_CREAT|O_TRUNC), 0); if (tdb->fd == -1) { TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_reopen: open failed (%s)\n", strerror(errno))); goto fail; } if ((tdb->flags & TDB_CLEAR_IF_FIRST) && (tdb->methods->tdb_brlock(tdb, ACTIVE_LOCK, F_RDLCK, F_SETLKW, 0, 1) == -1)) { TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_reopen: failed to obtain active lock\n")); goto fail; } if (fstat(tdb->fd, &st) != 0) { TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_reopen: fstat failed (%s)\n", strerror(errno))); goto fail; } if (st.st_ino != tdb->inode || st.st_dev != tdb->device) { TDB_LOG((tdb, TDB_DEBUG_FATAL, "tdb_reopen: file dev/inode has changed!\n")); goto fail; } tdb_mmap(tdb); return 0; fail: tdb_close(tdb); return -1; } /* reopen all tdb's */ int tdb_reopen_all(int parent_longlived) { struct tdb_context *tdb; for (tdb=tdbs; tdb; tdb = tdb->next) { /* * If the parent is longlived (ie. a * parent daemon architecture), we know * it will keep it's active lock on a * tdb opened with CLEAR_IF_FIRST. Thus * for child processes we don't have to * add an active lock. This is essential * to improve performance on systems that * keep POSIX locks as a non-scalable data * structure in the kernel. */ if (parent_longlived) { /* Ensure no clear-if-first. */ tdb->flags &= ~TDB_CLEAR_IF_FIRST; } if (tdb_reopen(tdb) != 0) return -1; } return 0; } e2fsprogs-1.41.14/lib/ext2fs/alloc_sb.c0000644031104000116100000000431111504417000016173 0ustar tytsoeng/* * alloc_sb.c --- Allocate the superblock and block group descriptors for a * newly initialized filesystem. Used by mke2fs when initializing a filesystem * * Copyright (C) 1994, 1995, 1996, 2003 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #include #include #if HAVE_SYS_STAT_H #include #endif #if HAVE_SYS_TYPES_H #include #endif #include "ext2_fs.h" #include "ext2fs.h" /* * This function reserves the superblock and block group descriptors * for a given block group. It currently returns the number of free * blocks assuming that inode table and allocation bitmaps will be in * the group. This is not necessarily the case when the flex_bg * feature is enabled, so callers should take care! It was only * really intended for use by mke2fs, and even there it's not that * useful. In the future, when we redo this function for 64-bit block * numbers, we should probably return the number of blocks used by the * super block and group descriptors instead. * * See also the comment for ext2fs_super_and_bgd_loc() */ int ext2fs_reserve_super_and_bgd(ext2_filsys fs, dgrp_t group, ext2fs_block_bitmap bmap) { blk_t super_blk, old_desc_blk, new_desc_blk; int j, old_desc_blocks, num_blocks; num_blocks = ext2fs_super_and_bgd_loc(fs, group, &super_blk, &old_desc_blk, &new_desc_blk, 0); if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_META_BG) old_desc_blocks = fs->super->s_first_meta_bg; else old_desc_blocks = fs->desc_blocks + fs->super->s_reserved_gdt_blocks; if (super_blk || (group == 0)) ext2fs_mark_block_bitmap(bmap, super_blk); if (old_desc_blk) { if (fs->super->s_reserved_gdt_blocks && fs->block_map == bmap) fs->group_desc[group].bg_flags &= ~EXT2_BG_BLOCK_UNINIT; for (j=0; j < old_desc_blocks; j++) if (old_desc_blk + j < fs->super->s_blocks_count) ext2fs_mark_block_bitmap(bmap, old_desc_blk + j); } if (new_desc_blk) ext2fs_mark_block_bitmap(bmap, new_desc_blk); return num_blocks; } e2fsprogs-1.41.14/lib/ext2fs/irel_ma.c0000644031104000366760000002175511405316370015401 0ustar tytso/* * irel_ma.c * * Copyright (C) 1996, 1997 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #include #include #include #if HAVE_UNISTD_H #include #endif #if HAVE_ERRNO_H #include #endif #include "ext2_fs.h" #include "ext2fs.h" #include "irel.h" static errcode_t ima_put(ext2_irel irel, ext2_ino_t old, struct ext2_inode_relocate_entry *ent); static errcode_t ima_get(ext2_irel irel, ext2_ino_t old, struct ext2_inode_relocate_entry *ent); static errcode_t ima_get_by_orig(ext2_irel irel, ext2_ino_t orig, ext2_ino_t *old, struct ext2_inode_relocate_entry *ent); static errcode_t ima_start_iter(ext2_irel irel); static errcode_t ima_next(ext2_irel irel, ext2_ino_t *old, struct ext2_inode_relocate_entry *ent); static errcode_t ima_add_ref(ext2_irel irel, ext2_ino_t ino, struct ext2_inode_reference *ref); static errcode_t ima_start_iter_ref(ext2_irel irel, ext2_ino_t ino); static errcode_t ima_next_ref(ext2_irel irel, struct ext2_inode_reference *ref); static errcode_t ima_move(ext2_irel irel, ext2_ino_t old, ext2_ino_t new); static errcode_t ima_delete(ext2_irel irel, ext2_ino_t old); static errcode_t ima_free(ext2_irel irel); /* * This data structure stores the array of inode references; there is * a structure for each inode. */ struct inode_reference_entry { __u16 num; struct ext2_inode_reference *refs; }; struct irel_ma { __u32 magic; ext2_ino_t max_inode; ext2_ino_t ref_current; int ref_iter; ext2_ino_t *orig_map; struct ext2_inode_relocate_entry *entries; struct inode_reference_entry *ref_entries; }; errcode_t ext2fs_irel_memarray_create(char *name, ext2_ino_t max_inode, ext2_irel *new_irel) { ext2_irel irel = 0; errcode_t retval; struct irel_ma *ma = 0; size_t size; *new_irel = 0; /* * Allocate memory structures */ retval = ext2fs_get_mem(sizeof(struct ext2_inode_relocation_table), &irel); if (retval) goto errout; memset(irel, 0, sizeof(struct ext2_inode_relocation_table)); retval = ext2fs_get_mem(strlen(name)+1, &irel->name); if (retval) goto errout; strcpy(irel->name, name); retval = ext2fs_get_mem(sizeof(struct irel_ma), &ma); if (retval) goto errout; memset(ma, 0, sizeof(struct irel_ma)); irel->priv_data = ma; size = (size_t) (sizeof(ext2_ino_t) * (max_inode+1)); retval = ext2fs_get_array(max_inode+1, sizeof(ext2_ino_t), &ma->orig_map); if (retval) goto errout; memset(ma->orig_map, 0, size); size = (size_t) (sizeof(struct ext2_inode_relocate_entry) * (max_inode+1)); retval = ext2fs_get_array((max_inode+1, sizeof(struct ext2_inode_relocate_entry), &ma->entries); if (retval) goto errout; memset(ma->entries, 0, size); size = (size_t) (sizeof(struct inode_reference_entry) * (max_inode+1)); retval = ext2fs_get_mem(max_inode+1, sizeof(struct inode_reference_entry), &ma->ref_entries); if (retval) goto errout; memset(ma->ref_entries, 0, size); ma->max_inode = max_inode; /* * Fill in the irel data structure */ irel->put = ima_put; irel->get = ima_get; irel->get_by_orig = ima_get_by_orig; irel->start_iter = ima_start_iter; irel->next = ima_next; irel->add_ref = ima_add_ref; irel->start_iter_ref = ima_start_iter_ref; irel->next_ref = ima_next_ref; irel->move = ima_move; irel->delete = ima_delete; irel->free = ima_free; *new_irel = irel; return 0; errout: ima_free(irel); return retval; } static errcode_t ima_put(ext2_irel irel, ext2_ino_t old, struct ext2_inode_relocate_entry *ent) { struct inode_reference_entry *ref_ent; struct irel_ma *ma; errcode_t retval; size_t size, old_size; ma = irel->priv_data; if (old > ma->max_inode) return EXT2_ET_INVALID_ARGUMENT; /* * Force the orig field to the correct value; the application * program shouldn't be messing with this field. */ if (ma->entries[(unsigned) old].new == 0) ent->orig = old; else ent->orig = ma->entries[(unsigned) old].orig; /* * If max_refs has changed, reallocate the refs array */ ref_ent = ma->ref_entries + (unsigned) old; if (ref_ent->refs && ent->max_refs != ma->entries[(unsigned) old].max_refs) { size = (sizeof(struct ext2_inode_reference) * ent->max_refs); old_size = (sizeof(struct ext2_inode_reference) * ma->entries[(unsigned) old].max_refs); retval = ext2fs_resize_mem(old_size, size, &ref_ent->refs); if (retval) return retval; } ma->entries[(unsigned) old] = *ent; ma->orig_map[(unsigned) ent->orig] = old; return 0; } static errcode_t ima_get(ext2_irel irel, ext2_ino_t old, struct ext2_inode_relocate_entry *ent) { struct irel_ma *ma; ma = irel->priv_data; if (old > ma->max_inode) return EXT2_ET_INVALID_ARGUMENT; if (ma->entries[(unsigned) old].new == 0) return ENOENT; *ent = ma->entries[(unsigned) old]; return 0; } static errcode_t ima_get_by_orig(ext2_irel irel, ext2_ino_t orig, ext2_ino_t *old, struct ext2_inode_relocate_entry *ent) { struct irel_ma *ma; ext2_ino_t ino; ma = irel->priv_data; if (orig > ma->max_inode) return EXT2_ET_INVALID_ARGUMENT; ino = ma->orig_map[(unsigned) orig]; if (ino == 0) return ENOENT; *old = ino; *ent = ma->entries[(unsigned) ino]; return 0; } static errcode_t ima_start_iter(ext2_irel irel) { irel->current = 0; return 0; } static errcode_t ima_next(ext2_irel irel, ext2_ino_t *old, struct ext2_inode_relocate_entry *ent) { struct irel_ma *ma; ma = irel->priv_data; while (++irel->current < ma->max_inode) { if (ma->entries[(unsigned) irel->current].new == 0) continue; *old = irel->current; *ent = ma->entries[(unsigned) irel->current]; return 0; } *old = 0; return 0; } static errcode_t ima_add_ref(ext2_irel irel, ext2_ino_t ino, struct ext2_inode_reference *ref) { struct irel_ma *ma; size_t size; struct inode_reference_entry *ref_ent; struct ext2_inode_relocate_entry *ent; errcode_t retval; ma = irel->priv_data; if (ino > ma->max_inode) return EXT2_ET_INVALID_ARGUMENT; ref_ent = ma->ref_entries + (unsigned) ino; ent = ma->entries + (unsigned) ino; /* * If the inode reference array doesn't exist, create it. */ if (ref_ent->refs == 0) { size = (size_t) ((sizeof(struct ext2_inode_reference) * ent->max_refs)); retval = ext2fs_get_array(ent->max_refs, sizeof(struct ext2_inode_reference), &ref_ent->refs); if (retval) return retval; memset(ref_ent->refs, 0, size); ref_ent->num = 0; } if (ref_ent->num >= ent->max_refs) return EXT2_ET_TOO_MANY_REFS; ref_ent->refs[(unsigned) ref_ent->num++] = *ref; return 0; } static errcode_t ima_start_iter_ref(ext2_irel irel, ext2_ino_t ino) { struct irel_ma *ma; ma = irel->priv_data; if (ino > ma->max_inode) return EXT2_ET_INVALID_ARGUMENT; if (ma->entries[(unsigned) ino].new == 0) return ENOENT; ma->ref_current = ino; ma->ref_iter = 0; return 0; } static errcode_t ima_next_ref(ext2_irel irel, struct ext2_inode_reference *ref) { struct irel_ma *ma; struct inode_reference_entry *ref_ent; ma = irel->priv_data; ref_ent = ma->ref_entries + ma->ref_current; if ((ref_ent->refs == NULL) || (ma->ref_iter >= ref_ent->num)) { ref->block = 0; ref->offset = 0; return 0; } *ref = ref_ent->refs[ma->ref_iter++]; return 0; } static errcode_t ima_move(ext2_irel irel, ext2_ino_t old, ext2_ino_t new) { struct irel_ma *ma; ma = irel->priv_data; if ((old > ma->max_inode) || (new > ma->max_inode)) return EXT2_ET_INVALID_ARGUMENT; if (ma->entries[(unsigned) old].new == 0) return ENOENT; ma->entries[(unsigned) new] = ma->entries[(unsigned) old]; if (ma->ref_entries[(unsigned) new].refs) ext2fs_free_mem(&ma->ref_entries[(unsigned) new].refs); ma->ref_entries[(unsigned) new] = ma->ref_entries[(unsigned) old]; ma->entries[(unsigned) old].new = 0; ma->ref_entries[(unsigned) old].num = 0; ma->ref_entries[(unsigned) old].refs = 0; ma->orig_map[ma->entries[new].orig] = new; return 0; } static errcode_t ima_delete(ext2_irel irel, ext2_ino_t old) { struct irel_ma *ma; ma = irel->priv_data; if (old > ma->max_inode) return EXT2_ET_INVALID_ARGUMENT; if (ma->entries[(unsigned) old].new == 0) return ENOENT; ma->entries[old].new = 0; if (ma->ref_entries[(unsigned) old].refs) ext2fs_free_mem(&ma->ref_entries[(unsigned) old].refs); ma->orig_map[ma->entries[(unsigned) old].orig] = 0; ma->ref_entries[(unsigned) old].num = 0; ma->ref_entries[(unsigned) old].refs = 0; return 0; } static errcode_t ima_free(ext2_irel irel) { struct irel_ma *ma; ext2_ino_t ino; if (!irel) return 0; ma = irel->priv_data; if (ma) { if (ma->orig_map) ext2fs_free_mem(&ma->orig_map); if (ma->entries) ext2fs_free_mem(&ma->entries); if (ma->ref_entries) { for (ino = 0; ino <= ma->max_inode; ino++) { if (ma->ref_entries[(unsigned) ino].refs) ext2fs_free_mem(&ma->ref_entries[(unsigned) ino].refs); } ext2fs_free_mem(&ma->ref_entries); } ext2fs_free_mem(&ma); } if (irel->name) ext2fs_free_mem(&irel->name); ext2fs_free_mem(&irel); return 0; } e2fsprogs-1.41.14/lib/ext2fs/flushb.c0000644031104000366760000000370311405316370015245 0ustar tytso/* * flushb.c --- Hides system-dependent information for both syncing a * device to disk and to flush any buffers from disk cache. * * Copyright (C) 2000 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #include #if HAVE_ERRNO_H #include #endif #if HAVE_UNISTD_H #include #endif #if HAVE_SYS_IOCTL_H #include #endif #if HAVE_SYS_MOUNT_H #include #include /* This may define BLKFLSBUF */ #endif #include "ext2_fs.h" #include "ext2fs.h" /* * For Linux, define BLKFLSBUF and FDFLUSH if necessary, since * not all portable header file does so for us. This really should be * fixed in the glibc header files. (Recent glibcs appear to define * BLKFLSBUF in sys/mount.h, but FDFLUSH still doesn't seem to be * defined anywhere portable.) Until then.... */ #ifdef __linux__ #ifndef BLKFLSBUF #define BLKFLSBUF _IO(0x12,97) /* flush buffer cache */ #endif #ifndef FDFLUSH #define FDFLUSH _IO(2,0x4b) /* flush floppy disk */ #endif #endif /* * This function will sync a device/file, and optionally attempt to * flush the buffer cache. The latter is basically only useful for * system benchmarks and for torturing systems in burn-in tests. :) */ errcode_t ext2fs_sync_device(int fd, int flushb) { /* * We always sync the device in case we're running on old * kernels for which we can lose data if we don't. (There * still is a race condition for those kernels, but this * reduces it greatly.) */ if (fsync (fd) == -1) return errno; if (flushb) { #ifdef BLKFLSBUF if (ioctl (fd, BLKFLSBUF, 0) == 0) return 0; #else #ifdef __GNUC__ #warning BLKFLSBUF not defined #endif /* __GNUC__ */ #endif #ifdef FDFLUSH ioctl (fd, FDFLUSH, 0); /* In case this is a floppy */ #else #ifdef __GNUC__ #warning FDFLUSH not defined #endif /* __GNUC__ */ #endif } return 0; } e2fsprogs-1.41.14/lib/ext2fs/mkjournal.c0000644031104000116100000003277611504417000016437 0ustar tytsoeng/* * mkjournal.c --- make a journal for a filesystem * * Copyright (C) 2000 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #if HAVE_ERRNO_H #include #endif #include #include #if HAVE_SYS_STAT_H #include #endif #if HAVE_SYS_TYPES_H #include #endif #if HAVE_SYS_IOCTL_H #include #endif #if HAVE_NETINET_IN_H #include #endif #include "ext2_fs.h" #include "e2p/e2p.h" #include "ext2fs.h" #include "jfs_user.h" /* * This function automatically sets up the journal superblock and * returns it as an allocated block. */ errcode_t ext2fs_create_journal_superblock(ext2_filsys fs, __u32 size, int flags, char **ret_jsb) { errcode_t retval; journal_superblock_t *jsb; if (size < 1024) return EXT2_ET_JOURNAL_TOO_SMALL; if ((retval = ext2fs_get_mem(fs->blocksize, &jsb))) return retval; memset (jsb, 0, fs->blocksize); jsb->s_header.h_magic = htonl(JFS_MAGIC_NUMBER); if (flags & EXT2_MKJOURNAL_V1_SUPER) jsb->s_header.h_blocktype = htonl(JFS_SUPERBLOCK_V1); else jsb->s_header.h_blocktype = htonl(JFS_SUPERBLOCK_V2); jsb->s_blocksize = htonl(fs->blocksize); jsb->s_maxlen = htonl(size); jsb->s_nr_users = htonl(1); jsb->s_first = htonl(1); jsb->s_sequence = htonl(1); memcpy(jsb->s_uuid, fs->super->s_uuid, sizeof(fs->super->s_uuid)); /* * If we're creating an external journal device, we need to * adjust these fields. */ if (fs->super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) { jsb->s_nr_users = 0; if (fs->blocksize == 1024) jsb->s_first = htonl(3); else jsb->s_first = htonl(2); } *ret_jsb = (char *) jsb; return 0; } /* * This function writes a journal using POSIX routines. It is used * for creating external journals and creating journals on live * filesystems. */ static errcode_t write_journal_file(ext2_filsys fs, char *filename, blk_t size, int flags) { errcode_t retval; char *buf = 0; int fd, ret_size; blk_t i; if ((retval = ext2fs_create_journal_superblock(fs, size, flags, &buf))) return retval; /* Open the device or journal file */ if ((fd = open(filename, O_WRONLY)) < 0) { retval = errno; goto errout; } /* Write the superblock out */ retval = EXT2_ET_SHORT_WRITE; ret_size = write(fd, buf, fs->blocksize); if (ret_size < 0) { retval = errno; goto errout; } if (ret_size != (int) fs->blocksize) goto errout; memset(buf, 0, fs->blocksize); for (i = 1; i < size; i++) { ret_size = write(fd, buf, fs->blocksize); if (ret_size < 0) { retval = errno; goto errout; } if (ret_size != (int) fs->blocksize) goto errout; } close(fd); retval = 0; errout: ext2fs_free_mem(&buf); return retval; } /* * Convenience function which zeros out _num_ blocks starting at * _blk_. In case of an error, the details of the error is returned * via _ret_blk_ and _ret_count_ if they are non-NULL pointers. * Returns 0 on success, and an error code on an error. * * As a special case, if the first argument is NULL, then it will * attempt to free the static zeroizing buffer. (This is to keep * programs that check for memory leaks happy.) */ #define STRIDE_LENGTH 8 errcode_t ext2fs_zero_blocks(ext2_filsys fs, blk_t blk, int num, blk_t *ret_blk, int *ret_count) { int j, count; static char *buf; errcode_t retval; /* If fs is null, clean up the static buffer and return */ if (!fs) { if (buf) { free(buf); buf = 0; } return 0; } /* Allocate the zeroizing buffer if necessary */ if (!buf) { buf = malloc(fs->blocksize * STRIDE_LENGTH); if (!buf) return ENOMEM; memset(buf, 0, fs->blocksize * STRIDE_LENGTH); } /* OK, do the write loop */ j=0; while (j < num) { if (blk % STRIDE_LENGTH) { count = STRIDE_LENGTH - (blk % STRIDE_LENGTH); if (count > (num - j)) count = num - j; } else { count = num - j; if (count > STRIDE_LENGTH) count = STRIDE_LENGTH; } retval = io_channel_write_blk(fs->io, blk, count, buf); if (retval) { if (ret_count) *ret_count = count; if (ret_blk) *ret_blk = blk; return retval; } j += count; blk += count; } return 0; } /* * Helper function for creating the journal using direct I/O routines */ struct mkjournal_struct { int num_blocks; int newblocks; blk_t goal; blk_t blk_to_zero; int zero_count; char *buf; errcode_t err; }; static int mkjournal_proc(ext2_filsys fs, blk_t *blocknr, e2_blkcnt_t blockcnt, blk_t ref_block EXT2FS_ATTR((unused)), int ref_offset EXT2FS_ATTR((unused)), void *priv_data) { struct mkjournal_struct *es = (struct mkjournal_struct *) priv_data; blk_t new_blk; errcode_t retval; if (*blocknr) { es->goal = *blocknr; return 0; } retval = ext2fs_new_block(fs, es->goal, 0, &new_blk); if (retval) { es->err = retval; return BLOCK_ABORT; } if (blockcnt >= 0) es->num_blocks--; es->newblocks++; retval = 0; if (blockcnt <= 0) retval = io_channel_write_blk(fs->io, new_blk, 1, es->buf); else { if (es->zero_count) { if ((es->blk_to_zero + es->zero_count == new_blk) && (es->zero_count < 1024)) es->zero_count++; else { retval = ext2fs_zero_blocks(fs, es->blk_to_zero, es->zero_count, 0, 0); es->zero_count = 0; } } if (es->zero_count == 0) { es->blk_to_zero = new_blk; es->zero_count = 1; } } if (blockcnt == 0) memset(es->buf, 0, fs->blocksize); if (retval) { es->err = retval; return BLOCK_ABORT; } *blocknr = es->goal = new_blk; ext2fs_block_alloc_stats(fs, new_blk, +1); if (es->num_blocks == 0) return (BLOCK_CHANGED | BLOCK_ABORT); else return BLOCK_CHANGED; } /* * This function creates a journal using direct I/O routines. */ static errcode_t write_journal_inode(ext2_filsys fs, ext2_ino_t journal_ino, blk_t size, int flags) { char *buf; dgrp_t group, start, end, i, log_flex; errcode_t retval; struct ext2_inode inode; struct mkjournal_struct es; if ((retval = ext2fs_create_journal_superblock(fs, size, flags, &buf))) return retval; if ((retval = ext2fs_read_bitmaps(fs))) return retval; if ((retval = ext2fs_read_inode(fs, journal_ino, &inode))) return retval; if (inode.i_blocks > 0) return EEXIST; es.num_blocks = size; es.newblocks = 0; es.buf = buf; es.err = 0; es.zero_count = 0; if (fs->super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_EXTENTS) { inode.i_flags |= EXT4_EXTENTS_FL; if ((retval = ext2fs_write_inode(fs, journal_ino, &inode))) return retval; } /* * Set the initial goal block to be roughly at the middle of * the filesystem. Pick a group that has the largest number * of free blocks. */ group = ext2fs_group_of_blk(fs, (fs->super->s_blocks_count - fs->super->s_first_data_block) / 2); log_flex = 1 << fs->super->s_log_groups_per_flex; if (fs->super->s_log_groups_per_flex && (group > log_flex)) { group = group & ~(log_flex - 1); while ((group < fs->group_desc_count) && fs->group_desc[group].bg_free_blocks_count == 0) group++; if (group == fs->group_desc_count) group = 0; start = group; } else start = (group > 0) ? group-1 : group; end = ((group+1) < fs->group_desc_count) ? group+1 : group; group = start; for (i=start+1; i <= end; i++) if (fs->group_desc[i].bg_free_blocks_count > fs->group_desc[group].bg_free_blocks_count) group = i; es.goal = (fs->super->s_blocks_per_group * group) + fs->super->s_first_data_block; retval = ext2fs_block_iterate2(fs, journal_ino, BLOCK_FLAG_APPEND, 0, mkjournal_proc, &es); if (es.err) { retval = es.err; goto errout; } if (es.zero_count) { retval = ext2fs_zero_blocks(fs, es.blk_to_zero, es.zero_count, 0, 0); if (retval) goto errout; } if ((retval = ext2fs_read_inode(fs, journal_ino, &inode))) goto errout; inode.i_size += fs->blocksize * size; ext2fs_iblk_add_blocks(fs, &inode, es.newblocks); inode.i_mtime = inode.i_ctime = fs->now ? fs->now : time(0); inode.i_links_count = 1; inode.i_mode = LINUX_S_IFREG | 0600; if ((retval = ext2fs_write_new_inode(fs, journal_ino, &inode))) goto errout; retval = 0; memcpy(fs->super->s_jnl_blocks, inode.i_block, EXT2_N_BLOCKS*4); fs->super->s_jnl_blocks[16] = inode.i_size; fs->super->s_jnl_backup_type = EXT3_JNL_BACKUP_BLOCKS; ext2fs_mark_super_dirty(fs); errout: ext2fs_free_mem(&buf); return retval; } /* * Find a reasonable journal file size (in blocks) given the number of blocks * in the filesystem. For very small filesystems, it is not reasonable to * have a journal that fills more than half of the filesystem. */ int ext2fs_default_journal_size(__u64 blocks) { if (blocks < 2048) return -1; if (blocks < 32768) return (1024); if (blocks < 256*1024) return (4096); if (blocks < 512*1024) return (8192); if (blocks < 1024*1024) return (16384); return 32768; } /* * This function adds a journal device to a filesystem */ errcode_t ext2fs_add_journal_device(ext2_filsys fs, ext2_filsys journal_dev) { struct stat st; errcode_t retval; char buf[1024]; journal_superblock_t *jsb; int start; __u32 i, nr_users; /* Make sure the device exists and is a block device */ if (stat(journal_dev->device_name, &st) < 0) return errno; if (!S_ISBLK(st.st_mode)) return EXT2_ET_JOURNAL_NOT_BLOCK; /* Must be a block device */ /* Get the journal superblock */ start = 1; if (journal_dev->blocksize == 1024) start++; if ((retval = io_channel_read_blk(journal_dev->io, start, -1024, buf))) return retval; jsb = (journal_superblock_t *) buf; if ((jsb->s_header.h_magic != (unsigned) ntohl(JFS_MAGIC_NUMBER)) || (jsb->s_header.h_blocktype != (unsigned) ntohl(JFS_SUPERBLOCK_V2))) return EXT2_ET_NO_JOURNAL_SB; if (ntohl(jsb->s_blocksize) != (unsigned long) fs->blocksize) return EXT2_ET_UNEXPECTED_BLOCK_SIZE; /* Check and see if this filesystem has already been added */ nr_users = ntohl(jsb->s_nr_users); for (i=0; i < nr_users; i++) { if (memcmp(fs->super->s_uuid, &jsb->s_users[i*16], 16) == 0) break; } if (i >= nr_users) { memcpy(&jsb->s_users[nr_users*16], fs->super->s_uuid, 16); jsb->s_nr_users = htonl(nr_users+1); } /* Writeback the journal superblock */ if ((retval = io_channel_write_blk(journal_dev->io, start, -1024, buf))) return retval; fs->super->s_journal_inum = 0; fs->super->s_journal_dev = st.st_rdev; memcpy(fs->super->s_journal_uuid, jsb->s_uuid, sizeof(fs->super->s_journal_uuid)); fs->super->s_feature_compat |= EXT3_FEATURE_COMPAT_HAS_JOURNAL; ext2fs_mark_super_dirty(fs); return 0; } /* * This function adds a journal inode to a filesystem, using either * POSIX routines if the filesystem is mounted, or using direct I/O * functions if it is not. */ errcode_t ext2fs_add_journal_inode(ext2_filsys fs, blk_t size, int flags) { errcode_t retval; ext2_ino_t journal_ino; struct stat st; char jfile[1024]; int mount_flags, f; int fd = -1; if ((retval = ext2fs_check_mount_point(fs->device_name, &mount_flags, jfile, sizeof(jfile)-10))) return retval; if (mount_flags & EXT2_MF_MOUNTED) { strcat(jfile, "/.journal"); /* * If .../.journal already exists, make sure any * immutable or append-only flags are cleared. */ #if defined(HAVE_CHFLAGS) && defined(UF_NODUMP) (void) chflags (jfile, 0); #else #if HAVE_EXT2_IOCTLS fd = open(jfile, O_RDONLY); if (fd >= 0) { f = 0; ioctl(fd, EXT2_IOC_SETFLAGS, &f); close(fd); } #endif #endif /* Create the journal file */ if ((fd = open(jfile, O_CREAT|O_WRONLY, 0600)) < 0) return errno; if ((retval = write_journal_file(fs, jfile, size, flags))) goto errout; /* Get inode number of the journal file */ if (fstat(fd, &st) < 0) { retval = errno; goto errout; } #if defined(HAVE_CHFLAGS) && defined(UF_NODUMP) retval = fchflags (fd, UF_NODUMP|UF_IMMUTABLE); #else #if HAVE_EXT2_IOCTLS if (ioctl(fd, EXT2_IOC_GETFLAGS, &f) < 0) { retval = errno; goto errout; } f |= EXT2_NODUMP_FL | EXT2_IMMUTABLE_FL; retval = ioctl(fd, EXT2_IOC_SETFLAGS, &f); #endif #endif if (retval) { retval = errno; goto errout; } if (close(fd) < 0) { retval = errno; fd = -1; goto errout; } journal_ino = st.st_ino; } else { if ((mount_flags & EXT2_MF_BUSY) && !(fs->flags & EXT2_FLAG_EXCLUSIVE)) { retval = EBUSY; goto errout; } journal_ino = EXT2_JOURNAL_INO; if ((retval = write_journal_inode(fs, journal_ino, size, flags))) return retval; } fs->super->s_journal_inum = journal_ino; fs->super->s_journal_dev = 0; memset(fs->super->s_journal_uuid, 0, sizeof(fs->super->s_journal_uuid)); fs->super->s_feature_compat |= EXT3_FEATURE_COMPAT_HAS_JOURNAL; ext2fs_mark_super_dirty(fs); return 0; errout: if (fd > 0) close(fd); return retval; } #ifdef DEBUG main(int argc, char **argv) { errcode_t retval; char *device_name; ext2_filsys fs; if (argc < 2) { fprintf(stderr, "Usage: %s filesystem\n", argv[0]); exit(1); } device_name = argv[1]; retval = ext2fs_open (device_name, EXT2_FLAG_RW, 0, 0, unix_io_manager, &fs); if (retval) { com_err(argv[0], retval, "while opening %s", device_name); exit(1); } retval = ext2fs_add_journal_inode(fs, 1024); if (retval) { com_err(argv[0], retval, "while adding journal to %s", device_name); exit(1); } retval = ext2fs_flush(fs); if (retval) { printf("Warning, had trouble writing out superblocks.\n"); } ext2fs_close(fs); exit(0); } #endif e2fsprogs-1.41.14/lib/ext2fs/native.c0000644031104000366760000000065511405316370015253 0ustar tytso/* * native.c --- returns the ext2_flag for a native byte order * * Copyright (C) 1996 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #include #include "ext2_fs.h" #include "ext2fs.h" int ext2fs_native_flag(void) { #ifdef WORDS_BIGENDIAN return EXT2_FLAG_SWAP_BYTES; #else return 0; #endif } e2fsprogs-1.41.14/lib/ext2fs/crc16.c0000644031104000366760000000604611374366146014716 0ustar tytso/* * crc16.c * * This source code is licensed under the GNU General Public License, * Version 2. See the file COPYING for more details. */ #if HAVE_SYS_TYPES_H #include #endif #include #include "crc16.h" /** CRC table for the CRC-16. The poly is 0x8005 (x16 + x15 + x2 + 1) */ static __u16 const crc16_table[256] = { 0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241, 0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440, 0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40, 0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841, 0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40, 0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41, 0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641, 0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040, 0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240, 0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441, 0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41, 0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840, 0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41, 0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40, 0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640, 0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041, 0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240, 0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441, 0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41, 0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840, 0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41, 0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40, 0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640, 0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041, 0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241, 0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440, 0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40, 0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841, 0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40, 0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41, 0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641, 0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040 }; /** * Compute the CRC-16 for the data buffer * * @param crc previous CRC value * @param buffer data pointer * @param len number of bytes in the buffer * @return the updated CRC value */ crc16_t ext2fs_crc16(crc16_t crc, const void *buffer, unsigned int len) { const unsigned char *cp = buffer; while (len--) /* * for an unknown reason, PPC treats __u16 as signed * and keeps doing sign extension on the value. * Instead, use only the low 16 bits of an unsigned * int for holding the CRC value to avoid this. */ crc = (((crc >> 8) & 0xffU) ^ crc16_table[(crc ^ *cp++) & 0xffU]) & 0x0000ffffU; return crc; } e2fsprogs-1.41.14/lib/ext2fs/bitops.h0000644031104000116100000003024411504417000015726 0ustar tytsoeng/* * bitops.h --- Bitmap frobbing code. The byte swapping routines are * also included here. * * Copyright (C) 1993, 1994, 1995, 1996 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ extern int ext2fs_set_bit(unsigned int nr,void * addr); extern int ext2fs_clear_bit(unsigned int nr, void * addr); extern int ext2fs_test_bit(unsigned int nr, const void * addr); extern void ext2fs_fast_set_bit(unsigned int nr,void * addr); extern void ext2fs_fast_clear_bit(unsigned int nr, void * addr); extern __u16 ext2fs_swab16(__u16 val); extern __u32 ext2fs_swab32(__u32 val); extern __u64 ext2fs_swab64(__u64 val); #ifdef WORDS_BIGENDIAN #define ext2fs_cpu_to_le64(x) ext2fs_swab64((x)) #define ext2fs_le64_to_cpu(x) ext2fs_swab64((x)) #define ext2fs_cpu_to_le32(x) ext2fs_swab32((x)) #define ext2fs_le32_to_cpu(x) ext2fs_swab32((x)) #define ext2fs_cpu_to_le16(x) ext2fs_swab16((x)) #define ext2fs_le16_to_cpu(x) ext2fs_swab16((x)) #define ext2fs_cpu_to_be32(x) ((__u32)(x)) #define ext2fs_be32_to_cpu(x) ((__u32)(x)) #define ext2fs_cpu_to_be16(x) ((__u16)(x)) #define ext2fs_be16_to_cpu(x) ((__u16)(x)) #else #define ext2fs_cpu_to_le64(x) ((__u64)(x)) #define ext2fs_le64_to_cpu(x) ((__u64)(x)) #define ext2fs_cpu_to_le32(x) ((__u32)(x)) #define ext2fs_le32_to_cpu(x) ((__u32)(x)) #define ext2fs_cpu_to_le16(x) ((__u16)(x)) #define ext2fs_le16_to_cpu(x) ((__u16)(x)) #define ext2fs_cpu_to_be32(x) ext2fs_swab32((x)) #define ext2fs_be32_to_cpu(x) ext2fs_swab32((x)) #define ext2fs_cpu_to_be16(x) ext2fs_swab16((x)) #define ext2fs_be16_to_cpu(x) ext2fs_swab16((x)) #endif /* * EXT2FS bitmap manipulation routines. */ /* Support for sending warning messages from the inline subroutines */ extern const char *ext2fs_block_string; extern const char *ext2fs_inode_string; extern const char *ext2fs_mark_string; extern const char *ext2fs_unmark_string; extern const char *ext2fs_test_string; extern void ext2fs_warn_bitmap(errcode_t errcode, unsigned long arg, const char *description); extern void ext2fs_warn_bitmap2(ext2fs_generic_bitmap bitmap, int code, unsigned long arg); extern int ext2fs_mark_block_bitmap(ext2fs_block_bitmap bitmap, blk_t block); extern int ext2fs_unmark_block_bitmap(ext2fs_block_bitmap bitmap, blk_t block); extern int ext2fs_test_block_bitmap(ext2fs_block_bitmap bitmap, blk_t block); extern int ext2fs_mark_inode_bitmap(ext2fs_inode_bitmap bitmap, ext2_ino_t inode); extern int ext2fs_unmark_inode_bitmap(ext2fs_inode_bitmap bitmap, ext2_ino_t inode); extern int ext2fs_test_inode_bitmap(ext2fs_inode_bitmap bitmap, ext2_ino_t inode); extern void ext2fs_fast_mark_block_bitmap(ext2fs_block_bitmap bitmap, blk_t block); extern void ext2fs_fast_unmark_block_bitmap(ext2fs_block_bitmap bitmap, blk_t block); extern int ext2fs_fast_test_block_bitmap(ext2fs_block_bitmap bitmap, blk_t block); extern void ext2fs_fast_mark_inode_bitmap(ext2fs_inode_bitmap bitmap, ext2_ino_t inode); extern void ext2fs_fast_unmark_inode_bitmap(ext2fs_inode_bitmap bitmap, ext2_ino_t inode); extern int ext2fs_fast_test_inode_bitmap(ext2fs_inode_bitmap bitmap, ext2_ino_t inode); extern blk_t ext2fs_get_block_bitmap_start(ext2fs_block_bitmap bitmap); extern ext2_ino_t ext2fs_get_inode_bitmap_start(ext2fs_inode_bitmap bitmap); extern blk_t ext2fs_get_block_bitmap_end(ext2fs_block_bitmap bitmap); extern ext2_ino_t ext2fs_get_inode_bitmap_end(ext2fs_inode_bitmap bitmap); extern void ext2fs_mark_block_bitmap_range(ext2fs_block_bitmap bitmap, blk_t block, int num); extern void ext2fs_unmark_block_bitmap_range(ext2fs_block_bitmap bitmap, blk_t block, int num); extern int ext2fs_test_block_bitmap_range(ext2fs_block_bitmap bitmap, blk_t block, int num); extern int ext2fs_test_inode_bitmap_range(ext2fs_inode_bitmap bitmap, ino_t inode, int num); extern void ext2fs_fast_mark_block_bitmap_range(ext2fs_block_bitmap bitmap, blk_t block, int num); extern void ext2fs_fast_unmark_block_bitmap_range(ext2fs_block_bitmap bitmap, blk_t block, int num); extern int ext2fs_fast_test_block_bitmap_range(ext2fs_block_bitmap bitmap, blk_t block, int num); extern void ext2fs_set_bitmap_padding(ext2fs_generic_bitmap map); /* These routines moved to gen_bitmap.c */ extern int ext2fs_mark_generic_bitmap(ext2fs_generic_bitmap bitmap, __u32 bitno); extern int ext2fs_unmark_generic_bitmap(ext2fs_generic_bitmap bitmap, blk_t bitno); extern int ext2fs_test_generic_bitmap(ext2fs_generic_bitmap bitmap, blk_t bitno); extern int ext2fs_test_block_bitmap_range(ext2fs_block_bitmap bitmap, blk_t block, int num); extern __u32 ext2fs_get_generic_bitmap_start(ext2fs_generic_bitmap bitmap); extern __u32 ext2fs_get_generic_bitmap_end(ext2fs_generic_bitmap bitmap); /* * The inline routines themselves... * * If NO_INLINE_FUNCS is defined, then we won't try to do inline * functions at all; they will be included as normal functions in * inline.c */ #ifdef NO_INLINE_FUNCS #if (defined(__GNUC__) && (defined(__i386__) || defined(__i486__) || \ defined(__i586__) || defined(__mc68000__))) /* This prevents bitops.c from trying to include the C */ /* function version of these functions */ #define _EXT2_HAVE_ASM_BITOPS_ #endif #endif /* NO_INLINE_FUNCS */ #if (defined(INCLUDE_INLINE_FUNCS) || !defined(NO_INLINE_FUNCS)) #ifdef INCLUDE_INLINE_FUNCS #define _INLINE_ extern #else #ifdef __GNUC__ #define _INLINE_ extern __inline__ #else /* For Watcom C */ #define _INLINE_ extern inline #endif #endif /* * Fast bit set/clear functions that doesn't need to return the * previous bit value. */ _INLINE_ void ext2fs_fast_set_bit(unsigned int nr,void * addr) { unsigned char *ADDR = (unsigned char *) addr; ADDR += nr >> 3; *ADDR |= (1 << (nr & 0x07)); } _INLINE_ void ext2fs_fast_clear_bit(unsigned int nr, void * addr) { unsigned char *ADDR = (unsigned char *) addr; ADDR += nr >> 3; *ADDR &= ~(1 << (nr & 0x07)); } #if ((defined __GNUC__) && !defined(_EXT2_USE_C_VERSIONS_) && \ (defined(__i386__) || defined(__i486__) || defined(__i586__))) #define _EXT2_HAVE_ASM_BITOPS_ #define _EXT2_HAVE_ASM_SWAB_ /* * These are done by inline assembly for speed reasons..... * * All bitoperations return 0 if the bit was cleared before the * operation and != 0 if it was not. Bit 0 is the LSB of addr; bit 32 * is the LSB of (addr+1). */ /* * Some hacks to defeat gcc over-optimizations.. */ struct __dummy_h { unsigned long a[100]; }; #define EXT2FS_ADDR (*(struct __dummy_h *) addr) #define EXT2FS_CONST_ADDR (*(const struct __dummy_h *) addr) _INLINE_ int ext2fs_set_bit(unsigned int nr, void * addr) { int oldbit; addr = (void *) (((unsigned char *) addr) + (nr >> 3)); __asm__ __volatile__("btsl %2,%1\n\tsbbl %0,%0" :"=r" (oldbit),"+m" (EXT2FS_ADDR) :"r" (nr & 7)); return oldbit; } _INLINE_ int ext2fs_clear_bit(unsigned int nr, void * addr) { int oldbit; addr = (void *) (((unsigned char *) addr) + (nr >> 3)); __asm__ __volatile__("btrl %2,%1\n\tsbbl %0,%0" :"=r" (oldbit),"+m" (EXT2FS_ADDR) :"r" (nr & 7)); return oldbit; } _INLINE_ int ext2fs_test_bit(unsigned int nr, const void * addr) { int oldbit; addr = (const void *) (((const unsigned char *) addr) + (nr >> 3)); __asm__ __volatile__("btl %2,%1\n\tsbbl %0,%0" :"=r" (oldbit) :"m" (EXT2FS_CONST_ADDR),"r" (nr & 7)); return oldbit; } _INLINE_ __u32 ext2fs_swab32(__u32 val) { #ifdef EXT2FS_REQUIRE_486 __asm__("bswap %0" : "=r" (val) : "0" (val)); #else __asm__("xchgb %b0,%h0\n\t" /* swap lower bytes */ "rorl $16,%0\n\t" /* swap words */ "xchgb %b0,%h0" /* swap higher bytes */ :"=q" (val) : "0" (val)); #endif return val; } _INLINE_ __u16 ext2fs_swab16(__u16 val) { __asm__("xchgb %b0,%h0" /* swap bytes */ \ : "=q" (val) \ : "0" (val)); \ return val; } #undef EXT2FS_ADDR #endif /* i386 */ #if ((defined __GNUC__) && !defined(_EXT2_USE_C_VERSIONS_) && \ (defined(__mc68000__))) #define _EXT2_HAVE_ASM_BITOPS_ _INLINE_ int ext2fs_set_bit(unsigned int nr,void * addr) { char retval; __asm__ __volatile__ ("bfset %2@{%1:#1}; sne %0" : "=d" (retval) : "d" (nr^7), "a" (addr)); return retval; } _INLINE_ int ext2fs_clear_bit(unsigned int nr, void * addr) { char retval; __asm__ __volatile__ ("bfclr %2@{%1:#1}; sne %0" : "=d" (retval) : "d" (nr^7), "a" (addr)); return retval; } _INLINE_ int ext2fs_test_bit(unsigned int nr, const void * addr) { char retval; __asm__ __volatile__ ("bftst %2@{%1:#1}; sne %0" : "=d" (retval) : "d" (nr^7), "a" (addr)); return retval; } #endif /* __mc68000__ */ #if !defined(_EXT2_HAVE_ASM_SWAB_) _INLINE_ __u16 ext2fs_swab16(__u16 val) { return (val >> 8) | (val << 8); } _INLINE_ __u32 ext2fs_swab32(__u32 val) { return ((val>>24) | ((val>>8)&0xFF00) | ((val<<8)&0xFF0000) | (val<<24)); } #endif /* !_EXT2_HAVE_ASM_SWAB */ _INLINE_ __u64 ext2fs_swab64(__u64 val) { return (ext2fs_swab32(val >> 32) | (((__u64)ext2fs_swab32(val & 0xFFFFFFFFUL)) << 32)); } _INLINE_ int ext2fs_mark_block_bitmap(ext2fs_block_bitmap bitmap, blk_t block) { return ext2fs_mark_generic_bitmap((ext2fs_generic_bitmap) bitmap, block); } _INLINE_ int ext2fs_unmark_block_bitmap(ext2fs_block_bitmap bitmap, blk_t block) { return ext2fs_unmark_generic_bitmap((ext2fs_generic_bitmap) bitmap, block); } _INLINE_ int ext2fs_test_block_bitmap(ext2fs_block_bitmap bitmap, blk_t block) { return ext2fs_test_generic_bitmap((ext2fs_generic_bitmap) bitmap, block); } _INLINE_ int ext2fs_mark_inode_bitmap(ext2fs_inode_bitmap bitmap, ext2_ino_t inode) { return ext2fs_mark_generic_bitmap((ext2fs_generic_bitmap) bitmap, inode); } _INLINE_ int ext2fs_unmark_inode_bitmap(ext2fs_inode_bitmap bitmap, ext2_ino_t inode) { return ext2fs_unmark_generic_bitmap((ext2fs_generic_bitmap) bitmap, inode); } _INLINE_ int ext2fs_test_inode_bitmap(ext2fs_inode_bitmap bitmap, ext2_ino_t inode) { return ext2fs_test_generic_bitmap((ext2fs_generic_bitmap) bitmap, inode); } _INLINE_ void ext2fs_fast_mark_block_bitmap(ext2fs_block_bitmap bitmap, blk_t block) { ext2fs_mark_generic_bitmap((ext2fs_generic_bitmap) bitmap, block); } _INLINE_ void ext2fs_fast_unmark_block_bitmap(ext2fs_block_bitmap bitmap, blk_t block) { ext2fs_unmark_generic_bitmap((ext2fs_generic_bitmap) bitmap, block); } _INLINE_ int ext2fs_fast_test_block_bitmap(ext2fs_block_bitmap bitmap, blk_t block) { return ext2fs_test_generic_bitmap((ext2fs_generic_bitmap) bitmap, block); } _INLINE_ void ext2fs_fast_mark_inode_bitmap(ext2fs_inode_bitmap bitmap, ext2_ino_t inode) { ext2fs_mark_generic_bitmap((ext2fs_generic_bitmap) bitmap, inode); } _INLINE_ void ext2fs_fast_unmark_inode_bitmap(ext2fs_inode_bitmap bitmap, ext2_ino_t inode) { ext2fs_unmark_generic_bitmap((ext2fs_generic_bitmap) bitmap, inode); } _INLINE_ int ext2fs_fast_test_inode_bitmap(ext2fs_inode_bitmap bitmap, ext2_ino_t inode) { return ext2fs_test_generic_bitmap((ext2fs_generic_bitmap) bitmap, inode); } _INLINE_ blk_t ext2fs_get_block_bitmap_start(ext2fs_block_bitmap bitmap) { return ext2fs_get_generic_bitmap_start((ext2fs_generic_bitmap) bitmap); } _INLINE_ ext2_ino_t ext2fs_get_inode_bitmap_start(ext2fs_inode_bitmap bitmap) { return ext2fs_get_generic_bitmap_start((ext2fs_generic_bitmap) bitmap); } _INLINE_ blk_t ext2fs_get_block_bitmap_end(ext2fs_block_bitmap bitmap) { return ext2fs_get_generic_bitmap_end((ext2fs_generic_bitmap) bitmap); } _INLINE_ ext2_ino_t ext2fs_get_inode_bitmap_end(ext2fs_inode_bitmap bitmap) { return ext2fs_get_generic_bitmap_end((ext2fs_generic_bitmap) bitmap); } _INLINE_ int ext2fs_fast_test_block_bitmap_range(ext2fs_block_bitmap bitmap, blk_t block, int num) { return ext2fs_test_block_bitmap_range(bitmap, block, num); } _INLINE_ void ext2fs_fast_mark_block_bitmap_range(ext2fs_block_bitmap bitmap, blk_t block, int num) { ext2fs_mark_block_bitmap_range(bitmap, block, num); } _INLINE_ void ext2fs_fast_unmark_block_bitmap_range(ext2fs_block_bitmap bitmap, blk_t block, int num) { ext2fs_unmark_block_bitmap_range(bitmap, block, num); } #undef _INLINE_ #endif e2fsprogs-1.41.14/lib/ext2fs/dupfs.c0000644031104000366760000000404311405316370015101 0ustar tytso/* * dupfs.c --- duplicate a ext2 filesystem handle * * Copyright (C) 1997, 1998, 2001, 2003, 2005 by Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #include #if HAVE_UNISTD_H #include #endif #include #include #include "ext2_fs.h" #include "ext2fsP.h" errcode_t ext2fs_dup_handle(ext2_filsys src, ext2_filsys *dest) { ext2_filsys fs; errcode_t retval; EXT2_CHECK_MAGIC(src, EXT2_ET_MAGIC_EXT2FS_FILSYS); retval = ext2fs_get_mem(sizeof(struct struct_ext2_filsys), &fs); if (retval) return retval; *fs = *src; fs->device_name = 0; fs->super = 0; fs->orig_super = 0; fs->group_desc = 0; fs->inode_map = 0; fs->block_map = 0; fs->badblocks = 0; fs->dblist = 0; io_channel_bumpcount(fs->io); if (fs->icache) fs->icache->refcount++; retval = ext2fs_get_mem(strlen(src->device_name)+1, &fs->device_name); if (retval) goto errout; strcpy(fs->device_name, src->device_name); retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &fs->super); if (retval) goto errout; memcpy(fs->super, src->super, SUPERBLOCK_SIZE); retval = ext2fs_get_mem(SUPERBLOCK_SIZE, &fs->orig_super); if (retval) goto errout; memcpy(fs->orig_super, src->orig_super, SUPERBLOCK_SIZE); retval = ext2fs_get_array(fs->desc_blocks, fs->blocksize, &fs->group_desc); if (retval) goto errout; memcpy(fs->group_desc, src->group_desc, (size_t) fs->desc_blocks * fs->blocksize); if (src->inode_map) { retval = ext2fs_copy_bitmap(src->inode_map, &fs->inode_map); if (retval) goto errout; } if (src->block_map) { retval = ext2fs_copy_bitmap(src->block_map, &fs->block_map); if (retval) goto errout; } if (src->badblocks) { retval = ext2fs_badblocks_copy(src->badblocks, &fs->badblocks); if (retval) goto errout; } if (src->dblist) { retval = ext2fs_copy_dblist(src->dblist, &fs->dblist); if (retval) goto errout; } *dest = fs; return 0; errout: ext2fs_free(fs); return retval; } e2fsprogs-1.41.14/lib/ext2fs/ext2fsP.h0000644031104000116100000000270011504417000015755 0ustar tytsoeng/* * ext2fsP.h --- private header file for ext2 library * * Copyright (C) 1997 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #include "ext2fs.h" /* * Badblocks list */ struct ext2_struct_u32_list { int magic; int num; int size; __u32 *list; int badblocks_flags; }; struct ext2_struct_u32_iterate { int magic; ext2_u32_list bb; int ptr; }; /* * Directory block iterator definition */ struct ext2_struct_dblist { int magic; ext2_filsys fs; ext2_ino_t size; ext2_ino_t count; int sorted; struct ext2_db_entry * list; }; /* * For directory iterators */ struct dir_context { ext2_ino_t dir; int flags; char *buf; int (*func)(ext2_ino_t dir, int entry, struct ext2_dir_entry *dirent, int offset, int blocksize, char *buf, void *priv_data); void *priv_data; errcode_t errcode; }; /* * Inode cache structure */ struct ext2_inode_cache { void * buffer; blk_t buffer_blk; int cache_last; int cache_size; int refcount; struct ext2_inode_cache_ent *cache; }; struct ext2_inode_cache_ent { ext2_ino_t ino; struct ext2_inode inode; }; /* Function prototypes */ extern int ext2fs_process_dir_block(ext2_filsys fs, blk_t *blocknr, e2_blkcnt_t blockcnt, blk_t ref_block, int ref_offset, void *priv_data); e2fsprogs-1.41.14/lib/ext2fs/newdir.c0000644031104000366760000000310611405316370015247 0ustar tytso/* * newdir.c --- create a new directory block * * Copyright (C) 1994, 1995 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #include "ext2_fs.h" #include "ext2fs.h" #ifndef EXT2_FT_DIR #define EXT2_FT_DIR 2 #endif /* * Create new directory block */ errcode_t ext2fs_new_dir_block(ext2_filsys fs, ext2_ino_t dir_ino, ext2_ino_t parent_ino, char **block) { struct ext2_dir_entry *dir = NULL; errcode_t retval; char *buf; int rec_len; int filetype = 0; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); retval = ext2fs_get_mem(fs->blocksize, &buf); if (retval) return retval; memset(buf, 0, fs->blocksize); dir = (struct ext2_dir_entry *) buf; retval = ext2fs_set_rec_len(fs, fs->blocksize, dir); if (retval) return retval; if (dir_ino) { if (fs->super->s_feature_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE) filetype = EXT2_FT_DIR << 8; /* * Set up entry for '.' */ dir->inode = dir_ino; dir->name_len = 1 | filetype; dir->name[0] = '.'; rec_len = fs->blocksize - EXT2_DIR_REC_LEN(1); dir->rec_len = EXT2_DIR_REC_LEN(1); /* * Set up entry for '..' */ dir = (struct ext2_dir_entry *) (buf + dir->rec_len); retval = ext2fs_set_rec_len(fs, rec_len, dir); if (retval) return retval; dir->inode = parent_ino; dir->name_len = 2 | filetype; dir->name[0] = '.'; dir->name[1] = '.'; } *block = buf; return 0; } e2fsprogs-1.41.14/lib/ext2fs/getsectsize.c0000644031104000366760000000313311405316370016310 0ustar tytso/* * getsectsize.c --- get the sector size of a device. * * Copyright (C) 1995, 1995 Theodore Ts'o. * Copyright (C) 2003 VMware, Inc. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #define _LARGEFILE_SOURCE #define _LARGEFILE64_SOURCE #include #if HAVE_UNISTD_H #include #endif #if HAVE_ERRNO_H #include #endif #include #ifdef HAVE_LINUX_FD_H #include #include #endif #if defined(__linux__) && defined(_IO) #if !defined(BLKSSZGET) #define BLKSSZGET _IO(0x12,104)/* get block device sector size */ #endif #if !defined(BLKPBSZGET) #define BLKPBSZGET _IO(0x12,123)/* get block physical sector size */ #endif #endif #include "ext2_fs.h" #include "ext2fs.h" /* * Returns the logical sector size of a device */ errcode_t ext2fs_get_device_sectsize(const char *file, int *sectsize) { int fd; #ifdef HAVE_OPEN64 fd = open64(file, O_RDONLY); #else fd = open(file, O_RDONLY); #endif if (fd < 0) return errno; #ifdef BLKSSZGET if (ioctl(fd, BLKSSZGET, sectsize) >= 0) { close(fd); return 0; } #endif *sectsize = 0; close(fd); return 0; } /* * Returns the physical sector size of a device */ errcode_t ext2fs_get_device_phys_sectsize(const char *file, int *sectsize) { int fd; #ifdef HAVE_OPEN64 fd = open64(file, O_RDONLY); #else fd = open(file, O_RDONLY); #endif if (fd < 0) return errno; #ifdef BLKPBSZGET if (ioctl(fd, BLKPBSZGET, sectsize) >= 0) { close(fd); return 0; } #endif *sectsize = 0; close(fd); return 0; } e2fsprogs-1.41.14/lib/ext2fs/bb_compat.c0000644031104000366760000000241411405316370015706 0ustar tytso/* * bb_compat.c --- compatibility badblocks routines * * Copyright (C) 1997 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #include #include #if HAVE_SYS_STAT_H #include #endif #if HAVE_SYS_TYPES_H #include #endif #include "ext2_fs.h" #include "ext2fsP.h" errcode_t badblocks_list_create(badblocks_list *ret, int size) { return ext2fs_badblocks_list_create(ret, size); } void badblocks_list_free(badblocks_list bb) { ext2fs_badblocks_list_free(bb); } errcode_t badblocks_list_add(badblocks_list bb, blk_t blk) { return ext2fs_badblocks_list_add(bb, blk); } int badblocks_list_test(badblocks_list bb, blk_t blk) { return ext2fs_badblocks_list_test(bb, blk); } errcode_t badblocks_list_iterate_begin(badblocks_list bb, badblocks_iterate *ret) { return ext2fs_badblocks_list_iterate_begin(bb, ret); } int badblocks_list_iterate(badblocks_iterate iter, blk_t *blk) { return ext2fs_badblocks_list_iterate(iter, blk); } void badblocks_list_iterate_end(badblocks_iterate iter) { ext2fs_badblocks_list_iterate_end(iter); } e2fsprogs-1.41.14/lib/ext2fs/badblocks.c0000644031104000366760000001456411405316370015715 0ustar tytso/* * badblocks.c --- routines to manipulate the bad block structure * * Copyright (C) 1994, 1995, 1996 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #include #include #if HAVE_SYS_STAT_H #include #endif #if HAVE_SYS_TYPES_H #include #endif #include "ext2_fs.h" #include "ext2fsP.h" /* * Helper function for making a badblocks list */ static errcode_t make_u32_list(int size, int num, __u32 *list, ext2_u32_list *ret) { ext2_u32_list bb; errcode_t retval; retval = ext2fs_get_mem(sizeof(struct ext2_struct_u32_list), &bb); if (retval) return retval; memset(bb, 0, sizeof(struct ext2_struct_u32_list)); bb->magic = EXT2_ET_MAGIC_BADBLOCKS_LIST; bb->size = size ? size : 10; bb->num = num; retval = ext2fs_get_array(bb->size, sizeof(blk_t), &bb->list); if (retval) { ext2fs_free_mem(&bb); return retval; } if (list) memcpy(bb->list, list, bb->size * sizeof(blk_t)); else memset(bb->list, 0, bb->size * sizeof(blk_t)); *ret = bb; return 0; } /* * This procedure creates an empty u32 list. */ errcode_t ext2fs_u32_list_create(ext2_u32_list *ret, int size) { return make_u32_list(size, 0, 0, ret); } /* * This procedure creates an empty badblocks list. */ errcode_t ext2fs_badblocks_list_create(ext2_badblocks_list *ret, int size) { return make_u32_list(size, 0, 0, (ext2_badblocks_list *) ret); } /* * This procedure copies a badblocks list */ errcode_t ext2fs_u32_copy(ext2_u32_list src, ext2_u32_list *dest) { errcode_t retval; retval = make_u32_list(src->size, src->num, src->list, dest); if (retval) return retval; (*dest)->badblocks_flags = src->badblocks_flags; return 0; } errcode_t ext2fs_badblocks_copy(ext2_badblocks_list src, ext2_badblocks_list *dest) { return ext2fs_u32_copy((ext2_u32_list) src, (ext2_u32_list *) dest); } /* * This procedure frees a badblocks list. * * (note: moved to closefs.c) */ /* * This procedure adds a block to a badblocks list. */ errcode_t ext2fs_u32_list_add(ext2_u32_list bb, __u32 blk) { errcode_t retval; int i, j; unsigned long old_size; EXT2_CHECK_MAGIC(bb, EXT2_ET_MAGIC_BADBLOCKS_LIST); if (bb->num >= bb->size) { old_size = bb->size * sizeof(__u32); bb->size += 100; retval = ext2fs_resize_mem(old_size, bb->size * sizeof(__u32), &bb->list); if (retval) { bb->size -= 100; return retval; } } /* * Add special case code for appending to the end of the list */ i = bb->num-1; if ((bb->num != 0) && (bb->list[i] == blk)) return 0; if ((bb->num == 0) || (bb->list[i] < blk)) { bb->list[bb->num++] = blk; return 0; } j = bb->num; for (i=0; i < bb->num; i++) { if (bb->list[i] == blk) return 0; if (bb->list[i] > blk) { j = i; break; } } for (i=bb->num; i > j; i--) bb->list[i] = bb->list[i-1]; bb->list[j] = blk; bb->num++; return 0; } errcode_t ext2fs_badblocks_list_add(ext2_badblocks_list bb, blk_t blk) { return ext2fs_u32_list_add((ext2_u32_list) bb, (__u32) blk); } /* * This procedure finds a particular block is on a badblocks * list. */ int ext2fs_u32_list_find(ext2_u32_list bb, __u32 blk) { int low, high, mid; if (bb->magic != EXT2_ET_MAGIC_BADBLOCKS_LIST) return -1; if (bb->num == 0) return -1; low = 0; high = bb->num-1; if (blk == bb->list[low]) return low; if (blk == bb->list[high]) return high; while (low < high) { mid = (low+high)/2; if (mid == low || mid == high) break; if (blk == bb->list[mid]) return mid; if (blk < bb->list[mid]) high = mid; else low = mid; } return -1; } /* * This procedure tests to see if a particular block is on a badblocks * list. */ int ext2fs_u32_list_test(ext2_u32_list bb, __u32 blk) { if (ext2fs_u32_list_find(bb, blk) < 0) return 0; else return 1; } int ext2fs_badblocks_list_test(ext2_badblocks_list bb, blk_t blk) { return ext2fs_u32_list_test((ext2_u32_list) bb, (__u32) blk); } /* * Remove a block from the badblock list */ int ext2fs_u32_list_del(ext2_u32_list bb, __u32 blk) { int remloc, i; if (bb->num == 0) return -1; remloc = ext2fs_u32_list_find(bb, blk); if (remloc < 0) return -1; for (i = remloc ; i < bb->num-1; i++) bb->list[i] = bb->list[i+1]; bb->num--; return 0; } void ext2fs_badblocks_list_del(ext2_u32_list bb, __u32 blk) { ext2fs_u32_list_del(bb, blk); } errcode_t ext2fs_u32_list_iterate_begin(ext2_u32_list bb, ext2_u32_iterate *ret) { ext2_u32_iterate iter; errcode_t retval; EXT2_CHECK_MAGIC(bb, EXT2_ET_MAGIC_BADBLOCKS_LIST); retval = ext2fs_get_mem(sizeof(struct ext2_struct_u32_iterate), &iter); if (retval) return retval; iter->magic = EXT2_ET_MAGIC_BADBLOCKS_ITERATE; iter->bb = bb; iter->ptr = 0; *ret = iter; return 0; } errcode_t ext2fs_badblocks_list_iterate_begin(ext2_badblocks_list bb, ext2_badblocks_iterate *ret) { return ext2fs_u32_list_iterate_begin((ext2_u32_list) bb, (ext2_u32_iterate *) ret); } int ext2fs_u32_list_iterate(ext2_u32_iterate iter, __u32 *blk) { ext2_u32_list bb; if (iter->magic != EXT2_ET_MAGIC_BADBLOCKS_ITERATE) return 0; bb = iter->bb; if (bb->magic != EXT2_ET_MAGIC_BADBLOCKS_LIST) return 0; if (iter->ptr < bb->num) { *blk = bb->list[iter->ptr++]; return 1; } *blk = 0; return 0; } int ext2fs_badblocks_list_iterate(ext2_badblocks_iterate iter, blk_t *blk) { return ext2fs_u32_list_iterate((ext2_u32_iterate) iter, (__u32 *) blk); } void ext2fs_u32_list_iterate_end(ext2_u32_iterate iter) { if (!iter || (iter->magic != EXT2_ET_MAGIC_BADBLOCKS_ITERATE)) return; iter->bb = 0; ext2fs_free_mem(&iter); } void ext2fs_badblocks_list_iterate_end(ext2_badblocks_iterate iter) { ext2fs_u32_list_iterate_end((ext2_u32_iterate) iter); } int ext2fs_u32_list_equal(ext2_u32_list bb1, ext2_u32_list bb2) { EXT2_CHECK_MAGIC(bb1, EXT2_ET_MAGIC_BADBLOCKS_LIST); EXT2_CHECK_MAGIC(bb2, EXT2_ET_MAGIC_BADBLOCKS_LIST); if (bb1->num != bb2->num) return 0; if (memcmp(bb1->list, bb2->list, bb1->num * sizeof(blk_t)) != 0) return 0; return 1; } int ext2fs_badblocks_equal(ext2_badblocks_list bb1, ext2_badblocks_list bb2) { return ext2fs_u32_list_equal((ext2_u32_list) bb1, (ext2_u32_list) bb2); } int ext2fs_u32_list_count(ext2_u32_list bb) { return bb->num; } e2fsprogs-1.41.14/lib/ext2fs/write_bb_file.c0000644031104000366760000000136711405316370016562 0ustar tytso/* * write_bb_file.c --- write a list of bad blocks to a FILE * * * Copyright (C) 1994, 1995 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #include #include "ext2_fs.h" #include "ext2fs.h" errcode_t ext2fs_write_bb_FILE(ext2_badblocks_list bb_list, unsigned int flags EXT2FS_ATTR((unused)), FILE *f) { badblocks_iterate bb_iter; blk_t blk; errcode_t retval; retval = ext2fs_badblocks_list_iterate_begin(bb_list, &bb_iter); if (retval) return retval; while (ext2fs_badblocks_list_iterate(bb_iter, &blk)) { fprintf(f, "%u\n", blk); } ext2fs_badblocks_list_iterate_end(bb_iter); return 0; } e2fsprogs-1.41.14/lib/ext2fs/read_bb.c0000644031104000116100000000430411504417000015775 0ustar tytsoeng/* * read_bb --- read the bad blocks inode * * Copyright (C) 1994 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #include #include #if HAVE_SYS_STAT_H #include #endif #if HAVE_SYS_TYPES_H #include #endif #include "ext2_fs.h" #include "ext2fs.h" struct read_bb_record { ext2_badblocks_list bb_list; errcode_t err; }; /* * Helper function for ext2fs_read_bb_inode() */ #ifdef __TURBOC__ #pragma argsused #endif static int mark_bad_block(ext2_filsys fs, blk_t *block_nr, e2_blkcnt_t blockcnt EXT2FS_ATTR((unused)), blk_t ref_block EXT2FS_ATTR((unused)), int ref_offset EXT2FS_ATTR((unused)), void *priv_data) { struct read_bb_record *rb = (struct read_bb_record *) priv_data; if (blockcnt < 0) return 0; if ((*block_nr < fs->super->s_first_data_block) || (*block_nr >= fs->super->s_blocks_count)) return 0; /* Ignore illegal blocks */ rb->err = ext2fs_badblocks_list_add(rb->bb_list, *block_nr); if (rb->err) return BLOCK_ABORT; return 0; } /* * Reads the current bad blocks from the bad blocks inode. */ errcode_t ext2fs_read_bb_inode(ext2_filsys fs, ext2_badblocks_list *bb_list) { errcode_t retval; struct read_bb_record rb; struct ext2_inode inode; blk_t numblocks; EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS); if (!*bb_list) { retval = ext2fs_read_inode(fs, EXT2_BAD_INO, &inode); if (retval) return retval; numblocks = inode.i_blocks; if (!((fs->super->s_feature_ro_compat & EXT4_FEATURE_RO_COMPAT_HUGE_FILE) && (inode.i_flags & EXT4_HUGE_FILE_FL))) numblocks = numblocks / (fs->blocksize / 512); numblocks += 20; if (numblocks < 50) numblocks = 50; if (numblocks > 50000) numblocks = 500; retval = ext2fs_badblocks_list_create(bb_list, numblocks); if (retval) return retval; } rb.bb_list = *bb_list; rb.err = 0; retval = ext2fs_block_iterate2(fs, EXT2_BAD_INO, BLOCK_FLAG_READ_ONLY, 0, mark_bad_block, &rb); if (retval) return retval; return rb.err; } e2fsprogs-1.41.14/lib/ext2fs/getsize.c0000644031104000366760000001460411405316370015436 0ustar tytso/* * getsize.c --- get the size of a partition. * * Copyright (C) 1995, 1995 Theodore Ts'o. * Copyright (C) 2003 VMware, Inc. * * Windows version of ext2fs_get_device_size by Chris Li, VMware. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #define _LARGEFILE_SOURCE #define _LARGEFILE64_SOURCE #include #if HAVE_UNISTD_H #include #endif #if HAVE_ERRNO_H #include #endif #include #ifdef HAVE_SYS_IOCTL_H #include #endif #ifdef HAVE_LINUX_FD_H #include #endif #ifdef HAVE_SYS_DISKLABEL_H #include #endif #ifdef HAVE_SYS_DISK_H #ifdef HAVE_SYS_QUEUE_H #include /* for LIST_HEAD */ #endif #include #endif #ifdef __linux__ #include #endif #if HAVE_SYS_STAT_H #include #endif #include #if defined(__linux__) && defined(_IO) && !defined(BLKGETSIZE) #define BLKGETSIZE _IO(0x12,96) /* return device size */ #endif #if defined(__linux__) && defined(_IOR) && !defined(BLKGETSIZE64) #define BLKGETSIZE64 _IOR(0x12,114,size_t) /* return device size in bytes (u64 *arg) */ #endif #ifdef APPLE_DARWIN #define BLKGETSIZE DKIOCGETBLOCKCOUNT32 #endif /* APPLE_DARWIN */ #include "ext2_fs.h" #include "ext2fs.h" #if defined(__CYGWIN__) || defined (WIN32) #include "windows.h" #include "winioctl.h" #if (_WIN32_WINNT >= 0x0500) #define HAVE_GET_FILE_SIZE_EX 1 #endif errcode_t ext2fs_get_device_size(const char *file, int blocksize, blk_t *retblocks) { HANDLE dev; PARTITION_INFORMATION pi; DISK_GEOMETRY gi; DWORD retbytes; #ifdef HAVE_GET_FILE_SIZE_EX LARGE_INTEGER filesize; #else DWORD filesize; #endif /* HAVE_GET_FILE_SIZE_EX */ dev = CreateFile(file, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE , NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (dev == INVALID_HANDLE_VALUE) return EBADF; if (DeviceIoControl(dev, IOCTL_DISK_GET_PARTITION_INFO, &pi, sizeof(PARTITION_INFORMATION), &pi, sizeof(PARTITION_INFORMATION), &retbytes, NULL)) { *retblocks = pi.PartitionLength.QuadPart / blocksize; } else if (DeviceIoControl(dev, IOCTL_DISK_GET_DRIVE_GEOMETRY, &gi, sizeof(DISK_GEOMETRY), &gi, sizeof(DISK_GEOMETRY), &retbytes, NULL)) { *retblocks = gi.BytesPerSector * gi.SectorsPerTrack * gi.TracksPerCylinder * gi.Cylinders.QuadPart / blocksize; #ifdef HAVE_GET_FILE_SIZE_EX } else if (GetFileSizeEx(dev, &filesize)) { *retblocks = filesize.QuadPart / blocksize; } #else } else { filesize = GetFileSize(dev, NULL); if (INVALID_FILE_SIZE != filesize) { *retblocks = filesize / blocksize; } } #endif /* HAVE_GET_FILE_SIZE_EX */ CloseHandle(dev); return 0; } #else static int valid_offset (int fd, ext2_loff_t offset) { char ch; if (ext2fs_llseek (fd, offset, 0) < 0) return 0; if (read (fd, &ch, 1) < 1) return 0; return 1; } /* * Returns the number of blocks in a partition */ errcode_t ext2fs_get_device_size2(const char *file, int blocksize, blk64_t *retblocks) { int fd, rc = 0; int valid_blkgetsize64 = 1; #ifdef __linux__ struct utsname ut; #endif unsigned long long size64; unsigned long size; ext2_loff_t high, low; #ifdef FDGETPRM struct floppy_struct this_floppy; #endif #ifdef HAVE_SYS_DISKLABEL_H int part; struct disklabel lab; struct partition *pp; char ch; #endif /* HAVE_SYS_DISKLABEL_H */ #ifdef HAVE_OPEN64 fd = open64(file, O_RDONLY); #else fd = open(file, O_RDONLY); #endif if (fd < 0) return errno; #ifdef DKIOCGETBLOCKCOUNT /* For Apple Darwin */ if (ioctl(fd, DKIOCGETBLOCKCOUNT, &size64) >= 0) { *retblocks = size64 / (blocksize / 512); goto out; } #endif #ifdef BLKGETSIZE64 #ifdef __linux__ if ((uname(&ut) == 0) && ((ut.release[0] == '2') && (ut.release[1] == '.') && (ut.release[2] < '6') && (ut.release[3] == '.'))) valid_blkgetsize64 = 0; #endif if (valid_blkgetsize64 && ioctl(fd, BLKGETSIZE64, &size64) >= 0) { *retblocks = size64 / blocksize; goto out; } #endif #ifdef BLKGETSIZE if (ioctl(fd, BLKGETSIZE, &size) >= 0) { *retblocks = size / (blocksize / 512); goto out; } #endif #ifdef FDGETPRM if (ioctl(fd, FDGETPRM, &this_floppy) >= 0) { *retblocks = this_floppy.size / (blocksize / 512); goto out; } #endif #ifdef HAVE_SYS_DISKLABEL_H #if defined(DIOCGMEDIASIZE) { off_t ms; u_int bs; if (ioctl(fd, DIOCGMEDIASIZE, &ms) >= 0) { *retblocks = ms / blocksize; goto out; } } #elif defined(DIOCGDINFO) /* old disklabel interface */ part = strlen(file) - 1; if (part >= 0) { ch = file[part]; if (isdigit(ch)) part = 0; else if (ch >= 'a' && ch <= 'h') part = ch - 'a'; else part = -1; } if (part >= 0 && (ioctl(fd, DIOCGDINFO, (char *)&lab) >= 0)) { pp = &lab.d_partitions[part]; if (pp->p_size) { *retblocks = pp->p_size / (blocksize / 512); goto out; } } #endif /* defined(DIOCG*) */ #endif /* HAVE_SYS_DISKLABEL_H */ { #ifdef HAVE_FSTAT64 struct stat64 st; if (fstat64(fd, &st) == 0) #else struct stat st; if (fstat(fd, &st) == 0) #endif if (S_ISREG(st.st_mode)) { *retblocks = st.st_size / blocksize; goto out; } } /* * OK, we couldn't figure it out by using a specialized ioctl, * which is generally the best way. So do binary search to * find the size of the partition. */ low = 0; for (high = 1024; valid_offset (fd, high); high *= 2) low = high; while (low < high - 1) { const ext2_loff_t mid = (low + high) / 2; if (valid_offset (fd, mid)) low = mid; else high = mid; } valid_offset (fd, 0); size64 = low + 1; *retblocks = size64 / blocksize; out: close(fd); return rc; } errcode_t ext2fs_get_device_size(const char *file, int blocksize, blk_t *retblocks) { errcode_t retval; blk64_t blocks; retval = ext2fs_get_device_size2(file, blocksize, &blocks); if (retval) return retval; if (blocks >= (1ULL << 32)) return EFBIG; *retblocks = (blk_t) blocks; return 0; } #endif /* WIN32 */ #ifdef DEBUG int main(int argc, char **argv) { blk_t blocks; int retval; if (argc < 2) { fprintf(stderr, "Usage: %s device\n", argv[0]); exit(1); } retval = ext2fs_get_device_size(argv[1], 1024, &blocks); if (retval) { com_err(argv[0], retval, "while calling ext2fs_get_device_size"); exit(1); } printf("Device %s has %u 1k blocks.\n", argv[1], blocks); exit(0); } #endif e2fsprogs-1.41.14/lib/ext2fs/tst_types.c0000644031104000366760000000253711405316370016024 0ustar tytso/* * This testing program makes sure the ext2_types header file * * Copyright (C) 2006 by Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #include #include #include #include "ext2fs/ext2_types.h" int main(int argc, char **argv) { if (sizeof(__u8) != 1) { printf("Sizeof(__u8) is %d should be 1\n", (int)sizeof(__u8)); exit(1); } if (sizeof(__s8) != 1) { printf("Sizeof(_s8) is %d should be 1\n", (int)sizeof(__s8)); exit(1); } if (sizeof(__u16) != 2) { printf("Sizeof(__u16) is %d should be 2\n", (int)sizeof(__u16)); exit(1); } if (sizeof(__s16) != 2) { printf("Sizeof(__s16) is %d should be 2\n", (int)sizeof(__s16)); exit(1); } if (sizeof(__u32) != 4) { printf("Sizeof(__u32) is %d should be 4\n", (int)sizeof(__u32)); exit(1); } if (sizeof(__s32) != 4) { printf("Sizeof(__s32) is %d should be 4\n", (int)sizeof(__s32)); exit(1); } if (sizeof(__u64) != 8) { printf("Sizeof(__u64) is %d should be 8\n", (int)sizeof(__u64)); exit(1); } if (sizeof(__s64) != 8) { printf("Sizeof(__s64) is %d should be 8\n", (int)sizeof(__s64)); exit(1); } printf("The ext2_types.h types are correct.\n"); exit(0); } e2fsprogs-1.41.14/lib/ext2fs/tst_bitops.c0000644031104000116100000001012211504417000016604 0ustar tytsoeng/* * This testing program makes sure the bitops functions work * * Copyright (C) 2001 by Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Library * General Public License, version 2. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #include #include #include #include #if HAVE_ERRNO_H #include #endif #include #include #include "ext2_fs.h" #include "ext2fs.h" unsigned char bitarray[] = { 0x80, 0xF0, 0x40, 0x40, 0x0, 0x0, 0x0, 0x0, 0x10, 0x20, 0x00, 0x00 }; int bits_list[] = { 7, 12, 13, 14,15, 22, 30, 68, 77, -1, }; #define BIG_TEST_BIT (((unsigned) 1 << 31) + 42) int main(int argc, char **argv) { int i, j, size; unsigned char testarray[12]; unsigned char *bigarray; size = sizeof(bitarray)*8; #if 0 i = ext2fs_find_first_bit_set(bitarray, size); while (i < size) { printf("Bit set: %d\n", i); i = ext2fs_find_next_bit_set(bitarray, size, i+1); } #endif /* Test test_bit */ for (i=0,j=0; i < size; i++) { if (ext2fs_test_bit(i, bitarray)) { if (bits_list[j] == i) { j++; } else { printf("Bit %d set, not expected\n", i); exit(1); } } else { if (bits_list[j] == i) { printf("Expected bit %d to be clear.\n", i); exit(1); } } } printf("ext2fs_test_bit appears to be correct\n"); /* Test ext2fs_set_bit */ memset(testarray, 0, sizeof(testarray)); for (i=0; bits_list[i] > 0; i++) { ext2fs_set_bit(bits_list[i], testarray); } if (memcmp(testarray, bitarray, sizeof(testarray)) == 0) { printf("ext2fs_set_bit test succeeded.\n"); } else { printf("ext2fs_set_bit test failed.\n"); for (i=0; i < sizeof(testarray); i++) { printf("%02x ", testarray[i]); } printf("\n"); exit(1); } for (i=0; bits_list[i] > 0; i++) { ext2fs_clear_bit(bits_list[i], testarray); } for (i=0; i < sizeof(testarray); i++) { if (testarray[i]) { printf("ext2fs_clear_bit failed, " "testarray[%d] is %d\n", i, testarray[i]); exit(1); } } printf("ext2fs_clear_bit test succeed.\n"); /* Do bigarray test */ bigarray = malloc(1 << 29); if (!bigarray) { fprintf(stderr, "Failed to allocate scratch memory!\n"); exit(1); } bigarray[BIG_TEST_BIT >> 3] = 0; ext2fs_set_bit(BIG_TEST_BIT, bigarray); printf("big bit number (%u) test: %d, expected %d\n", BIG_TEST_BIT, bigarray[BIG_TEST_BIT >> 3], (1 << (BIG_TEST_BIT & 7))); if (bigarray[BIG_TEST_BIT >> 3] != (1 << (BIG_TEST_BIT & 7))) exit(1); ext2fs_clear_bit(BIG_TEST_BIT, bigarray); printf("big bit number (%u) test: %d, expected 0\n", BIG_TEST_BIT, bigarray[BIG_TEST_BIT >> 3]); if (bigarray[BIG_TEST_BIT >> 3] != 0) exit(1); printf("ext2fs_set_bit big_test successful\n"); /* Now test ext2fs_fast_set_bit */ memset(testarray, 0, sizeof(testarray)); for (i=0; bits_list[i] > 0; i++) { ext2fs_fast_set_bit(bits_list[i], testarray); } if (memcmp(testarray, bitarray, sizeof(testarray)) == 0) { printf("ext2fs_fast_set_bit test succeeded.\n"); } else { printf("ext2fs_fast_set_bit test failed.\n"); for (i=0; i < sizeof(testarray); i++) { printf("%02x ", testarray[i]); } printf("\n"); exit(1); } for (i=0; bits_list[i] > 0; i++) { ext2fs_clear_bit(bits_list[i], testarray); } for (i=0; i < sizeof(testarray); i++) { if (testarray[i]) { printf("ext2fs_clear_bit failed, " "testarray[%d] is %d\n", i, testarray[i]); exit(1); } } printf("ext2fs_clear_bit test succeed.\n"); bigarray[BIG_TEST_BIT >> 3] = 0; ext2fs_fast_set_bit(BIG_TEST_BIT, bigarray); printf("big bit number (%u) test: %d, expected %d\n", BIG_TEST_BIT, bigarray[BIG_TEST_BIT >> 3], (1 << (BIG_TEST_BIT & 7))); if (bigarray[BIG_TEST_BIT >> 3] != (1 << (BIG_TEST_BIT & 7))) exit(1); ext2fs_fast_clear_bit(BIG_TEST_BIT, bigarray); printf("big bit number (%u) test: %d, expected 0\n", BIG_TEST_BIT, bigarray[BIG_TEST_BIT >> 3]); if (bigarray[BIG_TEST_BIT >> 3] != 0) exit(1); printf("ext2fs_fast_set_bit big_test successful\n"); exit(0); } e2fsprogs-1.41.14/lib/Makefile.profile0000644031104000366760000000143411405316370015501 0ustar tytsoall:: $(LIBRARY)_p.a real-subdirs:: Makefile $(E) " MKDIR profiled" $(Q) mkdir -p profiled clean:: $(RM) -rf profiled $(RM) -f $(LIBRARY)_p.a ../$(LIBRARY)_p.a $(LIBRARY)_p.a: $(OBJS) $(E) " GEN_PROFILED_LIB $(ELF_LIB)" $(Q) (if test -r $@; then $(RM) -f $@.bak && $(MV) $@ $@.bak; fi) $(Q) (cd profiled; $(ARUPD) ../$@ $(OBJS)) -$(Q) $(RANLIB) $@ $(Q) $(RM) -f ../$@ $(Q) (cd ..; $(LN) $(LINK_BUILD_FLAGS) \ `echo $(my_dir) | sed -e 's;lib/;;'`/$@ $@) install:: $(LIBRARY)_p.a installdirs $(E) " INSTALL_DATA $(libdir)/$(LIBRARY)_p.a" $(Q) $(INSTALL_DATA) $(LIBRARY)_p.a $(DESTDIR)$(libdir)/$(LIBRARY)_p.a -$(Q) $(RANLIB) $(DESTDIR)$(libdir)/$(LIBRARY)_p.a $(Q) $(CHMOD) $(LIBMODE) $(DESTDIR)$(libdir)/$(LIBRARY)_p.a uninstall:: $(RM) -f $(DESTDIR)$(libdir)/$(LIBRARY)_p.a e2fsprogs-1.41.14/lib/Makefile.solaris-lib0000644031104000366760000000354211405316370016263 0ustar tytso# # This is a Makefile stub which handles the creation of Linux ELF shared # libraries. # # In order to use this stub, the following makefile variables must be defined. # # ELF_VERSION = 1.0 # ELF_SO_VERSION = 1 # ELF_IMAGE = libce # ELF_MYDIR = et # ELF_INSTALL_DIR = $(SHLIBDIR) # ELF_OTHER_LIBS = -lc all:: image real-subdirs:: Makefile $(E) " MKDIR elfshared" $(Q) mkdir -p elfshared ELF_LIB = $(ELF_IMAGE).so.$(ELF_VERSION) ELF_SONAME = $(ELF_IMAGE).so.$(ELF_SO_VERSION) image: $(ELF_LIB) $(ELF_LIB): $(OBJS) $(E) " GEN_ELF_SOLIB $(ELF_LIB)" $(Q) (cd elfshared; $(CC) --shared -o $(ELF_LIB) $(LDFLAGS) \ -Wl,-h,$(ELF_SONAME) $(OBJS) $(ELF_OTHER_LIBS)) $(Q) $(MV) elfshared/$(ELF_LIB) . $(Q) $(RM) -f ../$(ELF_LIB) ../$(ELF_IMAGE).so ../$(ELF_SONAME) $(Q) (cd ..; $(LN) $(LINK_BUILD_FLAGS) \ `echo $(my_dir) | sed -e 's;lib/;;'`/$(ELF_LIB) $(ELF_LIB)) $(Q) (cd ..; $(LN) $(LINK_BUILD_FLAGS) $(ELF_LIB) $(ELF_IMAGE).so) $(Q) (cd ..; $(LN) $(LINK_BUILD_FLAGS) $(ELF_LIB) $(ELF_SONAME)) installdirs-elf-lib:: $(MKINSTALLDIRS) $(DESTDIR)$(ELF_INSTALL_DIR) \ $(DESTDIR)$(libdir) installdirs:: installdirs-elf-lib install-shlibs install:: $(ELF_LIB) installdirs-elf-lib $(INSTALL_PROGRAM) $(ELF_LIB) $(DESTDIR)$(ELF_INSTALL_DIR)/$(ELF_LIB) $(LN_S) -f $(ELF_LIB) $(DESTDIR)$(ELF_INSTALL_DIR)/$(ELF_SONAME) $(LN_S) -f $(ELF_INSTALL_DIR)/$(ELF_SONAME) \ $(DESTDIR)$(libdir)/$(ELF_IMAGE).so -$(LDCONFIG) install-strip: install $(STRIP) -x $(DESTDIR)$(ELF_INSTALL_DIR)/$(ELF_LIB) install-shlibs-strip: install-shlibs $(STRIP) -x $(DESTDIR)$(ELF_INSTALL_DIR)/$(ELF_LIB) uninstall-shlibs uninstall:: $(RM) -f $(DESTDIR)$(ELF_INSTALL_DIR)/$(ELF_LIB) \ $(DESTDIR)$(ELF_INSTALL_DIR)/$(ELF_SONAME) \ $(DESTDIR)$(libdir)/$(ELF_IMAGE).so -$(LDCONFIG) clean:: $(RM) -rf elfshared $(RM) -f $(ELF_LIB) $(RM) -f ../$(ELF_LIB) ../$(ELF_IMAGE).so ../$(ELF_SONAME) e2fsprogs-1.41.14/lib/fpopen.c0000644031104000366760000000457211240667355014054 0ustar tytso/* * fpopen.c --- unlike the libc popen, it directly executes the * command instead of call out to the shell. * * Copyright Theodore Ts'o, 1996-1999. * * Permission to use this file is granted for any purposes, as long as * this copyright statement is kept intact and the author is not held * liable for any damages resulting from the use of this program. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF * WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * USE OF THIS SOFTWARE. */ #include #include #include #include #include #include #define MAX_ARGV 256 extern FILE *fpopen(const char *cmd, const char *mode); FILE *fpopen(const char *cmd, const char *mode) { char *argv[MAX_ARGV]; int i = 0; char *buf, *prog = 0; char *p; int do_stdin, do_stderr = 0; int fds[2]; pid_t pid; if (!mode) { errno = EFAULT; return NULL; } switch (*mode) { case 'r': do_stdin = 0; break; case 'w': do_stdin = 1; break; default: errno = EINVAL; return NULL; } switch (*(mode+1)) { case '&': do_stderr = 1; } /* * Create the argv vector.... */ buf = malloc(strlen(cmd)+1); if (!buf) return NULL; strcpy(buf, cmd); p = buf; while (p && *p) { if (isspace(*p)) { p++; continue; } if (i == 0) prog = p; argv[i++] = p; p = strchr(p, ' '); if (p) *p++ = 0; } argv[i] = 0; /* * Get the pipe */ if (pipe(fds) < 0) return NULL; /* Fork and execute the correct program. */ if ((pid = fork()) < 0) { perror("fork"); return NULL; } else if (pid == 0) { if (do_stdin) { close(fds[1]); dup2(fds[0], 0); } else { close(fds[0]); dup2(fds[1], 1); if (do_stderr) dup2(fds[1], 2); } (void) execvp(prog, argv); perror(prog); exit(1); } return fdopen(do_stdin ? fds[1] : fds[0], mode); } e2fsprogs-1.41.14/lib/blkid/0000755031104000366760000000000011446754112013472 5ustar tytsoe2fsprogs-1.41.14/lib/blkid/blkid_types.h.in0000644031104000366760000000537111240667355016573 0ustar tytso/* * If linux/types.h is already been included, assume it has defined * everything we need. (cross fingers) Other header files may have * also defined the types that we need. */ #if (!defined(_LINUX_TYPES_H) && !defined(_BLKID_TYPES_H) && \ !defined(_EXT2_TYPES_H)) #define _BLKID_TYPES_H @ASM_TYPES_HEADER@ #ifdef __U8_TYPEDEF typedef __U8_TYPEDEF __u8; #else typedef unsigned char __u8; #endif #ifdef __S8_TYPEDEF typedef __S8_TYPEDEF __s8; #else typedef signed char __s8; #endif #ifdef __U16_TYPEDEF typedef __U16_TYPEDEF __u16; #else #if (@SIZEOF_INT@ == 2) typedef unsigned int __u16; #else #if (@SIZEOF_SHORT@ == 2) typedef unsigned short __u16; #else ?==error: undefined 16 bit type #endif /* SIZEOF_SHORT == 2 */ #endif /* SIZEOF_INT == 2 */ #endif /* __U16_TYPEDEF */ #ifdef __S16_TYPEDEF typedef __S16_TYPEDEF __s16; #else #if (@SIZEOF_INT@ == 2) typedef int __s16; #else #if (@SIZEOF_SHORT@ == 2) typedef short __s16; #else ?==error: undefined 16 bit type #endif /* SIZEOF_SHORT == 2 */ #endif /* SIZEOF_INT == 2 */ #endif /* __S16_TYPEDEF */ #ifdef __U32_TYPEDEF typedef __U32_TYPEDEF __u32; #else #if (@SIZEOF_INT@ == 4) typedef unsigned int __u32; #else #if (@SIZEOF_LONG@ == 4) typedef unsigned long __u32; #else #if (@SIZEOF_SHORT@ == 4) typedef unsigned short __u32; #else ?== error: undefined 32 bit type #endif /* SIZEOF_SHORT == 4 */ #endif /* SIZEOF_LONG == 4 */ #endif /* SIZEOF_INT == 4 */ #endif /* __U32_TYPEDEF */ #ifdef __S32_TYPEDEF typedef __S32_TYPEDEF __s32; #else #if (@SIZEOF_INT@ == 4) typedef int __s32; #else #if (@SIZEOF_LONG@ == 4) typedef long __s32; #else #if (@SIZEOF_SHORT@ == 4) typedef short __s32; #else ?== error: undefined 32 bit type #endif /* SIZEOF_SHORT == 4 */ #endif /* SIZEOF_LONG == 4 */ #endif /* SIZEOF_INT == 4 */ #endif /* __S32_TYPEDEF */ #ifdef __U64_TYPEDEF typedef __U64_TYPEDEF __u64; #else #if (@SIZEOF_INT@ == 8) typedef unsigned int __u64; #else #if (@SIZEOF_LONG@ == 8) typedef unsigned long __u64; #else #if (@SIZEOF_LONG_LONG@ == 8) typedef unsigned long long __u64; #endif /* SIZEOF_LONG_LONG == 8 */ #endif /* SIZEOF_LONG == 8 */ #endif /* SIZEOF_INT == 8 */ #endif /* __U64_TYPEDEF */ #ifdef __S64_TYPEDEF typedef __S64_TYPEDEF __s64; #else #if (@SIZEOF_INT@ == 8) typedef int __s64; #else #if (@SIZEOF_LONG@ == 8) typedef long __s64; #else #if (@SIZEOF_LONG_LONG@ == 8) #if defined(__GNUC__) typedef __signed__ long long __s64; #else typedef signed long long __s64; #endif /* __GNUC__ */ #endif /* SIZEOF_LONG_LONG == 8 */ #endif /* SIZEOF_LONG == 8 */ #endif /* SIZEOF_INT == 8 */ #endif /* __S64_TYPEDEF */ #undef __S8_TYPEDEF #undef __U8_TYPEDEF #undef __S16_TYPEDEF #undef __U16_TYPEDEF #undef __S32_TYPEDEF #undef __U32_TYPEDEF #undef __S64_TYPEDEF #undef __U64_TYPEDEF #endif /* _*_TYPES_H */ e2fsprogs-1.41.14/lib/blkid/probe.h0000644031104000366760000004721711240667355014771 0ustar tytso/* * probe.h - constants and on-disk structures for extracting device data * * Copyright (C) 1999 by Andries Brouwer * Copyright (C) 1999, 2000, 2003 by Theodore Ts'o * Copyright (C) 2001 by Andreas Dilger * * %Begin-Header% * This file may be redistributed under the terms of the * GNU Lesser General Public License. * %End-Header% */ #ifndef _BLKID_PROBE_H #define _BLKID_PROBE_H #include struct blkid_magic; #define SB_BUFFER_SIZE 0x11000 struct blkid_probe { int fd; blkid_cache cache; blkid_dev dev; unsigned char *sbbuf; size_t sb_valid; unsigned char *buf; size_t buf_max; }; typedef int (*blkid_probe_t)(struct blkid_probe *probe, struct blkid_magic *id, unsigned char *buf); struct blkid_magic { const char *bim_type; /* type name for this magic */ long bim_kboff; /* kilobyte offset of superblock */ unsigned bim_sboff; /* byte offset within superblock */ unsigned bim_len; /* length of magic */ const char *bim_magic; /* magic string */ blkid_probe_t bim_probe; /* probe function */ }; /* * Structures for each of the content types we want to extract information * from. We do not necessarily need the magic field here, because we have * already identified the content type before we get this far. It may still * be useful if there are probe functions which handle multiple content types. */ struct ext2_super_block { __u32 s_inodes_count; __u32 s_blocks_count; __u32 s_r_blocks_count; __u32 s_free_blocks_count; __u32 s_free_inodes_count; __u32 s_first_data_block; __u32 s_log_block_size; __u32 s_dummy3[7]; unsigned char s_magic[2]; __u16 s_state; __u32 s_dummy5[8]; __u32 s_feature_compat; __u32 s_feature_incompat; __u32 s_feature_ro_compat; unsigned char s_uuid[16]; char s_volume_name[16]; char s_last_mounted[64]; __u32 s_algorithm_usage_bitmap; __u8 s_prealloc_blocks; __u8 s_prealloc_dir_blocks; __u16 s_reserved_gdt_blocks; __u8 s_journal_uuid[16]; __u32 s_journal_inum; __u32 s_journal_dev; __u32 s_last_orphan; __u32 s_hash_seed[4]; __u8 s_def_hash_version; __u8 s_jnl_backup_type; __u16 s_reserved_word_pad; __u32 s_default_mount_opts; __u32 s_first_meta_bg; __u32 s_mkfs_time; __u32 s_jnl_blocks[17]; __u32 s_blocks_count_hi; __u32 s_r_blocks_count_hi; __u32 s_free_blocks_hi; __u16 s_min_extra_isize; __u16 s_want_extra_isize; __u32 s_flags; __u16 s_raid_stride; __u16 s_mmp_interval; __u64 s_mmp_block; __u32 s_raid_stripe_width; __u32 s_reserved[163]; }; /* for s_flags */ #define EXT2_FLAGS_TEST_FILESYS 0x0004 /* for s_feature_compat */ #define EXT3_FEATURE_COMPAT_HAS_JOURNAL 0x0004 /* for s_feature_ro_compat */ #define EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER 0x0001 #define EXT2_FEATURE_RO_COMPAT_LARGE_FILE 0x0002 #define EXT2_FEATURE_RO_COMPAT_BTREE_DIR 0x0004 #define EXT4_FEATURE_RO_COMPAT_HUGE_FILE 0x0008 #define EXT4_FEATURE_RO_COMPAT_GDT_CSUM 0x0010 #define EXT4_FEATURE_RO_COMPAT_DIR_NLINK 0x0020 #define EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE 0x0040 /* for s_feature_incompat */ #define EXT2_FEATURE_INCOMPAT_FILETYPE 0x0002 #define EXT3_FEATURE_INCOMPAT_RECOVER 0x0004 #define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV 0x0008 #define EXT2_FEATURE_INCOMPAT_META_BG 0x0010 #define EXT4_FEATURE_INCOMPAT_EXTENTS 0x0040 /* extents support */ #define EXT4_FEATURE_INCOMPAT_64BIT 0x0080 #define EXT4_FEATURE_INCOMPAT_MMP 0x0100 #define EXT4_FEATURE_INCOMPAT_FLEX_BG 0x0200 #define EXT2_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \ EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \ EXT2_FEATURE_RO_COMPAT_BTREE_DIR) #define EXT2_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE| \ EXT2_FEATURE_INCOMPAT_META_BG) #define EXT2_FEATURE_INCOMPAT_UNSUPPORTED ~EXT2_FEATURE_INCOMPAT_SUPP #define EXT2_FEATURE_RO_COMPAT_UNSUPPORTED ~EXT2_FEATURE_RO_COMPAT_SUPP #define EXT3_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \ EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \ EXT2_FEATURE_RO_COMPAT_BTREE_DIR) #define EXT3_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE| \ EXT3_FEATURE_INCOMPAT_RECOVER| \ EXT2_FEATURE_INCOMPAT_META_BG) #define EXT3_FEATURE_INCOMPAT_UNSUPPORTED ~EXT3_FEATURE_INCOMPAT_SUPP #define EXT3_FEATURE_RO_COMPAT_UNSUPPORTED ~EXT3_FEATURE_RO_COMPAT_SUPP struct xfs_super_block { unsigned char xs_magic[4]; __u32 xs_blocksize; __u64 xs_dblocks; __u64 xs_rblocks; __u32 xs_dummy1[2]; unsigned char xs_uuid[16]; __u32 xs_dummy2[15]; char xs_fname[12]; __u32 xs_dummy3[2]; __u64 xs_icount; __u64 xs_ifree; __u64 xs_fdblocks; }; struct reiserfs_super_block { __u32 rs_blocks_count; __u32 rs_free_blocks; __u32 rs_root_block; __u32 rs_journal_block; __u32 rs_journal_dev; __u32 rs_orig_journal_size; __u32 rs_dummy2[5]; __u16 rs_blocksize; __u16 rs_dummy3[3]; unsigned char rs_magic[12]; __u32 rs_dummy4[5]; unsigned char rs_uuid[16]; char rs_label[16]; }; struct reiser4_super_block { unsigned char rs4_magic[16]; __u16 rs4_dummy[2]; unsigned char rs4_uuid[16]; unsigned char rs4_label[16]; __u64 rs4_dummy2; }; struct jfs_super_block { unsigned char js_magic[4]; __u32 js_version; __u64 js_size; __u32 js_bsize; /* 4: aggregate block size in bytes */ __u16 js_l2bsize; /* 2: log2 of s_bsize */ __u16 js_l2bfactor; /* 2: log2(s_bsize/hardware block size) */ __u32 js_pbsize; /* 4: hardware/LVM block size in bytes */ __u16 js_l2pbsize; /* 2: log2 of s_pbsize */ __u16 js_pad; /* 2: padding necessary for alignment */ __u32 js_dummy2[26]; unsigned char js_uuid[16]; unsigned char js_label[16]; unsigned char js_loguuid[16]; }; struct romfs_super_block { unsigned char ros_magic[8]; __u32 ros_dummy1[2]; unsigned char ros_volume[16]; }; struct cramfs_super_block { __u8 magic[4]; __u32 size; __u32 flags; __u32 future; __u8 signature[16]; struct cramfs_info { __u32 crc; __u32 edition; __u32 blocks; __u32 files; } info; __u8 name[16]; }; struct swap_id_block { /* unsigned char sws_boot[1024]; */ __u32 sws_version; __u32 sws_lastpage; __u32 sws_nrbad; unsigned char sws_uuid[16]; char sws_volume[16]; unsigned char sws_pad[117]; __u32 sws_badpg; }; /* Yucky misaligned values */ struct vfat_super_block { /* 00*/ unsigned char vs_ignored[3]; /* 03*/ unsigned char vs_sysid[8]; /* 0b*/ unsigned char vs_sector_size[2]; /* 0d*/ __u8 vs_cluster_size; /* 0e*/ __u16 vs_reserved; /* 10*/ __u8 vs_fats; /* 11*/ unsigned char vs_dir_entries[2]; /* 13*/ unsigned char vs_sectors[2]; /* 15*/ unsigned char vs_media; /* 16*/ __u16 vs_fat_length; /* 18*/ __u16 vs_secs_track; /* 1a*/ __u16 vs_heads; /* 1c*/ __u32 vs_hidden; /* 20*/ __u32 vs_total_sect; /* 24*/ __u32 vs_fat32_length; /* 28*/ __u16 vs_flags; /* 2a*/ __u8 vs_version[2]; /* 2c*/ __u32 vs_root_cluster; /* 30*/ __u16 vs_insfo_sector; /* 32*/ __u16 vs_backup_boot; /* 34*/ __u16 vs_reserved2[6]; /* 40*/ unsigned char vs_unknown[3]; /* 43*/ unsigned char vs_serno[4]; /* 47*/ unsigned char vs_label[11]; /* 52*/ unsigned char vs_magic[8]; /* 5a*/ unsigned char vs_dummy2[164]; /*1fe*/ unsigned char vs_pmagic[2]; }; /* Yucky misaligned values */ struct msdos_super_block { /* 00*/ unsigned char ms_ignored[3]; /* 03*/ unsigned char ms_sysid[8]; /* 0b*/ unsigned char ms_sector_size[2]; /* 0d*/ __u8 ms_cluster_size; /* 0e*/ __u16 ms_reserved; /* 10*/ __u8 ms_fats; /* 11*/ unsigned char ms_dir_entries[2]; /* 13*/ unsigned char ms_sectors[2]; /* 15*/ unsigned char ms_media; /* 16*/ __u16 ms_fat_length; /* 18*/ __u16 ms_secs_track; /* 1a*/ __u16 ms_heads; /* 1c*/ __u32 ms_hidden; /* 20*/ __u32 ms_total_sect; /* 24*/ unsigned char ms_unknown[3]; /* 27*/ unsigned char ms_serno[4]; /* 2b*/ unsigned char ms_label[11]; /* 36*/ unsigned char ms_magic[8]; /* 3d*/ unsigned char ms_dummy2[192]; /*1fe*/ unsigned char ms_pmagic[2]; }; struct vfat_dir_entry { __u8 name[11]; __u8 attr; __u16 time_creat; __u16 date_creat; __u16 time_acc; __u16 date_acc; __u16 cluster_high; __u16 time_write; __u16 date_write; __u16 cluster_low; __u32 size; }; /* maximum number of clusters */ #define FAT12_MAX 0xFF4 #define FAT16_MAX 0xFFF4 #define FAT32_MAX 0x0FFFFFF6 struct minix_super_block { __u16 ms_ninodes; __u16 ms_nzones; __u16 ms_imap_blocks; __u16 ms_zmap_blocks; __u16 ms_firstdatazone; __u16 ms_log_zone_size; __u32 ms_max_size; unsigned char ms_magic[2]; __u16 ms_state; __u32 ms_zones; }; struct mdp_superblock_s { __u32 md_magic; __u32 major_version; __u32 minor_version; __u32 patch_version; __u32 gvalid_words; __u32 set_uuid0; __u32 ctime; __u32 level; __u32 size; __u32 nr_disks; __u32 raid_disks; __u32 md_minor; __u32 not_persistent; __u32 set_uuid1; __u32 set_uuid2; __u32 set_uuid3; }; struct hfs_super_block { char h_magic[2]; char h_dummy[18]; __u32 h_blksize; }; struct ocfs_volume_header { unsigned char minor_version[4]; unsigned char major_version[4]; unsigned char signature[128]; char mount[128]; unsigned char mount_len[2]; }; struct ocfs_volume_label { unsigned char disk_lock[48]; char label[64]; unsigned char label_len[2]; unsigned char vol_id[16]; unsigned char vol_id_len[2]; }; #define ocfsmajor(o) ((__u32)o.major_version[0] \ + (((__u32) o.major_version[1]) << 8) \ + (((__u32) o.major_version[2]) << 16) \ + (((__u32) o.major_version[3]) << 24)) #define ocfslabellen(o) ((__u32)o.label_len[0] + (((__u32) o.label_len[1]) << 8)) #define ocfsmountlen(o) ((__u32)o.mount_len[0] + (((__u32) o.mount_len[1])<<8)) #define OCFS_MAGIC "OracleCFS" struct ocfs2_super_block { unsigned char signature[8]; unsigned char s_dummy1[184]; unsigned char s_dummy2[80]; char s_label[64]; unsigned char s_uuid[16]; }; #define OCFS2_MIN_BLOCKSIZE 512 #define OCFS2_MAX_BLOCKSIZE 4096 #define OCFS2_SUPER_BLOCK_BLKNO 2 #define OCFS2_SUPER_BLOCK_SIGNATURE "OCFSV2" struct oracle_asm_disk_label { char dummy[32]; char dl_tag[8]; char dl_id[24]; }; #define ORACLE_ASM_DISK_LABEL_MARKED "ORCLDISK" #define ORACLE_ASM_DISK_LABEL_OFFSET 32 struct iso_volume_descriptor { unsigned char vd_type; unsigned char vd_id[5]; unsigned char vd_version; unsigned char flags; unsigned char system_id[32]; unsigned char volume_id[32]; unsigned char unused[8]; unsigned char space_size[8]; unsigned char escape_sequences[8]; }; /* Common gfs/gfs2 constants: */ #define GFS_MAGIC 0x01161970 #define GFS_DEFAULT_BSIZE 4096 #define GFS_SUPERBLOCK_OFFSET (0x10 * GFS_DEFAULT_BSIZE) #define GFS_METATYPE_SB 1 #define GFS_FORMAT_SB 100 #define GFS_LOCKNAME_LEN 64 /* gfs1 constants: */ #define GFS_FORMAT_FS 1309 #define GFS_FORMAT_MULTI 1401 /* gfs2 constants: */ #define GFS2_FORMAT_FS 1801 #define GFS2_FORMAT_MULTI 1900 struct gfs2_meta_header { __u32 mh_magic; __u32 mh_type; __u64 __pad0; /* Was generation number in gfs1 */ __u32 mh_format; __u32 __pad1; /* Was incarnation number in gfs1 */ }; struct gfs2_inum { __u64 no_formal_ino; __u64 no_addr; }; struct gfs2_sb { struct gfs2_meta_header sb_header; __u32 sb_fs_format; __u32 sb_multihost_format; __u32 __pad0; /* Was superblock flags in gfs1 */ __u32 sb_bsize; __u32 sb_bsize_shift; __u32 __pad1; /* Was journal segment size in gfs1 */ struct gfs2_inum sb_master_dir; /* Was jindex dinode in gfs1 */ struct gfs2_inum __pad2; /* Was rindex dinode in gfs1 */ struct gfs2_inum sb_root_dir; char sb_lockproto[GFS_LOCKNAME_LEN]; char sb_locktable[GFS_LOCKNAME_LEN]; /* In gfs1, quota and license dinodes followed */ }; struct ntfs_super_block { __u8 jump[3]; __u8 oem_id[8]; __u8 bios_parameter_block[25]; __u16 unused[2]; __u64 number_of_sectors; __u64 mft_cluster_location; __u64 mft_mirror_cluster_location; __s8 cluster_per_mft_record; __u8 reserved1[3]; __s8 cluster_per_index_record; __u8 reserved2[3]; __u64 volume_serial; __u16 checksum; }; struct master_file_table_record { __u32 magic; __u16 usa_ofs; __u16 usa_count; __u64 lsn; __u16 sequence_number; __u16 link_count; __u16 attrs_offset; __u16 flags; __u32 bytes_in_use; __u32 bytes_allocated; } __attribute__((__packed__)); struct file_attribute { __u32 type; __u32 len; __u8 non_resident; __u8 name_len; __u16 name_offset; __u16 flags; __u16 instance; __u32 value_len; __u16 value_offset; } __attribute__((__packed__)); #define MFT_RECORD_VOLUME 3 #define MFT_RECORD_ATTR_VOLUME_NAME 0x60 #define MFT_RECORD_ATTR_VOLUME_INFO 0x70 #define MFT_RECORD_ATTR_OBJECT_ID 0x40 #define MFT_RECORD_ATTR_END 0xffffffffu /* HFS / HFS+ */ struct hfs_finder_info { __u32 boot_folder; __u32 start_app; __u32 open_folder; __u32 os9_folder; __u32 reserved; __u32 osx_folder; __u8 id[8]; } __attribute__((packed)); struct hfs_mdb { __u8 signature[2]; __u32 cr_date; __u32 ls_Mod; __u16 atrb; __u16 nm_fls; __u16 vbm_st; __u16 alloc_ptr; __u16 nm_al_blks; __u32 al_blk_size; __u32 clp_size; __u16 al_bl_st; __u32 nxt_cnid; __u16 free_bks; __u8 label_len; __u8 label[27]; __u32 vol_bkup; __u16 vol_seq_num; __u32 wr_cnt; __u32 xt_clump_size; __u32 ct_clump_size; __u16 num_root_dirs; __u32 file_count; __u32 dir_count; struct hfs_finder_info finder_info; __u8 embed_sig[2]; __u16 embed_startblock; __u16 embed_blockcount; } __attribute__((packed)); #define HFS_NODE_LEAF 0xff #define HFSPLUS_POR_CNID 1 struct hfsplus_bnode_descriptor { __u32 next; __u32 prev; __u8 type; __u8 height; __u16 num_recs; __u16 reserved; } __attribute__((packed)); struct hfsplus_bheader_record { __u16 depth; __u32 root; __u32 leaf_count; __u32 leaf_head; __u32 leaf_tail; __u16 node_size; } __attribute__((packed)); struct hfsplus_catalog_key { __u16 key_len; __u32 parent_id; __u16 unicode_len; __u8 unicode[255 * 2]; } __attribute__((packed)); struct hfsplus_extent { __u32 start_block; __u32 block_count; } __attribute__((packed)); #define HFSPLUS_EXTENT_COUNT 8 struct hfsplus_fork { __u64 total_size; __u32 clump_size; __u32 total_blocks; struct hfsplus_extent extents[HFSPLUS_EXTENT_COUNT]; } __attribute__((packed)); struct hfsplus_vol_header { __u8 signature[2]; __u16 version; __u32 attributes; __u32 last_mount_vers; __u32 reserved; __u32 create_date; __u32 modify_date; __u32 backup_date; __u32 checked_date; __u32 file_count; __u32 folder_count; __u32 blocksize; __u32 total_blocks; __u32 free_blocks; __u32 next_alloc; __u32 rsrc_clump_sz; __u32 data_clump_sz; __u32 next_cnid; __u32 write_count; __u64 encodings_bmp; struct hfs_finder_info finder_info; struct hfsplus_fork alloc_file; struct hfsplus_fork ext_file; struct hfsplus_fork cat_file; struct hfsplus_fork attr_file; struct hfsplus_fork start_file; } __attribute__((packed)); /* this is lvm's label_header & pv_header combined. */ #define LVM2_ID_LEN 32 struct lvm2_pv_label_header { /* label_header */ __u8 id[8]; /* LABELONE */ __u64 sector_xl; /* Sector number of this label */ __u32 crc_xl; /* From next field to end of sector */ __u32 offset_xl; /* Offset from start of struct to contents */ __u8 type[8]; /* LVM2 001 */ /* pv_header */ __u8 pv_uuid[LVM2_ID_LEN]; } __attribute__ ((packed)); /* * this is a very generous portion of the super block, giving us * room to translate 14 chunks with 3 stripes each. */ #define BTRFS_SYSTEM_CHUNK_ARRAY_SIZE 2048 #define BTRFS_LABEL_SIZE 256 #define BTRFS_UUID_SIZE 16 #define BTRFS_FSID_SIZE 16 #define BTRFS_CSUM_SIZE 32 struct btrfs_dev_item { /* the internal btrfs device id */ __u64 devid; /* size of the device */ __u64 total_bytes; /* bytes used */ __u64 bytes_used; /* optimal io alignment for this device */ __u32 io_align; /* optimal io width for this device */ __u32 io_width; /* minimal io size for this device */ __u32 sector_size; /* type and info about this device */ __u64 type; /* expected generation for this device */ __u64 generation; /* * starting byte of this partition on the device, * to allowr for stripe alignment in the future */ __u64 start_offset; /* grouping information for allocation decisions */ __u32 dev_group; /* seek speed 0-100 where 100 is fastest */ __u8 seek_speed; /* bandwidth 0-100 where 100 is fastest */ __u8 bandwidth; /* btrfs generated uuid for this device */ __u8 uuid[BTRFS_UUID_SIZE]; /* uuid of FS who owns this device */ __u8 fsid[BTRFS_UUID_SIZE]; } __attribute__ ((__packed__)); /* * the super block basically lists the main trees of the FS * it currently lacks any block count etc etc */ struct btrfs_super_block { __u8 csum[BTRFS_CSUM_SIZE]; /* the first 3 fields must match struct btrfs_header */ __u8 fsid[BTRFS_FSID_SIZE]; /* FS specific uuid */ __u64 bytenr; /* this block number */ __u64 flags; /* allowed to be different from the btrfs_header from here own down */ __u64 magic; __u64 generation; __u64 root; __u64 chunk_root; __u64 log_root; /* this will help find the new super based on the log root */ __u64 log_root_transid; __u64 total_bytes; __u64 bytes_used; __u64 root_dir_objectid; __u64 num_devices; __u32 sectorsize; __u32 nodesize; __u32 leafsize; __u32 stripesize; __u32 sys_chunk_array_size; __u64 chunk_root_generation; __u64 compat_flags; __u64 compat_ro_flags; __u64 incompat_flags; __u16 csum_type; __u8 root_level; __u8 chunk_root_level; __u8 log_root_level; struct btrfs_dev_item dev_item; char label[BTRFS_LABEL_SIZE]; /* future expansion */ __u64 reserved[32]; __u8 sys_chunk_array[BTRFS_SYSTEM_CHUNK_ARRAY_SIZE]; } __attribute__ ((__packed__)); /* * Byte swap functions */ #ifdef __GNUC__ #define _INLINE_ static __inline__ #else /* For Watcom C */ #define _INLINE_ static inline #endif static __u16 blkid_swab16(__u16 val); static __u32 blkid_swab32(__u32 val); static __u64 blkid_swab64(__u64 val); #if ((defined __GNUC__) && \ (defined(__i386__) || defined(__i486__) || defined(__i586__))) #define _BLKID_HAVE_ASM_BITOPS_ _INLINE_ __u32 blkid_swab32(__u32 val) { #ifdef EXT2FS_REQUIRE_486 __asm__("bswap %0" : "=r" (val) : "0" (val)); #else __asm__("xchgb %b0,%h0\n\t" /* swap lower bytes */ "rorl $16,%0\n\t" /* swap words */ "xchgb %b0,%h0" /* swap higher bytes */ :"=q" (val) : "0" (val)); #endif return val; } _INLINE_ __u16 blkid_swab16(__u16 val) { __asm__("xchgb %b0,%h0" /* swap bytes */ \ : "=q" (val) \ : "0" (val)); \ return val; } _INLINE_ __u64 blkid_swab64(__u64 val) { return (blkid_swab32(val >> 32) | (((__u64) blkid_swab32(val & 0xFFFFFFFFUL)) << 32)); } #endif #if !defined(_BLKID_HAVE_ASM_BITOPS_) _INLINE_ __u16 blkid_swab16(__u16 val) { return (val >> 8) | (val << 8); } _INLINE_ __u32 blkid_swab32(__u32 val) { return ((val>>24) | ((val>>8)&0xFF00) | ((val<<8)&0xFF0000) | (val<<24)); } _INLINE_ __u64 blkid_swab64(__u64 val) { return (blkid_swab32(val >> 32) | (((__u64) blkid_swab32(val & 0xFFFFFFFFUL)) << 32)); } #endif #ifdef WORDS_BIGENDIAN #define blkid_le16(x) blkid_swab16(x) #define blkid_le32(x) blkid_swab32(x) #define blkid_le64(x) blkid_swab64(x) #define blkid_be16(x) (x) #define blkid_be32(x) (x) #define blkid_be64(x) (x) #else #define blkid_le16(x) (x) #define blkid_le32(x) (x) #define blkid_le64(x) (x) #define blkid_be16(x) blkid_swab16(x) #define blkid_be32(x) blkid_swab32(x) #define blkid_be64(x) blkid_swab64(x) #endif #undef _INLINE_ #endif /* _BLKID_PROBE_H */ e2fsprogs-1.41.14/lib/blkid/blkid.h.in0000644031104000366760000000635711333162126015341 0ustar tytso/* * blkid.h - Interface for libblkid, a library to identify block devices * * Copyright (C) 2001 Andreas Dilger * Copyright (C) 2003 Theodore Ts'o * * %Begin-Header% * This file may be redistributed under the terms of the * GNU Lesser General Public License. * %End-Header% */ #ifndef _BLKID_BLKID_H #define _BLKID_BLKID_H #include #include #ifdef __cplusplus extern "C" { #endif #define BLKID_VERSION "1.0.0" #define BLKID_DATE "12-Feb-2003" typedef struct blkid_struct_dev *blkid_dev; typedef struct blkid_struct_cache *blkid_cache; typedef __s64 blkid_loff_t; typedef struct blkid_struct_tag_iterate *blkid_tag_iterate; typedef struct blkid_struct_dev_iterate *blkid_dev_iterate; /* * Flags for blkid_get_dev * * BLKID_DEV_CREATE Create an empty device structure if not found * in the cache. * BLKID_DEV_VERIFY Make sure the device structure corresponds * with reality. * BLKID_DEV_FIND Just look up a device entry, and return NULL * if it is not found. * BLKID_DEV_NORMAL Get a valid device structure, either from the * cache or by probing the device. */ #define BLKID_DEV_FIND 0x0000 #define BLKID_DEV_CREATE 0x0001 #define BLKID_DEV_VERIFY 0x0002 #define BLKID_DEV_NORMAL (BLKID_DEV_CREATE | BLKID_DEV_VERIFY) /* cache.c */ extern void blkid_put_cache(blkid_cache cache); extern int blkid_get_cache(blkid_cache *cache, const char *filename); extern void blkid_gc_cache(blkid_cache cache); /* dev.c */ extern const char *blkid_dev_devname(blkid_dev dev); extern blkid_dev_iterate blkid_dev_iterate_begin(blkid_cache cache); extern int blkid_dev_set_search(blkid_dev_iterate iter, char *search_type, char *search_value); extern int blkid_dev_next(blkid_dev_iterate iterate, blkid_dev *dev); extern void blkid_dev_iterate_end(blkid_dev_iterate iterate); /* devno.c */ extern char *blkid_devno_to_devname(dev_t devno); /* devname.c */ extern int blkid_probe_all(blkid_cache cache); extern int blkid_probe_all_new(blkid_cache cache); extern blkid_dev blkid_get_dev(blkid_cache cache, const char *devname, int flags); /* getsize.c */ extern blkid_loff_t blkid_get_dev_size(int fd); /* probe.c */ int blkid_known_fstype(const char *fstype); extern blkid_dev blkid_verify(blkid_cache cache, blkid_dev dev); /* read.c */ /* resolve.c */ extern char *blkid_get_tag_value(blkid_cache cache, const char *tagname, const char *devname); extern char *blkid_get_devname(blkid_cache cache, const char *token, const char *value); /* tag.c */ extern blkid_tag_iterate blkid_tag_iterate_begin(blkid_dev dev); extern int blkid_tag_next(blkid_tag_iterate iterate, const char **type, const char **value); extern void blkid_tag_iterate_end(blkid_tag_iterate iterate); extern int blkid_dev_has_tag(blkid_dev dev, const char *type, const char *value); extern blkid_dev blkid_find_dev_with_tag(blkid_cache cache, const char *type, const char *value); extern int blkid_parse_tag_string(const char *token, char **ret_type, char **ret_val); /* version.c */ extern int blkid_parse_version_string(const char *ver_string); extern int blkid_get_library_version(const char **ver_string, const char **date_string); #ifdef __cplusplus } #endif #endif /* _BLKID_BLKID_H */ e2fsprogs-1.41.14/lib/blkid/save.c0000644031104000366760000001020611240667355014577 0ustar tytso/* * save.c - write the cache struct to disk * * Copyright (C) 2001 by Andreas Dilger * Copyright (C) 2003 Theodore Ts'o * * %Begin-Header% * This file may be redistributed under the terms of the * GNU Lesser General Public License. * %End-Header% */ #include #include #include #include #include #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_SYS_MKDEV_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #include "blkidP.h" static int save_dev(blkid_dev dev, FILE *file) { struct list_head *p; if (!dev || dev->bid_name[0] != '/') return 0; DBG(DEBUG_SAVE, printf("device %s, type %s\n", dev->bid_name, dev->bid_type ? dev->bid_type : "(null)")); fprintf(file, "bid_devno, (long) dev->bid_time); if (dev->bid_pri) fprintf(file, " PRI=\"%d\"", dev->bid_pri); list_for_each(p, &dev->bid_tags) { blkid_tag tag = list_entry(p, struct blkid_struct_tag, bit_tags); fprintf(file, " %s=\"%s\"", tag->bit_name,tag->bit_val); } fprintf(file, ">%s\n", dev->bid_name); return 0; } /* * Write out the cache struct to the cache file on disk. */ int blkid_flush_cache(blkid_cache cache) { struct list_head *p; char *tmp = NULL; const char *opened = NULL; const char *filename; FILE *file = NULL; int fd, ret = 0; struct stat st; if (!cache) return -BLKID_ERR_PARAM; if (list_empty(&cache->bic_devs) || !(cache->bic_flags & BLKID_BIC_FL_CHANGED)) { DBG(DEBUG_SAVE, printf("skipping cache file write\n")); return 0; } filename = cache->bic_filename ? cache->bic_filename: BLKID_CACHE_FILE; /* If we can't write to the cache file, then don't even try */ if (((ret = stat(filename, &st)) < 0 && errno != ENOENT) || (ret == 0 && access(filename, W_OK) < 0)) { DBG(DEBUG_SAVE, printf("can't write to cache file %s\n", filename)); return 0; } /* * Try and create a temporary file in the same directory so * that in case of error we don't overwrite the cache file. * If the cache file doesn't yet exist, it isn't a regular * file (e.g. /dev/null or a socket), or we couldn't create * a temporary file then we open it directly. */ if (ret == 0 && S_ISREG(st.st_mode)) { tmp = malloc(strlen(filename) + 8); if (tmp) { sprintf(tmp, "%s-XXXXXX", filename); fd = mkstemp(tmp); if (fd >= 0) { file = fdopen(fd, "w"); opened = tmp; } fchmod(fd, 0644); } } if (!file) { file = fopen(filename, "w"); opened = filename; } DBG(DEBUG_SAVE, printf("writing cache file %s (really %s)\n", filename, opened)); if (!file) { ret = errno; goto errout; } list_for_each(p, &cache->bic_devs) { blkid_dev dev = list_entry(p, struct blkid_struct_dev, bid_devs); if (!dev->bid_type) continue; if ((ret = save_dev(dev, file)) < 0) break; } if (ret >= 0) { cache->bic_flags &= ~BLKID_BIC_FL_CHANGED; ret = 1; } fclose(file); if (opened != filename) { if (ret < 0) { unlink(opened); DBG(DEBUG_SAVE, printf("unlinked temp cache %s\n", opened)); } else { char *backup; backup = malloc(strlen(filename) + 5); if (backup) { sprintf(backup, "%s.old", filename); unlink(backup); link(filename, backup); free(backup); } rename(opened, filename); DBG(DEBUG_SAVE, printf("moved temp cache %s\n", opened)); } } errout: free(tmp); return ret; } #ifdef TEST_PROGRAM int main(int argc, char **argv) { blkid_cache cache = NULL; int ret; blkid_debug_mask = DEBUG_ALL; if (argc > 2) { fprintf(stderr, "Usage: %s [filename]\n" "Test loading/saving a cache (filename)\n", argv[0]); exit(1); } if ((ret = blkid_get_cache(&cache, "/dev/null")) != 0) { fprintf(stderr, "%s: error creating cache (%d)\n", argv[0], ret); exit(1); } if ((ret = blkid_probe_all(cache)) < 0) { fprintf(stderr, "error (%d) probing devices\n", ret); exit(1); } cache->bic_filename = blkid_strdup(argv[1]); if ((ret = blkid_flush_cache(cache)) < 0) { fprintf(stderr, "error (%d) saving cache\n", ret); exit(1); } blkid_put_cache(cache); return ret; } #endif e2fsprogs-1.41.14/lib/blkid/devname.c0000644031104000366760000003267111240667355015272 0ustar tytso/* * devname.c - get a dev by its device inode name * * Copyright (C) Andries Brouwer * Copyright (C) 1999, 2000, 2001, 2002, 2003 Theodore Ts'o * Copyright (C) 2001 Andreas Dilger * * %Begin-Header% * This file may be redistributed under the terms of the * GNU Lesser General Public License. * %End-Header% */ #define _GNU_SOURCE 1 #include #include #include #if HAVE_UNISTD_H #include #endif #include #include #include #if HAVE_SYS_TYPES_H #include #endif #include #if HAVE_SYS_STAT_H #include #endif #if HAVE_ERRNO_H #include #endif #if HAVE_SYS_MKDEV_H #include #endif #include #include "blkidP.h" /* * Find a dev struct in the cache by device name, if available. * * If there is no entry with the specified device name, and the create * flag is set, then create an empty device entry. */ blkid_dev blkid_get_dev(blkid_cache cache, const char *devname, int flags) { blkid_dev dev = NULL, tmp; struct list_head *p, *pnext; if (!cache || !devname) return NULL; list_for_each(p, &cache->bic_devs) { tmp = list_entry(p, struct blkid_struct_dev, bid_devs); if (strcmp(tmp->bid_name, devname)) continue; DBG(DEBUG_DEVNAME, printf("found devname %s in cache\n", tmp->bid_name)); dev = tmp; break; } if (!dev && (flags & BLKID_DEV_CREATE)) { if (access(devname, F_OK) < 0) return NULL; dev = blkid_new_dev(); if (!dev) return NULL; dev->bid_time = INT_MIN; dev->bid_name = blkid_strdup(devname); dev->bid_cache = cache; list_add_tail(&dev->bid_devs, &cache->bic_devs); cache->bic_flags |= BLKID_BIC_FL_CHANGED; } if (flags & BLKID_DEV_VERIFY) { dev = blkid_verify(cache, dev); if (!dev || !(dev->bid_flags & BLKID_BID_FL_VERIFIED)) return dev; /* * If the device is verified, then search the blkid * cache for any entries that match on the type, uuid, * and label, and verify them; if a cache entry can * not be verified, then it's stale and so we remove * it. */ list_for_each_safe(p, pnext, &cache->bic_devs) { blkid_dev dev2; if (!p) break; dev2 = list_entry(p, struct blkid_struct_dev, bid_devs); if (dev2->bid_flags & BLKID_BID_FL_VERIFIED) continue; if (!dev->bid_type || !dev2->bid_type || strcmp(dev->bid_type, dev2->bid_type)) continue; if (dev->bid_label && dev2->bid_label && strcmp(dev->bid_label, dev2->bid_label)) continue; if (dev->bid_uuid && dev2->bid_uuid && strcmp(dev->bid_uuid, dev2->bid_uuid)) continue; if ((dev->bid_label && !dev2->bid_label) || (!dev->bid_label && dev2->bid_label) || (dev->bid_uuid && !dev2->bid_uuid) || (!dev->bid_uuid && dev2->bid_uuid)) continue; dev2 = blkid_verify(cache, dev2); if (dev2 && !(dev2->bid_flags & BLKID_BID_FL_VERIFIED)) blkid_free_dev(dev2); } } return dev; } /* Directories where we will try to search for device names */ static const char *dirlist[] = { "/dev", "/devfs", "/devices", NULL }; static int is_dm_leaf(const char *devname) { struct dirent *de, *d_de; DIR *dir, *d_dir; char path[256]; int ret = 1; if ((dir = opendir("/sys/block")) == NULL) return 0; while ((de = readdir(dir)) != NULL) { if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..") || !strcmp(de->d_name, devname) || strncmp(de->d_name, "dm-", 3) || strlen(de->d_name) > sizeof(path)-32) continue; sprintf(path, "/sys/block/%s/slaves", de->d_name); if ((d_dir = opendir(path)) == NULL) continue; while ((d_de = readdir(d_dir)) != NULL) { if (!strcmp(d_de->d_name, devname)) { ret = 0; break; } } closedir(d_dir); if (!ret) break; } closedir(dir); return ret; } /* * Since 2.6.29 (patch 784aae735d9b0bba3f8b9faef4c8b30df3bf0128) kernel sysfs * provides the real DM device names in /sys/block//dm/name */ static char *get_dm_name(const char *ptname) { FILE *f; size_t sz; char path[256], name[256], *res = NULL; snprintf(path, sizeof(path), "/sys/block/%s/dm/name", ptname); if ((f = fopen(path, "r")) == NULL) return NULL; /* read "\n" from sysfs */ if (fgets(name, sizeof(name), f) && (sz = strlen(name)) > 1) { name[sz - 1] = '\0'; snprintf(path, sizeof(path), "/dev/mapper/%s", name); res = blkid_strdup(path); } fclose(f); return res; } /* * Probe a single block device to add to the device cache. */ static void probe_one(blkid_cache cache, const char *ptname, dev_t devno, int pri, int only_if_new) { blkid_dev dev = NULL; struct list_head *p, *pnext; const char **dir; char *devname = NULL; /* See if we already have this device number in the cache. */ list_for_each_safe(p, pnext, &cache->bic_devs) { blkid_dev tmp = list_entry(p, struct blkid_struct_dev, bid_devs); if (tmp->bid_devno == devno) { if (only_if_new && !access(tmp->bid_name, F_OK)) return; dev = blkid_verify(cache, tmp); if (dev && (dev->bid_flags & BLKID_BID_FL_VERIFIED)) break; dev = 0; } } if (dev && dev->bid_devno == devno) goto set_pri; /* Try to translate private device-mapper dm- names * to standard /dev/mapper/. */ if (!strncmp(ptname, "dm-", 3) && isdigit(ptname[3])) { devname = get_dm_name(ptname); if (!devname) blkid__scan_dir("/dev/mapper", devno, 0, &devname); if (devname) goto get_dev; } /* * Take a quick look at /dev/ptname for the device number. We check * all of the likely device directories. If we don't find it, or if * the stat information doesn't check out, use blkid_devno_to_devname() * to find it via an exhaustive search for the device major/minor. */ for (dir = dirlist; *dir; dir++) { struct stat st; char device[256]; sprintf(device, "%s/%s", *dir, ptname); if ((dev = blkid_get_dev(cache, device, BLKID_DEV_FIND)) && dev->bid_devno == devno) goto set_pri; if (stat(device, &st) == 0 && S_ISBLK(st.st_mode) && st.st_rdev == devno) { devname = blkid_strdup(device); goto get_dev; } } /* Do a short-cut scan of /dev/mapper first */ if (!devname) devname = get_dm_name(ptname); if (!devname) blkid__scan_dir("/dev/mapper", devno, 0, &devname); if (!devname) { devname = blkid_devno_to_devname(devno); if (!devname) return; } get_dev: dev = blkid_get_dev(cache, devname, BLKID_DEV_NORMAL); free(devname); set_pri: if (dev) { if (pri) dev->bid_pri = pri; else if (!strncmp(dev->bid_name, "/dev/mapper/", 11)) { dev->bid_pri = BLKID_PRI_DM; if (is_dm_leaf(ptname)) dev->bid_pri += 5; } else if (!strncmp(ptname, "md", 2)) dev->bid_pri = BLKID_PRI_MD; } return; } #define PROC_PARTITIONS "/proc/partitions" #define VG_DIR "/proc/lvm/VGs" /* * This function initializes the UUID cache with devices from the LVM * proc hierarchy. We currently depend on the names of the LVM * hierarchy giving us the device structure in /dev. (XXX is this a * safe thing to do?) */ #ifdef VG_DIR static dev_t lvm_get_devno(const char *lvm_device) { FILE *lvf; char buf[1024]; int ma, mi; dev_t ret = 0; DBG(DEBUG_DEVNAME, printf("opening %s\n", lvm_device)); if ((lvf = fopen(lvm_device, "r")) == NULL) { DBG(DEBUG_DEVNAME, printf("%s: (%d) %s\n", lvm_device, errno, strerror(errno))); return 0; } while (fgets(buf, sizeof(buf), lvf)) { if (sscanf(buf, "device: %d:%d", &ma, &mi) == 2) { ret = makedev(ma, mi); break; } } fclose(lvf); return ret; } static void lvm_probe_all(blkid_cache cache, int only_if_new) { DIR *vg_list; struct dirent *vg_iter; int vg_len = strlen(VG_DIR); dev_t dev; if ((vg_list = opendir(VG_DIR)) == NULL) return; DBG(DEBUG_DEVNAME, printf("probing LVM devices under %s\n", VG_DIR)); while ((vg_iter = readdir(vg_list)) != NULL) { DIR *lv_list; char *vdirname; char *vg_name; struct dirent *lv_iter; vg_name = vg_iter->d_name; if (!strcmp(vg_name, ".") || !strcmp(vg_name, "..")) continue; vdirname = malloc(vg_len + strlen(vg_name) + 8); if (!vdirname) goto exit; sprintf(vdirname, "%s/%s/LVs", VG_DIR, vg_name); lv_list = opendir(vdirname); free(vdirname); if (lv_list == NULL) continue; while ((lv_iter = readdir(lv_list)) != NULL) { char *lv_name, *lvm_device; lv_name = lv_iter->d_name; if (!strcmp(lv_name, ".") || !strcmp(lv_name, "..")) continue; lvm_device = malloc(vg_len + strlen(vg_name) + strlen(lv_name) + 8); if (!lvm_device) { closedir(lv_list); goto exit; } sprintf(lvm_device, "%s/%s/LVs/%s", VG_DIR, vg_name, lv_name); dev = lvm_get_devno(lvm_device); sprintf(lvm_device, "%s/%s", vg_name, lv_name); DBG(DEBUG_DEVNAME, printf("LVM dev %s: devno 0x%04X\n", lvm_device, (unsigned int) dev)); probe_one(cache, lvm_device, dev, BLKID_PRI_LVM, only_if_new); free(lvm_device); } closedir(lv_list); } exit: closedir(vg_list); } #endif #define PROC_EVMS_VOLUMES "/proc/evms/volumes" static int evms_probe_all(blkid_cache cache, int only_if_new) { char line[100]; int ma, mi, sz, num = 0; FILE *procpt; char device[110]; procpt = fopen(PROC_EVMS_VOLUMES, "r"); if (!procpt) return 0; while (fgets(line, sizeof(line), procpt)) { if (sscanf (line, " %d %d %d %*s %*s %[^\n ]", &ma, &mi, &sz, device) != 4) continue; DBG(DEBUG_DEVNAME, printf("Checking partition %s (%d, %d)\n", device, ma, mi)); probe_one(cache, device, makedev(ma, mi), BLKID_PRI_EVMS, only_if_new); num++; } fclose(procpt); return num; } /* * Read the device data for all available block devices in the system. */ static int probe_all(blkid_cache cache, int only_if_new) { FILE *proc; char line[1024]; char ptname0[128], ptname1[128], *ptname = 0; char *ptnames[2]; dev_t devs[2]; int ma, mi; unsigned long long sz; int lens[2] = { 0, 0 }; int which = 0, last = 0; struct list_head *p, *pnext; ptnames[0] = ptname0; ptnames[1] = ptname1; if (!cache) return -BLKID_ERR_PARAM; if (cache->bic_flags & BLKID_BIC_FL_PROBED && time(0) - cache->bic_time < BLKID_PROBE_INTERVAL) return 0; blkid_read_cache(cache); evms_probe_all(cache, only_if_new); #ifdef VG_DIR lvm_probe_all(cache, only_if_new); #endif proc = fopen(PROC_PARTITIONS, "r"); if (!proc) return -BLKID_ERR_PROC; while (fgets(line, sizeof(line), proc)) { last = which; which ^= 1; ptname = ptnames[which]; if (sscanf(line, " %d %d %llu %128[^\n ]", &ma, &mi, &sz, ptname) != 4) continue; devs[which] = makedev(ma, mi); DBG(DEBUG_DEVNAME, printf("read partition name %s\n", ptname)); /* Skip whole disk devs unless they have no partitions. * If base name of device has changed, also * check previous dev to see if it didn't have a partn. * heuristic: partition name ends in a digit, & partition * names contain whole device name as substring. * * Skip extended partitions. * heuristic: size is 1 * * FIXME: skip /dev/{ida,cciss,rd} whole-disk devs */ lens[which] = strlen(ptname); /* ends in a digit, clearly a partition, so check */ if (isdigit(ptname[lens[which] - 1])) { DBG(DEBUG_DEVNAME, printf("partition dev %s, devno 0x%04X\n", ptname, (unsigned int) devs[which])); if (sz > 1) probe_one(cache, ptname, devs[which], 0, only_if_new); lens[which] = 0; /* mark as checked */ } /* * If last was a whole disk and we just found a partition * on it, remove the whole-disk dev from the cache if * it exists. */ if (lens[last] && !strncmp(ptnames[last], ptname, lens[last])) { list_for_each_safe(p, pnext, &cache->bic_devs) { blkid_dev tmp; /* find blkid dev for the whole-disk devno */ tmp = list_entry(p, struct blkid_struct_dev, bid_devs); if (tmp->bid_devno == devs[last]) { DBG(DEBUG_DEVNAME, printf("freeing %s\n", tmp->bid_name)); blkid_free_dev(tmp); cache->bic_flags |= BLKID_BIC_FL_CHANGED; break; } } lens[last] = 0; } /* * If last was not checked because it looked like a whole-disk * dev, and the device's base name has changed, * check last as well. */ if (lens[last] && strncmp(ptnames[last], ptname, lens[last])) { DBG(DEBUG_DEVNAME, printf("whole dev %s, devno 0x%04X\n", ptnames[last], (unsigned int) devs[last])); probe_one(cache, ptnames[last], devs[last], 0, only_if_new); lens[last] = 0; } } /* Handle the last device if it wasn't partitioned */ if (lens[which]) probe_one(cache, ptname, devs[which], 0, only_if_new); fclose(proc); blkid_flush_cache(cache); return 0; } int blkid_probe_all(blkid_cache cache) { int ret; DBG(DEBUG_PROBE, printf("Begin blkid_probe_all()\n")); ret = probe_all(cache, 0); cache->bic_time = time(0); cache->bic_flags |= BLKID_BIC_FL_PROBED; DBG(DEBUG_PROBE, printf("End blkid_probe_all()\n")); return ret; } int blkid_probe_all_new(blkid_cache cache) { int ret; DBG(DEBUG_PROBE, printf("Begin blkid_probe_all_new()\n")); ret = probe_all(cache, 1); DBG(DEBUG_PROBE, printf("End blkid_probe_all_new()\n")); return ret; } #ifdef TEST_PROGRAM int main(int argc, char **argv) { blkid_cache cache = NULL; int ret; blkid_debug_mask = DEBUG_ALL; if (argc != 1) { fprintf(stderr, "Usage: %s\n" "Probe all devices and exit\n", argv[0]); exit(1); } if ((ret = blkid_get_cache(&cache, "/dev/null")) != 0) { fprintf(stderr, "%s: error creating cache (%d)\n", argv[0], ret); exit(1); } if (blkid_probe_all(cache) < 0) printf("%s: error probing devices\n", argv[0]); blkid_put_cache(cache); return (0); } #endif e2fsprogs-1.41.14/lib/blkid/dev.c0000644031104000366760000001271011240667355014421 0ustar tytso/* * dev.c - allocation/initialization/free routines for dev * * Copyright (C) 2001 Andreas Dilger * Copyright (C) 2003 Theodore Ts'o * * %Begin-Header% * This file may be redistributed under the terms of the * GNU Lesser General Public License. * %End-Header% */ #include #include #include "blkidP.h" blkid_dev blkid_new_dev(void) { blkid_dev dev; if (!(dev = (blkid_dev) calloc(1, sizeof(struct blkid_struct_dev)))) return NULL; INIT_LIST_HEAD(&dev->bid_devs); INIT_LIST_HEAD(&dev->bid_tags); return dev; } void blkid_free_dev(blkid_dev dev) { if (!dev) return; DBG(DEBUG_DEV, printf(" freeing dev %s (%s)\n", dev->bid_name, dev->bid_type ? dev->bid_type : "(null)")); DBG(DEBUG_DEV, blkid_debug_dump_dev(dev)); list_del(&dev->bid_devs); while (!list_empty(&dev->bid_tags)) { blkid_tag tag = list_entry(dev->bid_tags.next, struct blkid_struct_tag, bit_tags); blkid_free_tag(tag); } free(dev->bid_name); free(dev); } /* * Given a blkid device, return its name */ extern const char *blkid_dev_devname(blkid_dev dev) { return dev->bid_name; } #ifdef CONFIG_BLKID_DEBUG void blkid_debug_dump_dev(blkid_dev dev) { struct list_head *p; if (!dev) { printf(" dev: NULL\n"); return; } printf(" dev: name = %s\n", dev->bid_name); printf(" dev: DEVNO=\"0x%0llx\"\n", (long long)dev->bid_devno); printf(" dev: TIME=\"%ld\"\n", (long)dev->bid_time); printf(" dev: PRI=\"%d\"\n", dev->bid_pri); printf(" dev: flags = 0x%08X\n", dev->bid_flags); list_for_each(p, &dev->bid_tags) { blkid_tag tag = list_entry(p, struct blkid_struct_tag, bit_tags); if (tag) printf(" tag: %s=\"%s\"\n", tag->bit_name, tag->bit_val); else printf(" tag: NULL\n"); } printf("\n"); } #endif /* * dev iteration routines for the public libblkid interface. * * These routines do not expose the list.h implementation, which are a * contamination of the namespace, and which force us to reveal far, far * too much of our internal implemenation. I'm not convinced I want * to keep list.h in the long term, anyway. It's fine for kernel * programming, but performance is not the #1 priority for this * library, and I really don't like the tradeoff of type-safety for * performance for this application. [tytso:20030125.2007EST] */ /* * This series of functions iterate over all devices in a blkid cache */ #define DEV_ITERATE_MAGIC 0x01a5284c struct blkid_struct_dev_iterate { int magic; blkid_cache cache; char *search_type; char *search_value; struct list_head *p; }; extern blkid_dev_iterate blkid_dev_iterate_begin(blkid_cache cache) { blkid_dev_iterate iter; iter = malloc(sizeof(struct blkid_struct_dev_iterate)); if (iter) { iter->magic = DEV_ITERATE_MAGIC; iter->cache = cache; iter->p = cache->bic_devs.next; iter->search_type = 0; iter->search_value = 0; } return (iter); } extern int blkid_dev_set_search(blkid_dev_iterate iter, char *search_type, char *search_value) { char *new_type, *new_value; if (!iter || iter->magic != DEV_ITERATE_MAGIC || !search_type || !search_value) return -1; new_type = malloc(strlen(search_type)+1); new_value = malloc(strlen(search_value)+1); if (!new_type || !new_value) { free(new_type); free(new_value); return -1; } strcpy(new_type, search_type); strcpy(new_value, search_value); free(iter->search_type); free(iter->search_value); iter->search_type = new_type; iter->search_value = new_value; return 0; } /* * Return 0 on success, -1 on error */ extern int blkid_dev_next(blkid_dev_iterate iter, blkid_dev *ret_dev) { blkid_dev dev; *ret_dev = 0; if (!iter || iter->magic != DEV_ITERATE_MAGIC) return -1; while (iter->p != &iter->cache->bic_devs) { dev = list_entry(iter->p, struct blkid_struct_dev, bid_devs); iter->p = iter->p->next; if (iter->search_type && !blkid_dev_has_tag(dev, iter->search_type, iter->search_value)) continue; *ret_dev = dev; return 0; } return -1; } extern void blkid_dev_iterate_end(blkid_dev_iterate iter) { if (!iter || iter->magic != DEV_ITERATE_MAGIC) return; iter->magic = 0; free(iter); } #ifdef TEST_PROGRAM #ifdef HAVE_GETOPT_H #include #else extern char *optarg; extern int optind; #endif void usage(char *prog) { fprintf(stderr, "Usage: %s [-f blkid_file] [-m debug_mask]\n", prog); fprintf(stderr, "\tList all devices and exit\n"); exit(1); } int main(int argc, char **argv) { blkid_dev_iterate iter; blkid_cache cache = NULL; blkid_dev dev; int c, ret; char *tmp; char *file = NULL; char *search_type = NULL; char *search_value = NULL; while ((c = getopt (argc, argv, "m:f:")) != EOF) switch (c) { case 'f': file = optarg; break; case 'm': blkid_debug_mask = strtoul (optarg, &tmp, 0); if (*tmp) { fprintf(stderr, "Invalid debug mask: %s\n", optarg); exit(1); } break; case '?': usage(argv[0]); } if (argc >= optind+2) { search_type = argv[optind]; search_value = argv[optind+1]; optind += 2; } if (argc != optind) usage(argv[0]); if ((ret = blkid_get_cache(&cache, file)) != 0) { fprintf(stderr, "%s: error creating cache (%d)\n", argv[0], ret); exit(1); } iter = blkid_dev_iterate_begin(cache); if (search_type) blkid_dev_set_search(iter, search_type, search_value); while (blkid_dev_next(iter, &dev) == 0) { printf("Device: %s\n", blkid_dev_devname(dev)); } blkid_dev_iterate_end(iter); blkid_put_cache(cache); return (0); } #endif e2fsprogs-1.41.14/lib/blkid/Makefile.in0000644031104000366760000001475411333162126015543 0ustar tytso# Makefile for libblkid # # Copyright (C) 2001 Theodore Ts'o (tytso@mit.edu) # # This file can be redistributed under the terms of the # GNU Lesser General Public License srcdir = @srcdir@ top_srcdir = @top_srcdir@ VPATH = @srcdir@ top_builddir = ../.. my_dir = lib/blkid INSTALL = @INSTALL@ @MCONFIG@ all:: SMANPAGES= libblkid.3 OBJS= cache.o dev.o devname.o devno.o getsize.o llseek.o probe.o \ read.o resolve.o save.o tag.o version.o SRCS= $(srcdir)/cache.c $(srcdir)/dev.c $(srcdir)/devname.c $(srcdir)/devno.c \ $(srcdir)/getsize.c $(srcdir)/llseek.c $(srcdir)/probe.c \ $(srcdir)/read.c $(srcdir)/resolve.c $(srcdir)/save.c $(srcdir)/tag.c \ $(srcdir)/version.c HFILES_IN= blkid.h blkid_types.h LIBRARY= libblkid LIBDIR= blkid ELF_VERSION = 1.0 ELF_SO_VERSION = 1 ELF_IMAGE = libblkid ELF_MYDIR = blkid ELF_INSTALL_DIR = $(root_libdir) ELF_OTHER_LIBS = -L../.. -luuid BSDLIB_VERSION = 2.0 BSDLIB_IMAGE = libblkid BSDLIB_MYDIR = blkid BSDLIB_INSTALL_DIR = $(root_libdir) @MAKEFILE_LIBRARY@ @MAKEFILE_ELF@ @MAKEFILE_BSDLIB@ @MAKEFILE_PROFILE@ @MAKEFILE_CHECKER@ LIBS_BLKID= $(STATIC_LIBBLKID) $(STATIC_LIBUUID) DEPLIBS_BLKID= $(DEPSTATIC_LIBBLKID) $(DEPSTATIC_LIBUUID) .c.o: $(E) " CC $<" $(Q) $(CC) $(ALL_CFLAGS) -c $< -o $@ @PROFILE_CMT@ $(Q) $(CC) $(ALL_CFLAGS) -g -pg -o profiled/$*.o -c $< @CHECKER_CMT@ $(Q) $(CC) $(ALL_CFLAGS) -checker -g -o checker/$*.o -c $< @ELF_CMT@ $(Q) $(CC) $(ALL_CFLAGS) -fPIC -o elfshared/$*.o -c $< @BSDLIB_CMT@ $(Q) $(CC) $(ALL_CFLAGS) $(BSDLIB_PIC_FLAG) -o pic/$*.o -c $< all:: $(SMANPAGES) blkid.pc blkid_types.h: $(srcdir)/blkid_types.h.in $(top_builddir)/config.status $(E) " CONFIG.STATUS $@" $(Q) cd $(top_builddir); \ CONFIG_FILES=$(my_dir)/blkid_types.h ./config.status blkid.h: $(srcdir)/blkid.h.in $(E) " CP $@" $(Q) cp $(srcdir)/blkid.h.in blkid.h libblkid.3: $(DEP_SUBSTITUTE) $(srcdir)/libblkid.3.in $(E) " SUBST $@" $(Q) $(SUBSTITUTE_UPTIME) $(srcdir)/libblkid.3.in libblkid.3 tst_cache: $(srcdir)/cache.c $(DEPLIBS_BLKID) $(E) " LD $@" $(Q) $(CC) -o tst_cache -DTEST_PROGRAM $(srcdir)/cache.c $(LIBS_BLKID) $(ALL_CFLAGS) tst_dev: $(srcdir)/dev.c $(DEPLIBS_BLKID) $(E) " LD $@" $(Q) $(CC) -o tst_dev -DTEST_PROGRAM $(srcdir)/dev.c $(LIBS_BLKID) $(ALL_CFLAGS) tst_devname: $(srcdir)/devname.c $(DEPLIBS_BLKID) $(E) " LD $@" $(Q) $(CC) -o tst_devname -DTEST_PROGRAM $(srcdir)/devname.c $(LIBS_BLKID) $(ALL_CFLAGS) tst_devno: $(srcdir)/devno.c $(DEPLIBS_BLKID) $(E) " LD $@" $(Q) $(CC) -o tst_devno -DTEST_PROGRAM $(srcdir)/devno.c $(LIBS_BLKID) $(ALL_CFLAGS) tst_getsize: $(srcdir)/getsize.c $(DEPLIBS_BLKID) $(E) " LD $@" $(Q) $(CC) -o tst_getsize -DTEST_PROGRAM $(srcdir)/getsize.c $(LIBS_BLKID) $(ALL_CFLAGS) tst_probe: $(srcdir)/probe.c $(DEPLIBS_BLKID) $(E) " LD $@" $(Q) $(CC) -o tst_probe -DTEST_PROGRAM $(srcdir)/probe.c $(LIBS_BLKID) $(ALL_CFLAGS) tst_read: $(srcdir)/read.c $(DEPLIBS_BLKID) $(E) " LD $@" $(Q) $(CC) -o tst_read -DTEST_PROGRAM $(srcdir)/read.c $(LIBS_BLKID) $(ALL_CFLAGS) tst_resolve: $(srcdir)/resolve.c $(DEPLIBS_BLKID) $(E) " LD $@" $(Q) $(CC) -o tst_resolve -DTEST_PROGRAM $(srcdir)/resolve.c $(LIBS_BLKID) $(ALL_CFLAGS) tst_save: $(srcdir)/save.c $(DEPLIBS_BLKID) $(E) " LD $@" $(Q) $(CC) -o tst_save -DTEST_PROGRAM $(srcdir)/save.c $(LIBS_BLKID) $(ALL_CFLAGS) tst_tag: $(srcdir)/tag.c $(DEPLIBS_BLKID) $(E) " LD $@" $(Q) $(CC) -o tst_tag -DTEST_PROGRAM $(srcdir)/tag.c $(LIBS_BLKID) $(ALL_CFLAGS) tst_types: tst_types.o blkid_types.h $(E) " LD $@" $(Q) $(CC) -o tst_types tst_types.o ../../misc/blkid.o: $(top_srcdir)/misc/blkid.c blkid.h $(E) " CC $@" $(Q) $(CC) $(ALL_CFLAGS) -c $(top_srcdir)/misc/blkid.c \ -o ../../misc/blkid.o blkid: ../../misc/blkid.o libblkid.a $(DEPLIBUUID) $(E) " LD $@" $(Q) $(CC) -o blkid ../../misc/blkid.o libblkid.a $(LIBUUID) test_probe: test_probe.in Makefile $(E) "Creating test_probe..." $(E) "#!/bin/sh" > test_probe $(E) "SRCDIR=@srcdir@" >> test_probe $(Q) cat $(srcdir)/test_probe.in >> test_probe $(Q) chmod +x test_probe check:: all tst_cache tst_dev tst_devname tst_devno tst_getsize tst_probe \ tst_read tst_resolve tst_save tst_tag test_probe tst_types ./test_probe ./tst_types blkid.pc: $(srcdir)/blkid.pc.in $(top_builddir)/config.status $(E) " CONFIG.STATUS $@" $(Q) cd $(top_builddir); CONFIG_FILES=lib/blkid/blkid.pc ./config.status installdirs:: $(E) " MKINSTALLDIRS $(libdir) $(includedir)/blkid" $(Q) $(MKINSTALLDIRS) $(DESTDIR)$(libdir) \ $(DESTDIR)$(includedir)/blkid $(DESTDIR)$(libdir)/pkgconfig install:: all installdirs $(E) " INSTALL_DATA $(libdir)/libblkid.a" $(Q) $(INSTALL_DATA) libblkid.a $(DESTDIR)$(libdir)/libblkid.a -$(Q) $(RANLIB) $(DESTDIR)$(libdir)/libblkid.a $(Q) $(CHMOD) $(LIBMODE) $(DESTDIR)$(libdir)/libblkid.a $(Q) set -e; for i in $(HFILES_IN); do \ echo " INSTALL_DATA $(includedir)/blkid/$$i"; \ $(INSTALL_DATA) $$i $(DESTDIR)$(includedir)/blkid/$$i; \ done $(Q) for i in $(SMANPAGES); do \ echo " INSTALL_DATA $(man3dir)/$$i"; \ $(INSTALL_DATA) $$i $(DESTDIR)$(man3dir)/$$i; \ done $(E) " INSTALL_DATA $(libdir)/pkgconfig/blkid.pc" $(Q) $(INSTALL_DATA) blkid.pc $(DESTDIR)$(libdir)/pkgconfig/blkid.pc uninstall:: $(RM) -f $(DESTDIR)$(libdir)/libblkid.a \ $(DESTDIR)$(libdir)/pkgconfig/blkid.pc $(RM) -rf $(DESTDIR)$(includedir)/blkid for i in $(SMANPAGES); do \ $(RM) -f $(DESTDIR)$(man3dir)/$$i; \ done clean:: $(RM) -f \#* *.s *.o *.orig *.a *~ *.bak tst_cache tst_dev tst_devname \ tst_devno tst_getsize tst_probe tst_read tst_resolve tst_save \ tst_tag tst_types tests/*.out tests/*.ok \ tests/*.img results test_probe core profiled/* \ checker/* blkid.h blkid_types.h ../libblkid.a ../libblkid_p.a \ $(SMANPAGES) blkid @echo rmdir tests/tmp tests @(rmdir tests/tmp tests 2> /dev/null ; exit 0) mostlyclean:: clean distclean:: clean $(RM) -f .depend Makefile blkid.pc \ $(srcdir)/TAGS $(srcdir)/Makefile.in.old $(OBJS): subdirs $(HFILES_IN) # +++ Dependency line eater +++ # # Makefile dependencies follow. This must be the last section in # the Makefile.in file # cache.o: $(srcdir)/cache.c $(srcdir)/list.h dev.o: $(srcdir)/dev.c $(srcdir)/list.h devname.o: $(srcdir)/devname.c $(srcdir)/list.h devno.o: $(srcdir)/devno.c $(srcdir)/list.h getsize.o: $(srcdir)/getsize.c $(srcdir)/list.h llseek.o: $(srcdir)/llseek.c $(srcdir)/list.h probe.o: $(srcdir)/probe.c $(srcdir)/list.h $(srcdir)/probe.h read.o: $(srcdir)/read.c $(srcdir)/list.h resolve.o: $(srcdir)/resolve.c $(srcdir)/list.h save.o: $(srcdir)/save.c $(srcdir)/list.h tag.o: $(srcdir)/tag.c $(srcdir)/list.h version.o: $(srcdir)/version.c $(top_srcdir)/version.h e2fsprogs-1.41.14/lib/blkid/probe.c0000644031104000366760000013257011251272375014755 0ustar tytso/* * probe.c - identify a block device by its contents, and return a dev * struct with the details * * Copyright (C) 1999 by Andries Brouwer * Copyright (C) 1999, 2000, 2003 by Theodore Ts'o * Copyright (C) 2001 by Andreas Dilger * Copyright (C) 2004 Kay Sievers * * %Begin-Header% * This file may be redistributed under the terms of the * GNU Lesser General Public License. * %End-Header% */ #include #include #include #include #include #include #include #ifdef HAVE_SYS_STAT_H #include #endif #ifdef HAVE_SYS_MKDEV_H #include #endif #ifdef __linux__ #include #endif #ifdef HAVE_ERRNO_H #include #endif #include "blkidP.h" #include "uuid/uuid.h" #include "probe.h" static int figure_label_len(const unsigned char *label, int len) { const unsigned char *end = label + len - 1; while ((*end == ' ' || *end == 0) && end >= label) --end; if (end >= label) { label = label; return end - label + 1; } return 0; } static unsigned char *get_buffer(struct blkid_probe *pr, blkid_loff_t off, size_t len) { ssize_t ret_read; unsigned char *newbuf; if (off + len <= SB_BUFFER_SIZE) { if (!pr->sbbuf) { pr->sbbuf = malloc(SB_BUFFER_SIZE); if (!pr->sbbuf) return NULL; if (lseek(pr->fd, 0, SEEK_SET) < 0) return NULL; ret_read = read(pr->fd, pr->sbbuf, SB_BUFFER_SIZE); if (ret_read < 0) ret_read = 0; pr->sb_valid = ret_read; } if (off+len > pr->sb_valid) return NULL; return pr->sbbuf + off; } else { if (len > pr->buf_max) { newbuf = realloc(pr->buf, len); if (newbuf == NULL) return NULL; pr->buf = newbuf; pr->buf_max = len; } if (blkid_llseek(pr->fd, off, SEEK_SET) < 0) return NULL; ret_read = read(pr->fd, pr->buf, len); if (ret_read != (ssize_t) len) return NULL; return pr->buf; } } /* * This is a special case code to check for an MDRAID device. We do * this special since it requires checking for a superblock at the end * of the device. */ static int check_mdraid(int fd, unsigned char *ret_uuid) { struct mdp_superblock_s *md; blkid_loff_t offset; char buf[4096]; if (fd < 0) return -BLKID_ERR_PARAM; offset = (blkid_get_dev_size(fd) & ~((blkid_loff_t)65535)) - 65536; if (blkid_llseek(fd, offset, 0) < 0 || read(fd, buf, 4096) != 4096) return -BLKID_ERR_IO; /* Check for magic number */ if (memcmp("\251+N\374", buf, 4) && memcmp("\374N+\251", buf, 4)) return -BLKID_ERR_PARAM; if (!ret_uuid) return 0; *ret_uuid = 0; /* The MD UUID is not contiguous in the superblock, make it so */ md = (struct mdp_superblock_s *)buf; if (md->set_uuid0 || md->set_uuid1 || md->set_uuid2 || md->set_uuid3) { memcpy(ret_uuid, &md->set_uuid0, 4); memcpy(ret_uuid + 4, &md->set_uuid1, 12); } return 0; } static void set_uuid(blkid_dev dev, uuid_t uuid, const char *tag) { char str[37]; if (!uuid_is_null(uuid)) { uuid_unparse(uuid, str); blkid_set_tag(dev, tag ? tag : "UUID", str, sizeof(str)); } } static void get_ext2_info(blkid_dev dev, struct blkid_magic *id, unsigned char *buf) { struct ext2_super_block *es = (struct ext2_super_block *) buf; const char *label = 0; DBG(DEBUG_PROBE, printf("ext2_sb.compat = %08X:%08X:%08X\n", blkid_le32(es->s_feature_compat), blkid_le32(es->s_feature_incompat), blkid_le32(es->s_feature_ro_compat))); if (strlen(es->s_volume_name)) label = es->s_volume_name; blkid_set_tag(dev, "LABEL", label, sizeof(es->s_volume_name)); set_uuid(dev, es->s_uuid, 0); if ((es->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL) && !uuid_is_null(es->s_journal_uuid)) set_uuid(dev, es->s_journal_uuid, "EXT_JOURNAL"); if (strcmp(id->bim_type, "ext2") && ((blkid_le32(es->s_feature_incompat) & EXT2_FEATURE_INCOMPAT_UNSUPPORTED) == 0)) blkid_set_tag(dev, "SEC_TYPE", "ext2", sizeof("ext2")); } /* * Check to see if a filesystem is in /proc/filesystems. * Returns 1 if found, 0 if not */ static int fs_proc_check(const char *fs_name) { FILE *f; char buf[80], *cp, *t; f = fopen("/proc/filesystems", "r"); if (!f) return (0); while (!feof(f)) { if (!fgets(buf, sizeof(buf), f)) break; cp = buf; if (!isspace(*cp)) { while (*cp && !isspace(*cp)) cp++; } while (*cp && isspace(*cp)) cp++; if ((t = strchr(cp, '\n')) != NULL) *t = 0; if ((t = strchr(cp, '\t')) != NULL) *t = 0; if ((t = strchr(cp, ' ')) != NULL) *t = 0; if (!strcmp(fs_name, cp)) { fclose(f); return (1); } } fclose(f); return (0); } /* * Check to see if a filesystem is available as a module * Returns 1 if found, 0 if not */ static int check_for_modules(const char *fs_name) { #ifdef __linux__ struct utsname uts; FILE *f; char buf[1024], *cp; int namesz; if (uname(&uts)) return (0); snprintf(buf, sizeof(buf), "/lib/modules/%s/modules.dep", uts.release); f = fopen(buf, "r"); if (!f) return (0); namesz = strlen(fs_name); while (!feof(f)) { if (!fgets(buf, sizeof(buf), f)) break; if ((cp = strchr(buf, ':')) != NULL) *cp = 0; else continue; if ((cp = strrchr(buf, '/')) != NULL) cp++; else cp = buf; if (!strncmp(cp, fs_name, namesz) && (!strcmp(cp + namesz, ".ko") || !strcmp(cp + namesz, ".ko.gz"))) { fclose(f); return (1); } } fclose(f); #endif return (0); } static int linux_version_code() { #ifdef __linux__ struct utsname ut; static version_code = -1; int major, minor, rev; char *endptr; const char *cp; if (version_code > 0) return version_code; if (uname(&ut)) return 0; cp = ut.release; major = strtol(cp, &endptr, 10); if (cp == endptr || *endptr != '.') return 0; cp = endptr + 1; minor = strtol(cp, &endptr, 10); if (cp == endptr || *endptr != '.') return 0; cp = endptr + 1; rev = strtol(cp, &endptr, 10); if (cp == endptr) return 0; version_code = (((major * 256) + minor) * 256) + rev; return version_code; #else return 0; #endif } #define EXT4_SUPPORTS_EXT2 (2 * 65536 + 6*256 + 29) static int system_supports_ext2(void) { static time_t last_check = 0; static int ret = -1; time_t now = time(0); if (ret != -1 || (now - last_check) < 5) return ret; last_check = now; ret = (fs_proc_check("ext2") || check_for_modules("ext2")); return ret; } static int system_supports_ext4(void) { static time_t last_check = 0; static int ret = -1; time_t now = time(0); if (ret != -1 || (now - last_check) < 5) return ret; last_check = now; ret = (fs_proc_check("ext4") || check_for_modules("ext4")); return ret; } static int system_supports_ext4dev(void) { static time_t last_check = 0; static int ret = -1; time_t now = time(0); if (ret != -1 || (now - last_check) < 5) return ret; last_check = now; ret = (fs_proc_check("ext4dev") || check_for_modules("ext4dev")); return ret; } static int probe_ext4dev(struct blkid_probe *probe, struct blkid_magic *id, unsigned char *buf) { struct ext2_super_block *es; es = (struct ext2_super_block *)buf; /* Distinguish from jbd */ if (blkid_le32(es->s_feature_incompat) & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) return -BLKID_ERR_PARAM; /* * If the filesystem does not have a journal and ext2 and ext4 * is not present, then force this to be detected as an * ext4dev filesystem. */ if (!(blkid_le32(es->s_feature_compat) & EXT3_FEATURE_COMPAT_HAS_JOURNAL) && !system_supports_ext2() && !system_supports_ext4() && system_supports_ext4dev() && linux_version_code() >= EXT4_SUPPORTS_EXT2) goto force_ext4dev; /* * If the filesystem is marked as OK for use by in-development * filesystem code, but ext4dev is not supported, and ext4 is, * then don't call ourselves ext4dev, since we should be * detected as ext4 in that case. * * If the filesystem is marked as in use by production * filesystem, then it can only be used by ext4 and NOT by * ext4dev, so always disclaim we are ext4dev in that case. */ if (blkid_le32(es->s_flags) & EXT2_FLAGS_TEST_FILESYS) { if (!system_supports_ext4dev() && system_supports_ext4()) return -BLKID_ERR_PARAM; } else return -BLKID_ERR_PARAM; force_ext4dev: get_ext2_info(probe->dev, id, buf); return 0; } static int probe_ext4(struct blkid_probe *probe, struct blkid_magic *id, unsigned char *buf) { struct ext2_super_block *es; es = (struct ext2_super_block *)buf; /* Distinguish from jbd */ if (blkid_le32(es->s_feature_incompat) & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) return -BLKID_ERR_PARAM; /* * If the filesystem does not have a journal and ext2 is not * present, then force this to be detected as an ext2 * filesystem. */ if (!(blkid_le32(es->s_feature_compat) & EXT3_FEATURE_COMPAT_HAS_JOURNAL) && !system_supports_ext2() && system_supports_ext4() && linux_version_code() >= EXT4_SUPPORTS_EXT2) goto force_ext4; /* Ext4 has at least one feature which ext3 doesn't understand */ if (!(blkid_le32(es->s_feature_ro_compat) & EXT3_FEATURE_RO_COMPAT_UNSUPPORTED) && !(blkid_le32(es->s_feature_incompat) & EXT3_FEATURE_INCOMPAT_UNSUPPORTED)) return -BLKID_ERR_PARAM; force_ext4: /* * If the filesystem is a OK for use by in-development * filesystem code, and ext4dev is supported or ext4 is not * supported, then don't call ourselves ext4, so we can redo * the detection and mark the filesystem as ext4dev. * * If the filesystem is marked as in use by production * filesystem, then it can only be used by ext4 and NOT by * ext4dev. */ if (blkid_le32(es->s_flags) & EXT2_FLAGS_TEST_FILESYS) { if (system_supports_ext4dev() || !system_supports_ext4()) return -BLKID_ERR_PARAM; } get_ext2_info(probe->dev, id, buf); return 0; } static int probe_ext3(struct blkid_probe *probe, struct blkid_magic *id, unsigned char *buf) { struct ext2_super_block *es; es = (struct ext2_super_block *)buf; /* ext3 requires journal */ if (!(blkid_le32(es->s_feature_compat) & EXT3_FEATURE_COMPAT_HAS_JOURNAL)) return -BLKID_ERR_PARAM; /* Any features which ext3 doesn't understand */ if ((blkid_le32(es->s_feature_ro_compat) & EXT3_FEATURE_RO_COMPAT_UNSUPPORTED) || (blkid_le32(es->s_feature_incompat) & EXT3_FEATURE_INCOMPAT_UNSUPPORTED)) return -BLKID_ERR_PARAM; get_ext2_info(probe->dev, id, buf); return 0; } static int probe_ext2(struct blkid_probe *probe, struct blkid_magic *id, unsigned char *buf) { struct ext2_super_block *es; es = (struct ext2_super_block *)buf; /* Distinguish between ext3 and ext2 */ if ((blkid_le32(es->s_feature_compat) & EXT3_FEATURE_COMPAT_HAS_JOURNAL)) return -BLKID_ERR_PARAM; /* Any features which ext2 doesn't understand */ if ((blkid_le32(es->s_feature_ro_compat) & EXT2_FEATURE_RO_COMPAT_UNSUPPORTED) || (blkid_le32(es->s_feature_incompat) & EXT2_FEATURE_INCOMPAT_UNSUPPORTED)) return -BLKID_ERR_PARAM; /* * If ext2 is not present, but ext4 or ext4dev are, then * disclaim we are ext2 */ if (!system_supports_ext2() && (system_supports_ext4() || system_supports_ext4dev()) && linux_version_code() >= EXT4_SUPPORTS_EXT2) return -BLKID_ERR_PARAM; get_ext2_info(probe->dev, id, buf); return 0; } static int probe_jbd(struct blkid_probe *probe, struct blkid_magic *id, unsigned char *buf) { struct ext2_super_block *es = (struct ext2_super_block *) buf; if (!(blkid_le32(es->s_feature_incompat) & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)) return -BLKID_ERR_PARAM; get_ext2_info(probe->dev, id, buf); return 0; } #define FAT_ATTR_VOLUME_ID 0x08 #define FAT_ATTR_DIR 0x10 #define FAT_ATTR_LONG_NAME 0x0f #define FAT_ATTR_MASK 0x3f #define FAT_ENTRY_FREE 0xe5 static const char *no_name = "NO NAME "; static unsigned char *search_fat_label(struct vfat_dir_entry *dir, int count) { int i; for (i = 0; i < count; i++) { if (dir[i].name[0] == 0x00) break; if ((dir[i].name[0] == FAT_ENTRY_FREE) || (dir[i].cluster_high != 0 || dir[i].cluster_low != 0) || ((dir[i].attr & FAT_ATTR_MASK) == FAT_ATTR_LONG_NAME)) continue; if ((dir[i].attr & (FAT_ATTR_VOLUME_ID | FAT_ATTR_DIR)) == FAT_ATTR_VOLUME_ID) { return dir[i].name; } } return 0; } /* FAT label extraction from the root directory taken from Kay * Sievers's volume_id library */ static int probe_fat(struct blkid_probe *probe, struct blkid_magic *id __BLKID_ATTR((unused)), unsigned char *buf) { struct vfat_super_block *vs = (struct vfat_super_block *) buf; struct msdos_super_block *ms = (struct msdos_super_block *) buf; struct vfat_dir_entry *dir; char serno[10]; const unsigned char *label = 0, *vol_label = 0, *tmp; unsigned char *vol_serno; int label_len = 0, maxloop = 100; __u16 sector_size, dir_entries, reserved; __u32 sect_count, fat_size, dir_size, cluster_count, fat_length; __u32 buf_size, start_data_sect, next, root_start, root_dir_entries; /* sector size check */ tmp = (unsigned char *)&ms->ms_sector_size; sector_size = tmp[0] + (tmp[1] << 8); if (sector_size != 0x200 && sector_size != 0x400 && sector_size != 0x800 && sector_size != 0x1000) return 1; tmp = (unsigned char *)&ms->ms_dir_entries; dir_entries = tmp[0] + (tmp[1] << 8); reserved = blkid_le16(ms->ms_reserved); tmp = (unsigned char *)&ms->ms_sectors; sect_count = tmp[0] + (tmp[1] << 8); if (sect_count == 0) sect_count = blkid_le32(ms->ms_total_sect); fat_length = blkid_le16(ms->ms_fat_length); if (fat_length == 0) fat_length = blkid_le32(vs->vs_fat32_length); fat_size = fat_length * ms->ms_fats; dir_size = ((dir_entries * sizeof(struct vfat_dir_entry)) + (sector_size-1)) / sector_size; cluster_count = sect_count - (reserved + fat_size + dir_size); if (ms->ms_cluster_size == 0) return 1; cluster_count /= ms->ms_cluster_size; if (cluster_count > FAT32_MAX) return 1; if (ms->ms_fat_length) { /* the label may be an attribute in the root directory */ root_start = (reserved + fat_size) * sector_size; root_dir_entries = vs->vs_dir_entries[0] + (vs->vs_dir_entries[1] << 8); buf_size = root_dir_entries * sizeof(struct vfat_dir_entry); dir = (struct vfat_dir_entry *) get_buffer(probe, root_start, buf_size); if (dir) vol_label = search_fat_label(dir, root_dir_entries); if (!vol_label || !memcmp(vol_label, no_name, 11)) vol_label = ms->ms_label; vol_serno = ms->ms_serno; blkid_set_tag(probe->dev, "SEC_TYPE", "msdos", sizeof("msdos")); } else { /* Search the FAT32 root dir for the label attribute */ buf_size = vs->vs_cluster_size * sector_size; start_data_sect = reserved + fat_size; next = blkid_le32(vs->vs_root_cluster); while (next && --maxloop) { __u32 next_sect_off; __u64 next_off, fat_entry_off; int count; next_sect_off = (next - 2) * vs->vs_cluster_size; next_off = (start_data_sect + next_sect_off) * sector_size; dir = (struct vfat_dir_entry *) get_buffer(probe, next_off, buf_size); if (dir == NULL) break; count = buf_size / sizeof(struct vfat_dir_entry); vol_label = search_fat_label(dir, count); if (vol_label) break; /* get FAT entry */ fat_entry_off = (reserved * sector_size) + (next * sizeof(__u32)); buf = get_buffer(probe, fat_entry_off, buf_size); if (buf == NULL) break; /* set next cluster */ next = blkid_le32(*((__u32 *) buf) & 0x0fffffff); } if (!vol_label || !memcmp(vol_label, no_name, 11)) vol_label = vs->vs_label; vol_serno = vs->vs_serno; } if (vol_label && memcmp(vol_label, no_name, 11)) { if ((label_len = figure_label_len(vol_label, 11))) label = vol_label; } /* We can't just print them as %04X, because they are unaligned */ sprintf(serno, "%02X%02X-%02X%02X", vol_serno[3], vol_serno[2], vol_serno[1], vol_serno[0]); blkid_set_tag(probe->dev, "LABEL", (const char *) label, label_len); blkid_set_tag(probe->dev, "UUID", serno, sizeof(serno)-1); return 0; } /* * The FAT filesystem could be without a magic string in superblock * (e.g. old floppies). This heuristic for FAT detection is inspired * by http://vrfy.org/projects/volume_id/ and Linux kernel. * [7-Jul-2005, Karel Zak ] */ static int probe_fat_nomagic(struct blkid_probe *probe, struct blkid_magic *id __BLKID_ATTR((unused)), unsigned char *buf) { struct msdos_super_block *ms; ms = (struct msdos_super_block *)buf; /* heads check */ if (ms->ms_heads == 0) return 1; /* cluster size check*/ if (ms->ms_cluster_size == 0 || (ms->ms_cluster_size & (ms->ms_cluster_size-1))) return 1; /* media check */ if (ms->ms_media < 0xf8 && ms->ms_media != 0xf0) return 1; /* fat counts(Linux kernel expects at least 1 FAT table) */ if (!ms->ms_fats) return 1; /* * OS/2 and apparently DFSee will place a FAT12/16-like * pseudo-superblock in the first 512 bytes of non-FAT * filesystems --- at least JFS and HPFS, and possibly others. * So we explicitly check for those filesystems at the * FAT12/16 filesystem magic field identifier, and if they are * present, we rule this out as a FAT filesystem, despite the * FAT-like pseudo-header. */ if ((memcmp(ms->ms_magic, "JFS ", 8) == 0) || (memcmp(ms->ms_magic, "HPFS ", 8) == 0)) return 1; return probe_fat(probe, id, buf); } static int probe_ntfs(struct blkid_probe *probe, struct blkid_magic *id __BLKID_ATTR((unused)), unsigned char *buf) { struct ntfs_super_block *ns; struct master_file_table_record *mft; struct file_attribute *attr; char uuid_str[17], label_str[129], *cp; int bytes_per_sector, sectors_per_cluster; int mft_record_size, attr_off, attr_len; unsigned int i, attr_type, val_len; int val_off; __u64 nr_clusters; blkid_loff_t off; unsigned char *buf_mft, *val; ns = (struct ntfs_super_block *) buf; bytes_per_sector = ns->bios_parameter_block[0] + (ns->bios_parameter_block[1] << 8); sectors_per_cluster = ns->bios_parameter_block[2]; if ((bytes_per_sector < 512) || (sectors_per_cluster == 0)) return 1; if (ns->cluster_per_mft_record < 0) mft_record_size = 1 << (0-ns->cluster_per_mft_record); else mft_record_size = ns->cluster_per_mft_record * sectors_per_cluster * bytes_per_sector; nr_clusters = blkid_le64(ns->number_of_sectors) / sectors_per_cluster; if ((blkid_le64(ns->mft_cluster_location) > nr_clusters) || (blkid_le64(ns->mft_mirror_cluster_location) > nr_clusters)) return 1; off = blkid_le64(ns->mft_mirror_cluster_location) * bytes_per_sector * sectors_per_cluster; buf_mft = get_buffer(probe, off, mft_record_size); if (!buf_mft) return 1; if (memcmp(buf_mft, "FILE", 4)) return 1; off = blkid_le64(ns->mft_cluster_location) * bytes_per_sector * sectors_per_cluster; buf_mft = get_buffer(probe, off, mft_record_size); if (!buf_mft) return 1; if (memcmp(buf_mft, "FILE", 4)) return 1; off += MFT_RECORD_VOLUME * mft_record_size; buf_mft = get_buffer(probe, off, mft_record_size); if (!buf_mft) return 1; if (memcmp(buf_mft, "FILE", 4)) return 1; mft = (struct master_file_table_record *) buf_mft; attr_off = blkid_le16(mft->attrs_offset); label_str[0] = 0; while (1) { attr = (struct file_attribute *) (buf_mft + attr_off); attr_len = blkid_le16(attr->len); attr_type = blkid_le32(attr->type); val_off = blkid_le16(attr->value_offset); val_len = blkid_le32(attr->value_len); attr_off += attr_len; if ((attr_off > mft_record_size) || (attr_len == 0)) break; if (attr_type == MFT_RECORD_ATTR_END) break; if (attr_type == MFT_RECORD_ATTR_VOLUME_NAME) { if (val_len > sizeof(label_str)) val_len = sizeof(label_str)-1; for (i=0, cp=label_str; i < val_len; i+=2,cp++) { val = ((__u8 *) attr) + val_off + i; *cp = val[0]; if (val[1]) *cp = '?'; } *cp = 0; } } sprintf(uuid_str, "%016llX", blkid_le64(ns->volume_serial)); blkid_set_tag(probe->dev, "UUID", uuid_str, 0); if (label_str[0]) blkid_set_tag(probe->dev, "LABEL", label_str, 0); return 0; } static int probe_xfs(struct blkid_probe *probe, struct blkid_magic *id __BLKID_ATTR((unused)), unsigned char *buf) { struct xfs_super_block *xs; const char *label = 0; xs = (struct xfs_super_block *)buf; if (strlen(xs->xs_fname)) label = xs->xs_fname; blkid_set_tag(probe->dev, "LABEL", label, sizeof(xs->xs_fname)); set_uuid(probe->dev, xs->xs_uuid, 0); return 0; } static int probe_reiserfs(struct blkid_probe *probe, struct blkid_magic *id, unsigned char *buf) { struct reiserfs_super_block *rs = (struct reiserfs_super_block *) buf; unsigned int blocksize; const char *label = 0; blocksize = blkid_le16(rs->rs_blocksize); /* The blocksize must be at least 1k */ if ((blocksize >> 10) == 0) return -BLKID_ERR_PARAM; /* If the superblock is inside the journal, we have the wrong one */ if (id->bim_kboff/(blocksize>>10) > blkid_le32(rs->rs_journal_block)) return -BLKID_ERR_BIG; /* LABEL/UUID are only valid for later versions of Reiserfs v3.6. */ if (id->bim_magic[6] == '2' || id->bim_magic[6] == '3') { if (strlen(rs->rs_label)) label = rs->rs_label; set_uuid(probe->dev, rs->rs_uuid, 0); } blkid_set_tag(probe->dev, "LABEL", label, sizeof(rs->rs_label)); return 0; } static int probe_reiserfs4(struct blkid_probe *probe, struct blkid_magic *id __BLKID_ATTR((unused)), unsigned char *buf) { struct reiser4_super_block *rs4 = (struct reiser4_super_block *) buf; const unsigned char *label = 0; if (strlen((char *) rs4->rs4_label)) label = rs4->rs4_label; set_uuid(probe->dev, rs4->rs4_uuid, 0); blkid_set_tag(probe->dev, "LABEL", (const char *) label, sizeof(rs4->rs4_label)); return 0; } static int probe_jfs(struct blkid_probe *probe, struct blkid_magic *id __BLKID_ATTR((unused)), unsigned char *buf) { struct jfs_super_block *js; const char *label = 0; js = (struct jfs_super_block *)buf; if (blkid_le32(js->js_bsize) != (1 << blkid_le16(js->js_l2bsize))) return 1; if (blkid_le32(js->js_pbsize) != (1 << blkid_le16(js->js_l2pbsize))) return 1; if ((blkid_le16(js->js_l2bsize) - blkid_le16(js->js_l2pbsize)) != blkid_le16(js->js_l2bfactor)) return 1; if (strlen((char *) js->js_label)) label = (char *) js->js_label; blkid_set_tag(probe->dev, "LABEL", label, sizeof(js->js_label)); set_uuid(probe->dev, js->js_uuid, 0); return 0; } static int probe_zfs(struct blkid_probe *probe, struct blkid_magic *id, unsigned char *buf) { #if 0 char *vdev_label; const char *pool_name = 0; /* read nvpair data for pool name, pool GUID (complex) */ blkid_set_tag(probe->dev, "LABEL", pool_name, sizeof(pool_name)); set_uuid(probe->dev, pool_guid, 0); #endif return 0; } static int probe_luks(struct blkid_probe *probe, struct blkid_magic *id __BLKID_ATTR((unused)), unsigned char *buf) { char uuid[40]; /* 168 is the offset to the 40 character uuid: * http://luks.endorphin.org/LUKS-on-disk-format.pdf */ strncpy(uuid, (char *) buf+168, 40); blkid_set_tag(probe->dev, "UUID", uuid, sizeof(uuid)); return 0; } static int probe_romfs(struct blkid_probe *probe, struct blkid_magic *id __BLKID_ATTR((unused)), unsigned char *buf) { struct romfs_super_block *ros; const char *label = 0; ros = (struct romfs_super_block *)buf; if (strlen((char *) ros->ros_volume)) label = (char *) ros->ros_volume; blkid_set_tag(probe->dev, "LABEL", label, 0); return 0; } static int probe_cramfs(struct blkid_probe *probe, struct blkid_magic *id __BLKID_ATTR((unused)), unsigned char *buf) { struct cramfs_super_block *csb; const char *label = 0; csb = (struct cramfs_super_block *)buf; if (strlen((char *) csb->name)) label = (char *) csb->name; blkid_set_tag(probe->dev, "LABEL", label, 0); return 0; } static int probe_swap0(struct blkid_probe *probe, struct blkid_magic *id __BLKID_ATTR((unused)), unsigned char *buf __BLKID_ATTR((unused))) { blkid_set_tag(probe->dev, "UUID", 0, 0); blkid_set_tag(probe->dev, "LABEL", 0, 0); return 0; } static int probe_swap1(struct blkid_probe *probe, struct blkid_magic *id, unsigned char *buf __BLKID_ATTR((unused))) { struct swap_id_block *sws; probe_swap0(probe, id, buf); /* * Version 1 swap headers are always located at offset of 1024 * bytes, although the swap signature itself is located at the * end of the page (which may vary depending on hardware * pagesize). */ sws = (struct swap_id_block *) get_buffer(probe, 1024, 1024); if (!sws) return 1; /* check for wrong version or zeroed pagecount, for sanity */ if (!memcmp(id->bim_magic, "SWAPSPACE2", id->bim_len) && (sws->sws_version != 1 || sws->sws_lastpage == 0)) return 1; /* arbitrary sanity check.. is there any garbage down there? */ if (sws->sws_pad[32] == 0 && sws->sws_pad[33] == 0) { if (sws->sws_volume[0]) blkid_set_tag(probe->dev, "LABEL", sws->sws_volume, sizeof(sws->sws_volume)); if (sws->sws_uuid[0]) set_uuid(probe->dev, sws->sws_uuid, 0); } return 0; } static int probe_iso9660(struct blkid_probe *probe, struct blkid_magic *id __BLKID_ATTR((unused)), unsigned char *buf) { struct iso_volume_descriptor *iso; const unsigned char *label; iso = (struct iso_volume_descriptor *) buf; label = iso->volume_id; blkid_set_tag(probe->dev, "LABEL", (const char *) label, figure_label_len(label, 32)); return 0; } static const char *udf_magic[] = { "BEA01", "BOOT2", "CD001", "CDW02", "NSR02", "NSR03", "TEA01", 0 }; static int probe_udf(struct blkid_probe *probe, struct blkid_magic *id __BLKID_ATTR((unused)), unsigned char *buf __BLKID_ATTR((unused))) { int j, bs; struct iso_volume_descriptor *isosb; const char ** m; /* determine the block size by scanning in 2K increments (block sizes larger than 2K will be null padded) */ for (bs = 1; bs < 16; bs++) { isosb = (struct iso_volume_descriptor *) get_buffer(probe, bs*2048+32768, sizeof(isosb)); if (!isosb) return 1; if (isosb->vd_id[0]) break; } /* Scan up to another 64 blocks looking for additional VSD's */ for (j = 1; j < 64; j++) { if (j > 1) { isosb = (struct iso_volume_descriptor *) get_buffer(probe, j*bs*2048+32768, sizeof(isosb)); if (!isosb) return 1; } /* If we find NSR0x then call it udf: NSR01 for UDF 1.00 NSR02 for UDF 1.50 NSR03 for UDF 2.00 */ if (!memcmp(isosb->vd_id, "NSR0", 4)) return probe_iso9660(probe, id, buf); for (m = udf_magic; *m; m++) if (!memcmp(*m, isosb->vd_id, 5)) break; if (*m == 0) return 1; } return 1; } static int probe_ocfs(struct blkid_probe *probe, struct blkid_magic *id __BLKID_ATTR((unused)), unsigned char *buf) { struct ocfs_volume_header ovh; struct ocfs_volume_label ovl; __u32 major; memcpy(&ovh, buf, sizeof(ovh)); memcpy(&ovl, buf+512, sizeof(ovl)); major = ocfsmajor(ovh); if (major == 1) blkid_set_tag(probe->dev,"SEC_TYPE","ocfs1",sizeof("ocfs1")); else if (major >= 9) blkid_set_tag(probe->dev,"SEC_TYPE","ntocfs",sizeof("ntocfs")); blkid_set_tag(probe->dev, "LABEL", ovl.label, ocfslabellen(ovl)); blkid_set_tag(probe->dev, "MOUNT", ovh.mount, ocfsmountlen(ovh)); set_uuid(probe->dev, ovl.vol_id, 0); return 0; } static int probe_ocfs2(struct blkid_probe *probe, struct blkid_magic *id __BLKID_ATTR((unused)), unsigned char *buf) { struct ocfs2_super_block *osb; osb = (struct ocfs2_super_block *)buf; blkid_set_tag(probe->dev, "LABEL", osb->s_label, sizeof(osb->s_label)); set_uuid(probe->dev, osb->s_uuid, 0); return 0; } static int probe_oracleasm(struct blkid_probe *probe, struct blkid_magic *id __BLKID_ATTR((unused)), unsigned char *buf) { struct oracle_asm_disk_label *dl; dl = (struct oracle_asm_disk_label *)buf; blkid_set_tag(probe->dev, "LABEL", dl->dl_id, sizeof(dl->dl_id)); return 0; } static int probe_gfs(struct blkid_probe *probe, struct blkid_magic *id __BLKID_ATTR((unused)), unsigned char *buf) { struct gfs2_sb *sbd; const char *label = 0; sbd = (struct gfs2_sb *)buf; if (blkid_be32(sbd->sb_fs_format) == GFS_FORMAT_FS && blkid_be32(sbd->sb_multihost_format) == GFS_FORMAT_MULTI) { blkid_set_tag(probe->dev, "UUID", 0, 0); if (strlen(sbd->sb_locktable)) label = sbd->sb_locktable; blkid_set_tag(probe->dev, "LABEL", label, sizeof(sbd->sb_locktable)); return 0; } return 1; } static int probe_gfs2(struct blkid_probe *probe, struct blkid_magic *id __BLKID_ATTR((unused)), unsigned char *buf) { struct gfs2_sb *sbd; const char *label = 0; sbd = (struct gfs2_sb *)buf; if (blkid_be32(sbd->sb_fs_format) == GFS2_FORMAT_FS && blkid_be32(sbd->sb_multihost_format) == GFS2_FORMAT_MULTI) { blkid_set_tag(probe->dev, "UUID", 0, 0); if (strlen(sbd->sb_locktable)) label = sbd->sb_locktable; blkid_set_tag(probe->dev, "LABEL", label, sizeof(sbd->sb_locktable)); return 0; } return 1; } static void unicode_16be_to_utf8(unsigned char *str, int out_len, const unsigned char *buf, int in_len) { int i, j; unsigned int c; for (i = j = 0; i + 2 <= in_len; i += 2) { c = (buf[i] << 8) | buf[i+1]; if (c == 0) { str[j] = '\0'; break; } else if (c < 0x80) { if (j+1 >= out_len) break; str[j++] = (unsigned char) c; } else if (c < 0x800) { if (j+2 >= out_len) break; str[j++] = (unsigned char) (0xc0 | (c >> 6)); str[j++] = (unsigned char) (0x80 | (c & 0x3f)); } else { if (j+3 >= out_len) break; str[j++] = (unsigned char) (0xe0 | (c >> 12)); str[j++] = (unsigned char) (0x80 | ((c >> 6) & 0x3f)); str[j++] = (unsigned char) (0x80 | (c & 0x3f)); } } str[j] = '\0'; } static int probe_hfs(struct blkid_probe *probe __BLKID_ATTR((unused)), struct blkid_magic *id __BLKID_ATTR((unused)), unsigned char *buf) { struct hfs_mdb *hfs = (struct hfs_mdb *) buf; char uuid_str[17]; __u64 uuid; if ((memcmp(hfs->embed_sig, "H+", 2) == 0) || (memcmp(hfs->embed_sig, "HX", 2) == 0)) return 1; /* Not hfs, but an embedded HFS+ */ uuid = blkid_le64(*((unsigned long long *) hfs->finder_info.id)); if (uuid) { sprintf(uuid_str, "%016llX", uuid); blkid_set_tag(probe->dev, "UUID", uuid_str, 0); } blkid_set_tag(probe->dev, "LABEL", hfs->label, hfs->label_len); return 0; } static int probe_hfsplus(struct blkid_probe *probe, struct blkid_magic *id, unsigned char *buf) { struct hfsplus_extent extents[HFSPLUS_EXTENT_COUNT]; struct hfsplus_bnode_descriptor *descr; struct hfsplus_bheader_record *bnode; struct hfsplus_catalog_key *key; struct hfsplus_vol_header *hfsplus; struct hfs_mdb *sbd = (struct hfs_mdb *) buf; unsigned int alloc_block_size; unsigned int alloc_first_block; unsigned int embed_first_block; unsigned int off = 0; unsigned int blocksize; unsigned int cat_block; unsigned int ext_block_start; unsigned int ext_block_count; unsigned int record_count; unsigned int leaf_node_head; unsigned int leaf_node_count; unsigned int leaf_node_size; unsigned int leaf_block; unsigned int label_len; int ext; __u64 leaf_off, uuid; char uuid_str[17], label[512]; /* Check for a HFS+ volume embedded in a HFS volume */ if (memcmp(sbd->signature, "BD", 2) == 0) { if ((memcmp(sbd->embed_sig, "H+", 2) != 0) && (memcmp(sbd->embed_sig, "HX", 2) != 0)) /* This must be an HFS volume, so fail */ return 1; alloc_block_size = blkid_be32(sbd->al_blk_size); alloc_first_block = blkid_be16(sbd->al_bl_st); embed_first_block = blkid_be16(sbd->embed_startblock); off = (alloc_first_block * 512) + (embed_first_block * alloc_block_size); buf = get_buffer(probe, off + (id->bim_kboff * 1024), sizeof(sbd)); if (!buf) return 1; hfsplus = (struct hfsplus_vol_header *) buf; } hfsplus = (struct hfsplus_vol_header *) buf; if ((memcmp(hfsplus->signature, "H+", 2) != 0) && (memcmp(hfsplus->signature, "HX", 2) != 0)) return 1; uuid = blkid_le64(*((unsigned long long *) hfsplus->finder_info.id)); if (uuid) { sprintf(uuid_str, "%016llX", uuid); blkid_set_tag(probe->dev, "UUID", uuid_str, 0); } blocksize = blkid_be32(hfsplus->blocksize); memcpy(extents, hfsplus->cat_file.extents, sizeof(extents)); cat_block = blkid_be32(extents[0].start_block); buf = get_buffer(probe, off + (cat_block * blocksize), 0x2000); if (!buf) return 0; bnode = (struct hfsplus_bheader_record *) &buf[sizeof(struct hfsplus_bnode_descriptor)]; leaf_node_head = blkid_be32(bnode->leaf_head); leaf_node_size = blkid_be16(bnode->node_size); leaf_node_count = blkid_be32(bnode->leaf_count); if (leaf_node_count == 0) return 0; leaf_block = (leaf_node_head * leaf_node_size) / blocksize; /* get physical location */ for (ext = 0; ext < HFSPLUS_EXTENT_COUNT; ext++) { ext_block_start = blkid_be32(extents[ext].start_block); ext_block_count = blkid_be32(extents[ext].block_count); if (ext_block_count == 0) return 0; /* this is our extent */ if (leaf_block < ext_block_count) break; leaf_block -= ext_block_count; } if (ext == HFSPLUS_EXTENT_COUNT) return 0; leaf_off = (ext_block_start + leaf_block) * blocksize; buf = get_buffer(probe, off + leaf_off, leaf_node_size); if (!buf) return 0; descr = (struct hfsplus_bnode_descriptor *) buf; record_count = blkid_be16(descr->num_recs); if (record_count == 0) return 0; if (descr->type != HFS_NODE_LEAF) return 0; key = (struct hfsplus_catalog_key *) &buf[sizeof(struct hfsplus_bnode_descriptor)]; if (blkid_be32(key->parent_id) != HFSPLUS_POR_CNID) return 0; label_len = blkid_be16(key->unicode_len) * 2; unicode_16be_to_utf8(label, sizeof(label), key->unicode, label_len); blkid_set_tag(probe->dev, "LABEL", label, 0); return 0; } #define LVM2_LABEL_SIZE 512 static unsigned int lvm2_calc_crc(const void *buf, unsigned int size) { static const unsigned int crctab[] = { 0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c }; unsigned int i, crc = 0xf597a6cf; const __u8 *data = (const __u8 *) buf; for (i = 0; i < size; i++) { crc ^= *data++; crc = (crc >> 4) ^ crctab[crc & 0xf]; crc = (crc >> 4) ^ crctab[crc & 0xf]; } return crc; } static int probe_lvm2(struct blkid_probe *probe, struct blkid_magic *id, unsigned char *buf) { int sector = (id->bim_kboff) << 1; struct lvm2_pv_label_header *label= (struct lvm2_pv_label_header *)buf; char *p, *q, uuid[40]; unsigned int i, b; /* buf is at 0k or 1k offset; find label inside */ if (memcmp(buf, "LABELONE", 8) == 0) { label = (struct lvm2_pv_label_header *)buf; } else if (memcmp(buf + 512, "LABELONE", 8) == 0) { label = (struct lvm2_pv_label_header *)(buf + 512); sector++; } else { return 1; } if (blkid_le64(label->sector_xl) != (unsigned) sector) { DBG(DEBUG_PROBE, printf("LVM2: label for sector %llu found at sector %d\n", blkid_le64(label->sector_xl), sector)); return 1; } if (lvm2_calc_crc(&label->offset_xl, LVM2_LABEL_SIZE - ((char *)&label->offset_xl - (char *)label)) != blkid_le32(label->crc_xl)) { DBG(DEBUG_PROBE, printf("LVM2: label checksum incorrect at sector %d\n", sector)); return 1; } for (i=0, b=1, p=uuid, q= (char *) label->pv_uuid; i <= 32; i++, b <<= 1) { if (b & 0x4444440) *p++ = '-'; *p++ = *q++; } blkid_set_tag(probe->dev, "UUID", uuid, LVM2_ID_LEN+6); return 0; } static int probe_btrfs(struct blkid_probe *probe, struct blkid_magic *id, unsigned char *buf) { struct btrfs_super_block *bs; const char *label = 0; bs = (struct btrfs_super_block *)buf; if (strlen(bs->label)) label = bs->label; blkid_set_tag(probe->dev, "LABEL", label, sizeof(bs->label)); set_uuid(probe->dev, bs->fsid, 0); return 0; } /* * Various filesystem magics that we can check for. Note that kboff and * sboff are in kilobytes and bytes respectively. All magics are in * byte strings so we don't worry about endian issues. */ static struct blkid_magic type_array[] = { /* type kboff sboff len magic probe */ { "oracleasm", 0, 32, 8, "ORCLDISK", probe_oracleasm }, { "ntfs", 0, 3, 8, "NTFS ", probe_ntfs }, { "jbd", 1, 0x38, 2, "\123\357", probe_jbd }, { "ext4dev", 1, 0x38, 2, "\123\357", probe_ext4dev }, { "ext4", 1, 0x38, 2, "\123\357", probe_ext4 }, { "ext3", 1, 0x38, 2, "\123\357", probe_ext3 }, { "ext2", 1, 0x38, 2, "\123\357", probe_ext2 }, { "reiserfs", 8, 0x34, 8, "ReIsErFs", probe_reiserfs }, { "reiserfs", 64, 0x34, 9, "ReIsEr2Fs", probe_reiserfs }, { "reiserfs", 64, 0x34, 9, "ReIsEr3Fs", probe_reiserfs }, { "reiserfs", 64, 0x34, 8, "ReIsErFs", probe_reiserfs }, { "reiserfs", 8, 20, 8, "ReIsErFs", probe_reiserfs }, { "reiser4", 64, 0, 7, "ReIsEr4", probe_reiserfs4 }, { "gfs2", 64, 0, 4, "\x01\x16\x19\x70", probe_gfs2 }, { "gfs", 64, 0, 4, "\x01\x16\x19\x70", probe_gfs }, { "vfat", 0, 0x52, 5, "MSWIN", probe_fat }, { "vfat", 0, 0x52, 8, "FAT32 ", probe_fat }, { "vfat", 0, 0x36, 5, "MSDOS", probe_fat }, { "vfat", 0, 0x36, 8, "FAT16 ", probe_fat }, { "vfat", 0, 0x36, 8, "FAT12 ", probe_fat }, { "vfat", 0, 0, 1, "\353", probe_fat_nomagic }, { "vfat", 0, 0, 1, "\351", probe_fat_nomagic }, { "vfat", 0, 0x1fe, 2, "\125\252", probe_fat_nomagic }, { "minix", 1, 0x10, 2, "\177\023", 0 }, { "minix", 1, 0x10, 2, "\217\023", 0 }, { "minix", 1, 0x10, 2, "\150\044", 0 }, { "minix", 1, 0x10, 2, "\170\044", 0 }, { "vxfs", 1, 0, 4, "\365\374\001\245", 0 }, { "xfs", 0, 0, 4, "XFSB", probe_xfs }, { "romfs", 0, 0, 8, "-rom1fs-", probe_romfs }, { "bfs", 0, 0, 4, "\316\372\173\033", 0 }, { "cramfs", 0, 0, 4, "E=\315\050", probe_cramfs }, { "qnx4", 0, 4, 6, "QNX4FS", 0 }, { "udf", 32, 1, 5, "BEA01", probe_udf }, { "udf", 32, 1, 5, "BOOT2", probe_udf }, { "udf", 32, 1, 5, "CD001", probe_udf }, { "udf", 32, 1, 5, "CDW02", probe_udf }, { "udf", 32, 1, 5, "NSR02", probe_udf }, { "udf", 32, 1, 5, "NSR03", probe_udf }, { "udf", 32, 1, 5, "TEA01", probe_udf }, { "iso9660", 32, 1, 5, "CD001", probe_iso9660 }, { "iso9660", 32, 9, 5, "CDROM", probe_iso9660 }, { "jfs", 32, 0, 4, "JFS1", probe_jfs }, { "zfs", 8, 0, 8, "\0\0\x02\xf5\xb0\x07\xb1\x0c", probe_zfs }, { "zfs", 8, 0, 8, "\x0c\xb1\x07\xb0\xf5\x02\0\0", probe_zfs }, { "zfs", 264, 0, 8, "\0\0\x02\xf5\xb0\x07\xb1\x0c", probe_zfs }, { "zfs", 264, 0, 8, "\x0c\xb1\x07\xb0\xf5\x02\0\0", probe_zfs }, { "hfsplus", 1, 0, 2, "BD", probe_hfsplus }, { "hfsplus", 1, 0, 2, "H+", probe_hfsplus }, { "hfsplus", 1, 0, 2, "HX", probe_hfsplus }, { "hfs", 1, 0, 2, "BD", probe_hfs }, { "ufs", 8, 0x55c, 4, "T\031\001\000", 0 }, { "hpfs", 8, 0, 4, "I\350\225\371", 0 }, { "sysv", 0, 0x3f8, 4, "\020~\030\375", 0 }, { "swap", 0, 0xff6, 10, "SWAP-SPACE", probe_swap0 }, { "swap", 0, 0xff6, 10, "SWAPSPACE2", probe_swap1 }, { "swsuspend", 0, 0xff6, 9, "S1SUSPEND", probe_swap1 }, { "swsuspend", 0, 0xff6, 9, "S2SUSPEND", probe_swap1 }, { "swsuspend", 0, 0xff6, 9, "ULSUSPEND", probe_swap1 }, { "swap", 0, 0x1ff6, 10, "SWAP-SPACE", probe_swap0 }, { "swap", 0, 0x1ff6, 10, "SWAPSPACE2", probe_swap1 }, { "swsuspend", 0, 0x1ff6, 9, "S1SUSPEND", probe_swap1 }, { "swsuspend", 0, 0x1ff6, 9, "S2SUSPEND", probe_swap1 }, { "swsuspend", 0, 0x1ff6, 9, "ULSUSPEND", probe_swap1 }, { "swap", 0, 0x3ff6, 10, "SWAP-SPACE", probe_swap0 }, { "swap", 0, 0x3ff6, 10, "SWAPSPACE2", probe_swap1 }, { "swsuspend", 0, 0x3ff6, 9, "S1SUSPEND", probe_swap1 }, { "swsuspend", 0, 0x3ff6, 9, "S2SUSPEND", probe_swap1 }, { "swsuspend", 0, 0x3ff6, 9, "ULSUSPEND", probe_swap1 }, { "swap", 0, 0x7ff6, 10, "SWAP-SPACE", probe_swap0 }, { "swap", 0, 0x7ff6, 10, "SWAPSPACE2", probe_swap1 }, { "swsuspend", 0, 0x7ff6, 9, "S1SUSPEND", probe_swap1 }, { "swsuspend", 0, 0x7ff6, 9, "S2SUSPEND", probe_swap1 }, { "swsuspend", 0, 0x7ff6, 9, "ULSUSPEND", probe_swap1 }, { "swap", 0, 0xfff6, 10, "SWAP-SPACE", probe_swap0 }, { "swap", 0, 0xfff6, 10, "SWAPSPACE2", probe_swap1 }, { "swsuspend", 0, 0xfff6, 9, "S1SUSPEND", probe_swap1 }, { "swsuspend", 0, 0xfff6, 9, "S2SUSPEND", probe_swap1 }, { "swsuspend", 0, 0xfff6, 9, "ULSUSPEND", probe_swap1 }, { "ocfs", 0, 8, 9, "OracleCFS", probe_ocfs }, { "ocfs2", 1, 0, 6, "OCFSV2", probe_ocfs2 }, { "ocfs2", 2, 0, 6, "OCFSV2", probe_ocfs2 }, { "ocfs2", 4, 0, 6, "OCFSV2", probe_ocfs2 }, { "ocfs2", 8, 0, 6, "OCFSV2", probe_ocfs2 }, { "crypt_LUKS", 0, 0, 6, "LUKS\xba\xbe", probe_luks }, { "squashfs", 0, 0, 4, "sqsh", 0 }, { "squashfs", 0, 0, 4, "hsqs", 0 }, { "lvm2pv", 0, 0x218, 8, "LVM2 001", probe_lvm2 }, { "lvm2pv", 0, 0x018, 8, "LVM2 001", probe_lvm2 }, { "lvm2pv", 1, 0x018, 8, "LVM2 001", probe_lvm2 }, { "lvm2pv", 1, 0x218, 8, "LVM2 001", probe_lvm2 }, { "btrfs", 64, 0x40, 8, "_BHRfS_M", probe_btrfs }, { NULL, 0, 0, 0, NULL, NULL } }; /* * Verify that the data in dev is consistent with what is on the actual * block device (using the devname field only). Normally this will be * called when finding items in the cache, but for long running processes * is also desirable to revalidate an item before use. * * If we are unable to revalidate the data, we return the old data and * do not set the BLKID_BID_FL_VERIFIED flag on it. */ blkid_dev blkid_verify(blkid_cache cache, blkid_dev dev) { struct blkid_magic *id; struct blkid_probe probe; blkid_tag_iterate iter; unsigned char *buf; const char *type, *value; struct stat st; time_t diff, now; int idx; if (!dev) return NULL; now = time(0); diff = now - dev->bid_time; if (stat(dev->bid_name, &st) < 0) { DBG(DEBUG_PROBE, printf("blkid_verify: error %s (%d) while " "trying to stat %s\n", strerror(errno), errno, dev->bid_name)); open_err: if ((errno == EPERM) || (errno == EACCES) || (errno == ENOENT)) { /* We don't have read permission, just return cache data. */ DBG(DEBUG_PROBE, printf("returning unverified data for %s\n", dev->bid_name)); return dev; } blkid_free_dev(dev); return NULL; } if ((now >= dev->bid_time) && (st.st_mtime <= dev->bid_time) && ((diff < BLKID_PROBE_MIN) || (dev->bid_flags & BLKID_BID_FL_VERIFIED && diff < BLKID_PROBE_INTERVAL))) return dev; DBG(DEBUG_PROBE, printf("need to revalidate %s (cache time %lu, stat time %lu,\n\t" "time since last check %lu)\n", dev->bid_name, (unsigned long)dev->bid_time, (unsigned long)st.st_mtime, (unsigned long)diff)); if ((probe.fd = open(dev->bid_name, O_RDONLY)) < 0) { DBG(DEBUG_PROBE, printf("blkid_verify: error %s (%d) while " "opening %s\n", strerror(errno), errno, dev->bid_name)); goto open_err; } probe.cache = cache; probe.dev = dev; probe.sbbuf = 0; probe.buf = 0; probe.buf_max = 0; /* * Iterate over the type array. If we already know the type, * then try that first. If it doesn't work, then blow away * the type information, and try again. * */ try_again: type = 0; if (!dev->bid_type || !strcmp(dev->bid_type, "mdraid")) { uuid_t uuid; if (check_mdraid(probe.fd, uuid) == 0) { set_uuid(dev, uuid, 0); type = "mdraid"; goto found_type; } } for (id = type_array; id->bim_type; id++) { if (dev->bid_type && strcmp(id->bim_type, dev->bid_type)) continue; idx = id->bim_kboff + (id->bim_sboff >> 10); buf = get_buffer(&probe, idx << 10, 1024); if (!buf) continue; if (memcmp(id->bim_magic, buf + (id->bim_sboff & 0x3ff), id->bim_len)) continue; if ((id->bim_probe == NULL) || (id->bim_probe(&probe, id, buf) == 0)) { type = id->bim_type; goto found_type; } } if (!id->bim_type && dev->bid_type) { /* * Zap the device filesystem information and try again */ DBG(DEBUG_PROBE, printf("previous fs type %s not valid, " "trying full probe\n", dev->bid_type)); iter = blkid_tag_iterate_begin(dev); while (blkid_tag_next(iter, &type, &value) == 0) blkid_set_tag(dev, type, 0, 0); blkid_tag_iterate_end(iter); goto try_again; } if (!dev->bid_type) { blkid_free_dev(dev); dev = 0; goto found_type; } found_type: if (dev && type) { dev->bid_devno = st.st_rdev; dev->bid_time = time(0); dev->bid_flags |= BLKID_BID_FL_VERIFIED; cache->bic_flags |= BLKID_BIC_FL_CHANGED; blkid_set_tag(dev, "TYPE", type, 0); DBG(DEBUG_PROBE, printf("%s: devno 0x%04llx, type %s\n", dev->bid_name, (long long)st.st_rdev, type)); } free(probe.sbbuf); free(probe.buf); if (probe.fd >= 0) close(probe.fd); return dev; } int blkid_known_fstype(const char *fstype) { struct blkid_magic *id; for (id = type_array; id->bim_type; id++) { if (strcmp(fstype, id->bim_type) == 0) return 1; } return 0; } #ifdef TEST_PROGRAM int main(int argc, char **argv) { blkid_dev dev; blkid_cache cache; int ret; if (argc != 2) { fprintf(stderr, "Usage: %s device\n" "Probe a single device to determine type\n", argv[0]); exit(1); } if ((ret = blkid_get_cache(&cache, "/dev/null")) != 0) { fprintf(stderr, "%s: error creating cache (%d)\n", argv[0], ret); exit(1); } dev = blkid_get_dev(cache, argv[1], BLKID_DEV_NORMAL); if (!dev) { printf("%s: %s has an unsupported type\n", argv[0], argv[1]); return (1); } printf("TYPE='%s'\n", dev->bid_type ? dev->bid_type : "(null)"); if (dev->bid_label) printf("LABEL='%s'\n", dev->bid_label); if (dev->bid_uuid) printf("UUID='%s'\n", dev->bid_uuid); blkid_free_dev(dev); return (0); } #endif e2fsprogs-1.41.14/lib/blkid/cache.c0000644031104000366760000001050511240667355014706 0ustar tytso/* * cache.c - allocation/initialization/free routines for cache * * Copyright (C) 2001 Andreas Dilger * Copyright (C) 2003 Theodore Ts'o * * %Begin-Header% * This file may be redistributed under the terms of the * GNU Lesser General Public License. * %End-Header% */ #if HAVE_UNISTD_H #include #endif #ifdef HAVE_ERRNO_H #include #endif #include #include #ifdef HAVE_SYS_PRCTL_H #include #else #define PR_GET_DUMPABLE 3 #endif #if (!defined(HAVE_PRCTL) && defined(linux)) #include #endif #ifdef HAVE_SYS_STAT_H #include #endif #include "blkidP.h" int blkid_debug_mask = 0; static char *safe_getenv(const char *arg) { if ((getuid() != geteuid()) || (getgid() != getegid())) return NULL; #if HAVE_PRCTL if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) == 0) return NULL; #else #if (defined(linux) && defined(SYS_prctl)) if (syscall(SYS_prctl, PR_GET_DUMPABLE, 0, 0, 0, 0) == 0) return NULL; #endif #endif #ifdef HAVE___SECURE_GETENV return __secure_getenv(arg); #else return getenv(arg); #endif } #if 0 /* ifdef CONFIG_BLKID_DEBUG */ static blkid_debug_dump_cache(int mask, blkid_cache cache) { struct list_head *p; if (!cache) { printf("cache: NULL\n"); return; } printf("cache: time = %lu\n", cache->bic_time); printf("cache: flags = 0x%08X\n", cache->bic_flags); list_for_each(p, &cache->bic_devs) { blkid_dev dev = list_entry(p, struct blkid_struct_dev, bid_devs); blkid_debug_dump_dev(dev); } } #endif int blkid_get_cache(blkid_cache *ret_cache, const char *filename) { blkid_cache cache; #ifdef CONFIG_BLKID_DEBUG if (!(blkid_debug_mask & DEBUG_INIT)) { char *dstr = getenv("BLKID_DEBUG"); if (dstr) blkid_debug_mask = strtoul(dstr, 0, 0); blkid_debug_mask |= DEBUG_INIT; } #endif DBG(DEBUG_CACHE, printf("creating blkid cache (using %s)\n", filename ? filename : "default cache")); if (!(cache = (blkid_cache) calloc(1, sizeof(struct blkid_struct_cache)))) return -BLKID_ERR_MEM; INIT_LIST_HEAD(&cache->bic_devs); INIT_LIST_HEAD(&cache->bic_tags); if (filename && !strlen(filename)) filename = 0; if (!filename) filename = safe_getenv("BLKID_FILE"); if (!filename) filename = BLKID_CACHE_FILE; cache->bic_filename = blkid_strdup(filename); blkid_read_cache(cache); *ret_cache = cache; return 0; } void blkid_put_cache(blkid_cache cache) { if (!cache) return; (void) blkid_flush_cache(cache); DBG(DEBUG_CACHE, printf("freeing cache struct\n")); /* DBG(DEBUG_CACHE, blkid_debug_dump_cache(cache)); */ while (!list_empty(&cache->bic_devs)) { blkid_dev dev = list_entry(cache->bic_devs.next, struct blkid_struct_dev, bid_devs); blkid_free_dev(dev); } while (!list_empty(&cache->bic_tags)) { blkid_tag tag = list_entry(cache->bic_tags.next, struct blkid_struct_tag, bit_tags); while (!list_empty(&tag->bit_names)) { blkid_tag bad = list_entry(tag->bit_names.next, struct blkid_struct_tag, bit_names); DBG(DEBUG_CACHE, printf("warning: unfreed tag %s=%s\n", bad->bit_name, bad->bit_val)); blkid_free_tag(bad); } blkid_free_tag(tag); } free(cache->bic_filename); free(cache); } void blkid_gc_cache(blkid_cache cache) { struct list_head *p, *pnext; struct stat st; if (!cache) return; list_for_each_safe(p, pnext, &cache->bic_devs) { blkid_dev dev = list_entry(p, struct blkid_struct_dev, bid_devs); if (!p) break; if (stat(dev->bid_name, &st) < 0) { DBG(DEBUG_CACHE, printf("freeing %s\n", dev->bid_name)); blkid_free_dev(dev); cache->bic_flags |= BLKID_BIC_FL_CHANGED; } else { DBG(DEBUG_CACHE, printf("Device %s exists\n", dev->bid_name)); } } } #ifdef TEST_PROGRAM int main(int argc, char** argv) { blkid_cache cache = NULL; int ret; blkid_debug_mask = DEBUG_ALL; if ((argc > 2)) { fprintf(stderr, "Usage: %s [filename] \n", argv[0]); exit(1); } if ((ret = blkid_get_cache(&cache, argv[1])) < 0) { fprintf(stderr, "error %d parsing cache file %s\n", ret, argv[1] ? argv[1] : BLKID_CACHE_FILE); exit(1); } if ((ret = blkid_get_cache(&cache, "/dev/null")) != 0) { fprintf(stderr, "%s: error creating cache (%d)\n", argv[0], ret); exit(1); } if ((ret = blkid_probe_all(cache) < 0)) fprintf(stderr, "error probing devices\n"); blkid_put_cache(cache); return ret; } #endif e2fsprogs-1.41.14/lib/blkid/devno.c0000644031104000366760000001125111240667355014755 0ustar tytso/* * devno.c - find a particular device by its device number (major/minor) * * Copyright (C) 2000, 2001, 2003 Theodore Ts'o * Copyright (C) 2001 Andreas Dilger * * %Begin-Header% * This file may be redistributed under the terms of the * GNU Lesser General Public License. * %End-Header% */ #include #include #if HAVE_UNISTD_H #include #endif #include #include #if HAVE_SYS_TYPES_H #include #endif #if HAVE_SYS_STAT_H #include #endif #include #if HAVE_ERRNO_H #include #endif #if HAVE_SYS_MKDEV_H #include #endif #include "blkidP.h" char *blkid_strndup(const char *s, int length) { char *ret; if (!s) return NULL; if (!length) length = strlen(s); ret = malloc(length + 1); if (ret) { strncpy(ret, s, length); ret[length] = '\0'; } return ret; } char *blkid_strdup(const char *s) { return blkid_strndup(s, 0); } /* * This function adds an entry to the directory list */ static void add_to_dirlist(const char *name, struct dir_list **list) { struct dir_list *dp; dp = malloc(sizeof(struct dir_list)); if (!dp) return; dp->name = blkid_strdup(name); if (!dp->name) { free(dp); return; } dp->next = *list; *list = dp; } /* * This function frees a directory list */ static void free_dirlist(struct dir_list **list) { struct dir_list *dp, *next; for (dp = *list; dp; dp = next) { next = dp->next; free(dp->name); free(dp); } *list = NULL; } void blkid__scan_dir(char *dirname, dev_t devno, struct dir_list **list, char **devname) { DIR *dir; struct dirent *dp; char path[1024]; int dirlen; struct stat st; if ((dir = opendir(dirname)) == NULL) return; dirlen = strlen(dirname) + 2; while ((dp = readdir(dir)) != 0) { if (dirlen + strlen(dp->d_name) >= sizeof(path)) continue; if (dp->d_name[0] == '.' && ((dp->d_name[1] == 0) || ((dp->d_name[1] == '.') && (dp->d_name[2] == 0)))) continue; sprintf(path, "%s/%s", dirname, dp->d_name); if (stat(path, &st) < 0) continue; if (S_ISBLK(st.st_mode) && st.st_rdev == devno) { *devname = blkid_strdup(path); DBG(DEBUG_DEVNO, printf("found 0x%llx at %s (%p)\n", (long long)devno, path, *devname)); break; } if (list && S_ISDIR(st.st_mode) && !lstat(path, &st) && S_ISDIR(st.st_mode)) add_to_dirlist(path, list); } closedir(dir); return; } /* Directories where we will try to search for device numbers */ static const char *devdirs[] = { "/devices", "/devfs", "/dev", NULL }; /* * This function finds the pathname to a block device with a given * device number. It returns a pointer to allocated memory to the * pathname on success, and NULL on failure. */ char *blkid_devno_to_devname(dev_t devno) { struct dir_list *list = NULL, *new_list = NULL; char *devname = NULL; const char **dir; /* * Add the starting directories to search in reverse order of * importance, since we are using a stack... */ for (dir = devdirs; *dir; dir++) add_to_dirlist(*dir, &list); while (list) { struct dir_list *current = list; list = list->next; DBG(DEBUG_DEVNO, printf("directory %s\n", current->name)); blkid__scan_dir(current->name, devno, &new_list, &devname); free(current->name); free(current); if (devname) break; /* * If we're done checking at this level, descend to * the next level of subdirectories. (breadth-first) */ if (list == NULL) { list = new_list; new_list = NULL; } } free_dirlist(&list); free_dirlist(&new_list); if (!devname) { DBG(DEBUG_DEVNO, printf("blkid: couldn't find devno 0x%04lx\n", (unsigned long) devno)); } else { DBG(DEBUG_DEVNO, printf("found devno 0x%04llx as %s\n", (long long)devno, devname)); } return devname; } #ifdef TEST_PROGRAM int main(int argc, char** argv) { char *devname, *tmp; int major, minor; dev_t devno; const char *errmsg = "Couldn't parse %s: %s\n"; blkid_debug_mask = DEBUG_ALL; if ((argc != 2) && (argc != 3)) { fprintf(stderr, "Usage:\t%s device_number\n\t%s major minor\n" "Resolve a device number to a device name\n", argv[0], argv[0]); exit(1); } if (argc == 2) { devno = strtoul(argv[1], &tmp, 0); if (*tmp) { fprintf(stderr, errmsg, "device number", argv[1]); exit(1); } } else { major = strtoul(argv[1], &tmp, 0); if (*tmp) { fprintf(stderr, errmsg, "major number", argv[1]); exit(1); } minor = strtoul(argv[2], &tmp, 0); if (*tmp) { fprintf(stderr, errmsg, "minor number", argv[2]); exit(1); } devno = makedev(major, minor); } printf("Looking for device 0x%04llx\n", (long long)devno); devname = blkid_devno_to_devname(devno); free(devname); return 0; } #endif e2fsprogs-1.41.14/lib/blkid/libblkid.3.in0000644031104000366760000000562711240667355015755 0ustar tytso.\" Copyright 2001 Andreas Dilger (adilger@turbolinux.com) .\" .\" This man page was created for libblkid.so.1.0 from e2fsprogs-1.24. .\" .\" This file may be copied under the terms of the GNU Public License. .\" .\" Created Wed Sep 14 12:02:12 2001, Andreas Dilger .TH LIBBLKID 3 "@E2FSPROGS_MONTH@ @E2FSPROGS_YEAR@" "E2fsprogs version @E2FSPROGS_VERSION@" .SH NAME libblkid \- block device identification library .SH SYNOPSIS .B #include .sp .B cc .I file.c .B \-lblkid .SH DESCRIPTION The .B libblkid library is used to identify block devices (disks) as to their content (e.g. filesystem type) as well as extracting additional information such as filesystem labels/volume names, unique identifiers/serial numbers, etc. A common use is to allow use of LABEL= and UUID= tags instead of hard-coding specific block device names into configuration files. .P Block device information is normally kept in a cache file .I /etc/blkid.tab and is verified to still be valid before being returned to the user (if the user has read permission on the raw block device, otherwise not). The cache file also allows unprivileged users (normally anyone other than root, or those not in the "disk" group) to locate devices by label/id. The standard location of the cache file can be overridden by the environment variable BLKID_FILE. .P In situations where one is getting information about a single known device, it does not impact performance whether the cache is used or not (unless you are not able to read the block device directly). If you are dealing with multiple devices, use of the cache is highly recommended (even if empty) as devices will be scanned at most one time and the on-disk cache will be updated if possible. There is rarely a reason not to use the cache. .P In some cases (modular kernels), block devices are not even visible until after they are accessed the first time, so it is critical that there is some way to locate these devices without enumerating only visible devices, so the use of the cache file is .B required in this situation. .SH AUTHOR .B libblkid was written by Andreas Dilger for the ext2 filesystem utilties, with input from Ted Ts'o. The library was subsequently heavily modified by Ted Ts'o. .SH FILES .TP .I /etc/blkid.tab Caches data extracted from each recognized block device. .SH AVAILABILITY .B libblkid is part of the e2fsprogs package since version 1.33 and is available from http://e2fsprogs.sourceforge.net. .SH COPYING .B libblkid is available under the terms of the GNU Library General Public License (LGPL), version 2 (or at your discretion any later version). A copy of the LGPL should be included with this library in the file COPYING. If not, write to .RS Free Software Foundation, Inc. .br 51 Franklin St .br Fifth Floor .br Boston, MA 02110-1301 USA .RE .PP or visit .UR http://www.gnu.org/licenses/licenses.html#LGPL http://www.gnu.org/licenses/licenses.html#LGPL .UE .SH "SEE ALSO" .BR blkid (8) e2fsprogs-1.41.14/lib/blkid/llseek.c0000644031104000366760000000562611240667355015132 0ustar tytso/* * llseek.c -- stub calling the llseek system call * * Copyright (C) 1994, 1995, 1996, 1997 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the * GNU Lesser General Public License. * %End-Header% */ #define _LARGEFILE_SOURCE #define _LARGEFILE64_SOURCE #if HAVE_SYS_TYPES_H #include #endif #if HAVE_ERRNO_H #include #endif #if HAVE_UNISTD_H #include #endif #ifdef __MSDOS__ #include #endif #include "blkidP.h" #ifdef __linux__ #if defined(HAVE_LSEEK64) && defined(HAVE_LSEEK64_PROTOTYPE) #define my_llseek lseek64 #elif defined(HAVE_LLSEEK) #include #ifndef HAVE_LLSEEK_PROTOTYPE extern long long llseek(int fd, long long offset, int origin); #endif #define my_llseek llseek #else /* ! HAVE_LLSEEK */ #if SIZEOF_LONG == SIZEOF_LONG_LONG #define llseek lseek #else /* SIZEOF_LONG != SIZEOF_LONG_LONG */ #include #ifndef __NR__llseek #define __NR__llseek 140 #endif #ifndef __i386__ static int _llseek(unsigned int, unsigned long, unsigned long, blkid_loff_t *, unsigned int); static _syscall5(int, _llseek, unsigned int, fd, unsigned long, offset_high, unsigned long, offset_low, blkid_loff_t *, result, unsigned int, origin) #endif static blkid_loff_t my_llseek(int fd, blkid_loff_t offset, int origin) { blkid_loff_t result; int retval; #ifndef __i386__ retval = _llseek(fd, ((unsigned long long) offset) >> 32, ((unsigned long long)offset) & 0xffffffff, &result, origin); #else retval = syscall(__NR__llseek, fd, ((unsigned long long) offset) >> 32, ((unsigned long long)offset) & 0xffffffff, &result, origin); #endif return (retval == -1 ? (blkid_loff_t) retval : result); } #endif /* __alpha__ || __ia64__ */ #endif /* HAVE_LLSEEK */ blkid_loff_t blkid_llseek(int fd, blkid_loff_t offset, int whence) { blkid_loff_t result; static int do_compat = 0; if ((sizeof(off_t) >= sizeof(blkid_loff_t)) || (offset < ((blkid_loff_t) 1 << ((sizeof(off_t)*8) -1)))) return lseek(fd, (off_t) offset, whence); if (do_compat) { errno = EOVERFLOW; return -1; } result = my_llseek(fd, offset, whence); if (result == -1 && errno == ENOSYS) { /* * Just in case this code runs on top of an old kernel * which does not support the llseek system call */ do_compat++; errno = EOVERFLOW; } return result; } #else /* !linux */ #ifndef EOVERFLOW #ifdef EXT2_ET_INVALID_ARGUMENT #define EOVERFLOW EXT2_ET_INVALID_ARGUMENT #else #define EOVERFLOW 112 #endif #endif blkid_loff_t blkid_llseek(int fd, blkid_loff_t offset, int origin) { #if defined(HAVE_LSEEK64) && defined(HAVE_LSEEK64_PROTOTYPE) return lseek64 (fd, offset, origin); #else if ((sizeof(off_t) < sizeof(blkid_loff_t)) && (offset >= ((blkid_loff_t) 1 << ((sizeof(off_t)*8) - 1)))) { errno = EOVERFLOW; return -1; } return lseek(fd, (off_t) offset, origin); #endif } #endif /* linux */ e2fsprogs-1.41.14/lib/blkid/tag.c0000644031104000366760000002445211240667355014424 0ustar tytso/* * tag.c - allocation/initialization/free routines for tag structs * * Copyright (C) 2001 Andreas Dilger * Copyright (C) 2003 Theodore Ts'o * * %Begin-Header% * This file may be redistributed under the terms of the * GNU Lesser General Public License. * %End-Header% */ #include #include #include #include #include "blkidP.h" static blkid_tag blkid_new_tag(void) { blkid_tag tag; if (!(tag = (blkid_tag) calloc(1, sizeof(struct blkid_struct_tag)))) return NULL; INIT_LIST_HEAD(&tag->bit_tags); INIT_LIST_HEAD(&tag->bit_names); return tag; } #ifdef CONFIG_BLKID_DEBUG void blkid_debug_dump_tag(blkid_tag tag) { if (!tag) { printf(" tag: NULL\n"); return; } printf(" tag: %s=\"%s\"\n", tag->bit_name, tag->bit_val); } #endif void blkid_free_tag(blkid_tag tag) { if (!tag) return; DBG(DEBUG_TAG, printf(" freeing tag %s=%s\n", tag->bit_name, tag->bit_val ? tag->bit_val : "(NULL)")); DBG(DEBUG_TAG, blkid_debug_dump_tag(tag)); list_del(&tag->bit_tags); /* list of tags for this device */ list_del(&tag->bit_names); /* list of tags with this type */ free(tag->bit_name); free(tag->bit_val); free(tag); } /* * Find the desired tag on a device. If value is NULL, then the * first such tag is returned, otherwise return only exact tag if found. */ blkid_tag blkid_find_tag_dev(blkid_dev dev, const char *type) { struct list_head *p; if (!dev || !type) return NULL; list_for_each(p, &dev->bid_tags) { blkid_tag tmp = list_entry(p, struct blkid_struct_tag, bit_tags); if (!strcmp(tmp->bit_name, type)) return tmp; } return NULL; } extern int blkid_dev_has_tag(blkid_dev dev, const char *type, const char *value) { blkid_tag tag; if (!dev || !type) return -1; tag = blkid_find_tag_dev(dev, type); if (!value) return (tag != NULL); if (!tag || strcmp(tag->bit_val, value)) return 0; return 1; } /* * Find the desired tag type in the cache. * We return the head tag for this tag type. */ static blkid_tag blkid_find_head_cache(blkid_cache cache, const char *type) { blkid_tag head = NULL, tmp; struct list_head *p; if (!cache || !type) return NULL; list_for_each(p, &cache->bic_tags) { tmp = list_entry(p, struct blkid_struct_tag, bit_tags); if (!strcmp(tmp->bit_name, type)) { DBG(DEBUG_TAG, printf(" found cache tag head %s\n", type)); head = tmp; break; } } return head; } /* * Set a tag on an existing device. * * If value is NULL, then delete the tagsfrom the device. */ int blkid_set_tag(blkid_dev dev, const char *name, const char *value, const int vlength) { blkid_tag t = 0, head = 0; char *val = 0; char **dev_var = 0; if (!dev || !name) return -BLKID_ERR_PARAM; if (!(val = blkid_strndup(value, vlength)) && value) return -BLKID_ERR_MEM; /* * Certain common tags are linked directly to the device struct * We need to know what they are before we do anything else because * the function name parameter might get freed later on. */ if (!strcmp(name, "TYPE")) dev_var = &dev->bid_type; else if (!strcmp(name, "LABEL")) dev_var = &dev->bid_label; else if (!strcmp(name, "UUID")) dev_var = &dev->bid_uuid; t = blkid_find_tag_dev(dev, name); if (!value) { if (t) blkid_free_tag(t); } else if (t) { if (!strcmp(t->bit_val, val)) { /* Same thing, exit */ free(val); return 0; } free(t->bit_val); t->bit_val = val; } else { /* Existing tag not present, add to device */ if (!(t = blkid_new_tag())) goto errout; t->bit_name = blkid_strdup(name); t->bit_val = val; t->bit_dev = dev; list_add_tail(&t->bit_tags, &dev->bid_tags); if (dev->bid_cache) { head = blkid_find_head_cache(dev->bid_cache, t->bit_name); if (!head) { head = blkid_new_tag(); if (!head) goto errout; DBG(DEBUG_TAG, printf(" creating new cache tag head %s\n", name)); head->bit_name = blkid_strdup(name); if (!head->bit_name) goto errout; list_add_tail(&head->bit_tags, &dev->bid_cache->bic_tags); } list_add_tail(&t->bit_names, &head->bit_names); } } /* Link common tags directly to the device struct */ if (dev_var) *dev_var = val; if (dev->bid_cache) dev->bid_cache->bic_flags |= BLKID_BIC_FL_CHANGED; return 0; errout: if (t) blkid_free_tag(t); else free(val); if (head) blkid_free_tag(head); return -BLKID_ERR_MEM; } /* * Parse a "NAME=value" string. This is slightly different than * parse_token, because that will end an unquoted value at a space, while * this will assume that an unquoted value is the rest of the token (e.g. * if we are passed an already quoted string from the command-line we don't * have to both quote and escape quote so that the quotes make it to * us). * * Returns 0 on success, and -1 on failure. */ int blkid_parse_tag_string(const char *token, char **ret_type, char **ret_val) { char *name, *value, *cp; DBG(DEBUG_TAG, printf("trying to parse '%s' as a tag\n", token)); if (!token || !(cp = strchr(token, '='))) return -1; name = blkid_strdup(token); if (!name) return -1; value = name + (cp - token); *value++ = '\0'; if (*value == '"' || *value == '\'') { char c = *value++; if (!(cp = strrchr(value, c))) goto errout; /* missing closing quote */ *cp = '\0'; } value = blkid_strdup(value); if (!value) goto errout; *ret_type = name; *ret_val = value; return 0; errout: free(name); return -1; } /* * Tag iteration routines for the public libblkid interface. * * These routines do not expose the list.h implementation, which are a * contamination of the namespace, and which force us to reveal far, far * too much of our internal implemenation. I'm not convinced I want * to keep list.h in the long term, anyway. It's fine for kernel * programming, but performance is not the #1 priority for this * library, and I really don't like the tradeoff of type-safety for * performance for this application. [tytso:20030125.2007EST] */ /* * This series of functions iterate over all tags in a device */ #define TAG_ITERATE_MAGIC 0x01a5284c struct blkid_struct_tag_iterate { int magic; blkid_dev dev; struct list_head *p; }; extern blkid_tag_iterate blkid_tag_iterate_begin(blkid_dev dev) { blkid_tag_iterate iter; iter = malloc(sizeof(struct blkid_struct_tag_iterate)); if (iter) { iter->magic = TAG_ITERATE_MAGIC; iter->dev = dev; iter->p = dev->bid_tags.next; } return (iter); } /* * Return 0 on success, -1 on error */ extern int blkid_tag_next(blkid_tag_iterate iter, const char **type, const char **value) { blkid_tag tag; *type = 0; *value = 0; if (!iter || iter->magic != TAG_ITERATE_MAGIC || iter->p == &iter->dev->bid_tags) return -1; tag = list_entry(iter->p, struct blkid_struct_tag, bit_tags); *type = tag->bit_name; *value = tag->bit_val; iter->p = iter->p->next; return 0; } extern void blkid_tag_iterate_end(blkid_tag_iterate iter) { if (!iter || iter->magic != TAG_ITERATE_MAGIC) return; iter->magic = 0; free(iter); } /* * This function returns a device which matches a particular * type/value pair. If there is more than one device that matches the * search specification, it returns the one with the highest priority * value. This allows us to give preference to EVMS or LVM devices. */ extern blkid_dev blkid_find_dev_with_tag(blkid_cache cache, const char *type, const char *value) { blkid_tag head; blkid_dev dev; int pri; struct list_head *p; int probe_new = 0; if (!cache || !type || !value) return NULL; blkid_read_cache(cache); DBG(DEBUG_TAG, printf("looking for %s=%s in cache\n", type, value)); try_again: pri = -1; dev = 0; head = blkid_find_head_cache(cache, type); if (head) { list_for_each(p, &head->bit_names) { blkid_tag tmp = list_entry(p, struct blkid_struct_tag, bit_names); if (!strcmp(tmp->bit_val, value) && (tmp->bit_dev->bid_pri > pri) && !access(tmp->bit_dev->bid_name, F_OK)) { dev = tmp->bit_dev; pri = dev->bid_pri; } } } if (dev && !(dev->bid_flags & BLKID_BID_FL_VERIFIED)) { dev = blkid_verify(cache, dev); if (!dev || (dev && (dev->bid_flags & BLKID_BID_FL_VERIFIED))) goto try_again; } if (!dev && !probe_new) { if (blkid_probe_all_new(cache) < 0) return NULL; probe_new++; goto try_again; } if (!dev && !(cache->bic_flags & BLKID_BIC_FL_PROBED)) { if (blkid_probe_all(cache) < 0) return NULL; goto try_again; } return dev; } #ifdef TEST_PROGRAM #ifdef HAVE_GETOPT_H #include #else extern char *optarg; extern int optind; #endif void usage(char *prog) { fprintf(stderr, "Usage: %s [-f blkid_file] [-m debug_mask] device " "[type value]\n", prog); fprintf(stderr, "\tList all tags for a device and exit\n"); exit(1); } int main(int argc, char **argv) { blkid_tag_iterate iter; blkid_cache cache = NULL; blkid_dev dev; int c, ret, found; int flags = BLKID_DEV_FIND; char *tmp; char *file = NULL; char *devname = NULL; char *search_type = NULL; char *search_value = NULL; const char *type, *value; while ((c = getopt (argc, argv, "m:f:")) != EOF) switch (c) { case 'f': file = optarg; break; case 'm': blkid_debug_mask = strtoul (optarg, &tmp, 0); if (*tmp) { fprintf(stderr, "Invalid debug mask: %s\n", optarg); exit(1); } break; case '?': usage(argv[0]); } if (argc > optind) devname = argv[optind++]; if (argc > optind) search_type = argv[optind++]; if (argc > optind) search_value = argv[optind++]; if (!devname || (argc != optind)) usage(argv[0]); if ((ret = blkid_get_cache(&cache, file)) != 0) { fprintf(stderr, "%s: error creating cache (%d)\n", argv[0], ret); exit(1); } dev = blkid_get_dev(cache, devname, flags); if (!dev) { fprintf(stderr, "%s: Can not find device in blkid cache\n", devname); exit(1); } if (search_type) { found = blkid_dev_has_tag(dev, search_type, search_value); printf("Device %s: (%s, %s) %s\n", blkid_dev_devname(dev), search_type, search_value ? search_value : "NULL", found ? "FOUND" : "NOT FOUND"); return(!found); } printf("Device %s...\n", blkid_dev_devname(dev)); iter = blkid_tag_iterate_begin(dev); while (blkid_tag_next(iter, &type, &value) == 0) { printf("\tTag %s has value %s\n", type, value); } blkid_tag_iterate_end(iter); blkid_put_cache(cache); return (0); } #endif e2fsprogs-1.41.14/lib/blkid/test_probe.in0000644031104000366760000000274311240667355016202 0ustar tytso TESTS=$* if test "$TESTS"x = x ; then for i in $SRCDIR/tests/*.img.bz2 do TESTS="$TESTS `basename $i .img.bz2`" done fi mkdir -p tests/tmp for i in $TESTS do printf "%s: " $i RESULTS=$SRCDIR/tests/$i.results IMAGE_BZ2=$SRCDIR/tests/$i.img.bz2 IMAGE=tests/tmp/$i.img.$$ if test ! -f $IMAGE_BZ2 -a ! -f $RESULTS ; then echo "non-existent" continue fi if [ "$i" = "swap0" ]; then # swap is native-endian, so regenerate before testing dd if=/dev/zero of=$IMAGE bs=16k count=64 2> /dev/null mkswap -v0 $IMAGE > /dev/null elif [ "$i" = "swap1" ]; then # swap is native-endian, so regenerate before testing dd if=/dev/zero of=$IMAGE bs=16k count=64 2> /dev/null # check if mkswap supports the "-U" option if mkswap -h 2>&1 | grep -q -- '-U'; then UUID="-U 8ff8e77f-8553-485e-8656-58be67a81666" else RMUUID="| grep -v UUID" RES_TMP=$SRCDIR/tests/tmp/$i.results grep -v UUID $RESULTS > $RES_TMP RESULTS=$RES_TMP fi mkswap -v1 -L SWAP-TEST $UUID $IMAGE >/dev/null else bunzip2 < $IMAGE_BZ2 > $IMAGE fi eval ./tst_probe $IMAGE $RMUUID > tests/$i.out rm -f $IMAGE tests/$i.ok tests/$i.failed cmp -s tests/$i.out $RESULTS unset RMUUID if [ $? = 0 ]; then echo ok touch tests/$i.ok else echo failed diff -c tests/$i.out $RESULTS > tests/$i.failed fi done num_ok=`ls tests/*.ok 2>/dev/null | wc -l` num_failed=`ls tests/*.failed 2>/dev/null | wc -l` echo "$num_ok tests succeeded $num_failed tests failed" test "$num_failed" -eq 0 || exit 1 e2fsprogs-1.41.14/lib/blkid/tests/0000755031104000366760000000000011240667355014640 5ustar tytsoe2fsprogs-1.41.14/lib/blkid/tests/fat32_label_64MB.results0000644031104000366760000000005311240667355021067 0ustar tytsoTYPE='vfat' LABEL='BINGO' UUID='8CB5-BA49' e2fsprogs-1.41.14/lib/blkid/tests/reiser4.results0000644031104000366760000000011211240667355017632 0ustar tytsoTYPE='reiser4' LABEL='TESTR4' UUID='9722633c-d69a-4881-b1c8-bedecbbf39d2' e2fsprogs-1.41.14/lib/blkid/tests/jbd.img.bz20000644031104000366760000000031511240667355016570 0ustar tytsoBZh91AY&SYeé<å ~ÿÿÞ@,H")A€@@D HB€‚0¹0”Šh=@ÐSFÔP= é h&€Ðd€’" 4dÐ#íŸ|eú‹™¯VKK …»B››ŠV_ $˜€ƒ  Âä•J‘5èYP'X¦¼[f„þùÌpr<ì•U‚Ç×"$8†°´W¡Ò/J9Ë(¬» ¦gP\2Wd`ˆ€‹¹"œ(H2ôžr€e2fsprogs-1.41.14/lib/blkid/tests/cramfs.results0000644031104000366760000000004011240667355017530 0ustar tytsoTYPE='cramfs' LABEL='test-cram' e2fsprogs-1.41.14/lib/blkid/tests/jfs.img.bz20000644031104000366760000000655211240667355016624 0ustar tytsoBZh91AY&SYhe5°ÚÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿàŸ7WÀªdÀ€?ÕT&i1 ‰¦ €a4Á0˜ ‰èhi†“h‰‚dÂ`™0`„À`€&&&L&Ò`ÐÐLÐ ˜&L`À& €˜“ÀLT‘@ ÁL¦Ú™OLi'¤Ù'êz(õHÚž¡£&M£ÊGªd Ô©=G¢zG¢yFƒõ4˜M4Ð6¡“Òƒ@4‘4 HÈØõ&§ÅAwúOë`u;ÜÎ¥¸<0s@îên@rƒ9|{°F p ‚# °hˆ€¼'ŒŒìù‰)9EQYh©}˜(`âŠx|€$`: •–m®¯pqrst4õö÷ø¹º»|R”T”À*ùª*«+¬,­-®.¯/°0±1²2û™™Ú=áZZšÃ6mnßðøˆpqræ'äè)ÕÙÝáåéíñõùþ \doâ:>BFJNRT ·æ^bfjläìð9ú /Õ JZjzŠšªºÊÚêû +0¶–¿ŸÑ2…K0fpѹӇO< $i¦N¡J¥k®^Á‹&m¶ná˧o¾ (q'§âÆ"L©s&ßð™2`¡B€©R ±bÀ¹ràÁƒÉ“"rrp4h6lNÎ΃‡§NƒÇçÏ„(P„H‘hÑ„‰©R„É“éÓ… *T…J•jÕ…‹«V…Ë—ë׆ ,X†L™lÙ†­Z†Í›íÛ‡.\‡Nn݇¯^‡ÏŸï߈ 0`ˆP¡pሑ" ééá??>"ÅŠ#FŒ#ÇŽ$H$É’%J”%Ë–&L˜&Í›ë ‚#úJYA ‰ï DþA ø7ãvÁÛûþ¦Úg íÓù éÈ.Â\ùÍ[gt¯{‹éÝ$ì×X |°6|ïÚ×äN9AØ]ÏU_fph+*ꃶÀ=ð8Üóü‘„+Wt *®Pm e jÊæq-­ê«˜ ~VhY%’«·Ýv­ [ƒجR» dù‹¡j•W‰xÙj$¿Ð ް1Jv»Z’Oo…'¹bªuÙ*N¾R{cÄç÷ûHëY { x+l…]Œ•#ÍxZºùn‚ ìÝÖgSÍx[ï¾ûë}º ãPW÷͸«km%KWãÝ/ºÚ7PW&)Uh«Ÿ«jª­æUg‡—(¯9jÐ3*ª¹ª¸ÕÉZŠ­-[ƒjUu8Z¶dhjmŠ«¨ ÀØ uÁË@Òªñ:¼` ÀbŒ }À;øAvÁÓ¢ ¹íƒ_°Sˆ=܆ öµ l ZÇõäìÁò€ÚøðoP?S@×H¦`i€Åd ´ «– kP2Àe g¤°6Ô ­*«@Ì04 hõàÊU¸7ÀÜÜÜ|íæïÖƒ~·ƒª°;ý¼Mù|Ø96J¸ú-ñ}ý_¤çð;ïø ÇqW®à7”GK…´ ë@ã«`m„+M@óàØ `i0(¬_†Î¾kÃÓ¡·o[pô`Ð0ß89mÕW?¡óÿxq½ÑÚéw7áún…ÿ`eÖÇíÊ샀7»Y}ˆ>¸•W.}Ú 0¯¢÷,mzpfùt,¢½À°X;èLÅådý­™‘éØÎÎ žÐ=§Äˆ;½k/É]Ø5Ôüô”Üs98ƒm°5g&míiWÔ+m³8ƒ\?1k΃8~^µ¯­µ¯„ rrƒÖƒ·¾´Öf®w Íó33oþ[ffu|€w?99Á z8®^¨5¬nk*«“º€à‚ÚÀÔ  `5oåꪵû-kD_?·øáé`k_ƒkYæ ÄÀÖùžWÆí¶oåZ@Û3ç­}?d ïˆ/Õƒç€ýü01½ l‚܂؃pl ßpl‚¾l x\€à ÂxñA°4šAt:z!JëƒU€Ê*V¹33?ío¡\›¸Üqõ`òï¶ù™ÉšøÖ¸P5¬ß5™•U˜Pt`k|¶7ÌÌé@éƒ8~P5ÃÏWËó}þ~µ¬\•U¸5·“@÷—©aÆáVk9sYH×@ÖÿB²hpòê€ó€õ áÃ÷ Ö½íz°{êܺ5UØíkú–÷âßd sø`åŽ Á𯵮>]­v®LÛ@ù˜m™›|Ø4 =P3|ÏJ k[8cê®Åß&å¿'ɃZÌîO¿¬p·æðA¨ ÎOIÎ áÖƒ@ãÇüTQm»ÿŠ ¸ú`9€Ø$äó°<ÀzXïö`Ö¿D ÜØ –ù™™®È:Ø: ᙙظæÜ­j­fÞ`5·± |…›}M­yv¹*«6ñA­fs[ÕWDïE"Zá™™›sÛƒ@Àq~™™¾k3èµ{ }Á ÖüHW¾·'Ú\333of¡É~€kY™¾ÀÖß3pŒ ÷ùè¤mª)2݃no•½ ƒeUäƒÁ³·6J¬ô Ö¿>IKÙ@ÛŸØ\?^ kÿ`Ö½]\rÀt­òª³3$Fþmow—±ÕÞqåý sgu5þ`kYÃìVmìÖ m·¸lmæ@ë&[sv¨Ín9™š¶ë[æeUftÁ›æÙffUV³^Fµ™‹l÷~(5¾WŒ ¨öð>`{@ú0o õ€ï€ÒN_¿ç z€{Ð^ò Ð8@ûàhˆô è  zàt€÷@ÚÓéû`uÁˆ/ª5ðÁäƒ`p'}€ð`däá€<ð8@â @üà8ÀÔ Ä¨Oz܃m±Ó®¶Ï’àïmº_rçƒíAì x Üáý°pÀ=>¼ƒÛ_Â04‡02éŒ ëã¼k‘õÊëk¼zï¢^ Áʪ«ú@ô:yXÌ^ÛÃÍU{PeR-d÷V*O®ÉUW¦öúk\6‹­òë¼_ÎúŽäégv žªAtªé\|]~­®lÕsY¯ÿ‹¹"œ(H42šØe2fsprogs-1.41.14/lib/blkid/tests/small-fat32.results0000644031104000366760000000005611240667355020311 0ustar tytsoTYPE='vfat' LABEL='TESTVFAT' UUID='1423-AAE1' e2fsprogs-1.41.14/lib/blkid/tests/iso.results0000644031104000366760000000004011240667355017047 0ustar tytsoTYPE='iso9660' LABEL='test-iso' e2fsprogs-1.41.14/lib/blkid/tests/zfs.results0000644031104000366760000000001311240667355017057 0ustar tytsoTYPE='zfs' e2fsprogs-1.41.14/lib/blkid/tests/ext2.results0000644031104000366760000000011211240667355017137 0ustar tytsoTYPE='ext2' LABEL='test-ext2' UUID='22f0eac3-5c89-4ec1-9076-60799119aaea' e2fsprogs-1.41.14/lib/blkid/tests/reiser4.img.bz20000644031104000366760000000055611240667355017415 0ustar tytsoBZh91AY&SYÇb#p ÇÿÿÿÿÊÔ”E${ãN&ž  @!”H b"°0D%TÄÈL™0˜&™2iF›M2z†ƒA$I©©ê‘„P Ðh4i£@‡2hÐ4ÄdhbF˜ƒ Â c=‹5éÿm““F—FH$a QaÊÑ‚÷`@’gc¸JÓ$–.5N—€KEÅ€Å<· Æ •\I„ކ]Ѐ‘1QÔ`øuk@D”œvLÈg`›ëRz ;°H›~ÑBüU+Þ \iÙaYd…’Ëâírô†C†âWO•(eýsa¤HüÀ„ÅÞ;ki»;ÆÁr,Ïñ!¬2ø zWÊÑ8TdiGÅ®¯™#{ÉDm2‰â@BÈoÜj æ çC²® ,uY,çc˜§CS éMR©bÄ!"T¾À’^ŸâîH§ ìDne2fsprogs-1.41.14/lib/blkid/tests/swap0.results0000644031104000366760000000001411240667355017310 0ustar tytsoTYPE='swap' e2fsprogs-1.41.14/lib/blkid/tests/ext3.img.bz20000644031104000366760000000272111240667355016717 0ustar tytsoBZh91AY&SYÁue&*ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿàŸ õ½ò@ðä4ÀL0@ #LÀ˜L ¦ÈÀaJ4Ð ¡ Ðbh÷øýUU ¤½UQ¡¡‘ ÓCF†† @Äi ˜ ÈÈ ƒ# ÐÁ €Ð ƒ4b€0!“bJ{ýT•F 2i 4Ц „iˆÐ4`C@a0!“@Ð 1`CA€Fb Œ ÈÁ4€0˜ÀL šD›SS'êL¦@Ð`=LÔò=L Ð4Ðd0 Ó&€ÐÐhh1F#G¨c$ôƒÔÔddbbf¦ÔõMHÙõ·èšëlI²*Oj„ë„’.×5RÐõòLi¥ÝšG6é(­–ºiR@¡ˆI" „²È$¥HbJë_3a6„–D¢ã–´ìçÊÑ5“niw›Õ'KM{¾O¾à§p\q8BÙ”ç _‹ù€0'qqX)ê§ Êë +Kk‹«Ëì ,@XÙY™ÚZšÍ›6·7¸8ƒrQstuvwxyz{|}~úý† >ð0Pp°Ðñ A3Š‹ŒŽ‘’“”•üKKÌLÍ~fç'_g§è(h¨é)i©ê*jªë+k«ì,l¬Ïm-m­î.n®ï/o¯ð0p„0ñ1q±ò2r²óìóe³³ô4t´ô‚þiêjÛëkìlímînãooðpñqòrƒæçèéê3¯³·»¿Ã?ú/—ú?óBI$’V?…–ÒÎT´Uµ±D\\\Z#Æ € P DO0A` 9Îsœç9€ç9Ìpöh¢ŠDç9€ˆˆˆˆ€ @ˆ‚€  œ'9 „¥)JIJIJR”€€ÒsœÀ €RD(øåE D• €ŒcÆ1Œ@RŒ6ð¥FP#´ŒTÄÆ$‰¬IN`;B1©n©Í– Ë:‹ûê¶¿ãWtŸ¬Öÿ:Ýv¿aýlvUÕßÞοü°ÿos(š*ªªª´Á€–4à¢úcÿô[mž%ÓðÞ[7É™™™™™™™™™™™™™™™Ìffffffffffffff€~€/zy²ÈºË&Ì^ËË,þü_a«åöûŒÕUU_ªªªªªªªªªªªªªªªªªªªªªªªªªª¨Ù€È0 Ë1Û¬Ê.¿]o•u¶Y’]{u‘yƒ¶Ë-Àü–Ûgµqzf*B)Š8#hi‚D‚Þö‚СR@>˜A T1n¾’A„€®¾@%b F’,6GTÒëm»E 4°Á³”d®ì Œ„€.9‡€0)Æ$—ÈÀ Ð,» +‚}ÖD]ÚQ†‰…]de†wQSÓH‰X Œ±* ‚ï?å & â`Ø/ÕJ¼`0Àä,`€ÙÀ`Ð ðÁ-£AÎñ° 0tpIJCÄ Éƒ`ØÄ%²aù4hFµí§``F€Ö4@&„ÚbFþÐTÉî.äŠp¡!‚êÊLe2fsprogs-1.41.14/lib/blkid/tests/jfs.results0000644031104000366760000000011011240667355017035 0ustar tytsoTYPE='jfs' LABEL='test-jfs' UUID='9bf7b82e-7583-4c74-99a4-189a691f27b5' e2fsprogs-1.41.14/lib/blkid/tests/minix.results0000644031104000366760000000001511240667355017403 0ustar tytsoTYPE='minix' e2fsprogs-1.41.14/lib/blkid/tests/swap0.img.bz20000644031104000366760000000011011240667355017054 0ustar tytsoBZh91AY&SYbp ÖÈ*H€€) T@ÓAÅZÑ@UÍ@ô¤Ïk€mx»’)„ƒã€e2fsprogs-1.41.14/lib/blkid/tests/zfs.img.bz20000644031104000366760000002516511240667355016645 0ustar tytsoBZh91AY&SYVÖ¿Ï¥ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿà¿ûî¬}÷›pûÞûæë{ÞõÎí77[î÷W³UÒvï¾Ú¼ÚûÕï¾=ë·o_}Ýö®öòîU»Š=·Ûç{è}½lëo^ç¶ë2ÛK^ï{;µxESS4 £hÍSÚO@M4bdÚ‰äÓi4Âz›L#L§¦†&I´È0¢i©²dÂŒ FM5OÔÚ`4ÄÓ<ˆÚ2&h6§¦†€T"ªz'©å4ÃM'¦€Ñ™!6ȆIé„2ž†ši„ÒyO!©ãHÓ©é„4Ñèêz 4&ѤÄm24Òm&›I“L˜§é2hLÄ›Ò6‰¦ ÒoS(ESÔªŸšhÓL¦§ê0&)àžQ<™O •=?BIí5OôM2z €››FŒ‚cIMªxi¥=£ ©êObžM0§¦šdѦ!‰ê›A”<4dÌÔõB*šhÐ&F˜ŒLô4Òz›L€4hÍM¦še4§½HØ©øE?&Lš0LL &©ìdÓ#i„bdžÔi<52 …<§é0##R~$ÆŠ T1 MM©å=Sza4Êiè™OjOS4i6hL›I¦SdÔõ¤ÏL£&Œi1)š&(Ì“he iå=M´sãz•´™/£š“ùˆNßfõœd[¶nEÒÓ9j^¦zQQëûü,]|¡½ÔEG1¨æšøC’hh)ø¨;§ˆ½À2jÉ©ÄMštµM`ïÄû‡?rÇ\¾/o!žs/°Y™:'IEÅòYt³¨äøœøŽ50©, Õu]cÜ~6C+†½»Æ.‹É÷+,¼ ÛŒa:¥ Æ!$‘˜TwsrõŽ,Hš¶F¾8‘¡ !) !1”hI¸‘…æŒW‰‰ *Á@I( ˜’'FF0 0I o"+†@bdBÀQ‚Wó‚D'F)$Ñ$B›&nÁcˆæ0@ŽE'MA¸ fp›&† ¶% Ñ$(âN:R9(H D 1Œ @—œ0CBVåŒ ÀK1ZŒ¤"H†”× "‰šBdb)$Ït`2 Áš"I ¢Á$¹,~† Գ䰅‰‚ÁÇaä0=vûŽÂy…¾+;5Ñ lØ¿ÃApf"––…s TX$ v‚ aVš3”F~yÈ €Æ@Œ @ @ 0÷Èô`$–cæLÄt+yòsßÀºßx1}>·ß婼mâÓþ»ˆ¼Ùwë,]Çöî> 94$¨1 vl ccRšÜãà’9µˆ5ŽÒÒ R±d™ iW´Œ;SìŠ<ûÙ±%á¼;[ö),KÙiÍP~+t­"CHWö.‹ˆÀê°›f™Ä=£øžñ>u†„Ö—@IX1 µ‰,s4ÔÐ}È>Bñ»çc€¼– |Ä•å‡ýýð:wÈÆž`z 4|LÜSâüÇÏ+ül‘Ç;Úbf€B[n/·—÷ðWéBœÂå4‹Ø‹G$Sl^ƒuŒh¶ ‡@¹Æ¨jòE €ž†¥†›FLÊ<œ-Ø­³›àþø€â¶4¯/]FþįŲ2HϪøLÃ|ƒÂqg—Ë®€xø_æÝFlw_ëÖó6Ò ­ŒÀ6„K»‚¸+†Ú—H!'djVüÓ¯Üè¼¹§DÀFËZ¸ö ‹Ywr5~¬×t<írÎì@W³bv%–7&¢$ eÜ`Æ(|H:†T«=âÐáœË®Oe/ïÔO~\tþžÚí._">þh·e=ŽB=5½V~¤ùÄ•wqö¶y,t,srÀ¿‡¬F5Fôµ8wngçî𴣚ë/náKþomZM¤@Çë:\Èæ è–«ó„—ãtaW¨—$]E_`ˡߺûÏGŠ Ñ¦\&\¿R‡#Ž£ÐLý,Ê  "nÂÉ|@åߨ57Ožž"ÛVq:!~•RHšäbŸ$ãµgÐxõ"|ˆ&ûyÓVð‰\ôN=ä^žçµÈR! ê•™G™[ú^Ë]Ÿ>·zQã¢ÞëôS_ºC,iûÛ&‰‡Ü¢¯º1å?®¦4: ™»rI¥·Fib˜•î· 8&D+Sh!Õ൮pþ¸|õ Œ £_÷OñëÌ$¦=!b!Rð;À6Ül]Ç$Ù2YcKOW¨çÞð„ègŽ^vÞé>zâ@UÞyük—s]±ð<[Y©«=ÖFŸµÎ8ÙjMâw9ŠšÓSÑéû˜oôdKÉŸÆPÍRTÒ‰´&Äf˜ciV‚ ´  llF(6ÄDi(0lC6&Ói6›´˜ÑtüŽÎ:AtªêÄ:¨û>µ/ÃE·¬óp=æ`/fÎÌf”i¢º‘XqÐ9L÷r|ÔëcÊǦ3UW@L!¥r´û—Ûô –‚ŒõÒÄùñ1‘¯àÍÞѾY‘и€ ¢õ@ñ1õ{üKé?“ñ‚åæáæ p ~MkÊñ3ìƒrÙ½¿YB:_šƒkÚŸ:eÙÕ/¸Õ C,wµ<ªX‚õÀöRsèÎá©…+¯kïq®¦µÞ㛈xð5ì+‹†^æ@x3¦€îïØ FòøP†V³¨Ãîâ·ÛQ–Ê$UðŒ²ŽÂ"4èæ"HÁΑö59á5×—øí<Ó+5d®îÞ)¸´Ó„ŠóÚÃó€ÎÕÆd/,kZ9øi”4 ~F9%ÄÔ¨æ9r¨'³PV!‚)ÁC­Qw6Xäõš¬=‚< MW;Û¿¢i´UWêî£Êö°øÎS¡ƒ°ÐI§kV¼¾¿è†P0Ì*Í«a²¦TgñÄRnA›Á¼ì'1 ³Ù¹èÔ?i`è5¯I~þÒ°3YŸ«ÞQ6&n©|ø~Ê]cֶk†+.;6iƒC™4?;—ŽbF^ÿ¼ÆQ€Þøž ˆ'ùWa±}ÄÌ-Lb+³ßÀ”ÍèP3VS]l¤/E¿ÿu‹/ @´—\3ÀHñöp,Õ+ªKñW°íXøM¡&ÍT¹ý¯Y‹îÇ»Éòa~<=€åãf4akF¬7„mV\:…ošœ]„ŠWÂQÞfÖ-¸®¨‰{œŠ(åé¨iSJ«õé3 Æ]Š}Þ1ÀUÿ=«{}ë²ÄVêÕº%”7‘DºùÃVÊuÒR>Ñîȸ°•`•\öMºÎ«ÐèíÒ ^€€$:røOÜÖDÆ?|î\ýèYLŸ²ß¬y<”$Í ;ùD ¤]äLÓŸä!:Õ…À€‰ÔviåW¢œe<9¯Ìj¦ÝÖ¾y]Õ†Žç®L£ƒwsûæØ´X7ÖÇý?ª/‚ñÖ üÿ•¥Ñ—K¡Z[LòKPE1潘H~…|^œþ~ø€±õjȰ§´^›e´~lí:ÔŒ?á¦wa–Ŭ×á)YGWÔèíõê…g|ºá'3*i,{¯Qó’G8mңȘ6Cû*@²MÌ;ËVI¼—X/(n¤,J¶–Â*9íŽúÎn3:ª¶JIÂý±PQè¡zÊ9ëZ·|¥dåFdÀH Em&ÚÑôü`†—ƨ«ÐczÙÙ>…fcÉuov·Åá"iÞï_òí¢ƒ·!Ý”] g؇A¼½§ÆŸ6©„°°J§¼Fˆ:ÆþI]‘£¿º®Ëç~ùÅÎᮜ7Üüƒ=ñ+ ª¶xÌa¹]JŸ@Ɇ I@(0PKló/'¥¤Õžº}JãÁo|íÐ^4ac T!0(à¾2´Ù.Ô—[*‡¢ë¦„"å7ùœuІ ¾ôݱ½xó"Z Ã)T&×dA–»WE–` •çÔïCñÙ8\Úî´ŸOÐ9…Ó÷Êv)kÙóÈ ¢W€…Ø3³NÆMÝôÍuòÙ¡2#¯üX²¾ÆñŽåcåÏu·î4õun÷ôÓí¿uxÔè‡çš 4¹‰8ݨ \",•h.H^é’«¼hΡÂLìÒH~dŒ›!1ëQrˆ|Ãâ…õzL;ê¡äéÆ?ÀƒèÎåÇo ‹¡*¬zÁLýªwö3Û/;:î3|‘Ïe ƒÑú3…4φ@3?œlŸYÓGçýñ…ŸßeŒÓ+9Éè6/Ÿ´~ÿ†ñr<ë¸)Y»p¨ìÈtÒë´æC³³øåÖu`²4zH]FKJû[À ½é?ÍÚ”aHÍ7.KJ¼ùº_÷z”®${^u˰Å‘ÿZ/‹=A!>ºÕ— &Bã0×åž-çà5¨þ2nKLU‰W1•ZD¼¢[% Ù®‹Ü …<>Cþáà§½ñÌCÆSR_¿SHžˆƒü{‰{õ™Lk”€óþA ~å(ìK¢Iþ%’á|WÎ7n®Ö<¸çú˜ Ô±îv¿ƒ–ÊŠžñÉÕßÈwr_0·|úô‰Þö‡bÝ9ÆÈ®ÛSvHÎI>T¶[„“™Ì„û k&ÖH/³dbí34—°~¯³n u^M¥ßKo¶¯õdÿC‰|"aVŒ¤øuóhuèÐÇÀ±qû- üÎ.Ÿnª0qHèÜÁb:(-ªÄ*wþ&~ëÏo?`Çl¬ÐÜžÚižäãÙÀÖƒ±rÛWürV»nñ4í\"ç÷ï'K }ñMñ°£÷rB>_Jñ“J`ÙËCÛx©}Bœ@訓žcÞw7:œ“ ÆA“aU)ú㟳¤²{QÿÆ¿¢rR_]æçµ"Âø67ø£÷âÒßr%Z,‘ ¹õ(àô#×ôÃà,®7ugbC}üUY—öPÛ÷ +=á,Ãùòׯh!'#Æ’,1æýåR»ýÕ¹ågã´ü3D7«Øûz¼žrRw«æl«'mê¶"…—¼åÕã³Ô"”6_\Ö [ŒAMGIÄ ndO½¸kyÙùδEµ>yמN2 µ{ø÷©`í‰'H‚‹–}~—niw~ÊûvŸ+€jÜÚé¬ôÍMŸ“*¼Ò]>&ñÆ…c—´8±NNhrž•­ ¯á÷*Ø`Jáù~ · óƒøÕ×ó-ös!Y–Ä?‘ñ ,óLøouÙñèkÉs`懲¶QÞ/qÚW|‚z­æ{F&ãÝÅJ4€Ksô™n>™ÏI–|dìJ—‚_CËÐáúÃ0ª++¸o©—[¾bB2màqIÙ…õŠ+Æl±åȬñ4m~Âä¥ ™:Q•â o:†Ú »8ð¡ß¦ôƒËÿbâU…æÚž­òÒùhu¾fEЬdL¯…F³«àõgBªb…[ÖéujmãΙ=g•ÈÔzR-Š^ºõÓßù–jÿ=;ÁU?éÚ<¼;½A[úÏw ¯áQ2í¥üî¤WQµF ”¾m Ìž9´Mn®&ãS9: R1kýúro‡»¡CŽ^;;™Ù´;Á“¿{{ æM¾h±- ÊhSydÉXüþÜkò*¯Mwz™J"¼ö ;@™L‹ç]îÊú…^çê¥úu%«{‚ÒP‡Ex„ýABœV×—Ò=¸©0Еnû`¥à²$}‰«‡ê A6`>ýyÎÐë½]Bõ–Æ‰Š °E —ôðÖD|Þ¹3‚"°t€p>ÁòVx»'NÖ—Ý…ÉõH‡¹®É>ÇVÖ;¹}ü}±¼ÿ"ÿ}:qÔŽB¢2ûþQì«`2‚9‰×¡Ž§Òrzú.Ÿµ÷¯z^ïƒv y×™¯¾˜29FŠ´X(¤˜.%L.(·Ï¢`‹É¸QœÇÇê£þV{§9%ƒÌ¦éKǾ*Ûhµ0 ?²ÉìÀE”°¹G(쳈¦$ÿ&%+΋dåÌq®±åÓåµäKáéí(½» ô½ï:®lÓ’ä]èÎtp˜T”fPëË0Œšª%©d|A>¿è195ôô[(?‚HS´‚8{sþÛ—&}h 4@ð¸×ét%QsÒÊÊ'ÂP¬¬i©¼³y°7Ü ÔïÍ6ø¸_ð—© ï7I¬íÃFØ€¥p×:zmÛ®€Ïh¤q´y ¨ Λú2—°R¸8·ñ“ fX°¤›ÆRÚK6übèà‚.pèuYäî»FÚ*}Òßêyÿràý Ìè)& Ê&LAHœB†ðyŒVÄÐÙlwÄäô+ÂåYô[ zí™BÔу¡íô– MøFû{ªgoDE¤½ÔÕzŠsNù¤RD†‡^ûÁggÏ{‡¹:a7¤–HÊ:hZZ1%ØoWýÓ¯·—³)áeÃsˆÞ)Ä3àyMh¯Ù`| GĈ§aYƒøž8GÖseq [H:Oåà>¬0e£Þ÷X<¼\Ã2l?VÊ<ñ‘—Å$ØÚZÉô»\( Ó™çèçŠmW«xËnp`Rh|¦Âc"Ä’~ˆä x†šœŒgOoØý¡³f>HP×K¸FÒ×­ \÷|u(‘£ç.m·">—4&CçLä½ßp«ÕëRN¶“¬Aà>ôâ±ßUø©+Þ'è@!µÌŠ®ëQÞ¾ÔR6§‘„×,µ–P¶mMäí0-}̬Ä⫵vf[öyöo&༲Jý¸îGGó‹ 3ÇÓ•„”ðÛx€ ‹q޽{½õ„ΊæËìOùÊ~ß[ª|jÍpx“LVI?‹¸u*,GÎNÀ9&÷Ã8ÏI®³MxØIGŒj]Óg"óHΰr»‡ó#ÙóV:X<ž¥ àÈk°È7Yg­Œ¨³Fü`„+á@Ð(åéºÒkÉ»%ÑaMv6˜ªÿ)¬Š>çTš•›…Ç?cc³y¥f-yÝÔ ßõø b;—íUÌÍ2‘½f@É®ÃëËa™ðVàQ¯°ìf.UZ$ï~{­®Ë¥¼_kó ~¼@Œ¾ŠŠU¥3¿#} xS¶.ÌÿžL½‚lg±³<l.F£­ä*éàŽÍµ ,¦ä·³M³—.VÙ`®âÄ:V Ð÷»'½€A¸JJPá¾æƒÜÅ÷YÛÄ[‡pžo2w¡¤Áª*ð#)igükxù©¸)?”™ãš·™ ¹Ç1FÖ¥Ò%©ôä×]ÈKùŸE±×6'ZÓ„zø"I—ySY¸dö8£ôy£ècJw?{-n²yÆš›)1£©‡>±’7ç2ÿòC÷©¨Þ á­´õšµÆ^î±ÓWÇK®h«ùfQ/]Zû× ¦x†~º?\w×áÁ:î’JLbÑ [¾»_KL?a[е!¹ÌyÌOôeè»›ÙZ } h} Gn‡7{P&>¼r$P¶\KÖSA§W €X½}°ã™}ˆôŸ ÊŒhY ¤D´ðƒÛ‰>œÛ4×[« ‚‚øÚ¢Ä4Ö¼vŸ±ãèvªw£HÀoeœ¬?HÉê¾ñW I‚û+ÒM²Œ„DhÙÔú¨špv8uƱÛï½MFÁ MrãX£e˜ª€íBDDtx:¸žÆ³ç ÄT/Wºó®ª3•#ÐÞ>Þ¡Wöž‡‹iq=ïÕmÿ(I·Á†¢t+ŸÅYˆÜ,••¤}i F:~¬1sV*.|¯6ñ ѨTýÇ›Ÿ–“±-äDV- Ù¯9Ëc$ã ÒÙoãåsØŠ¡ïì¢ZWɧΖÀŒLtjÕñ#MX×6©g©ÚðsKvmSì‡!£¨ŠØhåÁŸ!È´eL©GXœ¤x˜`õ’xÑU÷»˜”¡5³éô¾&ãew#«Nw¬‹}sLÙÌhh˜‘¶e®è½TRÖ¹µ€Ðµ•è›ñUئD~­jàçюήŠl ÜNF 'žtMƒ±8 RvˆjÒ/ÅÄ¥_Q#w¨ïØeýwIÂD½,«ý\êÌêxâü÷X¿[‰yͤ×Uœs×àFŒébNÄî©ø¨Ê¯£Å !4.EuZ®•6ÄK†]®V²h@îôÒ‚¼FäNgÂ4­¡Ï7{Ó®ûñ‹ÃïY‡©tôÛÚ§¬S1 Sˆ Jû„„å‘…Ü+âãÄšhÂ'·kD?÷ùÏyïå¥×tUØ­Ìó_P®¿ï¼â:*VØg^ËXf5\ïdû3ÓãÙ’$»#Ëy_iÜäÙÚXª`¬²_3qèÝåÀ›7tÝìô‘]y’†í ų©”çøáZÉZÔÅcÖ×$‰³êð›§ß)Ö¥èûàf•<õµE© ©õ®‘žœ¦^ >¬éÚ°Þ{û®¥&ñM•åAap²°§-$;°@3ß9è”W žŒoÜL!áK¼öÏÄÄÔU™ tÃób^Þ¯`¤]¾nöÂ’¥.˜%»ÖûjÙ°V¿f] lɱM¦ñq>”c4ïì_ÏätU{!ŽÓºšXë0<ÚÊ‹Ã9…èJ©=¤ž bªÜ£`@cm½¡ÝüŽ'•®¬ïþ-¿e†ÅÙs{oRëIÕåàý-…±U‘—Qœ²ÍcÓçh&êû¸îvm à=ÍE)ñ;x F{urŒþœN„lÚéx½¬-›Cˆ8k1×­Å87_Ìg´‡IÓ_QmÑlæ´[;M𣅆r"j;5ÿ ¨ydÃ}?ç©ÿ̤Ý[Ö{Õ.,à ôÿ+lß}•Ç—ƒ••±Ð|~'Vý¥I1Ó{uøÉNU>þÈ£¹p_›RÆ=¾ÙÃËý¬ÏÇ-Ç™ Ãað1IhßDä+–rM™7E§×»mײQÕWEª8ý§¹¸£´’c05Ú©8Ž …ô75oxâJE­ZÐ/ã¸ãüÝÂæµ”®_yYœYlã»=®|ŠYxL»òwø´wNټܻmšh¹ÝÀf b'ÈÙTî6÷w± j­vCÉ¢Ý>*—ï9Æ»<ÁJ{hb+3«-©“|¸ï ‚ùvs–ç¤ER…ñ?HIâ(›Ï ¿®Ç)èìÐ>ªO©½³µo#Òp êhaE©tL2E\,ü‡Ð³³_nM¸(·—Õ“³¤¸ö½ð4ûóç=½UÒ›ä\w½ãEÊ]d‘ÙîW+cœ‡n4X¾y÷4ß¿L?,î0fä¿AÕÅÆ½”·þ ö¬˜g°ÈÛ«œ¡Bòx4pú ‰ùòŽWÑ+‘u±_`‰³í~T`4ò¯¨Zaè·Õ¢Ow§ºè}ªÔè§SÀéRH!%çœ2(lö=_’í¿öÔ½*-äâ< û£.i- ÚXùL>d‘€ùjÜ´[§Ë:ê‚ÚQtcoO×鯓$ ¥”2¶K¬¯ó¥G‚FªUœ+nb$¦¯×¡ŠB—«$ñ}þ¹_sM¯MÇÝ¿kÆ2•’ÕáîëR³{ÒÃHeÜ $zå—OJw—óÇq4’vgSVJE÷miÁZZò?dõ*ï4+Lrg¸£+ç :M•MµDÙuuÊaö½4|° ƒXÝtõjk³Ap‡KZèOc_*¡³$G´ûPu`!ôƒ¤‚ƒ–±ÑVÅh#˜ÝšqæÄ`b·Á{÷f2{`­- ªréJE‰à ÂàÕi"rp×<Û…¸V R}'>.týÓ´½$üý¾ÛÈÅnqÜÝÂlˆ^·5—Q3™’§ÈÀ Â_>Ù+ú½f±=kÙ¥.ŸJ]T¹R9ô`µ„Ë_%|»…?9#SYÌ+kêt¯3ÿ½ï}æYÉwO‡¨G¼®4tÂ⼊;@G¶ö)Ù!æ®xe^ …£rg;­‚ørÌ._ܦ̷‹rž„øouâê€K$TVjÎD^™ÿ™Ú=œîo·Ï¼ €ŒF¿—üÁ·1_üQ+‰Ÿðr±zÝOè•c¹%[·úëR*u³ÅË3RWÎäš MJÕ:ONÓ;º"–{ZE¤­ûM¢¶r­MÝ¥YÀb%u-Ìâš¶ÕKgx­Lú ß!Z ïü¿a“¬í_µ)¿Q±p~@!:§ž‚­ÐÅeýË3œôåfج ½õ}ƹ Cf]ÿëç[†'Cú—á_”ÞF†Éá®rkÍ–1š8˜J¼ÙL»Ã8sr$ò½'° JÁ“à"´Ù“"Ò.S…Ø+a¡âÂ%·~Oô„=×à§ÍÍ0¦GE<0'ì±ï|þ‰^³9g§¹C=ˆ2]Å8 êªJØXCÅiÑáQøÇûëUÜ žÁÂÚ_/>)R/ŽŒJ¨®¿€óчé¼ÐeïE>FD¯§äY7J35×7<“ŒàYŒXÖÄIú—HV¥@¿¯í‚~–λfÂÜÚVL– C0ÍÙ9qê`=ẳº­tIys¾û#«Š+Z–æªîËtMކýõk".fb’©ÝnŽn)ýÞ®Á Ìúæå‰L)7mwÐÂìiì9>^êÊÛÇÊw›^CßwªÐ‡‚uµ‰wšéEcŸüE¬‚çÄNóz‡µ`¾+CÄ¥6ê*°«õá¨Û’}¯•«åãË&75¡vøÂ¥2ìÛ… È_²Ð«gX‡ÅÔŠ×%‰_5ÎítZß7mè| Ù*Ùûh̽°¾ü8•KeÕw6ÓŸæ¬Ý²vÌÕ¢uè/Ðì¡¡ö8ÁG¶× Šø¨ï°»j†õóòõY‰DŒ‘ò¸Çµ†Ë%aä:LÊÑÊ€|[s–]LD_0‰íVùæ[\h°”í]çÃknë5Ãc§È/;ü»!ê!W³cr—'‰êÏí“Ï'4Às¿“·a ±ÏdkóÅú—¬d’Aä-½ƒHŒöVCoö5¯äÇÿL¹G=Dq»ÏÉSL”·¢n˜¢$C¢þâMƒ9Ȥ§Ý/Î\™Få¯ /»·Gâ=(ÜW-§„Om›IÚQ+תےù IA/è6 ›·B¥qì–/"¶FÇÚr¾ñK:ÆÌÌ Ã8{É 6.zVN\" >§Åª-ÂU I:[O÷§qå%#"T’²fZ´&¬1 5ÖLðÃe¨Ï qyD„@ÊÀ•÷R™Þ'ãÛ=ã„OÌ” Üw×_†ÀLö(pªýo€´U)©ò!V¾JúÆ*þg”®5ßC¤ez}¡ÊÙùºßE#c½^3&¶R#P²ÁJ%,]ìClçšG±%l<+ÉÑþ%#KÐ^g Ñe’=dBʺ Ý¥‡¿î¦pUYþàF¢;LZfÔ‰Ê×7] '(Ö…pñ)­™4\w‰‹µ¿pÄü,þ¤ ì±Û6Œ‡ïé“uGkôÓ¦1*ª?»GÁÊ2‚|^ÁƒÁå´ç ‹;³¦|”:ÙÅHœ ì>†/7úزu«€>(Ì?¥¹¶*¿$5½oÍR3žPæÎš$÷4ƒ>iµòŸÅØÓ’„uŠŠóZÉíx‘á4Ü=1‡‡ë"ï¸o©%-àA!µö3Uò«>Œ,J•;èô9&%Рóâ¸Èâ5uì¥êÞ´ôǹ)Ê,+]ìñu'£?^ÂÅ´…ùˆT£¯Œ‚œ£*þ«ò/Ò×fhxI×IjPÌò0‘/Ý5´ËÄÁ€Ã,–\™±êÇrÇÃÇá˜j[¢!yà-p?é»›¿a€gÁ<ì‡Æb÷€¯Ì©éM?âl“SIõ|ÿ'þÒ…ï•[•ÇŒÕÒC–*'šw9Âa,àÒ÷Böuý(Þíõ…œ´µØÔ)UyÞC>shÍ Žq©çhfE"´gkIÂï‹ ñGzuÞ’©”B<‡êB‹lÁx¡iÌX bý“‘Å ‚ùAAA@Ðà³ù†Œ?ÆUäxK’Š–p†À¼ÜX²'âôEÔjÕÆ¢*è´F¶y–Òc³¸—¼ÏrÇõ$‹X¢‘‹"êªŽðØ¹ªòè}ñ€k 6¾éDòý¤;/–7!g¸cõ7t¸NœÃ<€áýq¼8&èæ•ùv#W)©,ÒïÎÚhÆîýÈIÖ)5 5ÈX,#smKHrvLK5õ°ŠÂ*ç3µPR%(»ô½»Í2ßÿrE8PVÖ¿Ïe2fsprogs-1.41.14/lib/blkid/tests/minix.img.bz20000644031104000366760000000021211240667355017151 0ustar tytsoBZh91AY&SY“—Äi õÁù@H4ah< ˆ@€  r(4€Ñèš”@ 4hÐBܪ\[d€Ö]4@TíT/ˆ <"ˆ:0²‘! Á¡òúzÊ…ó—v=3PÂbü÷>jÇxÅW-Œˆ€( ^Ñü]ÉáBBN_¤e2fsprogs-1.41.14/lib/blkid/tests/jbd.results0000644031104000366760000000006711240667355017025 0ustar tytsoTYPE='jbd' UUID='0d7a07df-7b06-4829-bce7-3b9c3ece570c' e2fsprogs-1.41.14/lib/blkid/tests/ocfs2.img.bz20000644031104000366760000000175211240667355017053 0ustar tytsoBZh91AY&SYbÞ³oÿÿÿÿüoKxo¡Ût¿ÿßhb @ E@„&LDr@ ÐxËVÀÉ)¨< =O(œ¨Eˆ=€öd÷GypJ)‚œÊXˆÈA^Ý+T¥ ‹Ð¨Ao5„p¡äNà;‡ôa/ˆ¨}Ö@ƒ,ïY¼æä¾FC‰NM'À@Td‚®f @@8ÅÜ Ë"QèËœaÈjttƒ5ߨcžó°dS»ªà0ꃀÂ_à@@h :è…P,ò郴q0fþõI1ÆÃ@0'tsP"ðèȘI²V¼F¿•KY’ ´5ççe@HúY(5àQ~¸4’7Cà¬îÛW¤½Š€Pí:¡H½EwàP3£îâîH§  [Ãv`e2fsprogs-1.41.14/lib/blkid/tests/fat.img.bz20000644031104000366760000000037111240667355016605 0ustar tytsoBZh91AY&SY4ú-|ÿÿòè @38$cŸ@@yÀA@Ȱ¹¶"†€&4É¡“hÚM2`5E?RSjM44Ñ‘ˆbhɦTˆ512bL ¦ ÀÉz?óÂtA$ˆ ¢ ZœŠ‚)ñŠLñ¢ Šh“o—´sÂÄPÛ·Ô¥AQPgÕŠœfH ƒbÞfôŽS&"Îðr@»¬)HÒ-AJRAH+=çÄ#ÅXV~Iòɯ (.ÐÂùŽÝ‘¸tì)‰@„ÔzË,·‰¸æ¨ˆ‚)nSyÜ]ÉáB@\Óèe2fsprogs-1.41.14/lib/blkid/tests/reiser3.img.bz20000644031104000366760000000043211240667355017405 0ustar tytsoBZh91AY&SYÈèú$žÿÿü9ØÌEH1+  hh  ! °ø(ŠhÓÓL&LÐæ0`0"¦“OS=Lš4h ÚhH|0î—ç íMåbS1m¡$ *Zé¢&í*ÉŒð²hšKBms³iŠ‹P!`hH@Ñ*`%¶–*¡ Ó¡”8aƒ$—Æ©;oÁªÈ0»y«ãæwU¾–gîþÓM5‰9ÓlØå_˜Ù9‡ÉHŽÛ£¦›>’j·—øFij^€~B¡ƒzvW®ÖãÖ3É¡!m¤¶4l­%³ü]ÉáBC#£èe2fsprogs-1.41.14/lib/blkid/tests/cramfs.img.bz20000644031104000366760000000017411240667355017307 0ustar tytsoBZh91AY&SY¸¬‡&2Çü@@B+˜.Ü@` Tg¤È24ÐÑ“AšžA’hê êRåå‰3ÊÈ@!Ô„ä´{æ-gÊÛ áv&— Äx®ÄuŸö‘Å Qw$S… ŠÈr`e2fsprogs-1.41.14/lib/blkid/tests/romfs.img.bz20000644031104000366760000000015111240667355017155 0ustar tytsoBZh91AY&SYPH.ã}³ÂB  CœÀ @ HJM¦šiêmM 29\HˆˆRh†±¢ÞÞOÃl²êãŒ8í½Ü25 I!ñw$S… ‚î0e2fsprogs-1.41.14/lib/blkid/tests/udf.results0000644031104000366760000000003411240667355017036 0ustar tytsoTYPE='udf' LABEL='test-udf' e2fsprogs-1.41.14/lib/blkid/tests/romfs.results0000644031104000366760000000004011240667355017403 0ustar tytsoTYPE='romfs' LABEL='test-romfs' e2fsprogs-1.41.14/lib/blkid/tests/fat32_label_64MB.img.bz20000644031104000366760000000217711240667355020647 0ustar tytsoBZh91AY&SY|ë&^~ÿÿÿÿïÿWZ^÷÷ßQg{ÿ¯ýÀFAœoÿýþqrþ{mýÐ~=€ 4i£@iˆ0€dd4a ‰ Ð@0MÈ0CM0™14 h hÐb  †˜A‚bh4Ð @Á2 #AÓL&L@ Fš4˜ƒFCA¡¦`˜š 4Ð0LƒÐd4Ó “@ÕH€Ñ„Ð4bŒˆdi“AŒ€ÓC#RBL„Ɇ©ê~Šy ½CM$yOI駨ÈÚ$õ=2jz5M êzžQÕ2›ShyLSÉ‘¡ Í'¢š='¤Ñ¦›5O4§£ò©2ÒåI÷®nÔQ2L¹*% tˆrRJHDEV®h‚"„éÖжHˆ€ˆˆ„?” î>tÑDD!„‘§_kÆ}*±$aç“Y–‚EºMóh¹ó*iZÇAÑ»UjRÏEÓF¤#Ác'"iÛ&lçm–eºmEU³•(gӸ̚ +¦ÍI»nU]j1RиFé(ñp?³ÕSv— çPÑ¿lDZì+r£ãÁê9ŠnìòÔÖÜ=Ö.ÛHÍÚ¯«½TÙï°Ò¶J±ËÒÄEÔzʺGûlÕ¦ëüjØ”$ÃLå'ç.Kz´Iß®R„zŽ+‹ÂPLÕ¶iÞsÚ’NRu ·¸sáQ½VxLJk« 㘱’Á_FK_¯yuô–ÑÀýi¢ýš™óµÐUa«4L†½Ó’ýí8.:Óûì›’ýÏÉÇ’h¤½í9Mv"ºÃ1–ù¯eº;g˜á¨æß9­1†™¶d'qyÛzÓ0*Ë…GWÌL®ÐÖéAâߣÄHàÔÄ÷*u-žBOæÁhžµNÐþg¦üêMi·w;O9”ßGq¬n8Ý„±*«d´VýÝ¿ºi“¬²Ì®ÔÎsö¼uEóˆÚsûì7ÙÏà:×1~Ö.ï5[/Ãë¹sé¾&µà>GŽ’u…ÚI1Þršw€“ÁuøŠnÙ£SLÎR\;ÆÊ£v×6CÖ\µy/Ýìä\3ËÖòñ¸²¤ò³ëtê|§†ÀuË÷•!œúMî“"RŽS)3[¡yÕ ¤ë¤Ž3åoÐ>è£Â`ðL  †çI#Íd-ç•®ÛÐðâ´KÔ v6ÜÉ}í½Þ¡‰=oeÀ~Fªž@™]„í´Q÷5mŸ¥Çn¾¤¬=6½eïòwN’»1i¤|ŽüÆŸàpÿ+‡tÊy\LÊt\†ÍÌb±šwØÜ³etĽ`³ñIit¡Ò©:^#šß¤•¤ß¨N¢þU5ç{;&ÖµÓ·+gã¹Ö›eâñ|º_G!=<Ťé¯W çÔ”'wjFtNÍ¥–ªJ‘DD!jMJ×R’ò–ÛÿYæå÷-_ÅG“úÉ‘eó&ZHâ%(ÎLZô5Ùþb‚²L¦³[Ya:,–€€@AJh3HŠFÅHŠG‹¹"œ(H*=~€e2fsprogs-1.41.14/lib/blkid/tests/udf.img.bz20000644031104000366760000000375711240667355016624 0ustar tytsoBZh91AY&SYOëSç¬ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÁ ‘ ÉÁÿÿÿÿàŸ¾w>GÎî@‚‰ =@=Òú¨ €ÓF@ÿª Ñ2#$jz˜#A¤41 h&&L„ô4Ð € L ´z@Ñ0i 2`Mb¤ÔÉ ÄІ†ŒC Ð @È£@ A¡ hhš¦4ÓÔh  =L€bh4h 4dÈ$I24™2h¡OQå£Ô<£5›SÔhhò™=MPh 2¡êzŒ'©¦†ÔÐ ¨ÄÄÐzF&šƒ@h$C§rL·5°Ô(ߥüP ÙÈxæyÛ¸f3G¾Ýa¼Ê%´sõn{Ãô5ƒŽúEÃxy ýíÛã(†ΊXX±².cL(€(Cœ(S<‚¦CRœB ¥²±fóD kËÐ@€"›Íí¬ìETMš@€Ôâ¶tEá RÃƒØ !'/7?GOR—_gowyöo^~ŸG¾ÿ ýjzB‹ù˜[H±Í :$ñ7à GAAƒ  2‘T"ÑÑò!IP¬œ¡bæ Êš–5/12`ɹ©³s“³Óô4Tg))i©ê*jªëÙYÚZÛ[Ü+r5ÑB·w—·×ø–¾   Ž´¦¢ˆ!! ï&l(°Üý fy^{¯£EÅ‚/ƒß@ÿ¾cßÝx§?Nh NóøÒ*<¨‘ÀðFQ!1M(Ô@ãAhˆ÷(tÖ–A° :’t!P‘P^6…nsAŠ, ¢•ßÐRXˆ¹`ÀªEBÐCá~¶N=äų¬¥@$!.|’[)uÊ@¶2ZÅž;òóx^íV’2F’I$` ] &…r‘D‘_=wüJèx;¾‚ù&W)œU„eá^®R‘ÞUfF€P4Å,qË[¥™!ŠÔËûk&dÁòN,” 3¹¾Šƒ§BfcR¥®¡üšœ X“©¡kY?RÚF¥' ¥(*ˆGOH±° (¨÷‰œ€6+‘$˜#AFy÷L2âAû“m8QÀ†ØeB$áN/ÅÀQBð‘ˆÉ’2 HPéÝ¿|`Fb9Ÿ)µ»\نĸy¡§ø…iy?$H’z @2+`‘EòDɱ€ÐU•˜ÌåJâXáÁ½ì6øüÿ ¡£¥§©«­¯±3³µ·¹»½¿5IÁÃÅÇÉËÍÏÑÓÕ5×ÙÚ7w‡—Ÿ§¯·¿ÇÉa‚°€. 8b™2Uõõõ“z5¼ ³xBz…dI*¾-`Pû¿4ƒ–ÿ L5€‡â”xÑÆš—2)@xÒksE9PEYÍàÒŽØ€™Hˆc>üP1DöäØÍ˜ÇûÆ/ôæ)²*ㄊ†BŠx€À¥è£v+Éžà€e'ü8ìw½y¢¡~Iœ×‰>mÄ7ÌC³ñÃÇËр陚T´Ó"sÝI†Jt®\4á4Š3Mª…6nB‚‚¦¦ÔÕ±˜NŒÂ¸«:²ÔÈ“¾–›ûÚIzøv©õʨIö%wjRA&"€*Žð×r<µÖȯ&Eð:ƒæ*®b–èŠh ª®|\Ôdkcöh]ñ­j+OCf\2è?g8a$$&RPMº;Çš¥ÆÃ ä ÍÖD¥Äãå$Zi »±F¹Ùú0ü¡d–Ï(Cb+sâ „*¡àÉ¥tãŠDµ±Ó9§„\â P7O8F¿rƒc(ÖA‚A…¯ñHd´`”K”é"ZV wCM:§–&yo Fl ¤**h’$ÉDIL—$Ã*rÀr0%æý„0ÍÓ놧Ȟ{(ÀÅ­ ôMDS HTÃ2”ÌÙCttŽ^k:°Ö[ËÃ7SR:0äfCQ×0Ír[MÐ!ê'Ö°×Å›T RÓ"$1¡Aó%§ 0J i°¡yóld Á$&ò!@1 ÄðÀ™BÒ–(Ÿ€Ü°A÷ñ^÷„×gæ—o¡¥œ^òI $&RnDæ›ÛÕXJ+µ²°Cµ7¢@1»!,'O4† 1t“2Ý™kvÂlDµ¤‡Fà&xK^2OCˆí_jPl¬k„Èíªl„׉ ú#.ØM€›to}Tj HšB° ãöÛêvP„H¼Êj~Ÿñw$S… ´|fPe2fsprogs-1.41.14/lib/blkid/tests/reiser3.results0000644031104000366760000000011711240667355017636 0ustar tytsoTYPE='reiserfs' LABEL='TESTREISER' UUID='9efe7863-b124-46dc-ad68-8ecd04230a7b' e2fsprogs-1.41.14/lib/blkid/tests/ext3.results0000644031104000366760000000011211240667355017140 0ustar tytsoTYPE='ext3' LABEL='test-ext3' UUID='35f66dab-477e-4090-a872-95ee0e493ad6' e2fsprogs-1.41.14/lib/blkid/tests/swap1.results0000644031104000366760000000011211240667355017310 0ustar tytsoTYPE='swap' LABEL='SWAP-TEST' UUID='8ff8e77f-8553-485e-8656-58be67a81666' e2fsprogs-1.41.14/lib/blkid/tests/swap1.img.bz20000644031104000366760000000017011240667355017063 0ustar tytsoBZh91AY&SYšÌ<Ûèª@MÁ€ƒ€@@€€H T%SÔú¡§© ’HÍ@5PHõ„€BIYAKºyM ‰=¸ÐÍÜ,O ã8’ŸØ»©4˜˜ñw$S… ¡LÃÀe2fsprogs-1.41.14/lib/blkid/tests/ext2.img.bz20000644031104000366760000000054411240667355016717 0ustar tytsoBZh91AY&SYÂU·ÿþÿøb|+P (HÄGÅaF ` 01h€SÞH°‰ È‘´jf£ ¦F†Éb#CÒ4ôD8hdÓCL 22 Œ €ÄÑ“@21 $QSG¦šCMC@4Р 4ÓOêÿ/¢u;³O¨çK)^XE¤ªµ¶ëÉbòƒRH`ˆœf,@`$q‹1xB\Çy›3ç›:Ú§WiÐq &Œj‹X­)UZ zpB@)#h¨±-F‘3D¬°Äö­0ðt<`HâïH•mÍ:娶À±ŸrS;p€M/k:1Ãx€Ðd_¥IÐfAìo_–¦ˆ>µiKxñ HV­ôý¨=/´÷vÈ`Œyšû¶@µ5«ÄbCûy^H‚aHƒ:Ѿdoþ`¢€µÕ îWø»’)„†­¼èe2fsprogs-1.41.14/lib/blkid/tests/ocfs2.results0000644031104000366760000000011411240667355017273 0ustar tytsoTYPE='ocfs2' LABEL='test-ocfs2' UUID='6b6bfbea-3a79-4f0c-b166-a20776102445' e2fsprogs-1.41.14/lib/blkid/tests/fat.results0000644031104000366760000000005611240667355017036 0ustar tytsoTYPE='vfat' LABEL='TEST-FAT' UUID='DEAD-BEEF' e2fsprogs-1.41.14/lib/blkid/tests/xfs.img.bz20000644031104000366760000000067311240667355016640 0ustar tytsoBZh91AY&SY{1[÷5ÿÿýþĨè@BéÐ Àt‘@ƒGÕð@A@ I°– ÒP4 ¢¬¦™@4h@)©ú¦õ'©§©µzê4ÓÔiå4i“ÔA§¨z'ŠF“òDª%(,@ˆ‘J¸—;¯Šôàq12Híë<ÿDȬ‹SbS€æÁ~£¦d„EÜ€QD¯û¾õ™©—bX|۴нH¨YÂ|bPÇFH€ˆ¨¬ðñ~»…\ÌNmÝiC~›ÒEpÅèP@º@€F±$®U-Ãá!Øœ‹PÓÇ"ì&¯ðó €ª²Â FÕ­ 0!.[$\HŠ È@å3pxP}“>"ÍB":q›>?·Ñ–#­½âÞ'î Tº…3ÆZΊñF~GµªÁp1m¹N=‹¯Äøé,I.pw8Pi ÄŒ°˜ñ}@80%ƹpØÜ®A¿"¾=žrNë—’‹ìmrÉÒzd¨¢¿ËˆÛ+hŽ~<šïçþ.äŠp¡ öb·îe2fsprogs-1.41.14/lib/blkid/tests/xfs.results0000644031104000366760000000011011240667355017053 0ustar tytsoTYPE='xfs' LABEL='test-xfs' UUID='8c8a0a5a-9f57-492e-9610-45a61f38f58a' e2fsprogs-1.41.14/lib/blkid/tests/iso.img.bz20000644031104000366760000000052011240667355016621 0ustar tytsoBZh91AY&SY°IŠá¤ÿÐ÷uŽ„Qcÿ ?ÿß` Œ@°8­¢‰âLŒŒ €šF‚ ¦š“È E!HòŒjhh4h hš4]®Ç6{Ð^¬)Z¶û…•V„RÛ4’ N!†Vï²™F¬Qš¢jA††Ä“Ï¢šŒƒjª²;$î@‚ÒH ™`ÊiwÐ<ñøä=>‡v’iòÄœ†Ž/Bí›>ØÉDÕïƒC“ &Fdñž&@Q…1˜0!¤†*DC À|àOЕ³M®å°©‘BWPxï -®D´Y«2å4ÄÅiÞf™ÂOÔù5¦4Wv°S‚Û¤¯ŽXqç™ò©?Yè3il°{cŽðÎÀ@" D$¿rE8P°IŠáe2fsprogs-1.41.14/lib/blkid/blkid.pc.in0000644031104000366760000000036011240667355015513 0ustar tytsoprefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: blkid Description: Block device id library Version: @E2FSPROGS_VERSION@ Requires.private: uuid Cflags: -I${includedir}/blkid Libs: -L${libdir} -lblkid e2fsprogs-1.41.14/lib/blkid/resolve.c0000644031104000366760000000567211240667355015333 0ustar tytso/* * resolve.c - resolve names and tags into specific devices * * Copyright (C) 2001, 2003 Theodore Ts'o. * Copyright (C) 2001 Andreas Dilger * * %Begin-Header% * This file may be redistributed under the terms of the * GNU Lesser General Public License. * %End-Header% */ #include #if HAVE_UNISTD_H #include #endif #include #include #include #include #include #include "blkidP.h" /* * Find a tagname (e.g. LABEL or UUID) on a specific device. */ char *blkid_get_tag_value(blkid_cache cache, const char *tagname, const char *devname) { blkid_tag found; blkid_dev dev; blkid_cache c = cache; char *ret = NULL; DBG(DEBUG_RESOLVE, printf("looking for %s on %s\n", tagname, devname)); if (!devname) return NULL; if (!cache) { if (blkid_get_cache(&c, NULL) < 0) return NULL; } if ((dev = blkid_get_dev(c, devname, BLKID_DEV_NORMAL)) && (found = blkid_find_tag_dev(dev, tagname))) ret = blkid_strdup(found->bit_val); if (!cache) blkid_put_cache(c); return ret; } /* * Locate a device name from a token (NAME=value string), or (name, value) * pair. In the case of a token, value is ignored. If the "token" is not * of the form "NAME=value" and there is no value given, then it is assumed * to be the actual devname and a copy is returned. */ char *blkid_get_devname(blkid_cache cache, const char *token, const char *value) { blkid_dev dev; blkid_cache c = cache; char *t = 0, *v = 0; char *ret = NULL; if (!token) return NULL; if (!cache) { if (blkid_get_cache(&c, NULL) < 0) return NULL; } DBG(DEBUG_RESOLVE, printf("looking for %s%s%s %s\n", token, value ? "=" : "", value ? value : "", cache ? "in cache" : "from disk")); if (!value) { if (!strchr(token, '=')) { ret = blkid_strdup(token); goto out; } blkid_parse_tag_string(token, &t, &v); if (!t || !v) goto out; token = t; value = v; } dev = blkid_find_dev_with_tag(c, token, value); if (!dev) goto out; ret = blkid_strdup(blkid_dev_devname(dev)); out: free(t); free(v); if (!cache) { blkid_put_cache(c); } return (ret); } #ifdef TEST_PROGRAM int main(int argc, char **argv) { char *value; blkid_cache cache; blkid_debug_mask = DEBUG_ALL; if (argc != 2 && argc != 3) { fprintf(stderr, "Usage:\t%s tagname=value\n" "\t%s tagname devname\n" "Find which device holds a given token or\n" "Find what the value of a tag is in a device\n", argv[0], argv[0]); exit(1); } if (blkid_get_cache(&cache, "/dev/null") < 0) { fprintf(stderr, "Couldn't get blkid cache\n"); exit(1); } if (argv[2]) { value = blkid_get_tag_value(cache, argv[1], argv[2]); printf("%s has tag %s=%s\n", argv[2], argv[1], value ? value : ""); } else { value = blkid_get_devname(cache, argv[1], NULL); printf("%s has tag %s\n", value ? value : "", argv[1]); } blkid_put_cache(cache); return value ? 0 : 1; } #endif e2fsprogs-1.41.14/lib/blkid/version.c0000644031104000366760000000171211333162126015315 0ustar tytso/* * version.c --- Return the version of the blkid library * * Copyright (C) 2004 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% */ #if HAVE_UNISTD_H #include #endif #include #include #include #include #include "../../version.h" static const char *lib_version = E2FSPROGS_VERSION; static const char *lib_date = E2FSPROGS_DATE; int blkid_parse_version_string(const char *ver_string) { const char *cp; int version = 0; for (cp = ver_string; *cp; cp++) { if (*cp == '.') continue; if (!isdigit(*cp)) break; version = (version * 10) + (*cp - '0'); } return version; } int blkid_get_library_version(const char **ver_string, const char **date_string) { if (ver_string) *ver_string = lib_version; if (date_string) *date_string = lib_date; return blkid_parse_version_string(lib_version); } e2fsprogs-1.41.14/lib/blkid/list.h0000644031104000366760000001045211240667355014624 0ustar tytso#if !defined(_BLKID_LIST_H) && !defined(LIST_HEAD) #define _BLKID_LIST_H #ifdef __cplusplus extern "C" { #endif #ifdef __GNUC__ #define _INLINE_ static __inline__ #else /* For Watcom C */ #define _INLINE_ static inline #endif /* * Simple doubly linked list implementation. * * Some of the internal functions ("__xxx") are useful when * manipulating whole lists rather than single entries, as * sometimes we already know the next/prev entries and we can * generate better code by using them directly rather than * using the generic single-entry routines. */ struct list_head { struct list_head *next, *prev; }; #define LIST_HEAD_INIT(name) { &(name), &(name) } #define LIST_HEAD(name) \ struct list_head name = LIST_HEAD_INIT(name) #define INIT_LIST_HEAD(ptr) do { \ (ptr)->next = (ptr); (ptr)->prev = (ptr); \ } while (0) /* * Insert a new entry between two known consecutive entries. * * This is only for internal list manipulation where we know * the prev/next entries already! */ _INLINE_ void __list_add(struct list_head * add, struct list_head * prev, struct list_head * next) { next->prev = add; add->next = next; add->prev = prev; prev->next = add; } /** * list_add - add a new entry * @add: new entry to be added * @head: list head to add it after * * Insert a new entry after the specified head. * This is good for implementing stacks. */ _INLINE_ void list_add(struct list_head *add, struct list_head *head) { __list_add(add, head, head->next); } /** * list_add_tail - add a new entry * @add: new entry to be added * @head: list head to add it before * * Insert a new entry before the specified head. * This is useful for implementing queues. */ _INLINE_ void list_add_tail(struct list_head *add, struct list_head *head) { __list_add(add, head->prev, head); } /* * Delete a list entry by making the prev/next entries * point to each other. * * This is only for internal list manipulation where we know * the prev/next entries already! */ _INLINE_ void __list_del(struct list_head * prev, struct list_head * next) { next->prev = prev; prev->next = next; } /** * list_del - deletes entry from list. * @entry: the element to delete from the list. * * list_empty() on @entry does not return true after this, @entry is * in an undefined state. */ _INLINE_ void list_del(struct list_head *entry) { __list_del(entry->prev, entry->next); } /** * list_del_init - deletes entry from list and reinitialize it. * @entry: the element to delete from the list. */ _INLINE_ void list_del_init(struct list_head *entry) { __list_del(entry->prev, entry->next); INIT_LIST_HEAD(entry); } /** * list_empty - tests whether a list is empty * @head: the list to test. */ _INLINE_ int list_empty(struct list_head *head) { return head->next == head; } /** * list_splice - join two lists * @list: the new list to add. * @head: the place to add it in the first list. */ _INLINE_ void list_splice(struct list_head *list, struct list_head *head) { struct list_head *first = list->next; if (first != list) { struct list_head *last = list->prev; struct list_head *at = head->next; first->prev = head; head->next = first; last->next = at; at->prev = last; } } /** * list_entry - get the struct for this entry * @ptr: the &struct list_head pointer. * @type: the type of the struct this is embedded in. * @member: the name of the list_struct within the struct. */ #define list_entry(ptr, type, member) \ ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) /** * list_for_each - iterate over elements in a list * @pos: the &struct list_head to use as a loop counter. * @head: the head for your list. */ #define list_for_each(pos, head) \ for (pos = (head)->next; pos != (head); pos = pos->next) /** * list_for_each_safe - iterate over elements in a list, but don't dereference * pos after the body is done (in case it is freed) * @pos: the &struct list_head to use as a loop counter. * @pnext: the &struct list_head to use as a pointer to the next item. * @head: the head for your list (not included in iteration). */ #define list_for_each_safe(pos, pnext, head) \ for (pos = (head)->next, pnext = pos->next; pos != (head); \ pos = pnext, pnext = pos->next) #undef _INLINE_ #ifdef __cplusplus } #endif #endif /* _BLKID_LIST_H */ e2fsprogs-1.41.14/lib/blkid/blkidP.h0000644031104000366760000001260611240667355015061 0ustar tytso/* * blkidP.h - Internal interfaces for libblkid * * Copyright (C) 2001 Andreas Dilger * Copyright (C) 2003 Theodore Ts'o * * %Begin-Header% * This file may be redistributed under the terms of the * GNU Lesser General Public License. * %End-Header% */ #ifndef _BLKID_BLKIDP_H #define _BLKID_BLKIDP_H #include #include #include #include #ifdef __GNUC__ #define __BLKID_ATTR(x) __attribute__(x) #else #define __BLKID_ATTR(x) #endif /* * This describes the attributes of a specific device. * We can traverse all of the tags by bid_tags (linking to the tag bit_names). * The bid_label and bid_uuid fields are shortcuts to the LABEL and UUID tag * values, if they exist. */ struct blkid_struct_dev { struct list_head bid_devs; /* All devices in the cache */ struct list_head bid_tags; /* All tags for this device */ blkid_cache bid_cache; /* Dev belongs to this cache */ char *bid_name; /* Device inode pathname */ char *bid_type; /* Preferred device TYPE */ int bid_pri; /* Device priority */ dev_t bid_devno; /* Device major/minor number */ time_t bid_time; /* Last update time of device */ unsigned int bid_flags; /* Device status bitflags */ char *bid_label; /* Shortcut to device LABEL */ char *bid_uuid; /* Shortcut to binary UUID */ }; #define BLKID_BID_FL_VERIFIED 0x0001 /* Device data validated from disk */ #define BLKID_BID_FL_INVALID 0x0004 /* Device is invalid */ /* * Each tag defines a NAME=value pair for a particular device. The tags * are linked via bit_names for a single device, so that traversing the * names list will get you a list of all tags associated with a device. * They are also linked via bit_values for all devices, so one can easily * search all tags with a given NAME for a specific value. */ struct blkid_struct_tag { struct list_head bit_tags; /* All tags for this device */ struct list_head bit_names; /* All tags with given NAME */ char *bit_name; /* NAME of tag (shared) */ char *bit_val; /* value of tag */ blkid_dev bit_dev; /* pointer to device */ }; typedef struct blkid_struct_tag *blkid_tag; /* * Minimum number of seconds between device probes, even when reading * from the cache. This is to avoid re-probing all devices which were * just probed by another program that does not share the cache. */ #define BLKID_PROBE_MIN 2 /* * Time in seconds an entry remains verified in the in-memory cache * before being reverified (in case of long-running processes that * keep a cache in memory and continue to use it for a long time). */ #define BLKID_PROBE_INTERVAL 200 /* This describes an entire blkid cache file and probed devices. * We can traverse all of the found devices via bic_list. * We can traverse all of the tag types by bic_tags, which hold empty tags * for each tag type. Those tags can be used as list_heads for iterating * through all devices with a specific tag type (e.g. LABEL). */ struct blkid_struct_cache { struct list_head bic_devs; /* List head of all devices */ struct list_head bic_tags; /* List head of all tag types */ time_t bic_time; /* Last probe time */ time_t bic_ftime; /* Mod time of the cachefile */ unsigned int bic_flags; /* Status flags of the cache */ char *bic_filename; /* filename of cache */ }; #define BLKID_BIC_FL_PROBED 0x0002 /* We probed /proc/partition devices */ #define BLKID_BIC_FL_CHANGED 0x0004 /* Cache has changed from disk */ extern char *blkid_strdup(const char *s); extern char *blkid_strndup(const char *s, const int length); #define BLKID_CACHE_FILE "/etc/blkid.tab" #define BLKID_ERR_IO 5 #define BLKID_ERR_PROC 9 #define BLKID_ERR_MEM 12 #define BLKID_ERR_CACHE 14 #define BLKID_ERR_DEV 19 #define BLKID_ERR_PARAM 22 #define BLKID_ERR_BIG 27 /* * Priority settings for different types of devices */ #define BLKID_PRI_DM 40 #define BLKID_PRI_EVMS 30 #define BLKID_PRI_LVM 20 #define BLKID_PRI_MD 10 #if defined(TEST_PROGRAM) && !defined(CONFIG_BLKID_DEBUG) #define CONFIG_BLKID_DEBUG #endif #define DEBUG_CACHE 0x0001 #define DEBUG_DUMP 0x0002 #define DEBUG_DEV 0x0004 #define DEBUG_DEVNAME 0x0008 #define DEBUG_DEVNO 0x0010 #define DEBUG_PROBE 0x0020 #define DEBUG_READ 0x0040 #define DEBUG_RESOLVE 0x0080 #define DEBUG_SAVE 0x0100 #define DEBUG_TAG 0x0200 #define DEBUG_INIT 0x8000 #define DEBUG_ALL 0xFFFF #ifdef CONFIG_BLKID_DEBUG #include extern int blkid_debug_mask; #define DBG(m,x) if ((m) & blkid_debug_mask) x; #else #define DBG(m,x) #endif #ifdef CONFIG_BLKID_DEBUG extern void blkid_debug_dump_dev(blkid_dev dev); extern void blkid_debug_dump_tag(blkid_tag tag); #endif /* devno.c */ struct dir_list { char *name; struct dir_list *next; }; extern void blkid__scan_dir(char *, dev_t, struct dir_list **, char **); /* lseek.c */ extern blkid_loff_t blkid_llseek(int fd, blkid_loff_t offset, int whence); /* read.c */ extern void blkid_read_cache(blkid_cache cache); /* save.c */ extern int blkid_flush_cache(blkid_cache cache); /* * Functions to create and find a specific tag type: tag.c */ extern void blkid_free_tag(blkid_tag tag); extern blkid_tag blkid_find_tag_dev(blkid_dev dev, const char *type); extern int blkid_set_tag(blkid_dev dev, const char *name, const char *value, const int vlength); /* * Functions to create and find a specific tag type: dev.c */ extern blkid_dev blkid_new_dev(void); extern void blkid_free_dev(blkid_dev dev); #ifdef __cplusplus } #endif #endif /* _BLKID_BLKIDP_H */ e2fsprogs-1.41.14/lib/blkid/read.c0000644031104000366760000002365611240667355014571 0ustar tytso/* * read.c - read the blkid cache from disk, to avoid scanning all devices * * Copyright (C) 2001, 2003 Theodore Y. Ts'o * Copyright (C) 2001 Andreas Dilger * * %Begin-Header% * This file may be redistributed under the terms of the * GNU Lesser General Public License. * %End-Header% */ #define _XOPEN_SOURCE 600 /* for inclusion of strtoull */ #include #include #include #include #include #include #include #include #if HAVE_ERRNO_H #include #endif #include "blkidP.h" #include "uuid/uuid.h" #ifdef HAVE_STRTOULL #define STRTOULL strtoull /* defined in stdlib.h if you try hard enough */ #else /* FIXME: need to support real strtoull here */ #define STRTOULL strtoul #endif #if HAVE_STDLIB_H #include #endif #ifdef TEST_PROGRAM #define blkid_debug_dump_dev(dev) (debug_dump_dev(dev)) static void debug_dump_dev(blkid_dev dev); #endif /* * File format: * * ...]>device_name * * The following tags are required for each entry: * unique (within this file) ID number of this device * (ascii time_t) time this entry was last read from disk * (detected) type of filesystem/data for this partition * * The following tags may be present, depending on the device contents * (user supplied) label (volume name, etc) * (generated) universally unique identifier (serial no) */ static char *skip_over_blank(char *cp) { while (*cp && isspace(*cp)) cp++; return cp; } static char *skip_over_word(char *cp) { char ch; while ((ch = *cp)) { /* If we see a backslash, skip the next character */ if (ch == '\\') { cp++; if (*cp == '\0') break; cp++; continue; } if (isspace(ch) || ch == '<' || ch == '>') break; cp++; } return cp; } static char *strip_line(char *line) { char *p; line = skip_over_blank(line); p = line + strlen(line) - 1; while (*line) { if (isspace(*p)) *p-- = '\0'; else break; } return line; } #if 0 static char *parse_word(char **buf) { char *word, *next; word = *buf; if (*word == '\0') return NULL; word = skip_over_blank(word); next = skip_over_word(word); if (*next) { char *end = next - 1; if (*end == '"' || *end == '\'') *end = '\0'; *next++ = '\0'; } *buf = next; if (*word == '"' || *word == '\'') word++; return word; } #endif /* * Start parsing a new line from the cache. * * line starts with " continue parsing line * line starts with " skip line * line starts with other, return -BLKID_ERR_CACHE -> error */ static int parse_start(char **cp) { char *p; p = strip_line(*cp); /* Skip comment or blank lines. We can't just NUL the first '#' char, * in case it is inside quotes, or escaped. */ if (*p == '\0' || *p == '#') return 0; if (!strncmp(p, "", 9)) { DBG(DEBUG_READ, printf("found device trailer %9s\n", *cp)); *cp += 9; return 0; } return -BLKID_ERR_CACHE; } /* * Allocate a new device struct with device name filled in. Will handle * finding the device on lines of the form: * devname * devnamebar */ static int parse_dev(blkid_cache cache, blkid_dev *dev, char **cp) { char *start, *tmp, *end, *name; int ret; if ((ret = parse_start(cp)) <= 0) return ret; start = tmp = strchr(*cp, '>'); if (!start) { DBG(DEBUG_READ, printf("blkid: short line parsing dev: %s\n", *cp)); return -BLKID_ERR_CACHE; } start = skip_over_blank(start + 1); end = skip_over_word(start); DBG(DEBUG_READ, printf("device should be %*s\n", (int)(end - start), start)); if (**cp == '>') *cp = end; else (*cp)++; *tmp = '\0'; if (!(tmp = strrchr(end, '<')) || parse_end(&tmp) < 0) { DBG(DEBUG_READ, printf("blkid: missing ending: %s\n", end)); } else if (tmp) *tmp = '\0'; if (end - start <= 1) { DBG(DEBUG_READ, printf("blkid: empty device name: %s\n", *cp)); return -BLKID_ERR_CACHE; } name = blkid_strndup(start, end-start); if (name == NULL) return -BLKID_ERR_MEM; DBG(DEBUG_READ, printf("found dev %s\n", name)); if (!(*dev = blkid_get_dev(cache, name, BLKID_DEV_CREATE))) { free(name); return -BLKID_ERR_MEM; } free(name); return 1; } /* * Extract a tag of the form NAME="value" from the line. */ static int parse_token(char **name, char **value, char **cp) { char *end; if (!name || !value || !cp) return -BLKID_ERR_PARAM; if (!(*value = strchr(*cp, '='))) return 0; **value = '\0'; *name = strip_line(*cp); *value = skip_over_blank(*value + 1); if (**value == '"') { end = strchr(*value + 1, '"'); if (!end) { DBG(DEBUG_READ, printf("unbalanced quotes at: %s\n", *value)); *cp = *value; return -BLKID_ERR_CACHE; } (*value)++; *end = '\0'; end++; } else { end = skip_over_word(*value); if (*end) { *end = '\0'; end++; } } *cp = end; return 1; } /* * Extract a tag of the form value from the line. */ /* static int parse_xml(char **name, char **value, char **cp) { char *end; if (!name || !value || !cp) return -BLKID_ERR_PARAM; *name = strip_line(*cp); if ((*name)[0] != '<' || (*name)[1] == '/') return 0; FIXME: finish this. } */ /* * Extract a tag from the line. * * Return 1 if a valid tag was found. * Return 0 if no tag found. * Return -ve error code. */ static int parse_tag(blkid_cache cache, blkid_dev dev, char **cp) { char *name; char *value; int ret; if (!cache || !dev) return -BLKID_ERR_PARAM; if ((ret = parse_token(&name, &value, cp)) <= 0 /* && (ret = parse_xml(&name, &value, cp)) <= 0 */) return ret; /* Some tags are stored directly in the device struct */ if (!strcmp(name, "DEVNO")) dev->bid_devno = STRTOULL(value, 0, 0); else if (!strcmp(name, "PRI")) dev->bid_pri = strtol(value, 0, 0); else if (!strcmp(name, "TIME")) dev->bid_time = STRTOULL(value, 0, 0); else ret = blkid_set_tag(dev, name, value, strlen(value)); DBG(DEBUG_READ, printf(" tag: %s=\"%s\"\n", name, value)); return ret < 0 ? ret : 1; } /* * Parse a single line of data, and return a newly allocated dev struct. * Add the new device to the cache struct, if one was read. * * Lines are of the form /dev/foo * * Returns -ve value on error. * Returns 0 otherwise. * If a valid device was read, *dev_p is non-NULL, otherwise it is NULL * (e.g. comment lines, unknown XML content, etc). */ static int blkid_parse_line(blkid_cache cache, blkid_dev *dev_p, char *cp) { blkid_dev dev; int ret; if (!cache || !dev_p) return -BLKID_ERR_PARAM; *dev_p = NULL; DBG(DEBUG_READ, printf("line: %s\n", cp)); if ((ret = parse_dev(cache, dev_p, &cp)) <= 0) return ret; dev = *dev_p; while ((ret = parse_tag(cache, dev, &cp)) > 0) { ; } if (dev->bid_type == NULL) { DBG(DEBUG_READ, printf("blkid: device %s has no TYPE\n",dev->bid_name)); blkid_free_dev(dev); } DBG(DEBUG_READ, blkid_debug_dump_dev(dev)); return ret; } /* * Parse the specified filename, and return the data in the supplied or * a newly allocated cache struct. If the file doesn't exist, return a * new empty cache struct. */ void blkid_read_cache(blkid_cache cache) { FILE *file; char buf[4096]; int fd, lineno = 0; struct stat st; if (!cache) return; /* * If the file doesn't exist, then we just return an empty * struct so that the cache can be populated. */ if ((fd = open(cache->bic_filename, O_RDONLY)) < 0) return; if (fstat(fd, &st) < 0) goto errout; if ((st.st_mtime == cache->bic_ftime) || (cache->bic_flags & BLKID_BIC_FL_CHANGED)) { DBG(DEBUG_CACHE, printf("skipping re-read of %s\n", cache->bic_filename)); goto errout; } DBG(DEBUG_CACHE, printf("reading cache file %s\n", cache->bic_filename)); file = fdopen(fd, "r"); if (!file) goto errout; while (fgets(buf, sizeof(buf), file)) { blkid_dev dev; unsigned int end; lineno++; if (buf[0] == 0) continue; end = strlen(buf) - 1; /* Continue reading next line if it ends with a backslash */ while (buf[end] == '\\' && end < sizeof(buf) - 2 && fgets(buf + end, sizeof(buf) - end, file)) { end = strlen(buf) - 1; lineno++; } if (blkid_parse_line(cache, &dev, buf) < 0) { DBG(DEBUG_READ, printf("blkid: bad format on line %d\n", lineno)); continue; } } fclose(file); /* * Initially we do not need to write out the cache file. */ cache->bic_flags &= ~BLKID_BIC_FL_CHANGED; cache->bic_ftime = st.st_mtime; return; errout: close(fd); return; } #ifdef TEST_PROGRAM static void debug_dump_dev(blkid_dev dev) { struct list_head *p; if (!dev) { printf(" dev: NULL\n"); return; } printf(" dev: name = %s\n", dev->bid_name); printf(" dev: DEVNO=\"0x%0llx\"\n", (long long)dev->bid_devno); printf(" dev: TIME=\"%lld\"\n", (long long)dev->bid_time); printf(" dev: PRI=\"%d\"\n", dev->bid_pri); printf(" dev: flags = 0x%08X\n", dev->bid_flags); list_for_each(p, &dev->bid_tags) { blkid_tag tag = list_entry(p, struct blkid_struct_tag, bit_tags); if (tag) printf(" tag: %s=\"%s\"\n", tag->bit_name, tag->bit_val); else printf(" tag: NULL\n"); } printf("\n"); } int main(int argc, char**argv) { blkid_cache cache = NULL; int ret; blkid_debug_mask = DEBUG_ALL; if (argc > 2) { fprintf(stderr, "Usage: %s [filename]\n" "Test parsing of the cache (filename)\n", argv[0]); exit(1); } if ((ret = blkid_get_cache(&cache, argv[1])) < 0) fprintf(stderr, "error %d reading cache file %s\n", ret, argv[1] ? argv[1] : BLKID_CACHE_FILE); blkid_put_cache(cache); return ret; } #endif e2fsprogs-1.41.14/lib/blkid/getsize.c0000644031104000366760000001101511240667355015312 0ustar tytso/* * getsize.c --- get the size of a partition. * * Copyright (C) 1995, 1995 Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the * GNU Lesser General Public License. * %End-Header% */ #define _LARGEFILE_SOURCE #define _LARGEFILE64_SOURCE /* include this before sys/queues.h! */ #include "blkidP.h" #include #if HAVE_UNISTD_H #include #endif #if HAVE_ERRNO_H #include #endif #include #ifdef HAVE_SYS_IOCTL_H #include #endif #ifdef HAVE_LINUX_FD_H #include #endif #ifdef HAVE_SYS_DISKLABEL_H #include #endif #ifdef HAVE_SYS_DISK_H #ifdef HAVE_SYS_QUEUE_H #include /* for LIST_HEAD */ #endif #include #endif #ifdef __linux__ #include #endif #if HAVE_SYS_STAT_H #include #endif #if defined(__linux__) && defined(_IO) && !defined(BLKGETSIZE) #define BLKGETSIZE _IO(0x12,96) /* return device size */ #endif #if defined(__linux__) && defined(_IOR) && !defined(BLKGETSIZE64) #define BLKGETSIZE64 _IOR(0x12,114,size_t) /* return device size in bytes (u64 *arg) */ #endif #ifdef APPLE_DARWIN #define BLKGETSIZE DKIOCGETBLOCKCOUNT32 #endif /* APPLE_DARWIN */ static int valid_offset(int fd, blkid_loff_t offset) { char ch; if (blkid_llseek(fd, offset, 0) < 0) return 0; if (read(fd, &ch, 1) < 1) return 0; return 1; } /* * Returns the number of bytes in a partition */ blkid_loff_t blkid_get_dev_size(int fd) { int valid_blkgetsize64 = 1; #ifdef __linux__ struct utsname ut; #endif unsigned long long size64; unsigned long size; blkid_loff_t high, low; #ifdef FDGETPRM struct floppy_struct this_floppy; #endif #ifdef HAVE_SYS_DISKLABEL_H int part = -1; struct disklabel lab; struct partition *pp; char ch; struct stat st; #endif /* HAVE_SYS_DISKLABEL_H */ #ifdef DKIOCGETBLOCKCOUNT /* For Apple Darwin */ if (ioctl(fd, DKIOCGETBLOCKCOUNT, &size64) >= 0) { if ((sizeof(blkid_loff_t) < sizeof(unsigned long long)) && (size64 << 9 > 0xFFFFFFFF)) return 0; /* EFBIG */ return (blkid_loff_t) size64 << 9; } #endif #ifdef BLKGETSIZE64 #ifdef __linux__ if ((uname(&ut) == 0) && ((ut.release[0] == '2') && (ut.release[1] == '.') && (ut.release[2] < '6') && (ut.release[3] == '.'))) valid_blkgetsize64 = 0; #endif if (valid_blkgetsize64 && ioctl(fd, BLKGETSIZE64, &size64) >= 0) { if ((sizeof(blkid_loff_t) < sizeof(unsigned long long)) && ((size64) > 0xFFFFFFFF)) return 0; /* EFBIG */ return size64; } #endif #ifdef BLKGETSIZE if (ioctl(fd, BLKGETSIZE, &size) >= 0) return (blkid_loff_t)size << 9; #endif /* tested on FreeBSD 6.1-RELEASE i386 */ #ifdef DIOCGMEDIASIZE if (ioctl(fd, DIOCGMEDIASIZE, &size64) >= 0) return (off_t)size64; #endif /* DIOCGMEDIASIZE */ #ifdef FDGETPRM if (ioctl(fd, FDGETPRM, &this_floppy) >= 0) return (blkid_loff_t)this_floppy.size << 9; #endif #ifdef HAVE_SYS_DISKLABEL_H /* * This code works for FreeBSD 4.11 i386, except for the full device * (such as /dev/ad0). It doesn't work properly for newer FreeBSD * though. FreeBSD >= 5.0 should be covered by the DIOCGMEDIASIZE * above however. * * Note that FreeBSD >= 4.0 has disk devices as unbuffered (raw, * character) devices, so we need to check for S_ISCHR, too. */ if ((fstat(fd, &st) >= 0) && (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode))) part = st.st_rdev & 7; if (part >= 0 && (ioctl(fd, DIOCGDINFO, (char *)&lab) >= 0)) { pp = &lab.d_partitions[part]; if (pp->p_size) return pp->p_size << 9; } #endif /* HAVE_SYS_DISKLABEL_H */ { #ifdef HAVE_FSTAT64 struct stat64 st; if (fstat64(fd, &st) == 0) #else struct stat st; if (fstat(fd, &st) == 0) #endif if (S_ISREG(st.st_mode)) return st.st_size; } /* * OK, we couldn't figure it out by using a specialized ioctl, * which is generally the best way. So do binary search to * find the size of the partition. */ low = 0; for (high = 1024; valid_offset(fd, high); high *= 2) low = high; while (low < high - 1) { const blkid_loff_t mid = (low + high) / 2; if (valid_offset(fd, mid)) low = mid; else high = mid; } return low + 1; } #ifdef TEST_PROGRAM int main(int argc, char **argv) { long long bytes; int fd; if (argc < 2) { fprintf(stderr, "Usage: %s device\n" "Determine the size of a device\n", argv[0]); return 1; } if ((fd = open(argv[1], O_RDONLY)) < 0) perror(argv[0]); bytes = blkid_get_dev_size(fd); printf("Device %s has %Ld 1k blocks.\n", argv[1], (unsigned long long) bytes >> 10); return 0; } #endif e2fsprogs-1.41.14/lib/blkid/tst_types.c0000644031104000366760000000251111240667355015677 0ustar tytso/* * This testing program makes sure the blkid_types header file * * Copyright (C) 2006 by Theodore Ts'o. * * %Begin-Header% * This file may be redistributed under the terms of the GNU Public * License. * %End-Header% */ #include #include "blkid/blkid_types.h" #include #include int main(int argc, char **argv) { if (sizeof(__u8) != 1) { printf("Sizeof(__u8) is %d should be 1\n", (int)sizeof(__u8)); exit(1); } if (sizeof(__s8) != 1) { printf("Sizeof(_s8) is %d should be 1\n", (int)sizeof(__s8)); exit(1); } if (sizeof(__u16) != 2) { printf("Sizeof(__u16) is %d should be 2\n", (int)sizeof(__u16)); exit(1); } if (sizeof(__s16) != 2) { printf("Sizeof(__s16) is %d should be 2\n", (int)sizeof(__s16)); exit(1); } if (sizeof(__u32) != 4) { printf("Sizeof(__u32) is %d should be 4\n", (int)sizeof(__u32)); exit(1); } if (sizeof(__s32) != 4) { printf("Sizeof(__s32) is %d should be 4\n", (int)sizeof(__s32)); exit(1); } if (sizeof(__u64) != 8) { printf("Sizeof(__u64) is %d should be 8\n", (int)sizeof(__u64)); exit(1); } if (sizeof(__s64) != 8) { printf("Sizeof(__s64) is %d should be 8\n", (int)sizeof(__s64)); exit(1); } printf("The blkid_types.h types are correct.\n"); exit(0); } e2fsprogs-1.41.14/lib/ss/0000755031104000366760000000000011501420251013014 5ustar tytsoe2fsprogs-1.41.14/lib/ss/data.c0000644031104000366760000000153011240667355014112 0ustar tytso/* * Copyright 1987, 1988, 1989 Massachusetts Institute of Technology * (Student Information Processing Board) * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose is hereby granted, provided that * the names of M.I.T. and the M.I.T. S.I.P.B. not be used in * advertising or publicity pertaining to distribution of the software * without specific, written prior permission. M.I.T. and the * M.I.T. S.I.P.B. make no representations about the suitability of * this software for any purpose. It is provided "as is" without * express or implied warranty. */ #include #include "ss_internal.h" static const char copyright[] = "Copyright 1987, 1988, 1989 by the Massachusetts Institute of Technology"; ss_data **_ss_table = (ss_data **)NULL; char *_ss_pager_name = (char *)NULL; e2fsprogs-1.41.14/lib/ss/std_rqs.ct0000644031104000366760000000164111240667355015047 0ustar tytso command_table ss_std_requests; request ss_self_identify, "Identify the subsystem.", ".", (dont_list, dont_summarize); request ss_help, "Display info on command or topic.", help; unimplemented ss_list_help, "List topics for which help is available.", list_help, lh; request ss_list_requests, "List available commands.", list_requests, lr, "?"; request ss_quit, "Leave the subsystem.", quit, q; unimplemented ss_abbrev, "Enable/disable abbreviation processing of request lines.", abbrev, ab; unimplemented ss_execute, "Execute a UNIX command line.", execute, e; unimplemented ss_summarize_requests, "Produce a list of the most commonly used requests.", "?"; request ss_subsystem_name, "Return the name of this subsystem.", subsystem_name, (dont_list); request ss_subsystem_version, "Return the version of this subsystem.", subsystem_version, (dont_list); end; e2fsprogs-1.41.14/lib/ss/request_tbl.c0000644031104000366760000000366711240667355015547 0ustar tytso/* * Copyright 1987, 1988 by MIT Student Information Processing Board * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose is hereby granted, provided that * the names of M.I.T. and the M.I.T. S.I.P.B. not be used in * advertising or publicity pertaining to distribution of the software * without specific, written prior permission. M.I.T. and the * M.I.T. S.I.P.B. make no representations about the suitability of * this software for any purpose. It is provided "as is" without * express or implied warranty. */ #ifdef HAVE_ERRNO_H #include #endif #include "ss_internal.h" #define ssrt ss_request_table /* for some readable code... */ void ss_add_request_table(sci_idx, rqtbl_ptr, position, code_ptr) int sci_idx; ssrt *rqtbl_ptr; int position; /* 1 -> becomes second... */ int *code_ptr; { register ss_data *info; register int i, size; ssrt **t; info = ss_info(sci_idx); for (size=0; info->rqt_tables[size] != (ssrt *)NULL; size++) ; /* size == C subscript of NULL == #elements */ size += 2; /* new element, and NULL */ t = (ssrt **)realloc(info->rqt_tables, (unsigned)size*sizeof(ssrt)); if (t == (ssrt **)NULL) { *code_ptr = errno; return; } info->rqt_tables = t; if (position > size - 2) position = size - 2; if (size > 1) for (i = size - 2; i >= position; i--) info->rqt_tables[i+1] = info->rqt_tables[i]; info->rqt_tables[position] = rqtbl_ptr; info->rqt_tables[size-1] = (ssrt *)NULL; *code_ptr = 0; } void ss_delete_request_table(sci_idx, rqtbl_ptr, code_ptr) int sci_idx; ssrt *rqtbl_ptr; int *code_ptr; { register ss_data *info; register ssrt **rt1, **rt2; *code_ptr = SS_ET_TABLE_NOT_FOUND; info = ss_info(sci_idx); rt1 = info->rqt_tables; for (rt2 = rt1; *rt1; rt1++) { if (*rt1 != rqtbl_ptr) { *rt2++ = *rt1; *code_ptr = 0; } } *rt2 = (ssrt *)NULL; return; } e2fsprogs-1.41.14/lib/ss/get_readline.c0000644031104000366760000000513411334637073015625 0ustar tytso/* * Copyright 2003 by MIT Student Information Processing Board * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose is hereby granted, provided that * the names of M.I.T. and the M.I.T. S.I.P.B. not be used in * advertising or publicity pertaining to distribution of the software * without specific, written prior permission. M.I.T. and the * M.I.T. S.I.P.B. make no representations about the suitability of * this software for any purpose. It is provided "as is" without * express or implied warranty. */ #ifdef HAS_STDLIB_H #include #endif #include "ss_internal.h" #define size sizeof(ss_data *) #ifdef HAVE_DLOPEN #include #endif static void ss_release_readline(ss_data *info) { #ifdef HAVE_DLOPEN if (!info->readline_handle) return; info->readline = 0; info->add_history = 0; info->redisplay = 0; info->rl_completion_matches = 0; dlclose(info->readline_handle); info->readline_handle = 0; #endif } /* Libraries we will try to use for readline/editline functionality */ #define DEFAULT_LIBPATH "libreadline.so.6:libreadline.so.5:libreadline.so.4:libreadline.so:libedit.so.2:libedit.so:libeditline.so.0:libeditline.so" void ss_get_readline(int sci_idx) { #ifdef HAVE_DLOPEN void *handle = NULL; ss_data *info = ss_info(sci_idx); const char **t, *libpath = 0; char *tmp, *cp, *next; char **(**completion_func)(const char *, int, int); if (info->readline_handle) return; libpath = ss_safe_getenv("SS_READLINE_PATH"); if (!libpath) libpath = DEFAULT_LIBPATH; if (*libpath == 0 || !strcmp(libpath, "none")) return; tmp = malloc(strlen(libpath)+1); if (!tmp) return; strcpy(tmp, libpath); for (cp = tmp; cp; cp = next) { next = strchr(cp, ':'); if (next) *next++ = 0; if (*cp == 0) continue; if ((handle = dlopen(cp, RTLD_NOW))) { /* printf("Using %s for readline library\n", cp); */ break; } } free(tmp); if (!handle) return; info->readline_handle = handle; info->readline = (char *(*)(const char *)) dlsym(handle, "readline"); info->add_history = (void (*)(const char *)) dlsym(handle, "add_history"); info->redisplay = (void (*)(void)) dlsym(handle, "rl_forced_update_display"); info->rl_completion_matches = (char **(*)(const char *, char *(*)(const char *, int))) dlsym(handle, "rl_completion_matches"); if ((t = dlsym(handle, "rl_readline_name")) != NULL) *t = info->subsystem_name; if ((completion_func = dlsym(handle, "rl_attempted_completion_function")) != NULL) *completion_func = ss_rl_completion; info->readline_shutdown = ss_release_readline; #endif } e2fsprogs-1.41.14/lib/ss/Makefile.in0000644031104000366760000001640311240667355015107 0ustar tytso# # Makefile for lib/ss # srcdir = @srcdir@ top_srcdir = @top_srcdir@ VPATH = @srcdir@ top_builddir = ../.. my_dir = lib/ss INSTALL = @INSTALL@ DLOPEN_LIB = @DLOPEN_LIB@ @MCONFIG@ LIBRARY= libss LIBDIR= ss ELF_VERSION = 2.0 ELF_SO_VERSION = 2 ELF_IMAGE = libss ELF_MYDIR = ss ELF_INSTALL_DIR = $(root_libdir) ELF_OTHER_LIBS = -L../.. -lcom_err $(DLOPEN_LIB) BSDLIB_VERSION = 1.0 BSDLIB_IMAGE = libss BSDLIB_MYDIR = ss BSDLIB_INSTALL_DIR = $(root_libdir) TAGS=etags COMPILE_ET=../et/compile_et --build-tree MK_CMDS=_SS_DIR_OVERRIDE=. ./mk_cmds # -I.. is so that ss/ss_err.h works # -I$(srcdir)/.. is so that ss/ss.h works # -I$(srcdir)/../et is so com_err.h works XTRA_CFLAGS= -I$(srcdir)/../et .c.o: $(E) " CC $<" $(Q) $(CC) $(ALL_CFLAGS) -c $< @PROFILE_CMT@ $(Q) $(CC) $(ALL_CFLAGS) -g -pg -o profiled/$*.o -c $< @CHECKER_CMT@ $(Q) $(CC) $(ALL_CFLAGS) -checker -g -o checker/$*.o -c $< @ELF_CMT@ $(Q) $(CC) $(ALL_CFLAGS) -DSHARED_ELF_LIB -fPIC -o elfshared/$*.o -c $< @BSDLIB_CMT@ $(Q) $(CC) $(ALL_CFLAGS) $(BSDLIB_PIC_FLAG) -o pic/$*.o -c $< # for the library # with ss_err.o first, ss_err.h should get rebuilt first too. should not # be relying on this, though. OBJS= ss_err.o \ std_rqs.o \ invocation.o help.o \ execute_cmd.o listen.o parse.o error.o prompt.o \ request_tbl.o list_rqs.o pager.o requests.o \ data.o get_readline.o SRCS= $(srcdir)/invocation.c $(srcdir)/help.c \ $(srcdir)/execute_cmd.c $(srcdir)/listen.c $(srcdir)/parse.c \ $(srcdir)/error.c $(srcdir)/prompt.c $(srcdir)/request_tbl.c \ $(srcdir)/list_rqs.c $(srcdir)/pager.c $(srcdir)/requests.c \ $(srcdir)/data.c $(srcdir)/get_readline.c all:: mk_cmds @MAKEFILE_LIBRARY@ @MAKEFILE_ELF@ @MAKEFILE_BSDLIB@ @MAKEFILE_PROFILE@ @MAKEFILE_CHECKER@ CODE= $(SRCS) $(MKCMDSFILES) MKCMDSOBJS= mk_cmds.o utils.o options.o ct.tab.o cmd_tbl.lex.o MKCMDSFILES= mk_cmds.c utils.c options.c ct.y cmd_tbl.lex.l MKCMDSCSRCS= mk_cmds.c utils.c options.c ct.tab.c cmd_tbl.lex.c HFILES= ss.h ss_internal.h SHARE_FILES= ct_c.awk ct_c.sed INSTALL_HFILES= ss.h # for 'tags' and dependencies CFILES= $(SRCS) $(MKCMDSCSRCS) test_ss.c # for building archives FILES= $(SRCS) $(MKCMDSFILES) $(HFILES) \ ss_err.et std_rqs.ct Makefile \ test_ss.c ss all:: libss.a ss.pc # libss_p.a lint std_rqs.c: std_rqs.ct mk_cmds $(E) " MK_CMDS $@" $(Q) DIR=$(srcdir) $(MK_CMDS) $(srcdir)/std_rqs.ct std_rqs.o: ss_err.h test_cmd.c: test_cmd.ct mk_cmds $(E) " MK_CMDS $@" $(Q) DIR=$(srcdir) $(MK_CMDS) $(srcdir)/test_cmd.ct ss_err.c ss_err.h: ss_err.et $(E) " COMPILE_ET ss_err.et" $(Q) $(COMPILE_ET) $(srcdir)/ss_err.et ct.tab.c ct.tab.h: ct.y $(RM) -f ct.tab.* y.* $(YACC) -d $(srcdir)/ct.y $(MV) -f y.tab.c ct.tab.c $(MV) -f y.tab.h ct.tab.h #libss.o: $(OBJS) # $(LD) -r -s -o $@ $(OBJS) # $(CHMOD) -x $@ mk_cmds: $(DEP_SUBSTITUTE) $(srcdir)/mk_cmds.sh.in $(E) " SUBST $@" $(Q) $(SUBSTITUTE) $(srcdir)/mk_cmds.sh.in mk_cmds $(Q) $(CHMOD) +x mk_cmds ss.pc: $(srcdir)/ss.pc.in $(top_builddir)/config.status $(E) " CONFIG.STATUS $@" $(Q) cd $(top_builddir); CONFIG_FILES=lib/ss/ss.pc ./config.status installdirs:: $(E) " MKINSTALLDIRS $(libdir) $(includedir)/ss $(datadir)/ss $(bindir)" $(Q) $(MKINSTALLDIRS) $(DESTDIR)$(libdir) \ $(DESTDIR)$(includedir)/ss $(DESTDIR)$(datadir)/ss \ $(DESTDIR)$(bindir) $(DESTDIR)$(libdir)/pkgconfig install:: libss.a $(INSTALL_HFILES) installdirs ss_err.h mk_cmds ss.pc $(E) " INSTALL_DATA $(DESTDIR)$(libdir)/libss.a" $(Q) $(INSTALL_DATA) libss.a $(DESTDIR)$(libdir)/libss.a -$(Q) $(RANLIB) $(DESTDIR)$(libdir)/libss.a $(Q) $(CHMOD) $(LIBMODE) $(DESTDIR)$(libdir)/libss.a $(Q) $(RM) -f $(DESTDIR)$(includedir)/ss/* $(Q) for i in $(INSTALL_HFILES); do \ echo " INSTALL_DATA $(DESTDIR)$(includedir)/ss/$$i"; \ $(INSTALL_DATA) $(srcdir)/$$i $(DESTDIR)$(includedir)/ss/$$i; \ done $(E) " INSTALL_DATA $(includedir)/ss/ss_err.h" $(Q) $(INSTALL_DATA) ss_err.h $(DESTDIR)$(includedir)/ss/ss_err.h $(Q) for i in $(SHARE_FILES); do \ echo " INSTALL_DATA $(DESTDIR)$(datadir)/ss/$$i"; \ $(INSTALL_DATA) $(srcdir)/$$i $(DESTDIR)$(datadir)/ss/$$i; \ done $(E) " INSTALL $(bindir)/mk_cmds" $(Q) $(INSTALL) mk_cmds $(DESTDIR)$(bindir)/mk_cmds $(E) " INSTALL_DATA $(man1dir)/mk_cmds.1" $(Q) $(INSTALL_DATA) $(srcdir)/mk_cmds.1 $(DESTDIR)$(man1dir)/mk_cmds.1 $(E) " INSTALL_DATA $(libdir)/pkgconfig/ss.pc" $(Q) $(INSTALL_DATA) ss.pc $(DESTDIR)$(libdir)/pkgconfig/ss.pc uninstall:: $(RM) -f $(DESTDIR)$(libdir)/libss.a $(DESTDIR)$(bindir)/mk_cmds \ $(DESTDIR)$(libdir)/pkgconfig/ss.pc \ $(DESTDIR)$(man1dir)/mk_cmds.1 $(RM) -rf $(DESTDIR)$(includedir)/ss $(DESTDIR)$(datadir)/ss test_ss: test_ss.o test_cmd.o $(DEPLIBSS) $(DEPLIBCOM_ERR) $(E) " LD $@" $(Q) $(CC) -o $@ test_ss.o test_cmd.o $(ALL_CFLAGS) \ $(LIBSS) $(LIBCOM_ERR) check:: all test_ss $(E) " RUN TEST test_ss" -@(LD_LIBRARY_PATH=$(LIB) DYLD_LIBRARY_PATH=$(LIB) ./test_ss -f $(srcdir)/test_script > test_out 2>&1; exit 0) $(Q) if diff test_out $(srcdir)/test_script_expected > test.diff; then \ true ; else echo "Regression test for ss library failed!"; exit 1 ; fi clean:: $(RM) -f ../libss.a libss.a mk_cmds ss_err.h ss_err.c std_rqs.c \ tst_cmds.c test_ss test_out test.diff *.o *~ \#* *.bak core \ test_cmd.c mostlyclean:: clean distclean:: clean $(RM) -f .depend Makefile ss.pc \ $(srcdir)/TAGS $(srcdir)/Makefile.in.old # # Hack to parallel makes recognize dependencies correctly. # $(top_builddir)/lib/ss/ss_err.h: ss_err.h $(OBJS): subdirs # +++ Dependency line eater +++ # # Makefile dependencies follow. This must be the last section in # the Makefile.in file # invocation.o: $(srcdir)/invocation.c $(srcdir)/ss_internal.h $(srcdir)/ss.h \ $(top_builddir)/lib/ss/ss_err.h $(top_srcdir)/lib/et/com_err.h help.o: $(srcdir)/help.c $(srcdir)/ss_internal.h $(srcdir)/ss.h \ $(top_builddir)/lib/ss/ss_err.h $(top_srcdir)/lib/et/com_err.h execute_cmd.o: $(srcdir)/execute_cmd.c $(srcdir)/ss_internal.h $(srcdir)/ss.h \ $(top_builddir)/lib/ss/ss_err.h $(top_srcdir)/lib/et/com_err.h listen.o: $(srcdir)/listen.c $(srcdir)/ss_internal.h $(srcdir)/ss.h \ $(top_builddir)/lib/ss/ss_err.h $(top_srcdir)/lib/et/com_err.h parse.o: $(srcdir)/parse.c $(srcdir)/ss_internal.h $(srcdir)/ss.h \ $(top_builddir)/lib/ss/ss_err.h $(top_srcdir)/lib/et/com_err.h error.o: $(srcdir)/error.c $(top_srcdir)/lib/et/com_err.h \ $(srcdir)/ss_internal.h $(srcdir)/ss.h $(top_builddir)/lib/ss/ss_err.h \ $(top_srcdir)/lib/et/com_err.h prompt.o: $(srcdir)/prompt.c $(srcdir)/ss_internal.h $(srcdir)/ss.h \ $(top_builddir)/lib/ss/ss_err.h $(top_srcdir)/lib/et/com_err.h request_tbl.o: $(srcdir)/request_tbl.c $(srcdir)/ss_internal.h $(srcdir)/ss.h \ $(top_builddir)/lib/ss/ss_err.h $(top_srcdir)/lib/et/com_err.h list_rqs.o: $(srcdir)/list_rqs.c $(srcdir)/ss_internal.h $(srcdir)/ss.h \ $(top_builddir)/lib/ss/ss_err.h $(top_srcdir)/lib/et/com_err.h pager.o: $(srcdir)/pager.c $(srcdir)/ss_internal.h $(srcdir)/ss.h \ $(top_builddir)/lib/ss/ss_err.h $(top_srcdir)/lib/et/com_err.h requests.o: $(srcdir)/requests.c $(srcdir)/ss_internal.h $(srcdir)/ss.h \ $(top_builddir)/lib/ss/ss_err.h $(top_srcdir)/lib/et/com_err.h data.o: $(srcdir)/data.c $(srcdir)/ss_internal.h $(srcdir)/ss.h \ $(top_builddir)/lib/ss/ss_err.h $(top_srcdir)/lib/et/com_err.h get_readline.o: $(srcdir)/get_readline.c $(srcdir)/ss_internal.h \ $(srcdir)/ss.h $(top_builddir)/lib/ss/ss_err.h \ $(top_srcdir)/lib/et/com_err.h e2fsprogs-1.41.14/lib/ss/ss.h0000644031104000366760000000606711240667355013645 0ustar tytso/* * Copyright 1987, 1988 by MIT Student Information Processing Board * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose is hereby granted, provided that * the names of M.I.T. and the M.I.T. S.I.P.B. not be used in * advertising or publicity pertaining to distribution of the software * without specific, written prior permission. M.I.T. and the * M.I.T. S.I.P.B. make no representations about the suitability of * this software for any purpose. It is provided "as is" without * express or implied warranty. * * This quote is just too good to not pass on: * * "BTW, I would have rejected the name Story Server because its * initials are SS, the name of the secret police in Nazi * Germany, probably the most despised pair of letters in western * culture." --- http://scriptingnewsarchive.userland.com/1999/12/13 * * Let no one say political correctness isn't dead.... */ #ifndef _ss_h #define _ss_h __FILE__ #include #define __SS_CONST const #define __SS_PROTO (int, const char * const *, int, void *) #ifdef __GNUC__ #define __SS_ATTR(x) __attribute__(x) #else #define __SS_ATTR(x) #endif typedef __SS_CONST struct _ss_request_entry { __SS_CONST char * __SS_CONST *command_names; /* whatever */ void (* __SS_CONST function) __SS_PROTO; /* foo */ __SS_CONST char * __SS_CONST info_string; /* NULL */ int flags; /* 0 */ } ss_request_entry; typedef __SS_CONST struct _ss_request_table { int version; ss_request_entry *requests; } ss_request_table; #define SS_RQT_TBL_V2 2 typedef struct _ss_rp_options { /* DEFAULT VALUES */ int version; /* SS_RP_V1 */ void (*unknown) __SS_PROTO; /* call for unknown command */ int allow_suspend; int catch_int; } ss_rp_options; #define SS_RP_V1 1 #define SS_OPT_DONT_LIST 0x0001 #define SS_OPT_DONT_SUMMARIZE 0x0002 void ss_help __SS_PROTO; #if 0 char *ss_current_request(); /* This is actually a macro */ #endif char *ss_name(int sci_idx); void ss_error (int, long, char const *, ...) __SS_ATTR((format(printf, 3, 4))); void ss_perror (int, long, char const *); int ss_create_invocation(const char *, const char *, void *, ss_request_table *, int *); void ss_delete_invocation(int); int ss_listen(int); int ss_execute_line(int, char *); void ss_add_request_table(int, ss_request_table *, int, int *); void ss_delete_request_table(int, ss_request_table *, int *); void ss_abort_subsystem(int sci_idx, int code); void ss_quit(int argc, const char * const *argv, int sci_idx, void *infop); void ss_self_identify(int argc, const char * const *argv, int sci_idx, void *infop); void ss_subsystem_name(int argc, const char * const *argv, int sci_idx, void *infop); void ss_subsystem_version(int argc, const char * const *argv, int sci_idx, void *infop); void ss_unimplemented(int argc, const char * const *argv, int sci_idx, void *infop); void ss_set_prompt(int sci_idx, char *new_prompt); char *ss_get_prompt(int sci_idx); void ss_get_readline(int sci_idx); extern ss_request_table ss_std_requests; #endif /* _ss_h */ e2fsprogs-1.41.14/lib/ss/pager.c0000644031104000366760000000613411240667355014304 0ustar tytso/* * Pager: Routines to create a "more" running out of a particular file * descriptor. * * Copyright 1987, 1988 by MIT Student Information Processing Board * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose is hereby granted, provided that * the names of M.I.T. and the M.I.T. S.I.P.B. not be used in * advertising or publicity pertaining to distribution of the software * without specific, written prior permission. M.I.T. and the * M.I.T. S.I.P.B. make no representations about the suitability of * this software for any purpose. It is provided "as is" without * express or implied warranty. */ #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_ERRNO_H #include #else extern int errno; #endif #include "ss_internal.h" #include #include #include #include #ifdef HAVE_SYS_PRCTL_H #include #else #define PR_GET_DUMPABLE 3 #endif #if (!defined(HAVE_PRCTL) && defined(linux)) #include #endif static char MORE[] = "more"; extern char *_ss_pager_name; extern char *getenv PROTOTYPE((const char *)); char *ss_safe_getenv(const char *arg) { if ((getuid() != geteuid()) || (getgid() != getegid())) return NULL; #if HAVE_PRCTL if (prctl(PR_GET_DUMPABLE, 0, 0, 0, 0) == 0) return NULL; #else #if (defined(linux) && defined(SYS_prctl)) if (syscall(SYS_prctl, PR_GET_DUMPABLE, 0, 0, 0, 0) == 0) return NULL; #endif #endif #ifdef HAVE___SECURE_GETENV return __secure_getenv(arg); #else return getenv(arg); #endif } /* * this needs a *lot* of work.... * * run in same process * handle SIGINT sensibly * allow finer control -- put-page-break-here */ #ifndef NO_FORK int ss_pager_create(void) { int filedes[2]; if (pipe(filedes) != 0) return(-1); switch(fork()) { case -1: return(-1); case 0: /* * Child; dup read half to 0, close all but 0, 1, and 2 */ if (dup2(filedes[0], 0) == -1) exit(1); ss_page_stdin(); default: /* * Parent: close "read" side of pipe, return * "write" side. */ (void) close(filedes[0]); return(filedes[1]); } } #else /* don't fork */ int ss_pager_create() { int fd; fd = open("/dev/tty", O_WRONLY, 0); return fd; } #endif static int write_all(int fd, char *buf, size_t count) { ssize_t ret; int c = 0; while (count > 0) { ret = write(fd, buf, count); if (ret < 0) { if ((errno == EAGAIN) || (errno == EINTR)) continue; return -1; } count -= ret; buf += ret; c += ret; } return c; } void ss_page_stdin() { int i; sigset_t mask; for (i = 3; i < 32; i++) (void) close(i); (void) signal(SIGINT, SIG_DFL); sigprocmask(SIG_BLOCK, 0, &mask); sigdelset(&mask, SIGINT); sigprocmask(SIG_SETMASK, &mask, 0); if (_ss_pager_name == (char *)NULL) { if ((_ss_pager_name = ss_safe_getenv("PAGER")) == (char *)NULL) _ss_pager_name = MORE; } (void) execlp(_ss_pager_name, _ss_pager_name, (char *) NULL); { /* minimal recovery if pager program isn't found */ char buf[80]; register int n; while ((n = read(0, buf, 80)) > 0) write_all(1, buf, n); } exit(errno); } e2fsprogs-1.41.14/lib/ss/ct_c.awk0000644031104000366760000000301711240667355014453 0ustar tytso/^command_table / { cmdtbl = $2; printf "/* %s.c - automatically generated from %s.ct */\n", \ rootname, rootname > outfile print "#include " > outfile print "" >outfile } /^BOR$/ { cmdnum++ options = 0 cmdtab = "" printf "static char const * const ssu%05d[] = {\n", cmdnum > outfile } /^sub/ { subr = substr($0, 6, length($0)-5) } /^hlp/ { help = substr($0, 6, length($0)-5) } /^cmd/ { cmd = substr($0, 6, length($0)-5) printf "%s\"%s\",\n", cmdtab, cmd > outfile cmdtab = " " } /^opt/ { opt = substr($0, 6, length($0)-5) if (opt == "dont_list") { options += 1 } if (opt == "dont_summarize") { options += 2 } } /^EOR/ { print " (char const *)0" > outfile print "};" > outfile printf "extern void %s __SS_PROTO;\n", subr > outfile # Work around a bug in gawk 3.0.5 awk_bug = cmdnum subr_tab[awk_bug] = subr options_tab[awk_bug] = options help_tab[awk_bug] = help } /^[0-9]/ { linenum = $1; } /^ERROR/ { error = substr($0, 8, length($0)-7) printf "Error in line %d: %s\n", linenum, error print "#__ERROR_IN_FILE__" > outfile } END { printf "static ss_request_entry ssu%05d[] = {\n", cmdnum+1 > outfile for (i=1; i <= cmdnum; i++) { printf " { ssu%05d,\n", i > outfile printf " %s,\n", subr_tab[i] > outfile printf " \"%s\",\n", help_tab[i] > outfile printf " %d },\n", options_tab[i] > outfile } print " { 0, 0, 0, 0 }" > outfile print "};" > outfile print "" > outfile printf "ss_request_table %s = { 2, ssu%05d };\n", \ cmdtbl, cmdnum+1 > outfile } e2fsprogs-1.41.14/lib/ss/requests.c0000644031104000366760000000370211240667355015057 0ustar tytso/* * Various minor routines... * * Copyright 1987, 1988, 1989 by MIT * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose is hereby granted, provided that * the names of M.I.T. and the M.I.T. S.I.P.B. not be used in * advertising or publicity pertaining to distribution of the software * without specific, written prior permission. M.I.T. and the * M.I.T. S.I.P.B. make no representations about the suitability of * this software for any purpose. It is provided "as is" without * express or implied warranty. */ #include #include "ss_internal.h" #define DECLARE(name) void name(int argc,const char * const *argv, \ int sci_idx, void *infop) /* * ss_self_identify -- assigned by default to the "." request */ void ss_self_identify(int argc __SS_ATTR((unused)), const char * const *argv __SS_ATTR((unused)), int sci_idx, void *infop __SS_ATTR((unused))) { register ss_data *info = ss_info(sci_idx); printf("%s version %s\n", info->subsystem_name, info->subsystem_version); } /* * ss_subsystem_name -- print name of subsystem */ void ss_subsystem_name(int argc __SS_ATTR((unused)), const char * const *argv __SS_ATTR((unused)), int sci_idx, void *infop __SS_ATTR((unused))) { printf("%s\n", ss_info(sci_idx)->subsystem_name); } /* * ss_subsystem_version -- print version of subsystem */ void ss_subsystem_version(int argc __SS_ATTR((unused)), const char * const *argv __SS_ATTR((unused)), int sci_idx, void *infop __SS_ATTR((unused))) { printf("%s\n", ss_info(sci_idx)->subsystem_version); } /* * ss_unimplemented -- routine not implemented (should be * set up as (dont_list,dont_summarize)) */ void ss_unimplemented(int argc __SS_ATTR((unused)), const char * const *argv __SS_ATTR((unused)), int sci_idx, void *infop __SS_ATTR((unused))) { ss_perror(sci_idx, SS_ET_UNIMPLEMENTED, ""); } e2fsprogs-1.41.14/lib/ss/help.c0000644031104000366760000001074611240667355014142 0ustar tytso/* * Copyright 1987, 1988 by MIT Student Information Processing Board * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose is hereby granted, provided that * the names of M.I.T. and the M.I.T. S.I.P.B. not be used in * advertising or publicity pertaining to distribution of the software * without specific, written prior permission. M.I.T. and the * M.I.T. S.I.P.B. make no representations about the suitability of * this software for any purpose. It is provided "as is" without * express or implied warranty. */ #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_ERRNO_H #include #else extern int errno; #endif #include #include #include #include #ifdef NEED_SYS_FCNTL_H /* just for O_* */ #include #endif #ifdef HAVE_SYS_WAIT_H #include #endif #include "ss_internal.h" void ss_help (argc, argv, sci_idx, info_ptr) int argc; char const * const *argv; int sci_idx; pointer info_ptr; { char *buffer; char const *request_name; int code; int fd, child; register int idx; register ss_data *info; request_name = ss_current_request(sci_idx, &code); if (code != 0) { ss_perror(sci_idx, code, ""); return; /* no ss_abort_line, if invalid invocation */ } if (argc == 1) { ss_list_requests(argc, argv, sci_idx, info_ptr); return; } else if (argc != 2) { /* should do something better than this */ buffer = malloc(80+2*strlen(request_name)); if (!buffer) { ss_perror(sci_idx, 0, "couldn't allocate memory to print usage message"); return; } sprintf(buffer, "usage:\n\t%s [topic|command]\nor\t%s\n", request_name, request_name); ss_perror(sci_idx, 0, buffer); free(buffer); return; } info = ss_info(sci_idx); if (info->info_dirs == (char **)NULL) { ss_perror(sci_idx, SS_ET_NO_INFO_DIR, (char *)NULL); return; } if (info->info_dirs[0] == (char *)NULL) { ss_perror(sci_idx, SS_ET_NO_INFO_DIR, (char *)NULL); return; } for (fd = -1, idx = 0; info->info_dirs[idx] != (char *)NULL; idx++) { buffer = malloc(strlen (info->info_dirs[idx]) + 1 + strlen (argv[1]) + 6); if (!buffer) { ss_perror(sci_idx, 0, "couldn't allocate memory for help filename"); return; } (void) strcpy(buffer, info->info_dirs[idx]); (void) strcat(buffer, "/"); (void) strcat(buffer, argv[1]); (void) strcat(buffer, ".info"); fd = open(buffer, O_RDONLY); free(buffer); if (fd >= 0) break; } if (fd < 0) { #define MSG "No info found for " char *buf = malloc(strlen (MSG) + strlen (argv[1]) + 1); strcpy(buf, MSG); strcat(buf, argv[1]); ss_perror(sci_idx, 0, buf); free(buf); return; } switch (child = fork()) { case -1: ss_perror(sci_idx, errno, "Can't fork for pager"); return; case 0: (void) dup2(fd, 0); /* put file on stdin */ ss_page_stdin(); default: (void) close(fd); /* what can we do if it fails? */ while (wait(0) != child) { /* do nothing if wrong pid */ }; } } #ifndef HAVE_DIRENT_H #include #else #include #endif void ss_add_info_dir(sci_idx, info_dir, code_ptr) int sci_idx; char *info_dir; int *code_ptr; { register ss_data *info; DIR *d; int n_dirs; register char **dirs; info = ss_info(sci_idx); if (info_dir == NULL || *info_dir == '\0') { *code_ptr = SS_ET_NO_INFO_DIR; return; } if ((d = opendir(info_dir)) == (DIR *)NULL) { *code_ptr = errno; return; } closedir(d); dirs = info->info_dirs; for (n_dirs = 0; dirs[n_dirs] != (char *)NULL; n_dirs++) ; /* get number of non-NULL dir entries */ dirs = (char **)realloc((char *)dirs, (unsigned)(n_dirs + 2)*sizeof(char *)); if (dirs == (char **)NULL) { info->info_dirs = (char **)NULL; *code_ptr = errno; return; } info->info_dirs = dirs; dirs[n_dirs + 1] = (char *)NULL; dirs[n_dirs] = malloc((unsigned)strlen(info_dir)+1); strcpy(dirs[n_dirs], info_dir); *code_ptr = 0; } void ss_delete_info_dir(sci_idx, info_dir, code_ptr) int sci_idx; char *info_dir; int *code_ptr; { register char **i_d; register char **info_dirs; info_dirs = ss_info(sci_idx)->info_dirs; for (i_d = info_dirs; *i_d; i_d++) { if (!strcmp(*i_d, info_dir)) { while (*i_d) { *i_d = *(i_d+1); i_d++; } *code_ptr = 0; return; } } *code_ptr = SS_ET_NO_INFO_DIR; } e2fsprogs-1.41.14/lib/ss/mk_cmds.sh.in0000644031104000366760000000240011240667355015410 0ustar tytso#!/bin/sh # # datarootdir=@datarootdir@ DIR="${DIR-@datadir@/ss}" SS_DIR="@SS_DIR@" AWK=@AWK@ SED=@SED@ for as_var in \ LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ LC_TELEPHONE LC_TIME do if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then eval $as_var=C; export $as_var else $as_unset $as_var fi done if test "x$1" = x ; then echo "Usage: mk_cmds file" exit 1 fi if test -n "$_SS_DIR_OVERRIDE" ; then DIR="$_SS_DIR_OVERRIDE"; fi if test ! -f $DIR/ct_c.sed || test ! -f $DIR/ct_c.awk ; then DIR="$SS_DIR" # echo "Falling back to $DIR..." if test ! -f "$DIR/ct_c.sed" || test ! -f "$DIR/ct_c.awk" ; then echo "mk_cmds: Couldn't find mk_cmds's template files." exit 1 fi fi FILE="$1" ROOT=`echo $1 | sed -e s/.ct$//` BASE=`basename "$ROOT"` TMP="ct$$.c" if test ! -f "$FILE" ; then echo "mk_cmds: $FILE: File not found" exit 1; fi ${SED} -f "${DIR}/ct_c.sed" "${FILE}" \ | ${AWK} -f "${DIR}/ct_c.awk" "rootname=${ROOT}" "outfile=${TMP}" - if grep "^#__ERROR_IN_FILE" "${TMP}" > /dev/null; then rm "${TMP}" exit 1 else rm -f "${BASE}.c" mv -f "${TMP}" "${BASE}.c" chmod -w "${BASE}.c" exit 0 fi e2fsprogs-1.41.14/lib/ss/execute_cmd.c0000644031104000366760000001403211240667355015467 0ustar tytso/* * Copyright 1987, 1988, 1989 by Massachusetts Institute of Technology * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose is hereby granted, provided that * the names of M.I.T. and the M.I.T. S.I.P.B. not be used in * advertising or publicity pertaining to distribution of the software * without specific, written prior permission. M.I.T. and the * M.I.T. S.I.P.B. make no representations about the suitability of * this software for any purpose. It is provided "as is" without * express or implied warranty. */ #ifdef HAS_STDLIB_H #include #endif #ifdef HAVE_ERRNO_H #include #else extern int errno; #endif #include "ss_internal.h" #include static int check_request_table PROTOTYPE((ss_request_table *rqtbl, int argc, char *argv[], int sci_idx)); static int really_execute_command PROTOTYPE((int sci_idx, int argc, char **argv[])); /* * get_request(tbl, idx) * * Function: * Gets the idx'th request from the request table pointed to * by tbl. * Arguments: * tbl (ss_request_table *) * pointer to request table * idx (int) * index into table * Returns: * (ss_request_entry *) * pointer to request table entry * Notes: * Has been replaced by a macro. */ #ifdef __SABER__ /* sigh. saber won't deal with pointer-to-const-struct */ static struct _ss_request_entry * get_request (tbl, idx) ss_request_table * tbl; int idx; { struct _ss_request_table *tbl1 = (struct _ss_request_table *) tbl; struct _ss_request_entry *e = (struct _ss_request_entry *) tbl1->requests; return e + idx; } #else #define get_request(tbl,idx) ((tbl) -> requests + (idx)) #endif /* * check_request_table(rqtbl, argc, argv, sci_idx) * * Function: * If the command string in argv[0] is in the request table, execute * the commands and return error code 0. Otherwise, return error * code ss_et_command_not_found. * Arguments: * rqtbl (ss_request_table *) * pointer to request table * argc (int) * number of elements in argv[] * argv (char *[]) * argument string array * sci_idx (int) * ss-internal index for subsystem control info structure * Returns: * (int) * zero if command found, ss_et_command_not_found otherwise * Notes: */ static int check_request_table (rqtbl, argc, argv, sci_idx) register ss_request_table *rqtbl; int argc; char *argv[]; int sci_idx; { #ifdef __SABER__ struct _ss_request_entry *request; #else register ss_request_entry *request; #endif register ss_data *info; register char const * const * name; char *string = argv[0]; int i; info = ss_info(sci_idx); info->argc = argc; info->argv = argv; for (i = 0; (request = get_request(rqtbl, i))->command_names; i++) { for (name = request->command_names; *name; name++) if (!strcmp(*name, string)) { info->current_request = request->command_names[0]; (request->function)(argc, (const char *const *) argv, sci_idx,info->info_ptr); info->current_request = (char *)NULL; return(0); } } return(SS_ET_COMMAND_NOT_FOUND); } /* * really_execute_command(sci_idx, argc, argv) * * Function: * Fills in the argc, argv values in the subsystem entry and * call the appropriate routine. * Arguments: * sci_idx (int) * ss-internal index for subsystem control info structure * argc (int) * number of arguments in argument list * argv (char **[]) * pointer to parsed argument list (may be reallocated * on abbrev expansion) * * Returns: * (int) * Zero if successful, ss_et_command_not_found otherwise. * Notes: */ static int really_execute_command (sci_idx, argc, argv) int sci_idx; int argc; char **argv[]; { register ss_request_table **rqtbl; register ss_data *info; info = ss_info(sci_idx); for (rqtbl = info->rqt_tables; *rqtbl; rqtbl++) { if (check_request_table (*rqtbl, argc, *argv, sci_idx) == 0) return(0); } return(SS_ET_COMMAND_NOT_FOUND); } /* * ss_execute_command(sci_idx, argv) * * Function: * Executes a parsed command list within the subsystem. * Arguments: * sci_idx (int) * ss-internal index for subsystem control info structure * argv (char *[]) * parsed argument list * Returns: * (int) * Zero if successful, ss_et_command_not_found otherwise. * Notes: */ int ss_execute_command(sci_idx, argv) int sci_idx; register char *argv[]; { register int i, argc; char **argp; argc = 0; for (argp = argv; *argp; argp++) argc++; argp = (char **)malloc((argc+1)*sizeof(char *)); for (i = 0; i <= argc; i++) argp[i] = argv[i]; i = really_execute_command(sci_idx, argc, &argp); free(argp); return(i); } /* * ss_execute_line(sci_idx, line_ptr) * * Function: * Parses and executes a command line within a subsystem. * Arguments: * sci_idx (int) * ss-internal index for subsystem control info structure * line_ptr (char *) * Pointer to command line to be parsed. * Returns: * (int) * Error code. * Notes: */ int ss_execute_line (sci_idx, line_ptr) int sci_idx; char *line_ptr; { char **argv; int argc, ret; /* flush leading whitespace */ while (line_ptr[0] == ' ' || line_ptr[0] == '\t') line_ptr++; /* check if it should be sent to operating system for execution */ if (*line_ptr == '!') { if (ss_info(sci_idx)->flags.escape_disabled) return SS_ET_ESCAPE_DISABLED; else { line_ptr++; return (system(line_ptr) < 0) ? errno : 0; } } /* parse it */ argv = ss_parse(sci_idx, line_ptr, &argc); if (argc == 0) { free(argv); return 0; } /* look it up in the request tables, execute if found */ ret = really_execute_command (sci_idx, argc, &argv); free(argv); return(ret); } e2fsprogs-1.41.14/lib/ss/ss.pc.in0000644031104000366760000000041711240667355014416 0ustar tytsoprefix=@prefix@ exec_prefix=@exec_prefix@ libdir=@libdir@ includedir=@includedir@ Name: ss Description: Subsystem command parsing library Version: @E2FSPROGS_VERSION@ Requires.private: com_err Cflags: -I${includedir}/ss Libs: -L${libdir} -lss Libs.private: @DLOPEN_LIB@ e2fsprogs-1.41.14/lib/ss/parse.c0000644031104000366760000000654011240667355014321 0ustar tytso/* * Copyright 1987, 1988 by MIT Student Information Processing Board * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose is hereby granted, provided that * the names of M.I.T. and the M.I.T. S.I.P.B. not be used in * advertising or publicity pertaining to distribution of the software * without specific, written prior permission. M.I.T. and the * M.I.T. S.I.P.B. make no representations about the suitability of * this software for any purpose. It is provided "as is" without * express or implied warranty. */ #ifdef HAS_STDLIB_H #include #endif #include #ifdef HAVE_ERRNO_H #include #endif #include "ss_internal.h" enum parse_mode { WHITESPACE, TOKEN, QUOTED_STRING }; /* * parse(line_ptr, argc_ptr) * * Function: * Parses line, dividing at whitespace, into tokens, returns * the "argc" and "argv" values. * Arguments: * line_ptr (char *) * Pointer to text string to be parsed. * argc_ptr (int *) * Where to put the "argc" (number of tokens) value. * Returns: * argv (char **) * Series of pointers to parsed tokens. */ #define NEW_ARGV(old,n) (char **)realloc((char *)old,\ (unsigned)(n+2)*sizeof(char*)) char **ss_parse (sci_idx, line_ptr, argc_ptr) int sci_idx; register char *line_ptr; int *argc_ptr; { register char **argv, *cp; register int argc; register enum parse_mode parse_mode; argv = (char **) malloc (sizeof(char *)); if (argv == (char **)NULL) { ss_error(sci_idx, errno, "Can't allocate storage"); *argc_ptr = 0; return(argv); } *argv = (char *)NULL; argc = 0; parse_mode = WHITESPACE; /* flushing whitespace */ cp = line_ptr; /* cp is for output */ while (1) { #ifdef DEBUG { printf ("character `%c', mode %d\n", *line_ptr, parse_mode); } #endif while (parse_mode == WHITESPACE) { if (*line_ptr == '\0') goto end_of_line; if (*line_ptr == ' ' || *line_ptr == '\t') { line_ptr++; continue; } if (*line_ptr == '"') { /* go to quoted-string mode */ parse_mode = QUOTED_STRING; cp = line_ptr++; argv = NEW_ARGV (argv, argc); argv[argc++] = cp; argv[argc] = NULL; } else { /* random-token mode */ parse_mode = TOKEN; cp = line_ptr; argv = NEW_ARGV (argv, argc); argv[argc++] = line_ptr; argv[argc] = NULL; } } while (parse_mode == TOKEN) { if (*line_ptr == '\0') { *cp++ = '\0'; goto end_of_line; } else if (*line_ptr == ' ' || *line_ptr == '\t') { *cp++ = '\0'; line_ptr++; parse_mode = WHITESPACE; } else if (*line_ptr == '"') { line_ptr++; parse_mode = QUOTED_STRING; } else { *cp++ = *line_ptr++; } } while (parse_mode == QUOTED_STRING) { if (*line_ptr == '\0') { ss_error (sci_idx, 0, "Unbalanced quotes in command line"); free (argv); *argc_ptr = 0; return NULL; } else if (*line_ptr == '"') { if (*++line_ptr == '"') { *cp++ = '"'; line_ptr++; } else { parse_mode = TOKEN; } } else { *cp++ = *line_ptr++; } } } end_of_line: *argc_ptr = argc; #ifdef DEBUG { int i; printf ("argc = %d\n", argc); for (i = 0; i <= argc; i++) printf ("\targv[%2d] = `%s'\n", i, argv[i] ? argv[i] : ""); } #endif return(argv); } e2fsprogs-1.41.14/lib/ss/list_rqs.c0000644031104000366760000000505711240667355015051 0ustar tytso/* * Copyright 1987, 1988 by MIT Student Information Processing Board * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose is hereby granted, provided that * the names of M.I.T. and the M.I.T. S.I.P.B. not be used in * advertising or publicity pertaining to distribution of the software * without specific, written prior permission. M.I.T. and the * M.I.T. S.I.P.B. make no representations about the suitability of * this software for any purpose. It is provided "as is" without * express or implied warranty. */ #include "ss_internal.h" #include #include #include typedef void sigret_t; static char const twentyfive_spaces[26] = " "; static char const NL[2] = "\n"; void ss_list_requests(int argc __SS_ATTR((unused)), const char * const *argv __SS_ATTR((unused)), int sci_idx, void *infop __SS_ATTR((unused))) { ss_request_entry *entry; char const * const *name; int spacing; ss_request_table **table; char buffer[BUFSIZ]; FILE *output; int fd; sigset_t omask, igmask; sigret_t (*func)(int); #ifndef NO_FORK int waitb; #endif sigemptyset(&igmask); sigaddset(&igmask, SIGINT); sigprocmask(SIG_BLOCK, &igmask, &omask); func = signal(SIGINT, SIG_IGN); fd = ss_pager_create(); output = fdopen(fd, "w"); sigprocmask(SIG_SETMASK, &omask, (sigset_t *) 0); fprintf (output, "Available %s requests:\n\n", ss_info (sci_idx) -> subsystem_name); for (table = ss_info(sci_idx)->rqt_tables; *table; table++) { entry = (*table)->requests; for (; entry->command_names; entry++) { spacing = -2; buffer[0] = '\0'; if (entry->flags & SS_OPT_DONT_LIST) continue; for (name = entry->command_names; *name; name++) { int len = strlen(*name); strncat(buffer, *name, len); spacing += len + 2; if (name[1]) { strcat(buffer, ", "); } } if (spacing > 23) { strcat(buffer, NL); fputs(buffer, output); spacing = 0; buffer[0] = '\0'; } strncat(buffer, twentyfive_spaces, 25-spacing); strcat(buffer, entry->info_string); strcat(buffer, NL); fputs(buffer, output); } } fclose(output); #ifndef NO_FORK wait(&waitb); #endif (void) signal(SIGINT, func); } e2fsprogs-1.41.14/lib/ss/error.c0000644031104000366760000000354611240667355014343 0ustar tytso/* * Copyright 1987, 1988, 1989 by MIT Student Information Processing * Board * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose is hereby granted, provided that * the names of M.I.T. and the M.I.T. S.I.P.B. not be used in * advertising or publicity pertaining to distribution of the software * without specific, written prior permission. M.I.T. and the * M.I.T. S.I.P.B. make no representations about the suitability of * this software for any purpose. It is provided "as is" without * express or implied warranty. */ #include #include #include "ss_internal.h" #include char * ss_name(sci_idx) int sci_idx; { register char *ret_val; register ss_data *infop; infop = ss_info(sci_idx); if (infop->current_request == (char const *)NULL) { ret_val = malloc((unsigned) (strlen(infop->subsystem_name)+1) * sizeof(char)); if (ret_val == (char *)NULL) return((char *)NULL); strcpy(ret_val, infop->subsystem_name); return(ret_val); } else { register char *cp; register char const *cp1; ret_val = malloc((unsigned)sizeof(char) * (strlen(infop->subsystem_name)+ strlen(infop->current_request)+ 4)); cp = ret_val; cp1 = infop->subsystem_name; while (*cp1) *cp++ = *cp1++; *cp++ = ' '; *cp++ = '('; cp1 = infop->current_request; while (*cp1) *cp++ = *cp1++; *cp++ = ')'; *cp = '\0'; return(ret_val); } } void ss_error (int sci_idx, long code, const char * fmt, ...) { register char *whoami; va_list pvar; va_start (pvar, fmt); whoami = ss_name (sci_idx); com_err_va (whoami, code, fmt, pvar); free (whoami); va_end(pvar); } void ss_perror (sci_idx, code, msg) /* for compatibility */ int sci_idx; long code; char const *msg; { ss_error (sci_idx, code, "%s", msg); } e2fsprogs-1.41.14/lib/ss/test_cmd.ct0000644031104000366760000000012311240667355015164 0ustar tytsocommand_table test_cmds; request test_cmd, "Test command", test_cmd, test; end; e2fsprogs-1.41.14/lib/ss/mk_cmds.10000644031104000366760000000213011240667355014531 0ustar tytso.\" Copyright (c) 2003 Theodore Ts'o .\" .TH MK_CMDS 1 "2003" E2FSPROGS .SH NAME mk_cmds \- error table compiler .SH SYNOPSIS .B mk_cmds file .SH DESCRIPTION .B Mk_cmds converts a table listing command names and associated help messages into a C source file suitable for use with the .IR ss (3) library. The source file name must end with a suffix of ``.ct''; the file consists of a declaration supplying the name of the command table: .B command_table .I name followed by entries of the form: [ .B request | .B unimplemented ] .I name, " .I string "[, abbrev]...; and a final .B end to indicate the end of the table. A C source file is generated which should be compiled and linked with the object files use the ss library. A ``#'' in the source file is treated as a comment character, and all remaining text to the end of the source line will be ignored. .SH BUGS Since the original .B mk_cmds uses a very simple parser based on .IR yacc (1), and this current version of .B mk_cmds uses an awk/sed combination of scripts, its error recovery leaves much to be desired. .SH "SEE ALSO" ss (3) e2fsprogs-1.41.14/lib/ss/ss_internal.h0000644031104000366760000000641311240667355015534 0ustar tytso/* * Copyright 1987, 1988 by MIT Student Information Processing Board * * Permission to use, copy, modify, and distribute this software and * its documentation for any purpose is hereby granted, provided that * the names of M.I.T. and the M.I.T. S.I.P.B. not be used in * advertising or publicity pertaining to distribution of the software * without specific, written prior permission. M.I.T. and the * M.I.T. S.I.P.B. make no representations about the suitability of * this software for any purpose. It is provided "as is" without * express or implied warranty. */ #ifndef _ss_ss_internal_h #define _ss_ss_internal_h __FILE__ #include #include #include #define PROTOTYPE(p) p typedef void * pointer; #include "ss.h" typedef char BOOL; typedef struct _ss_abbrev_entry { char *name; /* abbrev name */ char **abbrev; /* new tokens to insert */ unsigned int beginning_of_line : 1; } ss_abbrev_entry; typedef struct _ss_abbrev_list { int n_abbrevs; ss_abbrev_entry *first_abbrev; } ss_abbrev_list; typedef struct { /* char *path; */ ss_abbrev_list abbrevs[127]; } ss_abbrev_info; typedef struct _ss_data { /* init values */ /* this subsystem */ const char *subsystem_name; const char *subsystem_version; /* current request info */ int argc; char **argv; /* arg list */ char const *current_request; /* primary name */ /* info directory for 'help' */ char **info_dirs; /* to be extracted by subroutines */ pointer info_ptr; /* (void *) NULL */ /* for ss_listen processing */ char *prompt; ss_request_table **rqt_tables; ss_abbrev_info *abbrev_info; struct { unsigned int escape_disabled : 1, abbrevs_disabled : 1; } flags; /* * Dynamic usage of readline library if present */ void *readline_handle; void (*readline_shutdown)(struct _ss_data *info); char *(*readline)(const char *); void (*add_history)(const char *); void (*redisplay)(void); char **(*rl_completion_matches)(const char *, char *(*completer)(const char *, int)); /* to get out */ int abort; /* exit subsystem */ int exit_status; } ss_data; #define CURRENT_SS_VERSION 1 #define ss_info(sci_idx) (_ss_table[sci_idx]) #define ss_current_request(sci_idx,code_ptr) \ (*code_ptr=0,ss_info(sci_idx)->current_request) void ss_add_info_dir (int sci_idx, char *info_dir, int *code_ptr); void ss_delete_info_dir (int sci_idx, char *info_dir, int *code_ptr); int ss_execute_line(int sci_idx, char *line_ptr); char **ss_parse(int sci_idx, char *line_ptr, int *argc_ptr); ss_abbrev_info *ss_abbrev_initialize(char *, int *); void ss_page_stdin(void); void ss_list_requests(int, char const * const *, int, pointer); int ss_execute_command(int sci_idx, char *argv[]); int ss_pager_create(void); char *ss_safe_getenv(const char *arg); char **ss_rl_completion(const char *text, int start, int end); extern ss_data **_ss_table; extern char *ss_et_msgs[]; #ifdef USE_SIGPROCMASK /* fake sigmask, sigblock, sigsetmask */ #include #define sigmask(x) (1L<<(x)-1) #define sigsetmask(x) sigprocmask(SIG_SETMASK,&x,NULL) static int _fake_sigstore; #define sigblock(x) (_fake_sigstore=x,sigprocmask(SIG_BLOCK,&_fake_sigstore,0)) #endif #endif /* _ss_internal_h */ e2fsprogs-1.41.14/lib/ss/ct_c.sed0000644031104000366760000000436111240667355014447 0ustar tytso# # This script parses a command_table file into something which is a bit # easier for an awk script to understand. # # Input syntax: a .ct file # # Output syntax: # (for the command_table line) # command_table # #(for each request definition) # BOR # sub: # hlp: # cmd: # opt: