| /* |
| * 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; |
| } |
| |