/* $Id: sfsrodb.C,v 1.29 2002/01/02 18:55:08 fubob Exp $ */ /* * Copyright (C) 1999 Kevin Fu (fubob@mit.edu) * Copyright (C) 1999 Frans Kaashoek (kaashoek@mit.edu) * * 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, 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.,4 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA * */ /* * This program will generate the integrity database for the SFS read-only * file system. Run this program after every change to exported files */ /* The hash tree works very similar to that of the indirect data pointers in an inode. */ #include "sysconf.h" #include #include #include #include "sfsrodb.h" #include "parseopt.h" dbfe *sfsrodb; dbfe *sfsrofhdb; bool incremental_mode; bool verbose_mode; bool error_check; u_int32_t blocksize; extern int errno; char IV[SFSRO_IVSIZE]; int relpathlen; extern ptr < rabin_priv > sfsrokey; str hostname; /* Statistics */ u_int32_t reginode_cnt = 0; u_int32_t lnkinode_cnt = 0; u_int32_t filedatablk_cnt = 0; u_int32_t indir_cnt = 0; u_int32_t directory_cnt = 0; u_int32_t fhdb_cnt = 0; u_int32_t fh_cnt = 0; u_int32_t identical_block = 0; u_int32_t identical_indir = 0; u_int32_t identical_dir = 0; u_int32_t identical_inode = 0; u_int32_t identical_sym = 0; u_int32_t identical_fhdb = 0; u_int32_t identical_fh = 0; time_t sfsro_duration = 31536000; /* default to 365 days */ /* True if only can LOOKUP, not READDIR Really should make more fine grained. Allow specification of which directories to make opaque. */ bool opaque_directory = false; /* Given: A filled stat structure and allocated inode Return: A SFSRO inode which reflects all of the stat values. The data pointers are initialized with .setsize(0). The array of direct pointers is initialize with no members. However, the caller will eventually have to set the following values: .size, .used, and direct/indirect data pointers. */ void sfsrodb_setinode (const struct stat *st, sfsro_inode *inode) { /* SFSRO has implied read-access by all. We only care whether the file is a non-directory executable. Everything else is synthesised by sfsrocd. */ ftypero t; if (S_ISREG (st->st_mode)) { t = ((st->st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) ? SFSROREG_EXEC : SFSROREG); } else if (S_ISDIR (st->st_mode)) t = opaque_directory ? SFSRODIR_OPAQ : SFSRODIR; else if (S_ISLNK (st->st_mode)) t = SFSROLNK; else { warn << "Non-supported file type " << st->st_mode << "\n"; exit (1); } inode->set_type (t); if (inode->type == SFSROLNK) { rpc_clear (*inode->lnk); inode->lnk->nlink = st->st_nlink; // XXX bogus! cannot rely on this number #ifdef SFS_HAVE_STAT_ST_ATIMESPEC inode->lnk->mtime.seconds = st->st_mtimespec.tv_sec; inode->lnk->mtime.nseconds = st->st_mtimespec.tv_nsec; inode->lnk->ctime.seconds = st->st_ctimespec.tv_sec; inode->lnk->ctime.nseconds = st->st_ctimespec.tv_nsec; #else inode->lnk->mtime.seconds = st->st_mtime; inode->lnk->mtime.nseconds = 0; inode->lnk->ctime.seconds = st->st_ctime; inode->lnk->ctime.nseconds = 0; #endif /* SFS_HAVE_ST_ATIMESPEC */ } else { rpc_clear (*inode->reg); inode->reg->nlink = st->st_nlink; // XXX bogus! cannot rely on this number inode->reg->size = 0; inode->reg->used = 0; #ifdef SFS_HAVE_STAT_ST_ATIMESPEC inode->reg->mtime.seconds = st->st_mtimespec.tv_sec; inode->reg->mtime.nseconds = st->st_mtimespec.tv_nsec; inode->reg->ctime.seconds = st->st_ctimespec.tv_sec; inode->reg->ctime.nseconds = st->st_ctimespec.tv_nsec; #else inode->reg->mtime.seconds = st->st_mtime; inode->reg->mtime.nseconds = 0; inode->reg->ctime.seconds = st->st_ctime; inode->reg->ctime.nseconds = 0; #endif /* SFS_HAVE_ST_ATIMESPEC */ inode->reg->direct.setsize (SFSRO_NDIR); } // strbuf sb; // rpc_print (sb, inode, 5, NULL, NULL); // warn << "setinode " << sb << "\n"; } /* Given: A fully specified inode Effects: Stores the inode in the database with the fh as the key Return: A file handle in fh. fh must already be allocated. This function will set the IV appropriately with prandom bits. */ void store_inode (sfsro_inode *inode, sfs_hash *fh) { sfsro_data dat (SFSRO_INODE); size_t calllen = 0; char *callbuf = NULL; xdrsuio x (XDR_ENCODE); *dat.inode = *inode; if (xdr_sfsro_data (x.xdrp (), &dat)) { calllen = x.uio ()->resid (); callbuf = suio_flatten (x.uio ()); } create_sfsrofh (IV, SFSRO_IVSIZE, fh, callbuf, calllen); // Store the inode of this path in the database if (!sfsrodb_put (sfsrodb, fh->base (), fh->size (), callbuf, calllen)) { // warn << "Found identical inode, compressing.\n"; identical_inode++; identical_fh++; } else { fh_cnt++; if (inode->type == SFSROLNK) lnkinode_cnt++; else reginode_cnt++; } xfree (callbuf); } bool store_file_block (sfs_hash *fh, const char *block, size_t size) { sfsro_data res (SFSRO_FILEBLK); res.data->setsize (size); memcpy (res.data->base (), block, size); size_t calllen = 0; char *callbuf = NULL; xdrsuio x (XDR_ENCODE); if (xdr_sfsro_data (x.xdrp (), &res)) { calllen = x.uio ()->resid (); callbuf = suio_flatten (x.uio ()); } create_sfsrofh (IV, SFSRO_IVSIZE, fh, callbuf, calllen); if (!sfsrodb_put (sfsrodb, fh->base (), fh->size (), callbuf, calllen)) { // warnx << "Found identical block, compressing\n"; identical_block++; identical_fh++; xfree (callbuf); return false; } filedatablk_cnt++; fh_cnt++; xfree (callbuf); return true; } inline bool process_sindirect (int &fd, bool &wrote_stuff, sfsro_inode *inode, char *block, sfs_hash &block_fh, sfs_hash *fh) { size_t size = 0; // warnx << "Adding sindirect pointers\n"; uint32 blocknum = 0; sfsro_data sindir (SFSRO_INDIR); sindir.indir->handles.setsize (SFSRO_NFH); while (blocknum < SFSRO_NFH) { size = read (fd, block, SFSRO_BLKSIZE); if (size <= 0) break; inode->reg->size += size; /* Check for identical blocks */ if (store_file_block (&block_fh, block, size)) { inode->reg->used += size; // warnx << "Added direct, size " << size << ", blocknum " // << blocknum << "\n"; } sindir.indir->handles[blocknum] = block_fh; blocknum++; } if (size < 0) { warnx << "store_file: Read failed in sindirect pointers\n"; exit (1); } if (blocknum != 0) { size_t calllen = 0; char *callbuf = NULL; xdrsuio x (XDR_ENCODE); if (xdr_sfsro_data (x.xdrp(), &sindir)) { calllen = x.uio ()->resid (); callbuf = suio_flatten (x.uio ()); } create_sfsrofh (IV, SFSRO_IVSIZE, fh, callbuf, calllen); if (!sfsrodb_put (sfsrodb, fh->base(), fh->size(), callbuf, calllen)) { // warn << "Found identical sindirect, compressing.\n"; identical_indir++; identical_fh++; } else { indir_cnt++; fh_cnt++; } xfree (callbuf); wrote_stuff = true; } else { wrote_stuff = false; } return (size == 0); } inline bool process_dindirect (int &fd, bool &wrote_stuff, sfsro_inode *inode, char *block, sfs_hash &block_fh, sfs_hash *fh) { bool done = false; // warnx << "Adding dindirect pointers\n"; uint32 blocknum = 0; sfsro_data dindir (SFSRO_INDIR); dindir.indir->handles.setsize (SFSRO_NFH); // XXX we could be smarter here by only allocating // the number of file handles we actually need in the dindirect. while (!done && blocknum < SFSRO_NFH) { done = process_sindirect (fd, wrote_stuff, inode, block, block_fh, &dindir.indir->handles[blocknum]); if (!wrote_stuff) break; // warnx << "Added ddirect, blocknum " // << blocknum << "\n"; blocknum++; } if (blocknum != 0) { size_t calllen = 0; char *callbuf = NULL; xdrsuio x (XDR_ENCODE); if (xdr_sfsro_data (x.xdrp(), &dindir)) { calllen = x.uio ()->resid (); callbuf = suio_flatten (x.uio ()); } create_sfsrofh (IV, SFSRO_IVSIZE, fh, callbuf, calllen); if (!sfsrodb_put (sfsrodb, fh->base(), fh->size(), callbuf, calllen)) { // warn << "Found identical dindirect, compressing.\n"; identical_indir++; identical_fh++; } else { indir_cnt++; fh_cnt++; } xfree (callbuf); wrote_stuff = true; } else { wrote_stuff = false; } return done; } inline bool process_tindirect (int &fd, bool &wrote_stuff, sfsro_inode *inode, char *block, sfs_hash &block_fh, sfs_hash *fh) { bool done = false; // warnx << "Adding tindirect pointers\n"; uint32 blocknum = 0; sfsro_data tindir (SFSRO_INDIR); tindir.indir->handles.setsize (SFSRO_NFH); // XXX we could be smarter here by only allocating // the number of file handles we actually need in the tindirect. while (!done && blocknum < SFSRO_NFH) { done = process_dindirect (fd, wrote_stuff, inode, block, block_fh, &tindir.indir->handles[blocknum]); if (!wrote_stuff) break; // warnx << "Added tdirect, blocknum " // << blocknum << "\n"; blocknum++; } if (blocknum != 0) { size_t calllen = 0; char *callbuf = NULL; xdrsuio x (XDR_ENCODE); if (xdr_sfsro_data (x.xdrp(), &tindir)) { calllen = x.uio ()->resid (); callbuf = suio_flatten (x.uio ()); } create_sfsrofh (IV, SFSRO_IVSIZE, fh, callbuf, calllen); if (!sfsrodb_put (sfsrodb, fh->base(), fh->size(), callbuf, calllen)) { // warn << "Found identical tindirect, compressing.\n"; identical_indir++; identical_fh++; } else { indir_cnt++; fh_cnt++; } xfree (callbuf); wrote_stuff = true; } else { wrote_stuff = false; } return done; } /* Given: A fully specified inode for a file and pointer to its data (but not file sizes or data pointers) Effects: Store the file data, fully specify the inode Return: A file handle in fh. fh must already be allocated. This function will set the IV appropriately with prandom bits. */ void store_file (sfsro_inode *inode, str path) { char block[SFSRO_BLKSIZE]; sfs_hash block_fh; int fd; size_t size = 0; bool done = false; if ((fd = open (path, O_RDONLY)) < 0) { warn << "store_file: open failed" << fd << "\n"; exit (1); } // Deal with direct pointers uint32 blocknum = 0; bzero (inode->reg->direct.base (), SFSRO_NDIR * sizeof (sfs_hash)); while (blocknum < SFSRO_NDIR) { size = read (fd, block, SFSRO_BLKSIZE); if (size <= 0) break; inode->reg->size += size; /* Check for identical blocks */ if (store_file_block (&block_fh, block, size)) { inode->reg->used += size; // warnx << "Added direct, size " << size << ", blocknum " // << blocknum << "\n"; } inode->reg->direct[blocknum] = block_fh; blocknum++; } if (size < 0) { warnx << "store_file: Read failed in direct pointers\n"; exit (1); } else if (size != 0) { bool wrote_stuff = false; // Deal with sindirect pointers done = process_sindirect (fd, wrote_stuff, inode, block, block_fh, &inode->reg->indirect); // Deal with dindirect pointers if (!done) { done = process_dindirect (fd, wrote_stuff, inode, block, block_fh, &inode->reg->double_indirect); // Deal with tindirect pointers if (!done) done = process_tindirect (fd, wrote_stuff, inode, block, block_fh, &inode->reg->triple_indirect); } } if (close (fd) < 0) { warn << "store_file: close failed\n"; exit (1); } } void vec2indir (sfs_hash *fh, vec &ib_vec) { sfsro_data data (SFSRO_INDIR); data.indir->handles.setsize (SFSRO_NFH); int blocknum = 0; while (!ib_vec.empty ()) { data.indir->handles[blocknum] = ib_vec.pop_front (); blocknum++; } if (blocknum != 0) { size_t calllen = 0; char *callbuf = NULL; xdrsuio x (XDR_ENCODE); if (xdr_sfsro_data (x.xdrp(), &data)) { calllen = x.uio ()->resid (); callbuf = suio_flatten (x.uio ()); } create_sfsrofh (IV, SFSRO_IVSIZE, fh, callbuf, calllen); if (!sfsrodb_put (sfsrodb, fh->base(), fh->size(), callbuf, calllen)) { // warn << "Found identical indirect, compressing.\n"; identical_indir++; identical_fh++; } else { indir_cnt++; fh_cnt++; } xfree (callbuf); } } /* Given: a fully specified directory, inode filled in by setinode, allocated fh Return: file handle, store directory contents, final inode values Effects: After filling in a directory structure, fill in an inode structure, store the directory in the database, and compute file handle. */ bool store_directory_block (sfsro_inode *inode, xdrsuio &x, vec &sib_vec, // single vec &dib_vec, // double vec &tib_vec, // triple bool eof, uint32 b) { // assert? xdrsuio x (XDR_ENCODE); size_t calllen = 0; char *callbuf = NULL; sfs_hash fh; calllen = x.uio ()->resid (); callbuf = suio_flatten (x.uio ()); create_sfsrofh (IV, SFSRO_IVSIZE, &fh, callbuf, calllen); if (!sfsrodb_put (sfsrodb, fh.base (), fh.size (), callbuf, calllen)) { //warn << "Found identical directory, compressing.\n"; identical_dir++; identical_fh++; } else { directory_cnt++; fh_cnt++; } xfree (callbuf); // This is bogus, we're just using one data pointer regardless // of the size of the directory. In a correct implementation, // we should only store 8KB per data block inode->reg->size += calllen; inode->reg->used += calllen; if (b < SFSRO_NDIR) inode->reg->direct[b] = fh; else { sib_vec.push_back (fh); size_t i = (b - SFSRO_NDIR); if (i < SFSRO_NFH) { if (eof || sib_vec.size () % SFSRO_NFH == 0) vec2indir (&inode->reg->indirect, sib_vec); } else { i -= SFSRO_NFH; if (i < SFSRO_NFH * SFSRO_NFH) { if (eof || sib_vec.size () % SFSRO_NFH == 0) { vec2indir (&fh, sib_vec); dib_vec.push_back (fh); } if (eof || dib_vec.size () % SFSRO_NFH == 0) { vec2indir (&inode->reg->double_indirect, dib_vec); } } else { i -= SFSRO_NFH * SFSRO_NFH; if (i < SFSRO_NFH * SFSRO_NFH * SFSRO_NFH) { if (eof || sib_vec.size () % SFSRO_NFH == 0) { vec2indir (&fh, sib_vec); dib_vec.push_back (fh); } if (eof || dib_vec.size () % SFSRO_NFH == 0) { vec2indir (&fh, dib_vec); tib_vec.push_back (fh); } if (eof || tib_vec.size () % SFSRO_NFH == 0) { vec2indir (&inode->reg->triple_indirect, tib_vec); } } else { assert(0); // too big // XX should fail gracefully? hang? } } } } // strbuf sb; //rpc_print (sb, *inode, 5, NULL, " "); //warn << "storedirectory " << sb << "\n"; return true; } // Note, this ordering is different from that of // the default "ls" sorting routine, but it is // internally consistent within SFSRO static int compare_name (const void *file1, const void *file2) { return (strcmp (*(static_cast < char *const *>(file1)), *(static_cast < char *const *>(file2)))); } void sort_dir (const str path, vec &file_list) { DIR *dirp; struct dirent *de = NULL; if ((dirp = opendir (path)) == 0) { warn << path << " is not a directory\n"; return; } while ((de = readdir (dirp))) { str filename (de->d_name); /* The client manages . and .. */ if ((filename.cmp (".") == 0) || (filename.cmp ("..") == 0)) continue; file_list.push_back (filename); } /* warnx << "Before:\n"; for (unsigned int i = 0; i < file_list.size (); i++) { warnx << file_list[i] << "\n"; } */ qsort (file_list.base (), file_list.size (), sizeof (str), compare_name); /* warnx << "After:\n"; for (unsigned int i = 0; i < file_list.size (); i++) { warnx << file_list[i] << "\n"; } */ } static bool xdr_putsfsro_dirent (XDR *x, sfs_hash &fh, str filename) { rpc_str name = filename; return // sfsro_dirent * (non-null): xdr_putint (x, 1) // sfs_hash fh && rpc_traverse (x, fh) // string name<> && rpc_traverse (x, name); } /* Given: Path to file (any kind), an allocated fh, the inode number of the path's parent Return: The hash and IV in the fh. Effects: Recursively hash everything beneath a directory It computes the cryptographic file handle for the given path, inserts the mapping from the fh to its data */ int recurse_path (const str path, sfs_hash * fh) { struct stat st; if (verbose_mode) { warn << "recurse_path (" << path << ", " << "fh)\n"; } if (lstat (path, &st) < 0) fatal << path << ": " << strerror (errno) << "\n"; sfsro_inode inode; if (S_ISLNK (st.st_mode)) { char *buf = New char[st.st_size + 1]; int nchars = readlink (path, buf, st.st_size); if (nchars > MAXPATHLEN) { warn << "symlink target too large " << path << " " << nchars << "\n"; return -1; } // buf[nchars] = 0; // warn << "recurse_path: Link\n"; sfsrodb_setinode (&st, &inode); inode.lnk->dest = nfspath3 (buf, nchars); delete[] buf; } else if (S_ISREG (st.st_mode)) { // warn << "recurse_path: Regular file\n"; sfsrodb_setinode (&st, &inode); store_file (&inode, path); /* strbuf sb; rpc_print (sb, inode, 5, NULL, " "); warn << "inode " << sb << "\n"; */ } else if (S_ISDIR (st.st_mode)) { // warn << "recurse_path: Directory\n"; sfsrodb_setinode (&st, &inode); vec sib_vec; vec dib_vec; vec tib_vec; uint32 blkcnt = 0; /* strbuf sb; rpc_print (sb, inode, 5, NULL, " "); warn << "inode " << sb << "\n"; */ vec file_list; sort_dir (path, file_list); if (verbose_mode) { warnx << "file_list.size=" << file_list.size () << "\n"; for (unsigned int i = 0; i < file_list.size (); i++) warnx << file_list[i] << "\n"; } bool errors = false; while (file_list.size () != 0) { xdrsuio x (XDR_ENCODE, true); if (!xdr_putint (&x, SFSRO_DIRBLK)) errors = true; /* for (str filename = file_list.front (); (file_list.size () != 0) && (XDR_GETPOS (&x) + 24 + ((filename.len () + 3) & ~3)) <= SFSRO_BLKSIZE; filename = file_list.front ()) { */ while (file_list.size () > 0) { str filename = file_list.front (); if (XDR_GETPOS (&x) + 24 + ((filename.len () + 3) & ~3) > SFSRO_BLKSIZE) { break; } file_list.pop_front (); sfs_hash fh; recurse_path (path << "/" << filename, &fh); if (!xdr_putsfsro_dirent (&x, fh, filename)) errors=true; } if (!xdr_putint (&x, 0) // NULL entry * || !xdr_putint (&x, !file_list.size())) // bool eof errors = true; bool eof = false; if (file_list.size() == 0) eof = true; store_directory_block (&inode, x, sib_vec, dib_vec, tib_vec, eof, blkcnt); blkcnt++; } } else { warn << "Not symlink, reg file, or directory " << path << "\n"; return -1; } // XXX do something with errors? store_inode (&inode, fh); return 0; } int sfsrodb_main (const str root, const str keyfile, const char *dbfile) { ref info = dbGetImplInfo(); for (unsigned int i=0; i < info->supportedOptions.size(); i++) warn << info->supportedOptions[i] << "\n"; //create the generic object sfsrodb = new dbfe(); //set up the options we want dbOptions opts; opts.addOption("opt_async", 0); opts.addOption("opt_cachesize", 80000); opts.addOption("opt_nodesize", 4096); opts.addOption("opt_create", 1); if (int err = sfsrodb->createdb(const_cast < char *>(dbfile), opts)) { warn << "createdb failed\n" << strerror(err) << "\n"; } if (int err = sfsrodb->opendb(const_cast < char *>(dbfile), opts)) { warn << "open returned: " << strerror(err) << err << "\n"; exit (-1); } /* Set the sfs_connectres structure (with pub key) to db */ sfs_connectres cres (SFS_OK); cres.reply->servinfo.release = SFS_RELEASE; cres.reply->servinfo.host.type = SFS_HOSTINFO; cres.reply->servinfo.host.hostname = hostname; ptr < rabin_priv > sk; if (!keyfile) { warn << "cannot locate default file sfs_host_key\n"; fatal ("errors!\n"); } else { str key = file2wstr (keyfile); if (!key) { warn << keyfile << ": " << strerror (errno) << "\n"; fatal ("errors!\n"); } else if (!(sk = import_rabin_priv (key, NULL))) { warn << "could not decode " << keyfile << "\n"; warn << key << "\n"; fatal ("errors!\n"); } } cres.reply->servinfo.host.pubkey = sk->n; cres.reply->servinfo.prog = SFSRO_PROGRAM; cres.reply->servinfo.vers = SFSRO_VERSION; bzero (&cres.reply->charge, sizeof (sfs_hashcharge)); // Set IV sfs_hash id; sfs_mkhostid (&id, cres.reply->servinfo.host); memcpy (&IV[0], id.base(), SFSRO_IVSIZE); // store file system in db sfs_hash root_fh; relpathlen = root.len (); recurse_path (root, &root_fh); sfs_fsinfo res (SFSRO_PROGRAM); res.sfsro->set_vers (SFSRO_VERSION); memcpy (res.sfsro->v1->info.iv.base (), &IV[0], SFSRO_IVSIZE); res.sfsro->v1->info.rootfh = root_fh; #if 0 // fhdb is not necessary in this shape; XXX FIX create_fhdb (&res.sfsro->v1->info.fhdb, dbfile, IV); #endif time_t start, end; res.sfsro->v1->info.type = SFS_ROFSINFO; res.sfsro->v1->info.start = start = time (NULL); res.sfsro->v1->info.duration = sfsro_duration; end = start + sfsro_duration; // XX Should make sure timezone is correct str stime (ctime (&start)); str etime (ctime (&end)); warn << "Database good from: \n " << stime << "until:\n " << etime; create_sfsrosig (&(res.sfsro->v1->sig), &(res.sfsro->v1->info), keyfile); xdrsuio x (XDR_ENCODE); if (xdr_sfs_fsinfo (x.xdrp (), &res)) { void *v = suio_flatten (x.uio ()); int l = x.uio ()->resid (); if (!sfsrodb_put (sfsrodb, "fsinfo", 6, v, l)) { warn << "Found identical fsinfo. You found a collision!\n"; exit (-1); } warn << "Added fsinfo\n"; } xdrsuio x2 (XDR_ENCODE); if (xdr_sfs_connectres (x2.xdrp (), &cres)) { int l = x2.uio ()->resid (); void *v = suio_flatten (x2.uio ()); warn << "put conres in db\n"; if (!sfsrodb_put (sfsrodb, "conres", 6, v, l)) { warn << "Found identical conres. You found a collision!\n"; exit (-1); } } if (verbose_mode) { warn << "identical blocks: " << identical_block << "\n"; warn << "identical indirs: " << identical_indir << "\n"; warn << "identical dirs: " << identical_dir << "\n"; warn << "identical inodes: " << identical_inode << "\n"; warn << "identical symlinks: " << identical_sym << "\n"; warn << "identical fhdb: " << identical_fhdb << "\n\n\n"; warn << "Database contents:\n"; warn << "Regular inodes: " << reginode_cnt << "\n"; warn << "Symlink inodes: " << lnkinode_cnt << "\n"; warn << "Directory blocks " << directory_cnt << "\n"; warn << "File data blocks: " << filedatablk_cnt << "\n"; warn << "Indir blocks: " << indir_cnt << "\n"; warn << "Fhdb blocks: " << fhdb_cnt << "\n\n\n"; warn << "identical fh's overall : " << identical_fh << "\n"; warn << "unique fh's overall : " << fh_cnt << "\n\n\n"; } warn << "close db\n"; sfsrodb->closedb (); delete sfsrodb; // XXX here we should verify for debugging that the number of entries // in fhdb is the same as the number of database entries (minus // the conres and root) return 0; } static void usage () { warnx << "usage: " << progname << " -d -s -o \n"; warnx << " [-i] [-h ] [-v] [-b ]\n"; warnx << "-d : The directory hierarchy to export\n"; warnx << "-s : Path to the secret key file\n"; warnx << "-o : Filename to output database\n"; warnx << "Optional directives:\n"; warnx << "-i : Incremental mode, update a database\n"; warnx << "-h : Hostname of replication, if not this machine\n"; warnx << "-p : Make all directories opaque\n"; warnx << "-t : Seconds until signature expires\n"; warnx << "-v : Verbose debugging output\n"; warnx << "-b : Page size of underlying database\n"; // warnx << "usage: " << progname << " [command] [options]\n\n"; //warnx << "\tinit directory [-d sdb file] [-followsymlinks] [-maxdepth max] [-key keyfile]\n"; // warnx << "\tupdate directory \n"; exit (1); } int main (int argc, char **argv) { setprogname (argv[0]); hostname = myname (); char *exp_dir = NULL; char *sk_file = NULL; char *output_file = NULL; incremental_mode = false; verbose_mode = false; error_check = true; int ch; while ((ch = getopt (argc, argv, "b:d:s:o:h:t:vip")) != -1) switch (ch) { case 'b': if (!convertint (optarg, &blocksize) || blocksize < 512 || blocksize > 0x10000) usage (); break; case 'd': exp_dir = optarg; break; case 'e': error_check = false; break; case 'h': hostname = optarg; break; case 'i': incremental_mode = true; break; case 'o': output_file = optarg; break; case 'p': opaque_directory = true; break; case 's': sk_file = optarg; break; case 't': if (!convertint (optarg, &sfsro_duration)) usage (); break; case 'v': verbose_mode = true; break; case '?': default: usage (); } argc -= optind; argv += optind; if ( (argc > 0) || !exp_dir || !sk_file || !output_file ) usage (); const char *p = hostname; if (error_check && !sfsgethost (p)) { warnx << "The hostname " << hostname << " does not properly resolve.\n" << "The SFS server requires hostnames to resolve via DNS,\n" << "not /etc/hosts. If you wish to continue, rerun SFSRODB\n" << "with the -e flag.\n"; usage (); } if (verbose_mode) { warnx << "export directory : " << exp_dir << "\n"; warnx << "SK keyfile : " << sk_file << "\n"; warnx << "dbfile : " << output_file << "\n"; warnx << "Incremental mode : "; if (incremental_mode) warnx << "On\n"; else warnx << "Off\n"; warnx << "hostname for db : " << hostname << "\n"; } return (sfsrodb_main (exp_dir, sk_file, output_file)); }