]>
Commit | Line | Data |
---|---|---|
e5fd101c PS |
1 | autofs-5.0.4 - dont umount existing direct mount on master re-read |
2 | ||
3 | From: Ian Kent <raven@themaw.net> | |
4 | ||
5 | Since direct mounts can have multiple entries in the master map they each | |
6 | have an instance associated with them. If one entry changes, such as the | |
7 | mount options, the instance comparison test fails and a new instance is | |
8 | added. This causes autofs to get confused because there are now two | |
9 | entries that contain the same mount information in different internal | |
10 | caches. There are several consequences of this, most of which are just | |
11 | noise in the log, but it also causes confuion for the expiration of mounts | |
12 | since, for an active mount, the old cache entry can't be pruned until it's | |
13 | umounted. Also, the map caches were not being properly pruned. | |
14 | --- | |
15 | ||
16 | CHANGELOG | 1 | |
17 | daemon/lookup.c | 160 ++++++++++++++++++++++++++++----------------------- | |
18 | daemon/state.c | 90 +++++++++++++++++++++-------- | |
19 | include/automount.h | 1 | |
20 | 4 files changed, 156 insertions(+), 96 deletions(-) | |
21 | ||
22 | ||
23 | diff --git a/CHANGELOG b/CHANGELOG | |
24 | index 387af5e..7ca45fd 100644 | |
25 | --- a/CHANGELOG | |
26 | +++ b/CHANGELOG | |
27 | @@ -43,6 +43,7 @@ | |
28 | - use percent hack for master map keys. | |
29 | - use intr option as hosts mount default. | |
30 | - fix kernel includes. | |
31 | +- dont umount existing direct mount on master re-read. | |
32 | ||
33 | 4/11/2008 autofs-5.0.4 | |
34 | ----------------------- | |
35 | diff --git a/daemon/lookup.c b/daemon/lookup.c | |
36 | index fd2ce55..bc94655 100644 | |
37 | --- a/daemon/lookup.c | |
38 | +++ b/daemon/lookup.c | |
39 | @@ -1016,96 +1016,114 @@ static char *make_fullpath(const char *root, const char *key) | |
40 | return path; | |
41 | } | |
42 | ||
43 | -int lookup_prune_cache(struct autofs_point *ap, time_t age) | |
44 | +void lookup_prune_one_cache(struct autofs_point *ap, struct mapent_cache *mc, time_t age) | |
45 | { | |
46 | - struct master_mapent *entry = ap->entry; | |
47 | - struct map_source *map; | |
48 | - struct mapent_cache *mc; | |
49 | struct mapent *me, *this; | |
50 | char *path; | |
51 | int status = CHE_FAIL; | |
52 | ||
53 | - pthread_cleanup_push(master_source_lock_cleanup, entry); | |
54 | - master_source_readlock(entry); | |
55 | + me = cache_enumerate(mc, NULL); | |
56 | + while (me) { | |
57 | + struct mapent *valid; | |
58 | + char *key = NULL, *next_key = NULL; | |
59 | ||
60 | - map = entry->maps; | |
61 | - while (map) { | |
62 | - /* Is the map stale */ | |
63 | - if (!map->stale) { | |
64 | - map = map->next; | |
65 | + if (me->age >= age) { | |
66 | + me = cache_enumerate(mc, me); | |
67 | continue; | |
68 | } | |
69 | - mc = map->mc; | |
70 | - pthread_cleanup_push(cache_lock_cleanup, mc); | |
71 | - cache_readlock(mc); | |
72 | - me = cache_enumerate(mc, NULL); | |
73 | - while (me) { | |
74 | - char *key = NULL, *next_key = NULL; | |
75 | ||
76 | - if (me->age >= age) { | |
77 | - me = cache_enumerate(mc, me); | |
78 | - continue; | |
79 | - } | |
80 | + key = strdup(me->key); | |
81 | + me = cache_enumerate(mc, me); | |
82 | + if (!key || *key == '*') { | |
83 | + if (key) | |
84 | + free(key); | |
85 | + continue; | |
86 | + } | |
87 | ||
88 | - key = strdup(me->key); | |
89 | - me = cache_enumerate(mc, me); | |
90 | - if (!key || *key == '*') { | |
91 | - if (key) | |
92 | - free(key); | |
93 | - continue; | |
94 | - } | |
95 | + path = make_fullpath(ap->path, key); | |
96 | + if (!path) { | |
97 | + warn(ap->logopt, "can't malloc storage for path"); | |
98 | + free(key); | |
99 | + continue; | |
100 | + } | |
101 | ||
102 | - path = make_fullpath(ap->path, key); | |
103 | - if (!path) { | |
104 | - warn(ap->logopt, | |
105 | - "can't malloc storage for path"); | |
106 | - free(key); | |
107 | - continue; | |
108 | - } | |
109 | + /* | |
110 | + * If this key has another valid entry we want to prune it, | |
111 | + * even if it's a mount, as the valid entry will take the | |
112 | + * mount if it is a direct mount or it's just a stale indirect | |
113 | + * cache entry. | |
114 | + */ | |
115 | + valid = lookup_source_valid_mapent(ap, key, LKP_DISTINCT); | |
116 | + if (!valid && | |
117 | + is_mounted(_PATH_MOUNTED, path, MNTS_REAL)) { | |
118 | + debug(ap->logopt, | |
119 | + "prune check posponed, %s mounted", path); | |
120 | + free(key); | |
121 | + free(path); | |
122 | + continue; | |
123 | + } | |
124 | + if (valid) | |
125 | + cache_unlock(valid->mc); | |
126 | ||
127 | - if (is_mounted(_PATH_MOUNTED, path, MNTS_REAL)) { | |
128 | - debug(ap->logopt, | |
129 | - "prune check posponed, %s mounted", path); | |
130 | - free(key); | |
131 | - free(path); | |
132 | - continue; | |
133 | - } | |
134 | + if (me) | |
135 | + next_key = strdup(me->key); | |
136 | ||
137 | - if (me) | |
138 | - next_key = strdup(me->key); | |
139 | + cache_unlock(mc); | |
140 | ||
141 | + cache_writelock(mc); | |
142 | + this = cache_lookup_distinct(mc, key); | |
143 | + if (!this) { | |
144 | cache_unlock(mc); | |
145 | + goto next; | |
146 | + } | |
147 | ||
148 | - cache_writelock(mc); | |
149 | - this = cache_lookup_distinct(mc, key); | |
150 | - if (!this) { | |
151 | - cache_unlock(mc); | |
152 | - goto next; | |
153 | - } | |
154 | - | |
155 | - if (!is_mounted(_PROC_MOUNTS, path, MNTS_AUTOFS)) { | |
156 | - status = CHE_FAIL; | |
157 | - if (this->ioctlfd == -1) | |
158 | - status = cache_delete(mc, key); | |
159 | - if (status != CHE_FAIL) { | |
160 | - if (ap->type == LKP_INDIRECT) { | |
161 | - if (ap->flags & MOUNT_FLAG_GHOST) | |
162 | - rmdir_path(ap, path, ap->dev); | |
163 | - } else | |
164 | - rmdir_path(ap, path, this->dev); | |
165 | - } | |
166 | + if (valid) | |
167 | + cache_delete(mc, key); | |
168 | + else if (!is_mounted(_PROC_MOUNTS, path, MNTS_AUTOFS)) { | |
169 | + status = CHE_FAIL; | |
170 | + if (this->ioctlfd == -1) | |
171 | + status = cache_delete(mc, key); | |
172 | + if (status != CHE_FAIL) { | |
173 | + if (ap->type == LKP_INDIRECT) { | |
174 | + if (ap->flags & MOUNT_FLAG_GHOST) | |
175 | + rmdir_path(ap, path, ap->dev); | |
176 | + } else | |
177 | + rmdir_path(ap, path, this->dev); | |
178 | } | |
179 | - cache_unlock(mc); | |
180 | + } | |
181 | + cache_unlock(mc); | |
182 | ||
183 | next: | |
184 | - cache_readlock(mc); | |
185 | - if (next_key) { | |
186 | - me = cache_lookup_distinct(mc, next_key); | |
187 | - free(next_key); | |
188 | - } | |
189 | - free(key); | |
190 | - free(path); | |
191 | + cache_readlock(mc); | |
192 | + if (next_key) { | |
193 | + me = cache_lookup_distinct(mc, next_key); | |
194 | + free(next_key); | |
195 | } | |
196 | + free(key); | |
197 | + free(path); | |
198 | + } | |
199 | + | |
200 | + return; | |
201 | +} | |
202 | + | |
203 | +int lookup_prune_cache(struct autofs_point *ap, time_t age) | |
204 | +{ | |
205 | + struct master_mapent *entry = ap->entry; | |
206 | + struct map_source *map; | |
207 | + | |
208 | + pthread_cleanup_push(master_source_lock_cleanup, entry); | |
209 | + master_source_readlock(entry); | |
210 | + | |
211 | + map = entry->maps; | |
212 | + while (map) { | |
213 | + /* Is the map stale */ | |
214 | + if (!map->stale) { | |
215 | + map = map->next; | |
216 | + continue; | |
217 | + } | |
218 | + pthread_cleanup_push(cache_lock_cleanup, map->mc); | |
219 | + cache_readlock(map->mc); | |
220 | + lookup_prune_one_cache(ap, map->mc, age); | |
221 | pthread_cleanup_pop(1); | |
222 | map->stale = 0; | |
223 | map = map->next; | |
224 | @@ -1124,7 +1142,6 @@ struct mapent *lookup_source_valid_mapent(struct autofs_point *ap, const char *k | |
225 | struct mapent_cache *mc; | |
226 | struct mapent *me = NULL; | |
227 | ||
228 | - master_source_readlock(entry); | |
229 | map = entry->maps; | |
230 | while (map) { | |
231 | /* | |
232 | @@ -1147,7 +1164,6 @@ struct mapent *lookup_source_valid_mapent(struct autofs_point *ap, const char *k | |
233 | cache_unlock(mc); | |
234 | map = map->next; | |
235 | } | |
236 | - master_source_unlock(entry); | |
237 | ||
238 | return me; | |
239 | } | |
240 | diff --git a/daemon/state.c b/daemon/state.c | |
241 | index 533e241..84ccba3 100644 | |
242 | --- a/daemon/state.c | |
243 | +++ b/daemon/state.c | |
244 | @@ -352,6 +352,68 @@ static void tree_mnts_cleanup(void *arg) | |
245 | return; | |
246 | } | |
247 | ||
248 | +static void do_readmap_mount(struct autofs_point *ap, struct mnt_list *mnts, | |
249 | + struct map_source *map, struct mapent *me, time_t now) | |
250 | +{ | |
251 | + struct mapent_cache *nc; | |
252 | + struct mapent *ne, *nested, *valid; | |
253 | + | |
254 | + nc = ap->entry->master->nc; | |
255 | + | |
256 | + ne = cache_lookup_distinct(nc, me->key); | |
257 | + if (!ne) { | |
258 | + nested = cache_partial_match(nc, me->key); | |
259 | + if (nested) { | |
260 | + error(ap->logopt, | |
261 | + "removing invalid nested null entry %s", | |
262 | + nested->key); | |
263 | + nested = cache_partial_match(nc, me->key); | |
264 | + if (nested) | |
265 | + cache_delete(nc, nested->key); | |
266 | + } | |
267 | + } | |
268 | + | |
269 | + if (me->age < now || (ne && map->master_line > ne->age)) { | |
270 | + /* | |
271 | + * The map instance may have changed, such as the map name or | |
272 | + * the mount options, but the direct map entry may still exist | |
273 | + * in one of the other maps. If so then update the new cache | |
274 | + * entry device and inode so we can find it at lookup. Later, | |
275 | + * the mount for the new cache entry will just update the | |
276 | + * timeout. | |
277 | + * | |
278 | + * TODO: how do we recognise these orphaned map instances. We | |
279 | + * can't just delete these instances when the cache becomes | |
280 | + * empty because that is a valid state for a master map entry. | |
281 | + * This is becuase of the requirement to continue running with | |
282 | + * an empty cache awaiting a map re-load. | |
283 | + */ | |
284 | + valid = lookup_source_valid_mapent(ap, me->key, LKP_DISTINCT); | |
285 | + if (valid) { | |
286 | + struct mapent_cache *vmc = valid->mc; | |
287 | + cache_unlock(vmc); | |
288 | + debug(ap->logopt, | |
289 | + "updating cache entry for valid direct trigger %s", | |
290 | + me->key); | |
291 | + cache_writelock(vmc); | |
292 | + valid = cache_lookup_distinct(vmc, me->key); | |
293 | + /* Take over the mount if there is one */ | |
294 | + valid->ioctlfd = me->ioctlfd; | |
295 | + me->ioctlfd = -1; | |
296 | + /* Set device and inode number of the new mapent */ | |
297 | + cache_set_ino_index(vmc, me->key, me->dev, me->ino); | |
298 | + cache_unlock(vmc); | |
299 | + } else if (!tree_is_mounted(mnts, me->key, MNTS_REAL)) | |
300 | + do_umount_autofs_direct(ap, mnts, me); | |
301 | + else | |
302 | + debug(ap->logopt, | |
303 | + "%s is mounted", me->key); | |
304 | + } else | |
305 | + do_mount_autofs_direct(ap, mnts, me); | |
306 | + | |
307 | + return; | |
308 | +} | |
309 | + | |
310 | static void *do_readmap(void *arg) | |
311 | { | |
312 | struct autofs_point *ap; | |
313 | @@ -398,7 +460,8 @@ static void *do_readmap(void *arg) | |
314 | lookup_prune_cache(ap, now); | |
315 | status = lookup_ghost(ap, ap->path); | |
316 | } else { | |
317 | - struct mapent *me, *ne, *nested; | |
318 | + struct mapent *me; | |
319 | + | |
320 | mnts = tree_make_mnt_tree(_PROC_MOUNTS, "/"); | |
321 | pthread_cleanup_push(tree_mnts_cleanup, mnts); | |
322 | pthread_cleanup_push(master_source_lock_cleanup, ap->entry); | |
323 | @@ -418,31 +481,10 @@ static void *do_readmap(void *arg) | |
324 | cache_readlock(mc); | |
325 | me = cache_enumerate(mc, NULL); | |
326 | while (me) { | |
327 | - ne = cache_lookup_distinct(nc, me->key); | |
328 | - if (!ne) { | |
329 | - nested = cache_partial_match(nc, me->key); | |
330 | - if (nested) { | |
331 | - error(ap->logopt, | |
332 | - "removing invalid nested null entry %s", | |
333 | - nested->key); | |
334 | - nested = cache_partial_match(nc, me->key); | |
335 | - if (nested) | |
336 | - cache_delete(nc, nested->key); | |
337 | - } | |
338 | - } | |
339 | - | |
340 | - /* TODO: check return of do_... */ | |
341 | - if (me->age < now || (ne && map->master_line > ne->age)) { | |
342 | - if (!tree_is_mounted(mnts, me->key, MNTS_REAL)) | |
343 | - do_umount_autofs_direct(ap, mnts, me); | |
344 | - else | |
345 | - debug(ap->logopt, | |
346 | - "%s is mounted", me->key); | |
347 | - } else | |
348 | - do_mount_autofs_direct(ap, mnts, me); | |
349 | - | |
350 | + do_readmap_mount(ap, mnts, map, me, now); | |
351 | me = cache_enumerate(mc, me); | |
352 | } | |
353 | + lookup_prune_one_cache(ap, map->mc, now); | |
354 | pthread_cleanup_pop(1); | |
355 | map->stale = 0; | |
356 | map = map->next; | |
357 | diff --git a/include/automount.h b/include/automount.h | |
358 | index d4675bd..ae517a7 100644 | |
359 | --- a/include/automount.h | |
360 | +++ b/include/automount.h | |
361 | @@ -238,6 +238,7 @@ int lookup_enumerate(struct autofs_point *ap, | |
362 | int lookup_ghost(struct autofs_point *ap, const char *root); | |
363 | int lookup_nss_mount(struct autofs_point *ap, struct map_source *source, const char *name, int name_len); | |
364 | void lookup_close_lookup(struct autofs_point *ap); | |
365 | +void lookup_prune_one_cache(struct autofs_point *ap, struct mapent_cache *mc, time_t age); | |
366 | int lookup_prune_cache(struct autofs_point *ap, time_t age); | |
367 | struct mapent *lookup_source_valid_mapent(struct autofs_point *ap, const char *key, unsigned int type); | |
368 | struct mapent *lookup_source_mapent(struct autofs_point *ap, const char *key, unsigned int type); |