ar(1): remove separate xpg4 version
[unleashed.git] / usr / src / cmd / sgs / ar / common / cmd.c
blob1a643b493f428beeb286ba856d53a949506b9ff0
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
22 * Copyright (c) 1995, 2010, Oracle and/or its affiliates. All rights reserved.
26 * Copyright (c) 1988 AT&T
27 * All Rights Reserved
32 * Incompatible Archive Header
34 * The archive file member header used in SunOS 4.1 archive files and
35 * Solaris archive files are incompatible. The header file is:
36 * /usr/include/ar.h, struct ar_hdr.
37 * The member ar_name[] in Solaris comforms with Standard and the
38 * member name terminates with '/'. The SunOS's member does not terminate
39 * with '/' character. A bug 4046054 was filed:
40 * The ar command in Solaris 2.5.1 is incompatible with archives
41 * created on 4.x.
43 * To handle archive files created in SunOS 4.1 system on Solaris, the
44 * following changes were made:
46 * 1. file.c/writefile()
47 * Before writing each member files into the output
48 * archive file, ar_name[] is checked. If it is NULL,
49 * it means that the original archive header for this
50 * member was incompatible with Solaris format.
52 * The original Solaris ar command ended up having
53 * NULL name for the header. The change here uses the
54 * ar_rawname, which is much closer to the original
55 * name.
57 * 2. cmd.c
58 * For the p command, the code used to use only ar_longname
59 * to seach the matching name. The member is set to NULL
60 * if the archive member header was incompatible.
61 * The ar_rawname is also used to find the matching member name.
63 * For commands to update the archive file, we do not
64 * use ar_rawname, and just use the ar_longname. The commands are
65 * r (replace), m (modify the position) and d (delete).
68 #include "inc.h"
71 * Forward Declarations
73 static void ar_select(int *, unsigned long);
74 static void cleanup(Cmd_info *);
75 static int create_extract(ARFILE *, int, int, Cmd_info *);
76 static char *match(char *, Cmd_info *);
77 static void mesg(int, char *, Cmd_info *);
78 static void movefil(ARFILE *, struct stat *);
79 static FILE *stats(char *, struct stat *);
82 * Commands
84 void
85 rcmd(Cmd_info *cmd_info)
87 FILE *f;
88 ARFILE *fileptr;
89 ARFILE *abifile = NULL;
90 ARFILE *backptr = NULL;
91 ARFILE *endptr;
92 ARFILE *moved_files;
93 ARFILE *prev_entry, *new_listhead, *new_listend;
94 int deleted;
95 struct stat stbuf;
96 char *gfile;
98 new_listhead = NULL;
99 new_listend = NULL;
100 prev_entry = NULL;
102 for (fileptr = getfile(cmd_info);
103 fileptr; fileptr = getfile(cmd_info)) {
104 deleted = 0;
105 if (!abifile && cmd_info->ponam &&
106 strcmp(fileptr->ar_longname, cmd_info->ponam) == 0)
107 abifile = fileptr;
108 else if (!abifile)
109 backptr = fileptr;
111 if (cmd_info->namc == 0 ||
112 (gfile = match(fileptr->ar_longname, cmd_info)) != NULL) {
114 * NOTE:
115 * Refer to "Incompatible Archive Header"
116 * blocked comment at the beginning of this file.
118 f = stats(gfile, &stbuf); /* gfile is set by match */
119 if (f == NULL) {
120 if (cmd_info->namc) {
121 int err = errno;
122 (void) fprintf(stderr,
123 MSG_INTL(MSG_SYS_OPEN),
124 gfile, strerror(err));
127 * Created
129 mesg('c', gfile, cmd_info);
130 } else {
131 if ((cmd_info->opt_flgs & u_FLAG) &&
132 stbuf.st_mtime <= fileptr->ar_date) {
133 (void) fclose(f);
134 continue;
137 * Replaced
139 mesg('r', fileptr->ar_longname, cmd_info);
140 movefil(fileptr, &stbuf);
142 * Clear the previous contents.
144 if (fileptr->ar_flag & F_ELFRAW) {
146 * clear ar_elf
148 (void) elf_end(fileptr->ar_elf);
149 fileptr->ar_elf = 0;
151 /* clear 'ar_flag' */
152 fileptr->ar_flag &= ~F_ELFRAW;
155 * Defer reading contents until needed, and
156 * then use an in-kernel file-to-file transfer
157 * to avoid excessive in-process memory use.
159 fileptr->ar_contents = NULL;
161 free(fileptr->ar_pathname);
162 if ((fileptr->ar_pathname =
163 malloc(strlen(gfile) + 1)) == NULL) {
164 int err = errno;
165 (void) fprintf(stderr,
166 MSG_INTL(MSG_MALLOC),
167 strerror(err));
168 exit(1);
171 (void) strcpy(fileptr->ar_pathname, gfile);
172 (void) fclose(f);
174 if (cmd_info->ponam && (abifile != fileptr)) {
175 deleted = 1;
176 /* remove from archive list */
177 if (prev_entry != NULL)
178 prev_entry->ar_next = NULL;
179 else
180 listhead = NULL;
181 listend = prev_entry;
183 /* add to moved list */
184 if (new_listhead == NULL)
185 new_listhead = fileptr;
186 else
187 new_listend->ar_next = fileptr;
188 new_listend = fileptr;
190 cmd_info->modified++;
193 else
195 * Unchaged
197 mesg('u', fileptr->ar_longname, cmd_info);
199 if (deleted)
200 deleted = 0;
201 else
202 prev_entry = fileptr;
205 endptr = listend;
206 cleanup(cmd_info);
207 if (cmd_info->ponam && endptr &&
208 (((moved_files = endptr->ar_next) != NULL) || new_listhead)) {
209 if (!abifile) {
210 (void) fprintf(stderr, MSG_INTL(MSG_NOT_FOUND_POSNAM),
211 cmd_info->ponam);
212 exit(2);
214 endptr->ar_next = NULL;
217 * link new/moved files into archive entry list...
218 * 1: prepend newlist to moved/appended list
220 if (new_listhead) {
221 if (!moved_files)
222 listend = new_listend;
223 new_listend->ar_next = moved_files;
224 moved_files = new_listhead;
226 /* 2: insert at appropriate position... */
227 if (cmd_info->opt_flgs & b_FLAG)
228 abifile = backptr;
229 if (abifile) {
230 listend->ar_next = abifile->ar_next;
231 abifile->ar_next = moved_files;
232 } else {
233 listend->ar_next = listhead;
234 listhead = moved_files;
236 listend = endptr;
237 } else if (cmd_info->ponam && !abifile)
238 (void) fprintf(stderr, MSG_INTL(MSG_NOT_FOUND_POSNAM),
239 cmd_info->ponam);
242 void
243 dcmd(Cmd_info *cmd_info)
245 ARFILE *fptr;
246 ARFILE *backptr = NULL;
248 for (fptr = getfile(cmd_info); fptr; fptr = getfile(cmd_info)) {
249 if (match(fptr->ar_longname, cmd_info) != NULL) {
251 * NOTE:
252 * Refer to "Incompatible Archive Header"
253 * blocked comment at the beginning of this file.
257 * Deleted
259 mesg('d', fptr->ar_longname, cmd_info);
260 if (backptr == NULL) {
261 listhead = NULL;
262 listend = NULL;
263 } else {
264 backptr->ar_next = NULL;
265 listend = backptr;
267 cmd_info->modified = 1;
268 } else {
270 * Unchaged
272 mesg('u', fptr->ar_longname, cmd_info);
273 backptr = fptr;
278 void
279 xcmd(Cmd_info *cmd_info)
281 int f;
282 ARFILE *next;
283 int rawname = 0;
284 long f_len = 0;
287 * If -T is specified, get the maximum file name length.
289 if (cmd_info->opt_flgs & T_FLAG) {
290 f_len = pathconf(MSG_ORIG(MSG_STR_PERIOD), _PC_NAME_MAX);
291 if (f_len == -1) {
292 int err = errno;
293 (void) fprintf(stderr, MSG_INTL(MSG_PATHCONF),
294 strerror(err));
295 exit(1);
298 for (next = getfile(cmd_info); next; next = getfile(cmd_info)) {
299 if ((next->ar_longname[0] == 0) && (next->ar_rawname[0] != 0))
300 rawname = 1;
301 if (cmd_info->namc == 0 ||
302 match(next->ar_longname, cmd_info) != NULL ||
303 match(next->ar_rawname, cmd_info) != NULL) {
305 * NOTE:
306 * Refer to "Incompatible Archive Header"
307 * blocked comment at the beginning of this file.
309 f = create_extract(next, rawname, f_len, cmd_info);
310 if (f >= 0) {
311 if (rawname) {
313 * eXtracted
315 mesg('x', next->ar_rawname, cmd_info);
316 if (write(f, next->ar_contents,
317 (unsigned)next->ar_size) !=
318 next->ar_size) {
319 int err = errno;
320 (void) fprintf(stderr,
321 MSG_INTL(MSG_SYS_WRITE),
322 next->ar_rawname,
323 strerror(err));
324 exit(1);
326 } else {
328 * eXtracted
330 mesg('x', next->ar_longname, cmd_info);
331 if (write(f, next->ar_contents,
332 (unsigned)next->ar_size) !=
333 next->ar_size) {
334 int err = errno;
335 (void) fprintf(stderr,
336 MSG_INTL(MSG_SYS_WRITE),
337 next->ar_longname,
338 strerror(err));
339 exit(1);
342 (void) close(f);
343 } else
344 exit(1);
346 rawname = 0;
347 } /* for */
350 void
351 pcmd(Cmd_info *cmd_info)
353 ARFILE *next;
355 for (next = getfile(cmd_info); next; next = getfile(cmd_info)) {
356 if (cmd_info->namc == 0 ||
357 match(next->ar_longname, cmd_info) != NULL ||
358 match(next->ar_rawname, cmd_info) != NULL) {
360 * NOTE:
361 * Refer to "Incompatible Archive Header"
362 * blocked comment at the beginning of this file.
364 if (cmd_info->opt_flgs & v_FLAG) {
365 (void) fprintf(stdout,
366 MSG_ORIG(MSG_FMT_P_TITLE),
367 next->ar_longname);
368 (void) fflush(stdout);
370 (void) fwrite(next->ar_contents, sizeof (char),
371 next->ar_size, stdout);
376 void
377 mcmd(Cmd_info *cmd_info)
379 ARFILE *fileptr;
380 ARFILE *abifile = NULL;
381 ARFILE *tmphead = NULL;
382 ARFILE *tmpend = NULL;
383 ARFILE *backptr1 = NULL;
384 ARFILE *backptr2 = NULL;
386 for (fileptr = getfile(cmd_info);
387 fileptr; fileptr = getfile(cmd_info)) {
388 if (match(fileptr->ar_longname, cmd_info) != NULL) {
390 * position Modified
392 mesg('m', fileptr->ar_longname, cmd_info);
393 if (tmphead)
394 tmpend->ar_next = fileptr;
395 else
396 tmphead = fileptr;
397 tmpend = fileptr;
398 if (backptr1) {
399 listend = backptr1;
400 listend->ar_next = NULL;
402 else
403 listhead = NULL;
404 continue;
407 * position Unchaged
409 mesg('u', fileptr->ar_longname, cmd_info);
410 backptr1 = fileptr;
411 if (cmd_info->ponam && !abifile) {
412 if (strcmp(fileptr->ar_longname, cmd_info->ponam) == 0)
413 abifile = fileptr;
414 else
415 backptr2 = fileptr;
419 if (!tmphead)
420 return;
422 if (!cmd_info->ponam)
423 listend->ar_next = tmphead;
424 else {
425 if (!abifile) {
426 (void) fprintf(stderr, MSG_INTL(MSG_NOT_FOUND_POSNAM),
427 cmd_info->ponam);
428 exit(2);
430 if (cmd_info->opt_flgs & b_FLAG)
431 abifile = backptr2;
432 if (abifile) {
433 tmpend->ar_next = abifile->ar_next;
434 abifile->ar_next = tmphead;
435 } else {
436 tmphead->ar_next = listhead;
437 listhead = tmphead;
440 (cmd_info->modified)++;
443 void
444 tcmd(Cmd_info *cmd_info)
446 ARFILE *next;
447 int **mp;
448 char buf[DATESIZE];
449 int m1[] = {1, S_IRUSR, 'r', '-'};
450 int m2[] = {1, S_IWUSR, 'w', '-'};
451 int m3[] = {2, S_ISUID, 's', S_IXUSR, 'x', '-'};
452 int m4[] = {1, S_IRGRP, 'r', '-'};
453 int m5[] = {1, S_IWGRP, 'w', '-'};
454 int m6[] = {2, S_ISGID, 's', S_IXGRP, 'x', '-'};
455 int m7[] = {1, S_IROTH, 'r', '-'};
456 int m8[] = {1, S_IWOTH, 'w', '-'};
457 int m9[] = {2, S_ISVTX, 't', S_IXOTH, 'x', '-'};
458 int *m[10];
460 m[0] = m1;
461 m[1] = m2;
462 m[2] = m3;
463 m[3] = m4;
464 m[4] = m5;
465 m[5] = m6;
466 m[6] = m7;
467 m[7] = m8;
468 m[8] = m9;
469 m[9] = 0;
471 for (next = getfile(cmd_info); next; next = getfile(cmd_info)) {
472 if (cmd_info->namc == 0 ||
473 match(next->ar_longname, cmd_info) != NULL ||
474 match(next->ar_rawname, cmd_info) != NULL) {
476 * NOTE:
477 * Refer to "Incompatible Archive Header"
478 * blocked comment at the beginning of this file.
480 if (cmd_info->opt_flgs & v_FLAG) {
481 for (mp = &m[0]; mp < &m[9]; )
482 ar_select(*mp++, next->ar_mode);
484 (void) fprintf(stdout, MSG_ORIG(MSG_FMT_T_IDSZ),
485 next->ar_uid, next->ar_gid,
486 EC_XWORD(next->ar_size));
487 if ((strftime(buf,
488 DATESIZE, MSG_ORIG(MSG_FMT_T_DATE),
489 localtime(&(next->ar_date)))) == 0) {
490 (void) fprintf(stderr,
491 MSG_INTL(MSG_LOCALTIME));
492 exit(1);
494 (void) fprintf(stdout,
495 MSG_ORIG(MSG_FMT_SPSTRSP), buf);
497 if ((next->ar_longname[0] == 0) &&
498 (next->ar_rawname[0] != 0))
499 (void) fprintf(stdout,
500 MSG_ORIG(MSG_FMT_STRNL),
501 trim(next->ar_rawname));
502 else
503 (void) fprintf(stdout,
504 MSG_ORIG(MSG_FMT_STRNL),
505 trim(next->ar_longname));
507 } /* for */
510 void
511 qcmd(Cmd_info *cmd_info)
513 ARFILE *fptr;
515 if (cmd_info->opt_flgs & (a_FLAG | b_FLAG)) {
516 (void) fprintf(stderr, MSG_INTL(MSG_USAGE_05));
517 exit(1);
519 for (fptr = getfile(cmd_info); fptr; fptr = getfile(cmd_info))
521 cleanup(cmd_info);
525 * Supplementary functions
527 static char *
528 match(char *file, Cmd_info *cmd_info)
530 int i;
532 for (i = 0; i < cmd_info->namc; i++) {
533 if (cmd_info->namv[i] == 0)
534 continue;
535 if (strcmp(trim(cmd_info->namv[i]), file) == 0) {
536 file = cmd_info->namv[i];
537 cmd_info->namv[i] = 0;
538 return (file);
541 return (NULL);
545 * puts the file which was in the list in the linked list
547 static void
548 cleanup(Cmd_info *cmd_info)
550 int i;
551 FILE *f;
552 ARFILE *fileptr;
553 struct stat stbuf;
555 for (i = 0; i < cmd_info->namc; i++) {
556 if (cmd_info->namv[i] == 0)
557 continue;
559 * Appended
561 mesg('a', cmd_info->namv[i], cmd_info);
562 f = stats(cmd_info->namv[i], &stbuf);
563 if (f == NULL) {
564 int err = errno;
565 (void) fprintf(stderr, MSG_INTL(MSG_SYS_OPEN),
566 cmd_info->namv[i], strerror(err));
567 } else {
568 fileptr = newfile();
569 /* if short name */
570 (void) strncpy(fileptr->ar_name,
571 trim(cmd_info->namv[i]), SNAME);
573 if ((fileptr->ar_longname =
574 malloc(strlen(trim(cmd_info->namv[i])) + 1)) ==
575 NULL) {
576 int err = errno;
577 (void) fprintf(stderr, MSG_INTL(MSG_MALLOC),
578 strerror(err));
579 exit(1);
582 (void) strcpy(fileptr->ar_longname,
583 trim(cmd_info->namv[i]));
585 if ((fileptr->ar_pathname =
586 malloc(strlen(cmd_info->namv[i]) + 1)) == NULL) {
587 int err = errno;
588 (void) fprintf(stderr, MSG_INTL(MSG_MALLOC),
589 strerror(err));
590 exit(1);
593 (void) strcpy(fileptr->ar_pathname, cmd_info->namv[i]);
595 movefil(fileptr, &stbuf);
597 /* clear 'ar_flag' */
598 fileptr->ar_flag &= ~F_ELFRAW;
601 * Defer reading contents until needed, and then use
602 * an in-kernel file-to-file transfer to avoid
603 * excessive in-process memory use.
605 fileptr->ar_contents = NULL;
607 (void) fclose(f);
608 (cmd_info->modified)++;
609 cmd_info->namv[i] = 0;
615 * insert the file 'file' into the temporary file
617 static void
618 movefil(ARFILE *fileptr, struct stat *stbuf)
620 fileptr->ar_size = stbuf->st_size;
621 fileptr->ar_date = stbuf->st_mtime;
622 fileptr->ar_mode = stbuf->st_mode;
625 * The format of an 'ar' file includes a 6 character
626 * decimal string to contain the uid.
628 * If the uid or gid is too big to fit, then set it to
629 * nobody (for want of a better value). Clear the
630 * setuid/setgid bits in the mode to avoid setuid nobody
631 * or setgid nobody files unexpectedly coming into existence.
633 if ((fileptr->ar_uid = stbuf->st_uid) > 999999) {
634 fileptr->ar_uid = UID_NOBODY;
635 if (S_ISREG(fileptr->ar_mode))
636 fileptr->ar_mode &= ~S_ISUID;
638 if ((fileptr->ar_gid = stbuf->st_gid) > 999999) {
639 fileptr->ar_gid = GID_NOBODY;
640 if (S_ISREG(fileptr->ar_mode))
641 fileptr->ar_mode &= ~S_ISGID;
645 static FILE *
646 stats(char *file, struct stat *stbuf)
648 FILE *f;
650 f = fopen(file, MSG_ORIG(MSG_STR_LCR));
651 if (f == NULL)
652 return (f);
653 if (stat(file, stbuf) < 0) {
654 (void) fclose(f);
655 return (NULL);
657 return (f);
661 * Used by xcmd()
664 create_extract(ARFILE *a, int rawname, int f_len, Cmd_info *cmd_info)
667 int f;
668 char *f_name;
669 char *dup = NULL;
670 if (rawname)
671 f_name = a->ar_rawname;
672 else
673 f_name = a->ar_longname;
676 * If -T is specified, check the file length.
678 if (cmd_info->opt_flgs & T_FLAG) {
679 int len;
680 len = strlen(f_name);
681 if (f_len <= len) {
682 dup = malloc(f_len+1);
683 if (dup == NULL) {
684 int err = errno;
685 (void) fprintf(stderr, MSG_INTL(MSG_MALLOC),
686 strerror(err));
687 exit(1);
689 (void) strncpy(dup, f_name, f_len);
691 f_name = dup;
695 * Bug 4052067 - If a file to be extracted has the same
696 * filename as the archive, the archive gets overwritten
697 * which can lead to a corrupted archive or worse, a ufs
698 * deadlock because libelf has mmap'ed the archive! We
699 * can't rely on strcmp() to test for this case because
700 * the archive could be prefixed with a partial or full
701 * path (and we could be using the rawname from the archive)
702 * This means we have to do the same thing we did for mv,
703 * which is to explicitly check if the file we would extract
704 * to is identical to the archive. Because part of this
705 * test is essentially what the -C flag does, I've merged
706 * the code together.
708 if (access(f_name, F_OK) != -1) {
709 struct stat s1, s2;
712 * If -C is specified, this is an error anyway
714 if (cmd_info->opt_flgs & C_FLAG) {
715 (void) fprintf(stderr, MSG_INTL(MSG_OVERRIDE_WARN),
716 f_name);
717 free(dup);
718 return (-1);
722 * Okay, -C wasn't specified. However, now we do
723 * the check to see if the archive would be overwritten
724 * by extracting this file. stat() both objects and
725 * test to see if their identical.
727 if ((stat(f_name, &s1) == 0) &&
728 (stat(cmd_info->arnam, &s2) == 0)) {
730 if ((s1.st_dev == s2.st_dev) &&
731 (s1.st_ino == s2.st_ino)) {
733 (void) fprintf(stderr,
734 MSG_INTL(MSG_OVERRIDE_WARN), f_name);
735 free(dup);
736 return (-1);
742 * Okay to create extraction file...
744 f = creat(f_name, (mode_t)a->ar_mode & 0777);
745 if (f < 0) {
746 int err = errno;
747 (void) fprintf(stderr, MSG_INTL(MSG_SYS_OPEN), f_name,
748 strerror(err));
750 * Created
752 mesg('c', f_name, cmd_info);
754 free(dup);
755 return (f);
758 static void
759 mesg(int c, char *file, Cmd_info *cmd_info)
762 * XPG4 does not have any message defined for
763 * 'c' operation.
764 * In fact, XPG only defines messages for
765 * d, r, a and x at the present. (03/05/'96)
767 if (c == 'c' || c == 'u' || c == 'm')
768 return;
769 if (cmd_info->opt_flgs & v_FLAG)
770 (void) fprintf(stdout, MSG_ORIG(MSG_FMT_FILE), c, file);
773 static void
774 ar_select(int *pairp, unsigned long mode)
776 int n, *ap;
778 ap = pairp;
779 n = *ap++;
780 while (--n >= 0 && (mode & *ap++) == 0)
781 ap++;
782 (void) putchar(*ap);