This commit was manufactured by cvs2svn to create tag 'FreeBSD-release/1.0'.
[unix-history] / gnu / usr.bin / cpio / copypass.c
CommitLineData
78ed81a3 1/* copypass.c - cpio copy pass sub-function.
2 Copyright (C) 1990, 1991, 1992 Free Software Foundation, Inc.
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
7 any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
17
18#include <stdio.h>
19#include <sys/types.h>
20#include <sys/stat.h>
21#include "filetypes.h"
22#include "system.h"
23#include "cpiohdr.h"
24#include "dstring.h"
25#include "extern.h"
26
27#ifndef HAVE_LCHOWN
28#define lchown chown
29#endif
30
31/* Copy files listed on the standard input into directory `directory_name'.
32 If `link_flag', link instead of copying. */
33
34void
35process_copy_pass ()
36{
37 dynamic_string input_name; /* Name of file from stdin. */
38 dynamic_string output_name; /* Name of new file. */
39 int dirname_len; /* Length of `directory_name'. */
40 int res; /* Result of functions. */
41 char *slash; /* For moving past slashes in input name. */
42 struct utimbuf times; /* For resetting file times after copy. */
43 struct stat in_file_stat; /* Stat record for input file. */
44 struct stat out_file_stat; /* Stat record for output file. */
45 int in_file_des; /* Input file descriptor. */
46 int out_file_des; /* Output file descriptor. */
47 int existing_dir; /* True if file is a dir & already exists. */
48#ifdef HPUX_CDF
49 int cdf_flag;
50 int cdf_char;
51#endif
52
53 /* Initialize the copy pass. */
54 dirname_len = strlen (directory_name);
55 ds_init (&input_name, 128);
56 ds_init (&output_name, dirname_len + 2);
57 strcpy (output_name.ds_string, directory_name);
58 output_name.ds_string[dirname_len] = '/';
59 output_is_seekable = TRUE;
60 /* Initialize this in case it has members we don't know to set. */
61 bzero (&times, sizeof (struct utimbuf));
62
63 /* Copy files with names read from stdin. */
64 while (ds_fgetstr (stdin, &input_name, name_end) != NULL)
65 {
66 int link_res = -1;
67
68 /* Check for blank line and ignore it if found. */
69 if (input_name.ds_string[0] == '\0')
70 {
71 error (0, 0, "blank line ignored");
72 continue;
73 }
74
75 /* Check for current directory and ignore it if found. */
76 if (input_name.ds_string[0] == '.'
77 && (input_name.ds_string[1] == '\0'
78 || (input_name.ds_string[1] == '/'
79 && input_name.ds_string[2] == '\0')))
80 continue;
81
82 if ((*xstat) (input_name.ds_string, &in_file_stat) < 0)
83 {
84 error (0, errno, "%s", input_name.ds_string);
85 continue;
86 }
87
88 /* Make the name of the new file. */
89 for (slash = input_name.ds_string; *slash == '/'; ++slash)
90 ;
91#ifdef HPUX_CDF
92 /* For CDF's we add a 2nd `/' after all "hidden" directories.
93 This kind of a kludge, but it's what we do when creating
94 archives, and it's easier to do this than to separately
95 keep track of which directories in a path are "hidden". */
96 slash = add_cdf_double_slashes (slash);
97#endif
98 ds_resize (&output_name, dirname_len + strlen (slash) + 2);
99 strcpy (output_name.ds_string + dirname_len + 1, slash);
100
101 existing_dir = FALSE;
102 if (lstat (output_name.ds_string, &out_file_stat) == 0)
103 {
104 if (S_ISDIR (out_file_stat.st_mode)
105 && S_ISDIR (in_file_stat.st_mode))
106 {
107 /* If there is already a directory there that
108 we are trying to create, don't complain about it. */
109 existing_dir = TRUE;
110 }
111 else if (!unconditional_flag
112 && in_file_stat.st_mtime <= out_file_stat.st_mtime)
113 {
114 error (0, 0, "%s not created: newer or same age version exists",
115 output_name.ds_string);
116 continue; /* Go to the next file. */
117 }
118 else if (S_ISDIR (out_file_stat.st_mode)
119 ? rmdir (output_name.ds_string)
120 : unlink (output_name.ds_string))
121 {
122 error (0, errno, "cannot remove current %s",
123 output_name.ds_string);
124 continue; /* Go to the next file. */
125 }
126 }
127
128 /* Do the real copy or link. */
129 if (S_ISREG (in_file_stat.st_mode))
130 {
131#ifndef __MSDOS__
132 /* Can the current file be linked to a another file?
133 Set link_name to the original file name. */
134 if (link_flag)
135 /* User said to link it if possible. Try and link to
136 the original copy. If that fails we'll still try
137 and link to a copy we've already made. */
138 link_res = link_to_name (output_name.ds_string,
139 input_name.ds_string);
140 if ( (link_res < 0) && (in_file_stat.st_nlink > 1) )
141 link_res = link_to_maj_min_ino (output_name.ds_string,
142 major (in_file_stat.st_dev),
143 minor (in_file_stat.st_dev),
144 in_file_stat.st_ino);
145#endif
146
147 /* If the file was not linked, copy contents of file. */
148 if (link_res < 0)
149 {
150 in_file_des = open (input_name.ds_string,
151 O_RDONLY | O_BINARY, 0);
152 if (in_file_des < 0)
153 {
154 error (0, errno, "%s", input_name.ds_string);
155 continue;
156 }
157 out_file_des = open (output_name.ds_string,
158 O_CREAT | O_WRONLY | O_BINARY, 0600);
159 if (out_file_des < 0 && create_dir_flag)
160 {
161 create_all_directories (output_name.ds_string);
162 out_file_des = open (output_name.ds_string,
163 O_CREAT | O_WRONLY | O_BINARY, 0600);
164 }
165 if (out_file_des < 0)
166 {
167 error (0, errno, "%s", output_name.ds_string);
168 close (in_file_des);
169 continue;
170 }
171
172 copy_files (in_file_des, out_file_des, in_file_stat.st_size);
173 empty_output_buffer (out_file_des);
174 if (close (in_file_des) < 0)
175 error (0, errno, "%s", input_name.ds_string);
176 if (close (out_file_des) < 0)
177 error (0, errno, "%s", output_name.ds_string);
178
179 /* Set the attributes of the new file. */
180 if (!no_chown_flag)
181 if ((chown (output_name.ds_string,
182 set_owner_flag ? set_owner : in_file_stat.st_uid,
183 set_group_flag ? set_group : in_file_stat.st_gid) < 0)
184 && errno != EPERM)
185 error (0, errno, "%s", output_name.ds_string);
186 /* chown may have turned off some permissions we wanted. */
187 if (chmod (output_name.ds_string, in_file_stat.st_mode) < 0)
188 error (0, errno, "%s", output_name.ds_string);
189 if (reset_time_flag)
190 {
191 times.actime = in_file_stat.st_atime;
192 times.modtime = in_file_stat.st_mtime;
193 if (utime (input_name.ds_string, &times) < 0)
194 error (0, errno, "%s", input_name.ds_string);
195 if (utime (output_name.ds_string, &times) < 0)
196 error (0, errno, "%s", output_name.ds_string);
197 }
198 if (retain_time_flag)
199 {
200 times.actime = times.modtime = in_file_stat.st_mtime;
201 if (utime (output_name.ds_string, &times) < 0)
202 error (0, errno, "%s", output_name.ds_string);
203 }
204 }
205 }
206 else if (S_ISDIR (in_file_stat.st_mode))
207 {
208#ifdef HPUX_CDF
209 cdf_flag = 0;
210#endif
211 if (!existing_dir)
212 {
213#ifdef HPUX_CDF
214 /* If the directory name ends in a + and is SUID,
215 then it is a CDF. Strip the trailing + from the name
216 before creating it. */
217 cdf_char = strlen (output_name.ds_string) - 1;
218 if ( (cdf_char > 0) &&
219 (in_file_stat.st_mode & 04000) &&
220 (output_name.ds_string [cdf_char] == '+') )
221 {
222 output_name.ds_string [cdf_char] = '\0';
223 cdf_flag = 1;
224 }
225#endif
226 res = mkdir (output_name.ds_string, in_file_stat.st_mode);
227
228 }
229 else
230 res = 0;
231 if (res < 0 && create_dir_flag)
232 {
233 create_all_directories (output_name.ds_string);
234 res = mkdir (output_name.ds_string, in_file_stat.st_mode);
235 }
236 if (res < 0)
237 {
238 error (0, errno, "%s", output_name.ds_string);
239 continue;
240 }
241 if (!no_chown_flag)
242 if ((chown (output_name.ds_string,
243 set_owner_flag ? set_owner : in_file_stat.st_uid,
244 set_group_flag ? set_group : in_file_stat.st_gid) < 0)
245 && errno != EPERM)
246 error (0, errno, "%s", output_name.ds_string);
247 /* chown may have turned off some permissions we wanted. */
248 if (chmod (output_name.ds_string, in_file_stat.st_mode) < 0)
249 error (0, errno, "%s", output_name.ds_string);
250#ifdef HPUX_CDF
251 if (cdf_flag)
252 /* Once we "hide" the directory with the chmod(),
253 we have to refer to it using name+ isntead of name. */
254 output_name.ds_string [cdf_char] = '+';
255#endif
256 if (retain_time_flag)
257 {
258 times.actime = times.modtime = in_file_stat.st_mtime;
259 if (utime (output_name.ds_string, &times) < 0)
260 error (0, errno, "%s", output_name.ds_string);
261 }
262 }
263#ifndef __MSDOS__
264 else if (S_ISCHR (in_file_stat.st_mode) ||
265 S_ISBLK (in_file_stat.st_mode) ||
266#ifdef S_ISFIFO
267 S_ISFIFO (in_file_stat.st_mode) ||
268#endif
269#ifdef S_ISSOCK
270 S_ISSOCK (in_file_stat.st_mode) ||
271#endif
272 0)
273 {
274 /* Can the current file be linked to a another file?
275 Set link_name to the original file name. */
276 if (link_flag)
277 /* User said to link it if possible. */
278 link_res = link_to_name (output_name.ds_string,
279 input_name.ds_string);
280 if ( (link_res < 0) && (in_file_stat.st_nlink > 1) )
281 link_res = link_to_maj_min_ino (output_name.ds_string,
282 major (in_file_stat.st_dev),
283 minor (in_file_stat.st_dev),
284 in_file_stat.st_ino);
285
286 if (link_res < 0)
287 {
288 res = mknod (output_name.ds_string, in_file_stat.st_mode,
289 in_file_stat.st_rdev);
290 if (res < 0 && create_dir_flag)
291 {
292 create_all_directories (output_name.ds_string);
293 res = mknod (output_name.ds_string, in_file_stat.st_mode,
294 in_file_stat.st_rdev);
295 }
296 if (res < 0)
297 {
298 error (0, errno, "%s", output_name.ds_string);
299 continue;
300 }
301 if (!no_chown_flag)
302 if ((chown (output_name.ds_string,
303 set_owner_flag ? set_owner : in_file_stat.st_uid,
304 set_group_flag ? set_group : in_file_stat.st_gid) < 0)
305 && errno != EPERM)
306 error (0, errno, "%s", output_name.ds_string);
307 /* chown may have turned off some permissions we wanted. */
308 if (chmod (output_name.ds_string, in_file_stat.st_mode) < 0)
309 error (0, errno, "%s", output_name.ds_string);
310 if (retain_time_flag)
311 {
312 times.actime = times.modtime = in_file_stat.st_mtime;
313 if (utime (output_name.ds_string, &times) < 0)
314 error (0, errno, "%s", output_name.ds_string);
315 }
316 }
317 }
318#endif
319
320#ifdef S_ISLNK
321 else if (S_ISLNK (in_file_stat.st_mode))
322 {
323 char *link_name;
324 link_name = (char *) xmalloc ((unsigned int) in_file_stat.st_size + 1);
325
326 if (readlink (input_name.ds_string, link_name,
327 in_file_stat.st_size) < 0)
328 {
329 error (0, errno, "%s", input_name.ds_string);
330 free (link_name);
331 continue;
332 }
333 link_name[in_file_stat.st_size] = '\0';
334
335 res = UMASKED_SYMLINK (link_name, output_name.ds_string,
336 in_file_stat.st_mode);
337 if (res < 0 && create_dir_flag)
338 {
339 create_all_directories (output_name.ds_string);
340 res = UMASKED_SYMLINK (link_name, output_name.ds_string,
341 in_file_stat.st_mode);
342 }
343 if (res < 0)
344 {
345 error (0, errno, "%s", output_name.ds_string);
346 free (link_name);
347 continue;
348 }
349
350 /* Set the attributes of the new link. */
351 if (!no_chown_flag)
352 if ((lchown (output_name.ds_string,
353 set_owner_flag ? set_owner : in_file_stat.st_uid,
354 set_group_flag ? set_group : in_file_stat.st_gid) < 0)
355 && errno != EPERM)
356 error (0, errno, "%s", output_name.ds_string);
357 free (link_name);
358 }
359#endif
360 else
361 {
362 error (0, 0, "%s: unknown file type", input_name.ds_string);
363 }
364
365 if (verbose_flag)
366 fprintf (stderr, "%s\n", output_name.ds_string);
367 if (dot_flag)
368 fputc ('.', stderr);
369 }
370
371 if (dot_flag)
372 fputc ('\n', stderr);
373 res = (output_bytes + io_block_size - 1) / io_block_size;
374 if (res == 1)
375 fprintf (stderr, "1 block\n");
376 else
377 fprintf (stderr, "%d blocks\n", res);
378}
379\f
380/* Try and create a hard link from FILE_NAME to another file
381 with the given major/minor device number and inode. If no other
382 file with the same major/minor/inode numbers is known, add this file
383 to the list of known files and associated major/minor/inode numbers
384 and return -1. If another file with the same major/minor/inode
385 numbers is found, try and create another link to it using
386 link_to_name, and return 0 for success and -1 for failure. */
387
388int
389link_to_maj_min_ino (file_name, st_dev_maj, st_dev_min, st_ino)
390 char *file_name;
391 int st_dev_maj;
392 int st_dev_min;
393 int st_ino;
394{
395 int link_res;
396 char *link_name;
397 link_res = -1;
398#ifndef __MSDOS__
399 /* Is the file a link to a previously copied file? */
400 link_name = find_inode_file (st_ino,
401 st_dev_maj,
402 st_dev_min);
403 if (link_name == NULL)
404 add_inode (st_ino, file_name,
405 st_dev_maj,
406 st_dev_min);
407 else
408 link_res = link_to_name (file_name, link_name);
409#endif
410 return link_res;
411}
412\f
413/* Try and create a hard link from LINK_NAME to LINK_TARGET. If
414 `create_dir_flag' is set, any non-existent (parent) directories
415 needed by LINK_NAME will be created. If the link is successfully
416 created and `verbose_flag' is set, print "LINK_TARGET linked to LINK_NAME\n".
417 If the link can not be created and `link_flag' is set, print
418 "cannot link LINK_TARGET to LINK_NAME\n". Return 0 if the link
419 is created, -1 otherwise. */
420
421int
422link_to_name (link_name, link_target)
423 char *link_name;
424 char *link_target;
425{
426 int res;
427#ifdef __MSDOS__
428 res = -1;
429#else /* not __MSDOS__ */
430 res = link (link_target, link_name);
431 if (res < 0 && create_dir_flag)
432 {
433 create_all_directories (link_name);
434 res = link (link_target, link_name);
435 }
436 if (res == 0)
437 {
438 if (verbose_flag)
439 error (0, 0, "%s linked to %s",
440 link_target, link_name);
441 }
442 else if (link_flag)
443 {
444 error (0, errno, "cannot link %s to %s",
445 link_target, link_name);
446 }
447#endif /* not __MSDOS__ */
448 return res;
449}