/ [cpio] / cpio / src / copypass.c
To checkout: cvs -d:pserver:anonymous@cvs.gnu.org.ua:/cvsmirror/cpio co cpio/src/copypass.c
Puszcza

Contents of /cpio/src/copypass.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.17 - (show annotations)
Thu Jun 28 12:22:49 2007 UTC (14 years, 5 months ago) by gray
Branch: MAIN
CVS Tags: HEAD
Changes since 1.16: +4 -1 lines
File MIME type: text/plain
Save current umask before processing and call apply_delayed_set_stat afterwards

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

Send suggestions and bug reports to Sergey Poznyakoff
ViewVC Help
Powered by ViewVC 1.1.20