1 /*
2  * Copyright 2001-2004 Brandon Long
3  * All Rights Reserved.
4  *
5  * ClearSilver Templating System
6  *
7  * This code is made available under the terms of the ClearSilver License.
8  * http://www.clearsilver.net/license.hdf
9  *
10  */
11 
12 /*
13  * revision-controlled file system (RCFS) with meta-info storage
14  */
15 
16 #include "cs_config.h"
17 
18 #include <limits.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <errno.h>
22 #include <unistd.h>
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <fcntl.h>
26 #include <dirent.h>
27 
28 #include "util/neo_misc.h"
29 #include "util/neo_err.h"
30 #include "util/neo_files.h"
31 #include "util/neo_hdf.h"
32 #include "util/ulocks.h"
33 #include "rcfs.h"
34 
rcfs_meta_load(const char * path,HDF ** meta)35 NEOERR * rcfs_meta_load (const char *path, HDF **meta)
36 {
37   NEOERR *err;
38   char fpath[_POSIX_PATH_MAX];
39   HDF *m;
40 
41   snprintf (fpath, sizeof(fpath), "%s,log", path);
42 
43   err = hdf_init (&m);
44   if (err) return nerr_pass (err);
45   err = hdf_read_file (m, fpath);
46   if (err)
47   {
48     hdf_destroy (&m);
49     return nerr_pass (err);
50   }
51   *meta = m;
52   return STATUS_OK;
53 }
54 
_meta_save(const char * path,HDF * meta)55 static NEOERR * _meta_save (const char *path, HDF *meta)
56 {
57   NEOERR *err;
58   char ftmp[_POSIX_PATH_MAX];
59   char fpath[_POSIX_PATH_MAX];
60 
61   snprintf (ftmp, sizeof(ftmp), "%s,log.tmp", path);
62   snprintf (fpath, sizeof(fpath), "%s,log", path);
63 
64   err = hdf_write_file (meta, ftmp);
65   if (err) return nerr_pass (err);
66   if (rename (ftmp, fpath) == -1)
67   {
68     unlink (ftmp);
69     return nerr_raise_errno (NERR_IO, "Unable to rename file %s", ftmp);
70   }
71 
72   return STATUS_OK;
73 }
74 
rcfs_meta_save(const char * path,HDF * meta)75 NEOERR * rcfs_meta_save (const char *path, HDF *meta)
76 {
77   NEOERR *err;
78   int lock;
79   HDF *m;
80 
81   err = rcfs_lock (path, &lock);
82   if (err) return nerr_pass (err);
83   do
84   {
85     err = rcfs_meta_load (path, &m);
86     if (err) break;
87     err = hdf_copy (m, "Meta", meta);
88     if (err) break;
89     err = _meta_save (path, m);
90   } while (0);
91 
92   rcfs_unlock (lock);
93   return nerr_pass (err);
94 }
95 
96 /* load a specified version of the file, version -1 is latest */
rcfs_load(const char * path,int version,char ** data)97 NEOERR * rcfs_load (const char *path, int version, char **data)
98 {
99   NEOERR *err;
100   char fpath[_POSIX_PATH_MAX];
101 
102   if (version == -1)
103   {
104     HDF *meta, *vers;
105     int x;
106 
107     err = rcfs_meta_load (path, &meta);
108     if (err) return nerr_pass (err);
109     for (vers = hdf_get_child (meta, "Versions");
110 	vers;
111 	vers = hdf_obj_next (vers))
112     {
113       x = atoi (hdf_obj_name (vers));
114       if (x > version) version = x;
115     }
116     hdf_destroy (&meta);
117   }
118   snprintf (fpath, sizeof (fpath), "%s,%d", path, version);
119   err = ne_load_file (fpath, data);
120   return nerr_pass (err);
121 }
122 
rcfs_save(const char * path,const char * data,const char * user,const char * log)123 NEOERR * rcfs_save (const char *path, const char *data, const char *user,
124                     const char *log)
125 {
126   NEOERR *err;
127   HDF *meta = NULL, *vers;
128   char fpath[_POSIX_PATH_MAX];
129   char buf[256];
130   int version = 0;
131   int fd;
132   int lock;
133   int x, l, w;
134 
135   err = rcfs_lock (path, &lock);
136   if (err) return nerr_pass (err);
137   do
138   {
139     err = rcfs_meta_load (path, &meta);
140     if (err && nerr_handle (&err, NERR_NOT_FOUND))
141     {
142       /* new file! */
143       err = hdf_init (&meta);
144     }
145     if (err) return nerr_pass (err);
146     for (vers = hdf_get_child (meta, "Versions");
147 	vers;
148 	vers = hdf_obj_next (vers))
149     {
150       x = atoi (hdf_obj_name (vers));
151       if (x > version) version = x;
152     }
153 
154     /* new version */
155     version++;
156     snprintf (fpath, sizeof (fpath), "%s,%d", path, version);
157     fd = open (fpath, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
158     if (fd == -1)
159     {
160       err = nerr_raise_errno (NERR_IO, "Unable to create file %s", fpath);
161       break;
162     }
163     l = strlen(data);
164     w = write (fd, data, l);
165     if (w != l)
166     {
167       err = nerr_raise_errno (NERR_IO, "Unable to write file %s", fpath);
168       close (fd);
169       break;
170     }
171     close (fd);
172     snprintf (buf, sizeof(buf), "Versions.%d.Log", version);
173     err = hdf_set_value (meta, buf, log);
174     if (err) break;
175     snprintf (buf, sizeof(buf), "Versions.%d.User", version);
176     err = hdf_set_value (meta, buf, user);
177     if (err) break;
178     snprintf (buf, sizeof(buf), "Versions.%d.Date", version);
179     err = hdf_set_int_value (meta, buf, ne_timef());
180     if (err) break;
181     err = _meta_save (path, meta);
182   } while (0);
183 
184   rcfs_unlock (lock);
185   hdf_destroy (&meta);
186   return nerr_pass (err);
187 }
188 
rcfs_lock(const char * path,int * lock)189 NEOERR * rcfs_lock (const char *path, int *lock)
190 {
191   NEOERR *err;
192   char fpath[_POSIX_PATH_MAX];
193 
194   snprintf (fpath, sizeof (fpath), "%s,lock", path);
195   err = fCreate (lock, fpath);
196   if (err) return nerr_pass (err);
197   err = fLock (*lock);
198   if (err)
199   {
200     fDestroy (*lock);
201     return nerr_pass (err);
202   }
203   return STATUS_OK;
204 }
205 
rcfs_unlock(int lock)206 void rcfs_unlock (int lock)
207 {
208   fUnlock (lock);
209   fDestroy (lock);
210 }
211 
rcfs_listdir(const char * path,ULIST ** list)212 NEOERR * rcfs_listdir (const char *path, ULIST **list)
213 {
214   NEOERR *err;
215   DIR *dp;
216   ULIST *files;
217   struct dirent *de;
218   int l;
219   char *f;
220 
221   *list = NULL;
222   err = uListInit (&files, 10, 0);
223   if (err) return nerr_pass (err);
224   dp = opendir(path);
225   if (dp == NULL)
226   {
227     uListDestroy(&files, ULIST_FREE);
228     if (errno == ENOENT)
229       return nerr_raise (NERR_NOT_FOUND, "Directory %s doesn't exist", path);
230     return nerr_raise_errno (NERR_IO, "Unable to open directory %s", path);
231   }
232   while ((de = readdir (dp)) != NULL)
233   {
234     l = strlen (de->d_name);
235     if (l>4 && !strcmp (de->d_name+l-4, ",log"))
236     {
237       f = (char *) malloc ((l-3) * sizeof(char));
238       if (f == NULL)
239       {
240 	uListDestroy (&files, ULIST_FREE);
241 	closedir(dp);
242 	return nerr_raise (NERR_NOMEM,
243 	    "Unable to allocate memory for filename %s", de->d_name);
244       }
245       strncpy (f, de->d_name, l-4);
246       f[l-4] = '\0';
247       err = uListAppend (files, f);
248       if (err)
249       {
250 	free (f);
251 	uListDestroy (&files, ULIST_FREE);
252 	closedir(dp);
253 	return nerr_pass (err);
254       }
255     }
256   }
257   *list = files;
258   closedir(dp);
259 
260   return STATUS_OK;
261 }
262 
263