xref: /original-bsd/usr.bin/ar/archive.c (revision 08cd6844)
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.7 (Berkeley) 03/21/91";
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, 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 /*
165  * put_arobj --
166  *	Write an archive member to a file.
167  */
168 put_arobj(cfp, sb)
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;
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 
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);
192 		if (options & AR_TR) {
193 			if (lname > OLDARMAXNAME) {
194 				(void)fflush(stdout);
195 				(void)fprintf(stderr,
196 				    "ar: warning: %s truncated to %.*s\n",
197 				    name, OLDARMAXNAME, name);
198 				(void)fflush(stderr);
199 			}
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 {
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 	}
226 	copy_ar(cfp, size);
227 	already_written = 0;
228 }
229 
230 /*
231  * copy_ar --
232  *	Copy size bytes from one file to another - taking care to handle the
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.
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
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.
243  */
244 copy_ar(cfp, size)
245 	CF *cfp;
246 	off_t size;
247 {
248 	static char pad = '\n';
249 	register off_t sz;
250 	register int from, nr, nw, off, to;
251 	char buf[8*1024];
252 
253 	if (!(sz = size))
254 		return;
255 
256 	from = cfp->rfd;
257 	to = cfp->wfd;
258 	sz = size;
259 	while (sz && (nr = read(from, buf, MIN(sz, sizeof(buf)))) > 0) {
260 		sz -= nr;
261 		for (off = 0; off < nr; nr -= off, off += nw)
262 			if ((nw = write(to, buf + off, nr)) < 0)
263 				error(cfp->wname);
264 	}
265 	if (sz) {
266 		if (nr == 0)
267 			badfmt();
268 		error(cfp->rname);
269 	}
270 
271 	if (cfp->flags & RPAD && size & 1 && (nr = read(from, buf, 1)) != 1) {
272 		if (nr == 0)
273 			badfmt();
274 		error(cfp->rname);
275 	}
276 	if (cfp->flags & WPAD && (size + already_written) & 1 &&
277 	    write(to, &pad, 1) != 1)
278 		error(cfp->wname);
279 }
280 
281 /*
282  * skip_arobj -
283  *	Skip over an object -- taking care to skip the pad bytes.
284  */
285 void
286 skip_arobj(fd)
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);
294 }
295