1 /*
2 * save.c - write the cache struct to disk
3 *
4 * Copyright (C) 2001 by Andreas Dilger
5 * Copyright (C) 2003 Theodore Ts'o
6 *
7 * %Begin-Header%
8 * This file may be redistributed under the terms of the
9 * GNU Lesser General Public License.
10 * %End-Header%
11 */
12
13 #include <stdio.h>
14 #include <string.h>
15 #include <stdlib.h>
16 #include <unistd.h>
17 #include <sys/types.h>
18 #ifdef HAVE_SYS_STAT_H
19 #include <sys/stat.h>
20 #endif
21 #ifdef HAVE_ERRNO_H
22 #include <errno.h>
23 #endif
24
25 #include "closestream.h"
26 #include "fileutils.h"
27
28 #include "blkidP.h"
29
30
31 static void save_quoted(const char *data, FILE *file)
32 {
33 const char *p;
34
35 fputc('"', file);
36 for (p = data; p && *p; p++) {
37 if ((unsigned char) *p == 0x22 || /* " */
38 (unsigned char) *p == 0x5c) /* \ */
39 fputc('\\', file);
40
41 fputc(*p, file);
42 }
43 fputc('"', file);
44 }
45 static int save_dev(blkid_dev dev, FILE *file)
46 {
47 struct list_head *p;
48
49 if (!dev || dev->bid_name[0] != '/')
50 return 0;
51
52 DBG(SAVE, ul_debug("device %s, type %s", dev->bid_name, dev->bid_type ?
53 dev->bid_type : "(null)"));
54
55 fprintf(file, "<device DEVNO=\"0x%04lx\" TIME=\"%lld.%lld\"",
56 (unsigned long) dev->bid_devno,
57 (long long) dev->bid_time,
58 (long long) dev->bid_utime);
59
60 if (dev->bid_pri)
61 fprintf(file, " PRI=\"%d\"", dev->bid_pri);
62
63 list_for_each(p, &dev->bid_tags) {
64 blkid_tag tag = list_entry(p, struct blkid_struct_tag, bit_tags);
65
66 fputc(' ', file); /* space between tags */
67 fputs(tag->bit_name, file); /* tag NAME */
68 fputc('=', file); /* separator between NAME and VALUE */
69 save_quoted(tag->bit_val, file); /* tag "VALUE" */
70 }
71 fprintf(file, ">%s</device>\n", dev->bid_name);
72
73 return 0;
74 }
75
76 /*
77 * Write out the cache struct to the cache file on disk.
78 */
79 int blkid_flush_cache(blkid_cache cache)
80 {
81 struct list_head *p;
82 char *tmp = NULL;
83 char *opened = NULL;
84 char *filename;
85 FILE *file = NULL;
86 int fd, ret = 0;
87 struct stat st;
88
89 if (list_empty(&cache->bic_devs) ||
90 !(cache->bic_flags & BLKID_BIC_FL_CHANGED)) {
91 DBG(SAVE, ul_debug("skipping cache file write"));
92 return 0;
93 }
94
95 filename = cache->bic_filename ? cache->bic_filename :
96 blkid_get_cache_filename(NULL);
97 if (!filename)
98 return -BLKID_ERR_PARAM;
99
100 if (strncmp(filename,
101 BLKID_RUNTIME_DIR "/", sizeof(BLKID_RUNTIME_DIR)) == 0) {
102
103 /* default destination, create the directory if necessary */
104 if (stat(BLKID_RUNTIME_DIR, &st)
105 && errno == ENOENT
106 && mkdir(BLKID_RUNTIME_DIR, S_IWUSR|
107 S_IRUSR|S_IRGRP|S_IROTH|
108 S_IXUSR|S_IXGRP|S_IXOTH) != 0
109 && errno != EEXIST) {
110 DBG(SAVE, ul_debug("can't create %s directory for cache file",
111 BLKID_RUNTIME_DIR));
112 return 0;
113 }
114 }
115
116 /* If we can't write to the cache file, then don't even try */
117 if (((ret = stat(filename, &st)) < 0 && errno != ENOENT) ||
118 (ret == 0 && access(filename, W_OK) < 0)) {
119 DBG(SAVE, ul_debug("can't write to cache file %s", filename));
120 return 0;
121 }
122
123 /*
124 * Try and create a temporary file in the same directory so
125 * that in case of error we don't overwrite the cache file.
126 * If the cache file doesn't yet exist, it isn't a regular
127 * file (e.g. /dev/null or a socket), or we couldn't create
128 * a temporary file then we open it directly.
129 */
130 if (ret == 0 && S_ISREG(st.st_mode)) {
131 size_t len = strlen(filename) + 8;
132 tmp = malloc(len);
133 if (tmp) {
134 snprintf(tmp, len, "%s-XXXXXX", filename);
135 fd = mkstemp_cloexec(tmp);
136 if (fd >= 0) {
137 if (fchmod(fd, 0644) != 0)
138 DBG(SAVE, ul_debug("%s: fchmod failed", filename));
139 else if ((file = fdopen(fd, "w" UL_CLOEXECSTR)))
140 opened = tmp;
141 if (!file)
142 close(fd);
143 }
144 }
145 }
146
147 if (!file) {
148 file = fopen(filename, "w" UL_CLOEXECSTR);
149 opened = filename;
150 }
151
152 DBG(SAVE, ul_debug("writing cache file %s (really %s)",
153 filename, opened));
154
155 if (!file) {
156 ret = errno;
157 goto errout;
158 }
159
160 list_for_each(p, &cache->bic_devs) {
161 blkid_dev dev = list_entry(p, struct blkid_struct_dev, bid_devs);
162 if (!dev->bid_type || (dev->bid_flags & BLKID_BID_FL_REMOVABLE))
163 continue;
164 if ((ret = save_dev(dev, file)) < 0)
165 break;
166 }
167
168 if (ret >= 0) {
169 cache->bic_flags &= ~BLKID_BIC_FL_CHANGED;
170 ret = 1;
171 }
172
173 if (close_stream(file) != 0)
174 DBG(SAVE, ul_debug("write failed: %s", filename));
175
176 if (opened != filename) {
177 if (ret < 0) {
178 unlink(opened);
179 DBG(SAVE, ul_debug("unlinked temp cache %s", opened));
180 } else {
181 char *backup;
182 size_t len = strlen(filename) + 5;
183
184 backup = malloc(len);
185 if (backup) {
186 snprintf(backup, len, "%s.old", filename);
187 unlink(backup);
188 if (link(filename, backup)) {
189 DBG(SAVE, ul_debug("can't link %s to %s",
190 filename, backup));
191 }
192 free(backup);
193 }
194 if (rename(opened, filename)) {
195 ret = errno;
196 DBG(SAVE, ul_debug("can't rename %s to %s",
197 opened, filename));
198 } else {
199 DBG(SAVE, ul_debug("moved temp cache %s", opened));
200 }
201 }
202 }
203
204 errout:
205 free(tmp);
206 if (filename != cache->bic_filename)
207 free(filename);
208 return ret;
209 }
210
211 #ifdef TEST_PROGRAM
212 int main(int argc, char **argv)
213 {
214 blkid_cache cache = NULL;
215 int ret;
216
217 blkid_init_debug(BLKID_DEBUG_ALL);
218 if (argc != 2) {
219 fprintf(stderr, "Usage: %s [filename]\n"
220 "Test loading/saving a cache (filename)\n", argv[0]);
221 exit(1);
222 }
223
224 if ((ret = blkid_get_cache(&cache, "/dev/null")) != 0) {
225 fprintf(stderr, "%s: error creating cache (%d)\n",
226 argv[0], ret);
227 exit(1);
228 }
229 if ((ret = blkid_probe_all(cache)) < 0) {
230 fprintf(stderr, "error (%d) probing devices\n", ret);
231 exit(1);
232 }
233 cache->bic_filename = strdup(argv[1]);
234
235 if ((ret = blkid_flush_cache(cache)) < 0) {
236 fprintf(stderr, "error (%d) saving cache\n", ret);
237 exit(1);
238 }
239
240 blkid_put_cache(cache);
241
242 return ret;
243 }
244 #endif