e4520f411eba6c3c897317117a5801f2b5014065
[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           errno = saved_errno;
76           return NULL;
77         }
78
79       /* Open file as stream. */
80       *fp = fdopen (fd, mode);
81       if (*fp == NULL)
82         {
83           saved_errno = errno;     
84           msg (ME, _("Opening stream for %s: %s."),
85                file_name, strerror (saved_errno));
86           close (fd);
87           errno = saved_errno;
88           return NULL;
89         }
90
91       rf = xmalloc (sizeof *rf);
92       rf->file_name = NULL;
93       rf->tmp_name = xstrdup (file_name);
94       if (tmp_name != NULL)
95         *tmp_name = rf->tmp_name;
96       return rf;
97     }
98
99   if (!registered)
100     {
101       at_fatal_signal (unlink_replace_files);
102       registered = true;
103     }
104   block_fatal_signals ();
105
106   rf = xmalloc (sizeof *rf);
107   rf->file_name = xstrdup (file_name);
108   for (;;)
109     {
110       /* Generate unique temporary file name. */
111       rf->tmp_name = xasprintf ("%s.tmpXXXXXX", file_name);
112       if (gen_tempname (rf->tmp_name, 0, 0600, GT_NOCREATE) < 0)
113         {
114           msg (ME, _("Creating temporary file to replace %s: %s."),
115                rf->file_name, strerror (errno));
116           saved_errno = errno;
117           goto error;
118         }
119
120       /* Create file by that name. */
121       fd = open (rf->tmp_name, O_WRONLY | O_CREAT | O_EXCL | O_BINARY, permissions);
122       if (fd >= 0)
123         break;
124       if (errno != EEXIST)
125         {
126           msg (ME, _("Creating temporary file %s: %s."),
127                rf->tmp_name, strerror (errno));
128           saved_errno = errno;
129           goto error;
130         }
131       free (rf->tmp_name);
132     }
133
134
135   /* Open file as stream. */
136   *fp = fdopen (fd, mode);
137   if (*fp == NULL)
138     {
139       msg (ME, _("Opening stream for temporary file %s: %s."),
140            rf->tmp_name, strerror (errno));
141       close (fd);
142       unlink (rf->tmp_name);
143       saved_errno = errno;
144       goto error;
145     }
146
147   /* Register file for deletion. */
148   ll_push_head (&all_files, &rf->ll);
149   unblock_fatal_signals ();
150
151   if (tmp_name != NULL)
152     *tmp_name = rf->tmp_name;
153
154   return rf;
155
156 error:
157   unblock_fatal_signals ();
158   free_replace_file (rf);
159   *fp = NULL;
160   if (tmp_name != NULL)
161     *tmp_name = NULL;
162   errno = saved_errno;
163   return NULL;
164 }
165
166 bool
167 replace_file_commit (struct replace_file *rf)
168 {
169   bool ok = true;
170
171   if (rf->file_name != NULL)
172     {
173       int save_errno;
174
175       block_fatal_signals ();
176       ok = rename (rf->tmp_name, rf->file_name) == 0;
177       save_errno = errno;
178       ll_remove (&rf->ll);
179       unblock_fatal_signals ();
180
181       if (!ok)
182         msg (ME, _("Replacing %s by %s: %s."),
183              rf->tmp_name, rf->file_name, strerror (save_errno));
184     }
185   else
186     {
187       /* Special file: no temporary file to rename. */
188     }
189   free_replace_file (rf);
190
191   return ok;
192 }
193
194 bool
195 replace_file_abort (struct replace_file *rf)
196 {
197   bool ok = true;
198
199   if (rf->file_name != NULL)
200     {
201       int save_errno;
202
203       block_fatal_signals ();
204       ok = unlink (rf->tmp_name) == 0;
205       save_errno = errno;
206       ll_remove (&rf->ll);
207       unblock_fatal_signals ();
208
209       if (!ok)
210         msg (ME, _("Removing %s: %s."), rf->tmp_name, strerror (save_errno));
211     }
212   else
213     {
214       /* Special file: no temporary file to unlink. */
215     }
216   free_replace_file (rf);
217
218   return ok;
219 }
220
221 static void
222 free_replace_file (struct replace_file *rf)
223 {
224   free (rf->file_name);
225   free (rf->tmp_name);
226   free (rf);
227 }
228
229 static void
230 unlink_replace_files (void)
231 {
232   struct replace_file *rf;
233
234   block_fatal_signals ();
235   ll_for_each (rf, struct replace_file, ll, &all_files)
236     {
237       /* We don't free_replace_file(RF) because calling free is unsafe
238          from an asynchronous signal handler. */
239       unlink (rf->tmp_name);
240     }
241   unblock_fatal_signals ();
242 }