copyfs-1.0.1/0000755000175000017500000000000010522130601011774 5ustar boklmboklmcopyfs-1.0.1/TODO0000644000175000017500000000162210522115222012467 0ustar boklmboklmThoses features are planned for CopyFS 1.1 : - new fversion options : * fversion cat file version_of_file * fversion delete file version_of_file - new options when mounting the file system : * max number of versions to keep * max size to keep * what should be the oldest backup we keep - logging options - use diff between files, instead of keeping a full copy for each version. For CopyFS 1.0.2 (only bugfix), the following bugs should be fixed : - When creating a file inside a suid directory, the new file should be in the same group as the directory. - When you delete a file, and create it again, permission is taken from the old file instead of using umask. - When trying to mount a version-directory that does not exist, copyfs-daemon exit without an error, and the directory still appears to be mounted. This should be fixed, and more checks should be added in copyfs-mount. copyfs-1.0.1/ea.c0000644000175000017500000001751210522107610012537 0ustar boklmboklm/* * copyfs - copy on write filesystem http://n0x.org/copyfs/ * Copyright (C) 2004 Nicolas Vigier * Thomas Joubert * This program can be distributed under the terms of the GNU GPL. * See the file COPYING. */ #include #include #include #include #include #include #include #include "helper.h" #include "structs.h" #include "write.h" #include "rcs.h" #include "ea.h" /* * We support extended attributes to allow user-space scripts to manipulate * the file system state, such as forcing a specific version to appear, ... * * The supported attributes are : * * - rcs.locked_version : the current locked version for the file * - rcs.metadata_dump : a dump of the metadata, for scripts that need * to list the available versions. */ /* * Set the value of an extended attribute. */ int callback_setxattr(const char *path, const char *name, const char *value, size_t size, int flags) { metadata_t *metadata; version_t *version; metadata = rcs_translate_to_metadata(path, rcs_version_path); if (!metadata) return -ENOENT; if (!strcmp(name, "rcs.locked_version")) { struct fuse_context *context; unsigned int length; int vid, svid; char *dflfile, *local; /* Copy the value to NUL-terminate it */ local = safe_malloc(size + 1); local[size] = '\0'; memcpy(local, value, size); vid = 0; svid = 0; if ((sscanf(local, "%d.%d%n", &vid, &svid, &length) != 2) || (length != size)) { free(local); return -EINVAL; } free(local); /* Check if we actually have that version (or a compatible version) */ for (version = metadata->md_versions; version; version = version->v_next) { if (vid == -1) break; if ((version->v_vid == (unsigned)vid) && (svid == -1)) break; if ((version->v_vid == (unsigned)vid) && (version->v_svid == (unsigned)svid)) break; } if (!version) return -EINVAL; /* * Only allow a user to change the version if the new version has the * same owner as the one requesting the change, or if the user is root, * to prevent curious users from resurrecting versions with too lax * permissions. */ context = fuse_get_context(); if ((context->uid != 0) && (context->uid != version->v_uid)) return -EACCES; /* Try to commit to disk */ dflfile = helper_create_meta_name(path, "dfl-meta"); if (write_default_file(dflfile, vid, svid) != 0) { free(dflfile); return -errno; } free(dflfile); /* If ok, change in RAM */ metadata->md_dfl_vid = vid; metadata->md_dfl_svid = svid; return 0; } else if (!strcmp(name, "rcs.metadata_dump")) { /* This one is read-only */ return -EPERM; } else { int res; /* Pass those through */ version = rcs_find_version(metadata, LATEST, LATEST); if (!version) return -ENOENT; res = lsetxattr(version->v_rfile, name, value, size, flags); if (res == -1) return -errno; return 0; } } /* * Get the value of an extended attribute. */ int callback_getxattr(const char *path, const char *name, char *value, size_t size) { metadata_t *metadata; version_t *version; metadata = rcs_translate_to_metadata(path, rcs_version_path); if (!metadata) return -ENOENT; if (!strcmp(name, "rcs.locked_version")) { char buffer[64]; int vid, svid; if (metadata->md_dfl_vid == -1) { vid = metadata->md_versions->v_vid; svid = metadata->md_versions->v_svid; } else { vid = metadata->md_dfl_vid; svid = metadata->md_dfl_svid; } /* Get the version number */ snprintf(buffer, 64, "%i.%i", vid, svid); /* Handle the EA protocol */ if (size == 0) return strlen(buffer); if (strlen(buffer) > size) return -ERANGE; strcpy(value, buffer); return strlen(buffer); } else if (!strcmp(name, "rcs.metadata_dump")) { char **array, *result; unsigned int count; int res; /* * We need to pass the version metadata to userspace, but we also need * to pass the file type and modification type from the stat syscall, * since the userspace program may be running as a non-root, and thus * can't see the version store. */ for (count = 0, version = metadata->md_versions; version; version = version->v_next) count++; array = safe_malloc(sizeof(char *) * (count + 1)); memset(array, 0, sizeof(char *) * (count + 1)); /* Traverse the version list and build the individual strings */ for (count = 0, version = metadata->md_versions; version; version = version->v_next) { struct stat st_data; /* stat() the real file, but just ignore failures (bad version ?) */ if (lstat(version->v_rfile, &st_data) < 0) { st_data.st_mode = S_IFREG; st_data.st_mtime = -1; } else st_data.st_mode &= ~07777; if (asprintf(&array[count], "%d:%d:%d:%d:%d:%lld:%ld", version->v_vid, version->v_svid, version->v_mode | st_data.st_mode, version->v_uid, version->v_gid, st_data.st_size, st_data.st_mtime) < 0) { unsigned int i; /* Free everything if it failed */ for (i = 0; i < count; i++) free(array[i]); free(array); return -ENOMEM; } count++; } /* Build the final string */ result = helper_build_composite("A", "|", array); helper_free_array(array); /* Handle the EA protocol */ if (size == 0) res = strlen(result); else if (strlen(result) > size) res = -ERANGE; else { strcpy(value, result); res = strlen(result); } free(result); return res; } else { int res; /* * We are not interested in those, simply forward them to the real * filesystem. */ version = rcs_find_version(metadata, LATEST, LATEST); if (!version) return -ENOENT; res = lgetxattr(version->v_rfile, name, value, size); if (res == -1) return -errno; return res; } } #define ATTRIBUTE_STRING "rcs.locked_version\0rcs.metadata_dump" /* * List the supported extended attributes. */ int callback_listxattr(const char *path, char *list, size_t size) { char *rpath, *buffer; unsigned int length; int res; rpath = rcs_translate_path(path, rcs_version_path); if (!rpath) return -ENOENT; /* We need to get the EAs of the real file, and mix our own */ res = llistxattr(path, NULL, 0); if (res == -1) { /* Ignore errors, as many filesystems don't support EA */ length = 0; buffer = safe_malloc(sizeof(ATTRIBUTE_STRING)); } else { length = res; buffer = safe_malloc(length + sizeof(ATTRIBUTE_STRING)); if (llistxattr(rpath, buffer, length) == -1) { free(buffer); free(rpath); return -errno; } } free(rpath); /* Append ours to the buffer */ memcpy(buffer + length, ATTRIBUTE_STRING, sizeof(ATTRIBUTE_STRING)); length += sizeof(ATTRIBUTE_STRING); /* Handle the EA protocol */ if (size == 0) res = length; else if (length > size) res = -ERANGE; else { memcpy(list, buffer, length); res = length; } free(buffer); return res; } /* * Remove an extended attribute. */ int callback_removexattr(const char *path, const char *name) { if (!strcmp(name, "rcs.locked_version") || !strcmp(name, "rcs.metadata_dump")) { /* Our attributes can't be deleted */ return -EPERM; } else { char *rpath; int res; rpath = rcs_translate_path(path, rcs_version_path); res = lremovexattr(rpath, name); free(rpath); if (res == -1) return -errno; return 0; } } copyfs-1.0.1/ea.h0000644000175000017500000000121710522107621012541 0ustar boklmboklm/* * copyfs - copy on write filesystem http://n0x.org/copyfs/ * Copyright (C) 2004 Nicolas Vigier * Thomas Joubert * This program can be distributed under the terms of the GNU GPL. * See the file COPYING. */ #ifndef EA_H # define EA_H int callback_setxattr(const char *path, const char *name, const char *value, size_t size, int flags); int callback_getxattr(const char *path, const char *name, char *value, size_t size); int callback_listxattr(const char *path, char *list, size_t size); int callback_removexattr(const char *path, const char *name); #endif /* !EA_H */ copyfs-1.0.1/interface.c0000644000175000017500000002555110522107477014127 0ustar boklmboklm/* * copyfs - copy on write filesystem http://n0x.org/copyfs/ * Copyright (C) 2004 Nicolas Vigier * Thomas Joubert * This program can be distributed under the terms of the GNU GPL. * See the file COPYING. */ #ifdef linux /* For pread()/pwrite() */ #define _XOPEN_SOURCE 500 #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "helper.h" #include "cache.h" #include "structs.h" #include "rcs.h" #include "create.h" #include "write.h" #include "ea.h" static int callback_getattr(const char *path, struct stat *st_data) { metadata_t *metadata; version_t *version; char *rpath; int res; /* Use the real file */ rpath = rcs_translate_path(path, rcs_version_path); if (!rpath) return -ENOENT; metadata = cache_get_metadata(path); version = rcs_find_version(metadata, LATEST, LATEST); res = lstat(rpath, st_data); free(rpath); if(res == -1) return -errno; /* Mix our metadata to the stat results */ st_data->st_mode = (st_data->st_mode & ~0777) | version->v_mode; st_data->st_uid = version->v_uid; st_data->st_gid = version->v_gid; return 0; } static int callback_readlink(const char *path, char *buf, size_t size) { char *rpath; int res; rpath = rcs_translate_path(path, rcs_version_path); if (!rpath) return -ENOENT; res = readlink(rpath, buf, size - 1); if (res == -1) { free(rpath); return -errno; } buf[res] = '\0'; free(rpath); return 0; } #define METADATA_PREFIX "metadata." static int callback_getdir(const char *path, fuse_dirh_t h, fuse_dirfil_t fill) { struct dirent *entry; char *rpath; int res; DIR *dir; rpath = rcs_translate_path(path, rcs_version_path); dir = opendir(rpath); if (!dir) { free(rpath); return -errno; } /* Find the metadata files */ do { entry = readdir(dir); if (entry) { /* * We want the metadata files (because a versionned file is * behind them, the '.' and '..' directories, so that the listing * looks reasonable. */ if (!strcmp(entry->d_name, "metadata.")) { /* This is the root's metadata, ignore it */ continue; } else if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) { res = fill(h, entry->d_name, 0); if (res != 0) break; } else if (!strncmp(entry->d_name, METADATA_PREFIX, strlen(METADATA_PREFIX))) { metadata_t *metadata; char *file; /* Check if the file is not currently in deleted state */ if (strcmp(path, "/")) file = helper_build_composite("SS", "/", path, entry->d_name + strlen(METADATA_PREFIX)); else file = helper_build_composite("-S", "/", entry->d_name + strlen(METADATA_PREFIX)); metadata = rcs_translate_to_metadata(file, rcs_version_path); free(file); if (metadata && !metadata->md_deleted) { res = fill(h, entry->d_name + strlen(METADATA_PREFIX), 0); if (res != 0) break; } } } } while (entry); closedir(dir); free(rpath); return 0; } static int callback_mknod(const char *path, mode_t mode, dev_t rdev) { return create_new_file(path, mode, fuse_get_context()->uid, fuse_get_context()->gid, rdev); } static int callback_mkdir(const char *path, mode_t mode) { return create_new_directory(path, mode, fuse_get_context()->uid, fuse_get_context()->gid); } static int callback_unlink(const char *path) { metadata_t *metadata; version_t *version; struct stat st_rfile; char *metafile; metadata = rcs_translate_to_metadata(path, rcs_version_path); if (!metadata || metadata->md_deleted) return -ENOENT; version = rcs_find_version(metadata, LATEST, LATEST); if (lstat(version->v_rfile, &st_rfile) == -1) return -errno; if (S_ISDIR(st_rfile.st_mode)) return -EISDIR; metadata->md_deleted = 1; metafile = create_meta_name(metadata->md_vfile, "metadata"); if (write_metadata_file(metafile, metadata) == -1) { free(metafile); return -errno; } free(metafile); return 0; } static int callback_rmdir(const char *path) { metadata_t *dir_metadata; version_t *version; struct stat st_rfile; char *metafile; DIR *dir; struct dirent *entry; char *rpath; dir_metadata = rcs_translate_to_metadata(path, rcs_version_path); if (!dir_metadata || dir_metadata->md_deleted) return -ENOENT; version = rcs_find_version(dir_metadata, LATEST, LATEST); if (lstat(version->v_rfile, &st_rfile) == -1) return -errno; if (!S_ISDIR(st_rfile.st_mode)) return -ENOTDIR; rpath = rcs_translate_path(path, rcs_version_path); dir = opendir(rpath); if (!dir) { free(rpath); return -errno; } /* Check if there is any file in the directory */ do { entry = readdir(dir); if (entry) { /* * We want the metadata files (because a versionned file is * behind them, the '.' and '..' directories, so that the listing * looks reasonable. */ if (!strcmp(entry->d_name, "metadata.")) { /* This is the root's metadata, ignore it */ continue; } else if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) { continue ; } else if (!strncmp(entry->d_name, METADATA_PREFIX, strlen(METADATA_PREFIX))) { metadata_t *metadata; char *file; /* Check if the file is not currently in deleted state */ if (strcmp(path, "/")) file = helper_build_composite("SS", "/", path, entry->d_name + strlen(METADATA_PREFIX)); else file = helper_build_composite("-S", "/", entry->d_name + strlen(METADATA_PREFIX)); metadata = rcs_translate_to_metadata(file, rcs_version_path); free(file); if (metadata && !metadata->md_deleted) { /* we found a file */ closedir(dir); free(rpath); return -ENOTEMPTY; } } } } while (entry); closedir(dir); free(rpath); dir_metadata->md_deleted = 1; metafile = create_meta_name(dir_metadata->md_vfile, "metadata"); if (write_metadata_file(metafile, dir_metadata) == -1) { free(metafile); return -errno; } free(metafile); return 0; } static int callback_symlink(const char *from, const char *to) { return create_new_symlink(from, to, fuse_get_context()->uid, fuse_get_context()->gid); } static int callback_rename(const char *from, const char *to) { /* * Not suppored, because there is always a fallback path, and we don't * version moves per se, so just let the calling program do the move * "manually". */ (void)from; (void)to; return -EXDEV; } static int callback_link(const char *from, const char *to) { /* * Forbid hard links, since there is no way to make them point to a new * version if need be. */ (void)from; (void)to; return -EPERM; } static int callback_chmod(const char *path, mode_t mode) { metadata_t *metadata; version_t *version; metadata = rcs_translate_to_metadata(path, rcs_version_path); if (!metadata) return -ENOENT; version = rcs_find_version(metadata, LATEST, LATEST); if (!version) return -ENOENT; if (create_new_subversion(path, mode, version->v_uid, version->v_gid) != 0) return -errno; return 0; } static int callback_chown(const char *path, uid_t uid, gid_t gid) { metadata_t *metadata; version_t *version; metadata = rcs_translate_to_metadata(path, rcs_version_path); if (!metadata) return -ENOENT; version = rcs_find_version(metadata, LATEST, LATEST); if (!version) return -ENOENT; if (create_new_subversion(path, version->v_mode, uid, gid) != 0) return -errno; return 0; } static int callback_truncate(const char *path, off_t size) { int res; char *rpath; metadata_t *metadata; if (create_new_version(path) == -1) return -errno; rpath = rcs_translate_path(path, rcs_version_path); metadata = cache_get_metadata(path); metadata->md_timestamp = time(NULL); res = truncate(rpath, size); if(res == -1) return -errno; return 0; } static int callback_utime(const char *path, struct utimbuf *buf) { char *rpath; int res; rpath = rcs_translate_path(path, rcs_version_path); if (!rpath) return -ENOENT; res = utime(rpath, buf); if (res == -1) { free(rpath); return -errno; } free(rpath); return 0; } static int callback_open(const char *path, int flags) { char *rpath; int res; if ((flags & O_WRONLY) || (flags & O_RDWR)) { if (create_new_version(path) == -1) return -errno; } rpath = rcs_translate_path(path, rcs_version_path); res = open(rpath, flags); free(rpath); if(res == -1) return -errno; close(res); return 0; } static int callback_read(const char *path, char *buf, size_t size, off_t off) { char *rpath; int fd, res; rpath = rcs_translate_path(path, rcs_version_path); fd = open(rpath, O_RDONLY); if (fd == -1) { free(rpath); return -errno; } res = pread(fd, buf, size, off); if (res == -1) res = -errno; close(fd); free(rpath); return res; } static int callback_write(const char *path, const char *buf, size_t size, off_t offset) { int fd; int res; char *rpath; rpath = rcs_translate_path(path, rcs_version_path); fd = open(rpath, O_WRONLY); if(fd == -1) { free(rpath); return -errno; } res = pwrite(fd, buf, size, offset); if(res == -1) res = -errno; close(fd); free(rpath); return res; } static int callback_statfs(const char *path, struct statfs *st_buf) { int res; res = statfs(path, st_buf); if (res == -1) return -errno; return 0; } static int callback_release(const char *path, int flags) { /* No special treatment */ (void) path; (void) flags; return 0; } static int callback_fsync(const char *path, int isdatasync) { /* No special treatment */ (void) path; (void) isdatasync; return 0; } struct fuse_operations callback_oper = { .getattr = callback_getattr, .readlink = callback_readlink, .getdir = callback_getdir, .mknod = callback_mknod, .mkdir = callback_mkdir, .symlink = callback_symlink, .unlink = callback_unlink, .rmdir = callback_rmdir, .rename = callback_rename, .link = callback_link, .chmod = callback_chmod, .chown = callback_chown, .truncate = callback_truncate, .utime = callback_utime, .open = callback_open, .read = callback_read, .write = callback_write, .statfs = callback_statfs, .release = callback_release, .fsync = callback_fsync, /* Extended attributes support for userland interaction */ .setxattr = callback_setxattr, .getxattr = callback_getxattr, .listxattr = callback_listxattr, .removexattr= callback_removexattr }; copyfs-1.0.1/rcs.h0000644000175000017500000000123410522107723012745 0ustar boklmboklm/* * copyfs - copy on write filesystem http://n0x.org/copyfs/ * Copyright (C) 2004 Nicolas Vigier * Thomas Joubert * This program can be distributed under the terms of the GNU GPL. * See the file COPYING. */ #ifndef RCS_H # define RCS_H # include "structs.h" extern char *rcs_version_path; extern int rcs_ignore_deleted; version_t *rcs_find_version(metadata_t *metadata, int vid, int svid); char *rcs_translate_path(const char *virtual, char *vroot); metadata_t *rcs_translate_to_metadata(const char *vfile, char *vroot); void rcs_free_metadata(metadata_t *metadata); #endif /* !RCS_H */ copyfs-1.0.1/README0000644000175000017500000001266110522104447012673 0ustar boklmboklm ,ad8888ba, 88888888888 ad88888ba d8"' `"8b 88 d8" "8b d8' 88 Y8, 88 ,adPPYba, 8b,dPPYba, 8b d8 88aaaaa `Y8aaaaa, 88 a8" "8a 88P' "8a `8b d8' 88""""" `"""""8b, Y8, 8b d8 88 d8 `8b d8' 88 `8b Y8a. .a8P "8a, ,a8" 88b, ,a8" `8b,d8' 88 Y8a a8P `"Y8888Y"' `"YbbdP"' 88`YbbdP"' Y88' 88 "Y88888P" 88 d8' 88 d8' 1.0.1 A copy-on-write, versionned filesystem By Nicolas Vigier (boklm@mars-attacks.org) and Thomas Joubert (widan@net-42.eu.org) http://n0x.org/copyfs/ CopyFS aims to solve a common problem : given a directory, especially one full of configuration files, or other files that one can modify, and which can affect the functionning of a system, or of programs, that may be important to other users (or to the user himself), how to be sure that a person modifying the files will do a backup of the working version first ? This filesystem solves the problem by making the whole process transparent, automatically keeping versionned copies of all the changes done to file under its control. It also allows a user to select an old version of the files, for example to repair a mistake, and allows him/her to continue edition from this point. CopyFS is distributed under the terms of the GNU GPL. System requirements ------------------- Filesystem in Userspace (FUSE) is required to use this filesystem. FUSE was merged into the mainstream Linux kernel tree in kernel version 2.6.14, so any version >= 2.6.14 should be fine, if the option was selected when the kernel was compiled. More informations about FUSE is available on http://fuse.sourceforge.net/ It might work on FreeBSD and OpenSolaris too, but we never tried yet. How to install -------------- Simply do : copyfs-1.0 % ./configure copyfs-1.0 % make all copyfs-1.0 % su Password: copyfs-1.0 # make install How to use ---------- To mount a CopyFS, you need to use the command 'copyfs-mount' : if you want to mount a CopyFS at '/mnt/fs', whose version directory is at /var/versions, you would use : root # copyfs-mount /var/versions /mnt/fs To unmount it, simply do : root # umount /mnt/fs As you would do for any filesystem. Accessing old versions ---------------------- To know which versions are available, you can use the copyfs-fversion tool : cpy-fs $ copyfs-fversion test File test ('*' is active) : v1.0 : -rw-r--r-- widan users 0 Fri Dec 10 14:17:47 2004 v2.0 : -rw-r--r-- widan users 21 Fri Dec 10 14:17:55 2004 v2.1 : -rwx------ widan users 21 Fri Dec 10 14:17:55 2004 v3.0 : -rwx------ widan users 29 Fri Dec 10 14:19:35 2004 [*] You know there are 4 versions, with their associated information. In that state you are viewing version 3.0 (ie the last one). If you want to see another one, you can 'lock' it : cpy-fs $ cat test version 3.0 cpy-fs $ copyfs-fversion -l 2.0 test cpy-fs $ cat test version 2.0 If you want to remove the version lock, and return to the latest version available, use : cpy-fs $ copyfs-fversion -r test Tagging files ------------- Let's say you have the following file tree : cpy-fs $ copyfs-fversion somedir/file-1 File file-1 ('*' is active) : v1.0 : -rw-r--r-- widan users 3 Fri Dec 10 14:21:41 2004 v2.0 : -rw-r--r-- widan users 3 Fri Dec 10 14:21:43 2004 [*] v3.0 : -rw-r--r-- widan users 3 Fri Dec 10 14:21:45 2004 cpy-fs $ copyfs-fversion somedir/file-2 File file-2 ('*' is active) : v1.0 : -rw-r--r-- widan users 5 Fri Dec 10 14:21:51 2004 [*] v2.0 : -rw-r--r-- widan users 5 Fri Dec 10 14:21:53 2004 cpy-fs $ copyfs-fversion somedir/file-3 File file-3 ('*' is active) : v1.0 : -rw-r--r-- widan users 5 Fri Dec 10 14:21:58 2004 v2.0 : -rw-r--r-- widan users 5 Fri Dec 10 14:22:03 2004 [*] You have : cpy-fs $ cat somedir/file-1 v2 cpy-fs $ cat somedir/file-2 2 v1 cpy-fs $ cat somedir/file-3 3 v2 Suppose you want to save all the "current" versions for the directory 'somedir', you can create a tag file with : cpy-fs $ copyfs-fversion -t tag-file somedir Then you can edit the files as you want, and if you want to put all the files back to the version they were at when you tagged them, simply do : cpy-fs $ copyfs-fversion -u tag-file somedir Example : cpy-fs $ echo "new 1" > somedir/file-1 cpy-fs $ echo "new 2" > somedir/file-2 cpy-fs $ cat somedir/file-{1,2,3} new 1 new 2 3 v2 cpy-fs $ copyfs-fversion -t tag-file-new somedir cpy-fs $ copyfs-fversion -u tag-file somedir Restored somedir/file-1 to version 2.0 Restored somedir/file-2 to version 1.0 Restored somedir/file-3 to version 2.0 cpy-fs $ cat somedir/file-{1,2,3} v2 2 v1 3 v2 cpy-fs $ copyfs-fversion -u tag-file-new somedir Restored somedir/file-1 to version 6.0 Restored somedir/file-2 to version 3.0 Restored somedir/file-3 to version 2.0 cpy-fs $ cat somedir/file-{1,2,3} new 1 new 2 3 v2 copyfs-1.0.1/configure0000755000175000017500000014447610156322720013733 0ustar boklmboklm#! /bin/sh # Guess values for system-dependent variables and create Makefiles. # Generated automatically using autoconf version 2.13 # Copyright (C) 1992, 93, 94, 95, 96 Free Software Foundation, Inc. # # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. # Defaults: ac_help= ac_default_prefix=/usr/local # Any additions from configure.in: # Initialize some variables set by options. # The variables have the same names as the options, with # dashes changed to underlines. build=NONE cache_file=./config.cache exec_prefix=NONE host=NONE no_create= nonopt=NONE no_recursion= prefix=NONE program_prefix=NONE program_suffix=NONE program_transform_name=s,x,x, silent= site= srcdir= target=NONE verbose= x_includes=NONE x_libraries=NONE bindir='${exec_prefix}/bin' sbindir='${exec_prefix}/sbin' libexecdir='${exec_prefix}/libexec' datadir='${prefix}/share' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' libdir='${exec_prefix}/lib' includedir='${prefix}/include' oldincludedir='/usr/include' infodir='${prefix}/info' mandir='${prefix}/man' # Initialize some other variables. subdirs= MFLAGS= MAKEFLAGS= SHELL=${CONFIG_SHELL-/bin/sh} # Maximum number of lines to put in a shell here document. ac_max_here_lines=12 ac_prev= for ac_option do # If the previous option needs an argument, assign it. if test -n "$ac_prev"; then eval "$ac_prev=\$ac_option" ac_prev= continue fi case "$ac_option" in -*=*) ac_optarg=`echo "$ac_option" | sed 's/[-_a-zA-Z0-9]*=//'` ;; *) ac_optarg= ;; esac # Accept the important Cygnus configure options, so we can diagnose typos. case "$ac_option" in -bindir | --bindir | --bindi | --bind | --bin | --bi) ac_prev=bindir ;; -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) bindir="$ac_optarg" ;; -build | --build | --buil | --bui | --bu) ac_prev=build ;; -build=* | --build=* | --buil=* | --bui=* | --bu=*) build="$ac_optarg" ;; -cache-file | --cache-file | --cache-fil | --cache-fi \ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) ac_prev=cache_file ;; -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) cache_file="$ac_optarg" ;; -datadir | --datadir | --datadi | --datad | --data | --dat | --da) ac_prev=datadir ;; -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \ | --da=*) datadir="$ac_optarg" ;; -disable-* | --disable-*) ac_feature=`echo $ac_option|sed -e 's/-*disable-//'` # Reject names that are not valid shell variable names. if test -n "`echo $ac_feature| sed 's/[-a-zA-Z0-9_]//g'`"; then { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; } fi ac_feature=`echo $ac_feature| sed 's/-/_/g'` eval "enable_${ac_feature}=no" ;; -enable-* | --enable-*) ac_feature=`echo $ac_option|sed -e 's/-*enable-//' -e 's/=.*//'` # Reject names that are not valid shell variable names. if test -n "`echo $ac_feature| sed 's/[-_a-zA-Z0-9]//g'`"; then { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; } fi ac_feature=`echo $ac_feature| sed 's/-/_/g'` case "$ac_option" in *=*) ;; *) ac_optarg=yes ;; esac eval "enable_${ac_feature}='$ac_optarg'" ;; -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ | --exec | --exe | --ex) ac_prev=exec_prefix ;; -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ | --exec=* | --exe=* | --ex=*) exec_prefix="$ac_optarg" ;; -gas | --gas | --ga | --g) # Obsolete; use --with-gas. with_gas=yes ;; -help | --help | --hel | --he) # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat << EOF Usage: configure [options] [host] Options: [defaults in brackets after descriptions] Configuration: --cache-file=FILE cache test results in FILE --help print this message --no-create do not create output files --quiet, --silent do not print \`checking...' messages --version print the version of autoconf that created configure Directory and file names: --prefix=PREFIX install architecture-independent files in PREFIX [$ac_default_prefix] --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX [same as prefix] --bindir=DIR user executables in DIR [EPREFIX/bin] --sbindir=DIR system admin executables in DIR [EPREFIX/sbin] --libexecdir=DIR program executables in DIR [EPREFIX/libexec] --datadir=DIR read-only architecture-independent data in DIR [PREFIX/share] --sysconfdir=DIR read-only single-machine data in DIR [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data in DIR [PREFIX/com] --localstatedir=DIR modifiable single-machine data in DIR [PREFIX/var] --libdir=DIR object code libraries in DIR [EPREFIX/lib] --includedir=DIR C header files in DIR [PREFIX/include] --oldincludedir=DIR C header files for non-gcc in DIR [/usr/include] --infodir=DIR info documentation in DIR [PREFIX/info] --mandir=DIR man documentation in DIR [PREFIX/man] --srcdir=DIR find the sources in DIR [configure dir or ..] --program-prefix=PREFIX prepend PREFIX to installed program names --program-suffix=SUFFIX append SUFFIX to installed program names --program-transform-name=PROGRAM run sed PROGRAM on installed program names EOF cat << EOF Host type: --build=BUILD configure for building on BUILD [BUILD=HOST] --host=HOST configure for HOST [guessed] --target=TARGET configure for TARGET [TARGET=HOST] Features and packages: --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) --enable-FEATURE[=ARG] include FEATURE [ARG=yes] --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) --x-includes=DIR X include files are in DIR --x-libraries=DIR X library files are in DIR EOF if test -n "$ac_help"; then echo "--enable and --with options recognized:$ac_help" fi exit 0 ;; -host | --host | --hos | --ho) ac_prev=host ;; -host=* | --host=* | --hos=* | --ho=*) host="$ac_optarg" ;; -includedir | --includedir | --includedi | --included | --include \ | --includ | --inclu | --incl | --inc) ac_prev=includedir ;; -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ | --includ=* | --inclu=* | --incl=* | --inc=*) includedir="$ac_optarg" ;; -infodir | --infodir | --infodi | --infod | --info | --inf) ac_prev=infodir ;; -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) infodir="$ac_optarg" ;; -libdir | --libdir | --libdi | --libd) ac_prev=libdir ;; -libdir=* | --libdir=* | --libdi=* | --libd=*) libdir="$ac_optarg" ;; -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ | --libexe | --libex | --libe) ac_prev=libexecdir ;; -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ | --libexe=* | --libex=* | --libe=*) libexecdir="$ac_optarg" ;; -localstatedir | --localstatedir | --localstatedi | --localstated \ | --localstate | --localstat | --localsta | --localst \ | --locals | --local | --loca | --loc | --lo) ac_prev=localstatedir ;; -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ | --localstate=* | --localstat=* | --localsta=* | --localst=* \ | --locals=* | --local=* | --loca=* | --loc=* | --lo=*) localstatedir="$ac_optarg" ;; -mandir | --mandir | --mandi | --mand | --man | --ma | --m) ac_prev=mandir ;; -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) mandir="$ac_optarg" ;; -nfp | --nfp | --nf) # Obsolete; use --without-fp. with_fp=no ;; -no-create | --no-create | --no-creat | --no-crea | --no-cre \ | --no-cr | --no-c) no_create=yes ;; -no-recursion | --no-recursion | --no-recursio | --no-recursi \ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) no_recursion=yes ;; -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ | --oldin | --oldi | --old | --ol | --o) ac_prev=oldincludedir ;; -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) oldincludedir="$ac_optarg" ;; -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) ac_prev=prefix ;; -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) prefix="$ac_optarg" ;; -program-prefix | --program-prefix | --program-prefi | --program-pref \ | --program-pre | --program-pr | --program-p) ac_prev=program_prefix ;; -program-prefix=* | --program-prefix=* | --program-prefi=* \ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) program_prefix="$ac_optarg" ;; -program-suffix | --program-suffix | --program-suffi | --program-suff \ | --program-suf | --program-su | --program-s) ac_prev=program_suffix ;; -program-suffix=* | --program-suffix=* | --program-suffi=* \ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) program_suffix="$ac_optarg" ;; -program-transform-name | --program-transform-name \ | --program-transform-nam | --program-transform-na \ | --program-transform-n | --program-transform- \ | --program-transform | --program-transfor \ | --program-transfo | --program-transf \ | --program-trans | --program-tran \ | --progr-tra | --program-tr | --program-t) ac_prev=program_transform_name ;; -program-transform-name=* | --program-transform-name=* \ | --program-transform-nam=* | --program-transform-na=* \ | --program-transform-n=* | --program-transform-=* \ | --program-transform=* | --program-transfor=* \ | --program-transfo=* | --program-transf=* \ | --program-trans=* | --program-tran=* \ | --progr-tra=* | --program-tr=* | --program-t=*) program_transform_name="$ac_optarg" ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) silent=yes ;; -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ | --sbi=* | --sb=*) sbindir="$ac_optarg" ;; -sharedstatedir | --sharedstatedir | --sharedstatedi \ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ | --sharedst | --shareds | --shared | --share | --shar \ | --sha | --sh) ac_prev=sharedstatedir ;; -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ | --sha=* | --sh=*) sharedstatedir="$ac_optarg" ;; -site | --site | --sit) ac_prev=site ;; -site=* | --site=* | --sit=*) site="$ac_optarg" ;; -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) ac_prev=srcdir ;; -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) srcdir="$ac_optarg" ;; -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ | --syscon | --sysco | --sysc | --sys | --sy) ac_prev=sysconfdir ;; -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) sysconfdir="$ac_optarg" ;; -target | --target | --targe | --targ | --tar | --ta | --t) ac_prev=target ;; -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) target="$ac_optarg" ;; -v | -verbose | --verbose | --verbos | --verbo | --verb) verbose=yes ;; -version | --version | --versio | --versi | --vers) echo "configure generated by autoconf version 2.13" exit 0 ;; -with-* | --with-*) ac_package=`echo $ac_option|sed -e 's/-*with-//' -e 's/=.*//'` # Reject names that are not valid shell variable names. if test -n "`echo $ac_package| sed 's/[-_a-zA-Z0-9]//g'`"; then { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; } fi ac_package=`echo $ac_package| sed 's/-/_/g'` case "$ac_option" in *=*) ;; *) ac_optarg=yes ;; esac eval "with_${ac_package}='$ac_optarg'" ;; -without-* | --without-*) ac_package=`echo $ac_option|sed -e 's/-*without-//'` # Reject names that are not valid shell variable names. if test -n "`echo $ac_package| sed 's/[-a-zA-Z0-9_]//g'`"; then { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; } fi ac_package=`echo $ac_package| sed 's/-/_/g'` eval "with_${ac_package}=no" ;; --x) # Obsolete; use --with-x. with_x=yes ;; -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ | --x-incl | --x-inc | --x-in | --x-i) ac_prev=x_includes ;; -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) x_includes="$ac_optarg" ;; -x-libraries | --x-libraries | --x-librarie | --x-librari \ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) ac_prev=x_libraries ;; -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) x_libraries="$ac_optarg" ;; -*) { echo "configure: error: $ac_option: invalid option; use --help to show usage" 1>&2; exit 1; } ;; *) if test -n "`echo $ac_option| sed 's/[-a-z0-9.]//g'`"; then echo "configure: warning: $ac_option: invalid host type" 1>&2 fi if test "x$nonopt" != xNONE; then { echo "configure: error: can only configure for one host and one target at a time" 1>&2; exit 1; } fi nonopt="$ac_option" ;; esac done if test -n "$ac_prev"; then { echo "configure: error: missing argument to --`echo $ac_prev | sed 's/_/-/g'`" 1>&2; exit 1; } fi trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15 # File descriptor usage: # 0 standard input # 1 file creation # 2 errors and warnings # 3 some systems may open it to /dev/tty # 4 used on the Kubota Titan # 6 checking for... messages and results # 5 compiler messages saved in config.log if test "$silent" = yes; then exec 6>/dev/null else exec 6>&1 fi exec 5>./config.log echo "\ This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. " 1>&5 # Strip out --no-create and --no-recursion so they do not pile up. # Also quote any args containing shell metacharacters. ac_configure_args= for ac_arg do case "$ac_arg" in -no-create | --no-create | --no-creat | --no-crea | --no-cre \ | --no-cr | --no-c) ;; -no-recursion | --no-recursion | --no-recursio | --no-recursi \ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) ;; *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?]*) ac_configure_args="$ac_configure_args '$ac_arg'" ;; *) ac_configure_args="$ac_configure_args $ac_arg" ;; esac done # NLS nuisances. # Only set these to C if already set. These must not be set unconditionally # because not all systems understand e.g. LANG=C (notably SCO). # Fixing LC_MESSAGES prevents Solaris sh from translating var values in `set'! # Non-C LC_CTYPE values break the ctype check. if test "${LANG+set}" = set; then LANG=C; export LANG; fi if test "${LC_ALL+set}" = set; then LC_ALL=C; export LC_ALL; fi if test "${LC_MESSAGES+set}" = set; then LC_MESSAGES=C; export LC_MESSAGES; fi if test "${LC_CTYPE+set}" = set; then LC_CTYPE=C; export LC_CTYPE; fi # confdefs.h avoids OS command line length limits that DEFS can exceed. rm -rf conftest* confdefs.h # AIX cpp loses on an empty file, so make sure it contains at least a newline. echo > confdefs.h # A filename unique to this package, relative to the directory that # configure is in, which we can look for to find out if srcdir is correct. ac_unique_file=ea.c # Find the source files, if location was not specified. if test -z "$srcdir"; then ac_srcdir_defaulted=yes # Try the directory containing this script, then its parent. ac_prog=$0 ac_confdir=`echo $ac_prog|sed 's%/[^/][^/]*$%%'` test "x$ac_confdir" = "x$ac_prog" && ac_confdir=. srcdir=$ac_confdir if test ! -r $srcdir/$ac_unique_file; then srcdir=.. fi else ac_srcdir_defaulted=no fi if test ! -r $srcdir/$ac_unique_file; then if test "$ac_srcdir_defaulted" = yes; then { echo "configure: error: can not find sources in $ac_confdir or .." 1>&2; exit 1; } else { echo "configure: error: can not find sources in $srcdir" 1>&2; exit 1; } fi fi srcdir=`echo "${srcdir}" | sed 's%\([^/]\)/*$%\1%'` # Prefer explicitly selected file to automatically selected ones. if test -z "$CONFIG_SITE"; then if test "x$prefix" != xNONE; then CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site" else CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" fi fi for ac_site_file in $CONFIG_SITE; do if test -r "$ac_site_file"; then echo "loading site script $ac_site_file" . "$ac_site_file" fi done if test -r "$cache_file"; then echo "loading cache $cache_file" . $cache_file else echo "creating cache $cache_file" > $cache_file fi ac_ext=c # CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. ac_cpp='$CPP $CPPFLAGS' ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' cross_compiling=$ac_cv_prog_cc_cross ac_exeext= ac_objext=o if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null; then # Stardent Vistra SVR4 grep lacks -e, says ghazi@caip.rutgers.edu. if (echo -n testing; echo 1,2,3) | sed s/-n/xn/ | grep xn >/dev/null; then ac_n= ac_c=' ' ac_t=' ' else ac_n=-n ac_c= ac_t= fi else ac_n= ac_c='\c' ac_t= fi # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 echo "configure:529: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" ac_dummy="$PATH" for ac_dir in $ac_dummy; do test -z "$ac_dir" && ac_dir=. if test -f $ac_dir/$ac_word; then ac_cv_prog_CC="gcc" break fi done IFS="$ac_save_ifs" fi fi CC="$ac_cv_prog_CC" if test -n "$CC"; then echo "$ac_t""$CC" 1>&6 else echo "$ac_t""no" 1>&6 fi if test -z "$CC"; then # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 echo "configure:559: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" ac_prog_rejected=no ac_dummy="$PATH" for ac_dir in $ac_dummy; do test -z "$ac_dir" && ac_dir=. if test -f $ac_dir/$ac_word; then if test "$ac_dir/$ac_word" = "/usr/ucb/cc"; then ac_prog_rejected=yes continue fi ac_cv_prog_CC="cc" break fi done IFS="$ac_save_ifs" if test $ac_prog_rejected = yes; then # We found a bogon in the path, so make sure we never use it. set dummy $ac_cv_prog_CC shift if test $# -gt 0; then # We chose a different compiler from the bogus one. # However, it has the same basename, so the bogon will be chosen # first if we set CC to just the basename; use the full file name. shift set dummy "$ac_dir/$ac_word" "$@" shift ac_cv_prog_CC="$@" fi fi fi fi CC="$ac_cv_prog_CC" if test -n "$CC"; then echo "$ac_t""$CC" 1>&6 else echo "$ac_t""no" 1>&6 fi if test -z "$CC"; then case "`uname -s`" in *win32* | *WIN32*) # Extract the first word of "cl", so it can be a program name with args. set dummy cl; ac_word=$2 echo $ac_n "checking for $ac_word""... $ac_c" 1>&6 echo "configure:610: checking for $ac_word" >&5 if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":" ac_dummy="$PATH" for ac_dir in $ac_dummy; do test -z "$ac_dir" && ac_dir=. if test -f $ac_dir/$ac_word; then ac_cv_prog_CC="cl" break fi done IFS="$ac_save_ifs" fi fi CC="$ac_cv_prog_CC" if test -n "$CC"; then echo "$ac_t""$CC" 1>&6 else echo "$ac_t""no" 1>&6 fi ;; esac fi test -z "$CC" && { echo "configure: error: no acceptable cc found in \$PATH" 1>&2; exit 1; } fi echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works""... $ac_c" 1>&6 echo "configure:642: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) works" >&5 ac_ext=c # CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. ac_cpp='$CPP $CPPFLAGS' ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' cross_compiling=$ac_cv_prog_cc_cross cat > conftest.$ac_ext << EOF #line 653 "configure" #include "confdefs.h" main(){return(0);} EOF if { (eval echo configure:658: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then ac_cv_prog_cc_works=yes # If we can't run a trivial program, we are probably using a cross compiler. if (./conftest; exit) 2>/dev/null; then ac_cv_prog_cc_cross=no else ac_cv_prog_cc_cross=yes fi else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 ac_cv_prog_cc_works=no fi rm -fr conftest* ac_ext=c # CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options. ac_cpp='$CPP $CPPFLAGS' ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5' ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5' cross_compiling=$ac_cv_prog_cc_cross echo "$ac_t""$ac_cv_prog_cc_works" 1>&6 if test $ac_cv_prog_cc_works = no; then { echo "configure: error: installation or configuration problem: C compiler cannot create executables." 1>&2; exit 1; } fi echo $ac_n "checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler""... $ac_c" 1>&6 echo "configure:684: checking whether the C compiler ($CC $CFLAGS $LDFLAGS) is a cross-compiler" >&5 echo "$ac_t""$ac_cv_prog_cc_cross" 1>&6 cross_compiling=$ac_cv_prog_cc_cross echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&6 echo "configure:689: checking whether we are using GNU C" >&5 if eval "test \"`echo '$''{'ac_cv_prog_gcc'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.c <&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then ac_cv_prog_gcc=yes else ac_cv_prog_gcc=no fi fi echo "$ac_t""$ac_cv_prog_gcc" 1>&6 if test $ac_cv_prog_gcc = yes; then GCC=yes else GCC= fi ac_test_CFLAGS="${CFLAGS+set}" ac_save_CFLAGS="$CFLAGS" CFLAGS= echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&6 echo "configure:717: checking whether ${CC-cc} accepts -g" >&5 if eval "test \"`echo '$''{'ac_cv_prog_cc_g'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else echo 'void f(){}' > conftest.c if test -z "`${CC-cc} -g -c conftest.c 2>&1`"; then ac_cv_prog_cc_g=yes else ac_cv_prog_cc_g=no fi rm -f conftest* fi echo "$ac_t""$ac_cv_prog_cc_g" 1>&6 if test "$ac_test_CFLAGS" = set; then CFLAGS="$ac_save_CFLAGS" elif test $ac_cv_prog_cc_g = yes; then if test "$GCC" = yes; then CFLAGS="-g -O2" else CFLAGS="-g" fi else if test "$GCC" = yes; then CFLAGS="-O2" else CFLAGS= fi fi echo $ac_n "checking for fuse_main in -lfuse""... $ac_c" 1>&6 echo "configure:750: checking for fuse_main in -lfuse" >&5 ac_lib_var=`echo fuse'_'fuse_main | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else ac_save_LIBS="$LIBS" LIBS="-lfuse $LIBS" cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=no" fi rm -f conftest* LIBS="$ac_save_LIBS" fi if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then echo "$ac_t""yes" 1>&6 ac_tr_lib=HAVE_LIB`echo fuse | sed -e 's/[^a-zA-Z0-9_]/_/g' \ -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'` cat >> confdefs.h <&6 fi ac_header_dirent=no for ac_hdr in dirent.h sys/ndir.h sys/dir.h ndir.h do ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` echo $ac_n "checking for $ac_hdr that defines DIR""... $ac_c" 1>&6 echo "configure:802: checking for $ac_hdr that defines DIR" >&5 if eval "test \"`echo '$''{'ac_cv_header_dirent_$ac_safe'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < #include <$ac_hdr> int main() { DIR *dirp = 0; ; return 0; } EOF if { (eval echo configure:815: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* eval "ac_cv_header_dirent_$ac_safe=yes" else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_header_dirent_$ac_safe=no" fi rm -f conftest* fi if eval "test \"`echo '$ac_cv_header_dirent_'$ac_safe`\" = yes"; then echo "$ac_t""yes" 1>&6 ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'` cat >> confdefs.h <&6 fi done # Two versions of opendir et al. are in -ldir and -lx on SCO Xenix. if test $ac_header_dirent = dirent.h; then echo $ac_n "checking for opendir in -ldir""... $ac_c" 1>&6 echo "configure:840: checking for opendir in -ldir" >&5 ac_lib_var=`echo dir'_'opendir | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else ac_save_LIBS="$LIBS" LIBS="-ldir $LIBS" cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=no" fi rm -f conftest* LIBS="$ac_save_LIBS" fi if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then echo "$ac_t""yes" 1>&6 LIBS="$LIBS -ldir" else echo "$ac_t""no" 1>&6 fi else echo $ac_n "checking for opendir in -lx""... $ac_c" 1>&6 echo "configure:881: checking for opendir in -lx" >&5 ac_lib_var=`echo x'_'opendir | sed 'y%./+-%__p_%'` if eval "test \"`echo '$''{'ac_cv_lib_$ac_lib_var'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else ac_save_LIBS="$LIBS" LIBS="-lx $LIBS" cat > conftest.$ac_ext <&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=yes" else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_lib_$ac_lib_var=no" fi rm -f conftest* LIBS="$ac_save_LIBS" fi if eval "test \"`echo '$ac_cv_lib_'$ac_lib_var`\" = yes"; then echo "$ac_t""yes" 1>&6 LIBS="$LIBS -lx" else echo "$ac_t""no" 1>&6 fi fi echo $ac_n "checking how to run the C preprocessor""... $ac_c" 1>&6 echo "configure:923: checking how to run the C preprocessor" >&5 # On Suns, sometimes $CPP names a directory. if test -n "$CPP" && test -d "$CPP"; then CPP= fi if test -z "$CPP"; then if eval "test \"`echo '$''{'ac_cv_prog_CPP'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else # This must be in double quotes, not single quotes, because CPP may get # substituted into the Makefile and "${CC-cc}" will confuse make. CPP="${CC-cc} -E" # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. cat > conftest.$ac_ext < Syntax Error EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" { (eval echo configure:944: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` if test -z "$ac_err"; then : else echo "$ac_err" >&5 echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* CPP="${CC-cc} -E -traditional-cpp" cat > conftest.$ac_ext < Syntax Error EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" { (eval echo configure:961: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` if test -z "$ac_err"; then : else echo "$ac_err" >&5 echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* CPP="${CC-cc} -nologo -E" cat > conftest.$ac_ext < Syntax Error EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" { (eval echo configure:978: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` if test -z "$ac_err"; then : else echo "$ac_err" >&5 echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* CPP=/lib/cpp fi rm -f conftest* fi rm -f conftest* fi rm -f conftest* ac_cv_prog_CPP="$CPP" fi CPP="$ac_cv_prog_CPP" else ac_cv_prog_CPP="$CPP" fi echo "$ac_t""$CPP" 1>&6 echo $ac_n "checking for ANSI C header files""... $ac_c" 1>&6 echo "configure:1003: checking for ANSI C header files" >&5 if eval "test \"`echo '$''{'ac_cv_header_stdc'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < #include #include #include EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" { (eval echo configure:1016: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` if test -z "$ac_err"; then rm -rf conftest* ac_cv_header_stdc=yes else echo "$ac_err" >&5 echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* ac_cv_header_stdc=no fi rm -f conftest* if test $ac_cv_header_stdc = yes; then # SunOS 4.x string.h does not declare mem*, contrary to ANSI. cat > conftest.$ac_ext < EOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | egrep "memchr" >/dev/null 2>&1; then : else rm -rf conftest* ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. cat > conftest.$ac_ext < EOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | egrep "free" >/dev/null 2>&1; then : else rm -rf conftest* ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. if test "$cross_compiling" = yes; then : else cat > conftest.$ac_ext < #define ISLOWER(c) ('a' <= (c) && (c) <= 'z') #define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) #define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) int main () { int i; for (i = 0; i < 256; i++) if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) exit(2); exit (0); } EOF if { (eval echo configure:1083: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null then : else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -fr conftest* ac_cv_header_stdc=no fi rm -fr conftest* fi fi fi echo "$ac_t""$ac_cv_header_stdc" 1>&6 if test $ac_cv_header_stdc = yes; then cat >> confdefs.h <<\EOF #define STDC_HEADERS 1 EOF fi for ac_hdr in fcntl.h strings.h sys/time.h unistd.h attr/xattr.h do ac_safe=`echo "$ac_hdr" | sed 'y%./+-%__p_%'` echo $ac_n "checking for $ac_hdr""... $ac_c" 1>&6 echo "configure:1110: checking for $ac_hdr" >&5 if eval "test \"`echo '$''{'ac_cv_header_$ac_safe'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < EOF ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out" { (eval echo configure:1120: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; } ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"` if test -z "$ac_err"; then rm -rf conftest* eval "ac_cv_header_$ac_safe=yes" else echo "$ac_err" >&5 echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_header_$ac_safe=no" fi rm -f conftest* fi if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then echo "$ac_t""yes" 1>&6 ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'` cat >> confdefs.h <&6 fi done echo $ac_n "checking for working const""... $ac_c" 1>&6 echo "configure:1148: checking for working const" >&5 if eval "test \"`echo '$''{'ac_cv_c_const'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext <j = 5; } { /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */ const int foo = 10; } ; return 0; } EOF if { (eval echo configure:1202: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then rm -rf conftest* ac_cv_c_const=yes else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* ac_cv_c_const=no fi rm -f conftest* fi echo "$ac_t""$ac_cv_c_const" 1>&6 if test $ac_cv_c_const = no; then cat >> confdefs.h <<\EOF #define const EOF fi echo $ac_n "checking for uid_t in sys/types.h""... $ac_c" 1>&6 echo "configure:1223: checking for uid_t in sys/types.h" >&5 if eval "test \"`echo '$''{'ac_cv_type_uid_t'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < EOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | egrep "uid_t" >/dev/null 2>&1; then rm -rf conftest* ac_cv_type_uid_t=yes else rm -rf conftest* ac_cv_type_uid_t=no fi rm -f conftest* fi echo "$ac_t""$ac_cv_type_uid_t" 1>&6 if test $ac_cv_type_uid_t = no; then cat >> confdefs.h <<\EOF #define uid_t int EOF cat >> confdefs.h <<\EOF #define gid_t int EOF fi echo $ac_n "checking for mode_t""... $ac_c" 1>&6 echo "configure:1257: checking for mode_t" >&5 if eval "test \"`echo '$''{'ac_cv_type_mode_t'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < #if STDC_HEADERS #include #include #endif EOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | egrep "(^|[^a-zA-Z_0-9])mode_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then rm -rf conftest* ac_cv_type_mode_t=yes else rm -rf conftest* ac_cv_type_mode_t=no fi rm -f conftest* fi echo "$ac_t""$ac_cv_type_mode_t" 1>&6 if test $ac_cv_type_mode_t = no; then cat >> confdefs.h <<\EOF #define mode_t int EOF fi echo $ac_n "checking for off_t""... $ac_c" 1>&6 echo "configure:1290: checking for off_t" >&5 if eval "test \"`echo '$''{'ac_cv_type_off_t'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < #if STDC_HEADERS #include #include #endif EOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | egrep "(^|[^a-zA-Z_0-9])off_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then rm -rf conftest* ac_cv_type_off_t=yes else rm -rf conftest* ac_cv_type_off_t=no fi rm -f conftest* fi echo "$ac_t""$ac_cv_type_off_t" 1>&6 if test $ac_cv_type_off_t = no; then cat >> confdefs.h <<\EOF #define off_t long EOF fi echo $ac_n "checking for size_t""... $ac_c" 1>&6 echo "configure:1323: checking for size_t" >&5 if eval "test \"`echo '$''{'ac_cv_type_size_t'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < #if STDC_HEADERS #include #include #endif EOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | egrep "(^|[^a-zA-Z_0-9])size_t[^a-zA-Z_0-9]" >/dev/null 2>&1; then rm -rf conftest* ac_cv_type_size_t=yes else rm -rf conftest* ac_cv_type_size_t=no fi rm -f conftest* fi echo "$ac_t""$ac_cv_type_size_t" 1>&6 if test $ac_cv_type_size_t = no; then cat >> confdefs.h <<\EOF #define size_t unsigned EOF fi echo $ac_n "checking whether utime accepts a null argument""... $ac_c" 1>&6 echo "configure:1357: checking whether utime accepts a null argument" >&5 if eval "test \"`echo '$''{'ac_cv_func_utime_null'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else rm -f conftestdata; > conftestdata # Sequent interprets utime(file, 0) to mean use start of epoch. Wrong. if test "$cross_compiling" = yes; then ac_cv_func_utime_null=no else cat > conftest.$ac_ext < #include main() { struct stat s, t; exit(!(stat ("conftestdata", &s) == 0 && utime("conftestdata", (long *)0) == 0 && stat("conftestdata", &t) == 0 && t.st_mtime >= s.st_mtime && t.st_mtime - s.st_mtime < 120)); } EOF if { (eval echo configure:1378: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null then ac_cv_func_utime_null=yes else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -fr conftest* ac_cv_func_utime_null=no fi rm -fr conftest* fi rm -f core core.* *.core fi echo "$ac_t""$ac_cv_func_utime_null" 1>&6 if test $ac_cv_func_utime_null = yes; then cat >> confdefs.h <<\EOF #define HAVE_UTIME_NULL 1 EOF fi for ac_func in mkdir strdup lsetxattr lgetxattr llistxattr do echo $ac_n "checking for $ac_func""... $ac_c" 1>&6 echo "configure:1404: checking for $ac_func" >&5 if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then echo $ac_n "(cached) $ac_c" 1>&6 else cat > conftest.$ac_ext < /* Override any gcc2 internal prototype to avoid an error. */ /* We use char because int might match the return type of a gcc2 builtin and then its argument prototype would still apply. */ char $ac_func(); int main() { /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined (__stub_$ac_func) || defined (__stub___$ac_func) choke me #else $ac_func(); #endif ; return 0; } EOF if { (eval echo configure:1432: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then rm -rf conftest* eval "ac_cv_func_$ac_func=yes" else echo "configure: failed program was:" >&5 cat conftest.$ac_ext >&5 rm -rf conftest* eval "ac_cv_func_$ac_func=no" fi rm -f conftest* fi if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then echo "$ac_t""yes" 1>&6 ac_tr_func=HAVE_`echo $ac_func | tr 'abcdefghijklmnopqrstuvwxyz' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'` cat >> confdefs.h <&6 fi done trap '' 1 2 15 cat > confcache <<\EOF # This file is a shell script that caches the results of configure # tests run on this system so they can be shared between configure # scripts and configure runs. It is not useful on other systems. # If it contains results you don't want to keep, you may remove or edit it. # # By default, configure uses ./config.cache as the cache file, # creating it if it does not exist already. You can give configure # the --cache-file=FILE option to use a different cache file; that is # what configure does when it calls configure scripts in # subdirectories, so they share the cache. # Giving --cache-file=/dev/null disables caching, for debugging configure. # config.status only pays attention to the cache file if you give it the # --recheck option to rerun configure. # EOF # The following way of writing the cache mishandles newlines in values, # but we know of no workaround that is simple, portable, and efficient. # So, don't put newlines in cache variables' values. # Ultrix sh set writes to stderr and can't be redirected directly, # and sets the high bit in the cache file unless we assign to the vars. (set) 2>&1 | case `(ac_space=' '; set | grep ac_space) 2>&1` in *ac_space=\ *) # `set' does not quote correctly, so add quotes (double-quote substitution # turns \\\\ into \\, and sed turns \\ into \). sed -n \ -e "s/'/'\\\\''/g" \ -e "s/^\\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\\)=\\(.*\\)/\\1=\${\\1='\\2'}/p" ;; *) # `set' quotes correctly as required by POSIX, so do not add quotes. sed -n -e 's/^\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\)=\(.*\)/\1=${\1=\2}/p' ;; esac >> confcache if cmp -s $cache_file confcache; then : else if test -w $cache_file; then echo "updating cache $cache_file" cat confcache > $cache_file else echo "not updating unwritable cache $cache_file" fi fi rm -f confcache trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15 test "x$prefix" = xNONE && prefix=$ac_default_prefix # Let make expand exec_prefix. test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' # Any assignment to VPATH causes Sun make to only execute # the first set of double-colon rules, so remove it if not needed. # If there is a colon in the path, we need to keep it. if test "x$srcdir" = x.; then ac_vpsub='/^[ ]*VPATH[ ]*=[^:]*$/d' fi trap 'rm -f $CONFIG_STATUS conftest*; exit 1' 1 2 15 # Transform confdefs.h into DEFS. # Protect against shell expansion while executing Makefile rules. # Protect against Makefile macro expansion. cat > conftest.defs <<\EOF s%#define \([A-Za-z_][A-Za-z0-9_]*\) *\(.*\)%-D\1=\2%g s%[ `~#$^&*(){}\\|;'"<>?]%\\&%g s%\[%\\&%g s%\]%\\&%g s%\$%$$%g EOF DEFS=`sed -f conftest.defs confdefs.h | tr '\012' ' '` rm -f conftest.defs # Without the "./", some shells look in PATH for config.status. : ${CONFIG_STATUS=./config.status} echo creating $CONFIG_STATUS rm -f $CONFIG_STATUS cat > $CONFIG_STATUS </dev/null | sed 1q`: # # $0 $ac_configure_args # # Compiler output produced by configure, useful for debugging # configure, is in ./config.log if it exists. ac_cs_usage="Usage: $CONFIG_STATUS [--recheck] [--version] [--help]" for ac_option do case "\$ac_option" in -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) echo "running \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion" exec \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion ;; -version | --version | --versio | --versi | --vers | --ver | --ve | --v) echo "$CONFIG_STATUS generated by autoconf version 2.13" exit 0 ;; -help | --help | --hel | --he | --h) echo "\$ac_cs_usage"; exit 0 ;; *) echo "\$ac_cs_usage"; exit 1 ;; esac done ac_given_srcdir=$srcdir trap 'rm -fr `echo "Makefile" | sed "s/:[^ ]*//g"` conftest*; exit 1' 1 2 15 EOF cat >> $CONFIG_STATUS < conftest.subs <<\\CEOF $ac_vpsub $extrasub s%@SHELL@%$SHELL%g s%@CFLAGS@%$CFLAGS%g s%@CPPFLAGS@%$CPPFLAGS%g s%@CXXFLAGS@%$CXXFLAGS%g s%@FFLAGS@%$FFLAGS%g s%@DEFS@%$DEFS%g s%@LDFLAGS@%$LDFLAGS%g s%@LIBS@%$LIBS%g s%@exec_prefix@%$exec_prefix%g s%@prefix@%$prefix%g s%@program_transform_name@%$program_transform_name%g s%@bindir@%$bindir%g s%@sbindir@%$sbindir%g s%@libexecdir@%$libexecdir%g s%@datadir@%$datadir%g s%@sysconfdir@%$sysconfdir%g s%@sharedstatedir@%$sharedstatedir%g s%@localstatedir@%$localstatedir%g s%@libdir@%$libdir%g s%@includedir@%$includedir%g s%@oldincludedir@%$oldincludedir%g s%@infodir@%$infodir%g s%@mandir@%$mandir%g s%@CC@%$CC%g s%@CPP@%$CPP%g CEOF EOF cat >> $CONFIG_STATUS <<\EOF # Split the substitutions into bite-sized pieces for seds with # small command number limits, like on Digital OSF/1 and HP-UX. ac_max_sed_cmds=90 # Maximum number of lines to put in a sed script. ac_file=1 # Number of current file. ac_beg=1 # First line for current file. ac_end=$ac_max_sed_cmds # Line after last line for current file. ac_more_lines=: ac_sed_cmds="" while $ac_more_lines; do if test $ac_beg -gt 1; then sed "1,${ac_beg}d; ${ac_end}q" conftest.subs > conftest.s$ac_file else sed "${ac_end}q" conftest.subs > conftest.s$ac_file fi if test ! -s conftest.s$ac_file; then ac_more_lines=false rm -f conftest.s$ac_file else if test -z "$ac_sed_cmds"; then ac_sed_cmds="sed -f conftest.s$ac_file" else ac_sed_cmds="$ac_sed_cmds | sed -f conftest.s$ac_file" fi ac_file=`expr $ac_file + 1` ac_beg=$ac_end ac_end=`expr $ac_end + $ac_max_sed_cmds` fi done if test -z "$ac_sed_cmds"; then ac_sed_cmds=cat fi EOF cat >> $CONFIG_STATUS <> $CONFIG_STATUS <<\EOF for ac_file in .. $CONFIG_FILES; do if test "x$ac_file" != x..; then # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in". case "$ac_file" in *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'` ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;; *) ac_file_in="${ac_file}.in" ;; esac # Adjust a relative srcdir, top_srcdir, and INSTALL for subdirectories. # Remove last slash and all that follows it. Not all systems have dirname. ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'` if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then # The file is in a subdirectory. test ! -d "$ac_dir" && mkdir "$ac_dir" ac_dir_suffix="/`echo $ac_dir|sed 's%^\./%%'`" # A "../" for each directory in $ac_dir_suffix. ac_dots=`echo $ac_dir_suffix|sed 's%/[^/]*%../%g'` else ac_dir_suffix= ac_dots= fi case "$ac_given_srcdir" in .) srcdir=. if test -z "$ac_dots"; then top_srcdir=. else top_srcdir=`echo $ac_dots|sed 's%/$%%'`; fi ;; /*) srcdir="$ac_given_srcdir$ac_dir_suffix"; top_srcdir="$ac_given_srcdir" ;; *) # Relative path. srcdir="$ac_dots$ac_given_srcdir$ac_dir_suffix" top_srcdir="$ac_dots$ac_given_srcdir" ;; esac echo creating "$ac_file" rm -f "$ac_file" configure_input="Generated automatically from `echo $ac_file_in|sed 's%.*/%%'` by configure." case "$ac_file" in *Makefile*) ac_comsub="1i\\ # $configure_input" ;; *) ac_comsub= ;; esac ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"` sed -e "$ac_comsub s%@configure_input@%$configure_input%g s%@srcdir@%$srcdir%g s%@top_srcdir@%$top_srcdir%g " $ac_file_inputs | (eval "$ac_sed_cmds") > $ac_file fi; done rm -f conftest.s* EOF cat >> $CONFIG_STATUS <> $CONFIG_STATUS <<\EOF exit 0 EOF chmod +x $CONFIG_STATUS rm -fr confdefs* $ac_clean_files test "$no_create" = yes || ${CONFIG_SHELL-/bin/sh} $CONFIG_STATUS || exit 1 copyfs-1.0.1/structs.h0000644000175000017500000000257710522107733013701 0ustar boklmboklm/* * copyfs - copy on write filesystem http://n0x.org/copyfs/ * Copyright (C) 2004 Nicolas Vigier * Thomas Joubert * This program can be distributed under the terms of the GNU GPL. * See the file COPYING. */ #ifndef STRUCTS_H # define STRUCTS_H # include # include # define LATEST -1 /* Data types */ typedef struct version_t version_t; typedef struct metadata_t metadata_t; typedef struct bucket_t bucket_t; struct version_t { unsigned int v_vid; /* Version ID */ unsigned int v_svid; /* Subversion ID */ mode_t v_mode; /* File permissions */ uid_t v_uid; /* Owner */ gid_t v_gid; /* Group */ char *v_rfile; /* Real file name */ version_t *v_next; /* Next version */ }; struct metadata_t { char *md_vfile; /* Virtual file name */ char **md_vpath; /* Virtual path */ version_t *md_versions; /* List of versions */ int md_deleted; /* File deleted ? */ int md_dfl_vid; /* Default version */ int md_dfl_svid; /* Default subversion */ time_t md_timestamp; /* Mod. begin */ metadata_t *md_next; /* Next file in bucket */ metadata_t *md_previous; /* Previous " */ }; struct bucket_t { unsigned int b_count; /* Item count */ metadata_t *b_contents; /* Metadata chain */ }; #endif /* !STRUCTS_H */ copyfs-1.0.1/configure.in0000644000175000017500000000110210156322720014307 0ustar boklmboklmdnl Process this file with autoconf to produce a configure script. AC_INIT(ea.c) dnl Checks for programs. AC_PROG_CC dnl Checks for libraries. AC_CHECK_LIB(fuse, fuse_main) dnl Checks for header files. AC_HEADER_DIRENT AC_HEADER_STDC AC_CHECK_HEADERS(fcntl.h strings.h sys/time.h unistd.h attr/xattr.h) dnl Checks for typedefs, structures, and compiler characteristics. AC_C_CONST AC_TYPE_UID_T AC_TYPE_MODE_T AC_TYPE_OFF_T AC_TYPE_SIZE_T dnl Checks for library functions. AC_FUNC_UTIME_NULL AC_CHECK_FUNCS(mkdir strdup lsetxattr lgetxattr llistxattr) AC_OUTPUT(Makefile) copyfs-1.0.1/Makefile.in0000644000175000017500000000332510522105065014052 0ustar boklmboklmexec_prefix=@exec_prefix@ bindir=@bindir@ mandir=@mandir@ TARGET = copyfs-daemon SRC = cache.c \ create.c \ ea.c \ helper.c \ interface.c \ lookup.c \ main.c \ parse.c \ write.c HEADERS = cache.h \ create.h \ ea.h \ helper.h \ parse.h \ rcs.h \ structs.h \ write.h SCRIPTS = copyfs-mount copyfs-fversion EXTRA = $(SCRIPTS) Makefile.in configure.in configure README MANPAGES= copyfs.1 copyfs-daemon.1 copyfs-mount.1 copyfs-fversion.1 OBJ = $(SRC:.c=.o) CC = gcc CFLAGS = -Wall -ansi -W -std=c99 -g -ggdb -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 LIBS = -lfuse all: $(TARGET) install: $(TARGET) $(SCRIPTS) install -d $(bindir) install -m 755 $(TARGET) $(bindir) install -m 755 $(SCRIPTS) $(bindir) install -d $(mandir)/man1 install -m 644 $(MANPAGES) $(mandir)/man1 clean: rm -f *~ $(OBJ) \#*\# distclean: clean rm -f $(TARGET) dist: mkdir /tmp/copyfs-dist mkdir /tmp/copyfs-dist/copyfs-1.0 cp $(SRC) $(HEADERS) $(EXTRA) /tmp/copyfs-dist/copyfs-1.0 cd /tmp/copyfs-dist && tar jcvf copyfs-1.0.tar.bz2 copyfs-1.0 cp /tmp/copyfs-dist/copyfs-1.0.tar.bz2 . rm -rf /tmp/copyfs-dist $(TARGET): $(OBJ) gcc -o $(TARGET) $(OBJ) $(LIBS) # Dependencies (use gcc -MM -D_FILE_OFFSET_BITS=64 *.c to regenerate) cache.o: cache.c helper.h structs.h cache.h rcs.h create.o: create.c helper.h structs.h write.h rcs.h create.h cache.h ea.o: ea.c helper.h structs.h write.h rcs.h ea.h helper.o: helper.c helper.h rcs.h structs.h interface.o: interface.c helper.h cache.h structs.h rcs.h create.h \ write.h ea.h lookup.o: lookup.c helper.h structs.h parse.h cache.h rcs.h main.o: main.c helper.h structs.h cache.h create.h parse.o: parse.c helper.h structs.h write.o: write.c helper.h structs.h write.h copyfs-1.0.1/copyfs.10000644000175000017500000000222310514544542013376 0ustar boklmboklm.TH COPYFS "1" "October 2006" "copyfs" "Versionned File System" .SH NAME copyfs .SH DESCRIPTION CopyFS is a copy-on-write, versionned file system. This file system is usefull for example when you have a directory containing important files, for which you want to track changes, and be able to revert to an older version. CopyFS lets you do that by transparently making backups of each file that you modify. You are then able to see what version are available for a file, and get an older version. The main commands related to CopyFS are : .TP \fBcopyfs-mount\fR : The command that you should use to mount a CopyFS file system. .TP \fBcopyfs-daemon\fR : The CopyFS file system itself. You should not have to run it by yourself, instead use the copyfs-mount command. .TP \fBcopyfs-fversion\fR : The command that you should use when you want to check which version are available for a file, and maybe revert to an older version. .SH HISTORY CopyFS 1.0 was released in December 2004. .SH AUTHORS CopyFS was created by Thomas Joubert and Nicolas Vigier .SH "MORE INFOS" http://n0x.org/copyfs/ .SH SEE ALSO copyfs-fversion(1), copyfs-mount(1), copyfs-daemon(1) copyfs-1.0.1/copyfs-fversion.10000644000175000017500000000175310517502345015234 0ustar boklmboklm.TH COPYFS-FVERSION "1" "October 2006" "copyfs-fversion" "User Commands" .SH NAME copyfs-fversion .SH SYNOPSIS .B copyfs-fversion [-h] [-r] [-s] [-l version] [-g] file [\fIOPTIONS\fR]... .SH DESCRIPTION This is the copyfs-fversion program. This programs lets you see and change versions informations on files hosted on a copyfs file system. .TP \fB\-h\fR Print help and exit .TP \fB\-r\fR Release the version lock .TP \fB\-s\fR Show the versions available for this file (default action) .TP \fB\-g\fR Get the version number in use .TP \fB\-l\fR \fIversion\fR Lock this version .TP \fB\-t\fR \fItagfile\fR Create a tagfile. This tagfile will contain the versions informations about the selected files. You can then restore thoses version later using this tagfile. .TP \fB\-u\fR \fItagfile\fR Restore a tagfile. .SH AUTHORS CopyFS was created by Thomas Joubert and Nicolas Vigier .SH "MORE INFOS" http://n0x.org/copyfs/ .SH SEE ALSO copyfs(1), copyfs-mount(1), copyfs-daemon(1) copyfs-1.0.1/main.c0000644000175000017500000000341510522111551013072 0ustar boklmboklm/* * copyfs - copy on write filesystem http://n0x.org/copyfs/ * Copyright (C) 2004 Nicolas Vigier * Thomas Joubert * This program can be distributed under the terms of the GNU GPL. * See the file COPYING. */ #include #include #include #include #include #include #include #include #include "helper.h" #include "structs.h" #include "cache.h" #include "create.h" char *rcs_version_path = "/home/widan/versions"; void rcs_free_metadata(metadata_t *metadata) { version_t *version, *next; version = metadata->md_versions; while (version) { next = version->v_next; free(version->v_rfile); free(version); version = next; } if (metadata->md_vpath) helper_free_array(metadata->md_vpath); free(metadata->md_vfile); free(metadata); } #if 0 void set_config() { gl_config = getenv("CPYFS_MIRROR"); if (gl_config.mirrored_dir == NULL) { gl_config.mirrored_dir = "/var"; } gl_config.backup_dir = getenv("CPYFS_BACKDIR"); if (gl_config.backup_dir == NULL) { gl_config.backup_dir = "/tmp"; } } int main(int argc, char *argv[]) { fuse_main(argc, argv, &callback_oper); return 0; } #endif extern struct fuse_operations callback_oper; int main(int argc, char **argv) { rcs_version_path = getenv("RCS_VERSION_PATH"); if (!rcs_version_path) { fprintf(stderr, "RCS_VERSION_PATH not defined in environment.\n"); fprintf(stderr, "You really should use he `copyfs-mount' script.\n"); exit(1); } /* Restrict permissions on create files */ umask(0077); cache_initialize(); fuse_main(argc, argv, &callback_oper); cache_finalize(); exit(0); } copyfs-1.0.1/copyfs-daemon.10000644000175000017500000000070010517502771014636 0ustar boklmboklm.TH COPYFS-DAEMON "1" "October 2006" "copyfs-daemon" "User Commands" .SH NAME copyfs-daemon .SH SYNOPSIS .B copyfs-daemon [\fIOPTIONS\fR]... .SH DESCRIPTION This is the copyfs-daemon. You should not run this program directly, instead, use copyfs-mount. .SH AUTHORS CopyFS was created by Thomas Joubert and Nicolas Vigier .SH "MORE INFOS" http://n0x.org/copyfs/ .SH SEE ALSO copyfs(1), copyfs-fversion(1), copyfs-mount(1) copyfs-1.0.1/copyfs-mount.10000644000175000017500000000221610522117122014525 0ustar boklmboklm.TH COPYFS-MOUNT "1" "October 2006" "copyfs-mount" "User Commands" .SH NAME copyfs-mount .SH SYNOPSIS .B copyfs-mount \fIversion-directory\fR \fImount-point\fR .SH DESCRIPTION This script lets you mount a copyfs file system. \fIversion-directory\fR is the directory where the files and versions informations will be stored, so choose a directory where a lot of free space is available. This directory should not be accessed by the users, to it is a good idea to put that directory inside a directory owned by root, for which you don't allow access to users. If you mount your copyfs file system for the first time, an empty directory should be fine (copyfs-mount will create the required files before running copyfs-daemon). \fImount-point\fR is the directory where the copyfs file system should be mounted. This is where the users will access the files. In order to unmount the filesystem, use the \fIfusermount\fR command : .IP "" $ fusermount \-u \fImount-point\fR .SH AUTHORS CopyFS was created by Thomas Joubert and Nicolas Vigier .SH "MORE INFOS" http://n0x.org/copyfs/ .SH SEE ALSO copyfs(1), copyfs-fversion(1), copyfs-daemon(1) copyfs-1.0.1/parse.c0000644000175000017500000001211110522107702013254 0ustar boklmboklm/* * copyfs - copy on write filesystem http://n0x.org/copyfs/ * Copyright (C) 2004 Nicolas Vigier * Thomas Joubert * This program can be distributed under the terms of the GNU GPL. * See the file COPYING. */ #include #include #include #include #include #include "helper.h" #include "structs.h" /* * Parse a line of the metadata file into a version data structure. */ static version_t *parse_version_line(FILE *fh) { version_t *v_info; char *buffer; unsigned int l_vid, l_svid, l_mode, l_uid, l_gid; int name_pos; buffer = helper_read_line(fh); if (!buffer) return NULL; /* Parse the line */ if (sscanf(buffer, "%u:%u:%o:%u:%u:%n", &l_vid, &l_svid, &l_mode, &l_uid, &l_gid, &name_pos) != 5) { /* * Metadata has been corrupt. Someone tried to modify it manually and * screwed it up. Simply ignore it, and try to recover on the next * valid metadata. */ free(buffer); return NULL; } else { v_info = safe_malloc(sizeof(version_t)); v_info->v_vid = l_vid; v_info->v_svid = l_svid; v_info->v_mode = (mode_t)l_mode; v_info->v_uid = (uid_t)l_uid; v_info->v_gid = (gid_t)l_gid; /* * Don't try to append a path just now, since we may need those * metadata for the purpose of path translation. This will be done * later. */ v_info->v_rfile = safe_strdup(buffer + name_pos); free(buffer); return v_info; } } /* * Parse a complete metadata file into the equivalent memory structure, * but do not try to resolve paths to the actual versionned files. * The returned metadata lists all versions, but cannot be used as is : it * is still necessary to resolve all paths, put in the name of the virtual * file, put in the default version number... */ metadata_t *parse_metadata_file(char *metafile) { FILE *fh; metadata_t *md_info; version_t *v_info; int deleted; fh = fopen(metafile, "r"); if (!fh) { /* No metadata, maybe we do not know the file yet */ return NULL; } md_info = safe_malloc(sizeof(metadata_t)); /* We do not know the virtual path here, it is up to the caller to set it */ md_info->md_vfile = NULL; md_info->md_vpath = NULL; md_info->md_versions = NULL; /* Parse it line per line and link the data */ deleted = 0; do { v_info = parse_version_line(fh); if (v_info) { /* * The "zero-version" means the file has been deleted. It is not * actually used, it just flags deletion, so don't put it in the * list. */ if (!v_info->v_vid) { deleted = 1; free(v_info->v_rfile); free(v_info); } else { /* Remove the deleted flag */ deleted = 0; /* * Link it in decreasing order of versions. It depends on the * metadata file being ordered from first to last version (ie * normal reading order) ! */ v_info->v_next = md_info->md_versions; md_info->md_versions = v_info; } } } while (!feof(fh)); /* Tag the file as deleted or not */ md_info->md_deleted = deleted; /* Never touched */ md_info->md_timestamp = 0; /* Default version is latest (it will be replaced later if needed) */ md_info->md_dfl_vid = LATEST; md_info->md_dfl_svid = LATEST; /* Initialize the "control" data */ md_info->md_next = NULL; md_info->md_previous = NULL; fclose(fh); return md_info; } /* * Parse a default version file and extract the preferred version. */ void parse_default_file(char *dflfile, int *vid, int *svid) { char *line; FILE *fh; fh = fopen(dflfile, "r"); if (!fh) { /* No default, assume user wants real latest */ *vid = LATEST; *svid = LATEST; return; } line = helper_read_line(fh); if (sscanf(line, "%i.%i", vid, svid) != 2) { /* The file is probably corrupt, just assume real latest version */ *vid = LATEST; *svid = LATEST; } free(line); fclose(fh); } /* * Retrieve metadata in a given root for a given file. It does not resolve * paths yet, but gets the preferred versions. */ metadata_t *parse_metadata_for_file(char *root, char *filename) { char *metafile, *dflfile, *metapath, *dflpath; metadata_t *metadata; /* Build the path for the metadata file */ metafile = helper_get_file_name(filename, "metadata"); metapath = helper_build_composite("SS", "/", root, metafile); free(metafile); /* Read the partial metadata */ metadata = parse_metadata_file(metapath); if (!metadata) { /* No metadata, we don't know about this file yet */ free(metapath); return NULL; } /* Build the path for the default version file */ dflfile = helper_get_file_name(filename, "dfl-meta"); dflpath = helper_build_composite("SS", "/", root, dflfile); free(dflfile); /* Parse the default version file */ parse_default_file(dflpath, &metadata->md_dfl_vid, &metadata->md_dfl_svid); free(metapath); free(dflpath); return metadata; } copyfs-1.0.1/parse.h0000644000175000017500000000103210522107711013261 0ustar boklmboklm/* * copyfs - copy on write filesystem http://n0x.org/copyfs/ * Copyright (C) 2004 Nicolas Vigier * Thomas Joubert * This program can be distributed under the terms of the GNU GPL. * See the file COPYING. */ #ifndef PARSE_H # define PARSE_H # include "structs.h" metadata_t *parse_metadata_file(char *metafile); void parse_default_file(char *dflfile, int *vid, int *svid); metadata_t *parse_metadata_for_file(char *root, char *filename); #endif /* !PARSE_H */ copyfs-1.0.1/copyfs-mount0000755000175000017500000000303210522130506014367 0ustar boklmboklm#!/bin/sh # copyfs - copy on write filesystem http://n0x.org/copyfs/ # Copyright (C) 2004 Nicolas Vigier # Thomas Joubert # This program can be distributed under the terms of the GNU GPL. # See the file COPYING. FILESYSTEM_HANDLER=copyfs-daemon # Force single-threaded mode, as we aren't MT safe FILESYSTEM_PARAMETERS="-s" if [ $# -lt 2 ]; then echo "Usage: $0 version-directory mount-point" 1>&2 exit 1 fi if ! echo "$1" | grep '^/' then echo "Please use absolute path" 1>&2 exit 1 fi # Check fuse module if ! grep fuse /proc/modules > /dev/null; then if [ $UID = 0 ]; then modprobe fuse > /dev/null || { echo "Could not load fuse kernel module !" 1>&2 exit 1 } else echo "You need the fuse kernel module to run this. As you are" echo "not root, I can't load it. Become root and run :" echo echo " modprobe fuse" exit 1 fi fi if [ $UID == 0 ]; then # Allow other users and check permissions if run by root FILESYSTEM_PARAMETERS="${FILESYSTEM_PARAMETERS} -o default_permissions,allow_other" fi # Check if version dir sane if [ ! -w $1 -o ! -x $1 ]; then echo "Your version directory needs to be writable and executable" 1>&1 exit 2 fi # Check if there is the root directory metadata, else create it if [ ! -f $1/metadata. ]; then echo "1:0:0755:0:0:$(basename $1)" > "$1/metadata." chmod 700 "$1/metadata." fi # Run the daemon export RCS_VERSION_PATH="$1" eval ${FILESYSTEM_HANDLER} "${FILESYSTEM_PARAMETERS}" "$2" copyfs-1.0.1/create.c0000644000175000017500000002635010522107562013423 0ustar boklmboklm/* * copyfs - copy on write filesystem http://n0x.org/copyfs/ * Copyright (C) 2004 Nicolas Vigier * Thomas Joubert * This program can be distributed under the terms of the GNU GPL. * See the file COPYING. */ #include #include #include #include #include #include #include #include #include #include "helper.h" #include "structs.h" #include "write.h" #include "rcs.h" #include "create.h" #include "cache.h" /* * Build a version file name with the given serial for the given virtual file. */ static char *create_version_name(const char *vpath, int serial) { char buffer[9], *filename, *dirname, *translated, *cfile, *complete; filename = helper_extract_filename(vpath); dirname = helper_extract_dirname(vpath); /* Get the directory's path translation */ translated = rcs_translate_path(dirname, rcs_version_path); assert(translated != NULL); /* Build the version file name */ snprintf(buffer, 9, "%08X", serial); cfile = helper_build_composite("SS", ".", buffer, filename); complete = helper_build_composite("SS", "/", translated, cfile); free(translated); free(cfile); free(dirname); free(filename); return complete; } /* * Build a metadata file name with the given virtual file and prefix. * prefix is "metadata" for metadata file and "dfl-meta" for default file. */ char *create_meta_name(char *vpath, char *prefix) { char *dir, *file, *xlat, *name, *res; dir = helper_extract_dirname(vpath); file = helper_extract_filename(vpath); xlat = rcs_translate_path(dir, rcs_version_path); name = helper_build_composite("SS", ".", prefix, file); res = helper_build_composite("SS", "/", xlat, name); free(xlat); free(name); free(file); free(dir); return res; } /* * Link a version to a metadata structure, and flush changes to disk. It will * only link the version in memory if the changes were successfully committed * to disk first. */ static int create_link_version(metadata_t *metadata, version_t *version) { char *metafile, *dflfile; int old_vid, old_svid, old_deleted; /* Build the path for the metadata and default files */ metafile = create_meta_name(metadata->md_vfile, "metadata"); dflfile = create_meta_name(metadata->md_vfile, "dfl-meta"); /* Link in memory */ version->v_next = metadata->md_versions; metadata->md_versions = version; /* Remove the version lock */ old_vid = metadata->md_dfl_vid; old_svid = metadata->md_dfl_svid; metadata->md_dfl_vid = LATEST; metadata->md_dfl_svid = LATEST; /* We are not deleted anymore */ old_deleted = metadata->md_deleted; metadata->md_deleted = 0; /* Write the metafiles */ if (write_metadata_file(metafile, metadata) || write_default_file(dflfile, metadata->md_dfl_vid, metadata->md_dfl_svid)) { /* Something failed, remove the version from memory */ metadata->md_versions = version->v_next; metadata->md_dfl_vid = old_vid; metadata->md_dfl_svid = old_svid; metadata->md_deleted = old_deleted; free(metafile); free(dflfile); return -1; } free(metafile); free(dflfile); return 0; } #define TIME_LIMIT 1 /* * Create a new version or subversion of a file. This is a generic interface. * If subversion is set, it will create a subversion with the given attributes. * Else it will create a version. It won't work for initial version creation. */ static int create_new_version_generic(const char *vpath, int subversion, int do_copy, mode_t mode, uid_t uid, gid_t gid) { metadata_t *metadata; version_t *version, *current; int result; /* We *want* to see deleted files there */ rcs_ignore_deleted = 1; metadata = rcs_translate_to_metadata(vpath, rcs_version_path); assert(metadata != NULL); current = rcs_find_version(metadata, LATEST, LATEST); assert(current != NULL); /* Return to normal behavior */ rcs_ignore_deleted = 0; /* Check timestamp in order not to create bogus new versions */ if (time(NULL) - metadata->md_timestamp < TIME_LIMIT) return 0; /* Can't create a subversion from a deleted file */ if (subversion && metadata->md_deleted) return -1; /* Create a new version in memory */ version = safe_malloc(sizeof(version_t)); if (subversion) { /* If we have a locked version, we have to bump the real version */ if (current->v_vid != metadata->md_versions->v_vid) { version->v_vid = metadata->md_versions->v_vid + 1; version->v_svid = 0; } else { version->v_vid = current->v_vid; version->v_svid = current->v_svid + 1; } version->v_mode = mode & 07777; version->v_uid = uid; version->v_gid = gid; version->v_rfile = safe_strdup(current->v_rfile); } else { version->v_vid = metadata->md_versions->v_vid + 1; version->v_svid = 0; version->v_mode = current->v_mode & 07777; version->v_uid = do_copy ? current->v_uid : uid; version->v_gid = do_copy ? current->v_gid : gid; version->v_rfile = create_version_name(vpath, version->v_vid); } version->v_next = NULL; /* Create the file and copy the contents over, then link the version */ if (!subversion && do_copy) result = create_copy_file(current->v_rfile, version->v_rfile); else result = 0; if (!result) result = create_link_version(metadata, version); if (result) { free(version->v_rfile); free(version); } return result; } /* * Create the metadata file for a new file */ static int create_new_metadata(const char *vpath, char *rpath, mode_t mode, uid_t uid, gid_t gid) { metadata_t *metadata; version_t *version; char *metafile; int res; metadata = safe_malloc(sizeof (metadata_t)); metadata->md_vfile = safe_strdup(vpath); metadata->md_vpath = helper_split_to_array(vpath, '/'); metadata->md_deleted = 0; metadata->md_timestamp = time(NULL); metadata->md_dfl_vid = LATEST; metadata->md_dfl_svid = LATEST; version = safe_malloc(sizeof (version_t)); metadata->md_versions = version; version->v_vid = 1; version->v_svid = 0; version->v_mode = mode; version->v_uid = uid; version->v_gid = gid; version->v_rfile = rpath; version->v_next = NULL; cache_add_metadata(metadata); metafile = create_meta_name(metadata->md_vfile, "metadata"); res = write_metadata_file(metafile, metadata); free(metafile); return res; } /* * Create a new version of the file described by a virtual path. It handles * all the operations : copying the old version to a new version id, creating * the associated metadata and flushing everything to disk. It does *not* * handle the case where the file does not already exist ! */ int create_new_version(const char *vpath) { return create_new_version_generic(vpath, 0, 1, 0, 0, 0); } /* * Create a subversion of the file, using the specified new meta-information. * It needs a file with at least one version to work ! */ int create_new_subversion(const char *vpath, mode_t mode, uid_t uid, gid_t gid) { return create_new_version_generic(vpath, 1, 0, mode, uid, gid); } /* * Create a new empty file version 1. * Handle the case where an older version of the file aldready exists. */ int create_new_file(const char *vpath, mode_t mode, uid_t uid, gid_t gid, dev_t dev) { metadata_t *metadata; mode_t create_mode; char *path; int res; if (!(mode && (S_IFREG | S_IFCHR | S_IFBLK | S_IFIFO | S_IFSOCK))) { return -EPERM; } /* check if an older version existed */ rcs_ignore_deleted = 1; metadata = rcs_translate_to_metadata(vpath, rcs_version_path); rcs_ignore_deleted = 0; if (metadata && !metadata->md_deleted) return -EEXIST; if (!metadata) path = create_version_name(vpath, 1); else path = create_version_name(vpath, metadata->md_versions->v_vid + 1); /* remove suid, sgid, rwx for group and others */ create_mode = (mode & ~S_ISUID & ~S_ISGID & ~S_ISVTX & ~S_IRWXG & ~S_IRWXO) | S_IRWXU; if (mknod(path, create_mode, dev) == -1) { free(path); return -errno; } /* FIXME: the owned should be the same as the parent if suid */ if (!metadata) res = create_new_metadata(vpath, path, mode & 07777, uid, gid); else res = create_new_version_generic(vpath, 0, 0, mode, uid, gid); /* Update timestamp */ metadata = rcs_translate_to_metadata(vpath, rcs_version_path); if (metadata) metadata->md_timestamp = time(NULL); return res; } /* * Create a new symlink, version 1. * Handle the case where an older version of the file aldready exists. */ int create_new_symlink(const char *dest, const char *vpath, uid_t uid, gid_t gid) { char *realpath; metadata_t *metadata; int res; /* check if an older version existed */ rcs_ignore_deleted = 1; metadata = rcs_translate_to_metadata(vpath, rcs_version_path); rcs_ignore_deleted = 0; if (metadata && !metadata->md_deleted) return -EEXIST; if (!metadata) realpath = create_version_name(vpath, 1); else realpath = create_version_name(vpath, metadata->md_versions->v_vid + 1); if (symlink(dest, realpath) == -1) { free(realpath); return -errno; } /* FIXME: the owned should be the same as the parent if suid */ if (!metadata) res = create_new_metadata(vpath, realpath, S_IRWXU | S_IRWXG | S_IRWXO, uid, gid); else res = create_new_version_generic(vpath, 0, 0, S_IRWXU | S_IRWXG | S_IRWXO, uid, gid); return res; } int create_new_directory(const char *vpath, mode_t mode, uid_t uid, gid_t gid) { char *realpath; metadata_t *metadata; int res; /* check if an older version existed */ rcs_ignore_deleted = 1; metadata = rcs_translate_to_metadata(vpath, rcs_version_path); rcs_ignore_deleted = 0; if (metadata && !metadata->md_deleted) return -EEXIST; if (!metadata) realpath = create_version_name(vpath, 1); else realpath = create_version_name(vpath, metadata->md_versions->v_vid + 1); /* FIXME: */ if (mkdir(realpath, 0700) == -1) { free(realpath); return -errno; } /* FIXME: the owned should be the same as the parent if suid */ if (!metadata) res = create_new_metadata(vpath, realpath, mode, uid, gid); else res = create_new_version_generic(vpath, 0, 0, mode, uid, gid); return res; } /* * Copy a (real) file to another (real) file. * file can be a regular file or a simlink */ int create_copy_file(const char *source, const char *target) { struct stat src_stat; if (lstat(source, &src_stat) == -1) return -1; if (S_ISLNK(src_stat.st_mode)) { char lnk[1024]; int lnk_size; if ((lnk_size = readlink(source, lnk, 1023)) == -1) return -2; lnk[lnk_size] = '\0'; if (symlink(lnk, target) == -1) return -3; } else if (S_ISREG(src_stat.st_mode)) { int src, dst; int rsize; char buf[1024]; if ((src = open(source, O_RDONLY)) == -1) { close(dst); return -4; } if ((dst = creat(target, src_stat.st_mode)) == -1) return -5; while ((rsize = read(src, buf, 1024))) { if (rsize == -1 && errno == EINTR) continue ; if (rsize == -1) { close(src); close(dst); return -6; } while (write(dst, buf, rsize) == -1) if (errno != EINTR) { close(src); close(dst); return -7; } } close(src); close(dst); } else { return -8; } return 0; } copyfs-1.0.1/create.h0000644000175000017500000000147310522107574013432 0ustar boklmboklm/* * copyfs - copy on write filesystem http://n0x.org/copyfs/ * Copyright (C) 2004 Nicolas Vigier * Thomas Joubert * This program can be distributed under the terms of the GNU GPL. * See the file COPYING. */ #ifndef CREATE_H # define CREATE_H char *create_meta_name(char *vpath, char *prefix); int create_new_version(const char *vpath); int create_new_subversion(const char *vpath, mode_t mode, uid_t uid, gid_t gid); int create_new_file(const char *vpath, mode_t mode, uid_t uid, gid_t gid, dev_t dev); int create_new_symlink(const char *dest, const char *vpath, uid_t uid, gid_t gid); int create_new_directory(const char *vpath, mode_t mode, uid_t uid, gid_t gid); int create_copy_file(const char *source, const char *target); #endif /* !CREATE_H */ copyfs-1.0.1/cache.c0000644000175000017500000001044610522107520013214 0ustar boklmboklm/* * copyfs - copy on write filesystem http://n0x.org/copyfs/ * Copyright (C) 2004 Nicolas Vigier * Thomas Joubert * This program can be distributed under the terms of the GNU GPL. * See the file COPYING. */ #include #include #include #include #include "helper.h" #include "structs.h" #include "cache.h" #include "rcs.h" static bucket_t cache_hash_table[CACHE_HASH_BUCKETS]; static unsigned int cache_item_count = 0; /* * Initialize the cache control structures. */ void cache_initialize(void) { unsigned int i; /* Initialize the buckets */ for (i = 0; i < CACHE_HASH_BUCKETS; i++) { cache_hash_table[i].b_count = 0; cache_hash_table[i].b_contents = NULL; } /* No items in there */ cache_item_count = 0; } /* * Free the cache data. */ void cache_finalize(void) { unsigned int i; for (i = 0; i < CACHE_HASH_BUCKETS; i++) { metadata_t *metadata, *next; metadata = cache_hash_table[i].b_contents; while (metadata) { next = metadata->md_next; rcs_free_metadata(metadata); metadata = next; } cache_hash_table[i].b_count = 0; cache_hash_table[i].b_contents = NULL; } } /* * Retrieve file metadata from the cache, assuming there is some in there. * It returns NULL if there is no *cached* metadata. It is the caller's * responsibility to try to read the metadata on disk (and if possible feed * it to the cache too) * * Items accessed are bumped to the top of their hash bucket, so repeat * accesses are faster, and so these items don't get zapped when the cache * grows too much and it is necessary to clean it up. */ metadata_t *cache_get_metadata(const char *vpath) { bucket_t *bucket; metadata_t *metadata; /* Lookup the item */ bucket = &cache_hash_table[CACHE_HASH(vpath)]; metadata = bucket->b_contents; while (metadata && strcmp(metadata->md_vfile, vpath)) metadata = metadata->md_next; if (!metadata) return NULL; /* Disconnect it from the list, if we are not at the beginning */ if (metadata->md_previous) { metadata->md_previous->md_next = metadata->md_next; if (metadata->md_next) metadata->md_next->md_previous = metadata->md_previous; /* Reconnect it on top */ metadata->md_previous = NULL; metadata->md_next = bucket->b_contents; bucket->b_contents->md_previous = NULL; bucket->b_contents = metadata; } return metadata; } /* * Clean the older items out of the cache to free space. The goal is to * half the number of items, so that we don't get called constantly. */ void cache_cleanup_old_items() { /* FIXME: implement it if it really needed */ } /* * Insert file metadata into the cache. It does not check if the data is * already there, so try to avoid putting it twice (especially if the two * instances are different !) * * If the cache has grown too big, it is cleaned up, the least recently * used items going away. That way we avoid to trash frequently-used entries. */ void cache_add_metadata(metadata_t *metadata) { bucket_t *bucket; /* Check if cache needs cleaning */ if (cache_item_count == CACHE_SIZE) cache_cleanup_old_items(); /* Insert the element */ bucket = &cache_hash_table[CACHE_HASH(metadata->md_vfile)]; metadata->md_previous = NULL; metadata->md_next = bucket->b_contents; if (bucket->b_contents) bucket->b_contents->md_previous = metadata; bucket->b_contents = metadata; /* Bump the bucket's item counter, and the global counter */ cache_item_count++; bucket->b_count++; } /* * Try to find the path composed of the maximal number of elements of the * array that will have a cache hit. Return the count, and the metadata. It * returns a count of -1 if there is no hit at all. */ int cache_find_maximal_match(char **array, metadata_t **result) { int count; /* Count items */ for (count = 0; array[count]; count++) ; for (/* Nothing */; count >= 0; count--) { char *old_value, *path; /* Try with this count */ old_value = array[count]; array[count] = NULL; path = helper_build_composite("-A", "/", array); array[count] = old_value; *result = cache_get_metadata(path); free(path); if (*result) return count; } return -1; } copyfs-1.0.1/cache.h0000644000175000017500000000134010522107530013213 0ustar boklmboklm/* * copyfs - copy on write filesystem http://n0x.org/copyfs/ * Copyright (C) 2004 Nicolas Vigier * Thomas Joubert * This program can be distributed under the terms of the GNU GPL. * See the file COPYING. */ #ifndef CACHE_H # define CACHE_H # include "structs.h" # define CACHE_SIZE 256 /* low for testing */ # define CACHE_HASH_BUCKETS 128 # define CACHE_HASH(x) (helper_hash_string((x)) % (CACHE_HASH_BUCKETS)) void cache_initialize(void); void cache_finalize(void); metadata_t *cache_get_metadata(const char *vpath); void cache_add_metadata(metadata_t *metadata); int cache_find_maximal_match(char **array, metadata_t **result); #endif /* !CACHE_H */ copyfs-1.0.1/lookup.c0000644000175000017500000001545710522107657013504 0ustar boklmboklm/* * copyfs - copy on write filesystem http://n0x.org/copyfs/ * Copyright (C) 2004 Nicolas Vigier * Thomas Joubert * This program can be distributed under the terms of the GNU GPL. * See the file COPYING. */ #include #include #include #include "helper.h" #include "structs.h" #include "parse.h" #include "cache.h" #include "rcs.h" /* Ignore delete flags */ int rcs_ignore_deleted = 0; /* * Find a given version in a metadata set. If vid is LATEST, retrieves the * absolute latest version. If svid is LATEST and vid is not, retrieves the * latest subversion of that version, that is the latest revision of the * metadata. */ version_t *rcs_find_version(metadata_t *metadata, int vid, int svid) { version_t *version; version = metadata->md_versions; /* Latest version is first, else find version block */ if (vid == LATEST) { /* * If the file is marked as deleted, make it not appear. It should not * matter for callers. If the file was deleted last, just create a new * version, as if it never existed (except the version number will be * higher than 1) */ if (metadata->md_deleted && !rcs_ignore_deleted) return NULL; /* * Check if there is a pinned version. If there is, we have a normal * version search. */ if (metadata->md_dfl_vid == LATEST) return version; else { vid = metadata->md_dfl_vid; svid = metadata->md_dfl_svid; } } while (version && (version->v_vid > (unsigned)vid)) version = version->v_next; if (!version || (version->v_vid != (unsigned)vid)) { /* * If a default version makes no sense anymore, just use the real * latest and ignore the default. */ if (metadata->md_dfl_vid != LATEST) return metadata->md_versions; /* No such version */ return NULL; } /* Lookup subversion (ie metadata revision) */ if (svid == LATEST) return version; while (version && (version->v_vid == (unsigned)vid) && (version->v_svid > (unsigned)svid)) version = version->v_next; if (!version || (version->v_vid != (unsigned)vid) || (version->v_svid != (unsigned)svid)) { /* * If the "default" version does not exist anymore, use the real * default (it should never happen anyway) */ if (metadata->md_dfl_vid != LATEST) return metadata->md_versions; /* This subversion does not exist */ return NULL; } return version; } /* * Add the specified path to the versionned files in a metadata block. */ static void rcs_fixup_metadata_paths(metadata_t *metadata, char *path) { version_t *version; for (version = metadata->md_versions; version; version = version->v_next) { char *new; new = helper_build_composite("SS", "/", path, version->v_rfile); free(version->v_rfile); version->v_rfile = new; } } /* * Insert the given number of items from a path as a virtual file name * in the given metadata. */ static void rcs_fixup_metadata_vfile(metadata_t *metadata, char **elements, unsigned int count) { unsigned int i; /* Make a copy of the proper portion of the path elements */ metadata->md_vpath = safe_malloc(sizeof(char *) * (count + 1)); for (i = 0; i < count; i++) metadata->md_vpath[i] = safe_strdup(elements[i]); metadata->md_vpath[i] = NULL; /* Build the composite path */ metadata->md_vfile = helper_build_composite("-A", "/", metadata->md_vpath); } /* * Translate a path in the virtual filesystem to the real path of the * versionned file, assuming a given version directory. This does the * translation level by level, at each time using the preferred version * of the file. */ char *rcs_translate_path(const char *virtual, char *vroot) { char **elements, *path; unsigned int i; int base; metadata_t *metadata; version_t *version; /* The root directory is special */ if (!strcmp(virtual, "/")) { version_t *last; char *metafile, *dflfile; metadata = cache_get_metadata(virtual); if (metadata) { version = rcs_find_version(metadata, LATEST, LATEST); return safe_strdup(version->v_rfile); } metafile = helper_build_composite("SS", "/", vroot, "metadata."); dflfile = helper_build_composite("SS", "/", vroot, "dfl-meta."); /* The root HAS to have metadata */ metadata = parse_metadata_file(metafile); assert(metadata != NULL); last = rcs_find_version(metadata, LATEST, LATEST); assert(last != NULL); /* Parse the default version file */ parse_default_file(dflfile, &metadata->md_dfl_vid, &metadata->md_dfl_svid); free(metafile); free(dflfile); /* Complete the metadata */ metadata->md_vpath = safe_malloc(sizeof(char *)); metadata->md_vpath[0] = NULL; for (version = metadata->md_versions; version; version = version->v_next) { free(version->v_rfile); version->v_rfile = safe_strdup(vroot); } metadata->md_vfile = safe_strdup("/"); cache_add_metadata(metadata); return safe_strdup(last->v_rfile); } /* Get path elements */ elements = helper_split_to_array(virtual, '/'); /* Try to get a starting point from the cache, the more advanced possible */ base = cache_find_maximal_match(elements, &metadata); if (base > 0) { version = rcs_find_version(metadata, LATEST, LATEST); if (!version) { helper_free_array(elements); return NULL; } path = version->v_rfile; } else { path = vroot; base = 0; } /* Iterate from the root onwards */ for (i = base; elements[i]; i++) { /* Retrieve our metadata and find the proper version */ metadata = parse_metadata_for_file(path, elements[i]); if (!metadata) { helper_free_array(elements); return NULL; } version = rcs_find_version(metadata, LATEST, LATEST); if (!version) { helper_free_array(elements); rcs_free_metadata(metadata); return NULL; } /* Fixup this metadata, and add it to the cache */ rcs_fixup_metadata_paths(metadata, path); rcs_fixup_metadata_vfile(metadata, elements, i + 1); cache_add_metadata(metadata); /* Get the new path from there */ path = version->v_rfile; } /* If we got there, we found it, so return it */ helper_free_array(elements); return safe_strdup(path); } /* * Get the metadata structure associated with a virtual file. */ metadata_t *rcs_translate_to_metadata(const char *vfile, char *vroot) { char *path; path = rcs_translate_path(vfile, vroot); if (!path) return NULL; free(path); /* * Lookup the metadata that got put in the cache. It can't have been removed * yet, since cache cleaning is only done on insertions. */ return cache_get_metadata(vfile); } copyfs-1.0.1/COPYING0000644000175000017500000004310607017310612013042 0ustar boklmboklm GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 Library 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 Appendix: 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) 19yy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA 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) 19yy 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 Library General Public License instead of this License. copyfs-1.0.1/copyfs-fversion0000755000175000017500000002020510522111731015060 0ustar boklmboklm#!/usr/bin/perl -w # copyfs - copy on write filesystem http://n0x.org/copyfs/ # Copyright (C) 2004 Nicolas Vigier # Thomas Joubert # This program can be distributed under the terms of the GNU GPL. # See the file COPYING. use strict; use warnings; use IPC::Open3; use IO::Select; use POSIX qw(strftime); use Fcntl ':mode'; use File::Basename; use File::stat; use Errno; use Getopt::Std; # # Run a command, and returns both output and error. # sub run_command($) { my $command = shift; my ($output, $error); my $selector; my $pid; $output = ""; $error = ""; $pid = open3(*INPUT, *OUTPUT, *ERROR, $command); # No input close(INPUT); $selector = IO::Select->new(); $selector->add(*ERROR, *OUTPUT); # Get what gets passed on the descriptors while (my @ready = $selector->can_read) { foreach my $fh (@ready) { if (fileno($fh) == fileno(ERROR)) { $error .= scalar(); } else { $output .= scalar(); } $selector->remove($fh) if eof($fh); } } close(OUTPUT); close(ERROR); # Kill the zombie waitpid($pid, 0); return ($output, $error); } # # Get an extended attribute. # sub get_ea($$) { my ($file, $name) = @_; my ($output, $error); my $cmd; $cmd = "getfattr -h -n '$name' --only-values -- '$file'"; ($output, $error) = run_command($cmd); $error =~ s/^getfattr: // if $error; return ($output, $error); } # # Set an extended attribute. # sub set_ea($$$) { my ($file, $name, $value) = @_; my ($output, $error); my $cmd; $cmd = "setfattr -h -n '$name' -v '$value' -- '$file'"; ($output, $error) = run_command($cmd); $error =~ s/^setfattr: // if $error; return ($output, $error); } # # Get current version number. # sub get_current_version($) { my $file = shift; my ($value, $error); ($value, $error) = get_ea($file, "rcs.locked_version"); die "$0: $error" if $error; return split(/\./, $value); } # # Set current version number. # sub set_current_version($$$) { my ($file, $vid, $svid) = @_; my ($value, $error) = set_ea($file, "rcs.locked_version", "$vid.$svid"); die "$0: $error" if $error; } # # Dump a mode into an ls-like string. # sub get_perms($) { my $mode = shift; my $result = ""; # Type $result .= "s" if S_ISSOCK($mode); $result .= "l" if S_ISLNK($mode); $result .= "-" if S_ISREG($mode); $result .= "b" if S_ISBLK($mode); $result .= "d" if S_ISDIR($mode); $result .= "c" if S_ISCHR($mode); $result .= "p" if S_ISFIFO($mode); # Mode for owner $result .= (($mode & S_IRUSR) ? "r" : "-"); $result .= (($mode & S_IWUSR) ? "w" : "-"); $result .= (($mode & S_ISUID) ? (($mode & S_IXUSR) ? "s" : "S") : (($mode & S_IXUSR) ? "x" : "-")); # Mode for group $result .= (($mode & S_IRGRP) ? "r" : "-"); $result .= (($mode & S_IWGRP) ? "w" : "-"); $result .= (($mode & S_ISGID) ? (($mode & S_IXGRP) ? "s" : "S") : (($mode & S_IXGRP) ? "x" : "-")); # Mode for group $result .= (($mode & S_IROTH) ? "r" : "-"); $result .= (($mode & S_IWOTH) ? "w" : "-"); $result .= (($mode & S_ISVTX) ? (($mode & S_IXOTH) ? "t" : "T") : (($mode & S_IXGRP) ? "x" : "-")); return $result; } # # Dump the owner in string form. # sub dump_owner($) { my $uid = shift; my $value; $value = getpwuid($uid); return $value if $value; return "$uid"; } # # Dump the group in string form. # sub dump_group($) { my $gid = shift; my $value; $value = getgrgid($gid); return $value if $value; return "$gid"; } # # Dump the version information for a file. # sub dump_versions($) { my $file = shift; # Get the active revision my ($cvid, $csvid) = get_current_version($file); # Get the attribute my ($value, $error) = get_ea($file, "rcs.metadata_dump"); die "$0: $error" if $error; printf("File %s (%s is active) :\n", $file, ($cvid != -1) ? "'*'" : "last"); # Explode the attributes my @versions = reverse(split(/\|/, $value)); foreach my $vstring (@versions) { my ($vid, $svid, $mode, $uid, $gid, $size, $mtime) = split(/:/, $vstring); # Display it printf(" v%-4.4s : %s %-8.8s %-8.8s %10i %s", "$vid.$svid", get_perms($mode), dump_owner($uid), dump_group($gid), $size, strftime("%c", localtime($mtime))); printf(" [*]") if (($cvid == $vid) && ($csvid == $svid)); printf("\n"); } } # # Generate the tag data for a directory # sub generate_tag_file_for_directory { my ($fh, $directory) = @_; my @dirs = (); # Find files and directories opendir(DIR, $directory) or die "$0: Can't open directory !\n"; while (my $item = readdir(DIR)) { if (($item ne ".") && ($item ne "..")) { my ($vid, $svid) = get_current_version("$directory/$item"); my $st = lstat("$directory/$item"); push @dirs, $item if (S_ISDIR($st->mode)); print $fh "$directory/$item|$vid.$svid\n"; } } closedir(DIR); # Iterate over directories for my $item (@dirs) { generate_tag_file_for_directory($fh, "$directory/$item"); } } # # Generate the tag data # sub generate_tag_file($$) { my ($root, $tagname) = @_; my $fh; open $fh, ">$tagname" or die "$0: Can't open tagfile !\n"; generate_tag_file_for_directory($fh, $root); close $fh; } # # Restore a tag file # sub restore_tag_file($$) { my ($root, $tagname) = @_; # Create the root dir if it does no exist if (!lstat($root)) { print "Creating root directory $root\n"; mkdir($root); } open TAG, $tagname or die "$0: Can't open tagfile !\n"; while (my $line = ) { if ($line =~ m/^([^\|]+)\|(\d+).(\d+)/) { my ($file, $vid, $svid) = ($1, $2, $3); if ($file !~ m/^\Q$root\E\//) { print STDERR "$0: you are trying to put a tag in a different "; print STDERR "directory !\n"; exit(1); } my $st = lstat($file); if (!$st) { my $done = 0; # It can take some time to change files back into directories # since fuse caches the lookup info for about one second, so # we retry here. do { # Touch the file first, since it does not exist if (!open(FILE, ">$file")) { # "Not a directory" ignored print STDERR "$0: could not touch $file !\n" if (!$!{ENOTDIR}); } else { $done = 1; close(FILE); } } while (!$done && $!{ENOTDIR}); } # Fix version set_current_version($file, $vid, $svid); printf("Restored $file to version $vid.$svid\n"); } } close TAG; } # Strip the path $0 = basename($0); my %options = (); # Parse command line getopts("ghl:rst:u:", \%options); if ($options{h}) { printf("Usage: $0 [-h] [-r] [-s] [-l version] [-g] file\n"); printf("\n"); printf(" -h Show this help\n"); # Version management printf(" -r Release the version lock\n"); printf(" -s Show the versions for this file (default)\n"); printf(" -g Get the version number in use\n"); printf(" -l version Lock this version\n"); # Tagging printf(" -t tagfile Create a tag file\n"); printf(" -u tagfile Restore a tag file\n"); exit(0); } # We need a file there if (scalar(@ARGV) != 1) { print STDERR "$0: You need to specify one file to operate on.\n"; exit(1); } # Show by default $options{s} = 1 if (!$options{r} && !$options{g} && !$options{s} && !$options{l} && !$options{t} && !$options{u}); if ($options{r}) { set_current_version($ARGV[0], -1, -1); exit(0); } if ($options{g}) { my ($vid, $svid) = get_current_version($ARGV[0]); print "$vid.$svid\n"; exit(0); } if ($options{s}) { dump_versions($ARGV[0]); exit(0); } if ($options{l}) { if ($options{l} =~ m/^(\d+).(\d+)$/) { set_current_version($ARGV[0], $1, $2); exit(0); } else { print STDERR "$0: This version number is incorrect (format is x.y)\n"; exit(1); } } if ($options{t}) { generate_tag_file($ARGV[0], $options{t}); exit(0); } if ($options{u}) { restore_tag_file($ARGV[0], $options{u}); exit(0); } copyfs-1.0.1/write.c0000644000175000017500000000453710522107743013316 0ustar boklmboklm/* * copyfs - copy on write filesystem http://n0x.org/copyfs/ * Copyright (C) 2004 Nicolas Vigier * Thomas Joubert * This program can be distributed under the terms of the GNU GPL. * See the file COPYING. */ #include #include #include #include #include "helper.h" #include "structs.h" #include "write.h" /* * Worker function for version writing. */ static int write_metadata_file_worker(FILE *fh, version_t *version) { char *name; /* Write them in order */ if (version->v_next) if (write_metadata_file_worker(fh, version->v_next) != 0) return -1; /* Strip the path */ name = rindex(version->v_rfile, '/'); if (!name) name = version->v_rfile; else name++; /* Write this version's information */ if (fprintf(fh, "%i:%i:%04o:%i:%i:%s\n", version->v_vid, version->v_svid, version->v_mode, version->v_uid, version->v_gid, name) < 0) return -1; return 0; } /* * Write a metadata file on disk from the in-memory structures. We rewrite * the whole file to avoid leaving "deletion lines" at all the points the * file was deleted and re-created. Plus it cleans up partially corrupt files * automatically. Returns non-zero on error. */ int write_metadata_file(char *metafile, metadata_t *metadata) { FILE *fh; /* Open metadata file */ fh = fopen(metafile, "w"); if (!fh) return -1; /* Write normal versions */ if (write_metadata_file_worker(fh, metadata->md_versions) != 0) { fclose(fh); return -1; } /* If the file is marked deleted, put a killer version */ if (metadata->md_deleted) if (fprintf(fh, "0:0:0000:0:0:\n") < 0) { fclose(fh); return -1; } fclose(fh); return 0; } /* * Write a default version file for the given version. Return non-zero on * error. */ int write_default_file(char *dflfile, int vid, int svid) { FILE *fh; /* If we want to return it to default, remove the file */ if (vid == LATEST) { int result; result = unlink(dflfile); if (!result || (errno == ENOENT)) return 0; else return result; } fh = fopen(dflfile, "w"); if (!fh) return -1; /* Write new value */ if ((fprintf(fh, "%i.%i\n", vid, svid) < 0)) { fclose(fh); return -1; } fclose(fh); return 0; } copyfs-1.0.1/write.h0000644000175000017500000000071310522107754013315 0ustar boklmboklm/* * copyfs - copy on write filesystem http://n0x.org/copyfs/ * Copyright (C) 2004 Nicolas Vigier * Thomas Joubert * This program can be distributed under the terms of the GNU GPL. * See the file COPYING. */ #ifndef WRITE_H # define WRITE_H int write_metadata_file(char *metafile, metadata_t *metadata); int write_default_file(char *dflfile, int vid, int svid); #endif /* !WRITE_H */ copyfs-1.0.1/helper.c0000644000175000017500000001661110522107635013437 0ustar boklmboklm/* * copyfs - copy on write filesystem http://n0x.org/copyfs/ * Copyright (C) 2004 Nicolas Vigier * Thomas Joubert * This program can be distributed under the terms of the GNU GPL. * See the file COPYING. */ #include #include #include #include #include #include "helper.h" #include "rcs.h" /* * Error-checking replacement for malloc. */ void *helper_malloc(size_t size, char *file, int line, const char *fn) { void *result; result = malloc(size); if (!result) { fprintf(stderr, "Out of memory in malloc() at %s:%i [%s]\n", file, line, fn); abort(); } return result; } /* * Error-checking replacement for realloc. */ void *helper_realloc(void *ptr, size_t size, char *file, int line, const char *fn) { void *result; result = realloc(ptr, size); if (!result) { fprintf(stderr, "Out of memory in realloc() at %s:%i [%s]\n", file, line, fn); abort(); } return result; } /* * Error-checking replacement for strdup. */ char *helper_strdup(const char *str, char *file, int line, const char *fn) { char *result; result = strdup(str); if (!result) { fprintf(stderr, "Out of memory in strdup() at %s:%i [%s]\n", file, line, fn); abort(); } return result; } /* * Split a character string into its constitutive elements, using a given * separator. Ignores empty elements (like repeats of the separator, or a * separator at the beginning or the end of the string) Returns a * NULL-terminated array. */ char **helper_split_to_array(const char *string, char separator) { unsigned int count, i; char **result; int start; for (i = 0, count = 0; string[i]; i++) { /* * We have an item when : * - we are at the beginning and the first character is not a separator * - we are elsewhere and we are switching from a separator to another * character */ if (i == 0) { if (string[i] != separator) count++; } else if ((string[i - 1] == separator) && (string[i] != separator)) count ++; } result = safe_malloc(sizeof(char *) * (count + 1)); result[count] = NULL; /* Stop there if nothing to store */ if (!count) return result; /* We look one behind, so we can catch the '\0' as a separator */ for (count = 0, start = -1, i = 0; !i || string[i - 1]; i++) { if (!string[i] || (string[i] == separator)) { if (start == -1) continue; else { /* Save the item */ result[count] = safe_malloc(i - start + 1); memcpy(result[count], string + start, i - start); result[count][i - start] = '\0'; count++; start = -1; } } else if (start == -1) start = i; } return result; } /* * Free a string array. */ void helper_free_array(char **array) { unsigned int i; for (i = 0; array[i]; i++) free(array[i]); free(array); } /* * Check if an array has a given prefix, ie the first array begins with * the items of the second, in order. */ int helper_array_has_prefix(char **longest, char **shortest) { unsigned int i; for (i = 0; longest[i] && shortest[i]; i++) if (strcmp(longest[i], shortest[i])) return 0; return (shortest[i] == NULL); } /* * Concatenate strings and strings arrays with a given separator. The first * arguments tells the function what is expected at each position : 'S' is * a normal string, 'A' is an array, '-' means a fixed separator. No separator * is put at the beginning or end of the result by default. */ char *helper_build_composite(char *format, char *separator, ...) { unsigned int length, i; va_list args; char *result; /* Count chars */ va_start(args, separator); for (length = 0, i = 0; format[i]; i++) { assert((format[i] == 'S') || (format[i] == 'A') || (format[i] == '-')); if (format[i] == '-') length += strlen(separator); else { if (format[i] == 'S') length += strlen(va_arg(args, char *)); else { unsigned int j; char **array; array = va_arg(args, char **); for (j = 0; array[j]; j++) { length += strlen(array[j]); if (array[j + 1]) length += strlen(separator); } } /* Don't add separators at the end, or before explicit separators */ if (format[i + 1] && (format[i + 1] != '-')) length += strlen(separator); } } va_end(args); result = safe_malloc(length + 1); result[0] = '\0'; /* Now do it */ va_start(args, separator); for (i = 0; format[i]; i++) if (format[i] == '-') strcat(result, separator); else { if (format[i] == 'S') strcat(result, va_arg(args, char *)); else { unsigned int j; char **array; array = va_arg(args, char **); for (j = 0; array[j]; j++) { strcat(result, array[j]); if (array[j + 1]) strcat(result, separator); } } if (format[i + 1] && (format[i + 1] != '-')) strcat(result, separator); } /* All done */ return result; } /* * Hash a string into an 8-bit number. */ unsigned char helper_hash_string(const char *string) { unsigned char result; for (result = 0; *string; string++) result ^= *string; return result; } /* * Read a complete line from a file, that HAS to end with a '\n'. The * returned line does NOT contain the final '\n'. */ char *helper_read_line(FILE *fh) { char *buffer = safe_malloc(LINE_BUFFER_STEP); int buffer_size = LINE_BUFFER_STEP; int buffer_pos = 0; int i, done = 0; do { if (!fgets(buffer + buffer_pos, buffer_size - buffer_pos, fh)) { free(buffer); return NULL; } /* Check if we got whole line */ for (i = buffer_pos; i < buffer_size; i++) if (buffer[i] == '\n') { buffer[i] = '\0'; done = 1; break; } if (!done) { /* Not enough room yet */ buffer = safe_realloc(buffer, buffer_size + LINE_BUFFER_STEP); buffer_pos = buffer_size - 1; buffer_size += LINE_BUFFER_STEP; } } while (!done); return buffer; } /* * Get a complete prefixed file name, of the form .. */ char *helper_get_file_name(char *base, char *prefix) { char *result; result = safe_malloc(strlen(base) + strlen(prefix) + 2); sprintf(result, "%s.%s", prefix, base); return result; } /* * Return the filename part of a path. */ char *helper_extract_filename(const char *path) { char *result; result = rindex(path, '/'); if (result) return safe_strdup(result + 1); else return safe_strdup(path); } /* * Return the dirname part of a path. */ char *helper_extract_dirname(const char *path) { char *result, *position; result = safe_strdup(path); position = rindex(result, '/'); if (position) *position = '\0'; else *result = '\0'; return safe_realloc(result, strlen(result) + 1); } /* * Build a metadata file name with the given virtual file and prefix. * prefix is "metadata" for metadata file and "dfl-meta" for default file. */ char *helper_create_meta_name(const char *vpath, char *prefix) { char *dir, *file, *xlat, *name, *res; dir = helper_extract_dirname(vpath); file = helper_extract_filename(vpath); xlat = rcs_translate_path(dir, rcs_version_path); name = helper_build_composite("SS", ".", prefix, file); res = helper_build_composite("SS", "/", xlat, name); free(xlat); free(name); free(file); free(dir); return res; } copyfs-1.0.1/helper.h0000644000175000017500000000304610522107644013442 0ustar boklmboklm/* * copyfs - copy on write filesystem http://n0x.org/copyfs/ * Copyright (C) 2004 Nicolas Vigier * Thomas Joubert * This program can be distributed under the terms of the GNU GPL. * See the file COPYING. */ #ifndef HELPER_H # define HELPER_H # include # include # define LINE_BUFFER_STEP 1024 /* * Safe memory allocation */ void *helper_malloc(size_t size, char *file, int line, const char *fn); void *helper_realloc(void *ptr, size_t size, char *file, int line, const char *fn); char *helper_strdup(const char *str, char *file, int line, const char *fn); # define safe_malloc(s) \ helper_malloc(s, __FILE__, __LINE__, __func__) # define safe_realloc(p, s) \ helper_realloc(p, s, __FILE__, __LINE__, __func__) # define safe_strdup(s) \ helper_strdup(s, __FILE__, __LINE__, __func__) /* * String arrays */ char **helper_split_to_array(const char *string, char separator); void helper_free_array(char **array); int helper_array_has_prefix(char **longest, char **shortest); char *helper_build_composite(char *format, char *separator, ...); /* * Miscellaneous things */ unsigned char helper_hash_string(const char *string); char *helper_read_line(FILE *fh); char *helper_get_file_name(char *base, char *prefix); char *helper_extract_filename(const char *path); char *helper_extract_dirname(const char *path); char *helper_create_meta_name(const char *vpath, char *prefix); #endif /* !HELPER_H */