source: MondoRescue/branches/3.3/mindi-busybox/archival/ar.c@ 3791

Last change on this file since 3791 was 3621, checked in by Bruno Cornec, 10 years ago

New 3?3 banch for incorporation of latest busybox 1.25. Changing minor version to handle potential incompatibilities.

File size: 8.4 KB
Line 
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini ar implementation for busybox
4 *
5 * Copyright (C) 2000 by Glenn McGrath
6 *
7 * Based in part on BusyBox tar, Debian dpkg-deb and GNU ar.
8 *
9 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
10 *
11 * Archive creation support:
12 * Copyright (C) 2010 Nokia Corporation. All rights reserved.
13 * Written by Alexander Shishkin.
14 *
15 * There is no single standard to adhere to so ar may not portable
16 * between different systems
17 * http://www.unix-systems.org/single_unix_specification_v2/xcu/ar.html
18 */
19
20//config:config AR
21//config: bool "ar"
22//config: default n # needs to be improved to be able to replace binutils ar
23//config: help
24//config: ar is an archival utility program used to create, modify, and
25//config: extract contents from archives. In practice, it is used exclusively
26//config: for object module archives used by compilers.
27//config:
28//config: On an x86 system, the ar applet adds about 1K.
29//config:
30//config: Unless you have a specific application which requires ar, you should
31//config: probably say N here: most compilers come with their own ar utility.
32//config:
33//config:config FEATURE_AR_LONG_FILENAMES
34//config: bool "Support for long filenames (not needed for debs)"
35//config: default y
36//config: depends on AR
37//config: help
38//config: By default the ar format can only store the first 15 characters
39//config: of the filename, this option removes that limitation.
40//config: It supports the GNU ar long filename method which moves multiple long
41//config: filenames into a the data section of a new ar entry.
42//config:
43//config:config FEATURE_AR_CREATE
44//config: bool "Support archive creation"
45//config: default y
46//config: depends on AR
47//config: help
48//config: This enables archive creation (-c and -r) with busybox ar.
49
50//applet:IF_AR(APPLET(ar, BB_DIR_USR_BIN, BB_SUID_DROP))
51//kbuild:lib-$(CONFIG_AR) += ar.o
52
53//usage:#define ar_trivial_usage
54//usage: "[-o] [-v] [-p] [-t] [-x] ARCHIVE FILES"
55//usage:#define ar_full_usage "\n\n"
56//usage: "Extract or list FILES from an ar archive\n"
57//usage: "\n -o Preserve original dates"
58//usage: "\n -p Extract to stdout"
59//usage: "\n -t List"
60//usage: "\n -x Extract"
61//usage: "\n -v Verbose"
62
63#include "libbb.h"
64#include "bb_archive.h"
65#include "ar.h"
66
67#if ENABLE_FEATURE_AR_CREATE
68/* filter out entries with same names as specified on the command line */
69static char FAST_FUNC filter_replaceable(archive_handle_t *handle)
70{
71 if (find_list_entry(handle->accept, handle->file_header->name))
72 return EXIT_FAILURE;
73
74 return EXIT_SUCCESS;
75}
76
77static void output_ar_header(archive_handle_t *handle)
78{
79 /* GNU ar 2.19.51.0.14 creates malformed archives
80 * if input files are >10G. It also truncates files >4GB
81 * (uses "size mod 4G"). We abort in this case:
82 * We could add support for up to 10G files, but this is unlikely to be useful.
83 * Note that unpacking side limits all fields to "unsigned int" data type,
84 * and treats "all ones" as an error indicator. Thus max we allow here is UINT_MAX-1.
85 */
86 enum {
87 /* for 2nd field: mtime */
88 MAX11CHARS = UINT_MAX > 0xffffffff ? (unsigned)99999999999 : UINT_MAX-1,
89 /* for last field: filesize */
90 MAX10CHARS = UINT_MAX > 0xffffffff ? (unsigned)9999999999 : UINT_MAX-1,
91 };
92
93 struct file_header_t *fh = handle->file_header;
94
95 if (handle->offset & 1) {
96 xwrite(handle->src_fd, "\n", 1);
97 handle->offset++;
98 }
99
100 /* Careful! The widths should be exact. Fields must be separated */
101 if (sizeof(off_t) > 4 && fh->size > (off_t)MAX10CHARS) {
102 bb_error_msg_and_die("'%s' is bigger than ar can handle", fh->name);
103 }
104 fdprintf(handle->src_fd, "%-16.16s%-12lu%-6u%-6u%-8o%-10"OFF_FMT"u`\n",
105 fh->name,
106 (sizeof(time_t) > 4 && fh->mtime > MAX11CHARS) ? (long)0 : (long)fh->mtime,
107 fh->uid > 99999 ? 0 : (int)fh->uid,
108 fh->gid > 99999 ? 0 : (int)fh->gid,
109 (int)fh->mode & 07777777,
110 fh->size
111 );
112
113 handle->offset += AR_HEADER_LEN;
114}
115
116/*
117 * when replacing files in an existing archive, copy from the
118 * original archive those files that are to be left intact
119 */
120static void FAST_FUNC copy_data(archive_handle_t *handle)
121{
122 archive_handle_t *out_handle = handle->ar__out;
123 struct file_header_t *fh = handle->file_header;
124
125 out_handle->file_header = fh;
126 output_ar_header(out_handle);
127
128 bb_copyfd_exact_size(handle->src_fd, out_handle->src_fd, fh->size);
129 out_handle->offset += fh->size;
130}
131
132static int write_ar_header(archive_handle_t *handle)
133{
134 char *fn;
135 char fn_h[17]; /* 15 + "/" + NUL */
136 struct stat st;
137 int fd;
138
139 fn = llist_pop(&handle->accept);
140 if (!fn)
141 return -1;
142
143 xstat(fn, &st);
144
145 handle->file_header->mtime = st.st_mtime;
146 handle->file_header->uid = st.st_uid;
147 handle->file_header->gid = st.st_gid;
148 handle->file_header->mode = st.st_mode;
149 handle->file_header->size = st.st_size;
150 handle->file_header->name = fn_h;
151//TODO: if ENABLE_FEATURE_AR_LONG_FILENAMES...
152 sprintf(fn_h, "%.15s/", bb_basename(fn));
153
154 output_ar_header(handle);
155
156 fd = xopen(fn, O_RDONLY);
157 bb_copyfd_exact_size(fd, handle->src_fd, st.st_size);
158 close(fd);
159 handle->offset += st.st_size;
160
161 return 0;
162}
163
164static int write_ar_archive(archive_handle_t *handle)
165{
166 struct stat st;
167 archive_handle_t *out_handle;
168
169 xfstat(handle->src_fd, &st, handle->ar__name);
170
171 /* if archive exists, create a new handle for output.
172 * we create it in place of the old one.
173 */
174 if (st.st_size != 0) {
175 out_handle = init_handle();
176 xunlink(handle->ar__name);
177 out_handle->src_fd = xopen(handle->ar__name, O_WRONLY | O_CREAT | O_TRUNC);
178 out_handle->accept = handle->accept;
179 } else {
180 out_handle = handle;
181 }
182
183 handle->ar__out = out_handle;
184
185 xwrite(out_handle->src_fd, AR_MAGIC "\n", AR_MAGIC_LEN + 1);
186 out_handle->offset += AR_MAGIC_LEN + 1;
187
188 /* skip to the end of the archive if we have to append stuff */
189 if (st.st_size != 0) {
190 handle->filter = filter_replaceable;
191 handle->action_data = copy_data;
192 unpack_ar_archive(handle);
193 }
194
195 while (write_ar_header(out_handle) == 0)
196 continue;
197
198 /* optional, since we exit right after we return */
199 if (ENABLE_FEATURE_CLEAN_UP) {
200 close(handle->src_fd);
201 if (out_handle->src_fd != handle->src_fd)
202 close(out_handle->src_fd);
203 }
204
205 return EXIT_SUCCESS;
206}
207#endif /* FEATURE_AR_CREATE */
208
209static void FAST_FUNC header_verbose_list_ar(const file_header_t *file_header)
210{
211 const char *mode = bb_mode_string(file_header->mode);
212 char *mtime;
213
214 mtime = ctime(&file_header->mtime);
215 mtime[16] = ' ';
216 memmove(&mtime[17], &mtime[20], 4);
217 mtime[21] = '\0';
218 printf("%s %u/%u%7"OFF_FMT"u %s %s\n", &mode[1],
219 (int)file_header->uid, (int)file_header->gid,
220 file_header->size,
221 &mtime[4], file_header->name
222 );
223}
224
225#define AR_OPT_VERBOSE (1 << 0)
226#define AR_OPT_PRESERVE_DATE (1 << 1)
227/* "ar r" implies create, but warns about it. c suppresses warning.
228 * bbox accepts but ignores it: */
229#define AR_OPT_CREATE (1 << 2)
230
231#define AR_CMD_PRINT (1 << 3)
232#define FIRST_CMD AR_CMD_PRINT
233#define AR_CMD_LIST (1 << 4)
234#define AR_CMD_EXTRACT (1 << 5)
235#define AR_CMD_INSERT (1 << 6)
236
237int ar_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
238int ar_main(int argc UNUSED_PARAM, char **argv)
239{
240 archive_handle_t *archive_handle;
241 unsigned opt, t;
242
243 archive_handle = init_handle();
244
245 /* --: prepend '-' to the first argument if required */
246 /* -1: at least one param is reqd */
247 /* one of p,t,x[,r] is required */
248 opt_complementary = "--:-1:p:t:x"IF_FEATURE_AR_CREATE(":r");
249 opt = getopt32(argv, "voc""ptx"IF_FEATURE_AR_CREATE("r"));
250 argv += optind;
251
252 t = opt / FIRST_CMD;
253 if (t & (t-1)) /* more than one of p,t,x[,r] are specified */
254 bb_show_usage();
255
256 if (opt & AR_CMD_PRINT) {
257 archive_handle->action_data = data_extract_to_stdout;
258 }
259 if (opt & AR_CMD_LIST) {
260 archive_handle->action_header = header_list;
261 }
262 if (opt & AR_CMD_EXTRACT) {
263 archive_handle->action_data = data_extract_all;
264 }
265 if (opt & AR_OPT_PRESERVE_DATE) {
266 archive_handle->ah_flags |= ARCHIVE_RESTORE_DATE;
267 }
268 if (opt & AR_OPT_VERBOSE) {
269 archive_handle->action_header = header_verbose_list_ar;
270 }
271#if ENABLE_FEATURE_AR_CREATE
272 archive_handle->ar__name = *argv;
273#endif
274 archive_handle->src_fd = xopen(*argv++,
275 (opt & AR_CMD_INSERT)
276 ? O_RDWR | O_CREAT
277 : O_RDONLY
278 );
279
280 if (*argv)
281 archive_handle->filter = filter_accept_list;
282 while (*argv) {
283 llist_add_to_end(&archive_handle->accept, *argv++);
284 }
285
286#if ENABLE_FEATURE_AR_CREATE
287 if (opt & AR_CMD_INSERT)
288 return write_ar_archive(archive_handle);
289#endif
290
291 unpack_ar_archive(archive_handle);
292
293 return EXIT_SUCCESS;
294}
Note: See TracBrowser for help on using the repository browser.