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]
22 * Copyright (c) 1995, 2010, Oracle and/or its affiliates. All rights reserved.
26 * Copyright (c) 1988 AT&T
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
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
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).
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
*);
85 rcmd(Cmd_info
*cmd_info
)
89 ARFILE
*abifile
= NULL
;
90 ARFILE
*backptr
= NULL
;
93 ARFILE
*prev_entry
, *new_listhead
, *new_listend
;
102 for (fileptr
= getfile(cmd_info
);
103 fileptr
; fileptr
= getfile(cmd_info
)) {
105 if (!abifile
&& cmd_info
->ponam
&&
106 strcmp(fileptr
->ar_longname
, cmd_info
->ponam
) == 0)
111 if (cmd_info
->namc
== 0 ||
112 (gfile
= match(fileptr
->ar_longname
, cmd_info
)) != NULL
) {
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 */
120 if (cmd_info
->namc
) {
122 (void) fprintf(stderr
,
123 MSG_INTL(MSG_SYS_OPEN
),
124 gfile
, strerror(err
));
129 mesg('c', gfile
, cmd_info
);
131 if ((cmd_info
->opt_flgs
& u_FLAG
) &&
132 stbuf
.st_mtime
<= fileptr
->ar_date
) {
139 mesg('r', fileptr
->ar_longname
, cmd_info
);
140 movefil(fileptr
, &stbuf
);
142 * Clear the previous contents.
144 if (fileptr
->ar_flag
& F_ELFRAW
) {
148 (void) elf_end(fileptr
->ar_elf
);
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 if (fileptr
->ar_pathname
!= NULL
)
162 free(fileptr
->ar_pathname
);
163 if ((fileptr
->ar_pathname
=
164 malloc(strlen(gfile
) + 1)) == NULL
) {
166 (void) fprintf(stderr
,
167 MSG_INTL(MSG_MALLOC
),
172 (void) strcpy(fileptr
->ar_pathname
, gfile
);
175 if (cmd_info
->ponam
&& (abifile
!= fileptr
)) {
177 /* remove from archive list */
178 if (prev_entry
!= NULL
)
179 prev_entry
->ar_next
= NULL
;
182 listend
= prev_entry
;
184 /* add to moved list */
185 if (new_listhead
== NULL
)
186 new_listhead
= fileptr
;
188 new_listend
->ar_next
= fileptr
;
189 new_listend
= fileptr
;
191 cmd_info
->modified
++;
198 mesg('u', fileptr
->ar_longname
, cmd_info
);
203 prev_entry
= fileptr
;
208 if (cmd_info
->ponam
&& endptr
&&
209 (((moved_files
= endptr
->ar_next
) != NULL
) || new_listhead
)) {
211 (void) fprintf(stderr
, MSG_INTL(MSG_NOT_FOUND_POSNAM
),
215 endptr
->ar_next
= NULL
;
218 * link new/moved files into archive entry list...
219 * 1: prepend newlist to moved/appended list
223 listend
= new_listend
;
224 new_listend
->ar_next
= moved_files
;
225 moved_files
= new_listhead
;
227 /* 2: insert at appropriate position... */
228 if (cmd_info
->opt_flgs
& b_FLAG
)
231 listend
->ar_next
= abifile
->ar_next
;
232 abifile
->ar_next
= moved_files
;
234 listend
->ar_next
= listhead
;
235 listhead
= moved_files
;
238 } else if (cmd_info
->ponam
&& !abifile
)
239 (void) fprintf(stderr
, MSG_INTL(MSG_NOT_FOUND_POSNAM
),
244 dcmd(Cmd_info
*cmd_info
)
247 ARFILE
*backptr
= NULL
;
249 for (fptr
= getfile(cmd_info
); fptr
; fptr
= getfile(cmd_info
)) {
250 if (match(fptr
->ar_longname
, cmd_info
) != NULL
) {
253 * Refer to "Incompatible Archive Header"
254 * blocked comment at the beginning of this file.
260 mesg('d', fptr
->ar_longname
, cmd_info
);
261 if (backptr
== NULL
) {
265 backptr
->ar_next
= NULL
;
268 cmd_info
->modified
= 1;
273 mesg('u', fptr
->ar_longname
, cmd_info
);
280 xcmd(Cmd_info
*cmd_info
)
288 * If -T is specified, get the maximum file name length.
290 if (cmd_info
->opt_flgs
& T_FLAG
) {
291 f_len
= pathconf(MSG_ORIG(MSG_STR_PERIOD
), _PC_NAME_MAX
);
294 (void) fprintf(stderr
, MSG_INTL(MSG_PATHCONF
),
299 for (next
= getfile(cmd_info
); next
; next
= getfile(cmd_info
)) {
300 if ((next
->ar_longname
[0] == 0) && (next
->ar_rawname
[0] != 0))
302 if (cmd_info
->namc
== 0 ||
303 match(next
->ar_longname
, cmd_info
) != NULL
||
304 match(next
->ar_rawname
, cmd_info
) != NULL
) {
307 * Refer to "Incompatible Archive Header"
308 * blocked comment at the beginning of this file.
310 f
= create_extract(next
, rawname
, f_len
, cmd_info
);
316 mesg('x', next
->ar_rawname
, cmd_info
);
317 if (write(f
, next
->ar_contents
,
318 (unsigned)next
->ar_size
) !=
321 (void) fprintf(stderr
,
322 MSG_INTL(MSG_SYS_WRITE
),
331 mesg('x', next
->ar_longname
, cmd_info
);
332 if (write(f
, next
->ar_contents
,
333 (unsigned)next
->ar_size
) !=
336 (void) fprintf(stderr
,
337 MSG_INTL(MSG_SYS_WRITE
),
352 pcmd(Cmd_info
*cmd_info
)
356 for (next
= getfile(cmd_info
); next
; next
= getfile(cmd_info
)) {
357 if (cmd_info
->namc
== 0 ||
358 match(next
->ar_longname
, cmd_info
) != NULL
||
359 match(next
->ar_rawname
, cmd_info
) != NULL
) {
362 * Refer to "Incompatible Archive Header"
363 * blocked comment at the beginning of this file.
365 if (cmd_info
->opt_flgs
& v_FLAG
) {
366 (void) fprintf(stdout
,
367 MSG_ORIG(MSG_FMT_P_TITLE
),
369 (void) fflush(stdout
);
371 (void) fwrite(next
->ar_contents
, sizeof (char),
372 next
->ar_size
, stdout
);
378 mcmd(Cmd_info
*cmd_info
)
381 ARFILE
*abifile
= NULL
;
382 ARFILE
*tmphead
= NULL
;
383 ARFILE
*tmpend
= NULL
;
384 ARFILE
*backptr1
= NULL
;
385 ARFILE
*backptr2
= NULL
;
387 for (fileptr
= getfile(cmd_info
);
388 fileptr
; fileptr
= getfile(cmd_info
)) {
389 if (match(fileptr
->ar_longname
, cmd_info
) != NULL
) {
393 mesg('m', fileptr
->ar_longname
, cmd_info
);
395 tmpend
->ar_next
= fileptr
;
401 listend
->ar_next
= NULL
;
410 mesg('u', fileptr
->ar_longname
, cmd_info
);
412 if (cmd_info
->ponam
&& !abifile
) {
413 if (strcmp(fileptr
->ar_longname
, cmd_info
->ponam
) == 0)
423 if (!cmd_info
->ponam
)
424 listend
->ar_next
= tmphead
;
427 (void) fprintf(stderr
, MSG_INTL(MSG_NOT_FOUND_POSNAM
),
431 if (cmd_info
->opt_flgs
& b_FLAG
)
434 tmpend
->ar_next
= abifile
->ar_next
;
435 abifile
->ar_next
= tmphead
;
437 tmphead
->ar_next
= listhead
;
441 (cmd_info
->modified
)++;
445 tcmd(Cmd_info
*cmd_info
)
450 int m1
[] = {1, S_IRUSR
, 'r', '-'};
451 int m2
[] = {1, S_IWUSR
, 'w', '-'};
452 int m3
[] = {2, S_ISUID
, 's', S_IXUSR
, 'x', '-'};
453 int m4
[] = {1, S_IRGRP
, 'r', '-'};
454 int m5
[] = {1, S_IWGRP
, 'w', '-'};
455 int m6
[] = {2, S_ISGID
, 's', S_IXGRP
, 'x', '-'};
456 int m7
[] = {1, S_IROTH
, 'r', '-'};
457 int m8
[] = {1, S_IWOTH
, 'w', '-'};
458 int m9
[] = {2, S_ISVTX
, 't', S_IXOTH
, 'x', '-'};
472 for (next
= getfile(cmd_info
); next
; next
= getfile(cmd_info
)) {
473 if (cmd_info
->namc
== 0 ||
474 match(next
->ar_longname
, cmd_info
) != NULL
||
475 match(next
->ar_rawname
, cmd_info
) != NULL
) {
478 * Refer to "Incompatible Archive Header"
479 * blocked comment at the beginning of this file.
481 if (cmd_info
->opt_flgs
& v_FLAG
) {
482 for (mp
= &m
[0]; mp
< &m
[9]; )
483 ar_select(*mp
++, next
->ar_mode
);
485 (void) fprintf(stdout
, MSG_ORIG(MSG_FMT_T_IDSZ
),
486 next
->ar_uid
, next
->ar_gid
,
487 EC_XWORD(next
->ar_size
));
489 DATESIZE
, MSG_ORIG(MSG_FMT_T_DATE
),
490 localtime(&(next
->ar_date
)))) == 0) {
491 (void) fprintf(stderr
,
492 MSG_INTL(MSG_LOCALTIME
));
495 (void) fprintf(stdout
,
496 MSG_ORIG(MSG_FMT_SPSTRSP
), buf
);
498 if ((next
->ar_longname
[0] == 0) &&
499 (next
->ar_rawname
[0] != 0))
500 (void) fprintf(stdout
,
501 MSG_ORIG(MSG_FMT_STRNL
),
502 trim(next
->ar_rawname
));
504 (void) fprintf(stdout
,
505 MSG_ORIG(MSG_FMT_STRNL
),
506 trim(next
->ar_longname
));
512 qcmd(Cmd_info
*cmd_info
)
516 if (cmd_info
->opt_flgs
& (a_FLAG
| b_FLAG
)) {
517 (void) fprintf(stderr
, MSG_INTL(MSG_USAGE_05
));
520 for (fptr
= getfile(cmd_info
); fptr
; fptr
= getfile(cmd_info
))
526 * Supplementary functions
529 match(char *file
, Cmd_info
*cmd_info
)
533 for (i
= 0; i
< cmd_info
->namc
; i
++) {
534 if (cmd_info
->namv
[i
] == 0)
536 if (strcmp(trim(cmd_info
->namv
[i
]), file
) == 0) {
537 file
= cmd_info
->namv
[i
];
538 cmd_info
->namv
[i
] = 0;
546 * puts the file which was in the list in the linked list
549 cleanup(Cmd_info
*cmd_info
)
556 for (i
= 0; i
< cmd_info
->namc
; i
++) {
557 if (cmd_info
->namv
[i
] == 0)
562 mesg('a', cmd_info
->namv
[i
], cmd_info
);
563 f
= stats(cmd_info
->namv
[i
], &stbuf
);
566 (void) fprintf(stderr
, MSG_INTL(MSG_SYS_OPEN
),
567 cmd_info
->namv
[i
], strerror(err
));
571 (void) strncpy(fileptr
->ar_name
,
572 trim(cmd_info
->namv
[i
]), SNAME
);
574 if ((fileptr
->ar_longname
=
575 malloc(strlen(trim(cmd_info
->namv
[i
])) + 1)) ==
578 (void) fprintf(stderr
, MSG_INTL(MSG_MALLOC
),
583 (void) strcpy(fileptr
->ar_longname
,
584 trim(cmd_info
->namv
[i
]));
586 if ((fileptr
->ar_pathname
=
587 malloc(strlen(cmd_info
->namv
[i
]) + 1)) == NULL
) {
589 (void) fprintf(stderr
, MSG_INTL(MSG_MALLOC
),
594 (void) strcpy(fileptr
->ar_pathname
, cmd_info
->namv
[i
]);
596 movefil(fileptr
, &stbuf
);
598 /* clear 'ar_flag' */
599 fileptr
->ar_flag
&= ~F_ELFRAW
;
602 * Defer reading contents until needed, and then use
603 * an in-kernel file-to-file transfer to avoid
604 * excessive in-process memory use.
606 fileptr
->ar_contents
= NULL
;
609 (cmd_info
->modified
)++;
610 cmd_info
->namv
[i
] = 0;
616 * insert the file 'file' into the temporary file
619 movefil(ARFILE
*fileptr
, struct stat
*stbuf
)
621 fileptr
->ar_size
= stbuf
->st_size
;
622 fileptr
->ar_date
= stbuf
->st_mtime
;
623 fileptr
->ar_mode
= stbuf
->st_mode
;
626 * The format of an 'ar' file includes a 6 character
627 * decimal string to contain the uid.
629 * If the uid or gid is too big to fit, then set it to
630 * nobody (for want of a better value). Clear the
631 * setuid/setgid bits in the mode to avoid setuid nobody
632 * or setgid nobody files unexpectedly coming into existence.
634 if ((fileptr
->ar_uid
= stbuf
->st_uid
) > 999999) {
635 fileptr
->ar_uid
= UID_NOBODY
;
636 if (S_ISREG(fileptr
->ar_mode
))
637 fileptr
->ar_mode
&= ~S_ISUID
;
639 if ((fileptr
->ar_gid
= stbuf
->st_gid
) > 999999) {
640 fileptr
->ar_gid
= GID_NOBODY
;
641 if (S_ISREG(fileptr
->ar_mode
))
642 fileptr
->ar_mode
&= ~S_ISGID
;
647 stats(char *file
, struct stat
*stbuf
)
651 f
= fopen(file
, MSG_ORIG(MSG_STR_LCR
));
654 if (stat(file
, stbuf
) < 0) {
665 create_extract(ARFILE
*a
, int rawname
, int f_len
, Cmd_info
*cmd_info
)
672 f_name
= a
->ar_rawname
;
674 f_name
= a
->ar_longname
;
677 * If -T is specified, check the file length.
679 if (cmd_info
->opt_flgs
& T_FLAG
) {
681 len
= strlen(f_name
);
683 dup
= malloc(f_len
+1);
686 (void) fprintf(stderr
, MSG_INTL(MSG_MALLOC
),
690 (void) strncpy(dup
, f_name
, f_len
);
696 * Bug 4052067 - If a file to be extracted has the same
697 * filename as the archive, the archive gets overwritten
698 * which can lead to a corrupted archive or worse, a ufs
699 * deadlock because libelf has mmap'ed the archive! We
700 * can't rely on strcmp() to test for this case because
701 * the archive could be prefixed with a partial or full
702 * path (and we could be using the rawname from the archive)
703 * This means we have to do the same thing we did for mv,
704 * which is to explicitly check if the file we would extract
705 * to is identical to the archive. Because part of this
706 * test is essentially what the -C flag does, I've merged
709 if (access(f_name
, F_OK
) != -1) {
713 * If -C is specified, this is an error anyway
715 if (cmd_info
->opt_flgs
& C_FLAG
) {
716 (void) fprintf(stderr
, MSG_INTL(MSG_OVERRIDE_WARN
),
724 * Okay, -C wasn't specified. However, now we do
725 * the check to see if the archive would be overwritten
726 * by extracting this file. stat() both objects and
727 * test to see if their identical.
729 if ((stat(f_name
, &s1
) == 0) &&
730 (stat(cmd_info
->arnam
, &s2
) == 0)) {
732 if ((s1
.st_dev
== s2
.st_dev
) &&
733 (s1
.st_ino
== s2
.st_ino
)) {
735 (void) fprintf(stderr
,
736 MSG_INTL(MSG_OVERRIDE_WARN
), f_name
);
745 * Okay to create extraction file...
747 f
= creat(f_name
, (mode_t
)a
->ar_mode
& 0777);
750 (void) fprintf(stderr
, MSG_INTL(MSG_SYS_OPEN
), f_name
,
755 mesg('c', f_name
, cmd_info
);
763 mesg(int c
, char *file
, Cmd_info
*cmd_info
)
767 * XPG4 does not have any message defined for
769 * In fact, XPG only defines messages for
770 * d, r, a and x at the present. (03/05/'96)
772 if (c
== 'c' || c
== 'u' || c
== 'm')
776 * If 'u' is passed, convert it to 'c'.
777 * 'u' makes more sense since the operation did not
778 * do anything, Unchanged, but 'c' has been used so
779 * I do no want to break the compatibility at this moment.
784 if (cmd_info
->opt_flgs
& v_FLAG
)
786 (void) fprintf(stdout
, MSG_ORIG(MSG_FMT_FILE
), c
, file
);
790 ar_select(int *pairp
, unsigned long mode
)
796 while (--n
>= 0 && (mode
& *ap
++) == 0)