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