xref: /original-bsd/usr.bin/ar/archive.c (revision 02e832b2)
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
12 static char sccsid[] = "@(#)archive.c	5.9 (Berkeley) 10/24/92";
13 #endif /* not lint */
14 
15 #include <sys/param.h>
16 #include <sys/stat.h>
17 #include <fcntl.h>
18 #include <unistd.h>
19 #include <errno.h>
20 #include <dirent.h>
21 #include <ar.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include "archive.h"
26 #include "extern.h"
27 
28 extern CHDR chdr;			/* converted header */
29 extern char *archive;			/* archive name */
30 
31 typedef struct ar_hdr HDR;
32 static char hb[sizeof(HDR) + 1];	/* real header */
33 
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
60 	 * error then someone is already working on this library (or
61 	 * it's going across NFS).
62 	 */
63 opened:	if (flock(fd, LOCK_EX|LOCK_NB) && errno != EOPNOTSUPP)
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 
83 void
84 close_archive(fd)
85 	int fd;
86 {
87 	(void)close(fd);			/* Implicit unlock. */
88 }
89 
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 /*
98  * get_arobj --
99  *	read the archive header for this member
100  */
101 get_arobj(fd)
102 	int fd;
103 {
104 	struct ar_hdr *hdr;
105 	register int len, nr;
106 	register char *p;
107 	char buf[20];
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 /*
166  * put_arobj --
167  *	Write an archive member to a file.
168  */
169 put_arobj(cfp, sb)
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;
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 
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);
193 		if (options & AR_TR) {
194 			if (lname > OLDARMAXNAME) {
195 				(void)fflush(stdout);
196 				(void)fprintf(stderr,
197 				    "ar: warning: %s truncated to %.*s\n",
198 				    name, OLDARMAXNAME, name);
199 				(void)fflush(stderr);
200 			}
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);
204 			lname = 0;
205 		} else if (lname > sizeof(hdr->ar_name) || index(name, ' '))
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);
209 		else {
210 			lname = 0;
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);
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 	}
229 	copy_ar(cfp, size);
230 	already_written = 0;
231 }
232 
233 /*
234  * copy_ar --
235  *	Copy size bytes from one file to another - taking care to handle the
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.
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
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.
246  */
247 copy_ar(cfp, size)
248 	CF *cfp;
249 	off_t size;
250 {
251 	static char pad = '\n';
252 	register off_t sz;
253 	register int from, nr, nw, off, to;
254 	char buf[8*1024];
255 
256 	if (!(sz = size))
257 		return;
258 
259 	from = cfp->rfd;
260 	to = cfp->wfd;
261 	sz = size;
262 	while (sz && (nr = read(from, buf, MIN(sz, sizeof(buf)))) > 0) {
263 		sz -= nr;
264 		for (off = 0; off < nr; nr -= off, off += nw)
265 			if ((nw = write(to, buf + off, nr)) < 0)
266 				error(cfp->wname);
267 	}
268 	if (sz) {
269 		if (nr == 0)
270 			badfmt();
271 		error(cfp->rname);
272 	}
273 
274 	if (cfp->flags & RPAD && size & 1 && (nr = read(from, buf, 1)) != 1) {
275 		if (nr == 0)
276 			badfmt();
277 		error(cfp->rname);
278 	}
279 	if (cfp->flags & WPAD && (size + already_written) & 1 &&
280 	    write(to, &pad, 1) != 1)
281 		error(cfp->wname);
282 }
283 
284 /*
285  * skip_arobj -
286  *	Skip over an object -- taking care to skip the pad bytes.
287  */
288 void
289 skip_arobj(fd)
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);
297 }
298