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 free(fileptr
->ar_pathname
);
162 if ((fileptr
->ar_pathname
=
163 malloc(strlen(gfile
) + 1)) == NULL
) {
165 (void) fprintf(stderr
,
166 MSG_INTL(MSG_MALLOC
),
171 (void) strcpy(fileptr
->ar_pathname
, gfile
);
174 if (cmd_info
->ponam
&& (abifile
!= fileptr
)) {
176 /* remove from archive list */
177 if (prev_entry
!= NULL
)
178 prev_entry
->ar_next
= NULL
;
181 listend
= prev_entry
;
183 /* add to moved list */
184 if (new_listhead
== NULL
)
185 new_listhead
= fileptr
;
187 new_listend
->ar_next
= fileptr
;
188 new_listend
= fileptr
;
190 cmd_info
->modified
++;
197 mesg('u', fileptr
->ar_longname
, cmd_info
);
202 prev_entry
= fileptr
;
207 if (cmd_info
->ponam
&& endptr
&&
208 (((moved_files
= endptr
->ar_next
) != NULL
) || new_listhead
)) {
210 (void) fprintf(stderr
, MSG_INTL(MSG_NOT_FOUND_POSNAM
),
214 endptr
->ar_next
= NULL
;
217 * link new/moved files into archive entry list...
218 * 1: prepend newlist to moved/appended list
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
)
230 listend
->ar_next
= abifile
->ar_next
;
231 abifile
->ar_next
= moved_files
;
233 listend
->ar_next
= listhead
;
234 listhead
= moved_files
;
237 } else if (cmd_info
->ponam
&& !abifile
)
238 (void) fprintf(stderr
, MSG_INTL(MSG_NOT_FOUND_POSNAM
),
243 dcmd(Cmd_info
*cmd_info
)
246 ARFILE
*backptr
= NULL
;
248 for (fptr
= getfile(cmd_info
); fptr
; fptr
= getfile(cmd_info
)) {
249 if (match(fptr
->ar_longname
, cmd_info
) != NULL
) {
252 * Refer to "Incompatible Archive Header"
253 * blocked comment at the beginning of this file.
259 mesg('d', fptr
->ar_longname
, cmd_info
);
260 if (backptr
== NULL
) {
264 backptr
->ar_next
= NULL
;
267 cmd_info
->modified
= 1;
272 mesg('u', fptr
->ar_longname
, cmd_info
);
279 xcmd(Cmd_info
*cmd_info
)
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
);
293 (void) fprintf(stderr
, MSG_INTL(MSG_PATHCONF
),
298 for (next
= getfile(cmd_info
); next
; next
= getfile(cmd_info
)) {
299 if ((next
->ar_longname
[0] == 0) && (next
->ar_rawname
[0] != 0))
301 if (cmd_info
->namc
== 0 ||
302 match(next
->ar_longname
, cmd_info
) != NULL
||
303 match(next
->ar_rawname
, cmd_info
) != NULL
) {
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
);
315 mesg('x', next
->ar_rawname
, cmd_info
);
316 if (write(f
, next
->ar_contents
,
317 (unsigned)next
->ar_size
) !=
320 (void) fprintf(stderr
,
321 MSG_INTL(MSG_SYS_WRITE
),
330 mesg('x', next
->ar_longname
, cmd_info
);
331 if (write(f
, next
->ar_contents
,
332 (unsigned)next
->ar_size
) !=
335 (void) fprintf(stderr
,
336 MSG_INTL(MSG_SYS_WRITE
),
351 pcmd(Cmd_info
*cmd_info
)
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
) {
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
),
368 (void) fflush(stdout
);
370 (void) fwrite(next
->ar_contents
, sizeof (char),
371 next
->ar_size
, stdout
);
377 mcmd(Cmd_info
*cmd_info
)
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
) {
392 mesg('m', fileptr
->ar_longname
, cmd_info
);
394 tmpend
->ar_next
= fileptr
;
400 listend
->ar_next
= NULL
;
409 mesg('u', fileptr
->ar_longname
, cmd_info
);
411 if (cmd_info
->ponam
&& !abifile
) {
412 if (strcmp(fileptr
->ar_longname
, cmd_info
->ponam
) == 0)
422 if (!cmd_info
->ponam
)
423 listend
->ar_next
= tmphead
;
426 (void) fprintf(stderr
, MSG_INTL(MSG_NOT_FOUND_POSNAM
),
430 if (cmd_info
->opt_flgs
& b_FLAG
)
433 tmpend
->ar_next
= abifile
->ar_next
;
434 abifile
->ar_next
= tmphead
;
436 tmphead
->ar_next
= listhead
;
440 (cmd_info
->modified
)++;
444 tcmd(Cmd_info
*cmd_info
)
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', '-'};
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
) {
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
));
488 DATESIZE
, MSG_ORIG(MSG_FMT_T_DATE
),
489 localtime(&(next
->ar_date
)))) == 0) {
490 (void) fprintf(stderr
,
491 MSG_INTL(MSG_LOCALTIME
));
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
));
503 (void) fprintf(stdout
,
504 MSG_ORIG(MSG_FMT_STRNL
),
505 trim(next
->ar_longname
));
511 qcmd(Cmd_info
*cmd_info
)
515 if (cmd_info
->opt_flgs
& (a_FLAG
| b_FLAG
)) {
516 (void) fprintf(stderr
, MSG_INTL(MSG_USAGE_05
));
519 for (fptr
= getfile(cmd_info
); fptr
; fptr
= getfile(cmd_info
))
525 * Supplementary functions
528 match(char *file
, Cmd_info
*cmd_info
)
532 for (i
= 0; i
< cmd_info
->namc
; i
++) {
533 if (cmd_info
->namv
[i
] == 0)
535 if (strcmp(trim(cmd_info
->namv
[i
]), file
) == 0) {
536 file
= cmd_info
->namv
[i
];
537 cmd_info
->namv
[i
] = 0;
545 * puts the file which was in the list in the linked list
548 cleanup(Cmd_info
*cmd_info
)
555 for (i
= 0; i
< cmd_info
->namc
; i
++) {
556 if (cmd_info
->namv
[i
] == 0)
561 mesg('a', cmd_info
->namv
[i
], cmd_info
);
562 f
= stats(cmd_info
->namv
[i
], &stbuf
);
565 (void) fprintf(stderr
, MSG_INTL(MSG_SYS_OPEN
),
566 cmd_info
->namv
[i
], strerror(err
));
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)) ==
577 (void) fprintf(stderr
, MSG_INTL(MSG_MALLOC
),
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
) {
588 (void) fprintf(stderr
, MSG_INTL(MSG_MALLOC
),
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
;
608 (cmd_info
->modified
)++;
609 cmd_info
->namv
[i
] = 0;
615 * insert the file 'file' into the temporary file
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
;
646 stats(char *file
, struct stat
*stbuf
)
650 f
= fopen(file
, MSG_ORIG(MSG_STR_LCR
));
653 if (stat(file
, stbuf
) < 0) {
664 create_extract(ARFILE
*a
, int rawname
, int f_len
, Cmd_info
*cmd_info
)
671 f_name
= a
->ar_rawname
;
673 f_name
= a
->ar_longname
;
676 * If -T is specified, check the file length.
678 if (cmd_info
->opt_flgs
& T_FLAG
) {
680 len
= strlen(f_name
);
682 dup
= malloc(f_len
+1);
685 (void) fprintf(stderr
, MSG_INTL(MSG_MALLOC
),
689 (void) strncpy(dup
, f_name
, f_len
);
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
708 if (access(f_name
, F_OK
) != -1) {
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
),
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
);
742 * Okay to create extraction file...
744 f
= creat(f_name
, (mode_t
)a
->ar_mode
& 0777);
747 (void) fprintf(stderr
, MSG_INTL(MSG_SYS_OPEN
), f_name
,
752 mesg('c', f_name
, cmd_info
);
759 mesg(int c
, char *file
, Cmd_info
*cmd_info
)
762 * XPG4 does not have any message defined for
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')
769 if (cmd_info
->opt_flgs
& v_FLAG
)
770 (void) fprintf(stdout
, MSG_ORIG(MSG_FMT_FILE
), c
, file
);
774 ar_select(int *pairp
, unsigned long mode
)
780 while (--n
>= 0 && (mode
& *ap
++) == 0)