Commit | Line | Data |
---|---|---|
6276da5a KB |
1 | /*- |
2 | * Copyright (c) 1990 The Regents of the University of California. | |
3 | * All rights reserved. | |
4 | * | |
5 | * This code is derived from software contributed to Berkeley by | |
6 | * Hugh Smith at The University of Guelph. | |
7 | * | |
8 | * %sccs.include.redist.c% | |
9 | */ | |
10 | ||
11 | #ifndef lint | |
4f81e05f | 12 | static char sccsid[] = "@(#)archive.c 5.7 (Berkeley) %G%"; |
6276da5a KB |
13 | #endif /* not lint */ |
14 | ||
15 | #include <sys/param.h> | |
6276da5a KB |
16 | #include <sys/stat.h> |
17 | #include <fcntl.h> | |
18 | #include <unistd.h> | |
11dc4a38 | 19 | #include <errno.h> |
6276da5a | 20 | #include <dirent.h> |
6276da5a | 21 | #include <ar.h> |
11dc4a38 | 22 | #include <stdio.h> |
a862a678 KB |
23 | #include <stdlib.h> |
24 | #include <string.h> | |
6276da5a | 25 | #include "archive.h" |
a862a678 | 26 | #include "extern.h" |
6276da5a | 27 | |
11dc4a38 | 28 | extern CHDR chdr; /* converted header */ |
6276da5a KB |
29 | extern char *archive; /* archive name */ |
30 | ||
11dc4a38 KB |
31 | typedef struct ar_hdr HDR; |
32 | static char hb[sizeof(HDR) + 1]; /* real header */ | |
33 | ||
6276da5a KB |
34 | open_archive(mode) |
35 | int mode; | |
36 | { | |
37 | int created, fd, nr; | |
38 | char buf[SARMAG]; | |
39 | ||
40 | created = 0; | |
41 | if (mode & O_CREAT) { | |
42 | mode |= O_EXCL; | |
43 | if ((fd = open(archive, mode, DEFFILEMODE)) >= 0) { | |
44 | /* POSIX.2 puts create message on stderr. */ | |
45 | if (!(options & AR_C)) | |
46 | (void)fprintf(stderr, | |
47 | "ar: creating archive %s.\n", archive); | |
48 | created = 1; | |
49 | goto opened; | |
50 | } | |
51 | if (errno != EEXIST) | |
52 | error(archive); | |
53 | mode &= ~O_EXCL; | |
54 | } | |
55 | if ((fd = open(archive, mode, DEFFILEMODE)) < 0) | |
56 | error(archive); | |
57 | ||
58 | /* | |
59 | * Attempt to place a lock on the opened file - if we get an | |
11dc4a38 KB |
60 | * error then someone is already working on this library (or |
61 | * it's going across NFS). | |
6276da5a | 62 | */ |
11dc4a38 | 63 | opened: if (flock(fd, LOCK_EX|LOCK_NB) && errno != EOPNOTSUPP) |
6276da5a KB |
64 | error(archive); |
65 | ||
66 | /* | |
67 | * If not created, O_RDONLY|O_RDWR indicates that it has to be | |
68 | * in archive format. | |
69 | */ | |
70 | if (!created && | |
71 | ((mode & O_ACCMODE) == O_RDONLY || (mode & O_ACCMODE) == O_RDWR)) { | |
72 | if ((nr = read(fd, buf, SARMAG) != SARMAG)) { | |
73 | if (nr >= 0) | |
74 | badfmt(); | |
75 | error(archive); | |
76 | } else if (bcmp(buf, ARMAG, SARMAG)) | |
77 | badfmt(); | |
78 | } else if (write(fd, ARMAG, SARMAG) != SARMAG) | |
79 | error(archive); | |
80 | return(fd); | |
81 | } | |
82 | ||
a862a678 | 83 | void |
6276da5a KB |
84 | close_archive(fd) |
85 | int fd; | |
86 | { | |
a862a678 | 87 | (void)close(fd); /* Implicit unlock. */ |
6276da5a KB |
88 | } |
89 | ||
11dc4a38 KB |
90 | /* Convert ar header field to an integer. */ |
91 | #define AR_ATOI(from, to, len, base) { \ | |
92 | bcopy(from, buf, len); \ | |
93 | buf[len] = '\0'; \ | |
94 | to = strtol(buf, (char **)NULL, base); \ | |
95 | } | |
96 | ||
97 | /* | |
ce478bb7 | 98 | * get_arobj -- |
11dc4a38 KB |
99 | * read the archive header for this member |
100 | */ | |
ce478bb7 | 101 | get_arobj(fd) |
11dc4a38 KB |
102 | int fd; |
103 | { | |
104 | struct ar_hdr *hdr; | |
105 | register int len, nr; | |
106 | register char *p, buf[20]; | |
107 | ||
108 | nr = read(fd, hb, sizeof(HDR)); | |
109 | if (nr != sizeof(HDR)) { | |
110 | if (!nr) | |
111 | return(0); | |
112 | if (nr < 0) | |
113 | error(archive); | |
114 | badfmt(); | |
115 | } | |
116 | ||
117 | hdr = (struct ar_hdr *)hb; | |
118 | if (strncmp(hdr->ar_fmag, ARFMAG, sizeof(ARFMAG) - 1)) | |
119 | badfmt(); | |
120 | ||
121 | /* Convert the header into the internal format. */ | |
122 | #define DECIMAL 10 | |
123 | #define OCTAL 8 | |
124 | ||
125 | AR_ATOI(hdr->ar_date, chdr.date, sizeof(hdr->ar_date), DECIMAL); | |
126 | AR_ATOI(hdr->ar_uid, chdr.uid, sizeof(hdr->ar_uid), DECIMAL); | |
127 | AR_ATOI(hdr->ar_gid, chdr.gid, sizeof(hdr->ar_gid), DECIMAL); | |
128 | AR_ATOI(hdr->ar_mode, chdr.mode, sizeof(hdr->ar_mode), OCTAL); | |
129 | AR_ATOI(hdr->ar_size, chdr.size, sizeof(hdr->ar_size), DECIMAL); | |
130 | ||
131 | /* Leading spaces should never happen. */ | |
132 | if (hdr->ar_name[0] == ' ') | |
133 | badfmt(); | |
134 | ||
135 | /* | |
136 | * Long name support. Set the "real" size of the file, and the | |
137 | * long name flag/size. | |
138 | */ | |
139 | if (!bcmp(hdr->ar_name, AR_EFMT1, sizeof(AR_EFMT1) - 1)) { | |
140 | chdr.lname = len = atoi(hdr->ar_name + sizeof(AR_EFMT1) - 1); | |
141 | if (len <= 0 || len > MAXNAMLEN) | |
142 | badfmt(); | |
143 | nr = read(fd, chdr.name, len); | |
144 | if (nr != len) { | |
145 | if (nr < 0) | |
146 | error(archive); | |
147 | badfmt(); | |
148 | } | |
149 | chdr.name[len] = 0; | |
150 | chdr.size -= len; | |
151 | } else { | |
152 | chdr.lname = 0; | |
153 | bcopy(hdr->ar_name, chdr.name, sizeof(hdr->ar_name)); | |
154 | ||
155 | /* Strip trailing spaces, null terminate. */ | |
156 | for (p = chdr.name + sizeof(hdr->ar_name) - 1; *p == ' '; --p); | |
157 | *++p = '\0'; | |
158 | } | |
159 | return(1); | |
160 | } | |
161 | ||
162 | static int already_written; | |
163 | ||
164 | /* | |
ce478bb7 | 165 | * put_arobj -- |
11dc4a38 KB |
166 | * Write an archive member to a file. |
167 | */ | |
ce478bb7 | 168 | put_arobj(cfp, sb) |
11dc4a38 KB |
169 | CF *cfp; |
170 | struct stat *sb; | |
171 | { | |
172 | register int lname; | |
173 | register char *name; | |
174 | struct ar_hdr *hdr; | |
175 | off_t size; | |
11dc4a38 KB |
176 | |
177 | /* | |
178 | * If passed an sb structure, reading a file from disk. Get stat(2) | |
179 | * information, build a name and construct a header. (Files are named | |
180 | * by their last component in the archive.) If not, then just write | |
181 | * the last header read. | |
182 | */ | |
183 | if (sb) { | |
184 | name = rname(cfp->rname); | |
185 | (void)fstat(cfp->rfd, sb); | |
186 | ||
616b39cf KB |
187 | /* |
188 | * If not truncating names and the name is too long or contains | |
189 | * a space, use extended format 1. | |
190 | */ | |
191 | lname = strlen(name); | |
4f81e05f | 192 | if (options & AR_TR) { |
5504c5d3 | 193 | if (lname > OLDARMAXNAME) { |
a862a678 KB |
194 | (void)fflush(stdout); |
195 | (void)fprintf(stderr, | |
616b39cf | 196 | "ar: warning: %s truncated to %.*s\n", |
5504c5d3 | 197 | name, OLDARMAXNAME, name); |
616b39cf KB |
198 | (void)fflush(stderr); |
199 | } | |
5504c5d3 KB |
200 | (void)sprintf(hb, HDR3, name, sb->st_mtime, sb->st_uid, |
201 | sb->st_gid, sb->st_mode, sb->st_size, ARFMAG); | |
202 | lname = 0; | |
203 | } else if (lname > sizeof(hdr->ar_name) || index(name, ' ')) | |
204 | (void)sprintf(hb, HDR1, AR_EFMT1, lname, sb->st_mtime, | |
205 | sb->st_uid, sb->st_gid, sb->st_mode, | |
206 | sb->st_size + lname, ARFMAG); | |
207 | else { | |
11dc4a38 KB |
208 | lname = 0; |
209 | (void)sprintf(hb, HDR2, name, sb->st_mtime, sb->st_uid, | |
210 | sb->st_gid, sb->st_mode, sb->st_size, ARFMAG); | |
211 | } | |
212 | size = sb->st_size; | |
213 | } else { | |
214 | lname = chdr.lname; | |
215 | name = chdr.name; | |
216 | size = chdr.size; | |
217 | } | |
218 | ||
219 | if (write(cfp->wfd, hb, sizeof(HDR)) != sizeof(HDR)) | |
220 | error(cfp->wname); | |
221 | if (lname) { | |
222 | if (write(cfp->wfd, name, lname) != lname) | |
223 | error(cfp->wname); | |
224 | already_written = lname; | |
225 | } | |
ce478bb7 | 226 | copy_ar(cfp, size); |
11dc4a38 KB |
227 | already_written = 0; |
228 | } | |
229 | ||
6276da5a | 230 | /* |
ce478bb7 | 231 | * copy_ar -- |
6276da5a | 232 | * Copy size bytes from one file to another - taking care to handle the |
11dc4a38 KB |
233 | * extra byte (for odd size files) when reading archives and writing an |
234 | * extra byte if necessary when adding files to archive. The length of | |
235 | * the object is the long name plus the object itself; the variable | |
236 | * already_written gets set if a long name was written. | |
6276da5a KB |
237 | * |
238 | * The padding is really unnecessary, and is almost certainly a remnant | |
239 | * of early archive formats where the header included binary data which | |
11dc4a38 KB |
240 | * a PDP-11 required to start on an even byte boundary. (Or, perhaps, |
241 | * because 16-bit word addressed copies were faster?) Anyhow, it should | |
242 | * have been ripped out long ago. | |
6276da5a | 243 | */ |
ce478bb7 | 244 | copy_ar(cfp, size) |
6276da5a | 245 | CF *cfp; |
11dc4a38 | 246 | off_t size; |
6276da5a | 247 | { |
11dc4a38 KB |
248 | static char pad = '\n'; |
249 | register off_t sz; | |
6276da5a | 250 | register int from, nr, nw, off, to; |
11dc4a38 | 251 | char buf[8*1024]; |
6276da5a | 252 | |
11dc4a38 | 253 | if (!(sz = size)) |
6276da5a KB |
254 | return; |
255 | ||
6276da5a KB |
256 | from = cfp->rfd; |
257 | to = cfp->wfd; | |
11dc4a38 KB |
258 | sz = size; |
259 | while (sz && (nr = read(from, buf, MIN(sz, sizeof(buf)))) > 0) { | |
260 | sz -= nr; | |
6276da5a KB |
261 | for (off = 0; off < nr; nr -= off, off += nw) |
262 | if ((nw = write(to, buf + off, nr)) < 0) | |
263 | error(cfp->wname); | |
264 | } | |
11dc4a38 | 265 | if (sz) { |
6276da5a KB |
266 | if (nr == 0) |
267 | badfmt(); | |
268 | error(cfp->rname); | |
269 | } | |
270 | ||
11dc4a38 KB |
271 | if (cfp->flags & RPAD && size & 1 && (nr = read(from, buf, 1)) != 1) { |
272 | if (nr == 0) | |
273 | badfmt(); | |
274 | error(cfp->rname); | |
6276da5a | 275 | } |
11dc4a38 KB |
276 | if (cfp->flags & WPAD && (size + already_written) & 1 && |
277 | write(to, &pad, 1) != 1) | |
278 | error(cfp->wname); | |
279 | } | |
280 | ||
281 | /* | |
ce478bb7 | 282 | * skip_arobj - |
11dc4a38 KB |
283 | * Skip over an object -- taking care to skip the pad bytes. |
284 | */ | |
a862a678 | 285 | void |
ce478bb7 | 286 | skip_arobj(fd) |
11dc4a38 KB |
287 | int fd; |
288 | { | |
289 | off_t len; | |
290 | ||
291 | len = chdr.size + (chdr.size + chdr.lname & 1); | |
292 | if (lseek(fd, len, SEEK_CUR) == (off_t)-1) | |
293 | error(archive); | |
6276da5a | 294 | } |