/* * Copyright 2001-2004 Brandon Long * All Rights Reserved. * * ClearSilver Templating System * * This code is made available under the terms of the ClearSilver License. * http://www.clearsilver.net/license.hdf * */ /* * revision-controlled file system (RCFS) with meta-info storage */ #include "cs_config.h" #include <limits.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <dirent.h> #include "util/neo_misc.h" #include "util/neo_err.h" #include "util/neo_files.h" #include "util/neo_hdf.h" #include "util/ulocks.h" #include "rcfs.h" NEOERR * rcfs_meta_load (const char *path, HDF **meta) { NEOERR *err; char fpath[_POSIX_PATH_MAX]; HDF *m; snprintf (fpath, sizeof(fpath), "%s,log", path); err = hdf_init (&m); if (err) return nerr_pass (err); err = hdf_read_file (m, fpath); if (err) { hdf_destroy (&m); return nerr_pass (err); } *meta = m; return STATUS_OK; } static NEOERR * _meta_save (const char *path, HDF *meta) { NEOERR *err; char ftmp[_POSIX_PATH_MAX]; char fpath[_POSIX_PATH_MAX]; snprintf (ftmp, sizeof(ftmp), "%s,log.tmp", path); snprintf (fpath, sizeof(fpath), "%s,log", path); err = hdf_write_file (meta, ftmp); if (err) return nerr_pass (err); if (rename (ftmp, fpath) == -1) { unlink (ftmp); return nerr_raise_errno (NERR_IO, "Unable to rename file %s", ftmp); } return STATUS_OK; } NEOERR * rcfs_meta_save (const char *path, HDF *meta) { NEOERR *err; int lock; HDF *m; err = rcfs_lock (path, &lock); if (err) return nerr_pass (err); do { err = rcfs_meta_load (path, &m); if (err) break; err = hdf_copy (m, "Meta", meta); if (err) break; err = _meta_save (path, m); } while (0); rcfs_unlock (lock); return nerr_pass (err); } /* load a specified version of the file, version -1 is latest */ NEOERR * rcfs_load (const char *path, int version, char **data) { NEOERR *err; char fpath[_POSIX_PATH_MAX]; if (version == -1) { HDF *meta, *vers; int x; err = rcfs_meta_load (path, &meta); if (err) return nerr_pass (err); for (vers = hdf_get_child (meta, "Versions"); vers; vers = hdf_obj_next (vers)) { x = atoi (hdf_obj_name (vers)); if (x > version) version = x; } hdf_destroy (&meta); } snprintf (fpath, sizeof (fpath), "%s,%d", path, version); err = ne_load_file (fpath, data); return nerr_pass (err); } NEOERR * rcfs_save (const char *path, const char *data, const char *user, const char *log) { NEOERR *err; HDF *meta = NULL, *vers; char fpath[_POSIX_PATH_MAX]; char buf[256]; int version = 0; int fd; int lock; int x, l, w; err = rcfs_lock (path, &lock); if (err) return nerr_pass (err); do { err = rcfs_meta_load (path, &meta); if (err && nerr_handle (&err, NERR_NOT_FOUND)) { /* new file! */ err = hdf_init (&meta); } if (err) return nerr_pass (err); for (vers = hdf_get_child (meta, "Versions"); vers; vers = hdf_obj_next (vers)) { x = atoi (hdf_obj_name (vers)); if (x > version) version = x; } /* new version */ version++; snprintf (fpath, sizeof (fpath), "%s,%d", path, version); fd = open (fpath, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); if (fd == -1) { err = nerr_raise_errno (NERR_IO, "Unable to create file %s", fpath); break; } l = strlen(data); w = write (fd, data, l); if (w != l) { err = nerr_raise_errno (NERR_IO, "Unable to write file %s", fpath); close (fd); break; } close (fd); snprintf (buf, sizeof(buf), "Versions.%d.Log", version); err = hdf_set_value (meta, buf, log); if (err) break; snprintf (buf, sizeof(buf), "Versions.%d.User", version); err = hdf_set_value (meta, buf, user); if (err) break; snprintf (buf, sizeof(buf), "Versions.%d.Date", version); err = hdf_set_int_value (meta, buf, ne_timef()); if (err) break; err = _meta_save (path, meta); } while (0); rcfs_unlock (lock); hdf_destroy (&meta); return nerr_pass (err); } NEOERR * rcfs_lock (const char *path, int *lock) { NEOERR *err; char fpath[_POSIX_PATH_MAX]; snprintf (fpath, sizeof (fpath), "%s,lock", path); err = fCreate (lock, fpath); if (err) return nerr_pass (err); err = fLock (*lock); if (err) { fDestroy (*lock); return nerr_pass (err); } return STATUS_OK; } void rcfs_unlock (int lock) { fUnlock (lock); fDestroy (lock); } NEOERR * rcfs_listdir (const char *path, ULIST **list) { NEOERR *err; DIR *dp; ULIST *files; struct dirent *de; int l; char *f; *list = NULL; err = uListInit (&files, 10, 0); if (err) return nerr_pass (err); dp = opendir(path); if (dp == NULL) { uListDestroy(&files, ULIST_FREE); if (errno == ENOENT) return nerr_raise (NERR_NOT_FOUND, "Directory %s doesn't exist", path); return nerr_raise_errno (NERR_IO, "Unable to open directory %s", path); } while ((de = readdir (dp)) != NULL) { l = strlen (de->d_name); if (l>4 && !strcmp (de->d_name+l-4, ",log")) { f = (char *) malloc ((l-3) * sizeof(char)); if (f == NULL) { uListDestroy (&files, ULIST_FREE); closedir(dp); return nerr_raise (NERR_NOMEM, "Unable to allocate memory for filename %s", de->d_name); } strncpy (f, de->d_name, l-4); f[l-4] = '\0'; err = uListAppend (files, f); if (err) { free (f); uListDestroy (&files, ULIST_FREE); closedir(dp); return nerr_pass (err); } } } *list = files; closedir(dp); return STATUS_OK; }