[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-devel] [PATCH 1/2] introduce libfsimage for reading filesystem images
# HG changeset patch # User john.levon@xxxxxxx # Date 1162945967 28800 # Node ID f93406ba0c73b3193e216fec7fcd876ef0f081f0 # Parent d27d399bb3882320345e403cfb221dcf7681146b Add libfsimage, a C library for reading files from filesystem images. Initial support is provided for Solaris UFS, ext2 (both using libext2fs and not), and reiserfs. Signed-off-by: John Levon <john.levon@xxxxxxx> diff --git a/tools/Makefile b/tools/Makefile --- a/tools/Makefile +++ b/tools/Makefile @@ -18,6 +18,7 @@ SUBDIRS-y += xenstat SUBDIRS-y += xenstat SUBDIRS-$(CONFIG_Linux) += libaio SUBDIRS-$(CONFIG_Linux) += blktap +SUBDIRS-y += libfsimage # These don't cross-compile ifeq ($(XEN_COMPILE_ARCH),$(XEN_TARGET_ARCH)) diff --git a/tools/libfsimage/Makefile b/tools/libfsimage/Makefile new file mode 100644 --- /dev/null +++ b/tools/libfsimage/Makefile @@ -0,0 +1,13 @@ +XEN_ROOT = ../.. +include $(XEN_ROOT)/tools/Rules.mk + +SUBDIRS-y = common ufs reiserfs +SUBDIRS-y += $(shell ./check-libext2fs) + +.PHONY: all +all install clean: + @set -e; for subdir in $(SUBDIRS-y); do \ + $(MAKE) -C $$subdir $@; \ + done + +distclean: clean diff --git a/tools/libfsimage/Rules.mk b/tools/libfsimage/Rules.mk new file mode 100644 --- /dev/null +++ b/tools/libfsimage/Rules.mk @@ -0,0 +1,32 @@ +include $(XEN_ROOT)/tools/Rules.mk + +DEPS = .*.d + +CFLAGS += -I$(XEN_ROOT)/tools/libfsimage/common/ -Werror -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -Wp,-MD,.$(@F).d +LDFLAGS += -L../common/ + +PIC_OBJS := $(patsubst %.c,%.opic,$(LIB_SRCS-y)) + +FSDIR-$(CONFIG_Linux) = $(LIBDIR)/fs/$(FS) +FSDIR-$(CONFIG_SunOS)-x86_64 = lib/fs/$(FS)/64 +FSDIR-$(CONFIG_SunOS)-x86_32 = lib/fs/$(FS)/ +FSDIR-$(CONFIG_SunOS) = $(FSDIR-$(CONFIG_SunOS)-$(XEN_TARGET_ARCH)) +FSDIR = $(FSDIR-y) + +FSLIB = fsimage.so + +.PHONY: fs-all +fs-all: $(FSLIB) + +.PHONY: fs-install +fs-install: fs-all + $(INSTALL_DIR) $(DESTDIR)/usr/$(FSDIR) + $(INSTALL_PROG) $(FSLIB) $(DESTDIR)/usr/$(FSDIR) + +$(FSLIB): $(PIC_OBJS) + $(CC) $(CFLAGS) $(LDFLAGS) $(SHLIB_CFLAGS) -o $@ $^ -lfsimage $(FS_LIBDEPS) + +clean distclean: + rm -f $(PIC_OBJS) $(FSLIB) + +-include $(DEPS) diff --git a/tools/libfsimage/check-libext2fs b/tools/libfsimage/check-libext2fs new file mode 100755 --- /dev/null +++ b/tools/libfsimage/check-libext2fs @@ -0,0 +1,21 @@ +#!/bin/bash + +cat >ext2-test.c <<EOF +#include <ext2fs/ext2fs.h> + +int main() +{ + ext2fs_open2; +} +EOF + +gcc -o ext2-test ext2-test.c -lext2fs >/dev/null 2>&1 +if [ $? = 0 ]; then + echo ext2fs-lib +else + echo ext2fs +fi + +rm -f ext2-test ext2-test.c + +exit 0 diff --git a/tools/libfsimage/common/Makefile b/tools/libfsimage/common/Makefile new file mode 100644 --- /dev/null +++ b/tools/libfsimage/common/Makefile @@ -0,0 +1,46 @@ +XEN_ROOT = ../../.. +include $(XEN_ROOT)/tools/Rules.mk + +MAJOR = 1.0 +MINOR = 0 + +CFLAGS += -Werror -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -Wp,-MD,.$(@F).d +DEPS = .*.d + +LDFLAGS-$(CONFIG_SunOS) = -Wl,-M -Wl,mapfile-SunOS +LDFLAGS-$(CONFIG_Linux) = -Wl,mapfile-GNU +LDFLAGS = $(LDFLAGS-y) + +LIB_SRCS-y = fsimage.c fsimage_plugin.c fsimage_grub.c + +PIC_OBJS := $(patsubst %.c,%.opic,$(LIB_SRCS-y)) + +LIB = libfsimage.so libfsimage.so.$(MAJOR) libfsimage.so.$(MAJOR).$(MINOR) + +.PHONY: all +all: $(LIB) + +.PHONY: install +install: all + [ -d $(DESTDIR)/usr/$(LIBDIR) ] || $(INSTALL_DIR) $(DESTDIR)/usr/$(LIBDIR) + [ -d $(DESTDIR)/usr/include ] || $(INSTALL_DIR) $(DESTDIR)/usr/include + $(INSTALL_PROG) libfsimage.so.$(MAJOR).$(MINOR) $(DESTDIR)/usr/$(LIBDIR) + ln -sf libfsimage.so.$(MAJOR).$(MINOR) $(DESTDIR)/usr/$(LIBDIR)/libfsimage.so.$(MAJOR) + ln -sf libfsimage.so.$(MAJOR) $(DESTDIR)/usr/$(LIBDIR)/libfsimage.so + $(INSTALL_DATA) fsimage.h $(DESTDIR)/usr/include + $(INSTALL_DATA) fsimage_plugin.h $(DESTDIR)/usr/include + $(INSTALL_DATA) fsimage_grub.h $(DESTDIR)/usr/include + +clean distclean: + rm -f $(PIC_OBJS) $(LIB) + +libfsimage.so: libfsimage.so.$(MAJOR) + ln -sf $< $@ +libfsimage.so.$(MAJOR): libfsimage.so.$(MAJOR).$(MINOR) + ln -sf $< $@ + +libfsimage.so.$(MAJOR).$(MINOR): $(PIC_OBJS) + $(CC) $(CFLAGS) $(LDFLAGS) -Wl,$(SONAME_LDFLAG) -Wl,libfsimage.so.$(MAJOR) $(SHLIB_CFLAGS) -o $@ $^ -lpthread + +-include $(DEPS) + diff --git a/tools/libfsimage/common/fsimage.c b/tools/libfsimage/common/fsimage.c new file mode 100644 --- /dev/null +++ b/tools/libfsimage/common/fsimage.c @@ -0,0 +1,140 @@ +/* + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include <sys/stat.h> +#include <sys/types.h> +#include <strings.h> +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <fcntl.h> +#include <errno.h> +#include <pthread.h> + +#include "fsimage_plugin.h" +#include "fsimage_priv.h" + +static pthread_mutex_t fsi_lock = PTHREAD_MUTEX_INITIALIZER; + +fsi_t *fsi_open_fsimage(const char *path, uint64_t off) +{ + fsi_t *fsi = NULL; + int fd; + int err; + + if ((fd = open(path, O_RDONLY)) == -1) + goto fail; + + if ((fsi = malloc(sizeof(*fsi))) == NULL) + goto fail; + + fsi->f_fd = fd; + fsi->f_off = off; + fsi->f_data = NULL; + + pthread_mutex_lock(&fsi_lock); + err = find_plugin(fsi, path); + pthread_mutex_unlock(&fsi_lock); + if (err != 0) + goto fail; + + return (fsi); + +fail: + err = errno; + if (fd != -1) + (void) close(fd); + free(fsi); + errno = err; + return (NULL); +} + +void fsi_close_fsimage(fsi_t *fsi) +{ + pthread_mutex_lock(&fsi_lock); + fsip_fs_free(fsi); + pthread_mutex_unlock(&fsi_lock); +} + +int fsi_file_exists(fsi_t *fsi, const char *path) +{ + fsi_file_t *ffi; + + if ((ffi = fsi_open_file(fsi, path)) == NULL) + return (0); + + fsi_close_file(ffi); + return (1); +} + +fsi_file_t *fsi_open_file(fsi_t *fsi, const char *path) +{ + fsi_plugin_ops_t *ops; + fsi_file_t *ffi; + + pthread_mutex_lock(&fsi_lock); + ops = fsi->f_plugin->fp_ops; + ffi = ops->fpo_open(fsi, path); + pthread_mutex_unlock(&fsi_lock); + + return (ffi); +} + +int fsi_close_file(fsi_file_t *ffi) +{ + fsi_plugin_ops_t *ops; + int err; + + pthread_mutex_lock(&fsi_lock); + ops = ffi->ff_fsi->f_plugin->fp_ops; + err = ops->fpo_close(ffi); + pthread_mutex_unlock(&fsi_lock); + + return (err); +} + +ssize_t fsi_read_file(fsi_file_t *ffi, void *buf, size_t nbytes) +{ + fsi_plugin_ops_t *ops; + ssize_t ret; + + pthread_mutex_lock(&fsi_lock); + ops = ffi->ff_fsi->f_plugin->fp_ops; + ret = ops->fpo_read(ffi, buf, nbytes); + pthread_mutex_unlock(&fsi_lock); + + return (ret); +} + +ssize_t fsi_pread_file(fsi_file_t *ffi, void *buf, size_t nbytes, uint64_t off) +{ + fsi_plugin_ops_t *ops; + ssize_t ret; + + pthread_mutex_lock(&fsi_lock); + ops = ffi->ff_fsi->f_plugin->fp_ops; + ret = ops->fpo_pread(ffi, buf, nbytes, off); + pthread_mutex_unlock(&fsi_lock); + + return (ret); +} diff --git a/tools/libfsimage/common/fsimage.h b/tools/libfsimage/common/fsimage.h new file mode 100644 --- /dev/null +++ b/tools/libfsimage/common/fsimage.h @@ -0,0 +1,52 @@ +/* + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _FSIMAGE_H +#define _FSIMAGE_H + +#ifdef __cplusplus +extern C { +#endif + +#include <sys/types.h> +#include <stdint.h> +#include <unistd.h> + +typedef struct fsi fsi_t; +typedef struct fsi_file fsi_file_t; + +fsi_t *fsi_open_fsimage(const char *, uint64_t); +void fsi_close_fsimage(fsi_t *); + +int fsi_file_exists(fsi_t *, const char *); +fsi_file_t *fsi_open_file(fsi_t *, const char *); +int fsi_close_file(fsi_file_t *); + +ssize_t fsi_read_file(fsi_file_t *, void *, size_t); +ssize_t fsi_pread_file(fsi_file_t *, void *, size_t, uint64_t); + +#ifdef __cplusplus +}; +#endif + +#endif /* _FSIMAGE_H */ diff --git a/tools/libfsimage/common/fsimage_grub.c b/tools/libfsimage/common/fsimage_grub.c new file mode 100644 --- /dev/null +++ b/tools/libfsimage/common/fsimage_grub.c @@ -0,0 +1,277 @@ +/* + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef __sun__ +#define _XOPEN_SOURCE 500 +#endif +#include <stdlib.h> +#include <strings.h> +#include <errno.h> + +#include "fsimage_grub.h" +#include "fsimage_priv.h" + +static char *disk_read_junk; + +typedef struct fsig_data { + char fd_buf[FSYS_BUFLEN]; +} fsig_data_t; + +typedef struct fsig_file_data { + char ffd_buf[FSYS_BUFLEN]; + uint64_t ffd_curpos; + uint64_t ffd_filepos; + uint64_t ffd_filemax; + int ffd_int1; + int ffd_int2; + int ffd_errnum; +} fsig_file_data_t; + +fsi_file_t * +fsig_file_alloc(fsi_t *fsi) +{ + fsi_file_t *ffi; + fsig_file_data_t *data = malloc(sizeof (fsig_file_data_t)); + + if (data == NULL) + return (NULL); + + bzero(data, sizeof (fsig_file_data_t)); + bcopy(fsig_fs_buf(fsi), data->ffd_buf, FSYS_BUFLEN); + + if ((ffi = fsip_file_alloc(fsi, data)) == NULL) { + free(data); + return (NULL); + } + + return (ffi); +} + +void * +fsig_fs_buf(fsi_t *fsi) +{ + fsig_data_t *data = fsip_fs_data(fsi); + return ((void *)data->fd_buf); +} + +void * +fsig_file_buf(fsi_file_t *ffi) +{ + fsig_file_data_t *data = fsip_file_data(ffi); + return ((void *)data->ffd_buf); +} + +uint64_t * +fsig_filepos(fsi_file_t *ffi) +{ + fsig_file_data_t *data = fsip_file_data(ffi); + return (&data->ffd_filepos); +} + +uint64_t * +fsig_filemax(fsi_file_t *ffi) +{ + fsig_file_data_t *data = fsip_file_data(ffi); + return (&data->ffd_filemax); +} + +int * +fsig_int1(fsi_file_t *ffi) +{ + fsig_file_data_t *data = fsip_file_data(ffi); + return (&data->ffd_int1); +} + +int * +fsig_int2(fsi_file_t *ffi) +{ + fsig_file_data_t *data = fsip_file_data(ffi); + return (&data->ffd_int2); +} + +int * +fsig_errnum(fsi_file_t *ffi) +{ + fsig_file_data_t *data = fsip_file_data(ffi); + return (&data->ffd_errnum); +} + +char ** +fsig_disk_read_junk(void) +{ + return (&disk_read_junk); +} + +int +fsig_devread(fsi_file_t *ffi, unsigned int sector, unsigned int offset, + unsigned int bufsize, char *buf) +{ + uint64_t off = ffi->ff_fsi->f_off + ((uint64_t)(sector * 512)) + offset; + ssize_t bytes_read = 0; + + while (bufsize) { + ssize_t ret = pread(ffi->ff_fsi->f_fd, buf + bytes_read, + bufsize, (off_t)off); + if (ret == -1) + return (0); + if (ret == 0) + return (0); + + bytes_read += ret; + bufsize -= ret; + } + + return (1); +} + +int +fsig_substring(const char *s1, const char *s2) +{ + while (*s1 == *s2) { + if (*s1 == '\0') + return (0); + s1++; + s2++; + } + + if (*s1 == '\0') + return (-1); + + return (1); +} + +static int +fsig_mount(fsi_t *fsi, const char *path) +{ + fsig_plugin_ops_t *ops = fsi->f_plugin->fp_data; + fsi_file_t *ffi; + fsi->f_data = malloc(sizeof (fsig_data_t)); + + if (fsi->f_data == NULL) + return (-1); + + if ((ffi = fsig_file_alloc(fsi)) == NULL) { + free(fsi->f_data); + fsi->f_data = NULL; + return (-1); + } + + bzero(fsi->f_data, sizeof (fsig_data_t)); + + if (!ops->fpo_mount(ffi)) { + fsip_file_free(ffi); + free(fsi->f_data); + fsi->f_data = NULL; + return (-1); + } + + bcopy(fsig_file_buf(ffi), fsig_fs_buf(fsi), FSYS_BUFLEN); + fsip_file_free(ffi); + return (0); +} + +static int +fsig_umount(fsi_t *fsi) +{ + fsip_fs_free(fsi); + return (0); +} + +static fsi_file_t * +fsig_open(fsi_t *fsi, const char *name) +{ + fsig_plugin_ops_t *ops = fsi->f_plugin->fp_data; + char *path = strdup(name); + fsi_file_t *ffi = NULL; + + if (path == NULL || (ffi = fsig_file_alloc(fsi)) == NULL) + goto out; + + if (ops->fpo_dir(ffi, path) == 0) { + fsip_file_free(ffi); + ffi = NULL; + errno = ENOENT; + } + +out: + free(path); + return (ffi); +} + +static ssize_t +fsig_pread(fsi_file_t *ffi, void *buf, size_t nbytes, uint64_t off) +{ + fsig_plugin_ops_t *ops = ffi->ff_fsi->f_plugin->fp_data; + fsig_file_data_t *data = fsip_file_data(ffi); + + data->ffd_filepos = off; + + if (data->ffd_filepos >= data->ffd_filemax) + return (0); + + /* FIXME: check */ + if (data->ffd_filepos + nbytes > data->ffd_filemax) + nbytes = data->ffd_filemax - data->ffd_filepos; + + errnum = 0; + return (ops->fpo_read(ffi, buf, nbytes)); +} + +static ssize_t +fsig_read(fsi_file_t *ffi, void *buf, size_t nbytes) +{ + fsig_file_data_t *data = fsip_file_data(ffi); + ssize_t ret; + + ret = fsig_pread(ffi, buf, nbytes, data->ffd_curpos); + data->ffd_curpos = data->ffd_filepos; + return (ret); +} + +static int +fsig_close(fsi_file_t *ffi) +{ + fsip_file_free(ffi); + return (0); +} + +static fsi_plugin_ops_t fsig_grub_ops = { + .fpo_version = FSIMAGE_PLUGIN_VERSION, + .fpo_mount = fsig_mount, + .fpo_umount = fsig_umount, + .fpo_open = fsig_open, + .fpo_read = fsig_read, + .fpo_pread = fsig_pread, + .fpo_close = fsig_close +}; + +fsi_plugin_ops_t * +fsig_init(fsi_plugin_t *plugin, fsig_plugin_ops_t *ops) +{ + if (ops->fpo_version > FSIMAGE_PLUGIN_VERSION) + return (NULL); + + plugin->fp_data = ops; + + return (&fsig_grub_ops); +} diff --git a/tools/libfsimage/common/fsimage_grub.h b/tools/libfsimage/common/fsimage_grub.h new file mode 100644 --- /dev/null +++ b/tools/libfsimage/common/fsimage_grub.h @@ -0,0 +1,92 @@ +/* + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _FSIMAGE_GRUB_H +#define _FSIMAGE_GRUB_H + +#ifdef __cplusplus +extern C { +#endif + +#include <sys/types.h> +#include <string.h> +#include <stdlib.h> +#include <ctype.h> + +#include "fsimage.h" +#include "fsimage_plugin.h" + +typedef struct fsig_plugin_ops { + int fpo_version; + int (*fpo_mount)(fsi_file_t *); + int (*fpo_dir)(fsi_file_t *, char *); + int (*fpo_read)(fsi_file_t *, char *, int); +} fsig_plugin_ops_t; + +#define STAGE1_5 +#define FSYS_BUFLEN 0x8000 +#define SECTOR_BITS 9 +#define SECTOR_SIZE 0x200 + +#define FSYS_BUF (fsig_file_buf(ffi)) +#define filepos (*fsig_filepos(ffi)) +#define filemax (*fsig_filemax(ffi)) +#define devread fsig_devread +#define substring fsig_substring +#define errnum (*fsig_errnum(ffi)) +#define disk_read_func (*fsig_disk_read_junk()) +#define disk_read_hook (*fsig_disk_read_junk()) +#define print_possibilities 0 + +#define grub_memset memset +#define grub_memmove memmove + +extern char **fsig_disk_read_junk(void); + +#define ERR_FSYS_CORRUPT 1 +#define ERR_SYMLINK_LOOP 1 +#define ERR_FILELENGTH 1 +#define ERR_BAD_FILETYPE 1 +#define ERR_BAD_FILETYPE 1 +#define ERR_FILE_NOT_FOUND 1 + +fsi_plugin_ops_t *fsig_init(fsi_plugin_t *, fsig_plugin_ops_t *); + +int fsig_devread(fsi_file_t *, unsigned int, unsigned int, unsigned int, char *); +int fsig_substring(const char *, const char *); + +void *fsig_fs_buf(fsi_t *); + +fsi_file_t *fsig_file_alloc(fsi_t *); +void *fsig_file_buf(fsi_file_t *); +uint64_t *fsig_filepos(fsi_file_t *); +uint64_t *fsig_filemax(fsi_file_t *); +int *fsig_int1(fsi_file_t *); +int *fsig_int2(fsi_file_t *); +int *fsig_errnum(fsi_file_t *); + +#ifdef __cplusplus +}; +#endif + +#endif /* _FSIMAGE_GRUB_H */ diff --git a/tools/libfsimage/common/fsimage_plugin.c b/tools/libfsimage/common/fsimage_plugin.c new file mode 100644 --- /dev/null +++ b/tools/libfsimage/common/fsimage_plugin.c @@ -0,0 +1,214 @@ +/* + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <strings.h> +#include <string.h> +#include <dirent.h> +#include <dlfcn.h> +#include <errno.h> + +#include "fsimage_plugin.h" +#include "fsimage_priv.h" + +static fsi_plugin_t *plugins; + +void +fsip_fs_set_data(fsi_t *fsi, void *data) +{ + fsi->f_data = data; +} + +void +fsip_fs_free(fsi_t *fsi) +{ + free(fsi->f_data); + free(fsi); +} + +fsi_file_t * +fsip_file_alloc(fsi_t *fsi, void *data) +{ + fsi_file_t *ffi = malloc(sizeof (fsi_file_t)); + if (ffi == NULL) + return (NULL); + + bzero(ffi, sizeof (fsi_file_t)); + + ffi->ff_fsi = fsi; + ffi->ff_data = data; + return (ffi); +} + +void +fsip_file_free(fsi_file_t *ffi) +{ + free(ffi->ff_data); + free(ffi); +} + +fsi_t * +fsip_fs(fsi_file_t *ffi) +{ + return (ffi->ff_fsi); +} + +uint64_t +fsip_fs_offset(fsi_t *fsi) +{ + return (fsi->f_off); +} + +void * +fsip_fs_data(fsi_t *fsi) +{ + return (fsi->f_data); +} + +void * +fsip_file_data(fsi_file_t *ffi) +{ + return (ffi->ff_data); +} + +static int init_plugin(const char *lib) +{ + fsi_plugin_init_t init; + fsi_plugin_t *fp = malloc(sizeof (fsi_plugin_t)); + + if (fp == NULL) + return (-1); + + bzero(fp, sizeof (fsi_plugin_t)); + + if ((fp->fp_dlh = dlopen(lib, RTLD_LAZY | RTLD_LOCAL)) == NULL) { + free(fp); + return (0); + } + + init = dlsym(fp->fp_dlh, "fsi_init_plugin"); + + if (init == NULL) + goto fail; + + fp->fp_ops = init(FSIMAGE_PLUGIN_VERSION, fp, &fp->fp_name); + if (fp->fp_ops == NULL || + fp->fp_ops->fpo_version > FSIMAGE_PLUGIN_VERSION) + goto fail; + + fp->fp_next = plugins; + plugins = fp; + + return (0); +fail: + (void) dlclose(fp->fp_dlh); + free(fp); + return (-1); +} + +static int load_plugins(void) +{ + const char *fsdir = getenv("FSIMAGE_FSDIR"); + const char *isadir = ""; + struct dirent *dp = NULL; + struct dirent *dpp; + DIR *dir = NULL; + char *tmp = NULL; + size_t name_max; + int err; + int ret = -1; + +#ifdef __sun__ + if (fsdir == NULL) + fsdir = "/usr/lib/fs"; + + if (sizeof(void *) == 8) + isadir = "64/"; +#else + if (fsdir == NULL) { + if (sizeof(void *) == 8) + fsdir = "/usr/lib64/fs"; + else + fsdir = "/usr/lib/fs"; + } +#endif + + if ((name_max = pathconf(fsdir, _PC_NAME_MAX)) == -1) + goto fail; + + if ((tmp = malloc(name_max + 1)) == NULL) + goto fail; + + if ((dp = malloc(sizeof (struct dirent) + name_max + 1)) == NULL) + goto fail; + + if ((dir = opendir(fsdir)) == NULL) + goto fail; + + bzero(dp, sizeof (struct dirent) + name_max + 1); + + while (readdir_r(dir, dp, &dpp) == 0 && dpp != NULL) { + if (strcmp(dpp->d_name, ".") == 0) + continue; + if (strcmp(dpp->d_name, "..") == 0) + continue; + + (void) snprintf(tmp, name_max, "%s/%s/%sfsimage.so", fsdir, + dpp->d_name, isadir); + + if (init_plugin(tmp) != 0) + goto fail; + } + + ret = 0; + +fail: + err = errno; + if (dir != NULL) + (void) closedir(dir); + free(tmp); + free(dp); + errno = err; + return (ret); +} + +int find_plugin(fsi_t *fsi, const char *path) +{ + fsi_plugin_t *fp; + int ret = 0; + + if (plugins == NULL && (ret = load_plugins()) != 0) + goto out; + + for (fp = plugins; fp != NULL; fp = fp->fp_next) { + fsi->f_plugin = fp; + if (fp->fp_ops->fpo_mount(fsi, path) == 0) + goto out; + } + + ret = -1; + errno = ENOTSUP; +out: + return (ret); +} diff --git a/tools/libfsimage/common/fsimage_plugin.h b/tools/libfsimage/common/fsimage_plugin.h new file mode 100644 --- /dev/null +++ b/tools/libfsimage/common/fsimage_plugin.h @@ -0,0 +1,65 @@ +/* + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _FSIMAGE_PLUGIN_H +#define _FSIMAGE_PLUGIN_H + +#ifdef __cplusplus +extern C { +#endif + +#include <sys/types.h> + +#include "fsimage.h" + +#define FSIMAGE_PLUGIN_VERSION 1 + +typedef struct fsi_plugin fsi_plugin_t; + +typedef struct fsi_plugin_ops { + int fpo_version; + int (*fpo_mount)(fsi_t *, const char *); + int (*fpo_umount)(fsi_t *); + fsi_file_t *(*fpo_open)(fsi_t *, const char *); + ssize_t (*fpo_read)(fsi_file_t *, void *, size_t); + ssize_t (*fpo_pread)(fsi_file_t *, void *, size_t, uint64_t); + int (*fpo_close)(fsi_file_t *); +} fsi_plugin_ops_t; + +typedef fsi_plugin_ops_t * + (*fsi_plugin_init_t)(int, fsi_plugin_t *, const char **); + +void fsip_fs_set_data(fsi_t *, void *); +void fsip_fs_free(fsi_t *); +fsi_file_t *fsip_file_alloc(fsi_t *, void *); +void fsip_file_free(fsi_file_t *); +fsi_t * fsip_fs(fsi_file_t *ffi); +uint64_t fsip_fs_offset(fsi_t *fsi); +void *fsip_fs_data(fsi_t *); +void *fsip_file_data(fsi_file_t *); + +#ifdef __cplusplus +}; +#endif + +#endif /* _FSIMAGE_PLUGIN_H */ diff --git a/tools/libfsimage/common/fsimage_priv.h b/tools/libfsimage/common/fsimage_priv.h new file mode 100644 --- /dev/null +++ b/tools/libfsimage/common/fsimage_priv.h @@ -0,0 +1,62 @@ +/* + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _FSIMAGE_PRIV_H +#define _FSIMAGE_PRIV_H + +#ifdef __cplusplus +extern C { +#endif + +#include <sys/types.h> + +#include "fsimage.h" +#include "fsimage_plugin.h" + +struct fsi_plugin { + const char *fp_name; + void *fp_dlh; + fsi_plugin_ops_t *fp_ops; + struct fsi_plugin *fp_next; + void *fp_data; +}; + +struct fsi { + int f_fd; + uint64_t f_off; + void *f_data; + fsi_plugin_t *f_plugin; +}; + +struct fsi_file { + fsi_t *ff_fsi; + void *ff_data; +}; + +int find_plugin(fsi_t *, const char *); + +#ifdef __cplusplus +}; +#endif + +#endif /* _FSIMAGE_PRIV_H */ diff --git a/tools/libfsimage/common/mapfile-GNU b/tools/libfsimage/common/mapfile-GNU new file mode 100644 --- /dev/null +++ b/tools/libfsimage/common/mapfile-GNU @@ -0,0 +1,37 @@ +VERSION { + libfsimage.so.1.1 { + global: + fsi_open_fsimage; + fsi_close_fsimage; + fsi_file_exists; + fsi_open_file; + fsi_close_file; + fsi_read_file; + fsi_pread_file; + + fsip_fs_set_data; + fsip_fs_free; + fsip_file_alloc; + fsip_file_free; + fsip_fs; + fsip_fs_offset; + fsip_fs_data; + fsip_file_data; + + fsig_init; + fsig_devread; + fsig_substring; + fsig_fs_buf; + fsig_file_alloc; + fsig_file_buf; + fsig_filepos; + fsig_filemax; + fsig_int1; + fsig_int2; + fsig_errnum; + fsig_disk_read_junk; + + local: + *; + }; +} diff --git a/tools/libfsimage/common/mapfile-SunOS b/tools/libfsimage/common/mapfile-SunOS new file mode 100644 --- /dev/null +++ b/tools/libfsimage/common/mapfile-SunOS @@ -0,0 +1,35 @@ +libfsimage.so.1.1 { + global: + fsi_open_fsimage; + fsi_close_fsimage; + fsi_file_exists; + fsi_open_file; + fsi_close_file; + fsi_read_file; + fsi_pread_file; + + fsip_fs_set_data; + fsip_fs_free; + fsip_file_alloc; + fsip_file_free; + fsip_fs; + fsip_fs_data; + fsip_fs_offset; + fsip_file_data; + + fsig_init; + fsig_devread; + fsig_substring; + fsig_fs_buf; + fsig_file_alloc; + fsig_file_buf; + fsig_filepos; + fsig_filemax; + fsig_int1; + fsig_int2; + fsig_errnum; + fsig_disk_read_junk; + + local: + *; +}; diff --git a/tools/libfsimage/ext2fs-lib/Makefile b/tools/libfsimage/ext2fs-lib/Makefile new file mode 100644 --- /dev/null +++ b/tools/libfsimage/ext2fs-lib/Makefile @@ -0,0 +1,15 @@ +XEN_ROOT = ../../.. + +LIB_SRCS-y = ext2fs-lib.c + +FS = ext2fs-lib + +FS_LIBDEPS = -lext2fs + +.PHONY: all +all: fs-all + +.PHONY: install +install: fs-install + +include $(XEN_ROOT)/tools/libfsimage/Rules.mk diff --git a/tools/libfsimage/ext2fs-lib/ext2fs-lib.c b/tools/libfsimage/ext2fs-lib/ext2fs-lib.c new file mode 100644 --- /dev/null +++ b/tools/libfsimage/ext2fs-lib/ext2fs-lib.c @@ -0,0 +1,171 @@ +/* + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include <fsimage_plugin.h> +#include <ext2fs/ext2fs.h> +#include <errno.h> + +static int +ext2lib_mount(fsi_t *fsi, const char *name) +{ + int err; + char opts[30] = ""; + ext2_filsys *fs; + uint64_t offset = fsip_fs_offset(fsi); + + if (offset) + snprintf(opts, 29, "offset=%lld", offset); + + fs = malloc(sizeof (*fs)); + if (fs == NULL) + return (-1); + + err = ext2fs_open2(name, opts, 0, 0, 0, unix_io_manager, fs); + + if (err != 0) { + free(fs); + errno = EINVAL; + return (-1); + } + + fsip_fs_set_data(fsi, fs); + return (0); +} + +static int +ext2lib_umount(fsi_t *fsi) +{ + ext2_filsys *fs = fsip_fs_data(fsi); + if (ext2fs_close(*fs) != 0) { + errno = EINVAL; + return (-1); + } + return (0); +} + +fsi_file_t * +ext2lib_open(fsi_t *fsi, const char *path) +{ + ext2_ino_t ino; + ext2_filsys *fs = fsip_fs_data(fsi); + ext2_file_t *f; + fsi_file_t *file; + int err; + + err = ext2fs_namei_follow(*fs, EXT2_ROOT_INO, EXT2_ROOT_INO, + path, &ino); + + if (err != 0) { + errno = ENOENT; + return (NULL); + } + + f = malloc(sizeof (*f)); + if (f == NULL) + return (NULL); + + err = ext2fs_file_open(*fs, ino, 0, f); + + if (err != 0) { + free(f); + errno = EINVAL; + return (NULL); + } + + file = fsip_file_alloc(fsi, f); + if (file == NULL) + free(f); + return (file); +} + +ssize_t +ext2lib_read(fsi_file_t *file, void *buf, size_t nbytes) +{ + ext2_file_t *f = fsip_file_data(file); + unsigned int n; + int err; + + err = ext2fs_file_read(*f, buf, nbytes, &n); + if (err != 0) { + errno = EINVAL; + return (-1); + } + + return (n); +} + +ssize_t +ext2lib_pread(fsi_file_t *file, void *buf, size_t nbytes, uint64_t off) +{ + ext2_file_t *f = fsip_file_data(file); + uint64_t tmpoff; + unsigned int n; + int err; + + if ((err = ext2fs_file_llseek(*f, 0, EXT2_SEEK_CUR, &tmpoff)) != 0) { + errno = EINVAL; + return (-1); + } + + if ((err = ext2fs_file_llseek(*f, off, EXT2_SEEK_SET, NULL)) != 0) { + errno = EINVAL; + return (-1); + } + + err = ext2fs_file_read(*f, buf, nbytes, &n); + + ext2fs_file_llseek(*f, tmpoff, EXT2_SEEK_SET, NULL); + + if (err != 0) { + errno = EINVAL; + return (-1); + } + + return (n); +} + +int +ext2lib_close(fsi_file_t *file) +{ + ext2_file_t *f = fsip_file_data(file); + ext2fs_file_close(*f); + free(f); + return (0); +} + +fsi_plugin_ops_t * +fsi_init_plugin(int version, fsi_plugin_t *fp, const char **name) +{ + static fsi_plugin_ops_t ops = { + FSIMAGE_PLUGIN_VERSION, + .fpo_mount = ext2lib_mount, + .fpo_umount = ext2lib_umount, + .fpo_open = ext2lib_open, + .fpo_read = ext2lib_read, + .fpo_pread = ext2lib_pread, + .fpo_close = ext2lib_close + }; + + *name = "ext2fs-lib"; + return (&ops); +} diff --git a/tools/libfsimage/ext2fs/Makefile b/tools/libfsimage/ext2fs/Makefile new file mode 100644 --- /dev/null +++ b/tools/libfsimage/ext2fs/Makefile @@ -0,0 +1,13 @@ +XEN_ROOT = ../../.. + +LIB_SRCS-y = fsys_ext2fs.c + +FS = ext2fs + +.PHONY: all +all: fs-all + +.PHONY: install +install: fs-install + +include $(XEN_ROOT)/tools/libfsimage/Rules.mk diff --git a/tools/libfsimage/ext2fs/fsys_ext2fs.c b/tools/libfsimage/ext2fs/fsys_ext2fs.c new file mode 100644 --- /dev/null +++ b/tools/libfsimage/ext2fs/fsys_ext2fs.c @@ -0,0 +1,804 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 1999, 2001, 2003 Free Software Foundation, Inc. + * + * 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 <fsimage_grub.h> + +#define mapblock1 (*fsig_int1(ffi)) +#define mapblock2 (*fsig_int2(ffi)) + +/* sizes are always in bytes, BLOCK values are always in DEV_BSIZE (sectors) */ +#define DEV_BSIZE 512 + +/* include/linux/fs.h */ +#define BLOCK_SIZE 1024 /* initial block size for superblock read */ +/* made up, defaults to 1 but can be passed via mount_opts */ +#define WHICH_SUPER 1 +/* kind of from fs/ext2/super.c */ +#define SBLOCK (WHICH_SUPER * BLOCK_SIZE / DEV_BSIZE) /* = 2 */ + +/* include/asm-i386/types.h */ +typedef __signed__ char __s8; +typedef unsigned char __u8; +typedef __signed__ short __s16; +typedef unsigned short __u16; +typedef __signed__ int __s32; +typedef unsigned int __u32; + +/* + * Constants relative to the data blocks, from ext2_fs.h + */ +#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) + +/* include/linux/ext2_fs.h */ +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_pad; + __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 */ + __u32 s_reserved[235]; /* Padding to the end of the block */ + }; + +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_pad; + __u32 bg_reserved[3]; + }; + +struct ext2_inode + { + __u16 i_mode; /* File mode */ + __u16 i_uid; /* Owner Uid */ + __u32 i_size; /* 4: Size in bytes */ + __u32 i_atime; /* Access time */ + __u32 i_ctime; /* 12: Creation time */ + __u32 i_mtime; /* Modification time */ + __u32 i_dtime; /* 20: Deletion Time */ + __u16 i_gid; /* Group Id */ + __u16 i_links_count; /* 24: Links count */ + __u32 i_blocks; /* Blocks count */ + __u32 i_flags; /* 32: File flags */ + union + { + struct + { + __u32 l_i_reserved1; + } + linux1; + struct + { + __u32 h_i_translator; + } + hurd1; + struct + { + __u32 m_i_reserved1; + } + masix1; + } + osd1; /* OS dependent 1 */ + __u32 i_block[EXT2_N_BLOCKS]; /* 40: Pointers to blocks */ + __u32 i_version; /* File version (for NFS) */ + __u32 i_file_acl; /* File ACL */ + __u32 i_dir_acl; /* Directory ACL */ + __u32 i_faddr; /* Fragment address */ + union + { + struct + { + __u8 l_i_frag; /* Fragment number */ + __u8 l_i_fsize; /* Fragment size */ + __u16 i_pad1; + __u32 l_i_reserved2[2]; + } + 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; + struct + { + __u8 m_i_frag; /* Fragment number */ + __u8 m_i_fsize; /* Fragment size */ + __u16 m_pad1; + __u32 m_i_reserved2[2]; + } + masix2; + } + osd2; /* OS dependent 2 */ + }; + +/* linux/limits.h */ +#define NAME_MAX 255 /* # chars in a file name */ + +/* linux/posix_type.h */ +typedef long linux_off_t; + +/* linux/ext2fs.h */ +#define EXT2_NAME_LEN 255 +struct ext2_dir_entry + { + __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 */ + }; + +/* linux/ext2fs.h */ +/* + * 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) + + +/* ext2/super.c */ +#define log2(n) ffz(~(n)) + +#define EXT2_SUPER_MAGIC 0xEF53 /* include/linux/ext2_fs.h */ +#define EXT2_ROOT_INO 2 /* include/linux/ext2_fs.h */ +#define PATH_MAX 1024 /* include/linux/limits.h */ +#define MAX_LINK_COUNT 5 /* number of symbolic links to follow */ + +/* made up, these are pointers into FSYS_BUF */ +/* read once, always stays there: */ +#define SUPERBLOCK \ + ((struct ext2_super_block *)(FSYS_BUF)) +#define GROUP_DESC \ + ((struct ext2_group_desc *) \ + ((char *)SUPERBLOCK + sizeof(struct ext2_super_block))) +#define INODE \ + ((struct ext2_inode *)((caddr_t)GROUP_DESC + EXT2_BLOCK_SIZE(SUPERBLOCK))) +#define DATABLOCK1 \ + ((char *)((caddr_t)INODE + sizeof(struct ext2_inode))) +#define DATABLOCK2 \ + ((char *)((caddr_t)DATABLOCK1 + EXT2_BLOCK_SIZE(SUPERBLOCK))) + +/* linux/ext2_fs.h */ +#define EXT2_ADDR_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof (__u32)) +#define EXT2_ADDR_PER_BLOCK_BITS(s) (log2(EXT2_ADDR_PER_BLOCK(s))) + +/* linux/ext2_fs.h */ +#define EXT2_BLOCK_SIZE_BITS(s) ((s)->s_log_block_size + 10) +/* kind of from ext2/super.c */ +#define EXT2_BLOCK_SIZE(s) (1 << EXT2_BLOCK_SIZE_BITS(s)) +/* linux/ext2fs.h */ +#define EXT2_DESC_PER_BLOCK(s) \ + (EXT2_BLOCK_SIZE(s) / sizeof (struct ext2_group_desc)) +/* linux/stat.h */ +#define S_IFMT 00170000 +#define S_IFLNK 0120000 +#define S_IFREG 0100000 +#define S_IFDIR 0040000 +#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) +#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) +#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) + +/* include/asm-i386/bitops.h */ +/* + * ffz = Find First Zero in word. Undefined if no zero exists, + * so code should check against ~0UL first.. + */ +#ifdef __amd64 +#define BSF "bsfq" +#else +#define BSF "bsfl" +#endif +static __inline__ unsigned long +ffz (unsigned long word) +{ + __asm__ (BSF " %1,%0" +: "=r" (word) +: "r" (~word)); + return word; +} + +/* check filesystem types and read superblock into memory buffer */ +int +ext2fs_mount (fsi_file_t *ffi) +{ + int retval = 1; + + if (/*(((current_drive & 0x80) || (current_slice != 0)) + && (current_slice != PC_SLICE_TYPE_EXT2FS) + && (current_slice != PC_SLICE_TYPE_LINUX_RAID) + && (! IS_PC_SLICE_TYPE_BSD_WITH_FS (current_slice, FS_EXT2FS)) + && (! IS_PC_SLICE_TYPE_BSD_WITH_FS (current_slice, FS_OTHER))) + || part_length < (SBLOCK + (sizeof (struct ext2_super_block) / DEV_BSIZE)) + || */ !devread (ffi, SBLOCK, 0, sizeof (struct ext2_super_block), + (char *) SUPERBLOCK) + || SUPERBLOCK->s_magic != EXT2_SUPER_MAGIC) + retval = 0; + + return retval; +} + +/* Takes a file system block number and reads it into BUFFER. */ +static int +ext2_rdfsb (fsi_file_t *ffi, int fsblock, char *buffer) +{ +#ifdef E2DEBUG + printf ("fsblock %d buffer %d\n", fsblock, buffer); +#endif /* E2DEBUG */ + return devread (ffi, fsblock * (EXT2_BLOCK_SIZE (SUPERBLOCK) / DEV_BSIZE), 0, + EXT2_BLOCK_SIZE (SUPERBLOCK), (char *) buffer); +} + +/* from + ext2/inode.c:ext2_bmap() +*/ +/* Maps LOGICAL_BLOCK (the file offset divided by the blocksize) into + a physical block (the location in the file system) via an inode. */ +static int +ext2fs_block_map (fsi_file_t *ffi, int logical_block) +{ + +#ifdef E2DEBUG + unsigned char *i; + for (i = (unsigned char *) INODE; + i < ((unsigned char *) INODE + sizeof (struct ext2_inode)); + i++) + { + printf ("%c", "0123456789abcdef"[*i >> 4]); + printf ("%c", "0123456789abcdef"[*i % 16]); + if (!((i + 1 - (unsigned char *) INODE) % 16)) + { + printf ("\n"); + } + else + { + printf (" "); + } + } + printf ("logical block %d\n", logical_block); +#endif /* E2DEBUG */ + + /* if it is directly pointed to by the inode, return that physical addr */ + if (logical_block < EXT2_NDIR_BLOCKS) + { +#ifdef E2DEBUG + printf ("returning %d\n", (unsigned char *) (INODE->i_block[logical_block])); + printf ("returning %d\n", INODE->i_block[logical_block]); +#endif /* E2DEBUG */ + return INODE->i_block[logical_block]; + } + /* else */ + logical_block -= EXT2_NDIR_BLOCKS; + /* try the indirect block */ + if (logical_block < EXT2_ADDR_PER_BLOCK (SUPERBLOCK)) + { + if (mapblock1 != 1 + && !ext2_rdfsb (ffi, INODE->i_block[EXT2_IND_BLOCK], DATABLOCK1)) + { + errnum = ERR_FSYS_CORRUPT; + return -1; + } + mapblock1 = 1; + return ((__u32 *) DATABLOCK1)[logical_block]; + } + /* else */ + logical_block -= EXT2_ADDR_PER_BLOCK (SUPERBLOCK); + /* now try the double indirect block */ + if (logical_block < (1 << (EXT2_ADDR_PER_BLOCK_BITS (SUPERBLOCK) * 2))) + { + int bnum; + if (mapblock1 != 2 + && !ext2_rdfsb (ffi, INODE->i_block[EXT2_DIND_BLOCK], DATABLOCK1)) + { + errnum = ERR_FSYS_CORRUPT; + return -1; + } + mapblock1 = 2; + if ((bnum = (((__u32 *) DATABLOCK1) + [logical_block >> EXT2_ADDR_PER_BLOCK_BITS (SUPERBLOCK)])) + != mapblock2 + && !ext2_rdfsb (ffi, bnum, DATABLOCK2)) + { + errnum = ERR_FSYS_CORRUPT; + return -1; + } + mapblock2 = bnum; + return ((__u32 *) DATABLOCK2) + [logical_block & (EXT2_ADDR_PER_BLOCK (SUPERBLOCK) - 1)]; + } + /* else */ + mapblock2 = -1; + logical_block -= (1 << (EXT2_ADDR_PER_BLOCK_BITS (SUPERBLOCK) * 2)); + if (mapblock1 != 3 + && !ext2_rdfsb (ffi, INODE->i_block[EXT2_TIND_BLOCK], DATABLOCK1)) + { + errnum = ERR_FSYS_CORRUPT; + return -1; + } + mapblock1 = 3; + if (!ext2_rdfsb (ffi, ((__u32 *) DATABLOCK1) + [logical_block >> (EXT2_ADDR_PER_BLOCK_BITS (SUPERBLOCK) + * 2)], + DATABLOCK2)) + { + errnum = ERR_FSYS_CORRUPT; + return -1; + } + if (!ext2_rdfsb (ffi, ((__u32 *) DATABLOCK2) + [(logical_block >> EXT2_ADDR_PER_BLOCK_BITS (SUPERBLOCK)) + & (EXT2_ADDR_PER_BLOCK (SUPERBLOCK) - 1)], + DATABLOCK2)) + { + errnum = ERR_FSYS_CORRUPT; + return -1; + } + return ((__u32 *) DATABLOCK2) + [logical_block & (EXT2_ADDR_PER_BLOCK (SUPERBLOCK) - 1)]; +} + +/* preconditions: all preconds of ext2fs_block_map */ +int +ext2fs_read (fsi_file_t *ffi, char *buf, int len) +{ + int logical_block; + int offset; + int map; + int ret = 0; + int size = 0; + +#ifdef E2DEBUG + static char hexdigit[] = "0123456789abcdef"; + unsigned char *i; + for (i = (unsigned char *) INODE; + i < ((unsigned char *) INODE + sizeof (struct ext2_inode)); + i++) + { + printf ("%c", hexdigit[*i >> 4]); + printf ("%c", hexdigit[*i % 16]); + if (!((i + 1 - (unsigned char *) INODE) % 16)) + { + printf ("\n"); + } + else + { + printf (" "); + } + } +#endif /* E2DEBUG */ + while (len > 0) + { + /* find the (logical) block component of our location */ + logical_block = filepos >> EXT2_BLOCK_SIZE_BITS (SUPERBLOCK); + offset = filepos & (EXT2_BLOCK_SIZE (SUPERBLOCK) - 1); + map = ext2fs_block_map (ffi, logical_block); +#ifdef E2DEBUG + printf ("map=%d\n", map); +#endif /* E2DEBUG */ + if (map < 0) + break; + + size = EXT2_BLOCK_SIZE (SUPERBLOCK); + size -= offset; + if (size > len) + size = len; + + if (map == 0) { + memset ((char *) buf, 0, size); + } else { + disk_read_func = disk_read_hook; + + devread (ffi, map * (EXT2_BLOCK_SIZE (SUPERBLOCK) / DEV_BSIZE), + offset, size, buf); + + disk_read_func = NULL; + } + + buf += size; + len -= size; + filepos += size; + ret += size; + } + + if (errnum) + ret = 0; + + return ret; +} + + +/* Based on: + def_blk_fops points to + blkdev_open, which calls (I think): + sys_open() + do_open() + open_namei() + dir_namei() which accesses current->fs->root + fs->root was set during original mount: + (something)... which calls (I think): + ext2_read_super() + iget() + __iget() + read_inode() + ext2_read_inode() + uses desc_per_block_bits, which is set in ext2_read_super() + also uses group descriptors loaded during ext2_read_super() + lookup() + ext2_lookup() + ext2_find_entry() + ext2_getblk() + +*/ + +static inline +int ext2_is_fast_symlink (fsi_file_t *ffi) +{ + int ea_blocks; + ea_blocks = INODE->i_file_acl ? EXT2_BLOCK_SIZE (SUPERBLOCK) / DEV_BSIZE : 0; + return INODE->i_blocks == ea_blocks; +} + +/* preconditions: ext2fs_mount already executed, therefore supblk in buffer + * known as SUPERBLOCK + * returns: 0 if error, nonzero iff we were able to find the file successfully + * postconditions: on a nonzero return, buffer known as INODE contains the + * inode of the file we were trying to look up + * side effects: messes up GROUP_DESC buffer area + */ +int +ext2fs_dir (fsi_file_t *ffi, char *dirname) +{ + int current_ino = EXT2_ROOT_INO; /* start at the root */ + int updir_ino = current_ino; /* the parent of the current directory */ + int group_id; /* which group the inode is in */ + int group_desc; /* fs pointer to that group */ + int desc; /* index within that group */ + int ino_blk; /* fs pointer of the inode's information */ + int str_chk = 0; /* used to hold the results of a string compare */ + struct ext2_group_desc *gdp; + struct ext2_inode *raw_inode; /* inode info corresponding to current_ino */ + + char linkbuf[PATH_MAX]; /* buffer for following symbolic links */ + int link_count = 0; + + char *rest; + char ch; /* temp char holder */ + + int off; /* offset within block of directory entry (off mod blocksize) */ + int loc; /* location within a directory */ + int blk; /* which data blk within dir entry (off div blocksize) */ + long map; /* fs pointer of a particular block from dir entry */ + struct ext2_dir_entry *dp; /* pointer to directory entry */ +#ifdef E2DEBUG + unsigned char *i; +#endif /* E2DEBUG */ + + /* loop invariants: + current_ino = inode to lookup + dirname = pointer to filename component we are cur looking up within + the directory known pointed to by current_ino (if any) + */ + + while (1) + { +#ifdef E2DEBUG + printf ("inode %d\n", current_ino); + printf ("dirname=%s\n", dirname); +#endif /* E2DEBUG */ + + /* look up an inode */ + group_id = (current_ino - 1) / (SUPERBLOCK->s_inodes_per_group); + group_desc = group_id >> log2 (EXT2_DESC_PER_BLOCK (SUPERBLOCK)); + desc = group_id & (EXT2_DESC_PER_BLOCK (SUPERBLOCK) - 1); +#ifdef E2DEBUG + printf ("ipg=%d, dpb=%d\n", SUPERBLOCK->s_inodes_per_group, + EXT2_DESC_PER_BLOCK (SUPERBLOCK)); + printf ("group_id=%d group_desc=%d desc=%d\n", group_id, group_desc, desc); +#endif /* E2DEBUG */ + if (!ext2_rdfsb (ffi, + (WHICH_SUPER + group_desc + SUPERBLOCK->s_first_data_block), + (char *)GROUP_DESC)) + { + return 0; + } + gdp = GROUP_DESC; + ino_blk = gdp[desc].bg_inode_table + + (((current_ino - 1) % (SUPERBLOCK->s_inodes_per_group)) + >> log2 (EXT2_BLOCK_SIZE (SUPERBLOCK) / sizeof (struct ext2_inode))); +#ifdef E2DEBUG + printf ("inode table fsblock=%d\n", ino_blk); +#endif /* E2DEBUG */ + if (!ext2_rdfsb (ffi, ino_blk, (char *)INODE)) + { + return 0; + } + + /* reset indirect blocks! */ + mapblock2 = mapblock1 = -1; + + raw_inode = INODE + + ((current_ino - 1) + & (EXT2_BLOCK_SIZE (SUPERBLOCK) / sizeof (struct ext2_inode) - 1)); +#ifdef E2DEBUG + printf ("ipb=%d, sizeof(inode)=%d\n", + (EXT2_BLOCK_SIZE (SUPERBLOCK) / sizeof (struct ext2_inode)), + sizeof (struct ext2_inode)); + printf ("inode=%x, raw_inode=%x\n", INODE, raw_inode); + printf ("offset into inode table block=%d\n", (int) raw_inode - (int) INODE); + for (i = (unsigned char *) INODE; i <= (unsigned char *) raw_inode; + i++) + { + printf ("%c", "0123456789abcdef"[*i >> 4]); + printf ("%c", "0123456789abcdef"[*i % 16]); + if (!((i + 1 - (unsigned char *) INODE) % 16)) + { + printf ("\n"); + } + else + { + printf (" "); + } + } + printf ("first word=%x\n", *((int *) raw_inode)); +#endif /* E2DEBUG */ + + /* copy inode to fixed location */ + memmove ((void *) INODE, (void *) raw_inode, sizeof (struct ext2_inode)); + +#ifdef E2DEBUG + printf ("first word=%x\n", *((int *) INODE)); +#endif /* E2DEBUG */ + + /* If we've got a symbolic link, then chase it. */ + if (S_ISLNK (INODE->i_mode)) + { + int len; + if (++link_count > MAX_LINK_COUNT) + { + errnum = ERR_SYMLINK_LOOP; + return 0; + } + + /* Find out how long our remaining name is. */ + len = 0; + while (dirname[len] && !isspace (dirname[len])) + len++; + + /* Get the symlink size. */ + filemax = (INODE->i_size); + if (filemax + len > sizeof (linkbuf) - 2) + { + errnum = ERR_FILELENGTH; + return 0; + } + + if (len) + { + /* Copy the remaining name to the end of the symlink data. + Note that DIRNAME and LINKBUF may overlap! */ + memmove (linkbuf + filemax, dirname, len); + } + linkbuf[filemax + len] = '\0'; + + /* Read the symlink data. */ + if (! ext2_is_fast_symlink (ffi)) + { + /* Read the necessary blocks, and reset the file pointer. */ + len = ext2fs_read (ffi, linkbuf, filemax); + filepos = 0; + if (!len) + return 0; + } + else + { + /* Copy the data directly from the inode. */ + len = filemax; + memmove (linkbuf, (char *) INODE->i_block, len); + } + +#ifdef E2DEBUG + printf ("symlink=%s\n", linkbuf); +#endif + + dirname = linkbuf; + if (*dirname == '/') + { + /* It's an absolute link, so look it up in root. */ + current_ino = EXT2_ROOT_INO; + updir_ino = current_ino; + } + else + { + /* Relative, so look it up in our parent directory. */ + current_ino = updir_ino; + } + + /* Try again using the new name. */ + continue; + } + + /* if end of filename, INODE points to the file's inode */ + if (!*dirname || isspace (*dirname)) + { + if (!S_ISREG (INODE->i_mode)) + { + errnum = ERR_BAD_FILETYPE; + return 0; + } + + filemax = (INODE->i_size); + return 1; + } + + /* else we have to traverse a directory */ + updir_ino = current_ino; + + /* skip over slashes */ + while (*dirname == '/') + dirname++; + + /* if this isn't a directory of sufficient size to hold our file, abort */ + if (!(INODE->i_size) || !S_ISDIR (INODE->i_mode)) + { + errnum = ERR_BAD_FILETYPE; + return 0; + } + + /* skip to next slash or end of filename (space) */ + for (rest = dirname; (ch = *rest) && !isspace (ch) && ch != '/'; + rest++); + + /* look through this directory and find the next filename component */ + /* invariant: rest points to slash after the next filename component */ + *rest = 0; + loc = 0; + + do + { + +#ifdef E2DEBUG + printf ("dirname=%s, rest=%s, loc=%d\n", dirname, rest, loc); +#endif /* E2DEBUG */ + + /* if our location/byte offset into the directory exceeds the size, + give up */ + if (loc >= INODE->i_size) + { + if (print_possibilities < 0) + { +# if 0 + putchar ('\n'); +# endif + } + else + { + errnum = ERR_FILE_NOT_FOUND; + *rest = ch; + } + return (print_possibilities < 0); + } + + /* else, find the (logical) block component of our location */ + blk = loc >> EXT2_BLOCK_SIZE_BITS (SUPERBLOCK); + + /* we know which logical block of the directory entry we are looking + for, now we have to translate that to the physical (fs) block on + the disk */ + map = ext2fs_block_map (ffi, blk); +#ifdef E2DEBUG + printf ("fs block=%d\n", map); +#endif /* E2DEBUG */ + mapblock2 = -1; + if ((map < 0) || !ext2_rdfsb (ffi, map, DATABLOCK2)) + { + errnum = ERR_FSYS_CORRUPT; + *rest = ch; + return 0; + } + off = loc & (EXT2_BLOCK_SIZE (SUPERBLOCK) - 1); + dp = (struct ext2_dir_entry *) (DATABLOCK2 + off); + /* advance loc prematurely to next on-disk directory entry */ + loc += dp->rec_len; + + /* NOTE: ext2fs filenames are NOT null-terminated */ + +#ifdef E2DEBUG + printf ("directory entry ino=%d\n", dp->inode); + if (dp->inode) + printf ("entry=%s\n", dp->name); +#endif /* E2DEBUG */ + + if (dp->inode) + { + int saved_c = dp->name[dp->name_len]; + + dp->name[dp->name_len] = 0; + str_chk = substring (dirname, dp->name); + +# ifndef STAGE1_5 + if (print_possibilities && ch != '/' + && (!*dirname || str_chk <= 0)) + { + if (print_possibilities > 0) + print_possibilities = -print_possibilities; + print_a_completion (dp->name); + } +# endif + + dp->name[dp->name_len] = saved_c; + } + + } + while (!dp->inode || (str_chk || (print_possibilities && ch != '/'))); + + current_ino = dp->inode; + *(dirname = rest) = ch; + } + /* never get here */ +} + +fsi_plugin_ops_t * +fsi_init_plugin(int version, fsi_plugin_t *fp, const char **name) +{ + static fsig_plugin_ops_t ops = { + FSIMAGE_PLUGIN_VERSION, + .fpo_mount = ext2fs_mount, + .fpo_dir = ext2fs_dir, + .fpo_read = ext2fs_read + }; + + *name = "ext2fs"; + return (fsig_init(fp, &ops)); +} diff --git a/tools/libfsimage/reiserfs/Makefile b/tools/libfsimage/reiserfs/Makefile new file mode 100644 --- /dev/null +++ b/tools/libfsimage/reiserfs/Makefile @@ -0,0 +1,13 @@ +XEN_ROOT = ../../.. + +LIB_SRCS-y = fsys_reiserfs.c + +FS = reiserfs + +.PHONY: all +all: fs-all + +.PHONY: install +install: fs-install + +include $(XEN_ROOT)/tools/libfsimage/Rules.mk diff --git a/tools/libfsimage/reiserfs/fsys_reiserfs.c b/tools/libfsimage/reiserfs/fsys_reiserfs.c new file mode 100644 --- /dev/null +++ b/tools/libfsimage/reiserfs/fsys_reiserfs.c @@ -0,0 +1,1254 @@ +/* fsys_reiserfs.c - an implementation for the ReiserFS filesystem */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2000, 2001 Free Software Foundation, Inc. + * + * 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 <fsimage_grub.h> + +#undef REISERDEBUG + +/* Some parts of this code (mainly the structures and defines) are + * from the original reiser fs code, as found in the linux kernel. + */ + +/* include/asm-i386/types.h */ +typedef __signed__ char __s8; +typedef unsigned char __u8; +typedef __signed__ short __s16; +typedef unsigned short __u16; +typedef __signed__ int __s32; +typedef unsigned int __u32; +typedef unsigned long long __u64; + +/* linux/posix_type.h */ +typedef long linux_off_t; + +/* linux/little_endian.h */ +#define __cpu_to_le64(x) ((__u64) (x)) +#define __le64_to_cpu(x) ((__u64) (x)) +#define __cpu_to_le32(x) ((__u32) (x)) +#define __le32_to_cpu(x) ((__u32) (x)) +#define __cpu_to_le16(x) ((__u16) (x)) +#define __le16_to_cpu(x) ((__u16) (x)) + +/* include/linux/reiser_fs.h */ +/* This is the new super block of a journaling reiserfs system */ +struct reiserfs_super_block +{ + __u32 s_block_count; /* blocks count */ + __u32 s_free_blocks; /* free blocks count */ + __u32 s_root_block; /* root block number */ + __u32 s_journal_block; /* journal block number */ + __u32 s_journal_dev; /* journal device number */ + __u32 s_journal_size; /* size of the journal on FS creation. used to make sure they don't overflow it */ + __u32 s_journal_trans_max; /* max number of blocks in a transaction. */ + __u32 s_journal_magic; /* random value made on fs creation */ + __u32 s_journal_max_batch; /* max number of blocks to batch into a trans */ + __u32 s_journal_max_commit_age; /* in seconds, how old can an async commit be */ + __u32 s_journal_max_trans_age; /* in seconds, how old can a transaction be */ + __u16 s_blocksize; /* block size */ + __u16 s_oid_maxsize; /* max size of object id array */ + __u16 s_oid_cursize; /* current size of object id array */ + __u16 s_state; /* valid or error */ + char s_magic[16]; /* reiserfs magic string indicates that file system is reiserfs */ + __u16 s_tree_height; /* height of disk tree */ + __u16 s_bmap_nr; /* amount of bitmap blocks needed to address each block of file system */ + __u16 s_version; + char s_unused[128]; /* zero filled by mkreiserfs */ +}; + +#define REISERFS_MAX_SUPPORTED_VERSION 2 +#define REISERFS_SUPER_MAGIC_STRING "ReIsErFs" +#define REISER2FS_SUPER_MAGIC_STRING "ReIsEr2Fs" +#define REISER3FS_SUPER_MAGIC_STRING "ReIsEr3Fs" + +#define MAX_HEIGHT 7 + +/* must be correct to keep the desc and commit structs at 4k */ +#define JOURNAL_TRANS_HALF 1018 + +/* first block written in a commit. */ +struct reiserfs_journal_desc { + __u32 j_trans_id; /* id of commit */ + __u32 j_len; /* length of commit. len +1 is the commit block */ + __u32 j_mount_id; /* mount id of this trans*/ + __u32 j_realblock[JOURNAL_TRANS_HALF]; /* real locations for the first blocks */ + char j_magic[12]; +}; + +/* last block written in a commit */ +struct reiserfs_journal_commit { + __u32 j_trans_id; /* must match j_trans_id from the desc block */ + __u32 j_len; /* ditto */ + __u32 j_realblock[JOURNAL_TRANS_HALF]; /* real locations for the last blocks */ + char j_digest[16]; /* md5 sum of all the blocks involved, including desc and commit. not used, kill it */ +}; + +/* this header block gets written whenever a transaction is considered + fully flushed, and is more recent than the last fully flushed + transaction. + fully flushed means all the log blocks and all the real blocks are + on disk, and this transaction does not need to be replayed. +*/ +struct reiserfs_journal_header { + /* id of last fully flushed transaction */ + __u32 j_last_flush_trans_id; + /* offset in the log of where to start replay after a crash */ + __u32 j_first_unflushed_offset; + /* mount id to detect very old transactions */ + __u32 j_mount_id; +}; + +/* magic string to find desc blocks in the journal */ +#define JOURNAL_DESC_MAGIC "ReIsErLB" + + +/* + * directories use this key as well as old files + */ +struct offset_v1 +{ + /* + * for regular files this is the offset to the first byte of the + * body, contained in the object-item, as measured from the start of + * the entire body of the object. + * + * for directory entries, k_offset consists of hash derived from + * hashing the name and using few bits (23 or more) of the resulting + * hash, and generation number that allows distinguishing names with + * hash collisions. If number of collisions overflows generation + * number, we return EEXIST. High order bit is 0 always + */ + __u32 k_offset; + __u32 k_uniqueness; +}; + +struct offset_v2 +{ + /* + * for regular files this is the offset to the first byte of the + * body, contained in the object-item, as measured from the start of + * the entire body of the object. + * + * for directory entries, k_offset consists of hash derived from + * hashing the name and using few bits (23 or more) of the resulting + * hash, and generation number that allows distinguishing names with + * hash collisions. If number of collisions overflows generation + * number, we return EEXIST. High order bit is 0 always + */ + __u64 k_offset:60; + __u64 k_type: 4; +}; + + +struct key +{ + /* packing locality: by default parent directory object id */ + __u32 k_dir_id; + /* object identifier */ + __u32 k_objectid; + /* the offset and node type (old and new form) */ + union + { + struct offset_v1 v1; + struct offset_v2 v2; + } + u; +}; + +#define KEY_SIZE (sizeof (struct key)) + +/* Header of a disk block. More precisely, header of a formatted leaf + or internal node, and not the header of an unformatted node. */ +struct block_head +{ + __u16 blk_level; /* Level of a block in the tree. */ + __u16 blk_nr_item; /* Number of keys/items in a block. */ + __u16 blk_free_space; /* Block free space in bytes. */ + struct key blk_right_delim_key; /* Right delimiting key for this block (supported for leaf level nodes + only) */ +}; +#define BLKH_SIZE (sizeof (struct block_head)) +#define DISK_LEAF_NODE_LEVEL 1 /* Leaf node level. */ + +struct item_head +{ + struct key ih_key; /* Everything in the tree is found by searching for it based on its key.*/ + + union + { + __u16 ih_free_space; /* The free space in the last unformatted node of an indirect item if this + is an indirect item. This equals 0xFFFF iff this is a direct item or + stat data item. Note that the key, not this field, is used to determine + the item type, and thus which field this union contains. */ + __u16 ih_entry_count; /* Iff this is a directory item, this field equals the number of directory + entries in the directory item. */ + } + u; + __u16 ih_item_len; /* total size of the item body */ + __u16 ih_item_location; /* an offset to the item body within the block */ + __u16 ih_version; /* ITEM_VERSION_1 for all old items, + ITEM_VERSION_2 for new ones. + Highest bit is set by fsck + temporary, cleaned after all done */ +}; +/* size of item header */ +#define IH_SIZE (sizeof (struct item_head)) + +#define ITEM_VERSION_1 0 +#define ITEM_VERSION_2 1 +#define IH_KEY_OFFSET(ih) ((ih)->ih_version == ITEM_VERSION_1 \ + ? (ih)->ih_key.u.v1.k_offset \ + : (ih)->ih_key.u.v2.k_offset) + +#define IH_KEY_ISTYPE(ih, type) ((ih)->ih_version == ITEM_VERSION_1 \ + ? (ih)->ih_key.u.v1.k_uniqueness == V1_##type \ + : (ih)->ih_key.u.v2.k_type == V2_##type) + +struct disk_child +{ + unsigned long dc_block_number; /* Disk child's block number. */ + unsigned short dc_size; /* Disk child's used space. */ +}; + +#define DC_SIZE (sizeof (struct disk_child)) + +/* Stat Data on disk. + * + * Note that reiserfs has two different forms of stat data. Luckily + * the fields needed by grub are at the same position. + */ +struct stat_data +{ + __u16 sd_mode; /* file type, permissions */ + __u16 sd_notused1[3]; /* fields not needed by reiserfs */ + __u32 sd_size; /* file size */ + __u32 sd_size_hi; /* file size high 32 bits (since version 2) */ +}; + +struct reiserfs_de_head +{ + __u32 deh_offset; /* third component of the directory entry key */ + __u32 deh_dir_id; /* objectid of the parent directory of the + object, that is referenced by directory entry */ + __u32 deh_objectid;/* objectid of the object, that is referenced by + directory entry */ + __u16 deh_location;/* offset of name in the whole item */ + __u16 deh_state; /* whether 1) entry contains stat data (for + future), and 2) whether entry is hidden + (unlinked) */ +}; + +#define DEH_SIZE (sizeof (struct reiserfs_de_head)) + +#define DEH_Statdata (1 << 0) /* not used now */ +#define DEH_Visible (1 << 2) + +#define SD_OFFSET 0 +#define SD_UNIQUENESS 0 +#define DOT_OFFSET 1 +#define DOT_DOT_OFFSET 2 +#define DIRENTRY_UNIQUENESS 500 + +#define V1_TYPE_STAT_DATA 0x0 +#define V1_TYPE_DIRECT 0xffffffff +#define V1_TYPE_INDIRECT 0xfffffffe +#define V1_TYPE_DIRECTORY_MAX 0xfffffffd +#define V2_TYPE_STAT_DATA 0 +#define V2_TYPE_INDIRECT 1 +#define V2_TYPE_DIRECT 2 +#define V2_TYPE_DIRENTRY 3 + +#define REISERFS_ROOT_OBJECTID 2 +#define REISERFS_ROOT_PARENT_OBJECTID 1 +#define REISERFS_DISK_OFFSET_IN_BYTES (64 * 1024) +/* the spot for the super in versions 3.5 - 3.5.11 (inclusive) */ +#define REISERFS_OLD_DISK_OFFSET_IN_BYTES (8 * 1024) +#define REISERFS_OLD_BLOCKSIZE 4096 + +#define S_ISREG(mode) (((mode) & 0170000) == 0100000) +#define S_ISDIR(mode) (((mode) & 0170000) == 0040000) +#define S_ISLNK(mode) (((mode) & 0170000) == 0120000) + +#define PATH_MAX 1024 /* include/linux/limits.h */ +#define MAX_LINK_COUNT 5 /* number of symbolic links to follow */ + +/* The size of the node cache */ +#define FSYSREISER_CACHE_SIZE 24*1024 +#define FSYSREISER_MIN_BLOCKSIZE SECTOR_SIZE +#define FSYSREISER_MAX_BLOCKSIZE FSYSREISER_CACHE_SIZE / 3 + +/* Info about currently opened file */ +struct fsys_reiser_fileinfo +{ + __u32 k_dir_id; + __u32 k_objectid; +}; + +/* In memory info about the currently mounted filesystem */ +struct fsys_reiser_info +{ + /* The last read item head */ + struct item_head *current_ih; + /* The last read item */ + char *current_item; + /* The information for the currently opened file */ + struct fsys_reiser_fileinfo fileinfo; + /* The start of the journal */ + __u32 journal_block; + /* The size of the journal */ + __u32 journal_block_count; + /* The first valid descriptor block in journal + (relative to journal_block) */ + __u32 journal_first_desc; + + /* The ReiserFS version. */ + __u16 version; + /* The current depth of the reiser tree. */ + __u16 tree_depth; + /* SECTOR_SIZE << blocksize_shift == blocksize. */ + __u8 blocksize_shift; + /* 1 << full_blocksize_shift == blocksize. */ + __u8 fullblocksize_shift; + /* The reiserfs block size (must be a power of 2) */ + __u16 blocksize; + /* The number of cached tree nodes */ + __u16 cached_slots; + /* The number of valid transactions in journal */ + __u16 journal_transactions; + + unsigned int blocks[MAX_HEIGHT]; + unsigned int next_key_nr[MAX_HEIGHT]; +}; + +/* The cached s+tree blocks in FSYS_BUF, see below + * for a more detailed description. + */ +#define ROOT ((char *) FSYS_BUF) +#define CACHE(i) (ROOT + ((i) << INFO->fullblocksize_shift)) +#define LEAF CACHE (DISK_LEAF_NODE_LEVEL) + +#define BLOCKHEAD(cache) ((struct block_head *) cache) +#define ITEMHEAD ((struct item_head *) ((char *) LEAF + BLKH_SIZE)) +#define KEY(cache) ((struct key *) ((char *) cache + BLKH_SIZE)) +#define DC(cache) ((struct disk_child *) \ + ((char *) cache + BLKH_SIZE + KEY_SIZE * nr_item)) +/* The fsys_reiser_info block. + */ +#define INFO \ + ((struct fsys_reiser_info *) ((char *) FSYS_BUF + FSYSREISER_CACHE_SIZE)) +/* + * The journal cache. For each transaction it contains the number of + * blocks followed by the real block numbers of this transaction. + * + * If the block numbers of some transaction won't fit in this space, + * this list is stopped with a 0xffffffff marker and the remaining + * uncommitted transactions aren't cached. + */ +#define JOURNAL_START ((__u32 *) (INFO + 1)) +#define JOURNAL_END ((__u32 *) (FSYS_BUF + FSYS_BUFLEN)) + +#ifdef __amd64 +#define BSF "bsfq" +#else +#define BSF "bsfl" +#endif +static __inline__ unsigned long +grub_log2 (unsigned long word) +{ + __asm__ (BSF " %1,%0" + : "=r" (word) + : "r" (word)); + return word; +} +#define log2 grub_log2 + +static __inline__ int +is_power_of_two (unsigned long word) +{ + return (word & -word) == word; +} + +static int +journal_read (fsi_file_t *ffi, int block, int len, char *buffer) +{ + return devread (ffi, (INFO->journal_block + block) << INFO->blocksize_shift, + 0, len, buffer); +} + +/* Read a block from ReiserFS file system, taking the journal into + * account. If the block nr is in the journal, the block from the + * journal taken. + */ +static int +block_read (fsi_file_t *ffi, int blockNr, int start, int len, char *buffer) +{ + int transactions = INFO->journal_transactions; + int desc_block = INFO->journal_first_desc; + int journal_mask = INFO->journal_block_count - 1; + int translatedNr = blockNr; + __u32 *journal_table = JOURNAL_START; + while (transactions-- > 0) + { + int i = 0; + int j_len; + if (*journal_table != 0xffffffff) + { + /* Search for the blockNr in cached journal */ + j_len = *journal_table++; + while (i++ < j_len) + { + if (*journal_table++ == blockNr) + { + journal_table += j_len - i; + goto found; + } + } + } + else + { + /* This is the end of cached journal marker. The remaining + * transactions are still on disk. + */ + struct reiserfs_journal_desc desc; + struct reiserfs_journal_commit commit; + + if (! journal_read (ffi, desc_block, sizeof (desc), (char *) &desc)) + return 0; + + j_len = desc.j_len; + while (i < j_len && i < JOURNAL_TRANS_HALF) + if (desc.j_realblock[i++] == blockNr) + goto found; + + if (j_len >= JOURNAL_TRANS_HALF) + { + int commit_block = (desc_block + 1 + j_len) & journal_mask; + if (! journal_read (ffi, commit_block, + sizeof (commit), (char *) &commit)) + return 0; + while (i < j_len) + if (commit.j_realblock[i++ - JOURNAL_TRANS_HALF] == blockNr) + goto found; + } + } + goto not_found; + + found: + translatedNr = INFO->journal_block + ((desc_block + i) & journal_mask); +#ifdef REISERDEBUG + printf ("block_read: block %d is mapped to journal block %d.\n", + blockNr, translatedNr - INFO->journal_block); +#endif + /* We must continue the search, as this block may be overwritten + * in later transactions. + */ + not_found: + desc_block = (desc_block + 2 + j_len) & journal_mask; + } + return devread (ffi, translatedNr << INFO->blocksize_shift, start, len, buffer); +} + +/* Init the journal data structure. We try to cache as much as + * possible in the JOURNAL_START-JOURNAL_END space, but if it is full + * we can still read the rest from the disk on demand. + * + * The first number of valid transactions and the descriptor block of the + * first valid transaction are held in INFO. The transactions are all + * adjacent, but we must take care of the journal wrap around. + */ +static int +journal_init (fsi_file_t *ffi) +{ + unsigned int block_count = INFO->journal_block_count; + unsigned int desc_block; + unsigned int commit_block; + unsigned int next_trans_id; + struct reiserfs_journal_header header; + struct reiserfs_journal_desc desc; + struct reiserfs_journal_commit commit; + __u32 *journal_table = JOURNAL_START; + + journal_read (ffi, block_count, sizeof (header), (char *) &header); + desc_block = header.j_first_unflushed_offset; + if (desc_block >= block_count) + return 0; + + INFO->journal_first_desc = desc_block; + next_trans_id = header.j_last_flush_trans_id + 1; + +#ifdef REISERDEBUG + printf ("journal_init: last flushed %d\n", + header.j_last_flush_trans_id); +#endif + + while (1) + { + journal_read (ffi, desc_block, sizeof (desc), (char *) &desc); + if (substring (JOURNAL_DESC_MAGIC, desc.j_magic) + || desc.j_trans_id != next_trans_id + || desc.j_mount_id != header.j_mount_id) + /* no more valid transactions */ + break; + + commit_block = (desc_block + desc.j_len + 1) & (block_count - 1); + journal_read (ffi, commit_block, sizeof (commit), (char *) &commit); + if (desc.j_trans_id != commit.j_trans_id + || desc.j_len != commit.j_len) + /* no more valid transactions */ + break; + +#ifdef REISERDEBUG + printf ("Found valid transaction %d/%d at %d.\n", + desc.j_trans_id, desc.j_mount_id, desc_block); +#endif + + next_trans_id++; + if (journal_table < JOURNAL_END) + { + if ((journal_table + 1 + desc.j_len) >= JOURNAL_END) + { + /* The table is almost full; mark the end of the cached + * journal.*/ + *journal_table = 0xffffffff; + journal_table = JOURNAL_END; + } + else + { + int i; + /* Cache the length and the realblock numbers in the table. + * The block number of descriptor can easily be computed. + * and need not to be stored here. + */ + *journal_table++ = desc.j_len; + for (i = 0; i < desc.j_len && i < JOURNAL_TRANS_HALF; i++) + { + *journal_table++ = desc.j_realblock[i]; +#ifdef REISERDEBUG + printf ("block %d is in journal %d.\n", + desc.j_realblock[i], desc_block); +#endif + } + for ( ; i < desc.j_len; i++) + { + *journal_table++ = commit.j_realblock[i-JOURNAL_TRANS_HALF]; +#ifdef REISERDEBUG + printf ("block %d is in journal %d.\n", + commit.j_realblock[i-JOURNAL_TRANS_HALF], + desc_block); +#endif + } + } + } + desc_block = (commit_block + 1) & (block_count - 1); + } +#ifdef REISERDEBUG + printf ("Transaction %d/%d at %d isn't valid.\n", + desc.j_trans_id, desc.j_mount_id, desc_block); +#endif + + INFO->journal_transactions + = next_trans_id - header.j_last_flush_trans_id - 1; + return errnum == 0; +} + +/* check filesystem types and read superblock into memory buffer */ +int +reiserfs_mount (fsi_file_t *ffi) +{ + struct reiserfs_super_block super; + int superblock = REISERFS_DISK_OFFSET_IN_BYTES >> SECTOR_BITS; + + if (/*part_length < superblock + (sizeof (super) >> SECTOR_BITS) + || */ !devread (ffi, superblock, 0, sizeof (struct reiserfs_super_block), + (char *) &super) + || (substring (REISER3FS_SUPER_MAGIC_STRING, super.s_magic) > 0 + && substring (REISER2FS_SUPER_MAGIC_STRING, super.s_magic) > 0 + && substring (REISERFS_SUPER_MAGIC_STRING, super.s_magic) > 0) + || (/* check that this is not a copy inside the journal log */ + super.s_journal_block * super.s_blocksize + <= REISERFS_DISK_OFFSET_IN_BYTES)) + { + /* Try old super block position */ + superblock = REISERFS_OLD_DISK_OFFSET_IN_BYTES >> SECTOR_BITS; + if (/*part_length < superblock + (sizeof (super) >> SECTOR_BITS) + || */ ! devread (ffi, superblock, 0, sizeof (struct reiserfs_super_block), + (char *) &super)) + return 0; + + if (substring (REISER3FS_SUPER_MAGIC_STRING, super.s_magic) > 0 + && substring (REISER2FS_SUPER_MAGIC_STRING, super.s_magic) > 0 + && substring (REISERFS_SUPER_MAGIC_STRING, super.s_magic) > 0) + { + /* pre journaling super block ? */ + if (substring (REISERFS_SUPER_MAGIC_STRING, + (char*) ((char *) &super + 20)) > 0) + return 0; + + super.s_blocksize = REISERFS_OLD_BLOCKSIZE; + super.s_journal_block = 0; + super.s_version = 0; + } + } + + /* check the version number. */ + if (super.s_version > REISERFS_MAX_SUPPORTED_VERSION) + return 0; + + INFO->version = super.s_version; + INFO->blocksize = super.s_blocksize; + INFO->fullblocksize_shift = log2 (super.s_blocksize); + INFO->blocksize_shift = INFO->fullblocksize_shift - SECTOR_BITS; + INFO->cached_slots = + (FSYSREISER_CACHE_SIZE >> INFO->fullblocksize_shift) - 1; + +#ifdef REISERDEBUG + printf ("reiserfs_mount: version=%d, blocksize=%d\n", + INFO->version, INFO->blocksize); +#endif /* REISERDEBUG */ + + /* Clear node cache. */ + memset (INFO->blocks, 0, sizeof (INFO->blocks)); + + if (super.s_blocksize < FSYSREISER_MIN_BLOCKSIZE + || super.s_blocksize > FSYSREISER_MAX_BLOCKSIZE + || (SECTOR_SIZE << INFO->blocksize_shift) != super.s_blocksize) + return 0; + + /* Initialize journal code. If something fails we end with zero + * journal_transactions, so we don't access the journal at all. + */ + INFO->journal_transactions = 0; + if (super.s_journal_block != 0 && super.s_journal_dev == 0) + { + INFO->journal_block = super.s_journal_block; + INFO->journal_block_count = super.s_journal_size; + if (is_power_of_two (INFO->journal_block_count)) + journal_init (ffi); + + /* Read in super block again, maybe it is in the journal */ + block_read (ffi, superblock >> INFO->blocksize_shift, + 0, sizeof (struct reiserfs_super_block), (char *) &super); + } + + if (! block_read (ffi, super.s_root_block, 0, INFO->blocksize, (char*) ROOT)) + return 0; + + INFO->tree_depth = BLOCKHEAD (ROOT)->blk_level; + +#ifdef REISERDEBUG + printf ("root read_in: block=%d, depth=%d\n", + super.s_root_block, INFO->tree_depth); +#endif /* REISERDEBUG */ + + if (INFO->tree_depth >= MAX_HEIGHT) + return 0; + if (INFO->tree_depth == DISK_LEAF_NODE_LEVEL) + { + /* There is only one node in the whole filesystem, + * which is simultanously leaf and root */ + memcpy (LEAF, ROOT, INFO->blocksize); + } + return 1; +} + +/***************** TREE ACCESSING METHODS *****************************/ + +/* I assume you are familiar with the ReiserFS tree, if not go to + * http://www.namesys.com/content_table.html + * + * My tree node cache is organized as following + * 0 ROOT node + * 1 LEAF node (if the ROOT is also a LEAF it is copied here + * 2-n other nodes on current path from bottom to top. + * if there is not enough space in the cache, the top most are + * omitted. + * + * I have only two methods to find a key in the tree: + * search_stat(dir_id, objectid) searches for the stat entry (always + * the first entry) of an object. + * next_key() gets the next key in tree order. + * + * This means, that I can only sequential reads of files are + * efficient, but this really doesn't hurt for grub. + */ + +/* Read in the node at the current path and depth into the node cache. + * You must set INFO->blocks[depth] before. + */ +static char * +read_tree_node (fsi_file_t *ffi, unsigned int blockNr, int depth) +{ + char* cache = CACHE(depth); + int num_cached = INFO->cached_slots; + if (depth < num_cached) + { + /* This is the cached part of the path. Check if same block is + * needed. + */ + if (blockNr == INFO->blocks[depth]) + return cache; + } + else + cache = CACHE(num_cached); + +#ifdef REISERDEBUG + printf (" next read_in: block=%d (depth=%d)\n", + blockNr, depth); +#endif /* REISERDEBUG */ + if (! block_read (ffi, blockNr, 0, INFO->blocksize, cache)) + return 0; + /* Make sure it has the right node level */ + if (BLOCKHEAD (cache)->blk_level != depth) + { + errnum = ERR_FSYS_CORRUPT; + return 0; + } + + INFO->blocks[depth] = blockNr; + return cache; +} + +/* Get the next key, i.e. the key following the last retrieved key in + * tree order. INFO->current_ih and + * INFO->current_info are adapted accordingly. */ +static int +next_key (fsi_file_t *ffi) +{ + int depth; + struct item_head *ih = INFO->current_ih + 1; + char *cache; + +#ifdef REISERDEBUG + printf ("next_key:\n old ih: key %d:%d:%d:%d version:%d\n", + INFO->current_ih->ih_key.k_dir_id, + INFO->current_ih->ih_key.k_objectid, + INFO->current_ih->ih_key.u.v1.k_offset, + INFO->current_ih->ih_key.u.v1.k_uniqueness, + INFO->current_ih->ih_version); +#endif /* REISERDEBUG */ + + if (ih == &ITEMHEAD[BLOCKHEAD (LEAF)->blk_nr_item]) + { + depth = DISK_LEAF_NODE_LEVEL; + /* The last item, was the last in the leaf node. + * Read in the next block + */ + do + { + if (depth == INFO->tree_depth) + { + /* There are no more keys at all. + * Return a dummy item with MAX_KEY */ + ih = (struct item_head *) &BLOCKHEAD (LEAF)->blk_right_delim_key; + goto found; + } + depth++; +#ifdef REISERDEBUG + printf (" depth=%d, i=%d\n", depth, INFO->next_key_nr[depth]); +#endif /* REISERDEBUG */ + } + while (INFO->next_key_nr[depth] == 0); + + if (depth == INFO->tree_depth) + cache = ROOT; + else if (depth <= INFO->cached_slots) + cache = CACHE (depth); + else + { + cache = read_tree_node (ffi, INFO->blocks[depth], depth); + if (! cache) + return 0; + } + + do + { + int nr_item = BLOCKHEAD (cache)->blk_nr_item; + int key_nr = INFO->next_key_nr[depth]++; +#ifdef REISERDEBUG + printf (" depth=%d, i=%d/%d\n", depth, key_nr, nr_item); +#endif /* REISERDEBUG */ + if (key_nr == nr_item) + /* This is the last item in this block, set the next_key_nr to 0 */ + INFO->next_key_nr[depth] = 0; + + cache = read_tree_node (ffi, DC (cache)[key_nr].dc_block_number, --depth); + if (! cache) + return 0; + } + while (depth > DISK_LEAF_NODE_LEVEL); + + ih = ITEMHEAD; + } + found: + INFO->current_ih = ih; + INFO->current_item = &LEAF[ih->ih_item_location]; +#ifdef REISERDEBUG + printf (" new ih: key %d:%d:%d:%d version:%d\n", + INFO->current_ih->ih_key.k_dir_id, + INFO->current_ih->ih_key.k_objectid, + INFO->current_ih->ih_key.u.v1.k_offset, + INFO->current_ih->ih_key.u.v1.k_uniqueness, + INFO->current_ih->ih_version); +#endif /* REISERDEBUG */ + return 1; +} + +/* preconditions: reiserfs_mount already executed, therefore + * INFO block is valid + * returns: 0 if error (errnum is set), + * nonzero iff we were able to find the key successfully. + * postconditions: on a nonzero return, the current_ih and + * current_item fields describe the key that equals the + * searched key. INFO->next_key contains the next key after + * the searched key. + * side effects: messes around with the cache. + */ +static int +search_stat (fsi_file_t *ffi, __u32 dir_id, __u32 objectid) +{ + char *cache; + int depth; + int nr_item; + int i; + struct item_head *ih; +#ifdef REISERDEBUG + printf ("search_stat:\n key %d:%d:0:0\n", dir_id, objectid); +#endif /* REISERDEBUG */ + + depth = INFO->tree_depth; + cache = ROOT; + + while (depth > DISK_LEAF_NODE_LEVEL) + { + struct key *key; + nr_item = BLOCKHEAD (cache)->blk_nr_item; + + key = KEY (cache); + + for (i = 0; i < nr_item; i++) + { + if (key->k_dir_id > dir_id + || (key->k_dir_id == dir_id + && (key->k_objectid > objectid + || (key->k_objectid == objectid + && (key->u.v1.k_offset + | key->u.v1.k_uniqueness) > 0)))) + break; + key++; + } + +#ifdef REISERDEBUG + printf (" depth=%d, i=%d/%d\n", depth, i, nr_item); +#endif /* REISERDEBUG */ + INFO->next_key_nr[depth] = (i == nr_item) ? 0 : i+1; + cache = read_tree_node (ffi, DC (cache)[i].dc_block_number, --depth); + if (! cache) + return 0; + } + + /* cache == LEAF */ + nr_item = BLOCKHEAD (LEAF)->blk_nr_item; + ih = ITEMHEAD; + for (i = 0; i < nr_item; i++) + { + if (ih->ih_key.k_dir_id == dir_id + && ih->ih_key.k_objectid == objectid + && ih->ih_key.u.v1.k_offset == 0 + && ih->ih_key.u.v1.k_uniqueness == 0) + { +#ifdef REISERDEBUG + printf (" depth=%d, i=%d/%d\n", depth, i, nr_item); +#endif /* REISERDEBUG */ + INFO->current_ih = ih; + INFO->current_item = &LEAF[ih->ih_item_location]; + return 1; + } + ih++; + } + errnum = ERR_FSYS_CORRUPT; + return 0; +} + +int +reiserfs_read (fsi_file_t *ffi, char *buf, int len) +{ + unsigned int blocksize; + unsigned int offset; + unsigned int to_read; + char *prev_buf = buf; + +#ifdef REISERDEBUG + printf ("reiserfs_read: filepos=%d len=%d, offset=%x:%x\n", + filepos, len, (__u64) IH_KEY_OFFSET (INFO->current_ih) - 1); +#endif /* REISERDEBUG */ + + if (INFO->current_ih->ih_key.k_objectid != INFO->fileinfo.k_objectid + || IH_KEY_OFFSET (INFO->current_ih) > filepos + 1) + { + search_stat (ffi, INFO->fileinfo.k_dir_id, INFO->fileinfo.k_objectid); + goto get_next_key; + } + + while (! errnum) + { + if (INFO->current_ih->ih_key.k_objectid != INFO->fileinfo.k_objectid) + break; + + offset = filepos - IH_KEY_OFFSET (INFO->current_ih) + 1; + blocksize = INFO->current_ih->ih_item_len; + +#ifdef REISERDEBUG + printf (" loop: filepos=%d len=%d, offset=%d blocksize=%d\n", + filepos, len, offset, blocksize); +#endif /* REISERDEBUG */ + + if (IH_KEY_ISTYPE(INFO->current_ih, TYPE_DIRECT) + && offset < blocksize) + { +#ifdef REISERDEBUG + printf ("direct_read: offset=%d, blocksize=%d\n", + offset, blocksize); +#endif /* REISERDEBUG */ + to_read = blocksize - offset; + if (to_read > len) + to_read = len; + + if (disk_read_hook != NULL) + { + disk_read_func = disk_read_hook; + + block_read (ffi, INFO->blocks[DISK_LEAF_NODE_LEVEL], + (INFO->current_item - LEAF + offset), to_read, buf); + + disk_read_func = NULL; + } + else + memcpy (buf, INFO->current_item + offset, to_read); + goto update_buf_len; + } + else if (IH_KEY_ISTYPE(INFO->current_ih, TYPE_INDIRECT)) + { + blocksize = (blocksize >> 2) << INFO->fullblocksize_shift; +#ifdef REISERDEBUG + printf ("indirect_read: offset=%d, blocksize=%d\n", + offset, blocksize); +#endif /* REISERDEBUG */ + + while (offset < blocksize) + { + __u32 blocknr = ((__u32 *) INFO->current_item) + [offset >> INFO->fullblocksize_shift]; + int blk_offset = offset & (INFO->blocksize-1); + + to_read = INFO->blocksize - blk_offset; + if (to_read > len) + to_read = len; + + disk_read_func = disk_read_hook; + + /* Journal is only for meta data. Data blocks can be read + * directly without using block_read + */ + devread (ffi, blocknr << INFO->blocksize_shift, + blk_offset, to_read, buf); + + disk_read_func = NULL; + update_buf_len: + len -= to_read; + buf += to_read; + offset += to_read; + filepos += to_read; + if (len == 0) + goto done; + } + } + get_next_key: + next_key (ffi); + } + done: + return errnum ? 0 : buf - prev_buf; +} + + +/* preconditions: reiserfs_mount already executed, therefore + * INFO block is valid + * returns: 0 if error, nonzero iff we were able to find the file successfully + * postconditions: on a nonzero return, INFO->fileinfo contains the info + * of the file we were trying to look up, filepos is 0 and filemax is + * the size of the file. + */ +int +reiserfs_dir (fsi_file_t *ffi, char *dirname) +{ + struct reiserfs_de_head *de_head; + char *rest, ch; + __u32 dir_id, objectid, parent_dir_id = 0, parent_objectid = 0; +#ifndef STAGE1_5 + int do_possibilities = 0; +#endif /* ! STAGE1_5 */ + char linkbuf[PATH_MAX]; /* buffer for following symbolic links */ + int link_count = 0; + int mode; + + dir_id = REISERFS_ROOT_PARENT_OBJECTID; + objectid = REISERFS_ROOT_OBJECTID; + + while (1) + { +#ifdef REISERDEBUG + printf ("dirname=%s\n", dirname); +#endif /* REISERDEBUG */ + + /* Search for the stat info first. */ + if (! search_stat (ffi, dir_id, objectid)) + return 0; + +#ifdef REISERDEBUG + printf ("sd_mode=%x sd_size=%d\n", + ((struct stat_data *) INFO->current_item)->sd_mode, + ((struct stat_data *) INFO->current_item)->sd_size); +#endif /* REISERDEBUG */ + + mode = ((struct stat_data *) INFO->current_item)->sd_mode; + + /* If we've got a symbolic link, then chase it. */ + if (S_ISLNK (mode)) + { + int len; + if (++link_count > MAX_LINK_COUNT) + { + errnum = ERR_SYMLINK_LOOP; + return 0; + } + + /* Get the symlink size. */ + filemax = ((struct stat_data *) INFO->current_item)->sd_size; + + /* Find out how long our remaining name is. */ + len = 0; + while (dirname[len] && !isspace (dirname[len])) + len++; + + if (filemax + len > sizeof (linkbuf) - 1) + { + errnum = ERR_FILELENGTH; + return 0; + } + + /* Copy the remaining name to the end of the symlink data. + Note that DIRNAME and LINKBUF may overlap! */ + grub_memmove (linkbuf + filemax, dirname, len+1); + + INFO->fileinfo.k_dir_id = dir_id; + INFO->fileinfo.k_objectid = objectid; + filepos = 0; + if (! next_key (ffi) + || reiserfs_read (ffi, linkbuf, filemax) != filemax) + { + if (! errnum) + errnum = ERR_FSYS_CORRUPT; + return 0; + } + +#ifdef REISERDEBUG + printf ("symlink=%s\n", linkbuf); +#endif /* REISERDEBUG */ + + dirname = linkbuf; + if (*dirname == '/') + { + /* It's an absolute link, so look it up in root. */ + dir_id = REISERFS_ROOT_PARENT_OBJECTID; + objectid = REISERFS_ROOT_OBJECTID; + } + else + { + /* Relative, so look it up in our parent directory. */ + dir_id = parent_dir_id; + objectid = parent_objectid; + } + + /* Now lookup the new name. */ + continue; + } + + /* if we have a real file (and we're not just printing possibilities), + then this is where we want to exit */ + + if (! *dirname || isspace (*dirname)) + { + if (! S_ISREG (mode)) + { + errnum = ERR_BAD_FILETYPE; + return 0; + } + + filepos = 0; + filemax = ((struct stat_data *) INFO->current_item)->sd_size; + + /* If this is a new stat data and size is > 4GB set filemax to + * maximum + */ + if (INFO->current_ih->ih_version == ITEM_VERSION_2 + && ((struct stat_data *) INFO->current_item)->sd_size_hi > 0) + filemax = 0xffffffff; + + INFO->fileinfo.k_dir_id = dir_id; + INFO->fileinfo.k_objectid = objectid; + return next_key (ffi); + } + + /* continue with the file/directory name interpretation */ + while (*dirname == '/') + dirname++; + if (! S_ISDIR (mode)) + { + errnum = ERR_BAD_FILETYPE; + return 0; + } + for (rest = dirname; (ch = *rest) && ! isspace (ch) && ch != '/'; rest++); + *rest = 0; + +# ifndef STAGE1_5 + if (print_possibilities && ch != '/') + do_possibilities = 1; +# endif /* ! STAGE1_5 */ + + while (1) + { + char *name_end; + int num_entries; + + if (! next_key (ffi)) + return 0; +#ifdef REISERDEBUG + printf ("ih: key %d:%d:%d:%d version:%d\n", + INFO->current_ih->ih_key.k_dir_id, + INFO->current_ih->ih_key.k_objectid, + INFO->current_ih->ih_key.u.v1.k_offset, + INFO->current_ih->ih_key.u.v1.k_uniqueness, + INFO->current_ih->ih_version); +#endif /* REISERDEBUG */ + + if (INFO->current_ih->ih_key.k_objectid != objectid) + break; + + name_end = INFO->current_item + INFO->current_ih->ih_item_len; + de_head = (struct reiserfs_de_head *) INFO->current_item; + num_entries = INFO->current_ih->u.ih_entry_count; + while (num_entries > 0) + { + char *filename = INFO->current_item + de_head->deh_location; + char tmp = *name_end; + if ((de_head->deh_state & DEH_Visible)) + { + int cmp; + /* Directory names in ReiserFS are not null + * terminated. We write a temporary 0 behind it. + * NOTE: that this may overwrite the first block in + * the tree cache. That doesn't hurt as long as we + * don't call next_key () in between. + */ + *name_end = 0; + cmp = substring (dirname, filename); + *name_end = tmp; +# ifndef STAGE1_5 + if (do_possibilities) + { + if (cmp <= 0) + { + if (print_possibilities > 0) + print_possibilities = -print_possibilities; + *name_end = 0; + print_a_completion (filename); + *name_end = tmp; + } + } + else +# endif /* ! STAGE1_5 */ + if (cmp == 0) + goto found; + } + /* The beginning of this name marks the end of the next name. + */ + name_end = filename; + de_head++; + num_entries--; + } + } + +# ifndef STAGE1_5 + if (print_possibilities < 0) + return 1; +# endif /* ! STAGE1_5 */ + + errnum = ERR_FILE_NOT_FOUND; + *rest = ch; + return 0; + + found: + + *rest = ch; + dirname = rest; + + parent_dir_id = dir_id; + parent_objectid = objectid; + dir_id = de_head->deh_dir_id; + objectid = de_head->deh_objectid; + } +} + +int +reiserfs_embed (fsi_file_t *ffi, int *start_sector, int needed_sectors) +{ + struct reiserfs_super_block super; + int num_sectors; + + if (! devread (ffi, REISERFS_DISK_OFFSET_IN_BYTES >> SECTOR_BITS, 0, + sizeof (struct reiserfs_super_block), (char *) &super)) + return 0; + + *start_sector = 1; /* reserve first sector for stage1 */ + if ((substring (REISERFS_SUPER_MAGIC_STRING, super.s_magic) <= 0 + || substring (REISER2FS_SUPER_MAGIC_STRING, super.s_magic) <= 0 + || substring (REISER3FS_SUPER_MAGIC_STRING, super.s_magic) <= 0) + && (/* check that this is not a super block copy inside + * the journal log */ + super.s_journal_block * super.s_blocksize + > REISERFS_DISK_OFFSET_IN_BYTES)) + num_sectors = (REISERFS_DISK_OFFSET_IN_BYTES >> SECTOR_BITS) - 1; + else + num_sectors = (REISERFS_OLD_DISK_OFFSET_IN_BYTES >> SECTOR_BITS) - 1; + + return (needed_sectors <= num_sectors); +} + +fsi_plugin_ops_t * +fsi_init_plugin(int version, fsi_plugin_t *fp, const char **name) +{ + static fsig_plugin_ops_t ops = { + FSIMAGE_PLUGIN_VERSION, + .fpo_mount = reiserfs_mount, + .fpo_dir = reiserfs_dir, + .fpo_read = reiserfs_read + }; + + *name = "reiserfs"; + return (fsig_init(fp, &ops)); +} diff --git a/tools/libfsimage/ufs/Makefile b/tools/libfsimage/ufs/Makefile new file mode 100644 --- /dev/null +++ b/tools/libfsimage/ufs/Makefile @@ -0,0 +1,13 @@ +XEN_ROOT = ../../.. + +LIB_SRCS-y = fsys_ufs.c + +FS = ufs + +.PHONY: all +all: fs-all + +.PHONY: install +install: fs-install + +include $(XEN_ROOT)/tools/libfsimage/Rules.mk diff --git a/tools/libfsimage/ufs/fsys_ufs.c b/tools/libfsimage/ufs/fsys_ufs.c new file mode 100644 --- /dev/null +++ b/tools/libfsimage/ufs/fsys_ufs.c @@ -0,0 +1,276 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2006 Free Software Foundation, Inc. + * + * 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. + */ +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* From Solaris usr/src/stand/lib/fs/ufs/ufsops.c */ + +#include <fsimage_grub.h> + +#include "ufs.h" + +/* These are the pools of buffers, etc. */ + +#define SUPERBLOCK ((struct fs *)(FSYS_BUF + 0x2000)) +#define INODE ((struct icommon *)(FSYS_BUF + 0x1000)) +#define DIRENT (FSYS_BUF + 0x4000) +#define INDIRBLK1 ((grub_daddr32_t *)(FSYS_BUF + 0x4000)) /* 2+ indir blk */ +#define INDIRBLK0 ((grub_daddr32_t *)(FSYS_BUF+ 0x6000)) /* 1st indirect blk */ + +#define indirblk0 (*fsig_int1(ffi)) +#define indirblk1 (*fsig_int2(ffi)) + +static int openi(fsi_file_t *, grub_ino_t); +static grub_ino_t dlook(fsi_file_t *, grub_ino_t, char *); +static grub_daddr32_t sbmap(fsi_file_t *, grub_daddr32_t); + +/* read superblock and check fs magic */ +int +ufs_mount(fsi_file_t *ffi) +{ + if (/*! IS_PC_SLICE_TYPE_SOLARIS(current_slice) || */ + !devread(ffi, UFS_SBLOCK, 0, UFS_SBSIZE, (char *)SUPERBLOCK) || + SUPERBLOCK->fs_magic != UFS_MAGIC) + return 0; + + return 1; +} + + +/* + * searching for a file, if successful, inode will be loaded in INODE + * The entry point should really be named ufs_open(char *pathname). + * For now, keep it consistent with the rest of fsys modules. + */ +int +ufs_dir(fsi_file_t *ffi, char *dirname) +{ + grub_ino_t inode = ROOTINO; /* start from root */ + char *fname, ch; + + indirblk0 = indirblk1 = 0; + + /* skip leading slashes */ + while (*dirname == '/') + dirname++; + + while (inode && *dirname && !isspace(*dirname)) { + if (!openi(ffi, inode)) + return 0; + + /* parse for next path component */ + fname = dirname; + while (*dirname && !isspace(*dirname) && *dirname != '/') + dirname++; + ch = *dirname; + *dirname = 0; /* ensure null termination */ + + inode = dlook(ffi, inode, fname); + *dirname = ch; + while (*dirname == '/') + dirname++; + } + + /* return 1 only if inode exists and is a regular file */ + if (! openi(ffi, inode)) + return (0); + filepos = 0; + filemax = INODE->ic_sizelo; + return (inode && ((INODE->ic_smode & IFMT) == IFREG)); +} + +/* + * This is the high-level read function. + */ +int +ufs_read(fsi_file_t *ffi, char *buf, int len) +{ + int off, size, ret = 0, ok; + grub_daddr32_t lblk, dblk; + + while (len) { + off = blkoff(SUPERBLOCK, filepos); + lblk = lblkno(SUPERBLOCK, filepos); + size = SUPERBLOCK->fs_bsize; + size -= off; + if (size > len) + size = len; + + if ((dblk = sbmap(ffi, lblk)) <= 0) { + /* we are in a file hole, just zero the buf */ + grub_memset(buf, 0, size); + } else { + disk_read_func = disk_read_hook; + ok = devread(ffi, fsbtodb(SUPERBLOCK, dblk), + off, size, buf); + disk_read_func = 0; + if (!ok) + return 0; + } + buf += size; + len -= size; + filepos += size; + ret += size; + } + + return (ret); +} + +int +ufs_embed (int *start_sector, int needed_sectors) +{ + if (needed_sectors > 14) + return 0; + + *start_sector = 2; + return 1; +} + +/* read inode and place content in INODE */ +static int +openi(fsi_file_t *ffi, grub_ino_t inode) +{ + grub_daddr32_t dblk; + int off; + + /* get block and byte offset into the block */ + dblk = fsbtodb(SUPERBLOCK, itod(SUPERBLOCK, inode)); + off = itoo(SUPERBLOCK, inode) * sizeof (struct icommon); + + return (devread(ffi, dblk, off, sizeof (struct icommon), (char *)INODE)); +} + +/* + * Performs fileblock mapping. Convert file block no. to disk block no. + * Returns 0 when block doesn't exist and <0 when block isn't initialized + * (i.e belongs to a hole in the file). + */ +grub_daddr32_t +sbmap(fsi_file_t *ffi, grub_daddr32_t bn) +{ + int level, bound, i, index; + grub_daddr32_t nb, blkno; + grub_daddr32_t *db = INODE->ic_db; + + /* blocks 0..UFS_NDADDR are direct blocks */ + if (bn < UFS_NDADDR) { + return db[bn]; + } + + /* determine how many levels of indirection. */ + level = 0; + bn -= UFS_NDADDR; + bound = UFS_NINDIR(SUPERBLOCK); + while (bn >= bound) { + level++; + bn -= bound; + bound *= UFS_NINDIR(SUPERBLOCK); + } + if (level >= UFS_NIADDR) /* bn too big */ + return ((grub_daddr32_t)0); + + /* fetch the first indirect block */ + nb = INODE->ic_ib[level]; + if (nb == 0) { + return ((grub_daddr32_t)0); + } + if (indirblk0 != nb) { + indirblk0 = 0; + blkno = fsbtodb(SUPERBLOCK, nb); + if (!devread(ffi, blkno, 0, SUPERBLOCK->fs_bsize, + (char *)INDIRBLK0)) + return (0); + indirblk0 = nb; + } + bound /= UFS_NINDIR(SUPERBLOCK); + index = (bn / bound) % UFS_NINDIR(SUPERBLOCK); + nb = INDIRBLK0[index]; + + /* fetch through the indirect blocks */ + for (i = 1; i <= level; i++) { + if (indirblk1 != nb) { + blkno = fsbtodb(SUPERBLOCK, nb); + if (!devread(ffi, blkno, 0, SUPERBLOCK->fs_bsize, + (char *)INDIRBLK1)) + return (0); + indirblk1 = nb; + } + bound /= UFS_NINDIR(SUPERBLOCK); + index = (bn / bound) % UFS_NINDIR(SUPERBLOCK); + nb = INDIRBLK1[index]; + if (nb == 0) + return ((grub_daddr32_t)0); + } + + return (nb); +} + +/* search directory content for name, return inode number */ +static grub_ino_t +dlook(fsi_file_t *ffi, grub_ino_t dir_ino, char *name) +{ + int loc, off; + grub_daddr32_t lbn, dbn, dblk; + struct direct *dp; + + if ((INODE->ic_smode & IFMT) != IFDIR) + return 0; + + loc = 0; + while (loc < INODE->ic_sizelo) { + /* offset into block */ + off = blkoff(SUPERBLOCK, loc); + if (off == 0) { /* need to read in a new block */ + /* get logical block number */ + lbn = lblkno(SUPERBLOCK, loc); + /* resolve indrect blocks */ + dbn = sbmap(ffi, lbn); + if (dbn == 0) + return (0); + + dblk = fsbtodb(SUPERBLOCK, dbn); + if (!devread(ffi, dblk, 0, SUPERBLOCK->fs_bsize, + (char *)DIRENT)) { + return 0; + } + } + + dp = (struct direct *)(DIRENT + off); + if (dp->d_ino && substring(name, dp->d_name) == 0) + return (dp->d_ino); + loc += dp->d_reclen; + } + return (0); +} + +fsi_plugin_ops_t * +fsi_init_plugin(int version, fsi_plugin_t *fp, const char **name) +{ + static fsig_plugin_ops_t ops = { + FSIMAGE_PLUGIN_VERSION, + .fpo_mount = ufs_mount, + .fpo_dir = ufs_dir, + .fpo_read = ufs_read + }; + + *name = "ufs"; + return (fsig_init(fp, &ops)); +} diff --git a/tools/libfsimage/ufs/ufs.h b/tools/libfsimage/ufs/ufs.h new file mode 100644 --- /dev/null +++ b/tools/libfsimage/ufs/ufs.h @@ -0,0 +1,228 @@ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _GRUB_UFS_H +#define _GRUB_UFS_H_ + +/* ufs specific constants */ +#define UFS_SBLOCK 16 +#define UFS_SBSIZE 8192 +#define UFS_MAGIC 0x011954 +#define ROOTINO 2 /* i number of all roots */ +#define UFS_NDADDR 12 /* direct blocks */ +#define UFS_NIADDR 3 /* indirect blocks */ +#define MAXMNTLEN 512 +#define MAXCSBUFS 32 +#define MAXNAMELEN 256 + +/* file types */ +#define IFMT 0xf000 +#define IFREG 0x8000 +#define IFDIR 0x4000 + +typedef unsigned char grub_uchar_t; +typedef unsigned short grub_ushort_t; +typedef unsigned short grub_o_mode_t; +typedef unsigned short grub_o_uid_t; +typedef unsigned short grub_o_gid_t; +typedef uint32_t grub_ino_t; +typedef int32_t grub_int32_t; +typedef int32_t grub_uid_t; +typedef int32_t grub_gid_t; +typedef uint32_t grub_uint32_t; +typedef uint32_t grub_daddr32_t; +typedef uint32_t grub_time32_t; +typedef struct { int val[2]; } grub_quad_t; + +struct timeval32 { + grub_time32_t tv_sec; + grub_int32_t tv_usec; +}; + +/* + * Per cylinder group information; summarized in blocks allocated + * from first cylinder group data blocks. These blocks have to be + * read in from fs_csaddr (size fs_cssize) in addition to the + * super block. + * + * N.B. sizeof (struct csum) must be a power of two in order for + * the ``fs_cs'' macro to work (see below). + */ +struct csum { + grub_int32_t cs_ndir; /* number of directories */ + grub_int32_t cs_nbfree; /* number of free blocks */ + grub_int32_t cs_nifree; /* number of free inodes */ + grub_int32_t cs_nffree; /* number of free frags */ +}; + +/* Ufs super block */ +struct fs { + grub_uint32_t fs_link; /* linked list of file systems */ + grub_uint32_t fs_rolled; /* logging only: fs fully rolled */ + grub_daddr32_t fs_sblkno; /* addr of super-block in filesys */ + grub_daddr32_t fs_cblkno; /* offset of cyl-block in filesys */ + grub_daddr32_t fs_iblkno; /* offset of inode-blocks in filesys */ + grub_daddr32_t fs_dblkno; /* offset of first data after cg */ + grub_int32_t fs_cgoffset; /* cylinder group offset in cylinder */ + grub_int32_t fs_cgmask; /* used to calc mod fs_ntrak */ + grub_time32_t fs_time; /* last time written */ + grub_int32_t fs_size; /* number of blocks in fs */ + grub_int32_t fs_dsize; /* number of data blocks in fs */ + grub_int32_t fs_ncg; /* number of cylinder groups */ + grub_int32_t fs_bsize; /* size of basic blocks in fs */ + grub_int32_t fs_fsize; /* size of frag blocks in fs */ + grub_int32_t fs_frag; /* number of frags in a block in fs */ + /* these are configuration parameters */ + grub_int32_t fs_minfree; /* minimum percentage of free blocks */ + grub_int32_t fs_rotdelay; /* num of ms for optimal next block */ + grub_int32_t fs_rps; /* disk revolutions per second */ + /* these fields can be computed from the others */ + grub_int32_t fs_bmask; /* ``blkoff'' calc of blk offsets */ + grub_int32_t fs_fmask; /* ``fragoff'' calc of frag offsets */ + grub_int32_t fs_bshift; /* ``lblkno'' calc of logical blkno */ + grub_int32_t fs_fshift; /* ``numfrags'' calc number of frags */ + /* these are configuration parameters */ + grub_int32_t fs_maxcontig; /* max number of contiguous blks */ + grub_int32_t fs_maxbpg; /* max number of blks per cyl group */ + /* these fields can be computed from the others */ + grub_int32_t fs_fragshift; /* block to frag shift */ + grub_int32_t fs_fsbtodb; /* fsbtodb and dbtofsb shift constant */ + grub_int32_t fs_sbsize; /* actual size of super block */ + grub_int32_t fs_csmask; /* csum block offset */ + grub_int32_t fs_csshift; /* csum block number */ + grub_int32_t fs_nindir; /* value of NINDIR */ + grub_int32_t fs_inopb; /* value of INOPB */ + grub_int32_t fs_nspf; /* value of NSPF */ + /* yet another configuration parameter */ + grub_int32_t fs_optim; /* optimization preference, see below */ + /* these fields are derived from the hardware */ + /* USL SVR4 compatibility */ + /* + * * USL SVR4 compatibility + * + * There was a significant divergence here between Solaris and + * SVR4 for x86. By swapping these two members in the superblock, + * we get read-only compatibility of SVR4 filesystems. Otherwise + * there would be no compatibility. This change was introduced + * during bootstrapping of Solaris on x86. By making this ifdef'ed + * on byte order, we provide ongoing compatibility across all + * platforms with the same byte order, the highest compatibility + * that can be achieved. + */ + grub_int32_t fs_state; /* file system state time stamp */ + grub_int32_t fs_si; /* summary info state - lufs only */ + grub_int32_t fs_trackskew; /* sector 0 skew, per track */ + /* unique id for this filesystem (currently unused and unmaintained) */ + /* In 4.3 Tahoe this space is used by fs_headswitch and fs_trkseek */ + /* Neither of those fields is used in the Tahoe code right now but */ + /* there could be problems if they are. */ + grub_int32_t fs_id[2]; /* file system id */ + /* sizes determined by number of cylinder groups and their sizes */ + grub_daddr32_t fs_csaddr; /* blk addr of cyl grp summary area */ + grub_int32_t fs_cssize; /* size of cyl grp summary area */ + grub_int32_t fs_cgsize; /* cylinder group size */ + /* these fields are derived from the hardware */ + grub_int32_t fs_ntrak; /* tracks per cylinder */ + grub_int32_t fs_nsect; /* sectors per track */ + grub_int32_t fs_spc; /* sectors per cylinder */ + /* this comes from the disk driver partitioning */ + grub_int32_t fs_ncyl; /* cylinders in file system */ + /* these fields can be computed from the others */ + grub_int32_t fs_cpg; /* cylinders per group */ + grub_int32_t fs_ipg; /* inodes per group */ + grub_int32_t fs_fpg; /* blocks per group * fs_frag */ + /* this data must be re-computed after crashes */ + struct csum fs_cstotal; /* cylinder summary information */ + /* these fields are cleared at mount time */ + char fs_fmod; /* super block modified flag */ + char fs_clean; /* file system state flag */ + char fs_ronly; /* mounted read-only flag */ + char fs_flags; /* largefiles flag, etc. */ + char fs_fsmnt[MAXMNTLEN]; /* name mounted on */ + /* these fields retain the current block allocation info */ + grub_int32_t fs_cgrotor; /* last cg searched */ + /* + * The following used to be fs_csp[MAXCSBUFS]. It was not + * used anywhere except in old utilities. We removed this + * in 5.6 and expect fs_u.fs_csp to be used instead. + * We no longer limit fs_cssize based on MAXCSBUFS. + */ + union { /* fs_cs (csum) info */ + grub_uint32_t fs_csp_pad[MAXCSBUFS]; + struct csum *fs_csp; + } fs_u; + grub_int32_t fs_cpc; /* cyl per cycle in postbl */ + short fs_opostbl[16][8]; /* old rotation block list head */ + grub_int32_t fs_sparecon[51]; /* reserved for future constants */ + grub_int32_t fs_version; /* minor version of MTB ufs */ + grub_int32_t fs_logbno; /* block # of embedded log */ + grub_int32_t fs_reclaim; /* reclaim open, deleted files */ + grub_int32_t fs_sparecon2; /* reserved for future constant */ + /* USL SVR4 compatibility */ + grub_int32_t fs_npsect; /* # sectors/track including spares */ + grub_quad_t fs_qbmask; /* ~fs_bmask - for use with quad size */ + grub_quad_t fs_qfmask; /* ~fs_fmask - for use with quad size */ + grub_int32_t fs_postblformat; /* fmt of positional layout tables */ + grub_int32_t fs_nrpos; /* number of rotaional positions */ + grub_int32_t fs_postbloff; /* (short) rotation block list head */ + grub_int32_t fs_rotbloff; /* (grub_uchar_t) blocks for each */ + /* rotation */ + grub_int32_t fs_magic; /* magic number */ + grub_uchar_t fs_space[1]; /* list of blocks for each rotation */ + /* actually longer */ +}; + +struct icommon { + grub_o_mode_t ic_smode; /* 0: mode and type of file */ + short ic_nlink; /* 2: number of links to file */ + grub_o_uid_t ic_suid; /* 4: owner's user id */ + grub_o_gid_t ic_sgid; /* 6: owner's group id */ + grub_uint32_t ic_sizelo; /* 8: number of bytes in file */ + grub_uint32_t ic_sizehi; /* 12: number of bytes in file */ + struct timeval32 ic_atime; /* 16: time last accessed */ + struct timeval32 ic_mtime; /* 24: time last modified */ + struct timeval32 ic_ctime; /* 32: last time inode changed */ + grub_daddr32_t ic_db[UFS_NDADDR]; /* 40: disk block addresses */ + grub_daddr32_t ic_ib[UFS_NIADDR]; /* 88: indirect blocks */ + grub_int32_t ic_flags; /* 100: cflags */ + grub_int32_t ic_blocks; /* 104: 512 byte blocks actually held */ + grub_int32_t ic_gen; /* 108: generation number */ + grub_int32_t ic_shadow; /* 112: shadow inode */ + grub_uid_t ic_uid; /* 116: long EFT version of uid */ + grub_gid_t ic_gid; /* 120: long EFT version of gid */ + grub_uint32_t ic_oeftflag; /* 124: extended attr directory ino, */ + /* 0 = none */ +}; + +struct direct { + grub_ino_t d_ino; + grub_ushort_t d_reclen; + grub_ushort_t d_namelen; + char d_name[MAXNAMELEN + 1]; +}; + +/* inode macros */ +#define INOPB(fs) ((fs)->fs_inopb) +#define itoo(fs, x) ((x) % (grub_uint32_t)INOPB(fs)) +#define itog(fs, x) ((x) / (grub_uint32_t)(fs)->fs_ipg) +#define itod(fs, x) ((grub_daddr32_t)(cgimin(fs, itog(fs, x)) + \ + (blkstofrags((fs), \ + ((x) % (grub_uint32_t)(fs)->fs_ipg / (grub_uint32_t)INOPB(fs)))))) + +/* block conversion macros */ +#define UFS_NINDIR(fs) ((fs)->fs_nindir) /* # of indirects */ +#define blkoff(fs, loc) ((int)((loc & ~(fs)->fs_bmask))) +#define lblkno(fs, loc) ((grub_int32_t)((loc) >> (fs)->fs_bshift)) +/* frag to blk */ +#define fsbtodb(fs, b) (((grub_daddr32_t)(b)) << (fs)->fs_fsbtodb) +#define blkstofrags(fs, b) ((b) << (fs)->fs_fragshift) + +/* cynlinder group macros */ +#define cgbase(fs, c) ((grub_daddr32_t)((fs)->fs_fpg * (c))) +#define cgimin(fs, c) (cgstart(fs, c) + (fs)->fs_iblkno) /* inode block */ +#define cgstart(fs, c) \ + (cgbase(fs, c) + (fs)->fs_cgoffset * ((c) & ~((fs)->fs_cgmask))) + +#endif /* !_GRUB_UFS_H */ _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxxxxxxxx http://lists.xensource.com/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |