]>
Commit | Line | Data |
---|---|---|
a0948ffe KZ |
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 | |
a0948ffe KZ |
21 | #ifdef HAVE_ERRNO_H |
22 | #include <errno.h> | |
23 | #endif | |
8d21d9ab SK |
24 | |
25 | #include "closestream.h" | |
4d751c00 | 26 | #include "fileutils.h" |
8d21d9ab | 27 | |
a0948ffe KZ |
28 | #include "blkidP.h" |
29 | ||
89e90ae7 KZ |
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 | } | |
a0948ffe KZ |
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 | ||
c62a6311 | 52 | DBG(SAVE, ul_debug("device %s, type %s", dev->bid_name, dev->bid_type ? |
a0948ffe KZ |
53 | dev->bid_type : "(null)")); |
54 | ||
3fbeb9ee | 55 | fprintf(file, "<device DEVNO=\"0x%04lx\" TIME=\"%lld.%lld\"", |
6c2f2b9d | 56 | (unsigned long) dev->bid_devno, |
3fbeb9ee SN |
57 | (long long) dev->bid_time, |
58 | (long long) dev->bid_utime); | |
6c2f2b9d | 59 | |
a0948ffe KZ |
60 | if (dev->bid_pri) |
61 | fprintf(file, " PRI=\"%d\"", dev->bid_pri); | |
89e90ae7 | 62 | |
a0948ffe KZ |
63 | list_for_each(p, &dev->bid_tags) { |
64 | blkid_tag tag = list_entry(p, struct blkid_struct_tag, bit_tags); | |
89e90ae7 KZ |
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" */ | |
a0948ffe KZ |
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; | |
b82590ad KZ |
83 | char *opened = NULL; |
84 | char *filename; | |
a0948ffe KZ |
85 | FILE *file = NULL; |
86 | int fd, ret = 0; | |
87 | struct stat st; | |
88 | ||
a0948ffe KZ |
89 | if (list_empty(&cache->bic_devs) || |
90 | !(cache->bic_flags & BLKID_BIC_FL_CHANGED)) { | |
c62a6311 | 91 | DBG(SAVE, ul_debug("skipping cache file write")); |
a0948ffe KZ |
92 | return 0; |
93 | } | |
94 | ||
b82590ad KZ |
95 | filename = cache->bic_filename ? cache->bic_filename : |
96 | blkid_get_cache_filename(NULL); | |
71107465 KZ |
97 | if (!filename) |
98 | return -BLKID_ERR_PARAM; | |
b82590ad | 99 | |
71107465 KZ |
100 | if (strncmp(filename, |
101 | BLKID_RUNTIME_DIR "/", sizeof(BLKID_RUNTIME_DIR)) == 0) { | |
b82590ad KZ |
102 | |
103 | /* default destination, create the directory if necessary */ | |
7317ba6d KZ |
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) { | |
c62a6311 | 110 | DBG(SAVE, ul_debug("can't create %s directory for cache file", |
7317ba6d KZ |
111 | BLKID_RUNTIME_DIR)); |
112 | return 0; | |
b82590ad KZ |
113 | } |
114 | } | |
a0948ffe KZ |
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)) { | |
c62a6311 | 119 | DBG(SAVE, ul_debug("can't write to cache file %s", filename)); |
a0948ffe KZ |
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)) { | |
82232495 KZ |
131 | size_t len = strlen(filename) + 8; |
132 | tmp = malloc(len); | |
a0948ffe | 133 | if (tmp) { |
82232495 | 134 | snprintf(tmp, len, "%s-XXXXXX", filename); |
4d751c00 | 135 | fd = mkstemp_cloexec(tmp); |
a0948ffe | 136 | if (fd >= 0) { |
ceaff03b | 137 | if (fchmod(fd, 0644) != 0) |
c62a6311 | 138 | DBG(SAVE, ul_debug("%s: fchmod failed", filename)); |
4000fc12 | 139 | else if ((file = fdopen(fd, "w" UL_CLOEXECSTR))) |
ceaff03b KZ |
140 | opened = tmp; |
141 | if (!file) | |
142 | close(fd); | |
a0948ffe | 143 | } |
a0948ffe KZ |
144 | } |
145 | } | |
146 | ||
147 | if (!file) { | |
4000fc12 | 148 | file = fopen(filename, "w" UL_CLOEXECSTR); |
a0948ffe KZ |
149 | opened = filename; |
150 | } | |
151 | ||
c62a6311 | 152 | DBG(SAVE, ul_debug("writing cache file %s (really %s)", |
a0948ffe KZ |
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); | |
49361dc4 | 162 | if (!dev->bid_type || (dev->bid_flags & BLKID_BID_FL_REMOVABLE)) |
a0948ffe KZ |
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 | ||
8d21d9ab | 173 | if (close_stream(file) != 0) |
c62a6311 | 174 | DBG(SAVE, ul_debug("write failed: %s", filename)); |
8d21d9ab | 175 | |
a0948ffe KZ |
176 | if (opened != filename) { |
177 | if (ret < 0) { | |
178 | unlink(opened); | |
c62a6311 | 179 | DBG(SAVE, ul_debug("unlinked temp cache %s", opened)); |
a0948ffe KZ |
180 | } else { |
181 | char *backup; | |
82232495 | 182 | size_t len = strlen(filename) + 5; |
a0948ffe | 183 | |
82232495 | 184 | backup = malloc(len); |
a0948ffe | 185 | if (backup) { |
82232495 | 186 | snprintf(backup, len, "%s.old", filename); |
a0948ffe | 187 | unlink(backup); |
5da42397 | 188 | if (link(filename, backup)) { |
c62a6311 | 189 | DBG(SAVE, ul_debug("can't link %s to %s", |
5da42397 KZ |
190 | filename, backup)); |
191 | } | |
a0948ffe KZ |
192 | free(backup); |
193 | } | |
c461b8d8 KZ |
194 | if (rename(opened, filename)) { |
195 | ret = errno; | |
c62a6311 | 196 | DBG(SAVE, ul_debug("can't rename %s to %s", |
c461b8d8 KZ |
197 | opened, filename)); |
198 | } else { | |
c62a6311 | 199 | DBG(SAVE, ul_debug("moved temp cache %s", opened)); |
c461b8d8 | 200 | } |
a0948ffe KZ |
201 | } |
202 | } | |
203 | ||
204 | errout: | |
0e4ed1aa | 205 | free(tmp); |
b82590ad KZ |
206 | if (filename != cache->bic_filename) |
207 | free(filename); | |
a0948ffe KZ |
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 | ||
0540ea54 | 217 | blkid_init_debug(BLKID_DEBUG_ALL); |
9175f390 | 218 | if (argc != 2) { |
a0948ffe KZ |
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 | } | |
e0a9b8cf | 233 | cache->bic_filename = strdup(argv[1]); |
a0948ffe KZ |
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 |