1 /* Update a tar archive.
2
3 Copyright 1988-2023 Free Software Foundation, Inc.
4
5 This file is part of GNU tar.
6
7 GNU tar is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
11
12 GNU tar is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>. */
19
20 /* Implement the 'r', 'u' and 'A' options for tar. 'A' means that the
21 file names are tar files, and they should simply be appended to the end
22 of the archive. No attempt is made to record the reads from the args; if
23 they're on raw tape or something like that, it'll probably lose... */
24
25 #include <system.h>
26 #include <quotearg.h>
27 #include "common.h"
28
29 /* FIXME: This module should not directly handle the following variable,
30 instead, this should be done in buffer.c only. */
31 extern union block *current_block;
32
33 /* We've hit the end of the old stuff, and its time to start writing new
34 stuff to the tape. This involves seeking back one record and
35 re-writing the current record (which has been changed).
36 FIXME: Either eliminate it or move it to common.h.
37 */
38 bool time_to_start_writing;
39
40 /* Pointer to where we started to write in the first record we write out.
41 This is used if we can't backspace the output and have to null out the
42 first part of the record. */
43 char *output_start;
44
45 static bool acting_as_filter;
46
47 /* Catenate file FILE_NAME to the archive without creating a header for it.
48 It had better be a tar file or the archive is screwed. */
49 static void
50 append_file (char *file_name)
51 {
52 int handle = openat (chdir_fd, file_name, O_RDONLY | O_BINARY);
53
54 if (handle < 0)
55 {
56 open_error (file_name);
57 return;
58 }
59
60 while (true)
61 {
62 union block *start = find_next_block ();
63 size_t status = full_read (handle, start->buffer,
64 available_space_after (start));
65 if (status == 0)
66 {
67 if (errno == 0)
68 break;
69 read_fatal (file_name);
70 }
71 if (status == SAFE_READ_ERROR)
72 read_fatal (file_name);
73 if (status % BLOCKSIZE)
74 memset (start->buffer + status - status % BLOCKSIZE, 0,
75 BLOCKSIZE - status % BLOCKSIZE);
76 set_next_block_after (start + (status - 1) / BLOCKSIZE);
77 }
78
79 if (close (handle) != 0)
80 close_error (file_name);
81 }
82
83 /* If NAME is not a pattern, remove it from the namelist. Otherwise,
84 remove the FILE_NAME that matched it. Take care to look for exact
85 match when removing it. */
86 static void
87 remove_exact_name (struct name *name, char const *file_name)
88 {
89 if (name->is_wildcard)
90 {
91 struct name *match = name_scan (file_name, true);
92 name->found_count++;
93 if (match)
94 name = match;
95 else
96 return;
97 }
98
99 remname (name);
100 }
101
102 /* Implement the 'r' (add files to end of archive), and 'u' (add files
103 to end of archive if they aren't there, or are more up to date than
104 the version in the archive) commands. */
105 void
106 update_archive (void)
107 {
108 enum read_header previous_status = HEADER_STILL_UNREAD;
109 bool found_end = false;
110
111 name_gather ();
112 open_archive (ACCESS_UPDATE);
113 acting_as_filter = strcmp (archive_name_array[0], "-") == 0;
114 xheader_forbid_global ();
115
116 while (!found_end)
117 {
118 enum read_header status = read_header (¤t_header,
119 ¤t_stat_info,
120 read_header_auto);
121
122 switch (status)
123 {
124 case HEADER_STILL_UNREAD:
125 case HEADER_SUCCESS_EXTENDED:
126 abort ();
127
128 case HEADER_SUCCESS:
129 {
130 struct name *name;
131
132 decode_header (current_header, ¤t_stat_info,
133 ¤t_format, 0);
134 transform_stat_info (current_header->header.typeflag,
135 ¤t_stat_info);
136 archive_format = current_format;
137
138 if (subcommand_option == UPDATE_SUBCOMMAND
139 && (name = name_scan (current_stat_info.file_name, false)) != NULL)
140 {
141 struct stat s;
142
143 chdir_do (name->change_dir);
144 if (deref_stat (current_stat_info.file_name, &s) == 0)
145 {
146 if (S_ISDIR (s.st_mode))
147 {
148 char *p, *dirp = tar_savedir (current_stat_info.file_name, 1);
149 if (dirp)
150 {
151 namebuf_t nbuf = namebuf_create (current_stat_info.file_name);
152
153 for (p = dirp; *p; p += strlen (p) + 1)
154 addname (namebuf_name (nbuf, p),
155 name->change_dir, false, NULL);
156
157 namebuf_free (nbuf);
158 free (dirp);
159
160 remove_exact_name (name, current_stat_info.file_name);
161 }
162 }
163 else if (tar_timespec_cmp (get_stat_mtime (&s),
164 current_stat_info.mtime)
165 <= 0)
166 {
167 remove_exact_name (name, current_stat_info.file_name);
168 }
169 else if (name->is_wildcard)
170 addname (current_stat_info.file_name,
171 name->change_dir, false, NULL);
172 }
173 }
174
175 skim_member (acting_as_filter);
176 break;
177 }
178
179 case HEADER_ZERO_BLOCK:
180 current_block = current_header;
181 found_end = true;
182 break;
183
184 case HEADER_END_OF_FILE:
185 found_end = true;
186 break;
187
188 case HEADER_FAILURE:
189 set_next_block_after (current_header);
190 switch (previous_status)
191 {
192 case HEADER_STILL_UNREAD:
193 WARN ((0, 0, _("This does not look like a tar archive")));
194 FALLTHROUGH;
195 case HEADER_SUCCESS:
196 case HEADER_ZERO_BLOCK:
197 ERROR ((0, 0, _("Skipping to next header")));
198 FALLTHROUGH;
199 case HEADER_FAILURE:
200 break;
201
202 case HEADER_END_OF_FILE:
203 case HEADER_SUCCESS_EXTENDED:
204 abort ();
205 }
206 break;
207 }
208
209 tar_stat_destroy (¤t_stat_info);
210 previous_status = status;
211 }
212
213 reset_eof ();
214 time_to_start_writing = true;
215 output_start = current_block->buffer;
216
217 {
218 struct name const *p;
219 while ((p = name_from_list ()) != NULL)
220 {
221 char *file_name = p->name;
222 if (excluded_name (file_name, NULL))
223 continue;
224 if (interactive_option && !confirm ("add", file_name))
225 continue;
226 if (subcommand_option == CAT_SUBCOMMAND)
227 append_file (file_name);
228 else
229 dump_file (0, file_name, file_name);
230 }
231 }
232
233 write_eot ();
234 close_archive ();
235 finish_deferred_unlinks ();
236 names_notfound ();
237 }