1 /* copypass.c - cpio copy pass sub-function.
2 Copyright (C) 1990, 1991, 1992 Free Software Foundation, Inc.
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)
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.
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. */
19 #include <sys/types.h>
21 #include "filetypes.h"
31 /* Copy files listed on the standard input into directory `directory_name'.
32 If `link_flag', link instead of copying. */
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. */
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 (×
, sizeof (struct utimbuf
));
63 /* Copy files with names read from stdin. */
64 while (ds_fgetstr (stdin
, &input_name
, name_end
) != NULL
)
68 /* Check for blank line and ignore it if found. */
69 if (input_name
.ds_string
[0] == '\0')
71 error (0, 0, "blank line ignored");
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')))
82 if ((*xstat
) (input_name
.ds_string
, &in_file_stat
) < 0)
84 error (0, errno
, "%s", input_name
.ds_string
);
88 /* Make the name of the new file. */
89 for (slash
= input_name
.ds_string
; *slash
== '/'; ++slash
)
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
);
98 ds_resize (&output_name
, dirname_len
+ strlen (slash
) + 2);
99 strcpy (output_name
.ds_string
+ dirname_len
+ 1, slash
);
101 existing_dir
= FALSE
;
102 if (lstat (output_name
.ds_string
, &out_file_stat
) == 0)
104 if (S_ISDIR (out_file_stat
.st_mode
)
105 && S_ISDIR (in_file_stat
.st_mode
))
107 /* If there is already a directory there that
108 we are trying to create, don't complain about it. */
111 else if (!unconditional_flag
112 && in_file_stat
.st_mtime
<= out_file_stat
.st_mtime
)
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. */
118 else if (S_ISDIR (out_file_stat
.st_mode
)
119 ? rmdir (output_name
.ds_string
)
120 : unlink (output_name
.ds_string
))
122 error (0, errno
, "cannot remove current %s",
123 output_name
.ds_string
);
124 continue; /* Go to the next file. */
128 /* Do the real copy or link. */
129 if (S_ISREG (in_file_stat
.st_mode
))
132 /* Can the current file be linked to a another file?
133 Set link_name to the original file name. */
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
);
147 /* If the file was not linked, copy contents of file. */
150 in_file_des
= open (input_name
.ds_string
,
151 O_RDONLY
| O_BINARY
, 0);
154 error (0, errno
, "%s", input_name
.ds_string
);
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
)
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);
165 if (out_file_des
< 0)
167 error (0, errno
, "%s", output_name
.ds_string
);
172 copy_files_disk_to_disk (in_file_des
, out_file_des
, in_file_stat
.st_size
, input_name
.ds_string
);
173 disk_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
);
179 /* Set the attributes of the new file. */
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)
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
);
191 times
.actime
= in_file_stat
.st_atime
;
192 times
.modtime
= in_file_stat
.st_mtime
;
193 if (utime (input_name
.ds_string
, ×
) < 0)
194 error (0, errno
, "%s", input_name
.ds_string
);
195 if (utime (output_name
.ds_string
, ×
) < 0)
196 error (0, errno
, "%s", output_name
.ds_string
);
198 if (retain_time_flag
)
200 times
.actime
= times
.modtime
= in_file_stat
.st_mtime
;
201 if (utime (output_name
.ds_string
, ×
) < 0)
202 error (0, errno
, "%s", output_name
.ds_string
);
206 else if (S_ISDIR (in_file_stat
.st_mode
))
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
] == '+') )
222 output_name
.ds_string
[cdf_char
] = '\0';
226 res
= mkdir (output_name
.ds_string
, in_file_stat
.st_mode
);
231 if (res
< 0 && create_dir_flag
)
233 create_all_directories (output_name
.ds_string
);
234 res
= mkdir (output_name
.ds_string
, in_file_stat
.st_mode
);
238 /* In some odd cases where the output_name includes `.',
239 the directory may have actually been created by
240 create_all_directories(), so the mkdir will fail
241 because the directory exists. If that's the case,
242 don't complain about it. */
243 if ( (errno
!= EEXIST
) ||
244 (lstat (output_name
.ds_string
, &out_file_stat
) != 0) ||
245 !(S_ISDIR (out_file_stat
.st_mode
) ) )
247 error (0, errno
, "%s", output_name
.ds_string
);
252 if ((chown (output_name
.ds_string
,
253 set_owner_flag
? set_owner
: in_file_stat
.st_uid
,
254 set_group_flag
? set_group
: in_file_stat
.st_gid
) < 0)
256 error (0, errno
, "%s", output_name
.ds_string
);
257 /* chown may have turned off some permissions we wanted. */
258 if (chmod (output_name
.ds_string
, in_file_stat
.st_mode
) < 0)
259 error (0, errno
, "%s", output_name
.ds_string
);
262 /* Once we "hide" the directory with the chmod(),
263 we have to refer to it using name+ isntead of name. */
264 output_name
.ds_string
[cdf_char
] = '+';
266 if (retain_time_flag
)
268 times
.actime
= times
.modtime
= in_file_stat
.st_mtime
;
269 if (utime (output_name
.ds_string
, ×
) < 0)
270 error (0, errno
, "%s", output_name
.ds_string
);
274 else if (S_ISCHR (in_file_stat
.st_mode
) ||
275 S_ISBLK (in_file_stat
.st_mode
) ||
277 S_ISFIFO (in_file_stat
.st_mode
) ||
280 S_ISSOCK (in_file_stat
.st_mode
) ||
284 /* Can the current file be linked to a another file?
285 Set link_name to the original file name. */
287 /* User said to link it if possible. */
288 link_res
= link_to_name (output_name
.ds_string
,
289 input_name
.ds_string
);
290 if ( (link_res
< 0) && (in_file_stat
.st_nlink
> 1) )
291 link_res
= link_to_maj_min_ino (output_name
.ds_string
,
292 major (in_file_stat
.st_dev
),
293 minor (in_file_stat
.st_dev
),
294 in_file_stat
.st_ino
);
299 if (S_ISFIFO (in_file_stat
.st_mode
))
300 res
= mkfifo (output_name
.ds_string
, in_file_stat
.st_mode
);
303 res
= mknod (output_name
.ds_string
, in_file_stat
.st_mode
,
304 in_file_stat
.st_rdev
);
305 if (res
< 0 && create_dir_flag
)
307 create_all_directories (output_name
.ds_string
);
309 if (S_ISFIFO (in_file_stat
.st_mode
))
310 res
= mkfifo (output_name
.ds_string
, in_file_stat
.st_mode
);
313 res
= mknod (output_name
.ds_string
, in_file_stat
.st_mode
,
314 in_file_stat
.st_rdev
);
318 error (0, errno
, "%s", output_name
.ds_string
);
322 if ((chown (output_name
.ds_string
,
323 set_owner_flag
? set_owner
: in_file_stat
.st_uid
,
324 set_group_flag
? set_group
: in_file_stat
.st_gid
) < 0)
326 error (0, errno
, "%s", output_name
.ds_string
);
327 /* chown may have turned off some permissions we wanted. */
328 if (chmod (output_name
.ds_string
, in_file_stat
.st_mode
) < 0)
329 error (0, errno
, "%s", output_name
.ds_string
);
330 if (retain_time_flag
)
332 times
.actime
= times
.modtime
= in_file_stat
.st_mtime
;
333 if (utime (output_name
.ds_string
, ×
) < 0)
334 error (0, errno
, "%s", output_name
.ds_string
);
341 else if (S_ISLNK (in_file_stat
.st_mode
))
345 link_name
= (char *) xmalloc ((unsigned int) in_file_stat
.st_size
+ 1);
347 link_size
= readlink (input_name
.ds_string
, link_name
,
348 in_file_stat
.st_size
);
351 error (0, errno
, "%s", input_name
.ds_string
);
355 link_name
[link_size
] = '\0';
357 res
= UMASKED_SYMLINK (link_name
, output_name
.ds_string
,
358 in_file_stat
.st_mode
);
359 if (res
< 0 && create_dir_flag
)
361 create_all_directories (output_name
.ds_string
);
362 res
= UMASKED_SYMLINK (link_name
, output_name
.ds_string
,
363 in_file_stat
.st_mode
);
367 error (0, errno
, "%s", output_name
.ds_string
);
372 /* Set the attributes of the new link. */
374 if ((lchown (output_name
.ds_string
,
375 set_owner_flag
? set_owner
: in_file_stat
.st_uid
,
376 set_group_flag
? set_group
: in_file_stat
.st_gid
) < 0)
378 error (0, errno
, "%s", output_name
.ds_string
);
384 error (0, 0, "%s: unknown file type", input_name
.ds_string
);
388 fprintf (stderr
, "%s\n", output_name
.ds_string
);
394 fputc ('\n', stderr
);
397 res
= (output_bytes
+ io_block_size
- 1) / io_block_size
;
399 fprintf (stderr
, "1 block\n");
401 fprintf (stderr
, "%d blocks\n", res
);
405 /* Try and create a hard link from FILE_NAME to another file
406 with the given major/minor device number and inode. If no other
407 file with the same major/minor/inode numbers is known, add this file
408 to the list of known files and associated major/minor/inode numbers
409 and return -1. If another file with the same major/minor/inode
410 numbers is found, try and create another link to it using
411 link_to_name, and return 0 for success and -1 for failure. */
414 link_to_maj_min_ino (file_name
, st_dev_maj
, st_dev_min
, st_ino
)
424 /* Is the file a link to a previously copied file? */
425 link_name
= find_inode_file (st_ino
,
428 if (link_name
== NULL
)
429 add_inode (st_ino
, file_name
,
433 link_res
= link_to_name (file_name
, link_name
);
438 /* Try and create a hard link from LINK_NAME to LINK_TARGET. If
439 `create_dir_flag' is set, any non-existent (parent) directories
440 needed by LINK_NAME will be created. If the link is successfully
441 created and `verbose_flag' is set, print "LINK_TARGET linked to LINK_NAME\n".
442 If the link can not be created and `link_flag' is set, print
443 "cannot link LINK_TARGET to LINK_NAME\n". Return 0 if the link
444 is created, -1 otherwise. */
447 link_to_name (link_name
, link_target
)
454 #else /* not __MSDOS__ */
455 res
= link (link_target
, link_name
);
456 if (res
< 0 && create_dir_flag
)
458 create_all_directories (link_name
);
459 res
= link (link_target
, link_name
);
464 error (0, 0, "%s linked to %s",
465 link_target
, link_name
);
469 error (0, errno
, "cannot link %s to %s",
470 link_target
, link_name
);
472 #endif /* not __MSDOS__ */