8 |
* |
* |
9 |
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details. |
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details. |
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 |
* There is no single standard to adhere to so ar may not portable |
16 |
* between different systems |
* between different systems |
17 |
* http://www.unix-systems.org/single_unix_specification_v2/xcu/ar.html |
* http://www.unix-systems.org/single_unix_specification_v2/xcu/ar.html |
19 |
|
|
20 |
#include "libbb.h" |
#include "libbb.h" |
21 |
#include "unarchive.h" |
#include "unarchive.h" |
22 |
|
#include "ar.h" |
23 |
|
|
24 |
|
#if ENABLE_FEATURE_AR_CREATE |
25 |
|
/* filter out entries with same names as specified on the command line */ |
26 |
|
static char FAST_FUNC filter_replaceable(archive_handle_t *handle) |
27 |
|
{ |
28 |
|
if (find_list_entry(handle->accept, handle->file_header->name)) |
29 |
|
return EXIT_FAILURE; |
30 |
|
|
31 |
|
return EXIT_SUCCESS; |
32 |
|
} |
33 |
|
|
34 |
|
static void output_ar_header(archive_handle_t *handle) |
35 |
|
{ |
36 |
|
/* GNU ar 2.19.51.0.14 creates malformed archives |
37 |
|
* if input files are >10G. It also truncates files >4GB |
38 |
|
* (uses "size mod 4G"). We abort in this case: |
39 |
|
* We could add support for up to 10G files, but this is unlikely to be useful. |
40 |
|
* Note that unpacking side limits all fields to "unsigned int" data type, |
41 |
|
* and treats "all ones" as an error indicator. Thus max we allow here is UINT_MAX-1. |
42 |
|
*/ |
43 |
|
enum { |
44 |
|
/* for 2nd field: mtime */ |
45 |
|
MAX11CHARS = UINT_MAX > 0xffffffff ? (unsigned)99999999999 : UINT_MAX-1, |
46 |
|
/* for last field: filesize */ |
47 |
|
MAX10CHARS = UINT_MAX > 0xffffffff ? (unsigned)9999999999 : UINT_MAX-1, |
48 |
|
}; |
49 |
|
|
50 |
|
struct file_header_t *fh = handle->file_header; |
51 |
|
|
52 |
|
if (handle->offset & 1) { |
53 |
|
xwrite(handle->src_fd, "\n", 1); |
54 |
|
handle->offset++; |
55 |
|
} |
56 |
|
|
57 |
|
/* Careful! The widths should be exact. Fields must be separated */ |
58 |
|
if (sizeof(off_t) > 4 && fh->size > (off_t)MAX10CHARS) { |
59 |
|
bb_error_msg_and_die("'%s' is bigger than ar can handle", fh->name); |
60 |
|
} |
61 |
|
fdprintf(handle->src_fd, "%-16.16s%-12lu%-6u%-6u%-8o%-10"OFF_FMT"u`\n", |
62 |
|
fh->name, |
63 |
|
(sizeof(time_t) > 4 && fh->mtime > MAX11CHARS) ? (long)0 : (long)fh->mtime, |
64 |
|
fh->uid > 99999 ? 0 : (int)fh->uid, |
65 |
|
fh->gid > 99999 ? 0 : (int)fh->gid, |
66 |
|
(int)fh->mode & 07777777, |
67 |
|
fh->size |
68 |
|
); |
69 |
|
|
70 |
|
handle->offset += AR_HEADER_LEN; |
71 |
|
} |
72 |
|
|
73 |
|
/* |
74 |
|
* when replacing files in an existing archive, copy from the the |
75 |
|
* original archive those files that are to be left intact |
76 |
|
*/ |
77 |
|
static void FAST_FUNC copy_data(archive_handle_t *handle) |
78 |
|
{ |
79 |
|
archive_handle_t *out_handle = handle->ar__out; |
80 |
|
struct file_header_t *fh = handle->file_header; |
81 |
|
|
82 |
|
out_handle->file_header = fh; |
83 |
|
output_ar_header(out_handle); |
84 |
|
|
85 |
|
bb_copyfd_exact_size(handle->src_fd, out_handle->src_fd, fh->size); |
86 |
|
out_handle->offset += fh->size; |
87 |
|
} |
88 |
|
|
89 |
|
static int write_ar_header(archive_handle_t *handle) |
90 |
|
{ |
91 |
|
char *fn; |
92 |
|
char fn_h[17]; /* 15 + "/" + NUL */ |
93 |
|
struct stat st; |
94 |
|
int fd; |
95 |
|
|
96 |
|
fn = llist_pop(&handle->accept); |
97 |
|
if (!fn) |
98 |
|
return -1; |
99 |
|
|
100 |
|
xstat(fn, &st); |
101 |
|
|
102 |
|
handle->file_header->mtime = st.st_mtime; |
103 |
|
handle->file_header->uid = st.st_uid; |
104 |
|
handle->file_header->gid = st.st_gid; |
105 |
|
handle->file_header->mode = st.st_mode; |
106 |
|
handle->file_header->size = st.st_size; |
107 |
|
handle->file_header->name = fn_h; |
108 |
|
//TODO: if ENABLE_FEATURE_AR_LONG_FILENAMES... |
109 |
|
sprintf(fn_h, "%.15s/", bb_basename(fn)); |
110 |
|
|
111 |
|
output_ar_header(handle); |
112 |
|
|
113 |
|
fd = xopen(fn, O_RDONLY); |
114 |
|
bb_copyfd_exact_size(fd, handle->src_fd, st.st_size); |
115 |
|
close(fd); |
116 |
|
handle->offset += st.st_size; |
117 |
|
|
118 |
|
return 0; |
119 |
|
} |
120 |
|
|
121 |
|
static int write_ar_archive(archive_handle_t *handle) |
122 |
|
{ |
123 |
|
struct stat st; |
124 |
|
archive_handle_t *out_handle; |
125 |
|
|
126 |
|
if (fstat(handle->src_fd, &st) == -1) |
127 |
|
bb_simple_perror_msg_and_die(handle->ar__name); |
128 |
|
|
129 |
|
/* if archive exists, create a new handle for output. |
130 |
|
* we create it in place of the old one. |
131 |
|
*/ |
132 |
|
if (st.st_size != 0) { |
133 |
|
out_handle = init_handle(); |
134 |
|
xunlink(handle->ar__name); |
135 |
|
out_handle->src_fd = xopen(handle->ar__name, O_WRONLY | O_CREAT | O_TRUNC); |
136 |
|
out_handle->accept = handle->accept; |
137 |
|
} else { |
138 |
|
out_handle = handle; |
139 |
|
} |
140 |
|
|
141 |
|
handle->ar__out = out_handle; |
142 |
|
|
143 |
|
xwrite(out_handle->src_fd, AR_MAGIC "\n", AR_MAGIC_LEN + 1); |
144 |
|
out_handle->offset += AR_MAGIC_LEN + 1; |
145 |
|
|
146 |
|
/* skip to the end of the archive if we have to append stuff */ |
147 |
|
if (st.st_size != 0) { |
148 |
|
handle->filter = filter_replaceable; |
149 |
|
handle->action_data = copy_data; |
150 |
|
unpack_ar_archive(handle); |
151 |
|
} |
152 |
|
|
153 |
|
while (write_ar_header(out_handle) == 0) |
154 |
|
continue; |
155 |
|
|
156 |
|
/* optional, since we exit right after we return */ |
157 |
|
if (ENABLE_FEATURE_CLEAN_UP) { |
158 |
|
close(handle->src_fd); |
159 |
|
if (out_handle->src_fd != handle->src_fd) |
160 |
|
close(out_handle->src_fd); |
161 |
|
} |
162 |
|
|
163 |
|
return EXIT_SUCCESS; |
164 |
|
} |
165 |
|
#endif /* FEATURE_AR_CREATE */ |
166 |
|
|
167 |
static void FAST_FUNC header_verbose_list_ar(const file_header_t *file_header) |
static void FAST_FUNC header_verbose_list_ar(const file_header_t *file_header) |
168 |
{ |
{ |
173 |
mtime[16] = ' '; |
mtime[16] = ' '; |
174 |
memmove(&mtime[17], &mtime[20], 4); |
memmove(&mtime[17], &mtime[20], 4); |
175 |
mtime[21] = '\0'; |
mtime[21] = '\0'; |
176 |
printf("%s %d/%d%7d %s %s\n", &mode[1], file_header->uid, file_header->gid, |
printf("%s %u/%u%7"OFF_FMT"u %s %s\n", &mode[1], |
177 |
(int) file_header->size, &mtime[4], file_header->name); |
(int)file_header->uid, (int)file_header->gid, |
178 |
|
file_header->size, |
179 |
|
&mtime[4], file_header->name |
180 |
|
); |
181 |
} |
} |
182 |
|
|
183 |
#define AR_CTX_PRINT 0x01 |
#define AR_OPT_VERBOSE (1 << 0) |
184 |
#define AR_CTX_LIST 0x02 |
#define AR_OPT_PRESERVE_DATE (1 << 1) |
185 |
#define AR_CTX_EXTRACT 0x04 |
/* "ar r" implies create, but warns about it. c suppresses warning. |
186 |
#define AR_OPT_PRESERVE_DATE 0x08 |
* bbox accepts but ignores it: */ |
187 |
#define AR_OPT_VERBOSE 0x10 |
#define AR_OPT_CREATE (1 << 2) |
188 |
#define AR_OPT_CREATE 0x20 |
|
189 |
#define AR_OPT_INSERT 0x40 |
#define AR_CMD_PRINT (1 << 3) |
190 |
|
#define FIRST_CMD AR_CMD_PRINT |
191 |
|
#define AR_CMD_LIST (1 << 4) |
192 |
|
#define AR_CMD_EXTRACT (1 << 5) |
193 |
|
#define AR_CMD_INSERT (1 << 6) |
194 |
|
|
195 |
int ar_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
int ar_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
196 |
int ar_main(int argc UNUSED_PARAM, char **argv) |
int ar_main(int argc UNUSED_PARAM, char **argv) |
197 |
{ |
{ |
|
static const char msg_unsupported_err[] ALIGN1 = |
|
|
"archive %s is not supported"; |
|
|
|
|
198 |
archive_handle_t *archive_handle; |
archive_handle_t *archive_handle; |
199 |
unsigned opt; |
unsigned opt, t; |
200 |
|
|
201 |
archive_handle = init_handle(); |
archive_handle = init_handle(); |
202 |
|
|
203 |
/* Prepend '-' to the first argument if required */ |
/* --: prepend '-' to the first argument if required */ |
204 |
opt_complementary = "--:p:t:x:-1:p--tx:t--px:x--pt"; |
/* -1: at least one param is reqd */ |
205 |
opt = getopt32(argv, "ptxovcr"); |
/* one of p,t,x[,r] is required */ |
206 |
|
opt_complementary = "--:-1:p:t:x"IF_FEATURE_AR_CREATE(":r"); |
207 |
|
opt = getopt32(argv, "voc""ptx"IF_FEATURE_AR_CREATE("r")); |
208 |
argv += optind; |
argv += optind; |
209 |
|
|
210 |
if (opt & AR_CTX_PRINT) { |
t = opt / FIRST_CMD; |
211 |
|
if (t & (t-1)) /* more than one of p,t,x[,r] are specified */ |
212 |
|
bb_show_usage(); |
213 |
|
|
214 |
|
if (opt & AR_CMD_PRINT) { |
215 |
archive_handle->action_data = data_extract_to_stdout; |
archive_handle->action_data = data_extract_to_stdout; |
216 |
} |
} |
217 |
if (opt & AR_CTX_LIST) { |
if (opt & AR_CMD_LIST) { |
218 |
archive_handle->action_header = header_list; |
archive_handle->action_header = header_list; |
219 |
} |
} |
220 |
if (opt & AR_CTX_EXTRACT) { |
if (opt & AR_CMD_EXTRACT) { |
221 |
archive_handle->action_data = data_extract_all; |
archive_handle->action_data = data_extract_all; |
222 |
} |
} |
223 |
if (opt & AR_OPT_PRESERVE_DATE) { |
if (opt & AR_OPT_PRESERVE_DATE) { |
226 |
if (opt & AR_OPT_VERBOSE) { |
if (opt & AR_OPT_VERBOSE) { |
227 |
archive_handle->action_header = header_verbose_list_ar; |
archive_handle->action_header = header_verbose_list_ar; |
228 |
} |
} |
229 |
if (opt & AR_OPT_CREATE) { |
#if ENABLE_FEATURE_AR_CREATE |
230 |
bb_error_msg_and_die(msg_unsupported_err, "creation"); |
archive_handle->ar__name = *argv; |
231 |
} |
#endif |
232 |
if (opt & AR_OPT_INSERT) { |
archive_handle->src_fd = xopen(*argv++, |
233 |
bb_error_msg_and_die(msg_unsupported_err, "insertion"); |
(opt & AR_CMD_INSERT) |
234 |
} |
? O_RDWR | O_CREAT |
235 |
|
: O_RDONLY |
236 |
archive_handle->src_fd = xopen(*argv++, O_RDONLY); |
); |
237 |
|
|
238 |
while (*argv) { |
if (*argv) |
239 |
archive_handle->filter = filter_accept_list; |
archive_handle->filter = filter_accept_list; |
240 |
llist_add_to(&archive_handle->accept, *argv++); |
while (*argv) { |
241 |
|
llist_add_to_end(&archive_handle->accept, *argv++); |
242 |
} |
} |
243 |
|
|
244 |
|
#if ENABLE_FEATURE_AR_CREATE |
245 |
|
if (opt & AR_CMD_INSERT) |
246 |
|
return write_ar_archive(archive_handle); |
247 |
|
#endif |
248 |
|
|
249 |
unpack_ar_archive(archive_handle); |
unpack_ar_archive(archive_handle); |
250 |
|
|
251 |
return EXIT_SUCCESS; |
return EXIT_SUCCESS; |