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