1 /* sharefile.c -- open files just once.
2 Copyright (C) 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>.
18 /* config.h always comes first. */
27 #include <sys/types.h>
32 #include "stdio-safer.h"
35 #include "sharefile.h"
41 DefaultHashTableSize = 11
52 * We cannot use the name to determine that two strings represent the
53 * same file, since that test would be fooled by symbolic links.
54 * Instead we use the device and inode number.
56 * However, we remember the name of each file that we opened. This
57 * allows us to issue a fatal error message when (flushing and)
58 * closing a file fails.
64 char *name; /* not the only name for this file; error messages only */
70 entry_comparator (const void *av, const void *bv)
72 const struct SharefileEntry *a=av, *b=bv;
73 return (a->inode == b->inode) && (a->device == b->device);
79 struct SharefileEntry *p = pv;
82 if (0 != fclose (p->fp))
83 fatal_nontarget_file_error (errno, p->name);
90 entry_hashfunc (const void *pv, size_t buckets)
92 const struct SharefileEntry *p = pv;
93 return (p->device ^ p->inode) % buckets;
99 sharefile_init (const char *mode)
103 struct sharefile *p = malloc (sizeof (struct sharefile));
106 p->mode = strdup (mode);
109 p->table = hash_initialize (DefaultHashTableSize, NULL,
132 sharefile_destroy (sharefile_handle pv)
134 struct sharefile *p = pv;
136 hash_free (p->table);
141 sharefile_fopen (sharefile_handle h, const char *filename)
143 struct sharefile *p = h;
144 struct SharefileEntry *new_entry;
146 new_entry = malloc (sizeof (struct SharefileEntry));
150 new_entry->name = strdup (filename);
151 if (NULL == new_entry->name)
157 if (NULL == (new_entry->fp = fopen_safer (filename, p->mode)))
165 const int fd = fileno (new_entry->fp);
168 set_cloexec_flag (fd, true);
169 if (fstat (fd, &st) < 0)
171 entry_free (new_entry);
178 new_entry->device = st.st_dev;
179 new_entry->inode = st.st_ino;
181 existing = hash_lookup (p->table, new_entry);
182 if (existing) /* We have previously opened that file. */
184 entry_free (new_entry); /* don't need new_entry. */
185 return ((const struct SharefileEntry*)existing)->fp;
187 else /* We didn't open it already */
189 if (hash_insert (p->table, new_entry))
191 return new_entry->fp;
193 else /* failed to insert in hashtable. */
195 const int save_errno = errno;
196 entry_free (new_entry);