Commit | Line | Data |
---|---|---|
6276da5a | 1 | /*- |
4e6c9859 KB |
2 | * Copyright (c) 1990, 1993 |
3 | * The Regents of the University of California. All rights reserved. | |
6276da5a KB |
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 | |
4e6c9859 | 12 | static char sccsid[] = "@(#)archive.c 8.1 (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; | |
57565f06 RC |
106 | register char *p; |
107 | char buf[20]; | |
11dc4a38 KB |
108 | |
109 | nr = read(fd, hb, sizeof(HDR)); | |
110 | if (nr != sizeof(HDR)) { | |
111 | if (!nr) | |
112 | return(0); | |
113 | if (nr < 0) | |
114 | error(archive); | |
115 | badfmt(); | |
116 | } | |
117 | ||
118 | hdr = (struct ar_hdr *)hb; | |
119 | if (strncmp(hdr->ar_fmag, ARFMAG, sizeof(ARFMAG) - 1)) | |
120 | badfmt(); | |
121 | ||
122 | /* Convert the header into the internal format. */ | |
123 | #define DECIMAL 10 | |
124 | #define OCTAL 8 | |
125 | ||
126 | AR_ATOI(hdr->ar_date, chdr.date, sizeof(hdr->ar_date), DECIMAL); | |
127 | AR_ATOI(hdr->ar_uid, chdr.uid, sizeof(hdr->ar_uid), DECIMAL); | |
128 | AR_ATOI(hdr->ar_gid, chdr.gid, sizeof(hdr->ar_gid), DECIMAL); | |
129 | AR_ATOI(hdr->ar_mode, chdr.mode, sizeof(hdr->ar_mode), OCTAL); | |
130 | AR_ATOI(hdr->ar_size, chdr.size, sizeof(hdr->ar_size), DECIMAL); | |
131 | ||
132 | /* Leading spaces should never happen. */ | |
133 | if (hdr->ar_name[0] == ' ') | |
134 | badfmt(); | |
135 | ||
136 | /* | |
137 | * Long name support. Set the "real" size of the file, and the | |
138 | * long name flag/size. | |
139 | */ | |
140 | if (!bcmp(hdr->ar_name, AR_EFMT1, sizeof(AR_EFMT1) - 1)) { | |
141 | chdr.lname = len = atoi(hdr->ar_name + sizeof(AR_EFMT1) - 1); | |
142 | if (len <= 0 || len > MAXNAMLEN) | |
143 | badfmt(); | |
144 | nr = read(fd, chdr.name, len); | |
145 | if (nr != len) { | |
146 | if (nr < 0) | |
147 | error(archive); | |
148 | badfmt(); | |
149 | } | |
150 | chdr.name[len] = 0; | |
151 | chdr.size -= len; | |
152 | } else { | |
153 | chdr.lname = 0; | |
154 | bcopy(hdr->ar_name, chdr.name, sizeof(hdr->ar_name)); | |
155 | ||
156 | /* Strip trailing spaces, null terminate. */ | |
157 | for (p = chdr.name + sizeof(hdr->ar_name) - 1; *p == ' '; --p); | |
158 | *++p = '\0'; | |
159 | } | |
160 | return(1); | |
161 | } | |
162 | ||
163 | static int already_written; | |
164 | ||
165 | /* | |
ce478bb7 | 166 | * put_arobj -- |
11dc4a38 KB |
167 | * Write an archive member to a file. |
168 | */ | |
ce478bb7 | 169 | put_arobj(cfp, sb) |
11dc4a38 KB |
170 | CF *cfp; |
171 | struct stat *sb; | |
172 | { | |
173 | register int lname; | |
174 | register char *name; | |
175 | struct ar_hdr *hdr; | |
176 | off_t size; | |
11dc4a38 KB |
177 | |
178 | /* | |
179 | * If passed an sb structure, reading a file from disk. Get stat(2) | |
180 | * information, build a name and construct a header. (Files are named | |
181 | * by their last component in the archive.) If not, then just write | |
182 | * the last header read. | |
183 | */ | |
184 | if (sb) { | |
185 | name = rname(cfp->rname); | |
186 | (void)fstat(cfp->rfd, sb); | |
187 | ||
616b39cf KB |
188 | /* |
189 | * If not truncating names and the name is too long or contains | |
190 | * a space, use extended format 1. | |
191 | */ | |
192 | lname = strlen(name); | |
4f81e05f | 193 | if (options & AR_TR) { |
5504c5d3 | 194 | if (lname > OLDARMAXNAME) { |
a862a678 KB |
195 | (void)fflush(stdout); |
196 | (void)fprintf(stderr, | |
616b39cf | 197 | "ar: warning: %s truncated to %.*s\n", |
5504c5d3 | 198 | name, OLDARMAXNAME, name); |
616b39cf KB |
199 | (void)fflush(stderr); |
200 | } | |
6a8fdfdd KB |
201 | (void)sprintf(hb, HDR3, name, sb->st_mtimespec.ts_sec, |
202 | sb->st_uid, sb->st_gid, sb->st_mode, sb->st_size, | |
203 | ARFMAG); | |
5504c5d3 KB |
204 | lname = 0; |
205 | } else if (lname > sizeof(hdr->ar_name) || index(name, ' ')) | |
6a8fdfdd KB |
206 | (void)sprintf(hb, HDR1, AR_EFMT1, lname, |
207 | sb->st_mtimespec.ts_sec, sb->st_uid, sb->st_gid, | |
208 | sb->st_mode, sb->st_size + lname, ARFMAG); | |
5504c5d3 | 209 | else { |
11dc4a38 | 210 | lname = 0; |
6a8fdfdd KB |
211 | (void)sprintf(hb, HDR2, name, sb->st_mtimespec.ts_sec, |
212 | sb->st_uid, sb->st_gid, sb->st_mode, sb->st_size, | |
213 | ARFMAG); | |
11dc4a38 KB |
214 | } |
215 | size = sb->st_size; | |
216 | } else { | |
217 | lname = chdr.lname; | |
218 | name = chdr.name; | |
219 | size = chdr.size; | |
220 | } | |
221 | ||
222 | if (write(cfp->wfd, hb, sizeof(HDR)) != sizeof(HDR)) | |
223 | error(cfp->wname); | |
224 | if (lname) { | |
225 | if (write(cfp->wfd, name, lname) != lname) | |
226 | error(cfp->wname); | |
227 | already_written = lname; | |
228 | } | |
ce478bb7 | 229 | copy_ar(cfp, size); |
11dc4a38 KB |
230 | already_written = 0; |
231 | } | |
232 | ||
6276da5a | 233 | /* |
ce478bb7 | 234 | * copy_ar -- |
6276da5a | 235 | * Copy size bytes from one file to another - taking care to handle the |
11dc4a38 KB |
236 | * extra byte (for odd size files) when reading archives and writing an |
237 | * extra byte if necessary when adding files to archive. The length of | |
238 | * the object is the long name plus the object itself; the variable | |
239 | * already_written gets set if a long name was written. | |
6276da5a KB |
240 | * |
241 | * The padding is really unnecessary, and is almost certainly a remnant | |
242 | * of early archive formats where the header included binary data which | |
11dc4a38 KB |
243 | * a PDP-11 required to start on an even byte boundary. (Or, perhaps, |
244 | * because 16-bit word addressed copies were faster?) Anyhow, it should | |
245 | * have been ripped out long ago. | |
6276da5a | 246 | */ |
ce478bb7 | 247 | copy_ar(cfp, size) |
6276da5a | 248 | CF *cfp; |
11dc4a38 | 249 | off_t size; |
6276da5a | 250 | { |
11dc4a38 KB |
251 | static char pad = '\n'; |
252 | register off_t sz; | |
6276da5a | 253 | register int from, nr, nw, off, to; |
11dc4a38 | 254 | char buf[8*1024]; |
6276da5a | 255 | |
11dc4a38 | 256 | if (!(sz = size)) |
6276da5a KB |
257 | return; |
258 | ||
6276da5a KB |
259 | from = cfp->rfd; |
260 | to = cfp->wfd; | |
11dc4a38 KB |
261 | sz = size; |
262 | while (sz && (nr = read(from, buf, MIN(sz, sizeof(buf)))) > 0) { | |
263 | sz -= nr; | |
6276da5a KB |
264 | for (off = 0; off < nr; nr -= off, off += nw) |
265 | if ((nw = write(to, buf + off, nr)) < 0) | |
266 | error(cfp->wname); | |
267 | } | |
11dc4a38 | 268 | if (sz) { |
6276da5a KB |
269 | if (nr == 0) |
270 | badfmt(); | |
271 | error(cfp->rname); | |
272 | } | |
273 | ||
11dc4a38 KB |
274 | if (cfp->flags & RPAD && size & 1 && (nr = read(from, buf, 1)) != 1) { |
275 | if (nr == 0) | |
276 | badfmt(); | |
277 | error(cfp->rname); | |
6276da5a | 278 | } |
11dc4a38 KB |
279 | if (cfp->flags & WPAD && (size + already_written) & 1 && |
280 | write(to, &pad, 1) != 1) | |
281 | error(cfp->wname); | |
282 | } | |
283 | ||
284 | /* | |
ce478bb7 | 285 | * skip_arobj - |
11dc4a38 KB |
286 | * Skip over an object -- taking care to skip the pad bytes. |
287 | */ | |
a862a678 | 288 | void |
ce478bb7 | 289 | skip_arobj(fd) |
11dc4a38 KB |
290 | int fd; |
291 | { | |
292 | off_t len; | |
293 | ||
294 | len = chdr.size + (chdr.size + chdr.lname & 1); | |
295 | if (lseek(fd, len, SEEK_CUR) == (off_t)-1) | |
296 | error(archive); | |
6276da5a | 297 | } |