vmfs-tools-0.2.5/0000755000175000017500000000000011733555001011421 5ustar mhmhvmfs-tools-0.2.5/imager/0000755000175000017500000000000011733555001012665 5ustar mhmhvmfs-tools-0.2.5/imager/manifest.mk0000644000175000017500000000003111733303644015022 0ustar mhmhimager_OPTIONS := noinst vmfs-tools-0.2.5/imager/imager.c0000644000175000017500000002374111733536625014317 0ustar mhmh/* * vmfs-tools - Tools to access VMFS filesystems * Copyright (C) 2009 Mike Hommey * * 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. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /* * An image file consists of a header followed by a set of sequences, each * of which starts with a byte describing the sequence. * * The header is "VMFSIMG" followed by a byte containing the format version. * Backwards compatibility is to be preserved. * * The following sequence descriptor codes are used: * 0x00: In format version >= 2, following byte is the number of 32-bit words * constituting the beginning of a 512B block, followed by that number * of 32-bit words. * In format version < 2, following 512B are a raw block. * 0x01: following chars are the number of blocks (512B) with zeroed data - 1 * in a variable-length encoding. * 0x7f: following 4 bytes is the little-endian encoded Adler-32 checksum. * */ #define FORMAT_VERSION 2 #ifdef __linux__ #include #include #endif #include #include #include #include #include #include #include #include #include #include static void die(char *fmt, ...) { va_list ap; va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); exit(1); } static void show_usage(char *prog_name) { char *name = basename(prog_name); fprintf(stderr, "Syntax: %s [-x|-r] \n",name); } static size_t do_reads(void *buf, size_t sz, size_t count) { ssize_t hlen = 0, len; while (hlen < count * sz) { len = read(0, buf, count * sz - hlen); if ((len < 0) && (errno != EINTR)) die("Read error\n"); if (len == 0) break; hlen += len; buf += len; } if ((hlen > 0) && (hlen % sz)) die("Short read\n"); return(hlen); } static size_t do_read(void *buf, size_t count) { return do_reads(buf, count, 1); } static void do_write(const void *buf, size_t count) { ssize_t hlen = 0, len; while (hlen < count) { len = write(1, buf, count-hlen); if ((len < 0) && (errno != EINTR)) die("Write error\n"); if (len == 0) break; hlen += len; buf += len; } if (hlen != count) die("Short write\n"); } #define BLK_SIZE 512 static const u_char const zero_blk[BLK_SIZE] = {0,}; #define ADLER32_MODULO 65521 static struct { uint32_t sum1, sum2; } adler32 = { 1, 0 }; static void adler32_add(const u_char *buf, size_t blks) { size_t i; if (buf == zero_blk) { i = blks; while (i >= 65536 / BLK_SIZE) { adler32.sum2 = (adler32.sum2 + 65536 * adler32.sum1) % ADLER32_MODULO; i -= 65536 / BLK_SIZE; } adler32.sum2 = (adler32.sum2 + i * BLK_SIZE * adler32.sum1) % ADLER32_MODULO; } else { uint32_t sum; while(blks--) { for (i = 0, sum = 0; i < BLK_SIZE / 16; i++) { #define adler32_step sum += (*buf++); adler32.sum2 += sum #define fourtimes(stuff) stuff; stuff; stuff; stuff fourtimes(fourtimes(adler32_step)); } adler32.sum2 += adler32.sum1 * BLK_SIZE; adler32.sum1 += sum; adler32.sum1 %= ADLER32_MODULO; adler32.sum2 %= ADLER32_MODULO; } } } static uint32_t adler32_sum() { return adler32.sum1 | (adler32.sum2 << 16); } static uint32_t do_read_number(void) { u_char num; do_read(&num, 1); if (num & 0x80) return ((uint32_t) num & 0x7f) | (do_read_number() << 7); return (uint32_t) num; } static void skip_zero_blocks(size_t blks) { off_t pos; if ((pos = lseek(1, BLK_SIZE * (off_t) blks, SEEK_CUR)) == -1) die("Seek error\n"); ftruncate(1, pos); } static void write_zero_blocks_(size_t blks) { while (blks--) do_write(zero_blk, BLK_SIZE); } static void (*write_zero_blocks)(size_t blks) = write_zero_blocks_; static void write_blocks(const u_char *buf, size_t blks) { adler32_add(buf, blks); if (buf == zero_blk) write_zero_blocks(blks); else do_write(buf, blks * BLK_SIZE); } static void do_extract_(void (*write_blocks)(const u_char *, size_t)) { u_char buf[BLK_SIZE]; u_char version; u_char desc; uint32_t num; /* Read file header */ do_read(buf, 8); if (strncmp((char *)buf, "VMFSIMG", 7)) die("extract: not a VMFS image\n"); if ((version = buf[7]) > FORMAT_VERSION) die("extract: unsupported image format\n"); while (do_read(&desc, 1)) { switch (desc) { case 0x00: if (version >= 2) { num = do_read_number() * 4; memset(&buf[num], 0, BLK_SIZE - num); } else num = BLK_SIZE; do_read(buf, num); write_blocks(buf, 1); break; case 0x01: num = do_read_number(); write_blocks(zero_blk, num + 1); break; case 0x7f: do_read(buf, 4); num = buf[0] | buf[1] << 8 | buf[2] << 16 | buf[3] << 24; if (num != adler32_sum()) die("extract: checksum mismatch\n"); break; default: die("extract: corrupted image\n"); } } } static void do_extract(void) { do_extract_(write_blocks); } static void do_write_number(uint32_t num) { u_char buf[5], *b = buf; size_t sz; do { *b = (u_char) (num & 0x7f); } while ((num >>= 7) && (*(b++) |= 0x80)); sz = b - buf + 1; do_write(buf, sz); } enum block_type { none, zero, raw, }; static enum block_type detect_block_type(const u_char *buf) { int i; if (buf == NULL) return none; for (i = 0; i < BLK_SIZE / 8; i++) if (((uint64_t *)buf)[i]) return raw; return zero; } static void end_consecutive_blocks(enum block_type type, uint32_t blks) { if (type == zero) { do_write("\1", 1); do_write_number(blks - 1); } } static void do_init_image(void) { const u_char const buf[8] = { 'V', 'M', 'F', 'S', 'I', 'M', 'G', FORMAT_VERSION }; do_write(buf, 8); } static void import_blocks(const u_char *buf, size_t blks) { static enum block_type last = none, current; static uint32_t consecutive = 0; do { current = detect_block_type(buf); if ((last != none) && (current != last)) { end_consecutive_blocks(last, consecutive); consecutive = 0; } last = current; switch (current) { case zero: if (buf == zero_blk) { if (consecutive > (uint32_t) -blks) end_consecutive_blocks(zero, (uint32_t) -1); adler32_add(zero_blk, blks); consecutive += blks; return; } adler32_add(zero_blk, 1); consecutive++; break; case raw: do { int i; for (i = BLK_SIZE / 4; i;i--) if (((uint32_t *)buf)[i - 1]) break; do_write("\0", 1); do_write_number(i); do_write(buf, i * 4); adler32_add(buf, 1); } while(0); break; case none: do { uint32_t sum = adler32_sum(); u_char b[5] = { 0x7f, sum & 0xff, (sum >> 8) & 0xff, (sum >> 16) & 0xff, (sum >> 24) & 0xff }; do_write(b, 5); } while(0); return; } buf += BLK_SIZE; } while (--blks); } static void do_import(void) { u_char buf[BLK_SIZE * 16]; size_t len; #ifdef __linux__ struct stat st; int blocksize = 0; off_t filesize = 0; if ((fstat(0, &st) == 0) && S_ISREG(st.st_mode)) { ioctl(0, FIGETBSZ, &blocksize); filesize = st.st_size; } #endif do_init_image(); #ifdef __linux__ if (blocksize) { uint32_t i, max = filesize / blocksize; for (i = 0; i < max; i++) { uint32_t block = i; if (ioctl(0, FIBMAP, &block) < 0) goto fallback; if (block) { lseek(0, (off_t) i * blocksize, SEEK_SET); len = do_reads(buf, BLK_SIZE, blocksize / BLK_SIZE); import_blocks(buf, len / BLK_SIZE); } else import_blocks(zero_blk, blocksize / BLK_SIZE); } if (filesize % blocksize) import_blocks(zero_blk, (filesize % blocksize) / BLK_SIZE); import_blocks(NULL, 0); return; } fallback: #endif while ((len = do_reads(buf, BLK_SIZE, 16))) import_blocks(buf, len / BLK_SIZE); import_blocks(NULL, 0); } static void do_reimport(void) { do_init_image(); do_extract_(import_blocks); import_blocks(NULL, 0); } static void do_verify(void) { do_extract_(adler32_add); } int main(int argc,char *argv[]) { char *arg = NULL; void (*func)(void) = do_import; struct stat st; if (argc > 1) { if (strcmp(argv[1],"-x") == 0) { func = do_extract; argc--; } else if (strcmp(argv[1],"-r") == 0) { func = do_reimport; argc--; } else if (strcmp(argv[1],"-v") == 0) { func = do_verify; argc--; } if (argc == 2) arg = argv[(func == do_import) ? 1 : 2]; } if (argc > 2) { show_usage(argv[0]); return(0); } if (arg) { int fd = open(arg, O_RDONLY); if (fd == -1) { fprintf(stderr, "Error opening %s: %s\n", arg, strerror(errno)); return(1); } dup2(fd,0); close(fd); } if ((fstat(1, &st) == 0) && S_ISREG(st.st_mode) && ((st.st_size == 0) || !(fcntl(1, F_GETFL) & O_APPEND))) write_zero_blocks = skip_zero_blocks; func(); return(0); } vmfs-tools-0.2.5/configure.mk0000644000175000017500000000200211733303644013731 0ustar mhmhinclude utils.mk __VARS := $(foreach var,$(.VARIABLES),$(if $(filter-out command line,$(origin $(var))),$(var))) prefix := /usr/local exec_prefix := $$(prefix) sbindir := $$(exec_prefix)/sbin datarootdir := $$(prefix)/share mandir := $$(datarootdir)/man # configure rules really start here $(call PKG_CONFIG_CHK,uuid,-I/usr/include/uuid,-luuid) $(call PKG_CONFIG_CHK,fuse) $(call PATH_LOOKUP,asciidoc) $(call PATH_LOOKUP,xsltproc) $(call checking,docbook.xsl) __DOCBOOK_XSL := http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl ifneq (,$(XSLTPROC)) ifneq (,$(shell $(XSLTPROC) --nonet --noout $(__DOCBOOK_XSL) 2> /dev/null && echo ok)) DOCBOOK_XSL := $(__DOCBOOK_XSL) endif endif $(call result,$(DOCBOOK_XSL)) $(call LINK_CHECK,strndup) $(call LINK_CHECK,dlopen,-ldl) ifeq (,$(HAS_DLOPEN)) $(call LINK_CHECK,dlopen) endif $(call LINK_CHECK,posix_memalign) # Generate cache file $(shell ($(foreach var,$(filter-out $(__VARS) __%,$(.VARIABLES)),echo '$(var) = $($(var))';)) > config.cache) configure: vmfs-tools-0.2.5/libreadcmd/0000755000175000017500000000000011733555001013507 5ustar mhmhvmfs-tools-0.2.5/libreadcmd/manifest.mk0000644000175000017500000000000011733303644015640 0ustar mhmhvmfs-tools-0.2.5/libreadcmd/readcmd.c0000644000175000017500000001075211733303644015263 0ustar mhmh/* * vmfs-tools - Tools to access VMFS filesystems * Copyright (C) 2009 Christophe Fillot * Copyright (C) 2009,2010 Mike Hommey * * 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. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include "readcmd.h" static const cmd_t empty_cmd = { 0, }; static char *init_readline(const char *prompt); static char *(*readline)(const char *prompt) = init_readline; static void (*add_history)(const char *string); /* local_readline() buffer allocation increment */ #define READLINE_INCR 256 char *local_readline(const char *prompt) { char *buf = NULL, *buf2; size_t len, alloc = READLINE_INCR; if (prompt) fprintf(stdout, "%s", prompt); buf = malloc(alloc); if (!buf) return NULL; buf2 = buf; do { if (!fgets(buf2, READLINE_INCR, stdin)) break; len = strlen(buf2); if ((len > 0) && (buf2[len-1] == '\n')) { buf2[--len] = 0; return buf; } len += (buf2 - buf); alloc += READLINE_INCR; if (!(buf2 = realloc(buf, alloc))) break; buf = buf2; buf2 = &buf[len]; } while (1); free(buf); return NULL; } static void local_add_history(const char *string) { } /* Argv allocation increment */ #define ARGV_INCR 16 /* Return a command after having prompted for it */ const cmd_t *readcmd(const char *prompt) { char *buf; int i; cmd_t *cmd = NULL; if (!isatty(fileno(stdin))) prompt = NULL; if (!(buf = readline(prompt))) { if (prompt) fprintf(stdout, "\n"); return NULL; } for(i=strlen(buf)-1;(i>=0)&&(buf[i]==' ');buf[i--]=0); if (buf[0]==0) { free(buf); return &empty_cmd; } add_history(buf); cmd = calloc(sizeof(cmd_t),1); cmd->buf = buf; if ((cmd->redir = index(buf, '|'))) cmd->piped = 1; else cmd->redir = index(buf, '>'); if (cmd->redir && (cmd->redir != buf) && (cmd->redir[-1] == ' ')) { char *s; for(s=cmd->redir-1;(s>=buf)&&(*s==' ');*(s--)=0); *(cmd->redir++) = 0; if (!cmd->piped && *cmd->redir == '>') { cmd->append = 1; if (*(++cmd->redir) == '>') { fprintf(stderr,"Unexpected token '>'\n"); free(cmd); free(buf); return &empty_cmd; } } while (*cmd->redir == ' ') cmd->redir++; } else cmd->redir = NULL; cmd->argv = malloc(ARGV_INCR * sizeof(char *)); cmd->argv[0] = buf; do { while (*(cmd->argv[cmd->argc]) == ' ') *(cmd->argv[cmd->argc]++) = 0; if (!(++cmd->argc % ARGV_INCR)) { char **newargv = realloc(cmd->argv, (cmd->argc + ARGV_INCR) * sizeof(char *)); if (newargv) { cmd->argv = newargv; } else { freecmd(cmd); fprintf(stderr, "Not enough memory\n"); return NULL; } } } while((cmd->argv[cmd->argc] = strchr(cmd->argv[cmd->argc - 1], ' '))); return cmd; } /* Free a command */ void freecmd(const cmd_t *cmd) { if (!cmd || cmd == &empty_cmd) return; free(cmd->argv); free(cmd->buf); free((cmd_t *)cmd); } static void *dlhandle = NULL; static char *init_readline(const char *prompt) { if (isatty(fileno(stdin))) { dlhandle = dlopen("libreadline.so", RTLD_NOW); if (!dlhandle) dlhandle = dlopen("libreadline.so.6", RTLD_NOW); if (!dlhandle) dlhandle = dlopen("libreadline.so.5", RTLD_NOW); if (dlhandle) { readline = dlsym(dlhandle, "readline"); add_history = dlsym(dlhandle, "add_history"); if (readline && add_history) goto readline; } } readline = local_readline; add_history = local_add_history; readline: return readline(prompt); } static __attribute__((destructor)) void close_readline(void) { if (dlhandle) dlclose(dlhandle); } vmfs-tools-0.2.5/libreadcmd/readcmd.h0000644000175000017500000000220711733303644015264 0ustar mhmh/* * vmfs-tools - Tools to access VMFS filesystems * Copyright (C) 2009 Christophe Fillot * Copyright (C) 2009 Mike Hommey * * 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. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /* Type describing a command */ struct _cmd { int argc; char **argv; int piped; int append; char *redir; char *buf; }; typedef struct _cmd cmd_t; /* Return a line after having prompted for it */ char *local_readline(const char *prompt); /* Return a command after having prompted for it */ const cmd_t *readcmd(const char *prompt); /* Free a command */ void freecmd(const cmd_t *cmd); vmfs-tools-0.2.5/utils.mk0000644000175000017500000000546511733303644013130 0ustar mhmh#Usage: $(call checking,message]) checking = $(shell sh -c "echo -n Checking for $(1)... " >&2) #Usage: $(call result,value) result = $(shell echo $(if $(1),yes,no) >&2) #Usage: $(call PATH_LOOKUP,name) # Sets NAME to the full path of name define _PATH_LOOKUP __path_lookup := $(call UC,$(1)) ifndef __$$(__path_lookup) $$(call checking,$(1)) $$(__path_lookup) := $$(firstword $$(wildcard $$(foreach path,$$(subst :, ,$(PATH)),$$(path)/$(1)))) __$$(__path_lookup) := 1 $$(call result,$$($$(__path_lookup))) endif endef PATH_LOOKUP = $(eval $(call _PATH_LOOKUP,$(1))) ALPHA := A B C D E F G H I J K L M N O P Q R S T U V W X Y Z _ alpha := a b c d e f g h i j k l m n o p q r s t u v w x y z - alphaALPHA := $(join $(addsuffix /,$(alpha)), $(ALPHA)) UC = $(strip $(eval __uc := $1) \ $(foreach c,$(alphaALPHA), \ $(eval __uc := \ $(subst $(subst /,,$(dir $(c))),$(notdir $(c)),$(__uc)))) \ $(__uc)) #Usage: $(call PKG_CONFIG_CHK,name,cflags,ldflags) # cflags and ldflags used in case pkg-config fails. # Sets name/LDFLAGS and name/CFLAGS define _PKG_CONFIG_CHK $$(call PATH_LOOKUP,pkg-config) $$(call checking,$(1)) $(1)/LDFLAGS := $$(if $$(PKG_CONFIG),$$(strip $$(shell $$(PKG_CONFIG) --libs $(1) 2> /dev/null || echo __)),__) $(1)/CFLAGS := $$(if $$(PKG_CONFIG),$$(strip $$(shell $$(PKG_CONFIG) --cflags $(1) 2> /dev/null || echo __)),__) ifeq (__,$$($(1)/LDFLAGS)) $(1)/LDFLAGS := $(3) endif ifeq (__,$$($(1)/CFLAGS)) $(1)/CFLAGS := $(2) endif $$(call result,$$($(1)/CFLAGS)$$($(2)/LDFLAGS)) endef PKG_CONFIG_CHK = $(eval $(call _PKG_CONFIG_CHK,$(1),$(2),$(3))) #Usage: $(call LINK_CHECK,func,ldflags) # Try to link a simple program with a call to func() with given ldflags # Sets FUNC_LDFLAGS and HAS_FUNC define _LINK_CHECK $$(call checking,$(if $(2),$(1) in $(2),$(1))) __name := $(call UC,$(1)) __$$(__name) := $$(shell echo "char $(1)(); int main(void) { $(1)(); return(0); }" > __conftest.c; $(CC) -o __conftest __conftest.c $(2) 2> /dev/null && echo yes || echo no; rm -f __conftest*) ifeq ($$(__$$(__name)),yes) HAS_$$(__name) := 1 ifneq (,$(2)) $$(__name)_LDFLAGS := $(2) endif endif $$(call result,$$(HAS_$$(__name))) endef LINK_CHECK = $(eval $(call _LINK_CHECK,$(1),$(2))) GEN_VERSION = $(shell \ (if [ -d .git ]; then \ VER=$$(git describe --match "v[0-9].*" --abbrev=0 HEAD); \ git update-index -q --refresh; \ if git diff-index --name-status $${VER} -- | grep -q -v '^A'; then \ VER=$$(git describe --match "v[0-9].*" --abbrev=4 HEAD | sed s/-/./g); \ fi ; \ if [ -z "$${VER}" ]; then \ VER="v0.0.0.$$(git rev-list HEAD | wc -l).$$(git rev-parse --short=4 HEAD)"; \ fi; \ test -z "$$(git diff-index --name-only HEAD --)" || VER=$${VER}-dirty; \ else \ VER=$(patsubst %,%-patched,$(or $(VERSION:%-patched=%),v0.0.0)); \ fi; \ echo $$VER) 2> /dev/null) vmfs-tools-0.2.5/test.img0000644000175000017500000011424611733543306013113 0ustar mhmhVMFSIMG) Sun VOL1 V1.0vmhba0:C0:T0:L04KDI-{t!(~ahUah%@49edf842-dcb35d37-b383-0021280083cfBI7]܃!(~ahK?ahi! 107^/DI(e!(totoDIBI7]܃!( ͫ0ͫ0ͫ0ͫ0ͫ0ͫ 0ͫ 0ͫ0ͫ0ͫ0ͫ0ͫ0ͫ0ͫ0ͫ0ͫ0ͫ 0ͫ"0ͫ$0ͫ&0ͫ(0ͫ*0ͫ,0ͫ.0ͫ00ͫ20ͫ40ͫ60ͫ80ͫ:0ͫ<0ͫ>0ͫ@0ͫB0ͫD0ͫF0ͫH0ͫJ0ͫL0ͫN0ͫP0ͫR0ͫT0ͫV0ͫX0ͫZ0ͫ\0ͫ^0ͫ`0ͫb0ͫd0ͫf0ͫh0ͫj0ͫl0ͫn0ͫp0ͫr0ͫt0ͫv0ͫx0ͫz0ͫ|0ͫ~0ͫ0ͫ0ͫ0ͫ0ͫ0ͫ0ͫ0ͫ0ͫ0ͫ0ͫ0ͫ0ͫ0ͫ0ͫ0ͫ0ͫ0ͫ0ͫ0ͫ0ͫ0ͫ0ͫ0ͫ0ͫ0ͫ0ͫ0ͫ0ͫ0ͫ0ͫ0ͫ0ͫ0ͫ0ͫ0ͫ0ͫ0ͫ0ͫ0ͫ0ͫ0ͫ0ͫ0ͫ0ͫ0ͫ0ͫ0ͫ0ͫ0ͫ0ͫ0ͫ0ͫ0ͫ0ͫ0ͫ0ͫ0ͫ0ͫ0ͫ0ͫ0ͫ0ͫ0ͫ0ͫ1ͫ1ͫ1ͫ1ͫ1ͫ 1ͫ 1ͫ1ͫ1ͫ1ͫ1ͫ1ͫ1ͫ1ͫ1ͫ1ͫ 1ͫ"1ͫ$1ͫ&1ͫ(1ͫ*1ͫ,1ͫ.1ͫ01ͫ21ͫ41ͫ61ͫ81ͫ:1ͫ<1ͫ>1ͫ@1ͫB1ͫD1ͫF1ͫH1ͫJ1ͫL1ͫN1ͫP1ͫR1ͫT1ͫV1ͫX1ͫZ1ͫ\1ͫ^1ͫ`1ͫb1ͫd1ͫf1ͫh1ͫj1ͫl1ͫn1ͫp1ͫr1ͫt1ͫv1ͫx1ͫz1ͫ|1ͫ~1ͫ1ͫ1ͫ1ͫ1ͫ1ͫ1ͫ1ͫ1ͫ1ͫ1ͫ1ͫ1ͫ1ͫ1ͫ1ͫ1ͫ1ͫ1ͫ1ͫ1ͫ1ͫ1ͫ1ͫ1ͫ1ͫ1ͫ1ͫ1ͫ1ͫ1ͫ1ͫ1ͫ1ͫ1ͫ1ͫ1ͫ1ͫ1ͫ1ͫ1ͫ1ͫ1ͫ1ͫ1ͫ1ͫ1ͫ1ͫ1ͫ1ͫ1ͫ1ͫ1ͫ1ͫ1ͫ1ͫ1ͫ1ͫ1ͫ1ͫ1ͫ1ͫ1ͫ1ͫ1ͫ2ͫ2ͫ2ͫ2ͫ2ͫ 2ͫ 2ͫ2ͫ2ͫ2ͫ2ͫ2ͫ2ͫ2ͫ2ͫ2ͫ 2ͫ"2ͫ$2ͫ&2ͫ(2ͫ*2ͫ,2ͫ.2ͫ02ͫ22ͫ42ͫ62ͫ82ͫ:2ͫ<2ͫ>2ͫ@2ͫB2ͫD2ͫF2ͫH2ͫJ2ͫL2ͫN2ͫP2ͫR2ͫT2ͫV2ͫX2ͫZ2ͫ\2ͫ^2ͫ`2ͫb2ͫd2ͫf2ͫh2ͫj2ͫl2ͫn2ͫp2ͫr2ͫt2ͫv2ͫx2ͫz2ͫ|2ͫ~2ͫ2ͫ2ͫ2ͫ2ͫ2ͫ2ͫ2ͫ2ͫ2ͫ2ͫ2ͫ2ͫ2ͫ2ͫ2ͫ2ͫ2ͫ2ͫ2ͫ2ͫ2ͫ2ͫ2ͫ2ͫ2ͫ2ͫ2ͫ2ͫ2ͫ2ͫ2ͫ2ͫ2ͫ2ͫ2ͫ2ͫ2ͫ2ͫ2ͫ2ͫ2ͫ2ͫ2ͫ2ͫ2ͫ2ͫ2ͫ2ͫ2ͫ2ͫ2ͫ2ͫ2ͫ2ͫ2ͫ2ͫ2ͫ2ͫ2ͫ2ͫ2ͫ2ͫ2ͫ2ͫ3ͫ3ͫ3ͫ3ͫ3ͫ 3ͫ 3ͫ3ͫ3ͫ3ͫ3ͫ3ͫ3ͫ3ͫ3ͫ3ͫ 3ͫ"3ͫ$3ͫ&3ͫ(3ͫ*3ͫ,3ͫ.3ͫ03ͫ23ͫ43ͫ63ͫ83ͫ:3ͫ<3ͫ>3ͫ@3ͫB3ͫD3ͫF3ͫH3ͫJ3ͫL3ͫN3ͫP3ͫR3ͫT3ͫV3ͫX3ͫZ3ͫ\3ͫ^3ͫ`3ͫb3ͫd3ͫf3ͫh3ͫj3ͫl3ͫn3ͫp3ͫr3ͫt3ͫv3ͫx3ͫz3ͫ|3ͫ~3ͫ3ͫ3ͫ3ͫ3ͫ3ͫ3ͫ3ͫ3ͫ3ͫ3ͫ3ͫ3ͫ3ͫ3ͫ3ͫ3ͫ3ͫ3ͫ3ͫ3ͫ3ͫ3ͫ3ͫ3ͫ3ͫ3ͫ3ͫ3ͫ3ͫ3ͫ3ͫ3ͫ3ͫ3ͫ3ͫ3ͫ3ͫ3ͫ3ͫ3ͫ3ͫ3ͫ3ͫ3ͫ3ͫ3ͫ3ͫ3ͫ3ͫ3ͫ3ͫ3ͫ3ͫ3ͫ3ͫ3ͫ3ͫ3ͫ3ͫ3ͫ3ͫ3ͫ3ͫ3ͫ4ͫ4ͫ4ͫ4ͫ4ͫ 4ͫ 4ͫ4ͫ4ͫ4ͫ4ͫ4ͫ4ͫ4ͫ4ͫ4ͫ 4ͫ"4ͫ$4ͫ&4ͫ(4ͫ*4ͫ,4ͫ.4ͫ04ͫ24ͫ44ͫ64ͫ84ͫ:4ͫ<4ͫ>4ͫ@4ͫB4ͫD4ͫF4ͫH4ͫJ4ͫL4ͫN4ͫP4ͫR4ͫT4ͫV4ͫX4ͫZ4ͫ\4ͫ^4ͫ`4ͫb4ͫd4ͫf4ͫh4ͫj4ͫl4ͫn4ͫp4ͫr4ͫt4ͫv4ͫx4ͫz4ͫ|4ͫ~4ͫ4ͫ4ͫ4ͫ4ͫ4ͫ4ͫ4ͫ4ͫ4ͫ4ͫ4ͫ4ͫ4ͫ4ͫ4ͫ4ͫ4ͫ4ͫ4ͫ4ͫ4ͫ4ͫ4ͫ4ͫ4ͫ4ͫ4ͫ4ͫ4ͫ4ͫ4ͫ4ͫ4ͫ4ͫ4ͫ4ͫ4ͫ4ͫ4ͫ4ͫ4ͫ4ͫ4ͫ4ͫ4ͫ4ͫ4ͫ4ͫ4ͫ4ͫ4ͫ4ͫ4ͫ4ͫ4ͫ4ͫ4ͫ4ͫ4ͫ4ͫ4ͫ4ͫ4ͫ4ͫ5ͫ5ͫ5ͫ5ͫ5ͫ 5ͫ 5ͫ5ͫ5ͫ5ͫ5ͫ5ͫ5ͫ5ͫ5ͫ5ͫ 5ͫ"5ͫ$5ͫ&5ͫ(5ͫ*5ͫ,5ͫ.5ͫ05ͫ25ͫ45ͫ65ͫ85ͫ:5ͫ<5ͫ>5ͫ@5ͫB5ͫD5ͫF5ͫH5ͫJ5ͫL5ͫN5ͫP5ͫR5ͫT5ͫV5ͫX5ͫZ5ͫ\5ͫ^5ͫ`5ͫb5ͫd5ͫf5ͫh5ͫj5ͫl5ͫn5ͫp5ͫr5ͫt5ͫv5ͫx5ͫz5ͫ|5ͫ~5ͫ5ͫ5ͫ5ͫ5ͫ5ͫ5ͫ5ͫ5ͫ5ͫ5ͫ5ͫ5ͫ5ͫ5ͫ5ͫ5ͫ5ͫ5ͫ5ͫ5ͫ5ͫ5ͫ5ͫ5ͫ5ͫ5ͫ5ͫ5ͫ5ͫ5ͫ5ͫ5ͫ5ͫ5ͫ5ͫ5ͫ5ͫ5ͫ5ͫ5ͫ5ͫ5ͫ5ͫ5ͫ5ͫ5ͫ5ͫ5ͫ5ͫ5ͫ5ͫ5ͫ5ͫ5ͫ5ͫ5ͫ5ͫ5ͫ5ͫ5ͫ5ͫ5ͫ5ͫ5ͫ6ͫ6ͫ6ͫ6ͫ6ͫ 6ͫ 6ͫ6ͫ6ͫ6ͫ6ͫ6ͫ6ͫ6ͫ6ͫ6ͫ 6ͫ"6ͫ$6ͫ&6ͫ(6ͫ*6ͫ,6ͫ.6ͫ06ͫ26ͫ46ͫ66ͫ86ͫ:6ͫ<6ͫ>6ͫ@6ͫB6ͫD6ͫF6ͫH6ͫJ6ͫL6ͫN6ͫP6ͫR6ͫT6ͫV6ͫX6ͫZ6ͫ\6ͫ^6ͫ`6ͫb6ͫd6ͫf6ͫh6ͫj6ͫl6ͫn6ͫp6ͫr6ͫt6ͫv6ͫx6ͫz6ͫ|6ͫ~6ͫ6ͫ6ͫ6ͫ6ͫ6ͫ6ͫ6ͫ6ͫ6ͫ6ͫ6ͫ6ͫ6ͫ6ͫ6ͫ6ͫ6ͫ6ͫ6ͫ6ͫ6ͫ6ͫ6ͫ6ͫ6ͫ6ͫ6ͫ6ͫ6ͫ6ͫ6ͫ6ͫ6ͫ6ͫ6ͫ6ͫ6ͫ6ͫ6ͫ6ͫ6ͫ6ͫ6ͫ6ͫ6ͫ6ͫ6ͫ6ͫ6ͫ6ͫ6ͫ6ͫ6ͫ6ͫ6ͫ6ͫ6ͫ6ͫ6ͫ6ͫ6ͫ6ͫ6ͫ6ͫ7ͫ7ͫ7ͫ7ͫ7ͫ 7ͫ 7ͫ7ͫ7ͫ7ͫ7ͫ7ͫ7ͫ7ͫ7ͫ7ͫ 7ͫ"7ͫ$7ͫ&7ͫ(7ͫ*7ͫ,7ͫ.7ͫ07ͫ27ͫ47ͫ67ͫ87ͫ:7ͫ<7ͫ>7ͫ@7ͫB7ͫD7ͫF7ͫH7ͫJ7ͫL7ͫN7ͫP7ͫR7ͫT7ͫV7ͫX7ͫZ7ͫ\7ͫ^7ͫ`7ͫb7ͫd7ͫf7ͫh7ͫj7ͫl7ͫn7ͫp7ͫr7ͫt7ͫv7ͫx7ͫz7ͫ|7ͫ~7ͫ7ͫ7ͫ7ͫ7ͫ7ͫ7ͫ7ͫ7ͫ7ͫ7ͫ7ͫ7ͫ7ͫ7ͫ7ͫ7ͫ7ͫ7ͫ7ͫ7ͫ7ͫ7ͫ7ͫ7ͫ7ͫ7ͫ7ͫ7ͫ7ͫ7ͫ7ͫ7ͫ7ͫ7ͫ7ͫ7ͫ7ͫ7ͫ7ͫ7ͫ7ͫ7ͫ7ͫ7ͫ7ͫ7ͫ7ͫ7ͫ7ͫ7ͫ7ͫ7ͫ7ͫ7ͫ7ͫ7ͫ7ͫ7ͫ7ͫ7ͫ7ͫ7ͫ7ͫ7ͫ8ͫ8ͫ8ͫ8ͫ8ͫ 8ͫ 8ͫ8ͫ8ͫ8ͫ8ͫ8ͫ8ͫ8ͫ8ͫ8ͫ 8ͫ"8ͫ$8ͫ&8ͫ(8ͫ*8ͫ,8ͫ.8ͫ08ͫ28ͫ48ͫ68ͫ88ͫ:8ͫ<8ͫ>8ͫ@8ͫB8ͫD8ͫF8ͫH8ͫJ8ͫL8ͫN8ͫP8ͫR8ͫT8ͫV8ͫX8ͫZ8ͫ\8ͫ^8ͫ`8ͫb8ͫd8ͫf8ͫh8ͫj8ͫl8ͫn8ͫp8ͫr8ͫt8ͫv8ͫx8ͫz8ͫ|8ͫ~8ͫ8ͫ8ͫ8ͫ8ͫ8ͫ8ͫ8ͫ8ͫ8ͫ8ͫ8ͫ8ͫ8ͫ8ͫ8ͫ8ͫ8ͫ8ͫ8ͫ8ͫ8ͫ8ͫ8ͫ8ͫ8ͫ8ͫ8ͫ8ͫ8ͫ8ͫ8ͫ8ͫ8ͫ8ͫ8ͫ8ͫ8ͫ8ͫ8ͫ8ͫ8ͫ8ͫ8ͫ8ͫ8ͫ8ͫ8ͫ8ͫ8ͫ8ͫ8ͫ8ͫ8ͫ8ͫ8ͫ8ͫ8ͫ8ͫ8ͫ8ͫ8ͫ8ͫ8ͫ8ͫ9ͫ9ͫ9ͫ9ͫ9ͫ 9ͫ 9ͫ9ͫ9ͫ9ͫ9ͫ9ͫ9ͫ9ͫ9ͫ9ͫ 9ͫ"9ͫ$9ͫ&9ͫ(9ͫ*9ͫ,9ͫ.9ͫ09ͫ29ͫ49ͫ69ͫ89ͫ:9ͫ<9ͫ>9ͫ@9ͫB9ͫD9ͫF9ͫH9ͫJ9ͫL9ͫN9ͫP9ͫR9ͫT9ͫV9ͫX9ͫZ9ͫ\9ͫ^9ͫ`9ͫb9ͫd9ͫf9ͫh9ͫj9ͫl9ͫn9ͫp9ͫr9ͫt9ͫv9ͫx9ͫz9ͫ|9ͫ~9ͫ9ͫ9ͫ9ͫ9ͫ9ͫ9ͫ9ͫ9ͫ9ͫ9ͫ9ͫ9ͫ9ͫ9ͫ9ͫ9ͫ9ͫ9ͫ9ͫ9ͫ9ͫ9ͫ9ͫ9ͫ9ͫ9ͫ9ͫ9ͫ9ͫ9ͫ9ͫ9ͫ9ͫ9ͫ9ͫ9ͫ9ͫ9ͫ9ͫ9ͫ9ͫ9ͫ9ͫ9ͫ9ͫ9ͫ9ͫ9ͫ9ͫ9ͫ9ͫ9ͫ9ͫ9ͫ9ͫ9ͫ9ͫ9ͫ9ͫ9ͫ9ͫ9ͫ9ͫ9ͫ:ͫ:ͫ:ͫ:ͫ:ͫ :ͫ :ͫ:ͫ:ͫ:ͫ:ͫ:ͫ:ͫ:ͫ:ͫ:ͫ :ͫ":ͫ$:ͫ&:ͫ(:ͫ*:ͫ,:ͫ.:ͫ0:ͫ2:ͫ4:ͫ6:ͫ8:ͫ::ͫ<:ͫ>:ͫ@:ͫB:ͫD:ͫF:ͫH:ͫJ:ͫL:ͫN:ͫP:ͫR:ͫT:ͫV:ͫX:ͫZ:ͫ\:ͫ^:ͫ`:ͫb:ͫd:ͫf:ͫh:ͫj:ͫl:ͫn:ͫp:ͫr:ͫt:ͫv:ͫx:ͫz:ͫ|:ͫ~:ͫ:ͫ:ͫ:ͫ:ͫ:ͫ:ͫ:ͫ:ͫ:ͫ:ͫ:ͫ:ͫ:ͫ:ͫ:ͫ:ͫ:ͫ:ͫ:ͫ:ͫ:ͫ:ͫ:ͫ:ͫ:ͫ:ͫ:ͫ:ͫ:ͫ:ͫ:ͫ:ͫ:ͫ:ͫ:ͫ:ͫ:ͫ:ͫ:ͫ:ͫ:ͫ:ͫ:ͫ:ͫ:ͫ:ͫ:ͫ:ͫ:ͫ:ͫ:ͫ:ͫ:ͫ:ͫ:ͫ:ͫ:ͫ:ͫ:ͫ:ͫ:ͫ:ͫ:ͫ:ͫ;ͫ;ͫ;ͫ;ͫ;ͫ ;ͫ ;ͫ;ͫ;ͫ;ͫ;ͫ;ͫ;ͫ;ͫ;ͫ;ͫ ;ͫ";ͫ$;ͫ&;ͫ(;ͫ*;ͫ,;ͫ.;ͫ0;ͫ2;ͫ4;ͫ6;ͫ8;ͫ:;ͫ<;ͫ>;ͫ@;ͫB;ͫD;ͫF;ͫH;ͫJ;ͫL;ͫN;ͫP;ͫR;ͫT;ͫV;ͫX;ͫZ;ͫ\;ͫ^;ͫ`;ͫb;ͫd;ͫf;ͫh;ͫj;ͫl;ͫn;ͫp;ͫr;ͫt;ͫv;ͫx;ͫz;ͫ|;ͫ~;ͫ;ͫ;ͫ;ͫ;ͫ;ͫ;ͫ;ͫ;ͫ;ͫ;ͫ;ͫ;ͫ;ͫ;ͫ;ͫ;ͫ;ͫ;ͫ;ͫ;ͫ;ͫ;ͫ;ͫ;ͫ;ͫ;ͫ;ͫ;ͫ;ͫ;ͫ;ͫ;ͫ;ͫ;ͫ;ͫ;ͫ;ͫ;ͫ;ͫ;ͫ;ͫ;ͫ;ͫ;ͫ;ͫ;ͫ;ͫ;ͫ;ͫ;ͫ;ͫ;ͫ;ͫ;ͫ;ͫ;ͫ;ͫ;ͫ;ͫ;ͫ;ͫ;ͫ;ͫ;ͫ<ͫ<ͫ<ͫ<ͫ<ͫ <ͫ <ͫ<ͫ<ͫ<ͫ<ͫ<ͫ<ͫ<ͫ<ͫ<ͫ <ͫ"<ͫ$<ͫ&<ͫ(<ͫ*<ͫ,<ͫ.<ͫ0<ͫ2<ͫ4<ͫ6<ͫ8<ͫ:<ͫ<<ͫ><ͫ@<ͫB<ͫD<ͫF<ͫH<ͫJ<ͫL<ͫN<ͫP<ͫR<ͫT<ͫV<ͫX<ͫZ<ͫ\<ͫ^<ͫ`<ͫb<ͫd<ͫf<ͫh<ͫj<ͫl<ͫn<ͫp<ͫr<ͫt<ͫv<ͫx<ͫz<ͫ|<ͫ~<ͫ<ͫ<ͫ<ͫ<ͫ<ͫ<ͫ<ͫ<ͫ<ͫ<ͫ<ͫ<ͫ<ͫ<ͫ<ͫ<ͫ<ͫ<ͫ<ͫ<ͫ<ͫ<ͫ<ͫ<ͫ<ͫ<ͫ<ͫ<ͫ<ͫ<ͫ<ͫ<ͫ<ͫ<ͫ<ͫ<ͫ<ͫ<ͫ<ͫ<ͫ<ͫ<ͫ<ͫ<ͫ<ͫ<ͫ<ͫ<ͫ<ͫ<ͫ<ͫ<ͫ<ͫ<ͫ<ͫ<ͫ<ͫ<ͫ<ͫ<ͫ<ͫ<ͫ<ͫ<ͫ=ͫ=ͫ=ͫ=ͫ=ͫ =ͫ =ͫ=ͫ=ͫ=ͫ=ͫ=ͫ=ͫ=ͫ=ͫ=ͫ =ͫ"=ͫ$=ͫ&=ͫ(=ͫ*=ͫ,=ͫ.=ͫ0=ͫ2=ͫ4=ͫ6=ͫ8=ͫ:=ͫ<=ͫ>=ͫ@=ͫB=ͫD=ͫF=ͫH=ͫJ=ͫL=ͫN=ͫP=ͫR=ͫT=ͫV=ͫX=ͫZ=ͫ\=ͫ^=ͫ`=ͫb=ͫd=ͫf=ͫh=ͫj=ͫl=ͫn=ͫp=ͫr=ͫt=ͫv=ͫx=ͫz=ͫ|=ͫ~=ͫ=ͫ=ͫ=ͫ=ͫ=ͫ=ͫ=ͫ=ͫ=ͫ=ͫ=ͫ=ͫ=ͫ=ͫ=ͫ=ͫ=ͫ=ͫ=ͫ=ͫ=ͫ=ͫ=ͫ=ͫ=ͫ=ͫ=ͫ=ͫ=ͫ=ͫ=ͫ=ͫ=ͫ=ͫ=ͫ=ͫ=ͫ=ͫ=ͫ=ͫ=ͫ=ͫ=ͫ=ͫ=ͫ=ͫ=ͫ=ͫ=ͫ=ͫ=ͫ=ͫ=ͫ=ͫ=ͫ=ͫ=ͫ=ͫ=ͫ=ͫ=ͫ=ͫ=ͫ=ͫ>ͫ>ͫ>ͫ>bͫ>ͫ >ͫ >ͫ>ͫ>ͫ>ͫ>ͫ>ͫ>ͫ>ͫ>ͫ>ͫ >ͫ">ͫ$>ͫ&>ͫ(>ͫ*>ͫ,>ͫ.>ͫ0>ͫ2>ͫ4>ͫ6>ͫ8>ͫ:>ͫ<>ͫ>>ͫ@>ͫB>ͫD>ͫF>ͫH>ͫJ>ͫL>ͫN>ͫP>ͫR>ͫT>ͫV>ͫX>ͫZ>ͫ\>ͫ^>ͫ`>ͫb>ͫd>ͫf>ͫh>ͫj>ͫl>ͫn>ͫp>ͫr>ͫt>ͫv>ͫx>ͫz>ͫ|>ͫ~>ͫ>ͫ>ͫ>ͫ>ͫ>ͫ>ͫ>ͫ>ͫ>ͫ>ͫ>ͫ>ͫ>ͫ>ͫ>ͫ>ͫ>ͫ>ͫ>ͫ>ͫ>ͫ>ͫ>ͫ>ͫ>ͫ>ͫ>ͫ>ͫ>ͫ>ͫ>ͫ>ͫ>ͫ>ͫ>ͫ>ͫ>ͫ>ͫ>ͫ>ͫ>ͫ>ͫ>ͫ>ͫ>ͫ>ͫ>ͫ>ͫ>ͫ>ͫ>ͫ>ͫ>ͫ>ͫ>ͫ>ͫ>ͫ>ͫ>ͫ>ͫ>ͫ>ͫ>ͫ>ͫ?ͫ?ͫ?ͫ?ͫ?ͫ ?ͫ ?ͫ?ͫ?ͫ?ͫ?ͫ?ͫ?ͫ?ͫ?ͫ?ͫ ?ͫ"?ͫ$?ͫ&?ͫ(?ͫ*?ͫ,?ͫ.?ͫ0?ͫ2?ͫ4?ͫ6?ͫ8?ͫ:?ͫ?ͫ@?ͫB?ͫD?ͫF?ͫH?ͫJ?ͫL?ͫN?ͫP?ͫR?ͫT?ͫV?ͫX?ͫZ?ͫ\?ͫ^?ͫ`?ͫb?ͫd?ͫf?ͫh?ͫj?ͫl?ͫn?ͫp?ͫr?ͫt?ͫv?ͫx?ͫz?ͫ|?ͫ~?ͫ?ͫ?ͫ?ͫ?ͫ?ͫ?ͫ?ͫ?ͫ?ͫ?ͫ?ͫ?ͫ?ͫ?ͫ?ͫ?ͫ?ͫ?ͫ?ͫ?ͫ?ͫ?ͫ?ͫ?ͫ?ͫ?ͫ?ͫ?ͫ?ͫ?ͫ?ͫ?ͫ?ͫ?ͫ?ͫ?ͫ?ͫ?ͫ?ͫ?ͫ?ͫ?ͫ?ͫ?ͫ?ͫ?ͫ?ͫ?ͫ?ͫ?ͫ?ͫ?ͫ?ͫ?ͫ?ͫ?ͫ?ͫ?ͫ?ͫ?ͫ?ͫ?ͫ?ͫ?  ~Au Au Au  Au Au Au A> WW Au /A> 7R4IIIAAԢ~@GIDIGIA FIEIFIAӖGIFIFIAqܢAHIGIGIAAAAAAAA A  A  A  A  A AAAAA @@HIHIHIAG> pH IIIdG> H@IIIG>  IIIeAeeefAfffgAggghAhhhiAiiijAjjjkAkkklAlllmAmmmnAnnnoAooopApppqAqqqrArrrsAssstAtttuAuuuvAvvvwAwwwxAxxxyAyyyzAzzz{A{{{|A|||}A}}}~A~~~AAAAAAHAAAAAAAAAAAAAAAAAdAdAG> ߽pIIIG> ; III!a!a!a!a!a!a!a!a!a!a!a!a!a!a!a!a!a!a!a!a!a!a!a!a!a!a!a!a!a!a!a!aH!a!a!a!a!a!a!a!a!a!a!a!a!a!a!a!a!a!aG> #@vIII G> 0 IIIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHAAAAAAAAAAAAAAAAAAG> &Ǎ{III0n...@.fbb.sf.fdc.sf|.pbc.sf.sbc.sf@.vh.sfthin-flat.vmdkd@thin.vmdkthick-flat.vmdkthick.vmdkszeroedthick-flat.vmdk@zeroedthick.vmdkeagerzeroedthick-flat.vmdkeagerzeroedthick.vmdk ~aa Iaa a> 4b a> #a> hTWa> %W  ~qâ ~ݮݮݮ ݮݮݮݮݮ ݮ$ݮ (ݮ ,ݮ 0ݮ 4ݮ 8ݮ<ݮ@ݮDݮHݮLޮPޮTޮXޮ\ޮ`ޮdޮhޮlޮpޮtޮx> W |ޮc# Disk DescriptorFile version=1 CID=dab8f8df parentCID=ffffffff createType="vmfs" # Extent description RW 409600 VMFS "thin-flat.vmdk" # The Disk Data Base #DDB ddb.virtualHWVersion = "4" ddb.uuid = "60 00 C2 90 9c dd 83 ba-fb ce 1b d2 b8 5a 40 6b" ddb.geometry.cylinders = "200" ddb.geometry.heads = "64" ddb.geometry.sectors = "32" ddb.adapterType = "buslogic" ddb.thinProvisioned = "1" ~\# Disk DescriptorFile version=1 CID=05a15a72 parentCID=ffffffff createType="vmfs" # Extent description RW 409600 VMFS "thick-flat.vmdk" # The Disk Data Base #DDB ddb.virtualHWVersion = "4" ddb.uuid = "60 00 C2 94 dd 7e ff 9f-37 d5 f3 b7 a9 34 c6 3a" ddb.geometry.cylinders = "200" ddb.geometry.heads = "64" ddb.geometry.sectors = "32" ddb.adapterType = "buslogic" ~^# Disk DescriptorFile version=1 CID=501e91e0 parentCID=ffffffff createType="vmfs" # Extent description RW 409600 VMFS "zeroedthick-flat.vmdk" # The Disk Data Base #DDB ddb.virtualHWVersion = "4" ddb.uuid = "60 00 C2 94 48 26 0d 35-8e 25 14 37 3d 3d ec f0" ddb.geometry.cylinders = "200" ddb.geometry.heads = "64" ddb.geometry.sectors = "32" ddb.adapterType = "buslogic" ~_# Disk DescriptorFile version=1 CID=d425fe82 parentCID=ffffffff createType="vmfs" # Extent description RW 409600 VMFS "eagerzeroedthick-flat.vmdk" # The Disk Data Base #DDB ddb.virtualHWVersion = "4" ddb.adapterType = "buslogic" ddb.geometry.sectors = "32" ddb.geometry.heads = "64" ddb.geometry.cylinders = "200" ddb.uuid = "60 00 C2 96 e5 a7 01 d6-67 1c f5 95 60 c2 87 06"  !"#$%&'()*+,-./0ā1ȁ2́3Ё4ԁ5؁6܁789:;<=>?-nG{III]0vPzeroedthick-flat.vmdk_@zeroedthick.vmdkeagerzeroedthick-flat.vmdkeagerzeroedthick.vmdkA4IuIIAa1vmfs-tools-0.2.5/README0000644000175000017500000000430311733303644012305 0ustar mhmhvmfs-tools - Tools to access VMFS filesystems ============================================= Originally loosely based on http://code.google.com/p/vmfs/ from fluidOps, this set of tools has since evolved to handle more features from VMFS, such as extents, and allows to access VMFS through the standard Linux VFS with the help of the FUSE framework. While it is still work in progress and is not destined for production use yet, it can be of some help for some people. Build and install instructions ------------------------------ To get a full build of vmfs-tools, you need the following prerequisites: - gcc - GNU make - libuuid's development files - pkg-config - libfuse's development files - asciidoc - xsltproc - docbook-xsl From the above list, only the first three are strictly required. The lack of libfuse's development files will result in the vmfs-fuse program not being built. The lack of asciidoc, xsltproc or docbook-xsl will result in no manual pages (though you can still look at the .txt files within the source tarball). Building vmfs-tools should be as simple as running `make' or `gmake`, depending on how GNU make's binary is named on your system. To install vmfs-tools, just run `make install' (or `gmake install'). The install location for the binaries is $prefix/sbin, $prefix/share/man for the manual pages, where $prefix is /usr/local by default. If you wish to install to some other place, you can override $prefix with the command `./configure --prefix=/some/where'. Supported platforms ------------------- vmfs-tools has been verified to build on GNU/Linux, FreeBSD 7.2, Opensolaris 2009.06 and Cygwin 1.5.25. On FreeBSD 7.2, you will need to install e2fsprogs-libuuid and pkg-config so that the system uuid.h header is not used: it provides an incompatible definition of the uuid_t type. On Opensolaris, if you use the gcc-4.3.2 package instead of SUNWgcc, you need to set EXTRA_LDFLAGS to -L/lib. This can be done with the command `make EXTRA_LDFLAGS=-L/lib'. (you may need to use `gmake' instead of `make', depending on your system) On Cygwin, you need to EXTRA_LDFLAGS to -L/usr/lib/e2fsprogs to get libuuid from there. This can be done with the command `make EXTRA_LDFLAGS=-L/usr/lib/e2fsprogs'. vmfs-tools-0.2.5/configure0000755000175000017500000000102711733303644013334 0ustar mhmh#!/bin/sh if type gmake > /dev/null 2>&1; then MAKE=gmake elif type make > /dev/null 2>&1 && make -v 2>&1 | grep GNU > /dev/null; then MAKE=make else echo "Can't find GNU make" >&2 exit 1 fi MAKEOPTS= for opt; do case "$opt" in --prefix=* | --exec-prefix=* | --sbindir=* \ | --datarootdir=* | --mandir=*) name=`echo "$opt" | sed 's/^--\([^=]*\)=.*$/\1/' | tr - _` value=`echo "$opt" | sed 's/^--[^=]*=//'` MAKEOPTS="$MAKEOPTS $name='$value'" ;; esac done rm -f config.cache exec $MAKE config.cache ${MAKEOPTS} vmfs-tools-0.2.5/debugvmfs/0000755000175000017500000000000011733555001013403 5ustar mhmhvmfs-tools-0.2.5/debugvmfs/debugvmfs.txt0000644000175000017500000001722711733303644016143 0ustar mhmhdebugvmfs(8) ============ NAME ---- debugvmfs - VMFS file system debugger SYNOPSIS -------- *debugvmfs* 'VOLUME'... 'COMMAND' [ 'OPTIONS' ] DESCRIPTION ----------- The *debugvmfs* program allows to display various informations about VMFS file systems, and to access data within the file systems. The 'VOLUME' to be opened can be either a block device or an image file. When the VMFS spreads accross several extents, all extents must be given. Please note that most commands are still likely to change in future versions. SPECIFYING FILES ---------------- Some commands take a 'filespec' as an argument. A 'filespec' may take either of the following forms: - A path name, relative to the root of the filesystem if it starts with \'/', or relative to the current working directory otherwise (interactive mode only). - An inode number, enclosed between angle brackets, e.g. \'<4>', \'<0x1400004>' COMMANDS -------- *cat* 'filespec' [ ... ]:: Outputs the content of the given files from the VMFS. *ls* [ *-l* ] 'filespec':: Lists files contained at the given location within the VMFS. + With *-l*, gives some more information, much like the output from *ls*(1) when given the *-l* option. *truncate* 'filespec' 'length':: Truncate the file to the specified length. R/W support must be enabled. *chmod* 'filespec' 'mode':: Change file permissions to the given mode. *df*:: Outputs information about file system size. *get_file_block* 'filespec' 'position':: Get file block corresponding to position in the specified file. *check_vol_bitmaps*:: Checks volume bitmaps consistency. *show_heartbeats*:: Outputs active heartbeats on the file system. *read_block* 'block_id' [ ... ]:: Outputs content within the specified block_id in binary *get_block_status* 'block_id':: Get status (allocated or free) of the specified block_id. *alloc_block_fixed* 'block_id':: Allocate the specified block_id. R/W support must be enabled. *alloc_block* 'block_type':: Allocate a block of the specified type. R/W support must be enabled. + Allowed block types are: 1 (File Block), 2 (Sub-Block), 3 (Pointer Block) and 4 (File Descriptor / Inode). *free_block* 'block_id':: Free the specified block_id. R/W support must be enabled. + Warning: can cause damage since no heartbeat is used at this time and a block used by a file can be freed. *show*:: Display value(s) of the given variable. See the *VARIABLES* section for more details. When no variable is given, it lists the top-level properties. *shell*:: Starts an interactive session. All of the above commands can be executed from within the interactive session. + The current working directory can be changed with the *cd* command, followed by a filespec. + The following output redirections are supported within the shell: + - 'cmd' > 'output' + Puts the output of 'cmd' in the 'output' file. + - 'cmd' >> 'output' + Appends the output of 'cmd' to the 'output' file. + - 'cmd' | 'external command' + Sends the output of 'cmd' to the input of 'external command'. The 'external command' itself can contain output redirection. + Examples: + ** read_block 0x00000681 | less ** cat /.fdc.sf | hexdump -C ** cat /.fdc.sf | hexdump -C > /tmp/fdc.hex ** cat /.fdc.sf | hexdump -C | less VARIABLES --------- Variables in debugvmfs represent structures on the filesystem. The currently supported top-level variables are the following: - *vol_version* - *version* - *label* - *mode* - *uuid* - *ctime* - *block_size* - *subblock_size* - *fdc_header_size* - *fdc_bitmap_count* - *fbb* - *fdc* - *pbc* - *sbc* - *fs* - *lvm* - *blkid*['blk'] - *dirent*["'path'"] - *inode*["'filespec'"] The *vol_version*, *version*, *label*, *mode*, *uuid*, *ctime*, *block_size*, *subblock_size*, *fdc_header_size*, and *fdc_bitmap_count* properties are low-level information about the filesystem. The *fbb*, *fdc*, *pbc* and *sbc* variables are bitmaps. Running them through the *show* command will display all the bitmaps header fields. Each of these fields can also be displayed individually: - 'bitmap'.*items_per_bitmap_entry* - 'bitmap'.*bmp_entries_per_area* - 'bitmap'.*hdr_size* - 'bitmap'.*data_size* - 'bitmap'.*area_size* - 'bitmap'.*area_count* - 'bitmap'.*total_items* - 'bitmap'.*used_items* - 'bitmap'.*free_items* - 'bitmap'.*entry*['n'] e.g. to display the data size of the fdc bitmap, type *show fdc.data_size*. Each 'bitmap'.*entry*['n'] (where 0 ≤ 'n' < 'bitmap'.*bmp_entries_per_area* * 'bitmap'.*area_count*) contains information about each entry in the bitmap. The available fields are: - 'bitmap'.*entry*['n'].id - 'bitmap'.*entry*['n'].total - 'bitmap'.*entry*['n'].free - 'bitmap'.*entry*['n'].ffree - 'bitmap'.*entry*['n'].mdh - 'bitmap'.*entry*['n'].item['m'] The 'bitmap'.*entry*['n'].mdh is a metadata header. See further below for more details about metadata headers. Each 'bitmap'.*entry*['n'].item['m'] (where 0 ≤ 'm' < 'bitmap'.*items_per_bitmap_entry*) contains information about a given item in the given entry. The available fields are: - 'bitmap'.*entry*['n'].item['m'].used - 'bitmap'.*entry*['n'].item['m'].dump The *pbc* bitmap has an additional field: - *pbc*.*entry*['n'].item['m'].blocks The *lvm* variable contains low-level information about the physical and logical volumes. The available fields are: - *lvm*.*uuid* - *lvm*.*size* - *lvm*.*blocks* - *lvm*.*num_extents* - *lvm*.*extent*['n'] Each *lvm*.*extent*['n'] (where 0 ≤ 'n' < *lvm*.*num_extents*) contains low-level information about the physical volumes. The available fields are: - *lvm*.*extent*['n'].device - *lvm*.*extent*['n'].uuid - *lvm*.*extent*['n'].lun - *lvm*.*extent*['n'].version - *lvm*.*extent*['n'].name - *lvm*.*extent*['n'].size - *lvm*.*extent*['n'].num_segments - *lvm*.*extent*['n'].first_segment - *lvm*.*extent*['n'].last_segment Each *blkid*['blk'] (where 'blk' is a block id) contains information on the given block id. The available fields are: - *blkid*['blk'].item - *blkid*['blk'].flags Each *dirent*['path'] (where 'path' is a path relative to the current directory in the shell or / outside the shell) contains information on the directory entry corresponding to the given path. The available fields are: - *dirent*["'path'"].type - *dirent*["'path'"].block_id - *dirent*["'path'"].record_id - *dirent*["'path'"].name Each *inode*['filespec'] contains information on the inode corresponding to the given filespec. The available fields are: - *inode*["'filespec'"].id - *inode*["'filespec'"].id2 - *inode*["'filespec'"].nlink - *inode*["'filespec'"].type - *inode*["'filespec'"].flags - *inode*["'filespec'"].size - *inode*["'filespec'"].blk_size - *inode*["'filespec'"].blk_count - *inode*["'filespec'"].uid - *inode*["'filespec'"].gid - *inode*["'filespec'"].mode - *inode*["'filespec'"].zla - *inode*["'filespec'"].tbz - *inode*["'filespec'"].cow - *inode*["'filespec'"].atime - *inode*["'filespec'"].mtime - *inode*["'filespec'"].ctime - *inode*["'filespec'"].rdm_id - *inode*["'filespec'"].mdh - *inode*["'filespec'"].blocks Metadata headers are being used in several places, such as 'bitmap'.*entry*['n'].mdh and *inode*["'filespec'"].mdh. They mostly contain information about clustered accesses to metadata on the filesystem. The available fields are: - 'mdh'.magic - 'mdh'.pos - 'mdh'.hb_pos - 'mdh'.hb_lock - 'mdh'.hb_uuid - 'mdh'.hb_seq - 'mdh'.obj_seq - 'mdh'.mtime Variable values can also be used in expressions using square brackets to use the variable value as an index. For example: - *blkid*[*inode*["'filespec'"].id] Enclosing a variable name with parentheses will use that variable value as a variable name. For example: - (*blkid*['blk'].item).status AUTHORS ------- include::../AUTHORS[] SEE ALSO -------- vmfs-fuse(8) vmfs-tools-0.2.5/debugvmfs/manifest.mk0000644000175000017500000000014311733536116015546 0ustar mhmhLDFLAGS := $(DLOPEN_LDFLAGS) debugvmfs.o_CFLAGS := -include version REQUIRES := libvmfs libreadcmd vmfs-tools-0.2.5/debugvmfs/variables.c0000644000175000017500000006436711733550236015544 0ustar mhmh/* * vmfs-tools - Tools to access VMFS filesystems * Copyright (C) 2009,2010,2011,2012 Mike Hommey * * 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. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include "vmfs.h" struct var_member { const char *member_name; const char *description; const struct var_member *subvar; unsigned short offset; unsigned short length; void *(*get_member)(void *value, const char *index); void (*free_member)(void *value); char *(*get_value)(void *value, short len); }; struct var { const struct var_member *member; void *value; const struct var *parent; }; static void *get_pb_bitmap(void *value, const char *index); static void *get_bitmap_item(void *value, const char *index); #define free_bitmap_item free static void *get_bitmap_entry(void *value, const char *index); #define free_bitmap_entry free static void *get_lvm_extent(void *value, const char *index); #define free_lvm_extent NULL static void *get_blkid(void *value, const char *index); #define free_blkid free static void *get_dirent(void *value, const char *index); #define free_dirent (void (*)(void *))vmfs_dir_close static void *get_inode(void *value, const char *index); #define free_inode (void (*)(void *))vmfs_inode_release static void *get_lvm(void *value, const char *index); #define free_lvm NULL static char *get_value_none(void *value, short len); static char *get_value_uint(void *value, short len); static char *get_value_xint(void *value, short len); static char *get_value_size(void *value, short len); static char *get_value_string(void *value, short len); static char *get_value_uuid(void *value, short len); static char *get_value_date(void *value, short len); static char *get_value_fs_mode(void *value, short len); static char *get_value_hb_lock(void *value, short len); static char *get_value_bitmap_used(void *value, short len); static char *get_value_bitmap_free(void *value, short len); static char *get_value_bitmap_item_status(void *value, short len); static char *get_value_bitmap_item_dump(void *value, short len); static char *get_value_bitmap_item_blocks(void *value, short len); static char *get_value_vol_size(void *value, short len); static char *get_value_blkid_item(void *value, short len); static char *get_value_blkid_flags(void *value, short len); static char *get_value_mode(void *value, short len); static char *get_value_blocks(void *value, short len); #define PTR(type, name) 0 #define FIELD(type, name) sizeof(((type *)0)->name) #define MEMBER(type, name, desc, format) \ { # name, desc, NULL, offsetof(type, name), \ FIELD(type, name), NULL, NULL, get_value_ ## format } #define MEMBER2(type, sub, name, desc, format) \ { # name, desc, NULL, offsetof(type, sub.name), \ FIELD(type, sub.name), NULL, NULL, get_value_ ## format } #define VIRTUAL_MEMBER(type, name, desc, format) \ { # name, desc, NULL, 0, sizeof(type), NULL, NULL, get_value_ ## format } #define GET_SUBVAR(type, name, subvar, what) \ { # name, NULL, subvar, 0, sizeof(type), get_ ## what, free_ ## what, NULL } #define SUBVAR(type, name, subvar, kind) \ { # name, NULL, subvar, offsetof(type, name), kind(type, name), NULL, NULL, NULL } #define SUBVAR2(type, name, subvar, field_name, kind) \ { # name, NULL, subvar, offsetof(type, field_name), kind(type, field_name), NULL, NULL, NULL } static const struct var_member vmfs_metadata_hdr[] = { MEMBER(vmfs_metadata_hdr_t, magic, "Magic", xint), MEMBER(vmfs_metadata_hdr_t, pos, "Position", xint), MEMBER(vmfs_metadata_hdr_t, hb_pos, "HB Position", uint), MEMBER(vmfs_metadata_hdr_t, hb_lock, "HB Lock", hb_lock), MEMBER(vmfs_metadata_hdr_t, hb_uuid, "HB UUID", uuid), MEMBER(vmfs_metadata_hdr_t, hb_seq, "HB Sequence", uint), MEMBER(vmfs_metadata_hdr_t, obj_seq, "Obj Sequence", uint), MEMBER(vmfs_metadata_hdr_t, mtime, "MTime", uint), { NULL, } }; struct vmfs_bitmap_item_ref { vmfs_bitmap_entry_t entry; vmfs_bitmap_t *bitmap; uint32_t entry_idx, item_idx; }; static const struct var_member vmfs_bitmap_item[] = { VIRTUAL_MEMBER(struct vmfs_bitmap_item_ref, status, "Status", bitmap_item_status), VIRTUAL_MEMBER(struct vmfs_bitmap_item_ref, dump, NULL, bitmap_item_dump), { "blocks", NULL, NULL, 0, sizeof(struct vmfs_bitmap_item_ref), get_pb_bitmap, NULL, get_value_bitmap_item_blocks }, { NULL, } }; static const struct var_member vmfs_bitmap_entry[] = { MEMBER(vmfs_bitmap_entry_t, id, "Id", uint), MEMBER(vmfs_bitmap_entry_t, total, "Total items", uint), MEMBER(vmfs_bitmap_entry_t, free, "Free items", uint), MEMBER(vmfs_bitmap_entry_t, ffree, "First free", uint), SUBVAR(vmfs_inode_t, mdh, vmfs_metadata_hdr, FIELD), GET_SUBVAR(struct vmfs_bitmap_item_ref, item, vmfs_bitmap_item, bitmap_item), { NULL, } }; static const struct var_member vmfs_bitmap[] = { MEMBER2(vmfs_bitmap_t, bmh, items_per_bitmap_entry, "Item per bitmap entry", uint), MEMBER2(vmfs_bitmap_t, bmh, bmp_entries_per_area, "Bitmap entries per area", uint), MEMBER2(vmfs_bitmap_t, bmh, hdr_size, "Header size", size), MEMBER2(vmfs_bitmap_t, bmh, data_size, "Data size", size), MEMBER2(vmfs_bitmap_t, bmh, area_size, "Area size", size), MEMBER2(vmfs_bitmap_t, bmh, area_count, "Area count", uint), MEMBER2(vmfs_bitmap_t, bmh, total_items, "Total items", uint), VIRTUAL_MEMBER(vmfs_bitmap_t, used_items, "Used items", bitmap_used), VIRTUAL_MEMBER(vmfs_bitmap_t, free_items, "Free items", bitmap_free), GET_SUBVAR(vmfs_bitmap_t, entry, vmfs_bitmap_entry, bitmap_entry), { NULL, } }; static const struct var_member vmfs_volume[] = { MEMBER(vmfs_volume_t, device, "Device", string), MEMBER2(vmfs_volume_t, vol_info, uuid, "UUID", uuid), MEMBER2(vmfs_volume_t, vol_info, lun, "LUN", uint), MEMBER2(vmfs_volume_t, vol_info, version, "Version", uint), MEMBER2(vmfs_volume_t, vol_info, name, "Name", string), VIRTUAL_MEMBER(vmfs_volume_t, size, "Size", vol_size), MEMBER2(vmfs_volume_t, vol_info, num_segments, "Num. Segments", uint), MEMBER2(vmfs_volume_t, vol_info, first_segment, "First Segment", uint), MEMBER2(vmfs_volume_t, vol_info, last_segment, "Last Segment", uint), { NULL, } }; static const struct var_member vmfs_lvm[] = { MEMBER2(vmfs_lvm_t, lvm_info, uuid, "UUID", uuid), MEMBER2(vmfs_lvm_t, lvm_info, size, "Size", size), MEMBER2(vmfs_lvm_t, lvm_info, blocks, "Blocks", uint), MEMBER2(vmfs_lvm_t, lvm_info, num_extents, "Num. Extents", uint), GET_SUBVAR(vmfs_lvm_t, extent, vmfs_volume, lvm_extent), { NULL, } }; static const struct var_member blkid[] = { VIRTUAL_MEMBER(vmfs_block_info_t, item, "Referred Item", blkid_item), VIRTUAL_MEMBER(vmfs_block_info_t, flags, "Flags", blkid_flags), { NULL, } }; static const struct var_member dirent[] = { MEMBER2(vmfs_dir_t, dirent, type, "Type", uint), MEMBER2(vmfs_dir_t, dirent, block_id, "Block ID", xint), MEMBER2(vmfs_dir_t, dirent, record_id, "Record ID", xint), MEMBER2(vmfs_dir_t, dirent, name, "Name", string), { NULL, } }; static const struct var_member inode[] = { MEMBER(vmfs_inode_t, id, "ID", xint), MEMBER(vmfs_inode_t, id2, "ID2", xint), MEMBER(vmfs_inode_t, nlink, "Links", uint), MEMBER(vmfs_inode_t, type, "Type", uint), MEMBER(vmfs_inode_t, flags, "Flags", uint), MEMBER(vmfs_inode_t, size, "Size", size), MEMBER(vmfs_inode_t, blk_size, "Block size", size), MEMBER(vmfs_inode_t, blk_count, "Block count", uint), MEMBER(vmfs_inode_t, uid, "UID", uint), MEMBER(vmfs_inode_t, gid, "GID", uint), MEMBER(vmfs_inode_t, mode, "Mode", mode), MEMBER(vmfs_inode_t, zla, "ZLA", uint), MEMBER(vmfs_inode_t, tbz, "TBZ", uint), MEMBER(vmfs_inode_t, cow, "COW", uint), MEMBER(vmfs_inode_t, atime, "Access Time", date), MEMBER(vmfs_inode_t, mtime, "Modify Time", date), MEMBER(vmfs_inode_t, ctime, "Change Time", date), MEMBER(vmfs_inode_t, rdm_id, "RDM ID", xint), SUBVAR(vmfs_inode_t, mdh, vmfs_metadata_hdr, FIELD), VIRTUAL_MEMBER(vmfs_inode_t, blocks, NULL, blocks), { NULL, } }; static const struct var_member vmfs_fs[] = { GET_SUBVAR(vmfs_fs_t, lvm, vmfs_lvm, lvm), SUBVAR2(vmfs_fs_t, lvm, vmfs_lvm, dev, PTR), SUBVAR(vmfs_fs_t, fbb, vmfs_bitmap, PTR), SUBVAR(vmfs_fs_t, fdc, vmfs_bitmap, PTR), SUBVAR(vmfs_fs_t, pbc, vmfs_bitmap, PTR), SUBVAR(vmfs_fs_t, sbc, vmfs_bitmap, PTR), GET_SUBVAR(vmfs_fs_t, blkid, blkid, blkid), GET_SUBVAR(vmfs_fs_t, dirent, dirent, dirent), GET_SUBVAR(vmfs_fs_t, inode, inode, inode), MEMBER2(vmfs_fs_t, fs_info, vol_version, "Volume Version", uint), MEMBER2(vmfs_fs_t, fs_info, version, "Version", uint), MEMBER2(vmfs_fs_t, fs_info, label, "Label", string), MEMBER2(vmfs_fs_t, fs_info, mode, "Mode", fs_mode), MEMBER2(vmfs_fs_t, fs_info, uuid, "UUID", uuid), MEMBER2(vmfs_fs_t, fs_info, ctime, "Creation time", date), MEMBER2(vmfs_fs_t, fs_info, block_size, "Block size", size), MEMBER2(vmfs_fs_t, fs_info, subblock_size, "Subblock size", size), MEMBER2(vmfs_fs_t, fs_info, fdc_header_size, "FDC Header size", size), MEMBER2(vmfs_fs_t, fs_info, fdc_bitmap_count, "FDC Bitmap count", uint), { NULL, } }; static const struct var_member root = { NULL, NULL, vmfs_fs, 0, sizeof(vmfs_fs_t), NULL, NULL, NULL }; static void *get_member(const struct var_member *member, void *value, const char *index) { void *result = &((char *) value)[member->offset]; if (member->length == 0) result = *(void**)result; if (member->get_member) result = member->get_member(result, index); return result; } static void free_member(const struct var_member *member, void *value) { if (!value) return; if (member->free_member) member->free_member(value); } static void free_var(const struct var *var, const struct var *up_to) { const struct var *v; if (!var || var == up_to) return; free_member(var->member, var->value); v = var->parent; free((void *)var); if (v && v != up_to) free_var(v, up_to); } static const char *find_closing_thing(const char* openclose, const char *str) { size_t len = strcspn(str, openclose); if (str[len] == openclose[0]) { const char *thing = find_closing_thing(openclose, str + len + 1); if (!thing) return NULL; return find_closing_thing(openclose, thing + 1); } else if (str[len] == openclose[1]) return str + len; return NULL; } static int get_numeric_index(uint32_t *idx, const char *index); static const struct var *resolve_var(const struct var *var, const char *name) { /* TODO: this function really needs some simplification/splitting */ size_t len; const struct var_member *m; char *index = NULL; struct var *res; void *value; if (!name || !var) return var; len = strcspn(name, ".[("); if (name[len] == '(') { const struct var *idx_var; const char *end; if (len != 0 || var->parent) return NULL; if (!(end = find_closing_thing("()", name + 1))) return NULL; len = end - name - 1; index = malloc(len + 1); strncpy(index, name + 1, len); index[len] = 0; len += 2; idx_var = resolve_var(var, index); free(index); if (idx_var && idx_var->member->get_value) { index = idx_var->member->get_value(idx_var->value, idx_var->member->length); free_var(idx_var, var); var = resolve_var(var, index); free(index); index = NULL; name += len; if (*name == '.') name++; len = strcspn(name, ".["); } else return NULL; } if (!var || !name[0]) return var; if (len == 0) return NULL; m = var->member->subvar; while (m->member_name && !(!strncmp(m->member_name, name, len) && m->member_name[len] == 0)) m++; if (!m->member_name) return NULL; if (name[len] == '[') { size_t len2 = len + 1; const char *end; int is_str = 0; if (name[len2] == '"') { end = strchr(name + len2 + 1, '"'); if (end[1] != ']') return NULL; is_str = 1; len++; } else end = find_closing_thing("[]", name + len + 1); if (end) { len2 = end - name - 1; index = malloc(len2 - len + 1); strncpy(index, name + len + 1, len2 - len); index[len2 - len] = 0; len = len2 + 2; if (is_str) { len++; } else { uint32_t idx; if (!get_numeric_index(&idx, index)) { const struct var *root_var, *idx_var; for (root_var = var; root_var->parent; root_var = root_var->parent); idx_var = resolve_var(root_var, index); free(index); if (idx_var && idx_var->member->get_value) { index = idx_var->member->get_value(idx_var->value, idx_var->member->length); } else return NULL; free_var(idx_var, root_var); } } } else return NULL; } value = get_member(m, var->value, index); free(index); if (!value) return NULL; res = malloc(sizeof(struct var)); res->value = value; res->member = m; res->parent = var; if (name[len]) { const struct var *r = resolve_var(res, name + len + 1); if (!r) free(res); return r; } return res; } /* Get string corresponding to specified mode */ static char *vmfs_fs_mode_to_str(uint32_t mode) { /* only two lower bits seem to be significant */ switch(mode & 0x03) { case 0x00: return "private"; case 0x01: case 0x03: return "shared"; case 0x02: return "public"; } /* should not happen */ return NULL; } static const char * const units[] = { "", " KiB", " MiB", " GiB", " TiB", }; static char *human_readable_size(uint64_t size) { char buf[256]; int scale = 0; for (scale = 0; (size >> scale) >= 1024; scale += 10); if (size & ((1L << scale) - 1)) sprintf(buf, "%.2f%s", (float) size / (1L << scale), units[scale / 10]); else sprintf(buf, "%"PRIu64"%s", size >> scale, units[scale / 10]); return strdup(buf); } static char *get_value_uint(void *value, short len) { char buf[32]; switch (len) { case 4: sprintf(buf, "%" PRIu32, *((uint32_t *)value)); return strdup(buf); case 8: sprintf(buf, "%" PRIu64, *((uint64_t *)value)); return strdup(buf); } return get_value_none(value, len); } static char *get_value_xint(void *value, short len) { char buf[32]; switch (len) { case 4: sprintf(buf, "0x%" PRIx32, *((uint32_t *)value)); return strdup(buf); case 8: sprintf(buf, "0x%" PRIx64, *((uint64_t *)value)); return strdup(buf); } return get_value_none(value, len); } static char *get_value_size(void *value, short len) { switch (len) { case 4: return human_readable_size(*((uint32_t *)value)); case 8: return human_readable_size(*((uint64_t *)value)); } return get_value_none(value, len); } static char *get_value_string(void *value, short len) { if (len == sizeof(void *)) return strdup(*((char **)value)); return strdup((char *)value); } static char *get_value_uuid(void *value, short len) { char *buf = malloc(36); return m_uuid_to_str((u_char *)value,buf); } static char *get_value_date(void *value, short len) { char buf[256]; return strdup(m_ctime((time_t *)(uint32_t *)value, buf, 256)); } static char *get_value_fs_mode(void *value, short len) { return strdup(vmfs_fs_mode_to_str(*((uint32_t *)value))); } const char *hb_lock[] = { "unlocked", "write lock", "read lock", }; static char *get_value_hb_lock(void *value, short len) { uint32_t lock = *((uint32_t *)value); if (lock <= 2) return strdup(hb_lock[lock]); else { char buf[256]; sprintf(buf, "0x%x", lock); return strdup(buf); } } static char *get_value_none(void *value, short len) { return strdup("Don't know how to display"); } static char *get_value_bitmap_used(void *value, short len) { char buf[32]; sprintf(buf, "%d", vmfs_bitmap_allocated_items((vmfs_bitmap_t *)value)); return strdup(buf); } static char *get_value_bitmap_free(void *value, short len) { char buf[32]; sprintf(buf, "%d", ((vmfs_bitmap_t *)value)->bmh.total_items - vmfs_bitmap_allocated_items((vmfs_bitmap_t *)value)); return strdup(buf); } static char *get_value_vol_size(void *value, short len) { return human_readable_size( (uint64_t)(((vmfs_volume_t *)value)->vol_info.size) * 256); } static int longest_member_desc(const struct var_member *members) { int len = 0, curlen; for (; members->member_name; members++) { if (!members->description) continue; curlen = strlen(members->description); if (curlen > len) len = curlen; } return len; } static int get_numeric_index(uint32_t *idx, const char *index) { char *c; unsigned long ret; if (!idx) return 0; ret = strtoul(index, &c, 0); if (*c || ret > 0xffffffff) return 0; *idx = ret; return 1; } static void *get_lvm_extent(void *value, const char *index) { vmfs_lvm_t *lvm = (vmfs_lvm_t *)value; uint32_t idx; if (!index || !get_numeric_index(&idx, index) || idx >= lvm->lvm_info.num_extents) return NULL; return lvm->extents[idx]; } static void *get_bitmap_entry(void *value, const char *index) { struct vmfs_bitmap_item_ref *ref = calloc(1, sizeof(struct vmfs_bitmap_item_ref)); if (!index) return NULL; ref->bitmap = (vmfs_bitmap_t *)value; if (!get_numeric_index(&ref->entry_idx, index)) return NULL; if (ref->entry_idx >= ref->bitmap->bmh.bmp_entries_per_area * ref->bitmap->bmh.area_count) return NULL; vmfs_bitmap_get_entry(ref->bitmap, ref->entry_idx, 0, &ref->entry); return ref; } static char *get_value_bitmap_item_status(void *value, short len) { struct vmfs_bitmap_item_ref *ref = (struct vmfs_bitmap_item_ref *) value; int used = vmfs_bitmap_get_item_status(&ref->bitmap->bmh, &ref->entry, ref->entry_idx, ref->item_idx); return strdup(used ? "used" : "free"); } void dump_line(char *buf, uint32_t offset, u_char *data, size_t len) { u_char b[17]; size_t i; sprintf(buf, "%08x ", offset); buf += 10; for (i = 0; i < len; i++) { sprintf(buf, (i == 7) ? "%02x " : "%02x ", data[i]); buf += (i == 7) ? 4 : 3; b[i] = ((data[i] > 31) && (data[i] < 127)) ? data[i] : '.'; } b[i] = 0; for (; i < 16; i++) { sprintf(buf, (i == 7) ? " " : " "); buf += (i == 7) ? 4 : 3; } sprintf(buf, " |%s|\n", b); } static char *get_value_bitmap_item_dump(void *value, short len) { struct vmfs_bitmap_item_ref *ref = (struct vmfs_bitmap_item_ref *) value; uint32_t off = 0, size = ref->bitmap->bmh.data_size; u_char *data; char *dump, *buf; bool is_fbb = false; if (size == 0) { /* If bitmap data size is 0, it is very likely the bitmap is fbb. But just in case, we make sure it is */ if (ref->bitmap->f->inode->fs->fbb != ref->bitmap) return NULL; size = vmfs_fs_get_blocksize(ref->bitmap->f->inode->fs); is_fbb = true; } data = iobuffer_alloc(size); buf = dump = malloc(79 * (size + 15) / 16); if (is_fbb) vmfs_fs_read(ref->bitmap->f->inode->fs, ref->entry_idx * ref->bitmap->bmh.items_per_bitmap_entry + ref->item_idx, 0, data, size); else vmfs_bitmap_get_item(ref->bitmap, ref->entry_idx, ref->item_idx, data); while (size >= 16) { dump_line(buf, off, data + off, 16); size -= 16; off += 16; buf += 79; } if (size > 0) dump_line(buf, off, data + off, size); free(data); return dump; } static char *get_value_bitmap_item_blocks(void *value, short len) { struct vmfs_bitmap_item_ref *ref = (struct vmfs_bitmap_item_ref *) value; uint32_t size = ref->bitmap->bmh.data_size; u_char *data = iobuffer_alloc(size); uint32_t *blocks = (uint32_t *) data; int i, num = size / sizeof(uint32_t); void *b, *buf; vmfs_bitmap_get_item(ref->bitmap, ref->entry_idx, ref->item_idx, data); while (num > 0 && !blocks[num - 1]) num--; b = buf = malloc(sizeof("0x00000000") * num + 1); for (i = 0; i < num; i++) { sprintf(b, "0x%08x%c", read_le32(data, i * sizeof(uint32_t)), (i + 1) % 4 ? ' ' : '\n'); b += sizeof("0x00000000"); } return buf; } static void *get_pb_bitmap(void *value, const char *index) { struct vmfs_bitmap_item_ref *ref = (struct vmfs_bitmap_item_ref *) value; if (ref->bitmap->f->inode->fs->pbc != ref->bitmap) return NULL; return value; } static void *get_bitmap_item(void *value, const char *index) { struct vmfs_bitmap_item_ref *ref = malloc(sizeof(struct vmfs_bitmap_item_ref)); /* This is not nice, but we need to copy the struct for resolve_var to * be happy about it */ memcpy(ref, value, sizeof(struct vmfs_bitmap_item_ref)); if (!index) return NULL; if (!get_numeric_index(&ref->item_idx, index)) return NULL; if (ref->item_idx >= ref->bitmap->bmh.items_per_bitmap_entry) return NULL; return ref; } static const char *bitmaps[] = { "fbb", "sbc", "pbc", "fdc" }; static char *get_value_blkid_item(void *value, short len) { char buf[256]; vmfs_block_info_t *info = (vmfs_block_info_t *)value; sprintf(buf, "%s.entry[%d].item[%d]", bitmaps[info->type - 1], info->entry, info->item); return strdup(buf); } static char *get_value_blkid_flags(void *value, short len) { char buf[256]; vmfs_block_info_t *info = (vmfs_block_info_t *)value; int more_than_one = 0; if (sprintf(buf, "0x%x (", info->flags) <= 0) return NULL; if (info->type == VMFS_BLK_TYPE_FB) { if (info->flags & VMFS_BLK_FB_TBZ_FLAG) { strcat(buf, "tbz"); more_than_one = 1; } if (info->flags & ~VMFS_BLK_FB_TBZ_FLAG) { if (more_than_one) strcat(buf, ", "); strcat(buf, "unknown"); } } if (!info->flags) strcat(buf, "none"); strcat(buf, ")"); return strdup(buf); } static void *get_blkid(void *value, const char *index) { vmfs_block_info_t *info; uint32_t idx; if (!index || !get_numeric_index(&idx, index)) return NULL; info = calloc(1, sizeof(vmfs_block_info_t)); if (vmfs_block_get_info(idx, info) == -1) { free(info); return NULL; } /* Normalize entry and item for fbb */ if (info->type == VMFS_BLK_TYPE_FB) { vmfs_fs_t *fs = (vmfs_fs_t *)value; info->entry = info->item / fs->fbb->bmh.items_per_bitmap_entry; info->item = info->item % fs->fbb->bmh.items_per_bitmap_entry; } return info; } static vmfs_dir_t *current_dir = NULL; static void *get_dirent(void *value, const char *index) { vmfs_dir_t *dir; char *bname, *dname; if (!index) return NULL; bname = m_basename(index); dname = m_dirname(index); if (!(dir = vmfs_dir_open_at(current_dir,dname))) return NULL; if (!vmfs_dir_lookup(dir,bname)) return NULL; free(bname); free(dname); return dir; } static char *get_value_mode(void *value, short len) { char *buf = malloc(18); uint32_t mode = *((uint32_t *)value); sprintf(buf, "%04o (", mode); m_fmode_to_str(mode, buf + 6); buf[16] = ')'; buf[17] = 0; return buf; } static char *get_value_blocks(void *value, short len) { char *buf, *b; int i, num = FIELD(vmfs_inode_t, blocks) / sizeof(uint32_t); vmfs_inode_t *inode = (vmfs_inode_t *) value; while (num > 0 && !inode->blocks[num - 1]) num--; b = buf = malloc(sizeof("0x00000000") * num + 1); for (i = 0; i < num; i++) { sprintf(b, "0x%08x%c", inode->blocks[i], (i + 1) % 4 ? ' ' : '\n'); b += sizeof("0x00000000"); } return buf; } /* Defined in debugvmfs.c */ vmfs_file_t *vmfs_file_open_from_filespec(vmfs_dir_t *base_dir, const char *filespec); static void *get_inode(void *value, const char *index) { vmfs_file_t *file; vmfs_inode_t *inode; if (!index) return NULL; if (!(file = vmfs_file_open_from_filespec(current_dir,index))) return NULL; /* Awkward, need better API */ inode = vmfs_inode_acquire(vmfs_dir_get_fs(current_dir), file->inode->id); vmfs_file_close(file); return inode; } static void *get_lvm(void *value, const char *index) { vmfs_fs_t *fs = (vmfs_fs_t *) value; if (vmfs_device_is_lvm(fs->dev)) return fs->dev; return NULL; } int cmd_show(vmfs_dir_t *base_dir,int argc,char *argv[]) { current_dir = base_dir; const struct var root_var = { &root, (void *)vmfs_dir_get_fs(base_dir), NULL }; const struct var *var = resolve_var(&root_var, argv[0]); int ret = 0; if (var) { const struct var_member *m = var->member; if (m->get_value) { char *str = m->get_value(var->value, m->length); if (str) { if (m->description) printf("%s: %s\n", m->description, str); else printf("%s\n", str); free(str); } } else if (m->subvar) { char format[16]; const struct var_member *v; sprintf(format, "%%%ds: %%s\n", longest_member_desc(m->subvar)); for (v = m->subvar; v->member_name; v++) if (v->description && v->get_value) { void *m_value = get_member(v, var->value, NULL); char *str = v->get_value(m_value, v->length); printf(format, v->description, str); free(str); free_member(v, m_value); } } else ret = 1; } else ret = 1; free_var(var, &root_var); return ret; } vmfs-tools-0.2.5/debugvmfs/debugvmfs.c0000644000175000017500000004315111733550164015542 0ustar mhmh/* * vmfs-tools - Tools to access VMFS filesystems * Copyright (C) 2009 Christophe Fillot * Copyright (C) 2009,2010,2011,2012 Mike Hommey * * 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. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include "vmfs.h" #include "readcmd.h" /* Opens a vmfs_file_t/vmfs_dir_t corresponding to the given filespec */ #define vmfs_foo_open_from_filespec(foo) \ vmfs_## foo ##_t *vmfs_## foo ##_open_from_filespec( \ vmfs_dir_t *base_dir, const char *filespec) \ { \ if (filespec[0] == '<') { /* Possibly a "" filespec */ \ char *end; \ off_t inode = (off_t)strtoull(filespec + 1,&end,0); \ if ((end != filespec) && (end[0] == '>') && (end[1] == 0)) { \ const vmfs_fs_t *fs = vmfs_dir_get_fs(base_dir); \ return vmfs_## foo ##_open_from_blkid(fs, inode); \ } \ } \ return vmfs_## foo ##_open_at(base_dir, filespec); \ } vmfs_foo_open_from_filespec(file) vmfs_foo_open_from_filespec(dir) /* "cat" command */ static int cmd_cat(vmfs_dir_t *base_dir,int argc,char *argv[]) { vmfs_file_t *f; int i; if (argc == 0) { fprintf(stderr,"Usage: cat file1 ... fileN\n"); return(-1); } for(i=0;i= 1) && (strcmp(argv[0],"-l") == 0)) { long_format = 1; argv++; argc--; } switch (argc) { case 0: arg = "."; break; case 1: arg = argv[0]; break; default: fprintf(stderr,"Usage: ls [-l] [path]\n"); return(-1); } if (!(d = vmfs_dir_open_from_filespec(base_dir,arg))) { fprintf(stderr,"Unable to open directory %s\n",argv[0]); return(-1); } while((entry = vmfs_dir_read(d))) { if (long_format) { vmfs_file_t *f = vmfs_file_open_from_blkid(fs,entry->block_id); if (!f) continue; vmfs_file_fstat(f,&st_info); vmfs_file_close(f); printf("%-10s ",m_fmode_to_str(st_info.st_mode,buffer)); usr = getpwuid(st_info.st_uid); grp = getgrgid(st_info.st_gid); printf("%u ", (unsigned int)st_info.st_nlink); if (usr) printf("%8s ", usr->pw_name); else printf("%8u ", st_info.st_uid); if (grp) printf("%8s ", grp->gr_name); else printf("%8u ", st_info.st_gid); printf("%10llu %s %s\n", (unsigned long long)st_info.st_size, m_ctime(&st_info.st_ctime,buffer,sizeof(buffer)), entry->name); } else { printf("%s\n",entry->name); } } vmfs_dir_close(d); return(0); } /* "truncate" command */ static int cmd_truncate(vmfs_dir_t *base_dir,int argc,char *argv[]) { off_t new_size; vmfs_file_t *f; int ret = 0; if (argc < 2) { fprintf(stderr,"Usage: truncate filespec size\n"); return(-1); } new_size = (off_t)strtoull(argv[1],NULL,0); if (!(f = vmfs_file_open_from_filespec(base_dir, argv[0]))) { fprintf(stderr,"Unable to open file %s\n",argv[0]); return(-1); } if (vmfs_file_truncate(f,new_size) < 0) { fprintf(stderr,"Unable to truncate file.\n"); ret = -1; goto end; } printf("File truncated to %"PRIu64" (0x%"PRIx64") bytes\n", (uint64_t)new_size,(uint64_t)new_size); end: vmfs_file_close(f); return ret; } /* "copy_file" command */ static int cmd_copy_file(vmfs_dir_t *base_dir,int argc,char *argv[]) { u_char buffer[4096]; off_t pos; size_t len; vmfs_file_t *output; FILE *input; if (argc < 2) { fprintf(stderr,"Usage: copy_file local_filename vmfs_filename\n"); return(-1); } if (!(input = fopen(argv[0],"r"))) { fprintf(stderr,"Unable to open local file\n"); return(-1); } if (!(output = vmfs_file_create_at(base_dir,argv[1],0644))) { fprintf(stderr,"Unable to create file.\n"); return(-1); } pos = 0; while(!feof(input)) { len = fread(buffer,1,sizeof(buffer),input); if (!len) break; vmfs_file_pwrite(output,buffer,len,pos); pos += len; } vmfs_file_close(output); return(0); } /* "chmod" command */ static int cmd_chmod(vmfs_dir_t *base_dir,int argc,char *argv[]) { mode_t mode; vmfs_file_t *f; int ret = 0; if (argc < 2) { fprintf(stderr,"Usage: chmod filespec mode\n"); return(-1); } mode = (mode_t)strtoul(argv[1],NULL,0); if (!(f = vmfs_file_open_from_filespec(base_dir, argv[0]))) { fprintf(stderr,"Unable to open file %s\n",argv[0]); return(-1); } if (vmfs_file_chmod(f,mode) < 0) { fprintf(stderr,"Unable to change file permissions.\n"); ret = -1; } vmfs_file_close(f); return ret; } /* "mkdir" command */ static int cmd_mkdir(vmfs_dir_t *base_dir,int argc,char *argv[]) { if (argc < 1) { fprintf(stderr,"Usage: mkdir dirname\n"); return(-1); } return(vmfs_dir_mkdir_at(base_dir,argv[0],0755)); } /* "df" (disk free) command */ static int cmd_df(vmfs_dir_t *base_dir,int argc,char *argv[]) { const vmfs_fs_t *fs = vmfs_dir_get_fs(base_dir); uint32_t alloc,total; total = fs->fbb->bmh.total_items; alloc = vmfs_bitmap_allocated_items(fs->fbb); printf("Block size : %"PRIu64" bytes\n",vmfs_fs_get_blocksize(fs)); printf("Total blocks : %u\n",total); printf("Total size : %"PRIu64" MiB\n", (vmfs_fs_get_blocksize(fs)*total)/1048576); printf("Allocated blocks : %u\n",alloc); printf("Allocated space : %"PRIu64" MiB\n", (vmfs_fs_get_blocksize(fs)*alloc)/1048576); printf("Free blocks : %u\n",total-alloc); printf("Free size : %"PRIu64" MiB\n", (vmfs_fs_get_blocksize(fs)*(total-alloc))/1048576); return(0); } /* Get file block corresponding to specified position */ static int cmd_get_file_block(vmfs_dir_t *base_dir,int argc,char *argv[]) { vmfs_file_t *f; uint32_t blk_id; off_t pos; int ret = 0; if (argc < 2) { fprintf(stderr,"Usage: get_file_block \n"); return(-1); } if (!(f = vmfs_file_open_from_filespec(base_dir,argv[0]))) { fprintf(stderr,"Unable to open file '%s'\n",argv[0]); return(-1); } pos = (off_t)strtoull(argv[1],NULL,16); if (!vmfs_inode_get_block(f->inode,pos,&blk_id)) { printf("0x%8.8x\n",blk_id); } else { fprintf(stderr,"Unable to get block info\n"); ret = -1; } vmfs_file_close(f); return ret; } /* Check volume bitmaps */ static int cmd_check_vol_bitmaps(vmfs_dir_t *base_dir,int argc,char *argv[]) { const vmfs_fs_t *fs = vmfs_dir_get_fs(base_dir); int errors = 0; printf("Checking FBB bitmaps...\n"); errors += vmfs_bitmap_check(fs->fbb); printf("Checking FDC bitmaps...\n"); errors += vmfs_bitmap_check(fs->fdc); printf("Checking PBC bitmaps...\n"); errors += vmfs_bitmap_check(fs->pbc); printf("Checking SBC bitmaps...\n"); errors += vmfs_bitmap_check(fs->sbc); printf("Total errors: %d\n",errors); return(errors); } /* Show active heartbeats */ static int cmd_show_heartbeats(vmfs_dir_t *base_dir,int argc,char *argv[]) { const vmfs_fs_t *fs = vmfs_dir_get_fs(base_dir); return(vmfs_heartbeat_show_active(fs)); } /* Read a block */ static int cmd_read_block(vmfs_dir_t *base_dir,int argc,char *argv[]) { const vmfs_fs_t *fs = vmfs_dir_get_fs(base_dir); uint32_t blk_id,blk_type; uint64_t blk_size; u_char *buf; size_t len; int i; if (argc == 0) { fprintf(stderr,"Usage: read_block blk1 ... blkN\n"); return(-1); } blk_size = vmfs_fs_get_blocksize(fs); if (!(buf = iobuffer_alloc(blk_size))) return(-1); for(i=0;isbc,VMFS_BLK_SB_ENTRY(blk_id), VMFS_BLK_SB_ITEM(blk_id),buf); len = fs->sbc->bmh.data_size; break; /* Pointer Block */ case VMFS_BLK_TYPE_PB: vmfs_bitmap_get_item(fs->pbc,VMFS_BLK_PB_ENTRY(blk_id), VMFS_BLK_PB_ITEM(blk_id),buf); len = fs->pbc->bmh.data_size; break; /* File Descriptor / Inode */ case VMFS_BLK_TYPE_FD: vmfs_bitmap_get_item(fs->fdc,VMFS_BLK_FD_ENTRY(blk_id), VMFS_BLK_FD_ITEM(blk_id),buf); len = fs->fdc->bmh.data_size; break; default: fprintf(stderr,"Unsupported block type 0x%2.2x\n",blk_type); } if (len != 0) { if (fwrite(buf,1,len,stdout) != len) fprintf(stderr,"Block 0x%8.8x: incomplete write.\n",blk_id); } } free(buf); return(0); } /* Allocate a fixed block */ static int cmd_alloc_block_fixed(vmfs_dir_t *base_dir,int argc,char *argv[]) { const vmfs_fs_t *fs = vmfs_dir_get_fs(base_dir); uint32_t blk_id; int res; if (argc == 0) { fprintf(stderr,"Usage: alloc_block_fixed blk_id\n"); return(-1); } blk_id = (uint32_t)strtoul(argv[0],NULL,16); res = vmfs_block_alloc_specified(fs,blk_id); if (res == 0) { printf("Block 0x%8.8x allocated.\n",blk_id); } else { fprintf(stderr,"Unable to allocate block 0x%8.8x\n",blk_id); } return(0); } /* Allocate a block */ static int cmd_alloc_block(vmfs_dir_t *base_dir,int argc,char *argv[]) { const vmfs_fs_t *fs = vmfs_dir_get_fs(base_dir); uint32_t blk_type,blk_id; int res; if (argc == 0) { fprintf(stderr,"Usage: alloc_block blk_type\n"); return(-1); } blk_type = (uint32_t)strtoul(argv[0],NULL,16); res = vmfs_block_alloc(fs,blk_type,&blk_id); if (res == 0) { printf("Block 0x%8.8x allocated.\n",blk_id); } else { fprintf(stderr,"Unable to allocate block.\n"); } return(0); } /* Free a block */ static int cmd_free_block(vmfs_dir_t *base_dir,int argc,char *argv[]) { const vmfs_fs_t *fs = vmfs_dir_get_fs(base_dir); uint32_t blk_id; int res; if (argc == 0) { fprintf(stderr,"Usage: free_block blk_id\n"); return(-1); } blk_id = (uint32_t)strtoul(argv[0],NULL,16); res = vmfs_block_free(fs,blk_id); if (res == 0) { printf("Block 0x%8.8x freed.\n",blk_id); } else { fprintf(stderr,"Unable to free block 0x%8.8x\n",blk_id); } return(0); } int cmd_show(vmfs_dir_t *base_dir,int argc,char *argv[]); struct cmd { char *name; char *description; int (*fn)(vmfs_dir_t *base_dir,int argc,char *argv[]); }; /* Opens a shell */ static int cmd_shell(vmfs_dir_t *base_dir,int argc,char *argv[]); struct cmd cmd_array[] = { { "cat", "Concatenate files and print on standard output", cmd_cat }, { "ls", "List files in specified directory", cmd_ls }, { "truncate", "Truncate file", cmd_truncate }, { "copy_file", "Copy a file to VMFS volume", cmd_copy_file }, { "chmod", "Change permissions", cmd_chmod }, { "mkdir", "Create a directory", cmd_mkdir }, { "df", "Show available free space", cmd_df }, { "get_file_block", "Get file block", cmd_get_file_block }, { "check_vol_bitmaps", "Check volume bitmaps", cmd_check_vol_bitmaps }, { "show_heartbeats", "Show active heartbeats", cmd_show_heartbeats }, { "read_block", "Read a block", cmd_read_block }, { "alloc_block_fixed", "Allocate block (fixed)", cmd_alloc_block_fixed }, { "alloc_block", "Find and Allocate a block", cmd_alloc_block }, { "free_block", "Free block", cmd_free_block }, { "show", "Display value(s) for the given variable", cmd_show }, { "shell", "Opens a shell", cmd_shell }, { NULL, NULL }, }; static void show_usage(char *prog_name) { int i; char *name = basename(prog_name); fprintf(stderr,"%s " VERSION "\n",name); fprintf(stderr,"Syntax: %s \n\n",name); fprintf(stderr,"Available commands:\n"); for(i=0;cmd_array[i].name;i++) fprintf(stderr," - %s : %s\n",cmd_array[i].name,cmd_array[i].description); fprintf(stderr,"\n"); } static struct cmd *cmd_find(char *name) { int i; for(i=0;cmd_array[i].name;i++) if (!strcmp(cmd_array[i].name,name)) return(&cmd_array[i]); return NULL; } /* Executes a command through "sh -c", and returns the file descriptor to its * stdin or -1 on error */ static int pipe_exec(const char *cmd) { int fd[2]; pid_t p; if (pipe(fd) == -1) return -1; if ((p = fork()) == -1) return -1; if (p == 0) { /* Child process */ close(fd[1]); dup2(fd[0],0); close(fd[0]); execl("/bin/sh", "/bin/sh", "-c", cmd, NULL); return -1; } else { /* Parent process */ close(fd[0]); return fd[1]; } } /* Opens a shell */ static int cmd_shell(vmfs_dir_t *base_dir,int argc,char *argv[]) { struct cmd *cmd = NULL; const cmd_t *cmdline = NULL; vmfs_dir_t *cur_dir = vmfs_dir_open_at(base_dir,"."); if (!cur_dir) { fprintf(stderr, "Couldn't open base directory\n"); return -1; } do { freecmd(cmdline); cmdline = readcmd("debugvmfs> "); if (!cmdline) goto cleanup; if (!cmdline->argc) continue; if (!strcmp(cmdline->argv[0], "exit") || !strcmp(cmdline->argv[0], "quit")) goto cleanup; if (!strcmp(cmdline->argv[0], "cd")) { if (cmdline->argc == 2) { vmfs_dir_t *new_dir; if (!(new_dir = vmfs_dir_open_from_filespec(cur_dir, cmdline->argv[1]))) { fprintf(stderr, "No such directory: %s\n", cmdline->argv[1]); continue; } vmfs_dir_close(cur_dir); cur_dir = new_dir; } else { fprintf(stderr, "Usage: cd \n"); } continue; } cmd = cmd_find(cmdline->argv[0]); if (!cmd) { int i; fprintf(stderr,"Unknown command: %s\n", cmdline->argv[0]); fprintf(stderr,"Available commands:\n"); for(i=0;cmd_array[i].name;i++) if (cmd_array[i].fn != cmd_shell) fprintf(stderr," - %s : %s\n",cmd_array[i].name, cmd_array[i].description); } else if (cmd->fn != cmd_shell) { int out = -1; if (cmdline->redir) { int fd; if (cmdline->piped) { if ((fd = pipe_exec(cmdline->redir)) < 0) { fprintf(stderr, "Error executing pipe command: %s\n", strerror(errno)); continue; } } else if ((fd = open(cmdline->redir,O_CREAT|O_WRONLY| (cmdline->append?O_APPEND:O_TRUNC), 0666)) < 0) { fprintf(stderr, "Error opening %s: %s\n",cmdline->redir, strerror(errno)); continue; } out=dup(1); dup2(fd,1); close(fd); } cmd->fn(cur_dir,cmdline->argc-1,&cmdline->argv[1]); if (cmdline->redir) { dup2(out,1); close(out); if (cmdline->piped) wait(NULL); } } } while (1); cleanup: vmfs_dir_close(cur_dir); freecmd(cmdline); return(0); } int main(int argc,char *argv[]) { vmfs_fs_t *fs; vmfs_dir_t *root_dir; struct cmd *cmd = NULL; int arg, ret; vmfs_flags_t flags; if (argc < 3) { show_usage(argv[0]); return(0); } /* Scan arguments for a command */ for (arg = 1; arg < argc; arg++) { if ((cmd = cmd_find(argv[arg]))) break; } if (!cmd) { show_usage(argv[0]); return(0); } flags.packed = 0; #ifdef VMFS_WRITE flags.read_write = 1; #endif flags.allow_missing_extents = 1; argv[arg] = NULL; if (!(fs = vmfs_fs_open(&argv[1], flags))) { fprintf(stderr,"Unable to open filesystem\n"); exit(EXIT_FAILURE); } if (!(root_dir = vmfs_dir_open_from_blkid(fs,VMFS_BLK_FD_BUILD(0, 0, 0)))) { fprintf(stderr,"Unable to open root directory\n"); exit(EXIT_FAILURE); } ret = cmd->fn(root_dir,argc-arg-1,&argv[arg+1]); vmfs_dir_close(root_dir); vmfs_fs_close(fs); return(ret); } vmfs-tools-0.2.5/libvmfs/0000755000175000017500000000000011733555001013063 5ustar mhmhvmfs-tools-0.2.5/libvmfs/vmfs_block.c0000644000175000017500000002520711733550471015370 0ustar mhmh/* * vmfs-tools - Tools to access VMFS filesystems * Copyright (C) 2009 Christophe Fillot * Copyright (C) 2009,2012 Mike Hommey * * 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. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /* * VMFS blocks. */ #include #include #include #include #include #include "utils.h" #include "vmfs.h" /* Get bitmap info (bitmap type,entry and item) from a block ID */ int vmfs_block_get_info(uint32_t blk_id, vmfs_block_info_t *info) { uint32_t blk_type; blk_type = VMFS_BLK_TYPE(blk_id); switch(blk_type) { /* File Block */ case VMFS_BLK_TYPE_FB: info->entry = 0; info->item = VMFS_BLK_FB_ITEM(blk_id); info->flags = VMFS_BLK_FB_FLAGS(blk_id); break; /* Sub-Block */ case VMFS_BLK_TYPE_SB: info->entry = VMFS_BLK_SB_ENTRY(blk_id); info->item = VMFS_BLK_SB_ITEM(blk_id); info->flags = VMFS_BLK_SB_FLAGS(blk_id); break; /* Pointer Block */ case VMFS_BLK_TYPE_PB: info->entry = VMFS_BLK_PB_ENTRY(blk_id); info->item = VMFS_BLK_PB_ITEM(blk_id); info->flags = VMFS_BLK_PB_FLAGS(blk_id); info->flags = 0; break; /* Inode */ case VMFS_BLK_TYPE_FD: info->entry = VMFS_BLK_FD_ENTRY(blk_id); info->item = VMFS_BLK_FD_ITEM(blk_id); info->flags = VMFS_BLK_FD_FLAGS(blk_id); info->flags = 0; break; default: return(-1); } info->type = blk_type; return(0); } /* Get block status (allocated or free) */ int vmfs_block_get_status(const vmfs_fs_t *fs,uint32_t blk_id) { vmfs_bitmap_entry_t entry; vmfs_bitmap_t *bmp; vmfs_block_info_t info; if (vmfs_block_get_info(blk_id,&info) == -1) return(-1); if (!(bmp = vmfs_fs_get_bitmap(fs, info.type))) return(-1); if (vmfs_bitmap_get_entry(bmp,info.entry,info.item,&entry) == -1) return(-1); return(vmfs_bitmap_get_item_status(&bmp->bmh,&entry,info.entry,info.item)); } /* Allocate or free the specified block */ static int vmfs_block_set_status(const vmfs_fs_t *fs,uint32_t blk_id, int status) { DECL_ALIGNED_BUFFER(buf,VMFS_BITMAP_ENTRY_SIZE); vmfs_bitmap_entry_t entry; vmfs_bitmap_t *bmp; vmfs_block_info_t info; if (vmfs_block_get_info(blk_id,&info) == -1) return(-1); if (!(bmp = vmfs_fs_get_bitmap(fs, info.type))) return(-1); if (vmfs_bitmap_get_entry(bmp,info.entry,info.item,&entry) == -1) return(-1); /* Lock the bitmap entry to ensure exclusive access */ if (!vmfs_metadata_lock((vmfs_fs_t *)fs,entry.mdh.pos, buf,buf_len,&entry.mdh) == -1) return(-1); /* Mark the item as allocated */ if (vmfs_bitmap_set_item_status(&bmp->bmh,&entry, info.entry,info.item,status) == -1) { vmfs_metadata_unlock((vmfs_fs_t *)fs,&entry.mdh); return(-1); } /* Update entry and release lock */ vmfs_bme_update(fs,&entry); vmfs_metadata_unlock((vmfs_fs_t *)fs,&entry.mdh); return(0); } /* Allocate the specified block */ int vmfs_block_alloc_specified(const vmfs_fs_t *fs,uint32_t blk_id) { return(vmfs_block_set_status(fs,blk_id,1)); } /* Free the specified block */ int vmfs_block_free(const vmfs_fs_t *fs,uint32_t blk_id) { return(vmfs_block_set_status(fs,blk_id,0)); } /* Allocate a single block */ int vmfs_block_alloc(const vmfs_fs_t *fs,uint32_t blk_type,uint32_t *blk_id) { vmfs_bitmap_t *bmp; vmfs_bitmap_entry_t entry; uint32_t item,addr; if (!(bmp = vmfs_fs_get_bitmap(fs, blk_type))) return(-EINVAL); if (vmfs_bitmap_find_free_items(bmp,1,&entry) == -1) return(-ENOSPC); if (vmfs_bitmap_alloc_item(&entry,&item) == -1) { vmfs_metadata_unlock((vmfs_fs_t *)fs,&entry.mdh); return(-ENOSPC); } vmfs_bme_update(fs,&entry); vmfs_metadata_unlock((vmfs_fs_t *)fs,&entry.mdh); switch(blk_type) { case VMFS_BLK_TYPE_FB: addr = (entry.id * bmp->bmh.items_per_bitmap_entry) + item; *blk_id = VMFS_BLK_FB_BUILD(addr, 0); break; case VMFS_BLK_TYPE_SB: *blk_id = VMFS_BLK_SB_BUILD(entry.id, item, 0); break; case VMFS_BLK_TYPE_PB: *blk_id = VMFS_BLK_PB_BUILD(entry.id, item, 0); break; case VMFS_BLK_TYPE_FD: *blk_id = VMFS_BLK_FD_BUILD(entry.id, item, 0); break; } return(0); } /* Zeroize a file block */ int vmfs_block_zeroize_fb(const vmfs_fs_t *fs,uint32_t blk_id) { DECL_ALIGNED_BUFFER(buf,M_DIO_BLK_SIZE); uint32_t blk_item; off_t pos,len; if (VMFS_BLK_TYPE(blk_id) != VMFS_BLK_TYPE_FB) return(-EINVAL); memset(buf,0,buf_len); blk_item = VMFS_BLK_FB_ITEM(blk_id); len = vmfs_fs_get_blocksize(fs); pos = 0; while(pos < len) { if (vmfs_fs_write(fs,blk_item,pos,buf,buf_len) != buf_len) return(-EIO); pos += buf_len; } return(0); } /* Free blocks hold by a pointer block */ int vmfs_block_free_pb(const vmfs_fs_t *fs,uint32_t pb_blk, u_int start,u_int end) { DECL_ALIGNED_BUFFER(buf,fs->pbc->bmh.data_size); uint32_t pbc_entry,pbc_item; uint32_t blk_id; int i,count = 0; if (VMFS_BLK_TYPE(pb_blk) != VMFS_BLK_TYPE_PB) return(-EINVAL); pbc_entry = VMFS_BLK_PB_ENTRY(pb_blk); pbc_item = VMFS_BLK_PB_ITEM(pb_blk); if (!vmfs_bitmap_get_item(fs->pbc,pbc_entry,pbc_item,buf)) return(-EIO); for(i=start;ipbc,pbc_entry,pbc_item,buf)) return(-EIO); } return(count); } /* Read a piece of a sub-block */ ssize_t vmfs_block_read_sb(const vmfs_fs_t *fs,uint32_t blk_id,off_t pos, u_char *buf,size_t len) { DECL_ALIGNED_BUFFER_WOL(tmpbuf,fs->sbc->bmh.data_size); uint32_t offset,sbc_entry,sbc_item; size_t clen; offset = pos % fs->sbc->bmh.data_size; clen = m_min(fs->sbc->bmh.data_size - offset,len); sbc_entry = VMFS_BLK_SB_ENTRY(blk_id); sbc_item = VMFS_BLK_SB_ITEM(blk_id); if (!vmfs_bitmap_get_item(fs->sbc,sbc_entry,sbc_item,tmpbuf)) return(-EIO); memcpy(buf,tmpbuf+offset,clen); return(clen); } /* Write a piece of a sub-block */ ssize_t vmfs_block_write_sb(const vmfs_fs_t *fs,uint32_t blk_id,off_t pos, u_char *buf,size_t len) { DECL_ALIGNED_BUFFER_WOL(tmpbuf,fs->sbc->bmh.data_size); uint32_t offset,sbc_entry,sbc_item; size_t clen; offset = pos % fs->sbc->bmh.data_size; clen = m_min(fs->sbc->bmh.data_size - offset,len); sbc_entry = VMFS_BLK_SB_ENTRY(blk_id); sbc_item = VMFS_BLK_SB_ITEM(blk_id); /* If we write completely the sub-block, no need to read something */ if (!offset && (clen == fs->sbc->bmh.data_size)) { if (!vmfs_bitmap_set_item(fs->sbc,sbc_entry,sbc_item,tmpbuf)) return(-EIO); return(clen); } /* Read the full block and update a piece of it */ if (!vmfs_bitmap_get_item(fs->sbc,sbc_entry,sbc_item,tmpbuf)) return(-EIO); memcpy(tmpbuf+offset,buf,clen); if (!vmfs_bitmap_set_item(fs->sbc,sbc_entry,sbc_item,tmpbuf)) return(-EIO); return(clen); } /* Read a piece of a file block */ ssize_t vmfs_block_read_fb(const vmfs_fs_t *fs,uint32_t blk_id,off_t pos, u_char *buf,size_t len) { uint64_t offset,n_offset,blk_size; size_t clen,n_clen; uint32_t fb_item; u_char *tmpbuf; blk_size = vmfs_fs_get_blocksize(fs); offset = pos % blk_size; clen = m_min(blk_size - offset,len); /* Use "normalized" offset / length to access data (for direct I/O) */ n_offset = offset & ~(M_DIO_BLK_SIZE - 1); n_clen = ALIGN_NUM(clen + (offset - n_offset),M_DIO_BLK_SIZE); fb_item = VMFS_BLK_FB_ITEM(blk_id); /* If everything is aligned for direct I/O, store directly in user buffer */ if ((n_offset == offset) && (n_clen == clen) && ALIGN_CHECK((uintptr_t)buf,M_DIO_BLK_SIZE)) { if (vmfs_fs_read(fs,fb_item,n_offset,buf,n_clen) != n_clen) return(-EIO); return(n_clen); } /* Allocate a temporary buffer and copy result to user buffer */ if (!(tmpbuf = iobuffer_alloc(n_clen))) return(-1); if (vmfs_fs_read(fs,fb_item,n_offset,tmpbuf,n_clen) != n_clen) { iobuffer_free(tmpbuf); return(-EIO); } memcpy(buf,tmpbuf+(offset-n_offset),clen); iobuffer_free(tmpbuf); return(clen); } /* Write a piece of a file block */ ssize_t vmfs_block_write_fb(const vmfs_fs_t *fs,uint32_t blk_id,off_t pos, u_char *buf,size_t len) { uint64_t offset,n_offset,blk_size; size_t clen,n_clen; uint32_t fb_item; u_char *tmpbuf; blk_size = vmfs_fs_get_blocksize(fs); offset = pos % blk_size; clen = m_min(blk_size - offset,len); /* Use "normalized" offset / length to access data (for direct I/O) */ n_offset = offset & ~(M_DIO_BLK_SIZE - 1); n_clen = ALIGN_NUM(clen + (offset - n_offset),M_DIO_BLK_SIZE); fb_item = VMFS_BLK_FB_ITEM(blk_id); /* * If everything is aligned for direct I/O, write directly from user * buffer. */ if ((n_offset == offset) && (n_clen == clen) && ALIGN_CHECK((uintptr_t)buf,M_DIO_BLK_SIZE)) { if (vmfs_fs_write(fs,fb_item,n_offset,buf,n_clen) != n_clen) return(-EIO); return(n_clen); } /* Allocate a temporary buffer */ if (!(tmpbuf = iobuffer_alloc(n_clen))) return(-1); /* Read the original block and add user data */ if (vmfs_fs_read(fs,fb_item,n_offset,tmpbuf,n_clen) != n_clen) goto err_io; memcpy(tmpbuf+(offset-n_offset),buf,clen); /* Write the modified block */ if (vmfs_fs_write(fs,fb_item,n_offset,tmpbuf,n_clen) != n_clen) goto err_io; iobuffer_free(tmpbuf); return(clen); err_io: iobuffer_free(tmpbuf); return(-EIO); } vmfs-tools-0.2.5/libvmfs/utils.c0000644000175000017500000001212011733550414014366 0ustar mhmh/* * vmfs-tools - Tools to access VMFS filesystems * Copyright (C) 2009 Christophe Fillot * Copyright (C) 2009 Mike Hommey * * 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. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /* * Utility functions. */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #ifdef NO_POSIX_MEMALIGN #include #endif #include "utils.h" /* Convert an UUID into a string */ char *m_uuid_to_str(const uuid_t uuid,char *str) { uint32_t time_low; uint32_t time_mid; uint16_t clock_seq; time_low = read_le32(uuid,0); time_mid = read_le32(uuid,4); clock_seq = read_le16(uuid,8); sprintf(str, "%02x%02x%02x%02x-%02x%02x%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", time_low >> 24, time_low >> 16 & 0xff, time_low >> 8 & 0xff, time_low & 0xff, time_mid >> 24, time_mid >> 16 & 0xff, time_mid >> 8 & 0xff, time_mid & 0xff, clock_seq >> 8 & 0xff, clock_seq & 0xff, uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], uuid[15]); return str; } /* Convert a timestamp to a string */ char *m_ctime(const time_t *ct,char *buf,size_t buf_len) { struct tm ctm; localtime_r(ct,&ctm); snprintf(buf,buf_len,"%4.4d-%2.2d-%2.2d %2.2d:%2.2d:%2.2d", ctm.tm_year + 1900, ctm.tm_mon + 1, ctm.tm_mday, ctm.tm_hour, ctm.tm_min, ctm.tm_sec); return buf; } struct fmode_info { u_int flag; char c; int pos; }; static struct fmode_info fmode_flags[] = { { S_IFDIR, 'd', 0 }, { S_IFLNK, 'l', 0 }, { S_IRUSR, 'r', 1 }, { S_IWUSR, 'w', 2 }, { S_IXUSR, 'x', 3 }, { S_IRGRP, 'r', 4 }, { S_IWGRP, 'w', 5 }, { S_IXGRP, 'x', 6 }, { S_IROTH, 'r', 7 }, { S_IWOTH, 'w', 8 }, { S_IXOTH, 'x', 9 }, { S_ISUID, 's', 3 }, { S_ISVTX, 't', 9 }, { 0, 0, -1, }, }; /* Convert a file mode to a string */ char *m_fmode_to_str(u_int mode,char *buf) { struct fmode_info *fi; int i; for(i=0;i<10;i++) buf[i] = '-'; buf[10] = 0; for(i=0;fmode_flags[i].flag;i++) { fi = &fmode_flags[i]; if ((mode & fi->flag) == fi->flag) buf[fi->pos] = fi->c; } return buf; } /* Count the number of bits set in a byte */ int bit_count(u_char val) { static int qb[16] = { 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, }; return(qb[val >> 4] + qb[val & 0x0F]); } /* Allocate a buffer with alignment compatible for direct I/O */ u_char *iobuffer_alloc(size_t len) { size_t buf_len; void *buf; buf_len = ALIGN_NUM(len,M_DIO_BLK_SIZE); #ifdef NO_POSIX_MEMALIGN if (!(buf = memalign(M_DIO_BLK_SIZE,buf_len))) #else if (posix_memalign((void **)&buf,M_DIO_BLK_SIZE,buf_len)) #endif return NULL; return buf; } /* Free a buffer previously allocated by iobuffer_alloc() */ void iobuffer_free(u_char *buf) { free(buf); } /* Read from file descriptor at a given offset */ ssize_t m_pread(int fd,void *buf,size_t count,off_t offset) { int max_retries = 10; u_char *ptr = (u_char *)buf; size_t hlen = 0; ssize_t len; while(hlen < count) { len = pread(fd,ptr,count-hlen,offset+hlen); if (len < 0) { if (errno == EIO) { if (max_retries-- == 0) return(-1); continue; } if (errno != EINTR) return(-1); } else { if (len == 0) break; hlen += len; ptr += len; } } return(hlen); } /* Write to a file descriptor at a given offset */ ssize_t m_pwrite(int fd,const void *buf,size_t count,off_t offset) { int max_retries = 10; u_char *ptr = (u_char *)buf; size_t hlen = 0; ssize_t len; while(hlen < count) { len = pwrite(fd,ptr,count-hlen,offset+hlen); if (len < 0) { if (errno == EIO) { if (max_retries-- == 0) return(-1); continue; } if (errno != EINTR) return(-1); } else { if (len == 0) break; hlen += len; ptr += len; } } return(hlen); } /* Returns directory name */ char *m_dirname(const char *path) { char *dirc = strdup(path); char *dname = strdup(dirname(dirc)); free(dirc); return(dname); } /* Returns base name */ char *m_basename(const char *path) { char *basec = strdup(path); char *bname = strdup(basename(basec)); free(basec); return(bname); } vmfs-tools-0.2.5/libvmfs/vmfs_bitmap.h0000644000175000017500000001305011733407353015550 0ustar mhmh/* * vmfs-tools - Tools to access VMFS filesystems * Copyright (C) 2009 Christophe Fillot * Copyright (C) 2009 Mike Hommey * * 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. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef VMFS_BITMAP_H #define VMFS_BITMAP_H #include /* Bitmaps magic numbers */ #define VMFS_BITMAP_MAGIC_FBB 0x10c00002 #define VMFS_BITMAP_MAGIC_SBC 0x10c00003 #define VMFS_BITMAP_MAGIC_PBC 0x10c00004 #define VMFS_BITMAP_MAGIC_FDC 0x10c00005 /* === Bitmap header === */ struct vmfs_bitmap_header { uint32_t items_per_bitmap_entry; uint32_t bmp_entries_per_area; uint32_t hdr_size; uint32_t data_size; uint32_t area_size; uint32_t total_items; uint32_t area_count; }; /* === Bitmap entry === */ #define VMFS_BITMAP_ENTRY_SIZE 0x400 #define VMFS_BITMAP_BMP_MAX_SIZE 0x1f0 struct vmfs_bitmap_entry_raw { struct vmfs_metadata_hdr_raw mdh; /* Metadata header */ uint32_t id; /* Bitmap ID */ uint32_t total; /* Total number of items in this entry */ uint32_t free; /* Free items */ uint32_t ffree; /* First free item */ uint8_t bitmap[VMFS_BITMAP_BMP_MAX_SIZE]; } __attribute__((packed)); #define VMFS_BME_OFS_ID offsetof(struct vmfs_bitmap_entry_raw, id) #define VMFS_BME_OFS_TOTAL offsetof(struct vmfs_bitmap_entry_raw, total) #define VMFS_BME_OFS_FREE offsetof(struct vmfs_bitmap_entry_raw, free) #define VMFS_BME_OFS_FFREE offsetof(struct vmfs_bitmap_entry_raw, ffree) #define VMFS_BME_OFS_BITMAP offsetof(struct vmfs_bitmap_entry_raw, bitmap) struct vmfs_bitmap_entry { vmfs_metadata_hdr_t mdh; uint32_t id; uint32_t total; uint32_t free; uint32_t ffree; uint8_t bitmap[VMFS_BITMAP_BMP_MAX_SIZE]; }; /* A bitmap file instance */ struct vmfs_bitmap { vmfs_file_t *f; vmfs_bitmap_header_t bmh; }; /* Callback prototype for vmfs_bitmap_foreach() */ typedef void (*vmfs_bitmap_foreach_cbk_t)(vmfs_bitmap_t *b,uint32_t addr, void *opt_arg); /* Read a bitmap entry */ int vmfs_bme_read(vmfs_bitmap_entry_t *bme,const u_char *buf,int copy_bitmap); /* Write a bitmap entry */ int vmfs_bme_write(const vmfs_bitmap_entry_t *bme,u_char *buf); /* Update a bitmap entry on disk */ int vmfs_bme_update(const vmfs_fs_t *fs,const vmfs_bitmap_entry_t *bme); /* Read a bitmap entry given a block id */ int vmfs_bitmap_get_entry(vmfs_bitmap_t *b,uint32_t entry,uint32_t item, vmfs_bitmap_entry_t *bmp_entry); /* Get position of an item */ off_t vmfs_bitmap_get_item_pos(vmfs_bitmap_t *b,uint32_t entry,uint32_t item); /* Read a bitmap item from its entry and item numbers */ bool vmfs_bitmap_get_item(vmfs_bitmap_t *b, uint32_t entry, uint32_t item, u_char *buf); /* Write a bitmap given its entry and item numbers */ bool vmfs_bitmap_set_item(vmfs_bitmap_t *b,uint32_t entry,uint32_t item, u_char *buf); /* Mark an item as free or allocated */ int vmfs_bitmap_set_item_status(const vmfs_bitmap_header_t *bmh, vmfs_bitmap_entry_t *bmp_entry, uint32_t entry,uint32_t item, int status); /* Get the status of an item (0=free,1=allocated) */ int vmfs_bitmap_get_item_status(const vmfs_bitmap_header_t *bmh, vmfs_bitmap_entry_t *bmp_entry, uint32_t entry,uint32_t item); /* Find a free item in a bitmap entry and mark it allocated */ int vmfs_bitmap_alloc_item(vmfs_bitmap_entry_t *bmp_entry,uint32_t *item); /* Find a bitmap entry with at least "num_items" free in the specified area */ int vmfs_bitmap_area_find_free_items(vmfs_bitmap_t *b, u_int area,u_int num_items, vmfs_bitmap_entry_t *entry); /* Find a bitmap entry with at least "num_items" free (scan all areas) */ int vmfs_bitmap_find_free_items(vmfs_bitmap_t *b,u_int num_items, vmfs_bitmap_entry_t *entry); /* Count the total number of allocated items in a bitmap area */ uint32_t vmfs_bitmap_area_allocated_items(vmfs_bitmap_t *b,u_int area); /* Count the total number of allocated items in a bitmap */ uint32_t vmfs_bitmap_allocated_items(vmfs_bitmap_t *b); /* Call a user function for each allocated item in a bitmap */ void vmfs_bitmap_area_foreach(vmfs_bitmap_t *b,u_int area, vmfs_bitmap_foreach_cbk_t cbk, void *opt_arg); /* Call a user function for each allocated item in a bitmap */ void vmfs_bitmap_foreach(vmfs_bitmap_t *b,vmfs_bitmap_foreach_cbk_t cbk, void *opt_arg); /* Check coherency of a bitmap file */ int vmfs_bitmap_check(vmfs_bitmap_t *b); /* Open a bitmap file */ vmfs_bitmap_t *vmfs_bitmap_open_at(vmfs_dir_t *d, const char *name); vmfs_bitmap_t *vmfs_bitmap_open_from_inode(const vmfs_inode_t *inode); /* Close a bitmap file */ void vmfs_bitmap_close(vmfs_bitmap_t *b); #endif vmfs-tools-0.2.5/libvmfs/vmfs_fs.h0000644000175000017500000001037111733303644014705 0ustar mhmh/* * vmfs-tools - Tools to access VMFS filesystems * Copyright (C) 2009 Christophe Fillot * Copyright (C) 2009 Mike Hommey * * 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. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef VMFS_FS_H #define VMFS_FS_H #include /* === FS Info === */ #define VMFS_FSINFO_BASE 0x0200000 #define VMFS_FSINFO_MAGIC 0x2fabf15e struct vmfs_fsinfo_raw { uint32_t magic; uint32_t volver; u_char ver; uuid_t uuid; uint32_t mode; char label[128]; uint32_t dev_blocksize; uint64_t blocksize; uint32_t ctime; /* ctime? in seconds */ uint32_t _unknown3; uuid_t lvm_uuid; u_char _unknown4[16]; uint32_t fdc_header_size; uint32_t fdc_bitmap_count; uint32_t subblock_size; } __attribute__((packed)); #define VMFS_FSINFO_OFS_MAGIC offsetof(struct vmfs_fsinfo_raw, magic) #define VMFS_FSINFO_OFS_VOLVER offsetof(struct vmfs_fsinfo_raw, volver) #define VMFS_FSINFO_OFS_VER offsetof(struct vmfs_fsinfo_raw, ver) #define VMFS_FSINFO_OFS_UUID offsetof(struct vmfs_fsinfo_raw, uuid) #define VMFS_FSINFO_OFS_MODE offsetof(struct vmfs_fsinfo_raw, mode) #define VMFS_FSINFO_OFS_LABEL offsetof(struct vmfs_fsinfo_raw, label) #define VMFS_FSINFO_OFS_BLKSIZE offsetof(struct vmfs_fsinfo_raw, blocksize) #define VMFS_FSINFO_OFS_CTIME offsetof(struct vmfs_fsinfo_raw, ctime) #define VMFS_FSINFO_OFS_LVM_UUID offsetof(struct vmfs_fsinfo_raw, lvm_uuid) #define VMFS_FSINFO_OFS_SBSIZE offsetof(struct vmfs_fsinfo_raw, subblock_size) #define VMFS_FSINFO_OFS_FDC_HEADER_SIZE \ offsetof(struct vmfs_fsinfo_raw, fdc_header_size) #define VMFS_FSINFO_OFS_FDC_BITMAP_COUNT \ offsetof(struct vmfs_fsinfo_raw, fdc_bitmap_count) #define VMFS_FSINFO_OFS_LABEL_SIZE sizeof(((struct vmfs_fsinfo_raw *)(0))->label) struct vmfs_fsinfo { uint32_t magic; uint32_t vol_version; uint32_t version; uint32_t mode; uuid_t uuid; char *label; time_t ctime; uint64_t block_size; uint32_t subblock_size; uint32_t fdc_header_size; uint32_t fdc_bitmap_count; uuid_t lvm_uuid; }; /* === VMFS filesystem === */ #define VMFS_INODE_HASH_BUCKETS 256 struct vmfs_fs { int debug_level; /* FS information */ vmfs_fsinfo_t fs_info; /* Associated VMFS Device */ vmfs_device_t *dev; /* Meta-files containing file system structures */ vmfs_bitmap_t *fbb,*sbc,*pbc,*fdc; /* Heartbeat used to lock meta-data */ vmfs_heartbeat_t hb; u_int hb_id; uint64_t hb_seq; u_int hb_refcount; uint64_t hb_expire; /* Counter for "gen" field in inodes */ uint32_t inode_gen; /* In-core inodes hash table */ u_int inode_hash_buckets; vmfs_inode_t **inodes; }; /* Get the bitmap corresponding to the given type */ static inline vmfs_bitmap_t *vmfs_fs_get_bitmap(const vmfs_fs_t *fs, enum vmfs_block_type type) { if ((type > VMFS_BLK_TYPE_NONE) && (type < VMFS_BLK_TYPE_MAX)) { vmfs_bitmap_t * const *bitmap = (vmfs_bitmap_t **)&fs->fbb; return bitmap[type - 1]; } return NULL; } /* Get block size of a volume */ static inline uint64_t vmfs_fs_get_blocksize(const vmfs_fs_t *fs) { return(fs->fs_info.block_size); } /* Get read-write status of a FS */ static inline bool vmfs_fs_readwrite(const vmfs_fs_t *fs) { return(fs->dev->write); } /* Read a block from the filesystem */ ssize_t vmfs_fs_read(const vmfs_fs_t *fs,uint32_t blk,off_t offset, u_char *buf,size_t len); /* Write a block to the filesystem */ ssize_t vmfs_fs_write(const vmfs_fs_t *fs,uint32_t blk,off_t offset, const u_char *buf,size_t len); /* Open a FS */ vmfs_fs_t *vmfs_fs_open(char **paths, vmfs_flags_t flags); /* Close a FS */ void vmfs_fs_close(vmfs_fs_t *fs); #endif vmfs-tools-0.2.5/libvmfs/vmfs_metadata.h0000644000175000017500000000526011733303644016056 0ustar mhmh/* * vmfs-tools - Tools to access VMFS filesystems * Copyright (C) 2009 Christophe Fillot * Copyright (C) 2009 Mike Hommey * * 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. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef VMFS_METADATA_H #define VMFS_METADATA_H #include #define VMFS_METADATA_HDR_SIZE 512 struct vmfs_metadata_hdr_raw { uint32_t magic; /* Magic number */ uint64_t pos; /* Position in the volume */ uint64_t hb_pos; /* Heartbeat position */ uint64_t hb_seq; /* Heartbeat sequence */ uint64_t obj_seq; /* Object sequence */ uint32_t hb_lock; /* Heartbeat lock flag */ uuid_t hb_uuid; /* UUID of locking server */ uint64_t mtime; u_char pad1[0x1c0]; /* Padding/unknown */ } __attribute__((packed)); #define VMFS_MDH_OFS_MAGIC offsetof(struct vmfs_metadata_hdr_raw, magic) #define VMFS_MDH_OFS_POS offsetof(struct vmfs_metadata_hdr_raw, pos) #define VMFS_MDH_OFS_HB_POS offsetof(struct vmfs_metadata_hdr_raw, hb_pos) #define VMFS_MDH_OFS_HB_SEQ offsetof(struct vmfs_metadata_hdr_raw, hb_seq) #define VMFS_MDH_OFS_OBJ_SEQ offsetof(struct vmfs_metadata_hdr_raw, obj_seq) #define VMFS_MDH_OFS_HB_LOCK offsetof(struct vmfs_metadata_hdr_raw, hb_lock) #define VMFS_MDH_OFS_HB_UUID offsetof(struct vmfs_metadata_hdr_raw, hb_uuid) #define VMFS_MDH_OFS_MTIME offsetof(struct vmfs_metadata_hdr_raw, mtime) struct vmfs_metadata_hdr { uint32_t magic; uint64_t pos; uint64_t hb_pos; uint64_t hb_seq; uint64_t obj_seq; uint32_t hb_lock; uuid_t hb_uuid; uint64_t mtime; }; static inline bool vmfs_metadata_is_locked(vmfs_metadata_hdr_t *mdh) { return(mdh->hb_lock != 0); } /* Read a metadata header */ int vmfs_metadata_hdr_read(vmfs_metadata_hdr_t *mdh,const u_char *buf); /* Write a metadata header */ int vmfs_metadata_hdr_write(const vmfs_metadata_hdr_t *mdh,u_char *buf); /* Lock and read metadata at specified position */ int vmfs_metadata_lock(vmfs_fs_t *fs,off_t pos,u_char *buf,size_t buf_len, vmfs_metadata_hdr_t *mdh); /* Unlock metadata */ int vmfs_metadata_unlock(vmfs_fs_t *fs,vmfs_metadata_hdr_t *mdh); #endif vmfs-tools-0.2.5/libvmfs/vmfs_host.h0000644000175000017500000000203711733303644015252 0ustar mhmh/* * vmfs-tools - Tools to access VMFS filesystems * Copyright (C) 2009 Christophe Fillot * Copyright (C) 2009 Mike Hommey * * 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. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef VMFS_HOST_H #define VMFS_HOST_H /* Initialize host info (UUID,uptime,...) */ int vmfs_host_init(void); /* Show host info */ void vmfs_host_show_info(void); /* Get host uptime (in usecs) */ uint64_t vmfs_host_get_uptime(void); /* Get host UUID */ void vmfs_host_get_uuid(uuid_t dst); #endif vmfs-tools-0.2.5/libvmfs/vmfs_volume.c0000644000175000017500000001513611733551061015601 0ustar mhmh/* * vmfs-tools - Tools to access VMFS filesystems * Copyright (C) 2009 Christophe Fillot * Copyright (C) 2009,2011 Mike Hommey * * 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. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /* * VMFS volumes. */ #define _GNU_SOURCE #include #include #include #include #include #include #include "vmfs.h" #include "scsi.h" /* Read a raw block of data on logical volume */ static ssize_t vmfs_vol_read(const vmfs_device_t *dev,off_t pos, u_char *buf,size_t len) { vmfs_volume_t *vol = (vmfs_volume_t *) dev; pos += vol->vmfs_base + 0x1000000; return(m_pread(vol->fd,buf,len,pos)); } /* Write a raw block of data on logical volume */ static ssize_t vmfs_vol_write(const vmfs_device_t *dev,off_t pos, const u_char *buf,size_t len) { vmfs_volume_t *vol = (vmfs_volume_t *) dev; pos += vol->vmfs_base + 0x1000000; return(m_pwrite(vol->fd,buf,len,pos)); } /* Volume reservation */ static int vmfs_vol_reserve(const vmfs_device_t *dev, off_t pos) { vmfs_volume_t *vol = (vmfs_volume_t *) dev; return(scsi_reserve(vol->fd)); } /* Volume release */ static int vmfs_vol_release(const vmfs_device_t *dev, off_t pos) { vmfs_volume_t *vol = (vmfs_volume_t *) dev; return(scsi_release(vol->fd)); } /* * Check if physical volume support reservation. * TODO: We should probably check some capabilities info. */ static int vmfs_vol_check_reservation(vmfs_volume_t *vol) { int res[2]; /* The device must be a block device */ if (!vol->is_blkdev) return(0); /* Try SCSI commands */ res[0] = scsi_reserve(vol->fd); res[1] = scsi_release(vol->fd); /* Error with the commands */ if ((res[0] < 0) || (res[1] < 0)) return(0); vol->dev.reserve = vmfs_vol_reserve; vol->dev.release = vmfs_vol_release; return(1); } /* Read volume information */ static int vmfs_volinfo_read(vmfs_volume_t *volume) { DECL_ALIGNED_BUFFER(buf,1024); vmfs_volinfo_t *vol = &volume->vol_info; if (m_pread(volume->fd,buf,buf_len,volume->vmfs_base) != buf_len) return(-1); vol->magic = read_le32(buf,VMFS_VOLINFO_OFS_MAGIC); if (vol->magic != VMFS_VOLINFO_MAGIC) { fprintf(stderr,"VMFS VolInfo: invalid magic number 0x%8.8x\n", vol->magic); return(-1); } vol->version = read_le32(buf,VMFS_VOLINFO_OFS_VER); vol->size = read_le32(buf,VMFS_VOLINFO_OFS_SIZE); vol->lun = buf[VMFS_VOLINFO_OFS_LUN]; vol->name = strndup((char *)buf+VMFS_VOLINFO_OFS_NAME, VMFS_VOLINFO_OFS_NAME_SIZE); read_uuid(buf,VMFS_VOLINFO_OFS_UUID,&vol->uuid); vol->lvm_size = read_le64(buf,VMFS_LVMINFO_OFS_SIZE); vol->blocks = read_le64(buf,VMFS_LVMINFO_OFS_BLKS); vol->num_segments = read_le32(buf,VMFS_LVMINFO_OFS_NUM_SEGMENTS); vol->first_segment = read_le32(buf,VMFS_LVMINFO_OFS_FIRST_SEGMENT); vol->last_segment = read_le32(buf,VMFS_LVMINFO_OFS_LAST_SEGMENT); vol->num_extents = read_le32(buf,VMFS_LVMINFO_OFS_NUM_EXTENTS); read_uuid(buf,VMFS_LVMINFO_OFS_UUID,&vol->lvm_uuid); #ifdef VMFS_CHECK { /* The LVM UUID also appears as a string, so we can check whether our formatting function is correct. */ char uuidstr1[M_UUID_BUFLEN], uuidstr2[M_UUID_BUFLEN]; memcpy(uuidstr1,buf+VMFS_LVMINFO_OFS_UUID_STR,M_UUID_BUFLEN-1); uuidstr1[M_UUID_BUFLEN-1] = 0; if (memcmp(m_uuid_to_str(vol->lvm_uuid,uuidstr2),uuidstr1, M_UUID_BUFLEN-1)) { fprintf(stderr, "uuid mismatch:\n%s\n%s\n",uuidstr1,uuidstr2); return(-1); } } #endif return(0); } /* Close a VMFS volume */ static void vmfs_vol_close(vmfs_device_t *dev) { vmfs_volume_t *vol = (vmfs_volume_t *) dev; if (!vol) return; close(vol->fd); free(vol->device); free(vol->vol_info.name); free(vol); } /* Open a VMFS volume */ vmfs_volume_t *vmfs_vol_open(const char *filename,vmfs_flags_t flags) { vmfs_volume_t *vol; struct stat st; int file_flags; if (!(vol = calloc(1,sizeof(*vol)))) return NULL; if (!(vol->device = strdup(filename))) goto err_filename; file_flags = (flags.read_write) ? O_RDWR : O_RDONLY; if ((vol->fd = open(vol->device,file_flags)) < 0) { perror("open"); goto err_open; } vol->flags = flags; fstat(vol->fd,&st); vol->is_blkdev = S_ISBLK(st.st_mode); #if defined(O_DIRECT) || defined(DIRECTIO_ON) if (vol->is_blkdev) #ifdef O_DIRECT fcntl(vol->fd, F_SETFL, O_DIRECT); #else #ifdef DIRECTIO_ON directio(vol->fd, DIRECTIO_ON); #endif #endif #endif vol->vmfs_base = VMFS_VOLINFO_BASE; /* Read volume information */ if (vmfs_volinfo_read(vol) == -1) { DECL_ALIGNED_BUFFER(buf,512); uint16_t magic; fprintf(stderr,"VMFS: Unable to read volume information\n"); fprintf(stderr,"Trying to find partitions\n"); m_pread(vol->fd,buf,buf_len,0); magic = read_le16(buf, 510); if ((magic == 0xaa55) && (buf[450] == 0xfb)) { vol->vmfs_base += read_le32(buf, 454) * 512; if (vmfs_volinfo_read(vol) == -1) goto err_open; } else goto err_open; } /* We support only VMFS3 and VMFS5*/ if ((vol->vol_info.version != 3) && (vol->vol_info.version != 5)) { fprintf(stderr,"VMFS: Unsupported version %u\n",vol->vol_info.version); goto err_open; } if ((vol->vol_info.version == 5) && flags.read_write) { fprintf(stderr, "VMFS: Can't open VMFS read/write\n"); goto err_open; } if (vol->is_blkdev && (scsi_get_lun(vol->fd) != vol->vol_info.lun)) fprintf(stderr,"VMFS: Warning: Lun ID mismatch on %s\n", vol->device); vmfs_vol_check_reservation(vol); if (vol->flags.debug_level > 0) { printf("VMFS: volume opened successfully\n"); } vol->dev.read = vmfs_vol_read; if (vol->flags.read_write) vol->dev.write = vmfs_vol_write; vol->dev.close = vmfs_vol_close; vol->dev.uuid = &vol->vol_info.lvm_uuid; return vol; err_open: free(vol->device); err_filename: free(vol); return NULL; } vmfs-tools-0.2.5/libvmfs/vmfs_file.c0000644000175000017500000002342411733550570015214 0ustar mhmh/* * vmfs-tools - Tools to access VMFS filesystems * Copyright (C) 2009 Christophe Fillot * Copyright (C) 2009,2011,2012 Mike Hommey * * 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. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /* * VMFS file abstraction. */ #define _GNU_SOURCE #include #include #include #include #include "vmfs.h" /* Open a file based on an inode buffer */ vmfs_file_t *vmfs_file_open_from_inode(const vmfs_inode_t *inode) { vmfs_file_t *f; if (!(f = calloc(1,sizeof(*f)))) return NULL; f->inode = (vmfs_inode_t *)inode; return f; } /* Open a file based on a directory entry */ vmfs_file_t *vmfs_file_open_from_blkid(const vmfs_fs_t *fs,uint32_t blk_id) { vmfs_inode_t *inode; if (!(inode = vmfs_inode_acquire(fs,blk_id))) return NULL; return(vmfs_file_open_from_inode(inode)); } /* Open a file */ vmfs_file_t *vmfs_file_open_at(vmfs_dir_t *dir,const char *path) { uint32_t blk_id; if (!(blk_id = vmfs_dir_resolve_path(dir,path,1))) return(NULL); return(vmfs_file_open_from_blkid(vmfs_dir_get_fs(dir),blk_id)); } /* Create a new file entry */ int vmfs_file_create(vmfs_dir_t *d,const char *name,mode_t mode, vmfs_inode_t **inode) { vmfs_fs_t *fs = (vmfs_fs_t *)vmfs_dir_get_fs(d); vmfs_inode_t *new_inode; int res; if (!vmfs_fs_readwrite(fs)) return(-EROFS); if ((res = vmfs_inode_alloc(fs,VMFS_FILE_TYPE_FILE,mode,&new_inode)) < 0) return(res); if ((res = vmfs_dir_link_inode(d,name,new_inode)) < 0) { vmfs_block_free(fs,new_inode->id); vmfs_inode_release(new_inode); return(res); } *inode = new_inode; return(0); } /* Create a file */ vmfs_file_t *vmfs_file_create_at(vmfs_dir_t *dir,const char *path,mode_t mode) { char *dir_name,*base_name; vmfs_dir_t *d = NULL; vmfs_file_t *f = NULL; vmfs_inode_t *inode; dir_name = m_dirname(path); base_name = m_basename(path); if (!dir_name || !base_name) goto done; if (!(d = vmfs_dir_open_at(dir,dir_name))) goto done; if (vmfs_file_create(d,base_name,mode,&inode) < 0) goto done; if (!(f = vmfs_file_open_from_inode(inode))) vmfs_inode_release(inode); done: vmfs_dir_close(d); free(dir_name); free(base_name); return f; } /* Close a file */ int vmfs_file_close(vmfs_file_t *f) { if (f == NULL) return(-1); vmfs_inode_release(f->inode); free(f); return(0); } /* Read data from a file at the specified position */ ssize_t vmfs_file_pread(vmfs_file_t *f,u_char *buf,size_t len,off_t pos) { const vmfs_fs_t *fs = vmfs_file_get_fs(f); uint32_t blk_id,blk_type; uint64_t blk_size,blk_len; uint64_t file_size,offset; ssize_t res=0,rlen = 0; size_t exp_len; int err; /* We don't handle RDM files */ if (f->inode->type == VMFS_FILE_TYPE_RDM) return(-EIO); blk_size = vmfs_fs_get_blocksize(fs); file_size = vmfs_file_get_size(f); while(len > 0) { if (pos >= file_size) break; if ((err = vmfs_inode_get_block(f->inode,pos,&blk_id)) < 0) return(err); #if 0 if (f->vol->debug_level > 1) printf("vmfs_file_read: reading block 0x%8.8x\n",blk_id); #endif blk_type = VMFS_BLK_FB_TBZ(blk_id) ? VMFS_BLK_TYPE_NONE : VMFS_BLK_TYPE(blk_id); switch(blk_type) { /* Unallocated block */ case VMFS_BLK_TYPE_NONE: offset = pos % blk_size; blk_len = blk_size - offset; exp_len = m_min(blk_len,len); res = m_min(exp_len,file_size - pos); memset(buf,0,res); break; /* File-Block */ case VMFS_BLK_TYPE_FB: exp_len = m_min(len,file_size - pos); res = vmfs_block_read_fb(fs,blk_id,pos,buf,exp_len); break; /* Sub-Block */ case VMFS_BLK_TYPE_SB: { exp_len = m_min(len,file_size - pos); res = vmfs_block_read_sb(fs,blk_id,pos,buf,exp_len); break; } /* Inline in the inode */ case VMFS_BLK_TYPE_FD: if (blk_id == f->inode->id) { exp_len = m_min(len,file_size - pos); memcpy(buf, f->inode->content + pos, exp_len); res = exp_len; break; } default: fprintf(stderr,"VMFS: unknown block type 0x%2.2x\n",blk_type); return(-EIO); } /* Error while reading block, abort immediately */ if (res < 0) return(res); /* Move file position and keep track of bytes currently read */ pos += res; rlen += res; /* Move buffer position */ buf += res; len -= res; } return(rlen); } /* Write data to a file at the specified position */ ssize_t vmfs_file_pwrite(vmfs_file_t *f,u_char *buf,size_t len,off_t pos) { const vmfs_fs_t *fs = vmfs_file_get_fs(f); uint32_t blk_id,blk_type; ssize_t res=0,wlen = 0; int err; if (!vmfs_fs_readwrite(fs)) return(-EROFS); /* We don't handle RDM files */ if (f->inode->type == VMFS_FILE_TYPE_RDM) return(-EIO); while(len > 0) { if ((err = vmfs_inode_get_wrblock(f->inode,pos,&blk_id)) < 0) return(err); #if 0 if (f->vol->debug_level > 1) printf("vmfs_file_write: writing block 0x%8.8x\n",blk_id); #endif blk_type = VMFS_BLK_TYPE(blk_id); switch(blk_type) { /* File-Block */ case VMFS_BLK_TYPE_FB: res = vmfs_block_write_fb(fs,blk_id,pos,buf,len); break; /* Sub-Block */ case VMFS_BLK_TYPE_SB: res = vmfs_block_write_sb(fs,blk_id,pos,buf,len); break; default: fprintf(stderr,"VMFS: unknown block type 0x%2.2x\n",blk_type); return(-EIO); } /* Error while writing block, abort immediately */ if (res < 0) return(res); /* Move file position and keep track of bytes currently written */ pos += res; wlen += res; /* Move buffer position */ buf += res; len -= res; } /* Update file size */ if (pos > vmfs_file_get_size(f)) { f->inode->size = pos; f->inode->update_flags |= VMFS_INODE_SYNC_META; } return(wlen); } /* Dump a file */ int vmfs_file_dump(vmfs_file_t *f,off_t pos,uint64_t len,FILE *fd_out) { u_char *buf; ssize_t res; size_t clen,buf_len; if (!len) len = vmfs_file_get_size(f); buf_len = 0x100000; if (!(buf = iobuffer_alloc(buf_len))) return(-1); for(;pos < len; pos+=clen) { clen = m_min(len,buf_len); res = vmfs_file_pread(f,buf,clen,pos); if (res < 0) { fprintf(stderr,"vmfs_file_dump: problem reading input file.\n"); return(-1); } if (fwrite(buf,1,res,fd_out) != res) { fprintf(stderr,"vmfs_file_dump: error writing output file.\n"); return(-1); } if (res < clen) break; } free(buf); return(0); } /* Get file status */ int vmfs_file_fstat(const vmfs_file_t *f,struct stat *buf) { return(vmfs_inode_stat(f->inode,buf)); } /* Get file file status (follow symlink) */ int vmfs_file_stat_at(vmfs_dir_t *dir,const char *path,struct stat *buf) { uint32_t blk_id; if (!(blk_id = vmfs_dir_resolve_path(dir,path,1))) return(-ENOENT); return(vmfs_inode_stat_from_blkid(vmfs_dir_get_fs(dir),blk_id,buf)); } /* Get file file status (do not follow symlink) */ int vmfs_file_lstat_at(vmfs_dir_t *dir,const char *path,struct stat *buf) { const vmfs_dirent_t *entry; vmfs_dir_t *d; char *name; name = m_dirname(path); d = vmfs_dir_open_at(dir,name); free(name); if (!d) return(-1); name = m_basename(path); if (!strcmp(name,"/")) { free(name); return(vmfs_file_fstat(dir->dir,buf)); } entry = vmfs_dir_lookup(dir,name); free(name); if (!entry) return(-1); return(vmfs_inode_stat_from_blkid(vmfs_dir_get_fs(dir), entry->block_id,buf)); } /* Truncate a file (using a file descriptor) */ int vmfs_file_truncate(vmfs_file_t *f,off_t length) { return(vmfs_inode_truncate(f->inode,length)); } /* Truncate a file (using a path) */ int vmfs_file_truncate_at(vmfs_dir_t *dir,const char *path,off_t length) { vmfs_file_t *f; int res; if (!(f = vmfs_file_open_at(dir,path))) return(-ENOENT); res = vmfs_file_truncate(f,length); vmfs_file_close(f); return(res); } /* Change permissions of a file */ int vmfs_file_chmod(vmfs_file_t *f,mode_t mode) { return(vmfs_inode_chmod(f->inode,mode)); } /* Change permissions of a file (using a path) */ int vmfs_file_chmod_at(vmfs_dir_t *dir,const char *path,mode_t mode) { vmfs_file_t *f; int res; if (!(f = vmfs_file_open_at(dir,path))) return(-ENOENT); res = vmfs_file_chmod(f,mode); vmfs_file_close(f); return(res); } /* Delete a file */ int vmfs_file_delete(vmfs_dir_t *dir,const char *name) { vmfs_dirent_t *entry; off_t pos; if (!(entry = (vmfs_dirent_t *)vmfs_dir_lookup(dir,name))) return(-ENOENT); if ((entry->type != VMFS_FILE_TYPE_FILE) && (entry->type != VMFS_FILE_TYPE_SYMLINK)) return(-EPERM); pos = (dir->pos - 1) * VMFS_DIRENT_SIZE; return(vmfs_dir_unlink_inode(dir,pos,entry)); } vmfs-tools-0.2.5/libvmfs/vmfs_host.c0000644000175000017500000000336611733303644015253 0ustar mhmh/* * vmfs-tools - Tools to access VMFS filesystems * Copyright (C) 2009 Christophe Fillot * Copyright (C) 2009 Mike Hommey * * 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. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /* * VMFS host. */ #define _GNU_SOURCE #include #include #include "vmfs.h" static uuid_t host_uuid; static struct timeval host_tv_start; /* Initialize host info (UUID,uptime,...) */ int vmfs_host_init(void) { static bool initialized = 0; if (! initialized) { uuid_generate_time(host_uuid); gettimeofday(&host_tv_start,NULL); initialized = 1; } return(0); } /* Show host info */ void vmfs_host_show_info(void) { char uuid_str[M_UUID_BUFLEN]; printf("Host UUID : %s\n",m_uuid_to_str(host_uuid,uuid_str)); printf("Host Uptime : %"PRIu64" usecs\n",vmfs_host_get_uptime()); } /* Get host uptime (in usecs) */ uint64_t vmfs_host_get_uptime(void) { struct timeval cur_time,delta; uint64_t uptime; gettimeofday(&cur_time,NULL); timersub(&cur_time,&host_tv_start,&delta); uptime = ((uint64_t)delta.tv_sec * 1000000) + delta.tv_usec; return(uptime); } /* Get host UUID */ void vmfs_host_get_uuid(uuid_t dst) { uuid_copy(dst,host_uuid); } vmfs-tools-0.2.5/libvmfs/vmfs_metadata.c0000644000175000017500000000730711733303644016055 0ustar mhmh/* * vmfs-tools - Tools to access VMFS filesystems * Copyright (C) 2009 Christophe Fillot * Copyright (C) 2009 Mike Hommey * * 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. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /* * VMFS metadata headers. */ #include #include #include "vmfs.h" /* Read a metadata header */ int vmfs_metadata_hdr_read(vmfs_metadata_hdr_t *mdh,const u_char *buf) { mdh->magic = read_le32(buf,VMFS_MDH_OFS_MAGIC); mdh->pos = read_le64(buf,VMFS_MDH_OFS_POS); mdh->hb_pos = read_le64(buf,VMFS_MDH_OFS_HB_POS); mdh->hb_seq = read_le64(buf,VMFS_MDH_OFS_HB_SEQ); mdh->obj_seq = read_le64(buf,VMFS_MDH_OFS_OBJ_SEQ); mdh->hb_lock = read_le32(buf,VMFS_MDH_OFS_HB_LOCK); mdh->mtime = read_le64(buf,VMFS_MDH_OFS_MTIME); read_uuid(buf,VMFS_MDH_OFS_HB_UUID,&mdh->hb_uuid); return(0); } /* Write a metadata header */ int vmfs_metadata_hdr_write(const vmfs_metadata_hdr_t *mdh,u_char *buf) { memset(buf,0,VMFS_METADATA_HDR_SIZE); write_le32(buf,VMFS_MDH_OFS_MAGIC,mdh->magic); write_le64(buf,VMFS_MDH_OFS_POS,mdh->pos); write_le64(buf,VMFS_MDH_OFS_HB_POS,mdh->hb_pos); write_le64(buf,VMFS_MDH_OFS_HB_SEQ,mdh->hb_seq); write_le64(buf,VMFS_MDH_OFS_OBJ_SEQ,mdh->obj_seq); write_le32(buf,VMFS_MDH_OFS_HB_LOCK,mdh->hb_lock); write_le64(buf,VMFS_MDH_OFS_MTIME,mdh->mtime); write_uuid(buf,VMFS_MDH_OFS_HB_UUID,&mdh->hb_uuid); return(0); } /* Lock and read metadata at specified position */ int vmfs_metadata_lock(vmfs_fs_t *fs,off_t pos,u_char *buf,size_t buf_len, vmfs_metadata_hdr_t *mdh) { /* Acquire heartbeat */ if (vmfs_heartbeat_acquire(fs) == -1) return(-1); /* Reserve volume */ if (vmfs_device_reserve(fs->dev,pos) == -1) { fprintf(stderr,"VMFS: unable to reserve volume.\n"); goto err_reserve; } /* Read the complete metadata for the caller */ if (vmfs_device_read(fs->dev,pos,buf,buf_len) != buf_len) { fprintf(stderr,"VMFS: unable to read metadata.\n"); goto err_io; } vmfs_metadata_hdr_read(mdh,buf); if (mdh->hb_lock != 0) goto err_io; /* Update metadata information */ mdh->obj_seq++; mdh->hb_lock = 1; mdh->hb_pos = fs->hb.pos; mdh->hb_seq = fs->hb_seq; uuid_copy(mdh->hb_uuid,fs->hb.uuid); vmfs_metadata_hdr_write(mdh,buf); /* Rewrite the metadata header only */ if (vmfs_device_write(fs->dev,pos,buf,VMFS_METADATA_HDR_SIZE) != VMFS_METADATA_HDR_SIZE) { fprintf(stderr,"VMFS: unable to write metadata header.\n"); goto err_io; } vmfs_device_release(fs->dev,pos); return(0); err_io: vmfs_device_release(fs->dev,pos); err_reserve: vmfs_heartbeat_release(fs); return(-1); } /* Unlock metadata */ int vmfs_metadata_unlock(vmfs_fs_t *fs,vmfs_metadata_hdr_t *mdh) { DECL_ALIGNED_BUFFER(buf,VMFS_METADATA_HDR_SIZE); mdh->hb_lock = 0; uuid_clear(mdh->hb_uuid); vmfs_metadata_hdr_write(mdh,buf); /* Rewrite the metadata header only */ if (vmfs_device_write(fs->dev,mdh->pos,buf,buf_len) != buf_len) { fprintf(stderr,"VMFS: unable to write metadata header.\n"); return(-1); } return(vmfs_heartbeat_release(fs)); } vmfs-tools-0.2.5/libvmfs/vmfs_bitmap.c0000644000175000017500000003536011733303644015551 0ustar mhmh/* * vmfs-tools - Tools to access VMFS filesystems * Copyright (C) 2009 Christophe Fillot * Copyright (C) 2009 Mike Hommey * * 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. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /* * VMFS bitmaps. */ #include #include #include #include #include #include "utils.h" #include "vmfs.h" /* Read a bitmap header */ int vmfs_bmh_read(vmfs_bitmap_header_t *bmh,const u_char *buf) { bmh->items_per_bitmap_entry = read_le32(buf,0x0); bmh->bmp_entries_per_area = read_le32(buf,0x4); bmh->hdr_size = read_le32(buf,0x8); bmh->data_size = read_le32(buf,0xc); bmh->area_size = read_le32(buf,0x10); bmh->total_items = read_le32(buf,0x14); bmh->area_count = read_le32(buf,0x18); return(0); } /* Write a bitmap header */ int vmfs_bmh_write(const vmfs_bitmap_header_t *bmh,u_char *buf) { write_le32(buf,0x0,bmh->items_per_bitmap_entry); write_le32(buf,0x4,bmh->bmp_entries_per_area); write_le32(buf,0x8,bmh->hdr_size); write_le32(buf,0xc,bmh->data_size); write_le32(buf,0x10,bmh->area_size); write_le32(buf,0x14,bmh->total_items); write_le32(buf,0x18,bmh->area_count); return(0); } /* Read a bitmap entry */ int vmfs_bme_read(vmfs_bitmap_entry_t *bme,const u_char *buf,int copy_bitmap) { vmfs_metadata_hdr_read(&bme->mdh,buf); bme->id = read_le32(buf,VMFS_BME_OFS_ID); bme->total = read_le32(buf,VMFS_BME_OFS_TOTAL); bme->free = read_le32(buf,VMFS_BME_OFS_FREE); bme->ffree = read_le32(buf,VMFS_BME_OFS_FFREE); if (copy_bitmap) { memcpy(bme->bitmap,&buf[VMFS_BME_OFS_BITMAP],(bme->total+7)/8); } return(0); } /* Write a bitmap entry */ int vmfs_bme_write(const vmfs_bitmap_entry_t *bme,u_char *buf) { vmfs_metadata_hdr_write(&bme->mdh,buf); write_le32(buf,VMFS_BME_OFS_ID,bme->id); write_le32(buf,VMFS_BME_OFS_TOTAL,bme->total); write_le32(buf,VMFS_BME_OFS_FREE,bme->free); write_le32(buf,VMFS_BME_OFS_FFREE,bme->ffree); memcpy(&buf[VMFS_BME_OFS_BITMAP],bme->bitmap,(bme->total+7)/8); return(0); } /* Update a bitmap entry on disk */ int vmfs_bme_update(const vmfs_fs_t *fs,const vmfs_bitmap_entry_t *bme) { DECL_ALIGNED_BUFFER(buf,VMFS_BITMAP_ENTRY_SIZE); memset(buf,0,VMFS_BITMAP_ENTRY_SIZE); vmfs_bme_write(bme,buf); if (vmfs_device_write(fs->dev,bme->mdh.pos,buf,buf_len) != buf_len) return(-1); return(0); } /* Get number of items per area */ static inline u_int vmfs_bitmap_get_items_per_area(const vmfs_bitmap_header_t *bmh) { return(bmh->bmp_entries_per_area * bmh->items_per_bitmap_entry); } /* Get address of a given area (pointing to bitmap array) */ static inline off_t vmfs_bitmap_get_area_addr(const vmfs_bitmap_header_t *bmh,u_int area) { return(bmh->hdr_size + (area * bmh->area_size)); } /* Read a bitmap entry given a block id */ int vmfs_bitmap_get_entry(vmfs_bitmap_t *b,uint32_t entry,uint32_t item, vmfs_bitmap_entry_t *bmp_entry) { DECL_ALIGNED_BUFFER(buf,VMFS_BITMAP_ENTRY_SIZE); uint32_t items_per_area; u_int entry_idx,area; off_t addr; addr = (entry * b->bmh.items_per_bitmap_entry) + item; items_per_area = vmfs_bitmap_get_items_per_area(&b->bmh); area = addr / items_per_area; entry_idx = (addr % items_per_area) / b->bmh.items_per_bitmap_entry; addr = vmfs_bitmap_get_area_addr(&b->bmh,area); addr += entry_idx * VMFS_BITMAP_ENTRY_SIZE; if (vmfs_file_pread(b->f,buf,buf_len,addr) != buf_len) return(-1); vmfs_bme_read(bmp_entry,buf,1); return(0); } /* Get position of an item */ off_t vmfs_bitmap_get_item_pos(vmfs_bitmap_t *b,uint32_t entry,uint32_t item) { off_t pos; uint32_t addr; uint32_t items_per_area; u_int area; addr = (entry * b->bmh.items_per_bitmap_entry) + item; items_per_area = vmfs_bitmap_get_items_per_area(&b->bmh); area = addr / items_per_area; pos = b->bmh.hdr_size + (area * b->bmh.area_size); pos += b->bmh.bmp_entries_per_area * VMFS_BITMAP_ENTRY_SIZE; pos += (addr % items_per_area) * b->bmh.data_size; return(pos); } /* Read a bitmap item from its entry and item numbers */ bool vmfs_bitmap_get_item(vmfs_bitmap_t *b, uint32_t entry, uint32_t item, u_char *buf) { off_t pos = vmfs_bitmap_get_item_pos(b,entry,item); return(vmfs_file_pread(b->f,buf,b->bmh.data_size,pos) == b->bmh.data_size); } /* Write a bitmap given its entry and item numbers */ bool vmfs_bitmap_set_item(vmfs_bitmap_t *b,uint32_t entry,uint32_t item, u_char *buf) { off_t pos = vmfs_bitmap_get_item_pos(b,entry,item); return(vmfs_file_pwrite(b->f,buf,b->bmh.data_size,pos) == b->bmh.data_size); } /* Get offset of an item in a bitmap entry */ static void vmfs_bitmap_get_item_offset(const vmfs_bitmap_header_t *bmh,u_int addr, u_int *array_idx,u_int *bit_idx) { u_int idx; idx = addr % bmh->items_per_bitmap_entry; *array_idx = idx >> 3; *bit_idx = idx & 0x07; } /* Update the first free item field */ static void vmfs_bitmap_update_ffree(vmfs_bitmap_entry_t *entry) { u_int array_idx,bit_idx; int i; entry->ffree = 0; for(i=0;itotal;i++) { array_idx = i >> 3; bit_idx = i & 0x07; if (entry->bitmap[array_idx] & (1 << bit_idx)) { entry->ffree = i; break; } } } /* Mark an item as free or allocated */ int vmfs_bitmap_set_item_status(const vmfs_bitmap_header_t *bmh, vmfs_bitmap_entry_t *bmp_entry, uint32_t entry,uint32_t item, int status) { u_int array_idx,bit_idx; u_int bit_mask; uint32_t addr; addr = (entry * bmh->items_per_bitmap_entry) + item; vmfs_bitmap_get_item_offset(bmh,addr,&array_idx,&bit_idx); bit_mask = 1 << bit_idx; if (status == 0) { /* item is already freed */ if (bmp_entry->bitmap[array_idx] & bit_mask) return(-1); bmp_entry->bitmap[array_idx] |= bit_mask; bmp_entry->free++; } else { /* item is already allocated */ if (!(bmp_entry->bitmap[array_idx] & bit_mask)) return(-1); bmp_entry->bitmap[array_idx] &= ~bit_mask; bmp_entry->free--; } vmfs_bitmap_update_ffree(bmp_entry); return(0); } /* Get the status of an item (0=free,1=allocated) */ int vmfs_bitmap_get_item_status(const vmfs_bitmap_header_t *bmh, vmfs_bitmap_entry_t *bmp_entry, uint32_t entry,uint32_t item) { u_int array_idx,bit_idx; u_int bit_mask; uint32_t addr; addr = (entry * bmh->items_per_bitmap_entry) + item; vmfs_bitmap_get_item_offset(bmh,addr,&array_idx,&bit_idx); bit_mask = 1 << bit_idx; return((bmp_entry->bitmap[array_idx] & bit_mask) ? 0 : 1); } /* Find a free item in a bitmap entry and mark it allocated */ int vmfs_bitmap_alloc_item(vmfs_bitmap_entry_t *bmp_entry,uint32_t *item) { u_int array_idx,bit_idx; int i; /* TODO: use first free field as a hint */ for(i=0;itotal;i++) { array_idx = i >> 3; bit_idx = i & 0x07; if (bmp_entry->bitmap[array_idx] & (1 << bit_idx)) { *item = i; bmp_entry->bitmap[array_idx] &= ~(1 << bit_idx); bmp_entry->free--; vmfs_bitmap_update_ffree(bmp_entry); return(0); } } return(-1); } /* Find a bitmap entry with at least "num_items" free in the specified area */ int vmfs_bitmap_area_find_free_items(vmfs_bitmap_t *b, u_int area,u_int num_items, vmfs_bitmap_entry_t *entry) { vmfs_fs_t *fs; u_char *buf,*ptr; size_t buf_len; off_t pos; int res = -1; int i; fs = (vmfs_fs_t *)vmfs_file_get_fs(b->f); pos = vmfs_bitmap_get_area_addr(&b->bmh,area); buf_len = b->bmh.bmp_entries_per_area * VMFS_BITMAP_ENTRY_SIZE; if (!(buf = iobuffer_alloc(buf_len))) return(-1); if (vmfs_file_pread(b->f,buf,buf_len,pos) != buf_len) goto done; for(i=0;ibmh.bmp_entries_per_area;i++) { ptr = buf + (i * VMFS_BITMAP_ENTRY_SIZE); vmfs_bme_read(entry,ptr,1); if (vmfs_metadata_is_locked(&entry->mdh) || (entry->free < num_items)) continue; /* We now have to re-read the bitmap entry with the reservation taken */ if (!vmfs_metadata_lock(fs,entry->mdh.pos, ptr,VMFS_BITMAP_ENTRY_SIZE, &entry->mdh)) { vmfs_bme_read(entry,ptr,1); if (entry->free < num_items) { vmfs_metadata_unlock(fs,&entry->mdh); continue; } res = 0; break; } } done: iobuffer_free(buf); return(res); } /* Find a bitmap entry with at least "num_items" free (scan all areas) */ int vmfs_bitmap_find_free_items(vmfs_bitmap_t *b,u_int num_items, vmfs_bitmap_entry_t *entry) { u_int i; for(i=0;ibmh.area_count;i++) if (!vmfs_bitmap_area_find_free_items(b,i,num_items,entry)) return(0); return(-1); } /* Count the total number of allocated items in a bitmap area */ uint32_t vmfs_bitmap_area_allocated_items(vmfs_bitmap_t *b,u_int area) { u_char buf[VMFS_BITMAP_ENTRY_SIZE]; vmfs_bitmap_entry_t entry; uint32_t count; off_t pos; int i; pos = vmfs_bitmap_get_area_addr(&b->bmh,area); for(i=0,count=0;ibmh.bmp_entries_per_area;i++) { if (vmfs_file_pread(b->f,buf,sizeof(buf),pos) != sizeof(buf)) break; vmfs_bme_read(&entry,buf,0); count += entry.total - entry.free; pos += sizeof(buf); } return count; } /* Count the total number of allocated items in a bitmap */ uint32_t vmfs_bitmap_allocated_items(vmfs_bitmap_t *b) { uint32_t count; u_int i; for(i=0,count=0;ibmh.area_count;i++) count += vmfs_bitmap_area_allocated_items(b,i); return(count); } /* Call a user function for each allocated item in a bitmap */ void vmfs_bitmap_area_foreach(vmfs_bitmap_t *b,u_int area, vmfs_bitmap_foreach_cbk_t cbk, void *opt_arg) { DECL_ALIGNED_BUFFER(buf,VMFS_BITMAP_ENTRY_SIZE); vmfs_bitmap_entry_t entry; off_t pos; uint32_t addr; u_int array_idx,bit_idx; u_int i,j; pos = vmfs_bitmap_get_area_addr(&b->bmh,area); for(i=0;ibmh.bmp_entries_per_area;i++) { if (vmfs_file_pread(b->f,buf,buf_len,pos) != buf_len) break; vmfs_bme_read(&entry,buf,1); for(j=0;j> 3; bit_idx = j & 0x07; addr = area * vmfs_bitmap_get_items_per_area(&b->bmh); addr += i * b->bmh.items_per_bitmap_entry; addr += j; if (!(entry.bitmap[array_idx] & (1 << bit_idx))) cbk(b,addr,opt_arg); } pos += buf_len; } } /* Call a user function for each allocated item in a bitmap */ void vmfs_bitmap_foreach(vmfs_bitmap_t *b,vmfs_bitmap_foreach_cbk_t cbk, void *opt_arg) { u_int i; for(i=0;ibmh.area_count;i++) vmfs_bitmap_area_foreach(b,i,cbk,opt_arg); } /* Check coherency of a bitmap file */ int vmfs_bitmap_check(vmfs_bitmap_t *b) { u_char buf[VMFS_BITMAP_ENTRY_SIZE]; vmfs_bitmap_entry_t entry; uint32_t total_items; uint32_t magic; uint32_t entry_id; int i,j,k,errors; int bmap_size; int bmap_count; off_t pos; errors = 0; total_items = 0; magic = 0; entry_id = 0; for(i=0;ibmh.area_count;i++) { pos = vmfs_bitmap_get_area_addr(&b->bmh,i); for(j=0;jbmh.bmp_entries_per_area;j++) { if (vmfs_file_pread(b->f,buf,sizeof(buf),pos) != sizeof(buf)) break; vmfs_bme_read(&entry,buf,0); if (entry.mdh.magic == 0) goto done; /* check the entry ID */ if (entry.id != entry_id) { printf("Entry 0x%x has incorrect ID 0x%x\n",entry_id,entry.id); errors++; } /* check the magic number */ if (magic == 0) { magic = entry.mdh.magic; } else { if (entry.mdh.magic != magic) { printf("Entry 0x%x has an incorrect magic id (0x%x)\n", entry_id,entry.mdh.magic); errors++; } } /* check the number of items */ if (entry.total > b->bmh.items_per_bitmap_entry) { printf("Entry 0x%x has an incorrect total of 0x%2.2x items\n", entry_id,entry.total); errors++; } /* check the bitmap array */ bmap_size = (entry.total + 7) / 8; bmap_count = 0; for(k=0;kbmh.total_items) { printf("Total number of items (0x%x) doesn't match header info (0x%x)\n", total_items,b->bmh.total_items); errors++; } return(errors); } /* Open a bitmap file */ static inline vmfs_bitmap_t *vmfs_bitmap_open_from_file(vmfs_file_t *f) { DECL_ALIGNED_BUFFER(buf,512); vmfs_bitmap_t *b; if (!f) return NULL; if (vmfs_file_pread(f,buf,buf_len,0) != buf_len) { vmfs_file_close(f); return NULL; } if (!(b = calloc(1, sizeof(vmfs_bitmap_t)))) { vmfs_file_close(f); return NULL; } vmfs_bmh_read(&b->bmh, buf); b->f = f; return b; } vmfs_bitmap_t *vmfs_bitmap_open_at(vmfs_dir_t *d,const char *name) { return vmfs_bitmap_open_from_file(vmfs_file_open_at(d, name)); } vmfs_bitmap_t *vmfs_bitmap_open_from_inode(const vmfs_inode_t *inode) { return vmfs_bitmap_open_from_file(vmfs_file_open_from_inode(inode)); } /* Close a bitmap file */ void vmfs_bitmap_close(vmfs_bitmap_t *b) { if (b != NULL) { vmfs_file_close(b->f); free(b); } } vmfs-tools-0.2.5/libvmfs/vmfs_heartbeat.c0000644000175000017500000001432011733303644016225 0ustar mhmh/* * vmfs-tools - Tools to access VMFS filesystems * Copyright (C) 2009 Christophe Fillot * Copyright (C) 2009 Mike Hommey * * 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. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /* * VMFS heartbeats. */ #include #include #include #include #include #include "utils.h" #include "vmfs.h" /* Read a heartbeart info */ int vmfs_heartbeat_read(vmfs_heartbeat_t *hb,const u_char *buf) { hb->magic = read_le32(buf,VMFS_HB_OFS_MAGIC); hb->pos = read_le64(buf,VMFS_HB_OFS_POS); hb->seq = read_le64(buf,VMFS_HB_OFS_SEQ); hb->uptime = read_le64(buf,VMFS_HB_OFS_UPTIME); hb->journal_blk = read_le32(buf,VMFS_HB_OFS_JOURNAL_BLK); read_uuid(buf,VMFS_HB_OFS_UUID,&hb->uuid); return(0); } /* Write a heartbeat info */ int vmfs_heartbeat_write(const vmfs_heartbeat_t *hb,u_char *buf) { write_le32(buf,VMFS_HB_OFS_MAGIC,hb->magic); write_le64(buf,VMFS_HB_OFS_POS,hb->pos); write_le64(buf,VMFS_HB_OFS_SEQ,hb->seq); write_le64(buf,VMFS_HB_OFS_UPTIME,hb->uptime); write_le32(buf,VMFS_HB_OFS_JOURNAL_BLK,hb->journal_blk); write_uuid(buf,VMFS_HB_OFS_UUID,&hb->uuid); return(0); } /* Show heartbeat info */ void vmfs_heartbeat_show(const vmfs_heartbeat_t *hb) { char uuid_str[M_UUID_BUFLEN]; printf("Heartbeat ID 0x%"PRIx64":\n",hb->pos); printf(" - Magic : 0x%8.8x\n",hb->magic); printf(" - Sequence : 0x%8.8"PRIx64"\n",hb->seq); printf(" - Uptime : 0x%8.8"PRIx64"\n",hb->uptime); printf(" - UUID : %s\n",m_uuid_to_str(hb->uuid,uuid_str)); printf("\n"); } /* Show the active locks */ int vmfs_heartbeat_show_active(const vmfs_fs_t *fs) { DECL_ALIGNED_BUFFER(buf,VMFS_HB_SIZE); vmfs_heartbeat_t hb; ssize_t res; off_t pos = 0; int count = 0; while(pos < VMFS_HB_SIZE * VMFS_HB_NUM) { res = vmfs_device_read(fs->dev,VMFS_HB_BASE+pos,buf,buf_len); if (res != buf_len) { fprintf(stderr,"VMFS: unable to read heartbeat info.\n"); return(-1); } vmfs_heartbeat_read(&hb,buf); if (vmfs_heartbeat_active(&hb)) { vmfs_heartbeat_show(&hb); count++; } else if (hb.magic != VMFS_HB_MAGIC_OFF) { fprintf(stderr,"VMFS: invalid heartbeat info.\n"); break; } pos += res; } return(count); } /* Lock an heartbeat given its ID */ int vmfs_heartbeat_lock(vmfs_fs_t *fs,u_int id,vmfs_heartbeat_t *hb) { DECL_ALIGNED_BUFFER(buf,VMFS_HB_SIZE); off_t pos; int res = -1; if (id >= VMFS_HB_NUM) return(-1); pos = VMFS_HB_BASE + (id * VMFS_HB_SIZE); if (vmfs_device_reserve(fs->dev,pos) == -1) { fprintf(stderr,"VMFS: unable to reserve volume.\n"); return(-1); } if (vmfs_device_read(fs->dev,pos,buf,buf_len) != buf_len) { fprintf(stderr,"VMFS: unable to read heartbeat info.\n"); goto done; } vmfs_heartbeat_read(hb,buf); if (vmfs_heartbeat_active(hb)) goto done; hb->magic = VMFS_HB_MAGIC_ON; hb->uptime = vmfs_host_get_uptime(); hb->seq++; vmfs_host_get_uuid(hb->uuid); vmfs_heartbeat_write(hb,buf); if (vmfs_device_write(fs->dev,pos,buf,buf_len) != buf_len) { fprintf(stderr,"VMFS: unable to write heartbeat info.\n"); hb->magic = VMFS_HB_MAGIC_OFF; goto done; } res = 0; done: vmfs_device_release(fs->dev,pos); return(res); } /* Unlock an heartbeat */ int vmfs_heartbeat_unlock(vmfs_fs_t *fs,vmfs_heartbeat_t *hb) { DECL_ALIGNED_BUFFER(buf,VMFS_HB_SIZE); if (!vmfs_heartbeat_active(hb)) return(-1); hb->magic = VMFS_HB_MAGIC_OFF; hb->seq++; uuid_clear(hb->uuid); vmfs_heartbeat_write(hb,buf); return((vmfs_device_write(fs->dev,hb->pos,buf,buf_len) == buf_len) ? 0 : -1); } /* Update an heartbeat */ int vmfs_heartbeat_update(vmfs_fs_t *fs,vmfs_heartbeat_t *hb) { DECL_ALIGNED_BUFFER(buf,VMFS_HB_SIZE); if (!vmfs_heartbeat_active(hb)) return(-1); hb->uptime = vmfs_host_get_uptime(); vmfs_heartbeat_write(hb,buf); return((vmfs_device_write(fs->dev,hb->pos,buf,buf_len) == buf_len) ? 0 : -1); } /* Acquire an heartbeat (ID is chosen automatically) */ int vmfs_heartbeat_acquire(vmfs_fs_t *fs) { vmfs_heartbeat_t hb; u_char *buf; size_t buf_len; int i,res = -1; /* Heartbeat already active ? */ if (fs->hb_refcount > 0) { fs->hb_refcount++; fs->hb_expire = vmfs_host_get_uptime() + VMFS_HEARTBEAT_EXPIRE_DELAY; return(0); } /* Try to reuse the current ID */ if (!vmfs_heartbeat_lock(fs,fs->hb_id,&fs->hb)) return(0); buf_len = VMFS_HB_NUM * VMFS_HB_SIZE; if (!(buf = iobuffer_alloc(buf_len))) return(-1); if (vmfs_device_read(fs->dev,VMFS_HB_BASE,buf,buf_len) != buf_len) return(-1); /* * Heartbeat is taken by someone else, find a new one. * To avoid high contention with SCSI reservation, we first read * directly the heartbeat info, and if the heartbeat is not taken, * we try to acquire it definitely with reservation. */ for(i=0;ihb)) { fs->hb_id = i; fs->hb_seq = fs->hb.seq; fs->hb_refcount = 1; fs->hb_expire = vmfs_host_get_uptime() + VMFS_HEARTBEAT_EXPIRE_DELAY; res = 0; break; } } iobuffer_free(buf); return(res); } /* Release an heartbeat */ int vmfs_heartbeat_release(vmfs_fs_t *fs) { if (fs->hb_refcount == 0) return(-1); /* The heartbeat will be eventually released by the background process */ fs->hb_refcount--; return(0); } vmfs-tools-0.2.5/libvmfs/vmfs_heartbeat.h0000644000175000017500000000600511733303644016233 0ustar mhmh/* * vmfs-tools - Tools to access VMFS filesystems * Copyright (C) 2009 Christophe Fillot * Copyright (C) 2009 Mike Hommey * * 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. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef VMFS_HEARTBEAT_H #define VMFS_HEARTBEAT_H #include #include #define VMFS_HB_BASE 0x0300000 #define VMFS_HB_SIZE 0x200 #define VMFS_HB_NUM 2048 #define VMFS_HB_MAGIC_OFF 0xabcdef01 #define VMFS_HB_MAGIC_ON 0xabcdef02 struct vmfs_heartbeart_raw { uint32_t magic; uint64_t pos; uint64_t seq; uint64_t uptime; uuid_t uuid; uint32_t journal_block; uint32_t vol_version; /* from fs_info (?) */ uint32_t version; /* from fs_info (?) */ } __attribute__((packed)); #define VMFS_HB_OFS_MAGIC offsetof(struct vmfs_heartbeart_raw, magic) #define VMFS_HB_OFS_POS offsetof(struct vmfs_heartbeart_raw, pos) #define VMFS_HB_OFS_SEQ offsetof(struct vmfs_heartbeart_raw, seq) #define VMFS_HB_OFS_UPTIME offsetof(struct vmfs_heartbeart_raw, uptime) #define VMFS_HB_OFS_UUID offsetof(struct vmfs_heartbeart_raw, uuid) #define VMFS_HB_OFS_JOURNAL_BLK offsetof(struct vmfs_heartbeart_raw, journal_block) struct vmfs_heartbeat { uint32_t magic; uint64_t pos; uint64_t seq; /* Sequence number */ uint64_t uptime; /* Uptime (in usec) of the locker */ uuid_t uuid; /* UUID of the server */ uint32_t journal_blk; /* Journal block */ }; /* Delay for heartbeat expiration when not referenced anymore */ #define VMFS_HEARTBEAT_EXPIRE_DELAY (3 * 1000000) static inline bool vmfs_heartbeat_active(vmfs_heartbeat_t *hb) { return(hb->magic == VMFS_HB_MAGIC_ON); } /* Read a heartbeart info */ int vmfs_heartbeat_read(vmfs_heartbeat_t *hb,const u_char *buf); /* Write a heartbeat info */ int vmfs_heartbeat_write(const vmfs_heartbeat_t *hb,u_char *buf); /* Show heartbeat info */ void vmfs_heartbeat_show(const vmfs_heartbeat_t *hb); /* Show the active locks */ int vmfs_heartbeat_show_active(const vmfs_fs_t *fs); /* Lock an heartbeat given its ID */ int vmfs_heartbeat_lock(vmfs_fs_t *fs,u_int id,vmfs_heartbeat_t *hb); /* Unlock an heartbeat */ int vmfs_heartbeat_unlock(vmfs_fs_t *fs,vmfs_heartbeat_t *hb); /* Update an heartbeat */ int vmfs_heartbeat_update(vmfs_fs_t *fs,vmfs_heartbeat_t *hb); /* Acquire an heartbeat (ID is chosen automatically) */ int vmfs_heartbeat_acquire(vmfs_fs_t *fs); /* Release an heartbeat */ int vmfs_heartbeat_release(vmfs_fs_t *fs); #endif vmfs-tools-0.2.5/libvmfs/vmfs_inode.h0000644000175000017500000001275111733550713015400 0ustar mhmh/* * vmfs-tools - Tools to access VMFS filesystems * Copyright (C) 2009 Christophe Fillot * Copyright (C) 2009,2011 Mike Hommey * * 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. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef VMFS_INODE_H #define VMFS_INODE_H #include #include #define VMFS_INODE_SIZE 0x800 #define VMFS_INODE_BLK_COUNT 0x100 #define VMFS_INODE_MAGIC 0x10c00001 struct vmfs_inode_raw { struct vmfs_metadata_hdr_raw mdh; uint32_t id; uint32_t id2; /* seems to be VMFS_BLK_FD_ITEM(id) + 1 */ uint32_t nlink; uint32_t type; uint32_t flags; uint64_t size; uint64_t blk_size; uint64_t blk_count; uint32_t mtime; uint32_t ctime; uint32_t atime; uint32_t uid; uint32_t gid; uint32_t mode; uint32_t zla; uint32_t tbz; uint32_t cow; u_char _unknown2[432]; union { uint32_t blocks[VMFS_INODE_BLK_COUNT]; uint32_t rdm_id; char content[VMFS_INODE_BLK_COUNT * sizeof(uint32_t)]; }; } __attribute__((packed)); #define VMFS_INODE_OFS_ID offsetof(struct vmfs_inode_raw, id) #define VMFS_INODE_OFS_ID2 offsetof(struct vmfs_inode_raw, id2) #define VMFS_INODE_OFS_NLINK offsetof(struct vmfs_inode_raw, nlink) #define VMFS_INODE_OFS_TYPE offsetof(struct vmfs_inode_raw, type) #define VMFS_INODE_OFS_FLAGS offsetof(struct vmfs_inode_raw, flags) #define VMFS_INODE_OFS_SIZE offsetof(struct vmfs_inode_raw, size) #define VMFS_INODE_OFS_BLK_SIZE offsetof(struct vmfs_inode_raw, blk_size) #define VMFS_INODE_OFS_BLK_COUNT offsetof(struct vmfs_inode_raw, blk_count) #define VMFS_INODE_OFS_MTIME offsetof(struct vmfs_inode_raw, mtime) #define VMFS_INODE_OFS_CTIME offsetof(struct vmfs_inode_raw, ctime) #define VMFS_INODE_OFS_ATIME offsetof(struct vmfs_inode_raw, atime) #define VMFS_INODE_OFS_UID offsetof(struct vmfs_inode_raw, uid) #define VMFS_INODE_OFS_GID offsetof(struct vmfs_inode_raw, gid) #define VMFS_INODE_OFS_MODE offsetof(struct vmfs_inode_raw, mode) #define VMFS_INODE_OFS_ZLA offsetof(struct vmfs_inode_raw, zla) #define VMFS_INODE_OFS_TBZ offsetof(struct vmfs_inode_raw, tbz) #define VMFS_INODE_OFS_COW offsetof(struct vmfs_inode_raw, cow) #define VMFS_INODE_OFS_BLK_ARRAY offsetof(struct vmfs_inode_raw, blocks) #define VMFS_INODE_OFS_RDM_ID offsetof(struct vmfs_inode_raw, rdm_id) #define VMFS_INODE_OFS_CONTENT offsetof(struct vmfs_inode_raw, content) /* Synchronization flags */ #define VMFS_INODE_SYNC_META 0x01 #define VMFS_INODE_SYNC_BLK 0x02 #define VMFS_INODE_SYNC_ALL (VMFS_INODE_SYNC_META | VMFS_INODE_SYNC_BLK) /* Some VMFS 5 features use a weird ZLA */ #define VMFS5_ZLA_BASE 4301 struct vmfs_inode { vmfs_metadata_hdr_t mdh; uint32_t id,id2; uint32_t nlink; uint32_t type; uint32_t flags; uint64_t size; uint64_t blk_size; uint64_t blk_count; time_t mtime,ctime,atime; uint32_t uid,gid; uint32_t mode,cmode; uint32_t zla,tbz,cow; uint32_t rdm_id; union { uint32_t blocks[VMFS_INODE_BLK_COUNT]; char content[VMFS_INODE_BLK_COUNT * sizeof(uint32_t)]; }; /* In-core inode information */ const vmfs_fs_t *fs; vmfs_inode_t **pprev,*next; u_int ref_count; u_int update_flags; }; /* Callback function for vmfs_inode_foreach_block() */ typedef void (*vmfs_inode_foreach_block_cbk_t)(const vmfs_inode_t *inode, uint32_t pb_blk, uint32_t blk_id, void *opt_arg); /* Update an inode on disk */ int vmfs_inode_update(const vmfs_inode_t *inode,int update_blk_list); /* Get inode corresponding to a block id */ int vmfs_inode_get(const vmfs_fs_t *fs,uint32_t blk_id,vmfs_inode_t *inode); /* Acquire an inode */ vmfs_inode_t *vmfs_inode_acquire(const vmfs_fs_t *fs,uint32_t blk_id); /* Release an inode */ void vmfs_inode_release(vmfs_inode_t *inode); /* Allocate a new inode */ int vmfs_inode_alloc(vmfs_fs_t *fs,u_int type,mode_t mode,vmfs_inode_t **inode); /* * Get block ID corresponding the specified position. Pointer block * resolution is transparently done here. */ int vmfs_inode_get_block(const vmfs_inode_t *inode,off_t pos,uint32_t *blk_id); /* Get a block for writing corresponding to the specified position */ int vmfs_inode_get_wrblock(vmfs_inode_t *inode,off_t pos,uint32_t *blk_id); /* Truncate file */ int vmfs_inode_truncate(vmfs_inode_t *inode,off_t new_len); /* Call a function for each allocated block of an inode */ int vmfs_inode_foreach_block(const vmfs_inode_t *inode, vmfs_inode_foreach_block_cbk_t cbk,void *opt_arg); /* Get inode status */ int vmfs_inode_stat(const vmfs_inode_t *inode,struct stat *buf); /* Get inode status */ int vmfs_inode_stat_from_blkid(const vmfs_fs_t *fs,uint32_t blk_id, struct stat *buf); /* Change permissions */ int vmfs_inode_chmod(vmfs_inode_t *inode,mode_t mode); #endif vmfs-tools-0.2.5/libvmfs/manifest.mk0000644000175000017500000000012611733303644015225 0ustar mhmhutils.o_CFLAGS := $(if $(HAS_POSIX_MEMALIGN),,-DNO_POSIX_MEMALIGN=1) REQUIRES := uuid vmfs-tools-0.2.5/libvmfs/scsi.h0000644000175000017500000000240111733303644014176 0ustar mhmh/* * vmfs-tools - Tools to access VMFS filesystems * Copyright (C) 2009 Christophe Fillot * Copyright (C) 2009 Mike Hommey * * 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. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef SCSI_H #define SCSI_H #ifdef __linux__ #include #include struct scsi_idlun { int four_in_one; int host_unique_id; }; #endif static inline int scsi_get_lun(int fd) { #if __linux__ struct scsi_idlun idlun; if (ioctl(fd, SCSI_IOCTL_GET_IDLUN, &idlun)) #endif return(-1); #if __linux__ return((idlun.four_in_one >> 8) & 0xff); #endif } /* Send a SCSI "reserve" command */ int scsi_reserve(int fd); /* Send a SCSI "release" command */ int scsi_release(int fd); #endif vmfs-tools-0.2.5/libvmfs/vmfs_lvm.c0000644000175000017500000001363111733550772015076 0ustar mhmh/* * vmfs-tools - Tools to access VMFS filesystems * Copyright (C) 2009 Christophe Fillot * Copyright (C) 2009,2011 Mike Hommey * * 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. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /* * VMFS LVM layer */ #include #include "vmfs.h" /* * Until we uncover the details of the segment descriptors format, * it is useless to try to do something more efficient. */ static vmfs_volume_t *vmfs_lvm_get_extent_from_offset(const vmfs_lvm_t *lvm, off_t pos) { int extent; off_t segment = pos / VMFS_LVM_SEGMENT_SIZE; for (extent = 0; extent < lvm->loaded_extents; extent++) { if ((segment >= lvm->extents[extent]->vol_info.first_segment) && (segment <= lvm->extents[extent]->vol_info.last_segment)) return(lvm->extents[extent]); } return(NULL); } /* Get extent size */ static inline uint64_t vmfs_lvm_extent_size(const vmfs_volume_t *extent) { return((uint64_t)extent->vol_info.num_segments * VMFS_LVM_SEGMENT_SIZE); } typedef ssize_t (*vmfs_vol_io_func)(const vmfs_device_t *,off_t,u_char *,size_t); /* Read a raw block of data on logical volume */ static inline ssize_t vmfs_lvm_io(const vmfs_lvm_t *lvm,off_t pos,u_char *buf, size_t len,vmfs_vol_io_func func) { vmfs_volume_t *extent = vmfs_lvm_get_extent_from_offset(lvm,pos); if (!extent) return(-1); pos -= (uint64_t)extent->vol_info.first_segment * VMFS_LVM_SEGMENT_SIZE; if ((pos + len) > vmfs_lvm_extent_size(extent)) { /* TODO: Handle this case */ fprintf(stderr,"VMFS: i/o spanned over several extents is unsupported\n"); return(-1); } return(func(&extent->dev,pos,buf,len)); } /* Read a raw block of data on logical volume */ static ssize_t vmfs_lvm_read(const vmfs_device_t *dev,off_t pos, u_char *buf,size_t len) { vmfs_lvm_t *lvm = (vmfs_lvm_t *)dev; return(vmfs_lvm_io(lvm,pos,buf,len,vmfs_device_read)); } /* Write a raw block of data on logical volume */ static ssize_t vmfs_lvm_write(const vmfs_device_t *dev,off_t pos, const u_char *buf,size_t len) { vmfs_lvm_t *lvm = (vmfs_lvm_t *)dev; return(vmfs_lvm_io(lvm,pos,(u_char *)buf,len,(vmfs_vol_io_func)vmfs_device_write)); } /* Reserve the underlying volume given a LVM position */ static int vmfs_lvm_reserve(const vmfs_device_t *dev,off_t pos) { vmfs_lvm_t *lvm = (vmfs_lvm_t *)dev; vmfs_volume_t *extent = vmfs_lvm_get_extent_from_offset(lvm,pos); if (!extent) return(-1); return(vmfs_device_reserve(&extent->dev, 0)); } /* Release the underlying volume given a LVM position */ static int vmfs_lvm_release(const vmfs_device_t *dev,off_t pos) { vmfs_lvm_t *lvm = (vmfs_lvm_t *)dev; vmfs_volume_t *extent = vmfs_lvm_get_extent_from_offset(lvm,pos); if (!extent) return(-1); return(vmfs_device_release(&extent->dev, 0)); } /* Create a volume structure */ vmfs_lvm_t *vmfs_lvm_create(vmfs_flags_t flags) { vmfs_lvm_t *lvm; if (!(lvm = calloc(1,sizeof(*lvm)))) return NULL; lvm->flags = flags; if (flags.read_write) fprintf(stderr, "VMFS: R/W support is experimental. Use at your own risk\n"); return lvm; } /* Add an extent to the LVM */ int vmfs_lvm_add_extent(vmfs_lvm_t *lvm, vmfs_volume_t *vol) { uint32_t i; if (!vol) return(-1); if (lvm->loaded_extents == 0) { uuid_copy(lvm->lvm_info.uuid, vol->vol_info.lvm_uuid); lvm->lvm_info.size = vol->vol_info.lvm_size; lvm->lvm_info.blocks = vol->vol_info.blocks; lvm->lvm_info.num_extents = vol->vol_info.num_extents; } else if (uuid_compare(lvm->lvm_info.uuid, vol->vol_info.lvm_uuid)) { fprintf(stderr, "VMFS: The %s file/device is not part of the LVM\n", vol->device); return(-1); } else if ((lvm->lvm_info.size != vol->vol_info.lvm_size) || (lvm->lvm_info.blocks != vol->vol_info.blocks) || (lvm->lvm_info.num_extents != vol->vol_info.num_extents)) { fprintf(stderr, "VMFS: LVM information mismatch for the %s" " file/device\n", vol->device); return(-1); } for (i = 0; (i < lvm->loaded_extents) && (vol->vol_info.first_segment > lvm->extents[i]->vol_info.first_segment); i++); if (lvm->loaded_extents) memmove(&lvm->extents[i + 1], &lvm->extents[i], (lvm->loaded_extents - i) * sizeof(vmfs_volume_t *)); lvm->extents[i] = vol; lvm->loaded_extents++; return(0); } /* Close an LVM */ static void vmfs_lvm_close(vmfs_device_t *dev) { vmfs_lvm_t *lvm = (vmfs_lvm_t *)dev; if (!lvm) return; while(lvm->loaded_extents--) vmfs_device_close(&lvm->extents[lvm->loaded_extents]->dev); free(lvm); } /* Open an LVM */ int vmfs_lvm_open(vmfs_lvm_t *lvm) { if (!lvm->flags.allow_missing_extents && (lvm->loaded_extents != lvm->lvm_info.num_extents)) { fprintf(stderr, "VMFS: Missing extents\n"); return(-1); } lvm->dev.read = vmfs_lvm_read; if (lvm->flags.read_write) lvm->dev.write = vmfs_lvm_write; lvm->dev.reserve = vmfs_lvm_reserve; lvm->dev.release = vmfs_lvm_release; lvm->dev.close = vmfs_lvm_close; lvm->dev.uuid = &lvm->lvm_info.uuid; return(0); } /* Returns whether a given device is a vmfs_lvm */ bool vmfs_device_is_lvm(vmfs_device_t *dev) { return (dev->read == vmfs_lvm_read); } vmfs-tools-0.2.5/libvmfs/vmfs.h0000644000175000017500000000426011733303644014215 0ustar mhmh/* * vmfs-tools - Tools to access VMFS filesystems * Copyright (C) 2009 Christophe Fillot * Copyright (C) 2009 Mike Hommey * * 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. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef VMFS_H #define VMFS_H /* VMFS types - forward declarations */ typedef struct vmfs_volinfo vmfs_volinfo_t; typedef struct vmfs_fsinfo vmfs_fsinfo_t; typedef struct vmfs_lvminfo vmfs_lvminfo_t; typedef struct vmfs_heartbeat vmfs_heartbeat_t; typedef struct vmfs_metadata_hdr vmfs_metadata_hdr_t; typedef struct vmfs_block_info vmfs_block_info_t; typedef struct vmfs_bitmap_header vmfs_bitmap_header_t; typedef struct vmfs_bitmap_entry vmfs_bitmap_entry_t; typedef struct vmfs_bitmap vmfs_bitmap_t; typedef struct vmfs_inode vmfs_inode_t; typedef struct vmfs_dirent vmfs_dirent_t; typedef struct vmfs_dir vmfs_dir_t; typedef struct vmfs_blk_array vmfs_blk_array_t; typedef struct vmfs_blk_list vmfs_blk_list_t; typedef struct vmfs_file vmfs_file_t; typedef struct vmfs_device vmfs_device_t; typedef struct vmfs_volume vmfs_volume_t; typedef struct vmfs_lvm vmfs_lvm_t; typedef struct vmfs_fs vmfs_fs_t; union vmfs_flags { int packed; struct { unsigned int debug_level:4; unsigned int read_write:1; unsigned int allow_missing_extents:1; }; }; typedef union vmfs_flags vmfs_flags_t __attribute__((transparent_union)); #include "utils.h" #include "vmfs_heartbeat.h" #include "vmfs_metadata.h" #include "vmfs_block.h" #include "vmfs_bitmap.h" #include "vmfs_inode.h" #include "vmfs_dirent.h" #include "vmfs_file.h" #include "vmfs_device.h" #include "vmfs_volume.h" #include "vmfs_lvm.h" #include "vmfs_fs.h" #include "vmfs_host.h" #endif vmfs-tools-0.2.5/libvmfs/vmfs_inode.c0000644000175000017500000005262611733550674015406 0ustar mhmh/* * vmfs-tools - Tools to access VMFS filesystems * Copyright (C) 2009 Christophe Fillot * Copyright (C) 2009,2011 Mike Hommey * * 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. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /* * VMFS inodes. */ #include #include #include #include #include #include "vmfs.h" static inline uint32_t vmfs_inode_read_blk_id(const u_char *buf,u_int index) { return(read_le32(buf,VMFS_INODE_OFS_BLK_ARRAY+(index*sizeof(uint32_t)))); } static inline void vmfs_inode_write_blk_id(u_char *buf,u_int index, uint32_t blk_id) { write_le32(buf,VMFS_INODE_OFS_BLK_ARRAY+(index*sizeof(uint32_t)),blk_id); } /* Read an inode */ static int vmfs_inode_read(vmfs_inode_t *inode,const u_char *buf) { int i; vmfs_metadata_hdr_read(&inode->mdh,buf); if (inode->mdh.magic != VMFS_INODE_MAGIC) return(-1); inode->id = read_le32(buf,VMFS_INODE_OFS_ID); inode->id2 = read_le32(buf,VMFS_INODE_OFS_ID2); inode->nlink = read_le32(buf,VMFS_INODE_OFS_NLINK); inode->type = read_le32(buf,VMFS_INODE_OFS_TYPE); inode->flags = read_le32(buf,VMFS_INODE_OFS_FLAGS); inode->size = read_le64(buf,VMFS_INODE_OFS_SIZE); inode->blk_size = read_le64(buf,VMFS_INODE_OFS_BLK_SIZE); inode->blk_count = read_le64(buf,VMFS_INODE_OFS_BLK_COUNT); inode->mtime = read_le32(buf,VMFS_INODE_OFS_MTIME); inode->ctime = read_le32(buf,VMFS_INODE_OFS_CTIME); inode->atime = read_le32(buf,VMFS_INODE_OFS_ATIME); inode->uid = read_le32(buf,VMFS_INODE_OFS_UID); inode->gid = read_le32(buf,VMFS_INODE_OFS_GID); inode->mode = read_le32(buf,VMFS_INODE_OFS_MODE); inode->zla = read_le32(buf,VMFS_INODE_OFS_ZLA); inode->tbz = read_le32(buf,VMFS_INODE_OFS_TBZ); inode->cow = read_le32(buf,VMFS_INODE_OFS_COW); /* "corrected" mode */ inode->cmode = inode->mode | vmfs_file_type2mode(inode->type); if (inode->type == VMFS_FILE_TYPE_RDM) { inode->rdm_id = read_le32(buf,VMFS_INODE_OFS_RDM_ID); } else if (inode->zla == VMFS5_ZLA_BASE + VMFS_BLK_TYPE_FD) { memcpy(inode->content, buf + VMFS_INODE_OFS_CONTENT, inode->size); } else { for(i=0;iblocks[i] = vmfs_inode_read_blk_id(buf,i); } return(0); } /* Write an inode */ static int vmfs_inode_write(const vmfs_inode_t *inode,u_char *buf) { vmfs_metadata_hdr_write(&inode->mdh,buf); write_le32(buf,VMFS_INODE_OFS_ID,inode->id); write_le32(buf,VMFS_INODE_OFS_ID2,inode->id2); write_le32(buf,VMFS_INODE_OFS_NLINK,inode->nlink); write_le32(buf,VMFS_INODE_OFS_TYPE,inode->type); write_le32(buf,VMFS_INODE_OFS_FLAGS,inode->flags); write_le64(buf,VMFS_INODE_OFS_SIZE,inode->size); write_le64(buf,VMFS_INODE_OFS_BLK_SIZE,inode->blk_size); write_le64(buf,VMFS_INODE_OFS_BLK_COUNT,inode->blk_count); write_le32(buf,VMFS_INODE_OFS_MTIME,inode->mtime); write_le32(buf,VMFS_INODE_OFS_CTIME,inode->ctime); write_le32(buf,VMFS_INODE_OFS_ATIME,inode->atime); write_le32(buf,VMFS_INODE_OFS_UID,inode->uid); write_le32(buf,VMFS_INODE_OFS_GID,inode->gid); write_le32(buf,VMFS_INODE_OFS_MODE,inode->mode); write_le32(buf,VMFS_INODE_OFS_ZLA,inode->zla); write_le32(buf,VMFS_INODE_OFS_TBZ,inode->tbz); write_le32(buf,VMFS_INODE_OFS_COW,inode->cow); return(0); } /* Update block list */ static void vmfs_inode_write_blk_list(const vmfs_inode_t *inode,u_char *buf) { int i; for(i=0;iblocks[i]); } /* Update an inode on disk */ int vmfs_inode_update(const vmfs_inode_t *inode,int update_blk_list) { DECL_ALIGNED_BUFFER(buf,VMFS_INODE_SIZE); memset(buf,0,VMFS_INODE_SIZE); vmfs_inode_write(inode,buf); if (update_blk_list) { vmfs_inode_write_blk_list(inode,buf); } else { buf_len -= VMFS_INODE_BLK_COUNT * sizeof(uint32_t); } if (vmfs_device_write(inode->fs->dev,inode->mdh.pos,buf,buf_len) != buf_len) return(-1); return(0); } /* Get inode corresponding to a block id */ int vmfs_inode_get(const vmfs_fs_t *fs,uint32_t blk_id,vmfs_inode_t *inode) { DECL_ALIGNED_BUFFER_WOL(buf,VMFS_INODE_SIZE); if (VMFS_BLK_TYPE(blk_id) != VMFS_BLK_TYPE_FD) return(-1); if (!vmfs_bitmap_get_item(fs->fdc, VMFS_BLK_FD_ENTRY(blk_id), VMFS_BLK_FD_ITEM(blk_id), buf)) return(-1); return(vmfs_inode_read(inode,buf)); } /* Hash function to retrieve an in-core inode */ static inline u_int vmfs_inode_hash(const vmfs_fs_t *fs,uint32_t blk_id) { return( (blk_id ^ (blk_id >> 9)) & (fs->inode_hash_buckets - 1) ); } /* Register an inode in the in-core inode hash table */ static void vmfs_inode_register(const vmfs_fs_t *fs,vmfs_inode_t *inode) { u_int hb; hb = vmfs_inode_hash(fs,inode->id); inode->fs = fs; inode->ref_count = 1; /* Insert into hash table */ inode->next = fs->inodes[hb]; inode->pprev = &fs->inodes[hb]; if (inode->next != NULL) inode->next->pprev = &inode->next; fs->inodes[hb] = inode; } /* Acquire an inode */ vmfs_inode_t *vmfs_inode_acquire(const vmfs_fs_t *fs,uint32_t blk_id) { vmfs_inode_t *inode; u_int hb; hb = vmfs_inode_hash(fs,blk_id); for(inode=fs->inodes[hb];inode;inode=inode->next) if (inode->id == blk_id) { inode->ref_count++; return inode; } /* Inode not yet used, allocate room for it */ if (!(inode = calloc(1,sizeof(*inode)))) return NULL; if (vmfs_inode_get(fs,blk_id,inode) == -1) { free(inode); return NULL; } vmfs_inode_register(fs,inode); return inode; } /* Release an inode */ void vmfs_inode_release(vmfs_inode_t *inode) { assert(inode->ref_count > 0); if (--inode->ref_count == 0) { if (inode->update_flags) vmfs_inode_update(inode,inode->update_flags & VMFS_INODE_SYNC_BLK); if (inode->pprev != NULL) { /* remove the inode from hash table */ if (inode->next != NULL) inode->next->pprev = inode->pprev; *(inode->pprev) = inode->next; free(inode); } } } /* Allocate a new inode */ int vmfs_inode_alloc(vmfs_fs_t *fs,u_int type,mode_t mode,vmfs_inode_t **inode) { vmfs_inode_t *fdc_inode; off_t fdc_offset; uint32_t fdc_blk; time_t ct; time(&ct); if (!(*inode = calloc(1,sizeof(vmfs_inode_t)))) return(-ENOMEM); (*inode)->mdh.magic = VMFS_INODE_MAGIC; (*inode)->type = type; (*inode)->blk_size = fs->sbc->bmh.data_size; (*inode)->zla = VMFS_BLK_TYPE_SB; (*inode)->mtime = ct; (*inode)->ctime = ct; (*inode)->atime = ct; (*inode)->id2 = ++fs->inode_gen; (*inode)->mode = mode; (*inode)->cmode = (*inode)->mode | vmfs_file_type2mode((*inode)->type); if ((vmfs_block_alloc(fs,VMFS_BLK_TYPE_FD,&(*inode)->id)) < 0) { free(*inode); return(-ENOSPC); } /* Compute "physical" position of inode, using FDC file */ fdc_inode = fs->fdc->f->inode; fdc_offset = vmfs_bitmap_get_item_pos(fs->fdc, VMFS_BLK_FD_ENTRY((*inode)->id), VMFS_BLK_FD_ITEM((*inode)->id)); if ((vmfs_inode_get_block(fdc_inode,fdc_offset,&fdc_blk) == -1) || (VMFS_BLK_TYPE(fdc_blk) != VMFS_BLK_TYPE_FB)) { vmfs_block_free(fs,(*inode)->id); free(*inode); return(-ENOSPC); } (*inode)->mdh.pos = fdc_inode->blk_size * VMFS_BLK_FB_ITEM(fdc_blk); (*inode)->mdh.pos += fdc_offset % fdc_inode->blk_size; (*inode)->update_flags |= VMFS_INODE_SYNC_ALL; vmfs_inode_register(fs,*inode); return(0); } /* * Get block ID corresponding the specified position. Pointer block * resolution is transparently done here. */ int vmfs_inode_get_block(const vmfs_inode_t *inode,off_t pos,uint32_t *blk_id) { const vmfs_fs_t *fs = inode->fs; u_int blk_index; uint32_t zla; int vmfs5_extension; *blk_id = 0; if (!inode->blk_size) return(-EIO); /* This doesn't make much sense but looks like how it's being coded. At * least, the result has some sense. */ zla = inode->zla; if (zla >= VMFS5_ZLA_BASE) { vmfs5_extension = 1; zla -= VMFS5_ZLA_BASE; } else vmfs5_extension = 0; switch(zla) { case VMFS_BLK_TYPE_FB: case VMFS_BLK_TYPE_SB: blk_index = pos / inode->blk_size; if (blk_index >= VMFS_INODE_BLK_COUNT) return(-EINVAL); *blk_id = inode->blocks[blk_index]; break; case VMFS_BLK_TYPE_PB: { DECL_ALIGNED_BUFFER_WOL(buf,fs->pbc->bmh.data_size); uint32_t pb_blk_id; uint32_t blk_per_pb; u_int pb_index; u_int sub_index; blk_per_pb = fs->pbc->bmh.data_size / sizeof(uint32_t); blk_index = pos / inode->blk_size; pb_index = blk_index / blk_per_pb; sub_index = blk_index % blk_per_pb; if (pb_index >= VMFS_INODE_BLK_COUNT) return(-EINVAL); pb_blk_id = inode->blocks[pb_index]; if (!pb_blk_id) break; if (!vmfs_bitmap_get_item(fs->pbc, VMFS_BLK_PB_ENTRY(pb_blk_id), VMFS_BLK_PB_ITEM(pb_blk_id), buf)) return(-EIO); *blk_id = read_le32(buf,sub_index*sizeof(uint32_t)); break; } case VMFS_BLK_TYPE_FD: if (vmfs5_extension) { *blk_id = inode->id; break; } default: /* Unexpected ZLA type */ return(-EIO); } return(0); } /* Aggregate a sub-block to a file block */ static int vmfs_inode_aggregate_fb(vmfs_inode_t *inode) { const vmfs_fs_t *fs = inode->fs; DECL_ALIGNED_BUFFER(buf,fs->sbc->bmh.data_size); uint32_t fb_blk,sb_blk,fb_item; uint32_t sb_count; off_t pos; int i,res; sb_count = vmfs_fs_get_blocksize(fs) / buf_len; if (!(buf = iobuffer_alloc(buf_len))) return(-ENOMEM); sb_blk = inode->blocks[0]; if (!vmfs_bitmap_get_item(fs->sbc, VMFS_BLK_SB_ENTRY(sb_blk), VMFS_BLK_SB_ITEM(sb_blk), buf)) { res = -EIO; goto err_sb_blk_read; } if ((res = vmfs_block_alloc(fs,VMFS_BLK_TYPE_FB,&fb_blk)) < 0) goto err_blk_alloc; fb_item = VMFS_BLK_FB_ITEM(fb_blk); if (vmfs_fs_write(fs,fb_item,0,buf,buf_len) != buf_len) { res = -EIO; goto err_fs_write; } memset(buf,0,buf_len); pos = buf_len; for(i=1;iblocks[0] = fb_blk; inode->zla = VMFS_BLK_TYPE_FB; inode->blk_size = vmfs_fs_get_blocksize(fs); inode->update_flags |= VMFS_INODE_SYNC_BLK; iobuffer_free(buf); return(0); err_fs_write: vmfs_block_free(fs,fb_blk); err_sb_blk_read: err_blk_alloc: iobuffer_free(buf); return(res); } /* Aggregate block list of an inode to a pointer block */ static int vmfs_inode_aggregate_pb(vmfs_inode_t *inode) { const vmfs_fs_t *fs = inode->fs; uint32_t pb_blk,pb_len; uint32_t item,entry; u_char *buf; int i,res; pb_len = fs->pbc->bmh.data_size; if (pb_len < (VMFS_INODE_BLK_COUNT * sizeof(uint32_t))) { fprintf(stderr,"vmfs_inode_aggregate_pb: pb_len=0x%8.8x\n",pb_len); return(-EIO); } if (!(buf = iobuffer_alloc(pb_len))) return(-ENOMEM); memset(buf,0,pb_len); if ((res = vmfs_block_alloc(fs,VMFS_BLK_TYPE_PB,&pb_blk)) < 0) goto err_blk_alloc; for(i=0;iblocks[i]); entry = VMFS_BLK_PB_ENTRY(pb_blk); item = VMFS_BLK_PB_ITEM(pb_blk); if (vmfs_bitmap_set_item(fs->pbc,entry,item,buf) == -1) { res = -EIO; goto err_set_item; } memset(inode->blocks,0,sizeof(inode->blocks)); inode->blocks[0] = pb_blk; inode->zla = VMFS_BLK_TYPE_PB; inode->update_flags |= VMFS_INODE_SYNC_BLK; iobuffer_free(buf); return(0); err_set_item: vmfs_block_free(fs,pb_blk); err_blk_alloc: iobuffer_free(buf); return(res); } /* Proceed to block aggregation if the specified offset */ static int vmfs_inode_aggregate(vmfs_inode_t *inode,off_t pos) { int res; if ((inode->zla == VMFS_BLK_TYPE_SB) && (pos >= inode->blk_size)) { /* A directory consists only of sub-blocks (except the root dir) */ if (inode->type == VMFS_FILE_TYPE_DIR) return(-EFBIG); if ((res = vmfs_inode_aggregate_fb(inode)) < 0) return(res); } if ((inode->zla == VMFS_BLK_TYPE_FB) && (pos >= (inode->blk_size * VMFS_INODE_BLK_COUNT))) return(vmfs_inode_aggregate_pb(inode)); return(0); } /* Get a block for writing corresponding to the specified position */ int vmfs_inode_get_wrblock(vmfs_inode_t *inode,off_t pos,uint32_t *blk_id) { const vmfs_fs_t *fs = inode->fs; u_int blk_index; int res; if (!vmfs_fs_readwrite(fs)) return(-EROFS); *blk_id = 0; if ((res = vmfs_inode_aggregate(inode,pos)) < 0) return(res); if (inode->zla == VMFS_BLK_TYPE_PB) { DECL_ALIGNED_BUFFER_WOL(buf,fs->pbc->bmh.data_size); uint32_t pb_blk_id; uint32_t blk_per_pb; u_int pb_index; u_int sub_index; bool update_pb; update_pb = 0; blk_per_pb = fs->pbc->bmh.data_size / sizeof(uint32_t); blk_index = pos / inode->blk_size; pb_index = blk_index / blk_per_pb; sub_index = blk_index % blk_per_pb; if (pb_index >= VMFS_INODE_BLK_COUNT) return(-EINVAL); pb_blk_id = inode->blocks[pb_index]; /* Allocate a Pointer Block if none is currently present */ if (!pb_blk_id) { if ((res = vmfs_block_alloc(fs,VMFS_BLK_TYPE_PB,&pb_blk_id)) < 0) return(res); memset(buf,0,fs->pbc->bmh.data_size); inode->blocks[pb_index] = pb_blk_id; inode->update_flags |= VMFS_INODE_SYNC_BLK; update_pb = 1; } else { if (!vmfs_bitmap_get_item(fs->pbc, VMFS_BLK_PB_ENTRY(pb_blk_id), VMFS_BLK_PB_ITEM(pb_blk_id), buf)) return(-EIO); *blk_id = read_le32(buf,sub_index*sizeof(uint32_t)); } if (!*blk_id) { if ((res = vmfs_block_alloc(fs,VMFS_BLK_TYPE_FB,blk_id)) < 0) return(res); write_le32(buf,sub_index*sizeof(uint32_t),*blk_id); inode->blk_count++; inode->update_flags |= VMFS_INODE_SYNC_BLK; update_pb = 1; } else { if (VMFS_BLK_FB_TBZ(*blk_id)) { if ((res = vmfs_block_zeroize_fb(fs,*blk_id)) < 0) return(res); *blk_id = VMFS_BLK_FB_TBZ_CLEAR(*blk_id); write_le32(buf,sub_index*sizeof(uint32_t),*blk_id); inode->tbz--; inode->update_flags |= VMFS_INODE_SYNC_BLK; update_pb = 1; } } /* Update the pointer block on disk if it has been modified */ if (update_pb && !vmfs_bitmap_set_item(fs->pbc, VMFS_BLK_PB_ENTRY(pb_blk_id), VMFS_BLK_PB_ITEM(pb_blk_id), buf)) return(-EIO); } else { /* File Block or Sub-Block */ blk_index = pos / inode->blk_size; if (blk_index >= VMFS_INODE_BLK_COUNT) return(-EINVAL); *blk_id = inode->blocks[blk_index]; if (!*blk_id) { if ((res = vmfs_block_alloc(fs,inode->zla,blk_id)) < 0) return(res); inode->blocks[blk_index] = *blk_id; inode->blk_count++; inode->update_flags |= VMFS_INODE_SYNC_BLK; } else { if ((inode->zla == VMFS_BLK_TYPE_FB) && VMFS_BLK_FB_TBZ(*blk_id)) { if ((res = vmfs_block_zeroize_fb(fs,*blk_id)) < 0) return(res); *blk_id = VMFS_BLK_FB_TBZ_CLEAR(*blk_id); inode->blocks[blk_index] = *blk_id; inode->tbz--; inode->update_flags |= VMFS_INODE_SYNC_BLK; } } } return(0); } /* Truncate file */ int vmfs_inode_truncate(vmfs_inode_t *inode,off_t new_len) { const vmfs_fs_t *fs = inode->fs; u_int i; int res; if (!vmfs_fs_readwrite(fs)) return(-EROFS); if (new_len == inode->size) return(0); if (new_len > inode->size) { if ((res = vmfs_inode_aggregate(inode,new_len)) < 0) return(res); inode->size = new_len; inode->update_flags |= VMFS_INODE_SYNC_META; return(0); } switch(inode->zla) { case VMFS_BLK_TYPE_FB: case VMFS_BLK_TYPE_SB: { u_int start,end; start = ALIGN_NUM(new_len,inode->blk_size) / inode->blk_size; end = inode->size / inode->blk_size; for(i=start;i<=end;i++) { if (inode->blocks[i] != 0) { vmfs_block_free(fs,inode->blocks[i]); inode->blk_count--; inode->blocks[i] = 0; } } break; } case VMFS_BLK_TYPE_PB: { uint32_t blk_per_pb; u_int pb_start,pb_end; u_int sub_start,start; u_int blk_index; int count; blk_per_pb = fs->pbc->bmh.data_size / sizeof(uint32_t); blk_index = ALIGN_NUM(new_len,inode->blk_size) / inode->blk_size; pb_start = blk_index / blk_per_pb; sub_start = blk_index % blk_per_pb; pb_end = inode->size / (inode->blk_size * blk_per_pb); for(i=pb_start;i<=pb_end;i++) { if (inode->blocks[i] != 0) { start = (i == pb_start) ? sub_start : 0; /* Free blocks contained in PB */ count = vmfs_block_free_pb(fs,inode->blocks[i], start,blk_per_pb); if (count > 0) inode->blk_count -= count; if (start == 0) inode->blocks[i] = 0; } } break; } default: return(-EIO); } inode->size = new_len; inode->update_flags |= VMFS_INODE_SYNC_BLK; return(0); } /* Call a function for each allocated block of an inode */ int vmfs_inode_foreach_block(const vmfs_inode_t *inode, vmfs_inode_foreach_block_cbk_t cbk, void *opt_arg) { const vmfs_fs_t *fs = inode->fs; uint64_t blk_size; uint32_t blk_per_pb; uint32_t blk_id; u_int blk_total; u_int blk_count; int i,j; blk_total = 0; blk_per_pb = 0; blk_size = inode->blk_size; if (!blk_size) return(-1); blk_count = (inode->size + blk_size - 1) / blk_size; if (inode->zla == VMFS_BLK_TYPE_PB) { blk_per_pb = fs->pbc->bmh.data_size / sizeof(uint32_t); blk_total = blk_count; blk_count = (blk_count + blk_per_pb - 1) / blk_per_pb; } if (blk_count > VMFS_INODE_BLK_COUNT) return(-1); for(i=0;iblocks[i]; if (!blk_id) continue; cbk(inode,0,blk_id,opt_arg); /* Analyze pointer block */ if (inode->zla == VMFS_BLK_TYPE_PB) { DECL_ALIGNED_BUFFER_WOL(buf,fs->pbc->bmh.data_size); uint32_t blk_id2; u_int blk_rem; if (!vmfs_bitmap_get_item(fs->pbc, VMFS_BLK_PB_ENTRY(blk_id), VMFS_BLK_PB_ITEM(blk_id), buf)) return(-1); /* Compute remaining blocks */ blk_rem = m_min(blk_total - (i * blk_per_pb),blk_per_pb); for(j=0;jst_mode = inode->cmode; buf->st_nlink = inode->nlink; buf->st_uid = inode->uid; buf->st_gid = inode->gid; buf->st_size = inode->size; buf->st_atime = inode->atime; buf->st_mtime = inode->mtime; buf->st_ctime = inode->ctime; buf->st_blksize = M_BLK_SIZE; buf->st_blocks = inode->blk_count * (inode->blk_size / S_BLKSIZE); return(0); } /* Get inode status */ int vmfs_inode_stat_from_blkid(const vmfs_fs_t *fs,uint32_t blk_id, struct stat *buf) { vmfs_inode_t *inode; if (!(inode = vmfs_inode_acquire(fs,blk_id))) return(-EIO); vmfs_inode_stat(inode,buf); vmfs_inode_release(inode); return(0); } /* Change permissions */ int vmfs_inode_chmod(vmfs_inode_t *inode,mode_t mode) { inode->mode = mode; inode->update_flags |= VMFS_INODE_SYNC_META; return(0); } vmfs-tools-0.2.5/libvmfs/scsi.c0000644000175000017500000000462711733303644014205 0ustar mhmh/* * vmfs-tools - Tools to access VMFS filesystems * Copyright (C) 2009 Christophe Fillot * Copyright (C) 2009 Mike Hommey * * 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. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #ifdef __linux__ #include #include #endif #include /* SCSI "reserve" command */ #define SCSI_CMD_RESERVE 0x16 #define SCSI_CMD_LEN_RESERVE 6 /* SCSI "release command */ #define SCSI_CMD_RELEASE 0x17 #define SCSI_CMD_LEN_RELEASE 6 /* Send a SCSI "reserve" command */ int scsi_reserve(int fd) { #ifdef __linux__ sg_io_hdr_t io_hdr; u_char sense_buffer[32]; u_char cmd[SCSI_CMD_LEN_RESERVE] = { SCSI_CMD_RESERVE, 0, 0, 0, 0, 0 }; memset(&io_hdr, 0, sizeof(struct sg_io_hdr)); io_hdr.interface_id = 'S'; io_hdr.cmd_len = sizeof(cmd); io_hdr.mx_sb_len = sizeof(sense_buffer); io_hdr.dxfer_direction = SG_DXFER_NONE; io_hdr.cmdp = cmd; io_hdr.sbp = sense_buffer; io_hdr.timeout = 5000; if (ioctl(fd,SG_IO,&io_hdr) < 0) { perror("ioctl"); return(-1); } #endif return(0); } /* Send a SCSI "release" command */ int scsi_release(int fd) { #ifdef __linux__ sg_io_hdr_t io_hdr; u_char sense_buffer[32]; u_char cmd[SCSI_CMD_LEN_RELEASE] = { SCSI_CMD_RELEASE, 0, 0, 0, 0, 0 }; memset(&io_hdr, 0, sizeof(struct sg_io_hdr)); io_hdr.interface_id = 'S'; io_hdr.cmd_len = sizeof(cmd); io_hdr.mx_sb_len = sizeof(sense_buffer); io_hdr.dxfer_direction = SG_DXFER_NONE; io_hdr.cmdp = cmd; io_hdr.sbp = sense_buffer; io_hdr.timeout = 5000; if (ioctl(fd,SG_IO,&io_hdr) < 0) { perror("ioctl"); return(-1); } #endif return(0); } vmfs-tools-0.2.5/libvmfs/vmfs_dirent.h0000644000175000017500000000640611733303644015566 0ustar mhmh/* * vmfs-tools - Tools to access VMFS filesystems * Copyright (C) 2009 Christophe Fillot * Copyright (C) 2009 Mike Hommey * * 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. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef VMFS_DIRENT_H #define VMFS_DIRENT_H #include #include "vmfs_file.h" #define VMFS_DIRENT_SIZE 0x8c struct vmfs_dirent_raw { uint32_t type; uint32_t block_id; uint32_t record_id; char name[128]; } __attribute__((packed)); #define VMFS_DIRENT_OFS_TYPE offsetof(struct vmfs_dirent_raw, type) #define VMFS_DIRENT_OFS_BLK_ID offsetof(struct vmfs_dirent_raw, block_id) #define VMFS_DIRENT_OFS_REC_ID offsetof(struct vmfs_dirent_raw, record_id) #define VMFS_DIRENT_OFS_NAME offsetof(struct vmfs_dirent_raw, name) #define VMFS_DIRENT_OFS_NAME_SIZE sizeof(((struct vmfs_dirent_raw *)(0))->name) struct vmfs_dirent { uint32_t type; uint32_t block_id; uint32_t record_id; char name[129]; }; struct vmfs_dir { vmfs_file_t *dir; uint32_t pos; vmfs_dirent_t dirent; u_char *buf; }; static inline const vmfs_fs_t *vmfs_dir_get_fs(vmfs_dir_t *d) { return d ? vmfs_file_get_fs(d->dir) : NULL; } /* Search for an entry into a directory ; affects position of the next entry vmfs_dir_read will return */ const vmfs_dirent_t *vmfs_dir_lookup(vmfs_dir_t *dir,const char *name); /* Resolve a path to a block id */ uint32_t vmfs_dir_resolve_path(vmfs_dir_t *base_dir,const char *path, int follow_symlink); /* Open a directory based on an inode buffer */ vmfs_dir_t *vmfs_dir_open_from_inode(const vmfs_inode_t *inode); /* Open a directory based on a directory entry */ vmfs_dir_t *vmfs_dir_open_from_blkid(const vmfs_fs_t *fs,uint32_t blk_id); /* Open a directory */ vmfs_dir_t *vmfs_dir_open_at(vmfs_dir_t *d,const char *path); /* Return next entry in directory. Returned directory entry will be overwritten by subsequent calls */ const vmfs_dirent_t *vmfs_dir_read(vmfs_dir_t *d); /* Set position of the next entry that vmfs_dir_read will return */ static inline void vmfs_dir_seek(vmfs_dir_t *d, uint32_t pos) { if (d) d->pos = pos; } /* Close a directory */ int vmfs_dir_close(vmfs_dir_t *d); /* Link an inode to a directory with the specified name */ int vmfs_dir_link_inode(vmfs_dir_t *d,const char *name,vmfs_inode_t *inode); /* Unlink an inode from a directory */ int vmfs_dir_unlink_inode(vmfs_dir_t *d,off_t pos,vmfs_dirent_t *entry); /* Create a new directory */ int vmfs_dir_create(vmfs_dir_t *d,const char *name,mode_t mode, vmfs_inode_t **inode); /* Delete a directory */ int vmfs_dir_delete(vmfs_dir_t *d,const char *name); /* Create a new directory given a path */ int vmfs_dir_mkdir_at(vmfs_dir_t *d,const char *path,mode_t mode); #endif vmfs-tools-0.2.5/libvmfs/vmfs_file.h0000644000175000017500000000661411733303644015221 0ustar mhmh/* * vmfs-tools - Tools to access VMFS filesystems * Copyright (C) 2009 Christophe Fillot * Copyright (C) 2009 Mike Hommey * * 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. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef VMFS_FILE_H #define VMFS_FILE_H #include /* File types (in inode and directory entries) */ #define VMFS_FILE_TYPE_DIR 0x02 #define VMFS_FILE_TYPE_FILE 0x03 #define VMFS_FILE_TYPE_SYMLINK 0x04 #define VMFS_FILE_TYPE_META 0x05 #define VMFS_FILE_TYPE_RDM 0x06 /* File flags */ #define VMFS_FILE_FLAG_RW 0x01 /* === VMFS file abstraction === */ struct vmfs_file { vmfs_inode_t *inode; u_int flags; }; static inline const vmfs_fs_t *vmfs_file_get_fs(vmfs_file_t *f) { if (f && f->inode) return(f->inode->fs); return NULL; } static inline mode_t vmfs_file_type2mode(uint32_t type) { switch (type) { case VMFS_FILE_TYPE_DIR: return S_IFDIR; case VMFS_FILE_TYPE_SYMLINK: return S_IFLNK; default: return S_IFREG; } } /* Get file size */ static inline uint64_t vmfs_file_get_size(const vmfs_file_t *f) { return(f->inode->size); } /* Open a file based on an inode buffer */ vmfs_file_t *vmfs_file_open_from_inode(const vmfs_inode_t *inode); /* Open a file based on a block id */ vmfs_file_t *vmfs_file_open_from_blkid(const vmfs_fs_t *fs,uint32_t blk_id); /* Open a file */ vmfs_file_t *vmfs_file_open_at(vmfs_dir_t *dir,const char *path); /* Create a new file entry */ int vmfs_file_create(vmfs_dir_t *d,const char *name,mode_t mode, vmfs_inode_t **inode); /* Create a file */ vmfs_file_t *vmfs_file_create_at(vmfs_dir_t *dir,const char *path,mode_t mode); /* Close a file */ int vmfs_file_close(vmfs_file_t *f); /* Read data from a file at the specified position */ ssize_t vmfs_file_pread(vmfs_file_t *f,u_char *buf,size_t len,off_t pos); /* Write data to a file at the specified position */ ssize_t vmfs_file_pwrite(vmfs_file_t *f,u_char *buf,size_t len,off_t pos); /* Dump a file */ int vmfs_file_dump(vmfs_file_t *f,off_t pos,uint64_t len,FILE *fd_out); /* Get file status */ int vmfs_file_fstat(const vmfs_file_t *f,struct stat *buf); /* Get file file status (follow symlink) */ int vmfs_file_stat_at(vmfs_dir_t *dir,const char *path,struct stat *buf); /* Get file file status (do not follow symlink) */ int vmfs_file_lstat_at(vmfs_dir_t *dir,const char *path,struct stat *buf); /* Truncate a file (using a file descriptor) */ int vmfs_file_truncate(vmfs_file_t *f,off_t length); /* Truncate a file (using a path) */ int vmfs_file_truncate_at(vmfs_dir_t *dir,const char *path,off_t length); /* Change permissions of a file */ int vmfs_file_chmod(vmfs_file_t *f,mode_t mode); /* Change permissions of a file (using a path) */ int vmfs_file_chmod_at(vmfs_dir_t *dir,const char *path,mode_t mode); /* Delete a file */ int vmfs_file_delete(vmfs_dir_t *dir,const char *name); #endif vmfs-tools-0.2.5/libvmfs/vmfs_device.h0000644000175000017500000000371111733303644015534 0ustar mhmh/* * vmfs-tools - Tools to access VMFS filesystems * Copyright (C) 2009 Mike Hommey * * 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. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef VMFS_DEVICE_H #define VMFS_DEVICE_H #include "vmfs.h" struct vmfs_device { ssize_t (*read)(const vmfs_device_t *dev, off_t pos, u_char *buf, size_t len); ssize_t (*write)(const vmfs_device_t *dev, off_t pos, const u_char *buf, size_t len); int (*reserve)(const vmfs_device_t *dev, off_t pos); int (*release)(const vmfs_device_t *dev, off_t pos); void (*close)(vmfs_device_t *dev); uuid_t *uuid; }; static inline ssize_t vmfs_device_read(const vmfs_device_t *dev, off_t pos, u_char *buf, size_t len) { return dev->read(dev, pos, buf, len); } static inline ssize_t vmfs_device_write(const vmfs_device_t *dev, off_t pos, const u_char *buf, size_t len) { if (dev->write) return dev->write(dev, pos, buf, len); return -1; } static inline int vmfs_device_reserve(const vmfs_device_t *dev, off_t pos) { if (dev->reserve) return dev->reserve(dev, pos); return 0; } static inline int vmfs_device_release(const vmfs_device_t *dev, off_t pos) { if (dev->release) return dev->release(dev, pos); return 0; } static inline void vmfs_device_close(vmfs_device_t *dev) { if (dev->close) dev->close(dev); } #endif vmfs-tools-0.2.5/libvmfs/vmfs_block.h0000644000175000017500000001642611733550507015400 0ustar mhmh/* * vmfs-tools - Tools to access VMFS filesystems * Copyright (C) 2009 Christophe Fillot * Copyright (C) 2009,2011,2012 Mike Hommey * * 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. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef VMFS_BLOCK_H #define VMFS_BLOCK_H /* Block types */ enum vmfs_block_type { VMFS_BLK_TYPE_NONE = 0, VMFS_BLK_TYPE_FB, /* File Block */ VMFS_BLK_TYPE_SB, /* Sub-Block */ VMFS_BLK_TYPE_PB, /* Pointer Block */ VMFS_BLK_TYPE_FD, /* File Descriptor */ VMFS_BLK_TYPE_MAX, }; #define VMFS_BLK_SHIFT(mask) __builtin_ctz(mask) #define VMFS_BLK_VALUE(blk_id, mask) (((blk_id) & (mask)) >> VMFS_BLK_SHIFT(mask)) #define VMFS_BLK_MAX_VALUE(mask) (((mask) >> VMFS_BLK_SHIFT(mask)) + 1) #define VMFS_BLK_FILL(value, mask) (((value) << VMFS_BLK_SHIFT(mask)) & (mask)) #define VMFS_BLK_TYPE_MASK 0x00000007 /* Extract block type from a block ID */ #define VMFS_BLK_TYPE(blk_id) VMFS_BLK_VALUE(blk_id, VMFS_BLK_TYPE_MASK) /* File-Block * { unsigned int item:26; * unsigned int flags:3; * unsigned int type:3; } * There is probably really no more than one flag, but so far, nothing * indicates what can be stored between the significant bits for the block * type and the TBZ flag, so we'll consider they are flags of some sort, * and will display them as such. */ #define VMFS_BLK_FB_ITEM_MASK 0xffffffc0 #define VMFS_BLK_FB_FLAGS_MASK 0x00000038 /* TBZ flag specifies if the block must be zeroed. */ #define VMFS_BLK_FB_TBZ_FLAG 4 #define VMFS_BLK_FB_ITEM(blk_id) VMFS_BLK_VALUE(blk_id, VMFS_BLK_FB_ITEM_MASK) #define VMFS_BLK_FB_FLAGS(blk_id) VMFS_BLK_VALUE(blk_id, VMFS_BLK_FB_FLAGS_MASK) #define VMFS_BLK_FB_MAX_ITEM VMFS_BLK_MAX_VALUE(VMFS_BLK_FB_ITEM_MASK) #define VMFS_BLK_FB_TBZ(blk_id) \ (VMFS_BLK_FB_FLAGS(blk_id) & VMFS_BLK_FB_TBZ_FLAG) #define VMFS_BLK_FB_TBZ_CLEAR(blk_id) ((blk_id) & ~(VMFS_BLK_FILL(VMFS_BLK_FB_TBZ_FLAG, VMFS_BLK_FB_FLAGS_MASK))) #define VMFS_BLK_FB_BUILD(item, flags) \ (VMFS_BLK_FILL(item, VMFS_BLK_FB_ITEM_MASK) | \ VMFS_BLK_FILL(flags, VMFS_BLK_FB_FLAGS_MASK) | \ VMFS_BLK_TYPE_FB) /* Sub-Block * { unsigned int item_lsb:4; * unsigned int entry:22; * unsigned int flags:1; // Not sure it even exists * unsigned int item_msb: 2; * unsigned int type:3; } */ #define VMFS_BLK_SB_ITEM_LSB_MASK 0xf0000000 #define VMFS_BLK_SB_ENTRY_MASK 0x0fffffc0 #define VMFS_BLK_SB_FLAGS_MASK 0x00000020 #define VMFS_BLK_SB_ITEM_MSB_MASK 0x00000018 #define VMFS_BLK_SB_ITEM_VALUE_LSB_MASK 0x0000000f #define VMFS_BLK_SB_ITEM_VALUE_MSB_MASK 0x00000030 #define VMFS_BLK_SB_ITEM(blk_id) \ (VMFS_BLK_FILL(VMFS_BLK_VALUE(blk_id, VMFS_BLK_SB_ITEM_LSB_MASK), VMFS_BLK_SB_ITEM_VALUE_LSB_MASK) | \ VMFS_BLK_FILL(VMFS_BLK_VALUE(blk_id, VMFS_BLK_SB_ITEM_MSB_MASK), VMFS_BLK_SB_ITEM_VALUE_MSB_MASK)) #define VMFS_BLK_SB_ENTRY(blk_id) VMFS_BLK_VALUE(blk_id, VMFS_BLK_SB_ENTRY_MASK) #define VMFS_BLK_SB_FLAGS(blk_id) VMFS_BLK_VALUE(blk_id, VMFS_BLK_SB_FLAGS_MASK) #define VMFS_BLK_SB_MAX_ITEM VMFS_BLK_MAX_VALUE(VMFS_BLK_SB_ITEM_VALUE_LSB_MASK | VMFS_BLK_SB_ITEM_VALUE_MSB_MASK) #define VMFS_BLK_SB_MAX_ENTRY VMFS_BLK_MAX_VALUE(VMFS_BLK_SB_ENTRY_MASK) #define VMFS_BLK_SB_BUILD(entry, item, flags) \ (VMFS_BLK_FILL(entry, VMFS_BLK_SB_ENTRY_MASK) | \ VMFS_BLK_FILL(VMFS_BLK_VALUE(item, VMFS_BLK_SB_ITEM_VALUE_LSB_MASK), \ VMFS_BLK_SB_ITEM_LSB_MASK) | \ VMFS_BLK_FILL(VMFS_BLK_VALUE(item, VMFS_BLK_SB_ITEM_VALUE_MSB_MASK), \ VMFS_BLK_SB_ITEM_MSB_MASK) | \ VMFS_BLK_FILL(flags, VMFS_BLK_SB_FLAGS_MASK) | \ VMFS_BLK_TYPE_SB) /* Pointer-Block * { unsigned int item:4; * unsigned int entry:22; * unsigned int flags:3; * unsigned int type:3; } */ #define VMFS_BLK_PB_ITEM_MASK 0xf0000000 #define VMFS_BLK_PB_ENTRY_MASK 0x0fffffc0 #define VMFS_BLK_PB_FLAGS_MASK 0x00000038 #define VMFS_BLK_PB_ITEM(blk_id) VMFS_BLK_VALUE(blk_id, VMFS_BLK_PB_ITEM_MASK) #define VMFS_BLK_PB_ENTRY(blk_id) VMFS_BLK_VALUE(blk_id, VMFS_BLK_PB_ENTRY_MASK) #define VMFS_BLK_PB_FLAGS(blk_id) VMFS_BLK_VALUE(blk_id, VMFS_BLK_PB_FLAGS_MASK) #define VMFS_BLK_PB_MAX_ITEM VMFS_BLK_MAX_VALUE(VMFS_BLK_PB_ITEM_MASK) #define VMFS_BLK_PB_MAX_ENTRY VMFS_BLK_MAX_VALUE(VMFS_BLK_PB_ENTRY_MASK) #define VMFS_BLK_PB_BUILD(entry, item, flags) \ (VMFS_BLK_FILL(entry, VMFS_BLK_PB_ENTRY_MASK) | \ VMFS_BLK_FILL(item, VMFS_BLK_PB_ITEM_MASK) | \ VMFS_BLK_FILL(flags, VMFS_BLK_PB_FLAGS_MASK) | \ VMFS_BLK_TYPE_PB) /* File Descriptor * { unsigned int item:10; * unsigned int entry:16; * unsigned int flags:3; * unsigned int type:3; } */ #define VMFS_BLK_FD_ITEM_MASK 0xffc00000 #define VMFS_BLK_FD_ENTRY_MASK 0x003fffc0 #define VMFS_BLK_FD_FLAGS_MASK 0x00000038 #define VMFS_BLK_FD_ITEM(blk_id) VMFS_BLK_VALUE(blk_id, VMFS_BLK_FD_ITEM_MASK) #define VMFS_BLK_FD_ENTRY(blk_id) VMFS_BLK_VALUE(blk_id, VMFS_BLK_FD_ENTRY_MASK) #define VMFS_BLK_FD_FLAGS(blk_id) VMFS_BLK_VALUE(blk_id, VMFS_BLK_FD_FLAGS_MASK) #define VMFS_BLK_FD_MAX_ITEM VMFS_BLK_MAX_VALUE(VMFS_BLK_FD_ITEM_MASK) #define VMFS_BLK_FD_MAX_ENTRY VMFS_BLK_MAX_VALUE(VMFS_BLK_FD_ENTRY_MASK) #define VMFS_BLK_FD_BUILD(entry, item, flags) \ (VMFS_BLK_FILL(entry, VMFS_BLK_FD_ENTRY_MASK) | \ VMFS_BLK_FILL(item, VMFS_BLK_FD_ITEM_MASK) | \ VMFS_BLK_FILL(flags, VMFS_BLK_FD_FLAGS_MASK) | \ VMFS_BLK_TYPE_FD) struct vmfs_block_info { uint32_t entry, item, flags; enum vmfs_block_type type; }; /* Get bitmap info (bitmap type,entry and item) from a block ID */ int vmfs_block_get_info(uint32_t blk_id, vmfs_block_info_t *info); /* Get block status (allocated or free) */ int vmfs_block_get_status(const vmfs_fs_t *fs,uint32_t blk_id); /* Allocate the specified block */ int vmfs_block_alloc_specified(const vmfs_fs_t *fs,uint32_t blk_id); /* Free the specified block */ int vmfs_block_free(const vmfs_fs_t *fs,uint32_t blk_id); /* Allocate a single block */ int vmfs_block_alloc(const vmfs_fs_t *fs,uint32_t blk_type,uint32_t *blk_id); /* Zeroize a file block */ int vmfs_block_zeroize_fb(const vmfs_fs_t *fs,uint32_t blk_id); /* Free blocks hold by a pointer block */ int vmfs_block_free_pb(const vmfs_fs_t *fs,uint32_t pb_blk, u_int start,u_int end); /* Read a piece of a sub-block */ ssize_t vmfs_block_read_sb(const vmfs_fs_t *fs,uint32_t blk_id,off_t pos, u_char *buf,size_t len); /* Write a piece of a sub-block */ ssize_t vmfs_block_write_sb(const vmfs_fs_t *fs,uint32_t blk_id,off_t pos, u_char *buf,size_t len); /* Read a piece of a file block */ ssize_t vmfs_block_read_fb(const vmfs_fs_t *fs,uint32_t blk_id,off_t pos, u_char *buf,size_t len); /* Write a piece of a file block */ ssize_t vmfs_block_write_fb(const vmfs_fs_t *fs,uint32_t blk_id,off_t pos, u_char *buf,size_t len); #endif vmfs-tools-0.2.5/libvmfs/utils.h0000644000175000017500000001216511733303644014405 0ustar mhmh/* * vmfs-tools - Tools to access VMFS filesystems * Copyright (C) 2009 Christophe Fillot * Copyright (C) 2009 Mike Hommey * * 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. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef UTILS_H #define UTILS_H #include #include #include #include /* Max and min macro */ #define m_max(a,b) (((a) > (b)) ? (a) : (b)) #define m_min(a,b) (((a) < (b)) ? (a) : (b)) #define M_UUID_BUFLEN 36 #if defined(__amd64__) || defined(__i386__) #define LE_AND_NO_ALIGN 1 #endif /* Read a 16-bit word in little endian format */ static inline uint16_t read_le16(const u_char *p,int offset) { #ifdef LE_AND_NO_ALIGN return(*((uint16_t *)&p[offset])); #else return((uint16_t)p[offset] | ((uint16_t)p[offset+1] << 8)); #endif } /* Write a 16-bit word in little endian format */ static inline void write_le16(u_char *p,int offset,uint16_t val) { #ifdef LE_AND_NO_ALIGN *(uint16_t *)&p[offset] = val; #else p[offset] = val & 0xFF; p[offset+1] = val >> 8; #endif } /* Read a 32-bit word in little endian format */ static inline uint32_t read_le32(const u_char *p,int offset) { #ifdef LE_AND_NO_ALIGN return(*((uint32_t *)&p[offset])); #else return((uint32_t)p[offset] | ((uint32_t)p[offset+1] << 8) | ((uint32_t)p[offset+2] << 16) | ((uint32_t)p[offset+3] << 24)); #endif } /* Write a 32-bit word in little endian format */ static inline void write_le32(u_char *p,int offset,uint32_t val) { #ifdef LE_AND_NO_ALIGN *(uint32_t *)&p[offset] = val; #else p[offset] = val & 0xFF; p[offset+1] = val >> 8; p[offset+2] = val >> 16; p[offset+3] = val >> 24; #endif } /* Read a 64-bit word in little endian format */ static inline uint64_t read_le64(const u_char *p,int offset) { #ifdef LE_AND_NO_ALIGN return(*((uint64_t *)&p[offset])); #else return((uint64_t)read_le32(p,offset) + ((uint64_t)read_le32(p,offset+4) << 32)); #endif } /* Write a 64-bit word in little endian format */ static inline void write_le64(u_char *p,int offset,uint64_t val) { #ifdef LE_AND_NO_ALIGN *(uint64_t *)&p[offset] = val; #else write_le32(p,offset,val); write_le32(p,offset+4,val); #endif } #define cpu_to_lexx(bits) \ static inline uint##bits##_t cpu_to_le##bits(uint##bits##_t i) \ { \ u_char *p = (u_char *)&i; \ return read_le##bits(p, 0); \ } cpu_to_lexx(16) cpu_to_lexx(32) cpu_to_lexx(64) /* Read an UUID at a given offset in a buffer */ static inline void read_uuid(const u_char *buf,int offset,uuid_t *uuid) { memcpy(uuid,buf+offset,sizeof(uuid_t)); } /* Write an UUID to a given offset in a buffer */ static inline void write_uuid(u_char *buf,int offset,const uuid_t *uuid) { memcpy(buf+offset,uuid,sizeof(uuid_t)); } #define M_SECTOR_SIZE 512 #define M_BLK_SIZE 4096 /* * Block size/alignment required for direct I/O : * 4k bytes on Linux 2.4, * 512 bytes on Linux 2.6 */ #define M_DIO_BLK_SIZE 4096 #define ALIGN_CHECK(val, mult) (((val) & ((mult) - 1)) == 0) #define ALIGN_NUM(val, mult) (((val) + ((mult) - 1)) & ~(((mult) - 1))) #define ALIGN_PTR(ptr, mult) (void *)ALIGN_NUM((uintptr_t)(ptr), mult) #define DECL_ALIGNED_BUFFER(name, size) \ u_char __##name[(size) + M_SECTOR_SIZE]; \ u_char *name = (u_char *)ALIGN_PTR(__##name,M_SECTOR_SIZE); \ size_t name##_len = (size) #define DECL_ALIGNED_BUFFER_WOL(name, size) \ u_char __##name[(size) + M_SECTOR_SIZE]; \ u_char *name = (u_char *)ALIGN_PTR(__##name,M_SECTOR_SIZE); \ /* Convert an UUID into a string */ char *m_uuid_to_str(const uuid_t uuid,char *str); /* Convert a timestamp to a string */ char *m_ctime(const time_t *ct,char *buf,size_t buf_len); /* Convert a file mode to a string */ char *m_fmode_to_str(u_int mode,char *buf); /* Count the number of bits set in a byte */ int bit_count(u_char val); /* Allocate a buffer with alignment compatible for direct I/O */ u_char *iobuffer_alloc(size_t len); /* Free a buffer previously allocated by iobuffer_alloc() */ void iobuffer_free(u_char *buf); /* Read from file descriptor at a given offset */ ssize_t m_pread(int fd,void *buf,size_t count,off_t offset); /* Write to a file descriptor at a given offset */ ssize_t m_pwrite(int fd,const void *buf,size_t count,off_t offset); /* Returns directory name */ char *m_dirname(const char *path); /* Returns base name */ char *m_basename(const char *path); #ifdef NO_STRNDUP #include static inline char *strndup(const char *s, size_t n) { char *result; n = strnlen(s, n); result = malloc(n + 1); if (!result) return NULL; memcpy(result, s, n); result[n + 1] = 0; } #endif #endif vmfs-tools-0.2.5/libvmfs/vmfs_fs.c0000644000175000017500000002131611733550617014705 0ustar mhmh/* * vmfs-tools - Tools to access VMFS filesystems * Copyright (C) 2009 Christophe Fillot * Copyright (C) 2009,2012 Mike Hommey * * 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. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /* * VMFS filesystem.. */ #define _GNU_SOURCE #include #include #include "vmfs.h" /* VMFS meta-files */ #define VMFS_FBB_FILENAME ".fbb.sf" #define VMFS_FDC_FILENAME ".fdc.sf" #define VMFS_PBC_FILENAME ".pbc.sf" #define VMFS_SBC_FILENAME ".sbc.sf" /* Read a block from the filesystem */ ssize_t vmfs_fs_read(const vmfs_fs_t *fs,uint32_t blk,off_t offset, u_char *buf,size_t len) { off_t pos; pos = (uint64_t)blk * vmfs_fs_get_blocksize(fs); pos += offset; return(vmfs_device_read(fs->dev,pos,buf,len)); } /* Write a block to the filesystem */ ssize_t vmfs_fs_write(const vmfs_fs_t *fs,uint32_t blk,off_t offset, const u_char *buf,size_t len) { off_t pos; pos = (uint64_t)blk * vmfs_fs_get_blocksize(fs); pos += offset; return(vmfs_device_write(fs->dev,pos,buf,len)); } /* Read filesystem information */ static int vmfs_fsinfo_read(vmfs_fs_t *fs) { DECL_ALIGNED_BUFFER(buf,512); vmfs_fsinfo_t *fsi = &fs->fs_info; if (vmfs_device_read(fs->dev,VMFS_FSINFO_BASE,buf,buf_len) != buf_len) return(-1); fsi->magic = read_le32(buf,VMFS_FSINFO_OFS_MAGIC); if (fsi->magic != VMFS_FSINFO_MAGIC) { fprintf(stderr,"VMFS FSInfo: invalid magic number 0x%8.8x\n",fsi->magic); return(-1); } fsi->vol_version = read_le32(buf,VMFS_FSINFO_OFS_VOLVER); fsi->version = buf[VMFS_FSINFO_OFS_VER]; fsi->mode = read_le32(buf,VMFS_FSINFO_OFS_MODE); fsi->block_size = read_le64(buf,VMFS_FSINFO_OFS_BLKSIZE); fsi->subblock_size = read_le32(buf,VMFS_FSINFO_OFS_SBSIZE); fsi->fdc_header_size = read_le32(buf,VMFS_FSINFO_OFS_FDC_HEADER_SIZE); fsi->fdc_bitmap_count = read_le32(buf,VMFS_FSINFO_OFS_FDC_BITMAP_COUNT); fsi->ctime = (time_t)read_le32(buf,VMFS_FSINFO_OFS_CTIME); read_uuid(buf,VMFS_FSINFO_OFS_UUID,&fsi->uuid); fsi->label = strndup((char *)buf+VMFS_FSINFO_OFS_LABEL, VMFS_FSINFO_OFS_LABEL_SIZE); read_uuid(buf,VMFS_FSINFO_OFS_LVM_UUID,&fsi->lvm_uuid); return(0); } static vmfs_bitmap_t *vmfs_open_meta_file(vmfs_dir_t *root_dir, char *name, uint32_t max_item, uint32_t max_entry, char *desc) { vmfs_bitmap_t *bitmap = vmfs_bitmap_open_at(root_dir, name); if (!bitmap) { fprintf(stderr, "Unable to open %s.\n", desc); return NULL; } if (bitmap->bmh.items_per_bitmap_entry > max_item) { fprintf(stderr, "Unsupported number of items per entry in %s.\n", desc); return NULL; } if ((bitmap->bmh.total_items + bitmap->bmh.items_per_bitmap_entry - 1) / bitmap->bmh.items_per_bitmap_entry > max_entry) { fprintf(stderr,"Unsupported number of entries in %s.\n", desc); return NULL; } return bitmap; } /* Open all the VMFS meta files */ static int vmfs_open_all_meta_files(vmfs_fs_t *fs) { vmfs_bitmap_t *fdc = fs->fdc; vmfs_dir_t *root_dir; /* Read the first inode */ if (!(root_dir = vmfs_dir_open_from_blkid(fs,VMFS_BLK_FD_BUILD(0, 0, 0)))) { fprintf(stderr,"VMFS: unable to open root directory\n"); return(-1); } if (!(fs->fbb = vmfs_bitmap_open_at(root_dir,VMFS_FBB_FILENAME))) { fprintf(stderr,"Unable to open file-block bitmap (FBB).\n"); return(-1); } if (fs->fbb->bmh.total_items > VMFS_BLK_FB_MAX_ITEM) { fprintf(stderr, "Unsupported number of items in file-block bitmap (FBB).\n"); return(-1); } fs->fdc = vmfs_open_meta_file(root_dir, VMFS_FDC_FILENAME, VMFS_BLK_FD_MAX_ITEM, VMFS_BLK_FD_MAX_ENTRY, "file descriptor bitmap (FDC)"); if (!fs->fdc) return(-1); fs->pbc = vmfs_open_meta_file(root_dir, VMFS_PBC_FILENAME, VMFS_BLK_PB_MAX_ITEM, VMFS_BLK_PB_MAX_ENTRY, "pointer block bitmap (PBC)"); if (!fs->pbc) return(-1); fs->sbc = vmfs_open_meta_file(root_dir, VMFS_SBC_FILENAME, VMFS_BLK_SB_MAX_ITEM, VMFS_BLK_SB_MAX_ENTRY, "pointer block bitmap (PBC)"); if (!fs->sbc) return(-1); vmfs_bitmap_close(fdc); vmfs_dir_close(root_dir); return(0); } /* Read FDC base information */ static int vmfs_read_fdc_base(vmfs_fs_t *fs) { vmfs_inode_t inode = { { 0, }, }; uint32_t fdc_base; /* * Compute position of FDC base: it is located at the first * block after heartbeat information. * When blocksize = 8 Mb, there is free space between heartbeats * and FDC. */ fdc_base = m_max(1, (VMFS_HB_BASE + VMFS_HB_NUM * VMFS_HB_SIZE) / vmfs_fs_get_blocksize(fs)); if (fs->debug_level > 0) printf("FDC base = block #%u\n", fdc_base); inode.fs = fs; inode.mdh.magic = VMFS_INODE_MAGIC; inode.size = fs->fs_info.block_size; inode.type = VMFS_FILE_TYPE_META; inode.blk_size = fs->fs_info.block_size; inode.blk_count = 1; inode.zla = VMFS_BLK_TYPE_FB; inode.blocks[0] = VMFS_BLK_FB_BUILD(fdc_base, 0); inode.ref_count = 1; fs->fdc = vmfs_bitmap_open_from_inode(&inode); /* Read the meta files */ if (vmfs_open_all_meta_files(fs) == -1) return(-1); return(0); } static vmfs_device_t *vmfs_device_open(char **paths, vmfs_flags_t flags) { vmfs_lvm_t *lvm; if (!(lvm = vmfs_lvm_create(flags))) { fprintf(stderr,"Unable to create LVM structure\n"); return NULL; } for (; *paths; paths++) { if (vmfs_lvm_add_extent(lvm, vmfs_vol_open(*paths, flags)) == -1) { fprintf(stderr,"Unable to open device/file \"%s\".\n",*paths); return NULL; } } if (vmfs_lvm_open(lvm)) { vmfs_device_close(&lvm->dev); return NULL; } return &lvm->dev; } /* Open a filesystem */ vmfs_fs_t *vmfs_fs_open(char **paths, vmfs_flags_t flags) { vmfs_device_t *dev; vmfs_fs_t *fs; vmfs_host_init(); dev = vmfs_device_open(paths, flags); if (!dev || !(fs = calloc(1,sizeof(*fs)))) return NULL; fs->inode_hash_buckets = VMFS_INODE_HASH_BUCKETS; fs->inodes = calloc(fs->inode_hash_buckets,sizeof(vmfs_inode_t *)); if (!fs->inodes) { free(fs); return NULL; } fs->dev = dev; fs->debug_level = flags.debug_level; /* Read FS info */ if (vmfs_fsinfo_read(fs) == -1) { fprintf(stderr,"VMFS: Unable to read FS information\n"); vmfs_fs_close(fs); return NULL; } if (uuid_compare(fs->fs_info.lvm_uuid, *fs->dev->uuid)) { fprintf(stderr,"VMFS: FS doesn't belong to the underlying LVM\n"); vmfs_fs_close(fs); return NULL; } /* Read FDC base information */ if (vmfs_read_fdc_base(fs) == -1) { fprintf(stderr,"VMFS: Unable to read FDC information\n"); vmfs_fs_close(fs); return NULL; } if (fs->debug_level > 0) printf("VMFS: filesystem opened successfully\n"); return fs; } /* * Check that all inodes have been released, and synchronize them if this * is not the case. */ static void vmfs_fs_sync_inodes(vmfs_fs_t *fs) { vmfs_inode_t *inode; int i; for(i=0;iinodes[i];inode;inode=inode->next) { #if 0 printf("Inode 0x%8.8x: ref_count=%u, update_flags=0x%x\n", inode->id,inode->ref_count,inode->update_flags); #endif if (inode->update_flags) vmfs_inode_update(inode,inode->update_flags & VMFS_INODE_SYNC_BLK); } } } /* Close a FS */ void vmfs_fs_close(vmfs_fs_t *fs) { if (!fs) return; if (fs->hb_refcount > 0) { fprintf(stderr, "Warning: heartbeat still active in metadata (ref_count=%u)\n", fs->hb_refcount); } vmfs_heartbeat_unlock(fs,&fs->hb); vmfs_bitmap_close(fs->fbb); vmfs_bitmap_close(fs->fdc); vmfs_bitmap_close(fs->pbc); vmfs_bitmap_close(fs->sbc); vmfs_fs_sync_inodes(fs); vmfs_device_close(fs->dev); free(fs->inodes); free(fs->fs_info.label); free(fs); } vmfs-tools-0.2.5/libvmfs/vmfs_volume.h0000644000175000017500000001045111733303644015603 0ustar mhmh/* * vmfs-tools - Tools to access VMFS filesystems * Copyright (C) 2009 Christophe Fillot * Copyright (C) 2009 Mike Hommey * * 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. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef VMFS_VOLUME_H #define VMFS_VOLUME_H #include /* === Volume Info === */ #define VMFS_VOLINFO_BASE 0x100000 #define VMFS_VOLINFO_MAGIC 0xc001d00d struct vmfs_volinfo_raw { uint32_t magic; uint32_t ver; u_char _unknown0[6]; u_char lun; u_char _unknown1[3]; char name[28]; u_char _unknown2[49]; /* The beginning of this array looks like it is a LUN * GUID for 3.31 * filesystems, and the LUN identifier * string as given by ESX for 3.21 filesystems. */ uint32_t size; /* Size of the physical volume, divided by 256 */ u_char _unknown3[31]; uuid_t uuid; uint64_t ctime; /* ctime? in usec */ uint64_t mtime; /* mtime? in usec */ } __attribute__((packed)); #define VMFS_VOLINFO_OFS_MAGIC offsetof(struct vmfs_volinfo_raw, magic) #define VMFS_VOLINFO_OFS_VER offsetof(struct vmfs_volinfo_raw, ver) #define VMFS_VOLINFO_OFS_LUN offsetof(struct vmfs_volinfo_raw, lun) #define VMFS_VOLINFO_OFS_NAME offsetof(struct vmfs_volinfo_raw, name) #define VMFS_VOLINFO_OFS_SIZE offsetof(struct vmfs_volinfo_raw, size) #define VMFS_VOLINFO_OFS_UUID offsetof(struct vmfs_volinfo_raw, uuid) #define VMFS_VOLINFO_OFS_NAME_SIZE \ sizeof(((struct vmfs_volinfo_raw *)(0))->name) /* === LVM Info === */ #define VMFS_LVMINFO_OFFSET 0x0200 struct vmfs_lvminfo_raw { uint64_t size; uint64_t blocks; /* Seems to always be sum(num_segments for all extents) + * num_extents */ uint32_t _unknown0; char uuid_str[35]; u_char _unknown1[29]; uuid_t uuid; uint32_t _unknown2; uint64_t ctime; /* ctime? in usec */ uint32_t _unknown3; uint32_t num_segments; uint32_t first_segment; uint32_t _unknown4; uint32_t last_segment; uint32_t _unknown5; uint64_t mtime; /* mtime? in usec */ uint32_t num_extents; } __attribute__((packed)); #define VMFS_LVMINFO(field) \ (VMFS_LVMINFO_OFFSET + offsetof(struct vmfs_lvminfo_raw, field)) #define VMFS_LVMINFO_OFS_SIZE VMFS_LVMINFO(size) #define VMFS_LVMINFO_OFS_BLKS VMFS_LVMINFO(blocks) #define VMFS_LVMINFO_OFS_UUID_STR VMFS_LVMINFO(uuid_str) #define VMFS_LVMINFO_OFS_UUID VMFS_LVMINFO(uuid) #define VMFS_LVMINFO_OFS_NUM_SEGMENTS VMFS_LVMINFO(num_segments) #define VMFS_LVMINFO_OFS_FIRST_SEGMENT VMFS_LVMINFO(first_segment) #define VMFS_LVMINFO_OFS_LAST_SEGMENT VMFS_LVMINFO(last_segment) #define VMFS_LVMINFO_OFS_NUM_EXTENTS VMFS_LVMINFO(num_extents) /* * Segment bitmap is at 0x80200. * Segment information are at 0x80600 + i * 0x80 for i between 0 and * VMFS_LVMINFO_OFS_NUM_SEGMENTS * * At 0x10 (64-bits) or 0x14 (32-bits) within a segment info, it seems like * something related to the absolute segment number in the logical volume * (looks like absolute segment number << 4 on 32-bits). * Other segment information seem relative to the extent (always the same * pattern on all extents) */ struct vmfs_volinfo { uint32_t magic; uint32_t version; char *name; uuid_t uuid; int lun; uint32_t size; uint64_t lvm_size; uint64_t blocks; uuid_t lvm_uuid; uint32_t num_segments, first_segment, last_segment, num_extents; }; /* === VMFS mounted-volume === */ struct vmfs_volume { vmfs_device_t dev; char *device; int fd; vmfs_flags_t flags; int is_blkdev; int scsi_reservation; /* VMFS volume base */ off_t vmfs_base; /* Volume and FS information */ vmfs_volinfo_t vol_info; }; /* Open a VMFS volume */ vmfs_volume_t *vmfs_vol_open(const char *filename,vmfs_flags_t flags); #endif vmfs-tools-0.2.5/libvmfs/vmfs_dirent.c0000644000175000017500000002507511733550537015571 0ustar mhmh/* * vmfs-tools - Tools to access VMFS filesystems * Copyright (C) 2009 Christophe Fillot * Copyright (C) 2009,2012 Mike Hommey * * 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. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /* * VMFS directory entries. */ #define _GNU_SOURCE #include #include #include #include "vmfs.h" /* Read a directory entry */ static int vmfs_dirent_read(vmfs_dirent_t *entry,const u_char *buf) { entry->type = read_le32(buf,VMFS_DIRENT_OFS_TYPE); entry->block_id = read_le32(buf,VMFS_DIRENT_OFS_BLK_ID); entry->record_id = read_le32(buf,VMFS_DIRENT_OFS_REC_ID); memcpy(entry->name,buf+VMFS_DIRENT_OFS_NAME,VMFS_DIRENT_OFS_NAME_SIZE); entry->name[VMFS_DIRENT_OFS_NAME_SIZE] = 0; return(0); } /* Write a directory entry */ static int vmfs_dirent_write(const vmfs_dirent_t *entry,u_char *buf) { write_le32(buf,VMFS_DIRENT_OFS_TYPE,entry->type); write_le32(buf,VMFS_DIRENT_OFS_BLK_ID,entry->block_id); write_le32(buf,VMFS_DIRENT_OFS_REC_ID,entry->record_id); memcpy(buf+VMFS_DIRENT_OFS_NAME,entry->name,VMFS_DIRENT_OFS_NAME_SIZE); return(0); } /* Search for an entry into a directory ; affects position of the next entry vmfs_dir_read will return */ const vmfs_dirent_t *vmfs_dir_lookup(vmfs_dir_t *d,const char *name) { const vmfs_dirent_t *rec; vmfs_dir_seek(d,0); while((rec = vmfs_dir_read(d))) { if (!strcmp(rec->name,name)) return(rec); } return(NULL); } /* Read a symlink */ static char *vmfs_dirent_read_symlink(const vmfs_fs_t *fs, const vmfs_dirent_t *entry) { vmfs_file_t *f; size_t str_len; char *str = NULL; if (!(f = vmfs_file_open_from_blkid(fs,entry->block_id))) return NULL; str_len = vmfs_file_get_size(f); if (!(str = malloc(str_len+1))) goto done; if ((str_len = vmfs_file_pread(f,(u_char *)str,str_len,0)) == -1) { free(str); goto done; } str[str_len] = 0; done: vmfs_file_close(f); return str; } /* Resolve a path name to a block id */ uint32_t vmfs_dir_resolve_path(vmfs_dir_t *base_dir,const char *path, int follow_symlink) { vmfs_dir_t *cur_dir,*sub_dir; const vmfs_dirent_t *rec; char *nam, *ptr,*sl,*symlink; int close_dir = 0; const vmfs_fs_t *fs = vmfs_dir_get_fs(base_dir); uint32_t ret = 0; cur_dir = base_dir; if (*path == '/') { if (!(cur_dir = vmfs_dir_open_from_blkid(fs,VMFS_BLK_FD_BUILD(0, 0, 0)))) return(0); path++; close_dir = 1; } if (!(rec = vmfs_dir_lookup(cur_dir,"."))) return(0); ret = rec->block_id; nam = ptr = strdup(path); while(*ptr != 0) { sl = strchr(ptr,'/'); if (sl != NULL) *sl = 0; if (*ptr == 0) { ptr = sl + 1; continue; } if (!(rec = vmfs_dir_lookup(cur_dir,ptr))) { ret = 0; break; } ret = rec->block_id; if ((sl == NULL) && !follow_symlink) break; /* follow the symlink if we have an entry of this type */ if (rec->type == VMFS_FILE_TYPE_SYMLINK) { if (!(symlink = vmfs_dirent_read_symlink(fs,rec))) { ret = 0; break; } ret = vmfs_dir_resolve_path(cur_dir,symlink,1); free(symlink); if (!ret) break; } /* last token */ if (sl == NULL) break; /* we must have a directory here */ if (!(sub_dir = vmfs_dir_open_from_blkid(fs,ret))) { ret = 0; break; } if (close_dir) vmfs_dir_close(cur_dir); cur_dir = sub_dir; close_dir = 1; ptr = sl + 1; } free(nam); if (close_dir) vmfs_dir_close(cur_dir); return(ret); } /* Cache content of a directory */ static int vmfs_dir_cache_entries(vmfs_dir_t *d) { off_t dir_size; if (d->buf != NULL) free(d->buf); dir_size = vmfs_file_get_size(d->dir); if (!(d->buf = calloc(1,dir_size))) return(-1); if (vmfs_file_pread(d->dir,d->buf,dir_size,0) != dir_size) { free(d->buf); return(-1); } return(0); } /* Open a directory file */ static vmfs_dir_t *vmfs_dir_open_from_file(vmfs_file_t *file) { vmfs_dir_t *d; if (file == NULL) return NULL; if (!(d = calloc(1, sizeof(*d))) || (file->inode->type != VMFS_FILE_TYPE_DIR)) { vmfs_file_close(file); return NULL; } d->dir = file; vmfs_dir_cache_entries(d); return d; } /* Open a directory based on an inode buffer */ vmfs_dir_t *vmfs_dir_open_from_inode(const vmfs_inode_t *inode) { return vmfs_dir_open_from_file(vmfs_file_open_from_inode(inode)); } /* Open a directory based on a directory entry */ vmfs_dir_t *vmfs_dir_open_from_blkid(const vmfs_fs_t *fs,uint32_t blk_id) { return vmfs_dir_open_from_file(vmfs_file_open_from_blkid(fs,blk_id)); } /* Open a directory */ vmfs_dir_t *vmfs_dir_open_at(vmfs_dir_t *d,const char *path) { return vmfs_dir_open_from_file(vmfs_file_open_at(d,path)); } /* Return next entry in directory. Returned directory entry will be overwritten by subsequent calls */ const vmfs_dirent_t *vmfs_dir_read(vmfs_dir_t *d) { u_char *buf; if (d == NULL) return(NULL); if (d->buf) { if (d->pos*VMFS_DIRENT_SIZE >= vmfs_file_get_size(d->dir)) return(NULL); buf = &d->buf[d->pos*VMFS_DIRENT_SIZE]; } else { u_char _buf[VMFS_DIRENT_SIZE]; if ((vmfs_file_pread(d->dir,_buf,sizeof(_buf), d->pos*sizeof(_buf)) != sizeof(_buf))) return(NULL); buf = _buf; } vmfs_dirent_read(&d->dirent,buf); d->pos++; return &d->dirent; } /* Close a directory */ int vmfs_dir_close(vmfs_dir_t *d) { if (d == NULL) return(-1); if (d->buf) free(d->buf); vmfs_file_close(d->dir); free(d); return(0); } /* Link an inode to a directory with the specified name */ int vmfs_dir_link_inode(vmfs_dir_t *d,const char *name,vmfs_inode_t *inode) { vmfs_fs_t *fs = (vmfs_fs_t *)vmfs_dir_get_fs(d); u_char buf[VMFS_DIRENT_SIZE]; vmfs_dirent_t entry; off_t dir_size; ssize_t res; if (!vmfs_fs_readwrite(fs)) return(-EROFS); if (vmfs_dir_lookup(d,name) != NULL) return(-EEXIST); memset(&entry,0,sizeof(entry)); entry.type = inode->type; entry.block_id = inode->id; entry.record_id = inode->id2; strncpy(entry.name,name,VMFS_DIRENT_OFS_NAME_SIZE); vmfs_dirent_write(&entry,buf); dir_size = vmfs_file_get_size(d->dir); res = vmfs_file_pwrite(d->dir,buf,sizeof(buf),dir_size); if (res != sizeof(buf)) return((res < 0) ? res : -ENOSPC); inode->nlink++; vmfs_dir_cache_entries(d); return(0); } /* Unlink an inode from a directory */ int vmfs_dir_unlink_inode(vmfs_dir_t *d,off_t pos,vmfs_dirent_t *entry) { vmfs_fs_t *fs = (vmfs_fs_t *)vmfs_dir_get_fs(d); vmfs_inode_t *inode; off_t last_entry; if (!vmfs_fs_readwrite(fs)) return(-EROFS); /* Update the inode, delete it if nlink reaches 0 */ if (!(inode = vmfs_inode_acquire(fs,entry->block_id))) return(-ENOENT); if (!--inode->nlink) { vmfs_inode_truncate(inode,0); vmfs_block_free(fs,inode->id); } else { inode->update_flags |= VMFS_INODE_SYNC_META; } vmfs_inode_release(inode); /* Remove the entry itself */ last_entry = vmfs_file_get_size(d->dir) - VMFS_DIRENT_SIZE; if (pos != last_entry) { u_char buf[VMFS_DIRENT_SIZE]; vmfs_file_pread(d->dir,buf,sizeof(buf),last_entry); vmfs_file_pwrite(d->dir,buf,sizeof(buf),pos); } vmfs_file_truncate(d->dir,last_entry); vmfs_dir_cache_entries(d); return(0); } /* Create a new directory */ int vmfs_dir_create(vmfs_dir_t *d,const char *name,mode_t mode, vmfs_inode_t **inode) { vmfs_fs_t *fs = (vmfs_fs_t *)vmfs_dir_get_fs(d); vmfs_dir_t *new_dir; vmfs_inode_t *new_inode; int res; if (!vmfs_fs_readwrite(fs)) return(-EROFS); if (vmfs_dir_lookup(d,name)) return(-EEXIST); /* Allocate inode for the new directory */ if ((res = vmfs_inode_alloc(fs,VMFS_FILE_TYPE_DIR,mode,&new_inode)) < 0) return(res); if (!(new_dir = vmfs_dir_open_from_inode(new_inode))) { res = -ENOENT; goto err_open_dir; } vmfs_dir_link_inode(new_dir,".",new_inode); vmfs_dir_link_inode(new_dir,"..",d->dir->inode); vmfs_dir_link_inode(d,name,new_inode); *inode = new_inode; return(0); err_open_dir: vmfs_inode_release(new_inode); return(res); } /* Delete a directory */ int vmfs_dir_delete(vmfs_dir_t *d,const char *name) { vmfs_fs_t *fs = (vmfs_fs_t *)vmfs_dir_get_fs(d); vmfs_dirent_t *entry; vmfs_dir_t *sub; off_t pos; if (!vmfs_fs_readwrite(fs)) return(-EROFS); if (!(entry = (vmfs_dirent_t *)vmfs_dir_lookup(d,name))) return(-ENOENT); if (entry->type != VMFS_FILE_TYPE_DIR) return(-ENOTDIR); if (!(sub = vmfs_dir_open_from_blkid(fs,entry->block_id))) return(-ENOENT); /* The directory must be empty (excepted for . and ..) */ if (vmfs_file_get_size(sub->dir) != (2 * VMFS_DIRENT_SIZE)) { vmfs_dir_close(sub); return(-ENOTEMPTY); } d->dir->inode->nlink--; sub->dir->inode->nlink = 1; sub->dir->inode->update_flags |= VMFS_INODE_SYNC_META; /* Update the parent directory */ pos = (d->pos - 1) * VMFS_DIRENT_SIZE; vmfs_dir_unlink_inode(d,pos,entry); vmfs_dir_close(sub); return(0); } /* Create a new directory given a path */ int vmfs_dir_mkdir_at(vmfs_dir_t *d,const char *path,mode_t mode) { char *dir_name,*base_name; vmfs_dir_t *dir; vmfs_inode_t *new_inode = NULL; int res; dir_name = m_dirname(path); base_name = m_basename(path); if (!dir_name || !base_name) { res = -EFAULT; goto done; } if (!(dir = vmfs_dir_open_at(d,dir_name))) { res = -ENOENT; goto done; } res = vmfs_dir_create(dir,base_name,mode,&new_inode); if (new_inode) vmfs_inode_release(new_inode); vmfs_dir_close(dir); done: free(dir_name); free(base_name); return(res); } vmfs-tools-0.2.5/libvmfs/vmfs_lvm.h0000644000175000017500000000310611733551012015063 0ustar mhmh/* * vmfs-tools - Tools to access VMFS filesystems * Copyright (C) 2009 Christophe Fillot * Copyright (C) 2009,2011 Mike Hommey * * 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. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef VMFS_LVM_H #define VMFS_LVM_H #define VMFS_LVM_MAX_EXTENTS 32 #define VMFS_LVM_SEGMENT_SIZE (256 * 1024 * 1024) struct vmfs_lvminfo { uuid_t uuid; uint32_t num_extents; uint64_t size; uint64_t blocks; }; /* === LVM === */ struct vmfs_lvm { vmfs_device_t dev; vmfs_flags_t flags; /* LVM information */ vmfs_lvminfo_t lvm_info; /* number of extents currently loaded in the lvm */ int loaded_extents; /* extents */ vmfs_volume_t *extents[VMFS_LVM_MAX_EXTENTS]; }; /* Create a volume structure */ vmfs_lvm_t *vmfs_lvm_create(vmfs_flags_t flags); /* Add an extent to the LVM */ int vmfs_lvm_add_extent(vmfs_lvm_t *lvm, vmfs_volume_t *vol); /* Open an LVM */ int vmfs_lvm_open(vmfs_lvm_t *lvm); /* Returns whether a given device is a vmfs_lvm */ bool vmfs_device_is_lvm(vmfs_device_t *dev); #endif vmfs-tools-0.2.5/GNUmakefile0000644000175000017500000001303711733536116013505 0ustar mhmhifeq (,$(filter clean distclean,$(MAKECMDGOALS))) -include version -include config.cache endif include utils.mk PACKAGE := vmfs-tools all: SUBDIRS := $(subst /,,$(dir $(wildcard */manifest.mk))) ifeq (,$(fuse/LDFLAGS)) ifeq (,$(filter clean distclean,$(MAKECMDGOALS))) SUBDIRS := $(filter-out vmfs-fuse,$(SUBDIRS)) endif endif ENV_CFLAGS := $(CFLAGS) ENV_LDFLAGS := $(LDFLAGS) CFLAGS := LDFLAGS := __NEW_VARS := CFLAGS LDFLAGS define subdir_variables __VARS := $$(filter-out $$(__NEW_VARS),$$(.VARIABLES)) include $(1)/manifest.mk __NEW_VARS := $$(filter-out $$(__VARS) __VARS,$$(.VARIABLES)) $$(foreach var,$$(__NEW_VARS), $$(if $$($$(var)),$$(eval $(1)/$$(var) := $$($$(var)))$$(eval $$(var) := ))) $(1)/SRC := $(wildcard $(1)/*.c) $(1)/HEADERS := $(wildcard $(1)/*.h) $(1)/OBJS := $$($(1)/SRC:%.c=%.o) ifeq (,$(filter lib%,$(1))) $(1)/TARGET := $(1)/$(1) $(1)/MANSRC := $(wildcard $(1)/$(1).txt) else $(1)/TARGET := $(1)/$(1).a endif $(1)/CFLAGS += -I$(1) endef $(foreach subdir,$(SUBDIRS), $(eval $(call subdir_variables,$(subdir)))) define order_by_requires $(eval __result := $(foreach subdir,$(1),$(if $(filter $(1),$($(subdir)/REQUIRES)),,$(subdir)))) $(eval __tmp := $(filter-out $(__result),$(1))) $(if $(filter-out $(__tmp),$(1)),,$(error Dependency loop between subdirectories)) $(if $(__tmp),$(call order_by_requires,$(__tmp),$(2) $(__result)),$(2) $(__result)) endef define subdir_rules $(1)/CFLAGS += $$(foreach require,$$($(1)/REQUIRES),$$($$(require)/CFLAGS)) $(1)/LDFLAGS += $$(foreach require,$$($(1)/REQUIRES),$$($$(require)/LDFLAGS)) $$($(1)/TARGET): LDFLAGS += $$($(1)/LDFLAGS) $$($(1)/TARGET): $$($(1)/OBJS) $$(foreach require,$$($(1)/REQUIRES),$$($$(require)/TARGET)) $$(foreach obj,$$($(1)/OBJS), $$(eval $$(obj): CFLAGS += $$($(1)/CFLAGS) $$($$(obj)_CFLAGS))) endef $(foreach subdir,$(strip $(call order_by_requires,$(SUBDIRS))),$(eval $(call subdir_rules,$(subdir)))) CC := gcc OPTIMFLAGS := $(if $(filter -O%,$(ENV_CFLAGS)),,-O2) CFLAGS := $(ENV_CFLAGS) $(filter-out $(ENV_CFLAGS),-Wall $(OPTIMFLAGS) -g -D_FILE_OFFSET_BITS=64 $(EXTRA_CFLAGS)) CFLAGS += $(if $(HAS_STRNDUP),,-DNO_STRNDUP=1) LDFLAGS := $(ENV_LDFLAGS) $(filter-out $(ENV_LDFLAGS),$(EXTRA_LDFLAGS)) SRC := $(wildcard *.c) $(foreach subdir,$(SUBDIRS),$($(subdir)/SRC)) HEADERS := $(wildcard *.h) $(foreach subdir,$(SUBDIRS),$($(subdir)/HEADERS)) OBJS := $(SRC:%.c=%.o) BUILD_PROGRAMS := $(foreach subdir,$(filter-out lib%,$(SUBDIRS)),$($(subdir)/TARGET)) BUILD_LIBS := $(foreach subdir,$(filter lib%,$(SUBDIRS)),$($(subdir)/TARGET)) MANSRCS := $(foreach subdir,$(SUBDIRS),$($(subdir)/MANSRC)) MANDOCBOOK := $(MANSRCS:%.txt=%.xml) MANPAGES := $(foreach man,$(MANSRCS),$(man:%.txt=%.$(shell sed '1{s/^.*(//;s/)//;q;}' $(man)))) EXTRA_DIST := LICENSE README TODO AUTHORS test.img configure all: $(BUILD_PROGRAMS) $(wildcard .gitignore) test.img ALL_MAKEFILES = $(filter-out config.cache,$(MAKEFILE_LIST)) configure.mk version: VERSION := $(GEN_VERSION) version: $(filter-out version, $(ALL_MAKEFILES)) $(SRC) $(HEADERS) $(wildcard .git/logs/HEAD .git/refs/tags) echo "#if 1" > $@ echo "#define VERSION \"$(VERSION)\"" >> $@ echo "#else" >> $@ echo VERSION := $(VERSION) >> $@ echo "#endif" >> $@ $(BUILD_LIBS): ar -r $@ $^ ranlib $@ $(OBJS): %.o: %.c $(HEADERS) $(BUILD_PROGRAMS): $(CC) -o $@ $^ $(LDFLAGS) clean distclean: CLEAN := $(wildcard $(BUILD_LIBS) $(BUILD_PROGRAMS) $(OBJS) $(PACKAGE)-*.tar.gz $(MANPAGES) $(MANDOCBOOK)) distclean: CLEAN += $(wildcard config.cache) clean distclean: $(if $(strip $(CLEAN)),rm $(strip $(CLEAN))) ALL_DIST := $(SRC) $(HEADERS) $(ALL_MAKEFILES) $(MANSRCS) $(EXTRA_DIST) DIST_DIR := $(PACKAGE)-$(VERSION:v%=%) dist: $(ALL_DIST) @rm -rf "$(DIST_DIR)" @mkdir "$(DIST_DIR)" $(foreach subdir,$(SUBDIRS),"$(DIST_DIR)/$(subdir)") tar -cf - $(ALL_DIST) | tar -C "$(DIST_DIR)" -xf - tar -zcf "$(DIST_DIR).tar.gz" "$(DIST_DIR)" @rm -rf "$(DIST_DIR)" ifneq (,$(ASCIIDOC)) ifneq (,$(XSLTPROC)) ifneq (,$(DOCBOOK_XSL)) $(MANDOCBOOK): %.xml: %.txt $(ASCIIDOC) -a manversion=$(VERSION:v%=%) -a manmanual=$(PACKAGE) -b docbook -d manpage -o $@ $< $(MANPAGES): %.8: %.xml $(XSLTPROC) -o $@ --nonet --novalid $(DOCBOOK_XSL) $< doc: $(MANPAGES) endif endif endif $(DESTDIR)/%: install -d -m 0755 $@ INSTALL_PROGRAMS := $(foreach prog,$(BUILD_PROGRAMS),$(if $(filter noinst,$($(prog)_OPTIONS)),,$(prog))) INSTALLED_PROGRAMS := $(patsubst %,$(DESTDIR)$(sbindir)/%,$(notdir $(INSTALL_PROGRAMS))) $(foreach prog, $(INSTALL_PROGRAMS), $(eval $(DESTDIR)$(sbindir)/$(notdir $(prog)): $(prog))) $(INSTALLED_PROGRAMS): %: $(DESTDIR)$(sbindir) install $(if $(NO_STRIP),,-s )-m 0755 $(filter-out $<,$^) $(dir $@) INSTALLED_MANPAGES := $(patsubst %.txt,$(DESTDIR)$(mandir)/man8/%.8,$(notdir $(foreach subdir,$(SUBDIRS),$($(subdir)/MANSRC)))) $(foreach man,$(foreach subdir,$(SUBDIRS),$($(subdir)/MANSRC:%.txt=%.8)), $(eval $(DESTDIR)$(mandir)/man8/$(notdir $(man)): $(man))) $(INSTALLED_MANPAGES): %: $(DESTDIR)$(mandir)/man8 install -m 0755 $(filter-out $<,$^) $(dir $@) install: $(INSTALLED_PROGRAMS) $(INSTALLED_MANPAGES) ifeq (,$(filter dist clean distclean,$(MAKECMDGOALS))) test.img: imager/imager.c | imager/imager ./imager/imager -r $@ > $@.new diff $@ $@.new || ./imager/imager -v $@.new mv -f $@.new $@ endif .PHONY: all clean distclean dist install doc .gitignore: $(ALL_MAKEFILES) (echo "*.tar.gz"; \ echo "*.[ao]"; \ echo "*.xml"; \ echo "*.8"; \ echo "version"; \ echo "config.cache"; \ $(foreach program, $(BUILD_PROGRAMS),echo $(program);) \ ) > $@ config.cache: configure.mk @$(MAKE) -s -f $^ $(if $(ENV_CFLAGS),CFLAGS='$(ENV_CFLAGS)') $(if $(ENV_LDFLAGS),LDFLAGS='$(ENV_LDFLAGS)') vmfs-tools-0.2.5/LICENSE0000644000175000017500000004310311733303644012433 0ustar mhmh GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. vmfs-tools-0.2.5/AUTHORS0000644000175000017500000000007511733303644012477 0ustar mhmhChristophe Fillot , Mike Hommey vmfs-tools-0.2.5/TODO0000644000175000017500000000074611733303644012124 0ustar mhmhExpected for v0.3.0: - Test suite - Make debugvmfs more of a VMFS debugging tool and less of a vmfs-tools testing tool - Better fsck.vmfs Expected for v0.5.0: - Stable internal API - mkfs.vmfs Expected for v1.0.0: - Full write support. Any time: - Check if we want to read/update separately metadata header from the metadata itself (for example: vmfs_inode_read/vmfs_inode_write) - Add locking/unlocking/update functions for metadata headers - Improve build system and portability vmfs-tools-0.2.5/vmfs-fuse/0000755000175000017500000000000011733555001013334 5ustar mhmhvmfs-tools-0.2.5/vmfs-fuse/vmfs-fuse.c0000644000175000017500000003275611733551100015424 0ustar mhmh/* * vmfs-tools - Tools to access VMFS filesystems * Copyright (C) 2009 Christophe Fillot * Copyright (C) 2009,2012 Mike Hommey * * 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. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #define FUSE_USE_VERSION 26 #include #include #include #include #include "vmfs.h" static vmfs_fs_t *fs; static inline uint32_t ino2blkid(fuse_ino_t ino) { if (ino == FUSE_ROOT_ID) return(VMFS_BLK_FD_BUILD(0, 0, 0)); return((uint32_t)ino); } static inline fuse_ino_t blkid2ino(uint32_t blk_id) { if (blk_id == VMFS_BLK_FD_BUILD(0, 0, 0)) return(FUSE_ROOT_ID); return((fuse_ino_t)blk_id); } static void vmfs_fuse_getattr(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) { vmfs_fs_t *fs = (vmfs_fs_t *) fuse_req_userdata(req); struct stat stbuf = { 0, }; if (!vmfs_inode_stat_from_blkid(fs, ino2blkid(ino), &stbuf)) { stbuf.st_ino = ino; fuse_reply_attr(req, &stbuf, 1.0); } else fuse_reply_err(req, ENOENT); } static void vmfs_fuse_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, int to_set, struct fuse_file_info *fi) { vmfs_fs_t *fs = (vmfs_fs_t *) fuse_req_userdata(req); struct stat stbuf = { 0, }; vmfs_inode_t *inode; if (!(inode = vmfs_inode_acquire(fs,ino2blkid(ino)))) { fuse_reply_err(req, ENOENT); return; } if (to_set & FUSE_SET_ATTR_MODE) inode->mode = attr->st_mode; if (to_set & FUSE_SET_ATTR_UID) inode->uid = attr->st_uid; if (to_set & FUSE_SET_ATTR_GID) inode->gid = attr->st_gid; if (to_set & FUSE_SET_ATTR_ATIME) inode->atime = attr->st_atime; if (to_set & FUSE_SET_ATTR_MTIME) inode->atime = attr->st_mtime; if (to_set & FUSE_SET_ATTR_SIZE) vmfs_inode_truncate(inode,attr->st_size); vmfs_inode_stat(inode,&stbuf); stbuf.st_ino = blkid2ino(inode->id); fuse_reply_attr(req, &stbuf, 1.0); vmfs_inode_release(inode); } static void vmfs_fuse_readlink(fuse_req_t req,fuse_ino_t ino) { vmfs_fs_t *fs = (vmfs_fs_t *) fuse_req_userdata(req); vmfs_file_t *f; size_t str_len; char *str; if (!(f = vmfs_file_open_from_blkid(fs, ino2blkid(ino)))) { fuse_reply_err(req, ENOENT); return; } str_len = vmfs_file_get_size(f); if (!(str = malloc(str_len+1))) { vmfs_file_close(f); fuse_reply_err(req, ENOMEM); return; } if (vmfs_file_pread(f,(u_char *)str,str_len,0) != str_len) { vmfs_file_close(f); free(str); fuse_reply_err(req, EIO); return; } str[str_len] = 0; fuse_reply_readlink(req,str); free(str); } static void vmfs_fuse_mknod(fuse_req_t req,fuse_ino_t parent,const char *name, mode_t mode, dev_t rdev) { struct fuse_entry_param entry = { 0, }; vmfs_inode_t *inode; vmfs_dir_t *dir; int res; if (!(dir = vmfs_dir_open_from_blkid(fs, ino2blkid(parent)))) { fuse_reply_err(req, ENOENT); return; } if ((res = vmfs_file_create(dir,name,mode,&inode)) < 0) { fuse_reply_err(req, -res); return; } vmfs_dir_close(dir); vmfs_inode_stat(inode,&entry.attr); entry.ino = entry.attr.st_ino = blkid2ino(inode->id); entry.generation = 1; entry.attr_timeout = 1.0; entry.entry_timeout = 1.0; fuse_reply_entry(req, &entry); vmfs_inode_release(inode); } static void vmfs_fuse_mkdir(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode) { struct fuse_entry_param entry = { 0, }; vmfs_inode_t *inode; vmfs_dir_t *dir; int res; if (!(dir = vmfs_dir_open_from_blkid(fs, ino2blkid(parent)))) { fuse_reply_err(req, ENOENT); return; } if ((res = vmfs_dir_create(dir,name,mode,&inode)) < 0) { fuse_reply_err(req, -res); return; } vmfs_dir_close(dir); vmfs_inode_stat(inode,&entry.attr); entry.ino = entry.attr.st_ino = blkid2ino(inode->id); entry.generation = 1; entry.attr_timeout = 1.0; entry.entry_timeout = 1.0; fuse_reply_entry(req, &entry); vmfs_inode_release(inode); } static void vmfs_fuse_unlink(fuse_req_t req,fuse_ino_t parent,const char *name) { vmfs_dir_t *dir; int res; if (!(dir = vmfs_dir_open_from_blkid(fs, ino2blkid(parent)))) { fuse_reply_err(req, ENOENT); return; } res = vmfs_file_delete(dir,name); vmfs_dir_close(dir); fuse_reply_err(req,-res); } static void vmfs_fuse_rmdir(fuse_req_t req,fuse_ino_t parent,const char *name) { vmfs_dir_t *dir; int res; if (!(dir = vmfs_dir_open_from_blkid(fs, ino2blkid(parent)))) { fuse_reply_err(req, ENOENT); return; } res = vmfs_dir_delete(dir,name); vmfs_dir_close(dir); fuse_reply_err(req,-res); } static void vmfs_fuse_opendir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) { vmfs_fs_t *fs = (vmfs_fs_t *) fuse_req_userdata(req); fi->fh = (uint64_t)(unsigned long) vmfs_dir_open_from_blkid(fs, ino2blkid(ino)); if (fi->fh) fuse_reply_open(req, fi); else fuse_reply_err(req, ENOTDIR); } static void vmfs_fuse_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi) { char buf[size]; const vmfs_dirent_t *entry; struct stat st = {0, }; size_t sz; if (!fi->fh) { fuse_reply_err(req, EBADF); return; } if ((entry = vmfs_dir_read((vmfs_dir_t *)(unsigned long)fi->fh))) { st.st_mode = vmfs_file_type2mode(entry->type); st.st_ino = blkid2ino(entry->block_id); sz = fuse_add_direntry(req, buf, size, entry->name, &st, off + 1); fuse_reply_buf(req, buf, sz); } else fuse_reply_buf(req, NULL, 0); } static void vmfs_fuse_releasedir(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) { if (!fi->fh) { fuse_reply_err(req, EBADF); return; } vmfs_dir_close((vmfs_dir_t *)(unsigned long)fi->fh); fuse_reply_err(req, 0); } static void vmfs_fuse_statfs(fuse_req_t req, fuse_ino_t ino) { vmfs_fs_t *fs = (vmfs_fs_t *) fuse_req_userdata(req); struct statvfs st; u_int alloc_count; memset(&st,0,sizeof(st)); /* Blocks */ alloc_count = vmfs_bitmap_allocated_items(fs->fbb); st.f_bsize = st.f_frsize = vmfs_fs_get_blocksize(fs); st.f_blocks = fs->fbb->bmh.total_items; st.f_bfree = st.f_bavail = st.f_blocks - alloc_count; /* Inodes */ alloc_count = vmfs_bitmap_allocated_items(fs->fdc); st.f_files = fs->fdc->bmh.total_items; st.f_ffree = st.f_favail = st.f_files - alloc_count; fuse_reply_statfs(req,&st); } static void vmfs_fuse_lookup(fuse_req_t req, fuse_ino_t parent, const char *name) { struct fuse_entry_param entry = { 0, }; vmfs_fs_t *fs = (vmfs_fs_t *) fuse_req_userdata(req); vmfs_dir_t *d = vmfs_dir_open_from_blkid(fs, ino2blkid(parent)); const vmfs_dirent_t *rec; if (!d) { fuse_reply_err(req, ENOENT); return; } rec = vmfs_dir_lookup(d, name); if (rec && !vmfs_inode_stat_from_blkid(fs, rec->block_id, &entry.attr)) { entry.ino = entry.attr.st_ino = blkid2ino(rec->block_id); entry.generation = 1; entry.attr_timeout = 1.0; entry.entry_timeout = 1.0; fuse_reply_entry(req, &entry); } else fuse_reply_err(req, ENOENT); vmfs_dir_close(d); } static void vmfs_fuse_open(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) { vmfs_fs_t *fs = (vmfs_fs_t *) fuse_req_userdata(req); fi->fh = (uint64_t)(unsigned long) vmfs_file_open_from_blkid(fs, ino2blkid(ino)); if (fi->fh) fuse_reply_open(req, fi); else fuse_reply_err(req, ENOTDIR); } static void vmfs_fuse_create(fuse_req_t req, fuse_ino_t parent, const char *name, mode_t mode, struct fuse_file_info *fi) { struct fuse_entry_param entry = { 0, }; vmfs_dir_t *dir; vmfs_inode_t *inode; vmfs_file_t *f; int res; if (!(dir = vmfs_dir_open_from_blkid(fs, ino2blkid(parent)))) { fuse_reply_err(req, ENOENT); return; } if ((res = vmfs_file_create(dir,name,mode,&inode)) < 0) { fuse_reply_err(req, -res); return; } vmfs_dir_close(dir); if (!(f = vmfs_file_open_from_inode(inode))) { vmfs_inode_release(inode); fuse_reply_err(req,ENOMEM); } fi->fh = (uint64_t)(unsigned long)f; vmfs_inode_stat(inode,&entry.attr); entry.ino = entry.attr.st_ino = blkid2ino(inode->id); entry.generation = 1; entry.attr_timeout = 1.0; entry.entry_timeout = 1.0; fuse_reply_create(req,&entry,fi); } static void vmfs_fuse_read(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi) { char buf[size]; ssize_t sz; if (!fi->fh) { fuse_reply_err(req, EBADF); return; } sz = vmfs_file_pread((vmfs_file_t *)(unsigned long)fi->fh, (u_char *)buf, size, off); if (sz < 0) { fuse_reply_err(req, -sz); return; } fuse_reply_buf(req, buf, sz); } static void vmfs_fuse_write(fuse_req_t req, fuse_ino_t ino, const char *buf, size_t size, off_t off, struct fuse_file_info *fi) { ssize_t sz; if (!fi->fh) { fuse_reply_err(req, EBADF); return; } sz = vmfs_file_pwrite((vmfs_file_t *)(unsigned long)fi->fh, (u_char *)buf, size, off); if (sz < 0) { fuse_reply_err(req, -sz); return; } fuse_reply_write(req,sz); } static void vmfs_fuse_release(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) { if (!fi->fh) { fuse_reply_err(req, EBADF); return; } vmfs_file_close((vmfs_file_t *)(unsigned long)fi->fh); fuse_reply_err(req, 0); } const static struct fuse_lowlevel_ops vmfs_oper = { .getattr = vmfs_fuse_getattr, .setattr = vmfs_fuse_setattr, .readlink = vmfs_fuse_readlink, .mknod = vmfs_fuse_mknod, .mkdir = vmfs_fuse_mkdir, .unlink = vmfs_fuse_unlink, .rmdir = vmfs_fuse_rmdir, .opendir = vmfs_fuse_opendir, .readdir = vmfs_fuse_readdir, .releasedir = vmfs_fuse_releasedir, .statfs = vmfs_fuse_statfs, .lookup = vmfs_fuse_lookup, .open = vmfs_fuse_open, .create = vmfs_fuse_create, .read = vmfs_fuse_read, .write = vmfs_fuse_write, .release = vmfs_fuse_release, }; struct vmfs_fuse_opts { char **path; char *paths[VMFS_LVM_MAX_EXTENTS + 1]; char *mountpoint; int foreground; }; static const struct fuse_opt vmfs_fuse_args[] = { { "-d", offsetof(struct vmfs_fuse_opts, foreground), 1 }, { "-f", offsetof(struct vmfs_fuse_opts, foreground), 1 }, FUSE_OPT_KEY("-d", FUSE_OPT_KEY_KEEP), }; static int vmfs_fuse_opts_func(void *data, const char *arg, int key, struct fuse_args *outargs) { struct vmfs_fuse_opts *opts = (struct vmfs_fuse_opts *) data; struct stat st; if (key == FUSE_OPT_KEY_NONOPT) { if (opts->mountpoint) { fprintf(stderr, "'%s' is not allowed here\n", arg); return -1; } if (stat(arg, &st)) { fprintf(stderr, "Error stat()ing '%s'\n", arg); return -1; } if (S_ISDIR(st.st_mode)) { opts->mountpoint = strdup(arg); } else if (S_ISREG(st.st_mode) || S_ISBLK(st.st_mode)) { *(opts->path++) = strdup(arg); } return 0; } return 1; } int main(int argc, char *argv[]) { struct fuse_args args = FUSE_ARGS_INIT(argc, argv); struct vmfs_fuse_opts opts = { 0, }; struct fuse_chan *chan; vmfs_flags_t flags; int err = -1; flags.packed = 0; #ifdef VMFS_WRITE flags.read_write = 1; #endif flags.allow_missing_extents = 1; opts.path = &opts.paths[0]; if ((fuse_opt_parse(&args, &opts, vmfs_fuse_args, &vmfs_fuse_opts_func) == -1) || (fuse_opt_add_arg(&args, "-odefault_permissions"))) { goto cleanup; } if (!(fs = vmfs_fs_open(opts.paths, flags))) { fprintf(stderr,"Unable to open filesystem\n"); goto cleanup; } if ((chan = fuse_mount(opts.mountpoint, &args)) != NULL) { struct fuse_session *session; session = fuse_lowlevel_new(&args, &vmfs_oper, sizeof(vmfs_oper), fs); if (session != NULL) { fuse_daemonize(opts.foreground); if (fuse_set_signal_handlers(session) != -1) { fuse_session_add_chan(session, chan); err = fuse_session_loop(session); fuse_remove_signal_handlers(session); fuse_session_remove_chan(chan); } fuse_session_destroy(session); } fuse_unmount(opts.mountpoint, chan); } cleanup: vmfs_fs_close(fs); fuse_opt_free_args(&args); opts.path = &opts.paths[0]; for(;*opts.path;opts.path++) free(*opts.path); free(opts.mountpoint); return err ? 1 : 0; } vmfs-tools-0.2.5/vmfs-fuse/vmfs-fuse.txt0000644000175000017500000000100711733303644016012 0ustar mhmhvmfs-fuse(8) ============ NAME ---- vmfs-fuse - mount VMFS file system SYNOPSIS -------- *vmfs-fuse* 'VOLUME'... 'MOUNT_POINT' DESCRIPTION ----------- The *vmfs-fuse* program allows to mount VMFS file systems. The 'VOLUME' to be opened can be either a block device or an image file. When the VMFS spreads accross several extents, all extents must be given. The 'MOUNT_POINT' indicates where the file system will be attached on the system. AUTHORS ------- include::../AUTHORS[] SEE ALSO -------- debugvmfs(8) vmfs-tools-0.2.5/vmfs-fuse/manifest.mk0000644000175000017500000000003111733303644015471 0ustar mhmhREQUIRES := libvmfs fuse vmfs-tools-0.2.5/vmfs-lvm/0000755000175000017500000000000011733555001013170 5ustar mhmhvmfs-tools-0.2.5/vmfs-lvm/vmfs-lvm.txt0000644000175000017500000000130511733303644015503 0ustar mhmhvmfs-lvm(8) =========== NAME ---- vmfs-lvm - VMFS logical volume manager tool SYNOPSIS -------- *vmfs-lvm* 'VOLUME'... 'COMMAND' DESCRIPTION ----------- The *vmfs-lvm* program allows handle VMFS physical and logical volumes. The 'VOLUME' to be opened can be either a block device or an image file. When the VMFS spreads accross several extents, all extents must be given. COMMANDS -------- *remove*:: Removes the last extent of the logical volume. THIS IS EXPERIMENTAL. USE AT YOUR OWN RISK. It is highly recommended that the volume is not mounted at the same time. This means the tool shouldn't be run on the ESX service console. AUTHORS ------- include::../AUTHORS[] SEE ALSO -------- vmfs-fuse(8) vmfs-tools-0.2.5/vmfs-lvm/manifest.mk0000644000175000017500000000014211733536146015335 0ustar mhmhLDFLAGS := $(DLOPEN_LDFLAGS) vmfs-lvm.o_CFLAGS := -include version REQUIRES := libvmfs libreadcmd vmfs-tools-0.2.5/vmfs-lvm/vmfs-lvm.c0000755000175000017500000001536511733551126015124 0ustar mhmh/* * vmfs-tools - Tools to access VMFS filesystems * Copyright (C) 2009,2011,2012 Mike Hommey * * 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. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include "readcmd.h" #include "vmfs.h" /* Until a better API is defined */ int vmfs_bmh_write(const vmfs_bitmap_header_t *bmh,u_char *buf); static int cmd_remove(vmfs_fs_t *fs,int argc,char *argv[]) { /* Dangerous */ vmfs_lvm_t *lvm = (vmfs_lvm_t *)fs->dev; vmfs_volume_t *extent; DECL_ALIGNED_BUFFER(buf,512); vmfs_bitmap_entry_t entry; uint32_t i, blocks_per_segment, items_per_area, items_in_last_entry, old_area_count; fprintf(stderr, "Extents removal is experimental ! Use at your own risk !\n"); while (1) { char *answer = local_readline("Are you sure you want to go further? [y/N] "); char a = answer[0]; char null = answer[1]; free(answer); if ((a == 0) || ((tolower(a) == 'n') && (null == 0))) return(2); if ((tolower(a) == 'y') && (null == 0)) break; } if (lvm->lvm_info.num_extents == 1) { fprintf(stderr, "Can't remove an extent when there is only one\n"); return(1); } /* Get last extent */ extent = lvm->extents[lvm->loaded_extents - 1]; /* Check whether data blocks are used on the last extents */ blocks_per_segment = VMFS_LVM_SEGMENT_SIZE / vmfs_fs_get_blocksize(fs); for (i = extent->vol_info.first_segment * blocks_per_segment; i < extent->vol_info.last_segment * blocks_per_segment; i++) if (vmfs_block_get_status(fs, VMFS_BLK_FB_BUILD(i, 0))) { fprintf(stderr, "There is data on the last extent ; can't remove it\n"); return(1); } /* At filesystem level, only the FBB needs to be downsized */ fs->fbb->bmh.total_items -= extent->vol_info.num_segments * blocks_per_segment; items_per_area = fs->fbb->bmh.items_per_bitmap_entry * fs->fbb->bmh.bmp_entries_per_area; old_area_count = fs->fbb->bmh.area_count; fs->fbb->bmh.area_count = (fs->fbb->bmh.total_items + items_per_area - 1) / items_per_area; memset(buf, 0, buf_len); vmfs_bmh_write(&fs->fbb->bmh, buf); /* TODO: Error handling */ vmfs_file_pwrite(fs->fbb->f, buf, buf_len, 0); /* Adjust new last entry */ if ((items_in_last_entry = fs->fbb->bmh.total_items % fs->fbb->bmh.items_per_bitmap_entry)) { vmfs_bitmap_get_entry(fs->fbb, 0, fs->fbb->bmh.total_items, &entry); entry.free -= entry.total - items_in_last_entry; entry.total = items_in_last_entry; if (entry.ffree > entry.total) entry.ffree = 0; /* Adjust bitmap in last entry */ if (items_in_last_entry % 8) entry.bitmap[items_in_last_entry / 8] &= 0xff << (8 - (items_in_last_entry % 8)); memset(&entry.bitmap[(items_in_last_entry + 7) / 8], 0, (fs->fbb->bmh.items_per_bitmap_entry - items_in_last_entry - 7) / 8); vmfs_bme_update(fs, &entry); } /* Truncate the fbb file depending on the new area count */ if (old_area_count != fs->fbb->bmh.area_count) vmfs_file_truncate(fs->fbb->f, fs->fbb->bmh.hdr_size + fs->fbb->bmh.area_count * fs->fbb->bmh.area_size); /* Adjust entries after the new last one */ for (i = fs->fbb->bmh.total_items + (items_in_last_entry ? (fs->fbb->bmh.items_per_bitmap_entry - items_in_last_entry) : 0); i < fs->fbb->bmh.area_count * items_per_area; i += fs->fbb->bmh.items_per_bitmap_entry) { uint64_t pos; vmfs_bitmap_get_entry(fs->fbb, 0, i, &entry); pos = entry.mdh.pos; memset(&entry, 0, sizeof(entry)); vmfs_device_write(fs->dev, pos, (u_char *)&entry, sizeof(entry)); } /* At LVM level, all extents need to have the LVM information updated */ for (i = 0; i < lvm->loaded_extents - 1; i++) { lvm->extents[i]->vol_info.blocks -= extent->vol_info.num_segments + 1; lvm->extents[i]->vol_info.num_extents--; lvm->extents[i]->vol_info.lvm_size = (lvm->extents[i]->vol_info.blocks - lvm->extents[i]->vol_info.num_extents) * VMFS_LVM_SEGMENT_SIZE; /* Until there is an API for that, do it by hand */ m_pread(lvm->extents[i]->fd,buf,buf_len, lvm->extents[i]->vmfs_base + VMFS_LVMINFO_OFFSET); write_le64(buf, VMFS_LVMINFO_OFS_SIZE - VMFS_LVMINFO_OFFSET, lvm->extents[i]->vol_info.lvm_size); write_le64(buf, VMFS_LVMINFO_OFS_BLKS - VMFS_LVMINFO_OFFSET, lvm->extents[i]->vol_info.blocks); write_le32(buf, VMFS_LVMINFO_OFS_NUM_EXTENTS - VMFS_LVMINFO_OFFSET, lvm->extents[i]->vol_info.num_extents); m_pwrite(lvm->extents[i]->fd,buf,buf_len, lvm->extents[i]->vmfs_base + VMFS_LVMINFO_OFFSET); } return(0); } struct cmd { char *name; char *description; int (*fn)(vmfs_fs_t *fs,int argc,char *argv[]); }; struct cmd cmd_array[] = { { "remove", "Remove an extent", cmd_remove }, { NULL, } }; static void show_usage(char *prog_name) { int i; char *name = basename(prog_name); fprintf(stderr,"%s " VERSION "\n",name); fprintf(stderr,"Syntax: %s \n\n",name); fprintf(stderr,"Available commands:\n"); for(i=0;cmd_array[i].name;i++) fprintf(stderr," - %s : %s\n",cmd_array[i].name,cmd_array[i].description); fprintf(stderr,"\n"); } static struct cmd *cmd_find(char *name) { int i; for(i=0;cmd_array[i].name;i++) if (!strcmp(cmd_array[i].name,name)) return(&cmd_array[i]); return NULL; } int main(int argc,char *argv[]) { vmfs_fs_t *fs; struct cmd *cmd = NULL; int arg, ret; vmfs_flags_t flags; if (argc < 3) { show_usage(argv[0]); return(0); } /* Scan arguments for a command */ for (arg = 1; arg < argc; arg++) { if ((cmd = cmd_find(argv[arg]))) break; } if (!cmd) { show_usage(argv[0]); return(0); } flags.packed = 0; flags.read_write = 1; argv[arg] = NULL; if (!(fs = vmfs_fs_open(&argv[1], flags))) { fprintf(stderr,"Unable to open filesystem\n"); exit(EXIT_FAILURE); } ret = cmd->fn(fs,argc-arg-1,&argv[arg+1]); vmfs_fs_close(fs); return(ret); } vmfs-tools-0.2.5/fsck.vmfs/0000755000175000017500000000000011733555001013321 5ustar mhmhvmfs-tools-0.2.5/fsck.vmfs/vmfs_fsck.c0000644000175000017500000003436511733554656015500 0ustar mhmh/* * vmfs-tools - Tools to access VMFS filesystems * Copyright (C) 2009 Christophe Fillot * Copyright (C) 2009,2012 Mike Hommey * * 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. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include "vmfs.h" /* Forward declarations */ typedef struct vmfs_dir_map vmfs_dir_map_t; typedef struct vmfs_blk_map vmfs_blk_map_t; /* Directory mapping */ struct vmfs_dir_map { char *name; uint32_t blk_id; int is_dir; vmfs_blk_map_t *blk_map; vmfs_dir_map_t *parent; vmfs_dir_map_t *next; vmfs_dir_map_t *child_list,*child_last; }; /* * Block mapping, which allows to keep track of inodes given a block number. * Used for troubleshooting/debugging/future fsck. */ #define VMFS_BLK_MAP_BUCKETS 512 #define VMFS_BLK_MAP_MAX_INODES 32 struct vmfs_blk_map { uint32_t blk_id; union { uint32_t inode_id[VMFS_BLK_MAP_MAX_INODES]; vmfs_inode_t inode; }; vmfs_dir_map_t *dir_map; u_int ref_count; u_int nlink; int status; vmfs_blk_map_t *prev,*next; }; typedef struct vmfs_fsck_info vmfs_fsck_info_t; struct vmfs_fsck_info { vmfs_blk_map_t *blk_map[VMFS_BLK_MAP_BUCKETS]; u_int blk_count[VMFS_BLK_TYPE_MAX]; vmfs_dir_map_t *dir_map; /* Inodes referenced in directory structure but not in FDC */ u_int undef_inodes; /* Inodes present in FDC but not in directory structure */ u_int orphaned_inodes; /* Blocks present in inodes but not marked as allocated */ u_int unallocated_blocks; /* Lost blocks (ie allocated but not used) */ u_int lost_blocks; /* Directory structure errors */ u_int dir_struct_errors; }; /* Allocate a directory map structure */ vmfs_dir_map_t *vmfs_dir_map_alloc(const char *name,uint32_t blk_id) { vmfs_dir_map_t *map; if (!(map = calloc(1,sizeof(*map)))) return NULL; if (!(map->name = strdup(name))) { free(map); return NULL; } map->blk_id = blk_id; return map; } /* Create the root directory mapping */ vmfs_dir_map_t *vmfs_dir_map_alloc_root(void) { vmfs_dir_map_t *map; uint32_t root_blk_id; root_blk_id = VMFS_BLK_FD_BUILD(0, 0, 0); if (!(map = vmfs_dir_map_alloc("/",root_blk_id))) return NULL; map->parent = map; return map; } /* Add a child entry */ void vmfs_dir_map_add_child(vmfs_dir_map_t *parent,vmfs_dir_map_t *child) { child->parent = parent; if (parent->child_last != NULL) parent->child_last->next = child; else parent->child_list = child; parent->child_last = child; child->next = NULL; } /* Display a directory map */ void vmfs_dir_map_show_entry(vmfs_dir_map_t *map,int level) { vmfs_dir_map_t *child; int i; for(i=0;iname,map->blk_id); for(child=map->child_list;child;child=child->next) vmfs_dir_map_show_entry(child,level+1); } /* Display the directory hierarchy */ void vmfs_dir_map_show(vmfs_fsck_info_t *fi) { vmfs_dir_map_show_entry(fi->dir_map,0); } /* Get directory full path */ static char *vmfs_dir_map_get_path_internal(vmfs_dir_map_t *map, char *buffer,size_t len) { char *ptr; size_t plen; if (map->parent != map) { ptr = vmfs_dir_map_get_path_internal(map->parent,buffer,len); plen = len - (ptr - buffer); ptr += snprintf(ptr,plen,"%s/",map->name); } else { ptr = buffer; ptr += snprintf(buffer,len,"%s",map->name); } return ptr; } /* Get directory full path */ char *vmfs_dir_map_get_path(vmfs_dir_map_t *map,char *buffer,size_t len) { char *p; p = vmfs_dir_map_get_path_internal(map,buffer,len); if ((--p > buffer) && (*p == '/')) *p = 0; return buffer; } /* Hash function for a block ID */ static inline u_int vmfs_block_map_hash(uint32_t blk_id) { return(blk_id ^ (blk_id >> 5)); } /* Find a block mapping */ vmfs_blk_map_t *vmfs_block_map_find(vmfs_blk_map_t **ht,uint32_t blk_id) { vmfs_blk_map_t *map; u_int bucket; bucket = vmfs_block_map_hash(blk_id) % VMFS_BLK_MAP_BUCKETS; for(map=ht[bucket];map;map=map->next) if (map->blk_id == blk_id) return map; return NULL; } /* Get a block mapping */ vmfs_blk_map_t *vmfs_block_map_get(vmfs_blk_map_t **ht,uint32_t blk_id) { vmfs_blk_map_t *map; u_int bucket; if ((map = vmfs_block_map_find(ht,blk_id)) != NULL) return map; if (!(map = calloc(1,sizeof(*map)))) return NULL; bucket = vmfs_block_map_hash(blk_id) % VMFS_BLK_MAP_BUCKETS; map->blk_id = blk_id; map->prev = NULL; map->next = ht[bucket]; ht[bucket] = map; return map; } /* Store block mapping of an inode */ static void vmfs_fsck_store_block(const vmfs_inode_t *inode, uint32_t pb_blk, uint32_t blk_id, void *opt_arg) { vmfs_blk_map_t **ht = opt_arg; vmfs_blk_map_t *map; if (!(map = vmfs_block_map_get(ht,blk_id))) return; if (map->ref_count < VMFS_BLK_MAP_MAX_INODES) map->inode_id[map->ref_count] = inode->id; map->ref_count++; map->status = vmfs_block_get_status(inode->fs,blk_id); } /* Store inode info */ static int vmfs_fsck_store_inode(const vmfs_fs_t *fs,vmfs_blk_map_t **ht, const vmfs_inode_t *inode) { vmfs_blk_map_t *map; if (!(map = vmfs_block_map_get(ht,inode->id))) return(-1); memcpy(&map->inode,inode,sizeof(*inode)); map->status = vmfs_block_get_status(fs,inode->id); return(0); } /* Iterate over all inodes of the FS and get all block mappings */ int vmfs_fsck_get_all_block_mappings(const vmfs_fs_t *fs, vmfs_fsck_info_t *fi) { vmfs_inode_t inode; vmfs_bitmap_header_t *fdc_bmp; uint32_t entry,item; int i; fdc_bmp = &fs->fdc->bmh; printf("Scanning %u FDC entries...\n",fdc_bmp->total_items); for(i=0;itotal_items;i++) { entry = i / fdc_bmp->items_per_bitmap_entry; item = i % fdc_bmp->items_per_bitmap_entry; /* Skip undefined/deleted inodes */ if ((vmfs_inode_get(fs, VMFS_BLK_FD_BUILD(entry, item, 0), &inode) == -1) || !inode.nlink) continue; inode.fs = fs; vmfs_fsck_store_inode(fs,fi->blk_map,&inode); vmfs_inode_foreach_block(&inode,vmfs_fsck_store_block,fi->blk_map); } return(0); } /* Display Inode IDs for blocks incorrectly shared by multiple inodes */ void vmfs_fsck_show_inode_id(vmfs_blk_map_t *map) { u_int i,inode_count; inode_count = m_min(map->ref_count,VMFS_BLK_MAP_MAX_INODES); for(i=0;iinode_id[i]); printf("\n"); } /* Count block types */ void vmfs_fsck_count_blocks(vmfs_fsck_info_t *fi) { vmfs_blk_map_t *map; u_int blk_type; int i; for(i=0;iblk_map[i];map;map=map->next) { blk_type = VMFS_BLK_TYPE(map->blk_id); if ((blk_type != VMFS_BLK_TYPE_FD) && (map->ref_count > 1)) { printf("Block 0x%8.8x is referenced by multiple inodes: \n", map->blk_id); vmfs_fsck_show_inode_id(map); } if (blk_type < VMFS_BLK_TYPE_MAX) fi->blk_count[blk_type]++; /* Check that block is allocated */ if (map->status <= 0) { printf("Block 0x%8.8x is used but not allocated.\n",map->blk_id); fi->unallocated_blocks++; } } } printf("Data collected from inode entries:\n"); printf(" File Blocks : %u\n",fi->blk_count[VMFS_BLK_TYPE_FB]); printf(" Sub-Blocks : %u\n",fi->blk_count[VMFS_BLK_TYPE_SB]); printf(" Pointer Blocks : %u\n",fi->blk_count[VMFS_BLK_TYPE_PB]); printf(" Inodes : %u\n\n",fi->blk_count[VMFS_BLK_TYPE_FD]); } /* * Walk recursively through the directory structure and check inode * allocation. */ int vmfs_fsck_walk_dir(const vmfs_fs_t *fs, vmfs_fsck_info_t *fi, vmfs_dir_map_t *dir_map, vmfs_dir_t *dir_entry) { const vmfs_dirent_t *rec; vmfs_dir_t *sub_dir; vmfs_blk_map_t *map; vmfs_dir_map_t *dm; int res; vmfs_dir_seek(dir_entry,0); while((rec = vmfs_dir_read(dir_entry))) { if (!(map = vmfs_block_map_find(fi->blk_map,rec->block_id))) { fi->undef_inodes++; continue; } if (!(dm = vmfs_dir_map_alloc(rec->name,rec->block_id))) return(-1); vmfs_dir_map_add_child(dir_map,dm); map->dir_map = dm; map->nlink++; if (rec->type == VMFS_FILE_TYPE_DIR) { dm->is_dir = 1; if (strcmp(rec->name,".") && strcmp(rec->name,"..")) { if (!(sub_dir = vmfs_dir_open_from_blkid(fs,rec->block_id))) return(-1); res = vmfs_fsck_walk_dir(fs,fi,dm,sub_dir); vmfs_dir_close(sub_dir); if (res == -1) return(-1); } } } return(0); } /* Display orphaned inodes (ie present in FDC but not in directories) */ void vmfs_fsck_show_orphaned_inodes(vmfs_fsck_info_t *fi) { vmfs_blk_map_t *map; int i; for(i=0;iblk_map[i];map;map=map->next) { if (VMFS_BLK_TYPE(map->blk_id) != VMFS_BLK_TYPE_FD) continue; if (map->nlink == 0) { printf("Orphaned inode 0x%8.8x\n",map->inode.id); fi->orphaned_inodes++; } } } } /* Check if a File Block is lost */ void vmfs_fsck_check_fb_lost(vmfs_bitmap_t *b,uint32_t addr,void *opt) { vmfs_fsck_info_t *fi = opt; uint32_t blk_id; blk_id = VMFS_BLK_FB_BUILD(addr, 0); if (!vmfs_block_map_find(fi->blk_map,blk_id)) { printf("File Block 0x%8.8x is lost.\n",blk_id); fi->lost_blocks++; } } /* Check if a Sub-Block is lost */ void vmfs_fsck_check_sb_lost(vmfs_bitmap_t *b,uint32_t addr,void *opt) { vmfs_fsck_info_t *fi = opt; uint32_t entry,item; uint32_t blk_id; entry = addr / b->bmh.items_per_bitmap_entry; item = addr % b->bmh.items_per_bitmap_entry; blk_id = VMFS_BLK_SB_BUILD(entry, item, 0); if (!vmfs_block_map_find(fi->blk_map,blk_id)) { printf("Sub-Block 0x%8.8x is lost.\n",blk_id); fi->lost_blocks++; } } /* Check if a Pointer Block is lost */ void vmfs_fsck_check_pb_lost(vmfs_bitmap_t *b,uint32_t addr,void *opt) { vmfs_fsck_info_t *fi = opt; uint32_t entry,item; uint32_t blk_id; entry = addr / b->bmh.items_per_bitmap_entry; item = addr % b->bmh.items_per_bitmap_entry; blk_id = VMFS_BLK_PB_BUILD(entry, item, 0); if (!vmfs_block_map_find(fi->blk_map,blk_id)) { printf("Pointer Block 0x%8.8x is lost.\n",blk_id); fi->lost_blocks++; } } /* Check the directory has minimal . and .. entries with correct inode IDs */ void vmfs_fsck_check_dir(vmfs_fsck_info_t *fi,vmfs_dir_map_t *dir) { char buffer[256]; vmfs_dir_map_t *child; int cdir,pdir; cdir = pdir = 0; for(child=dir->child_list;child;child=child->next) { if (!strcmp(child->name,".")) { cdir++; if (child->blk_id != dir->blk_id) { printf("Invalid . entry in %s\n", vmfs_dir_map_get_path(dir,buffer,sizeof(buffer))); fi->dir_struct_errors++; } continue; } if (!strcmp(child->name,"..")) { pdir++; if (child->blk_id != dir->parent->blk_id) { printf("Invalid .. entry in %s\n", vmfs_dir_map_get_path(dir,buffer,sizeof(buffer))); fi->dir_struct_errors++; } continue; } if (child->is_dir) vmfs_fsck_check_dir(fi,child); } if ((cdir != 1) || (pdir != 1)) fi->dir_struct_errors++; } /* Check the directory structure */ void vmfs_fsck_check_dir_all(vmfs_fsck_info_t *fi) { vmfs_fsck_check_dir(fi,fi->dir_map); } /* Initialize fsck structures */ static void vmfs_fsck_init(vmfs_fsck_info_t *fi) { memset(fi,0,sizeof(*fi)); fi->dir_map = vmfs_dir_map_alloc_root(); } static void show_usage(char *prog_name) { char *name = basename(prog_name); fprintf(stderr,"%s " VERSION "\n",name); fprintf(stderr,"Syntax: %s \n\n",name); } int main(int argc,char *argv[]) { vmfs_fsck_info_t fsck_info; vmfs_fs_t *fs; vmfs_flags_t flags; vmfs_dir_t *root_dir; if (argc < 2) { show_usage(argv[0]); return(0); } flags.packed = 0; if (!(fs = vmfs_fs_open(&argv[1], flags))) { fprintf(stderr,"Unable to open filesystem\n"); exit(EXIT_FAILURE); } vmfs_fsck_init(&fsck_info); vmfs_fsck_get_all_block_mappings(fs,&fsck_info); if (!(root_dir = vmfs_dir_open_from_blkid(fs, VMFS_BLK_FD_BUILD(0, 0, 0)))) { fprintf(stderr,"Unable to open root directory\n"); exit(EXIT_FAILURE); } vmfs_fsck_walk_dir(fs,&fsck_info,fsck_info.dir_map,root_dir); vmfs_dir_close(root_dir); vmfs_fsck_count_blocks(&fsck_info); vmfs_fsck_show_orphaned_inodes(&fsck_info); vmfs_bitmap_foreach(fs->fbb,vmfs_fsck_check_fb_lost,&fsck_info); vmfs_bitmap_foreach(fs->sbc,vmfs_fsck_check_sb_lost,&fsck_info); vmfs_bitmap_foreach(fs->pbc,vmfs_fsck_check_pb_lost,&fsck_info); vmfs_fsck_check_dir_all(&fsck_info); printf("Unallocated blocks : %u\n",fsck_info.unallocated_blocks); printf("Lost blocks : %u\n",fsck_info.lost_blocks); printf("Undefined inodes : %u\n",fsck_info.undef_inodes); printf("Orphaned inodes : %u\n",fsck_info.orphaned_inodes); printf("Directory errors : %u\n",fsck_info.dir_struct_errors); vmfs_fs_close(fs); return(0); } vmfs-tools-0.2.5/fsck.vmfs/manifest.mk0000644000175000017500000000007311733536116015466 0ustar mhmhvmfs_fsck.o_CFLAGS := -include version REQUIRES := libvmfs vmfs-tools-0.2.5/fsck.vmfs/fsck.vmfs.txt0000644000175000017500000000067311733303644015774 0ustar mhmhfsck.vmfs(8) ============ NAME ---- fsck.vmfs - VMFS file system check utility SYNOPSIS -------- *fsck.vmfs* 'VOLUME'... DESCRIPTION ----------- The *fsck.vmfs* performs various integrity checks on a VMFS file system. The 'VOLUME' to be opened can be either a block device or an image file. When the VMFS spreads accross several extents, all extents must be given. AUTHORS ------- include::../AUTHORS[] SEE ALSO -------- debugvmfs(8) vmfs-tools-0.2.5/version0000644000175000017500000000007611733555001013034 0ustar mhmh#if 1 #define VERSION "v0.2.5" #else VERSION := v0.2.5 #endif