be1a0127493586d42262c022d705c0c2e2657d2d
[pspp] / src / data / make-file.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2004, 2010 Free Software Foundation, Inc.
3
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.
8
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.
13
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/>. */
16
17 #include <config.h>
18
19 #include "data/make-file.h"
20
21 #include <assert.h>
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <sys/stat.h>
28 #include <unistd.h>
29
30 #include "data/file-name.h"
31 #include "data/file-handle-def.h"
32 #include "libpspp/ll.h"
33 #include "libpspp/message.h"
34
35 #include "gl/fatal-signal.h"
36 #include "gl/tempname.h"
37 #include "gl/xalloc.h"
38 #include "gl/xvasprintf.h"
39
40 #include "gettext.h"
41 #define _(msgid) gettext (msgid)
42
43 struct replace_file
44   {
45     struct ll ll;
46     char *file_name;
47     char *tmp_name;
48   };
49  
50 static struct ll_list all_files = LL_INITIALIZER (all_files);
51
52 static void free_replace_file (struct replace_file *);
53 static void unlink_replace_files (void);
54
55 struct replace_file *
56 replace_file_start (const struct file_handle *fh, const char *mode,
57                     mode_t permissions, FILE **fp, char **tmp_name)
58 {
59   static bool registered;
60   struct stat s;
61   struct replace_file *rf;
62   int fd;
63   int saved_errno = errno;
64
65   const char *file_name = fh_get_file_name (fh);
66
67   /* If FILE_NAME represents a special file, write to it directly
68      instead of trying to replace it. */
69   if (stat (file_name, &s) == 0 && !S_ISREG (s.st_mode))
70     {
71       /* Open file descriptor. */
72       fd = open (file_name, O_WRONLY);
73       if (fd < 0)
74         {
75           saved_errno = errno;     
76           msg (ME, _("Opening %s for writing: %s."),
77                file_name, strerror (saved_errno));
78           return NULL;
79         }
80
81       /* Open file as stream. */
82       *fp = fdopen (fd, mode);
83       if (*fp == NULL)
84         {
85           saved_errno = errno;     
86           msg (ME, _("Opening stream for %s: %s."),
87                file_name, strerror (saved_errno));
88           close (fd);
89           return NULL;
90         }
91
92       rf = xmalloc (sizeof *rf);
93       rf->file_name = NULL;
94       rf->tmp_name = xstrdup (file_name);
95       if (tmp_name != NULL)
96         *tmp_name = rf->tmp_name;
97       return rf;
98     }
99
100   if (!registered)
101     {
102       at_fatal_signal (unlink_replace_files);
103       registered = true;
104     }
105   block_fatal_signals ();
106
107   rf = xmalloc (sizeof *rf);
108   rf->file_name = xstrdup (file_name);
109   for (;;)
110     {
111       /* Generate unique temporary file name. */
112       rf->tmp_name = xasprintf ("%s.tmpXXXXXX", file_name);
113       if (gen_tempname (rf->tmp_name, 0, 0600, GT_NOCREATE) < 0)
114         {
115           saved_errno = errno;
116           msg (ME, _("Creating temporary file to replace %s: %s."),
117                rf->file_name, strerror (saved_errno));
118           goto error;
119         }
120
121       /* Create file by that name. */
122       fd = open (rf->tmp_name, O_WRONLY | O_CREAT | O_EXCL | O_BINARY, permissions);
123       if (fd >= 0)
124         break;
125       if (errno != EEXIST)
126         {
127           saved_errno = errno;
128           msg (ME, _("Creating temporary file %s: %s."),
129                rf->tmp_name, strerror (saved_errno));
130           goto error;
131         }
132       free (rf->tmp_name);
133     }
134
135
136   /* Open file as stream. */
137   *fp = fdopen (fd, mode);
138   if (*fp == NULL)
139     {
140       saved_errno = errno;
141       msg (ME, _("Opening stream for temporary file %s: %s."),
142            rf->tmp_name, strerror (saved_errno));
143       close (fd);
144       unlink (rf->tmp_name);
145       goto error;
146     }
147
148   /* Register file for deletion. */
149   ll_push_head (&all_files, &rf->ll);
150   unblock_fatal_signals ();
151
152   if (tmp_name != NULL)
153     *tmp_name = rf->tmp_name;
154
155   return rf;
156
157 error:
158   unblock_fatal_signals ();
159   free_replace_file (rf);
160   *fp = NULL;
161   if (tmp_name != NULL)
162     *tmp_name = NULL;
163   errno = saved_errno;
164   return NULL;
165 }
166
167 bool
168 replace_file_commit (struct replace_file *rf)
169 {
170   bool ok = true;
171
172   if (rf->file_name != NULL)
173     {
174       int save_errno;
175
176       block_fatal_signals ();
177       ok = rename (rf->tmp_name, rf->file_name) == 0;
178       save_errno = errno;
179       ll_remove (&rf->ll);
180       unblock_fatal_signals ();
181
182       if (!ok)
183         msg (ME, _("Replacing %s by %s: %s."),
184              rf->tmp_name, rf->file_name, strerror (save_errno));
185     }
186   else
187     {
188       /* Special file: no temporary file to rename. */
189     }
190   free_replace_file (rf);
191
192   return ok;
193 }
194
195 bool
196 replace_file_abort (struct replace_file *rf)
197 {
198   bool ok = true;
199
200   if (rf->file_name != NULL)
201     {
202       int save_errno;
203
204       block_fatal_signals ();
205       ok = unlink (rf->tmp_name) == 0;
206       save_errno = errno;
207       ll_remove (&rf->ll);
208       unblock_fatal_signals ();
209
210       if (!ok)
211         msg (ME, _("Removing %s: %s."), rf->tmp_name, strerror (save_errno));
212     }
213   else
214     {
215       /* Special file: no temporary file to unlink. */
216     }
217   free_replace_file (rf);
218
219   return ok;
220 }
221
222 static void
223 free_replace_file (struct replace_file *rf)
224 {
225   free (rf->file_name);
226   free (rf->tmp_name);
227   free (rf);
228 }
229
230 static void
231 unlink_replace_files (void)
232 {
233   struct replace_file *rf;
234
235   block_fatal_signals ();
236   ll_for_each (rf, struct replace_file, ll, &all_files)
237     {
238       /* We don't free_replace_file(RF) because calling free is unsafe
239          from an asynchronous signal handler. */
240       unlink (rf->tmp_name);
241     }
242   unblock_fatal_signals ();
243 }