TODO: add an item for a chmod optimization
[platform/upstream/coreutils.git] / src / cp-hash.c
1 /* cp-hash.c  -- file copying (hash search routines)
2    Copyright (C) 89, 90, 91, 1995-2008 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 3 of the License, or
7    (at your option) 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, see <http://www.gnu.org/licenses/>.
16
17    Written by Torbjorn Granlund, Sweden (tege@sics.se).
18    Rewritten to use lib/hash.c by Jim Meyering.  */
19
20 #include <config.h>
21
22 #include <stdio.h>
23 #include <sys/types.h>
24 #include "system.h"
25
26 #include "same.h"
27 #include "quote.h"
28 #include "hash.h"
29 #include "error.h"
30 #include "cp-hash.h"
31
32 /* Use ST_DEV and ST_INO as the key, FILENAME as the value.
33    These are used e.g., in copy.c to associate the destination name with
34    the source device/inode pair so that if we encounter a matching dev/ino
35    pair in the source tree we can arrange to create a hard link between
36    the corresponding names in the destination tree.  */
37 struct Src_to_dest
38 {
39   ino_t st_ino;
40   dev_t st_dev;
41   /* Destination file name (of non-directory or pre-existing directory)
42      corresponding to the dev/ino of a copied file, or the destination file
43      name corresponding to a dev/ino pair for a newly-created directory. */
44   char *name;
45 };
46
47 /* This table maps source dev/ino to destination file name.
48    We use it to preserve hard links when copying.  */
49 static Hash_table *src_to_dest;
50
51 /* Initial size of the above hash table.  */
52 #define INITIAL_TABLE_SIZE 103
53
54 static size_t
55 src_to_dest_hash (void const *x, size_t table_size)
56 {
57   struct Src_to_dest const *p = x;
58
59   /* Ignoring the device number here should be fine.  */
60   /* The cast to uintmax_t prevents negative remainders
61      if st_ino is negative.  */
62   return (uintmax_t) p->st_ino % table_size;
63 }
64
65 /* Compare two Src_to_dest entries.
66    Return true if their keys are judged `equal'.  */
67 static bool
68 src_to_dest_compare (void const *x, void const *y)
69 {
70   struct Src_to_dest const *a = x;
71   struct Src_to_dest const *b = y;
72   return SAME_INODE (*a, *b) ? true : false;
73 }
74
75 static void
76 src_to_dest_free (void *x)
77 {
78   struct Src_to_dest *a = x;
79   free (a->name);
80   free (x);
81 }
82
83 /* Remove the entry matching INO/DEV from the table
84    that maps source ino/dev to destination file name.  */
85 extern void
86 forget_created (ino_t ino, dev_t dev)
87 {
88   struct Src_to_dest probe;
89   struct Src_to_dest *ent;
90
91   probe.st_ino = ino;
92   probe.st_dev = dev;
93   probe.name = NULL;
94
95   ent = hash_delete (src_to_dest, &probe);
96   if (ent)
97     src_to_dest_free (ent);
98 }
99
100 /* Add FILE to the list of files that we have created.
101    Return true if successful.  */
102
103 extern bool
104 remember_created (char const *file)
105 {
106   struct stat sb;
107
108   if (stat (file, &sb) < 0)
109     {
110       error (0, errno, "%s", quote (file));
111       return false;
112     }
113
114   remember_copied (file, sb.st_ino, sb.st_dev);
115   return true;
116 }
117
118 /* If INO/DEV correspond to an already-copied source file, return the
119    name of the corresponding destination file.  Otherwise, return NULL.  */
120
121 extern char *
122 src_to_dest_lookup (ino_t ino, dev_t dev)
123 {
124   struct Src_to_dest ent;
125   struct Src_to_dest const *e;
126   ent.st_ino = ino;
127   ent.st_dev = dev;
128   e = hash_lookup (src_to_dest, &ent);
129   return e ? e->name : NULL;
130 }
131
132 /* Add file NAME, copied from inode number INO and device number DEV,
133    to the list of files we have copied.
134    Return NULL if inserted, otherwise non-NULL. */
135
136 extern char *
137 remember_copied (const char *name, ino_t ino, dev_t dev)
138 {
139   struct Src_to_dest *ent;
140   struct Src_to_dest *ent_from_table;
141
142   ent = xmalloc (sizeof *ent);
143   ent->name = xstrdup (name);
144   ent->st_ino = ino;
145   ent->st_dev = dev;
146
147   ent_from_table = hash_insert (src_to_dest, ent);
148   if (ent_from_table == NULL)
149     {
150       /* Insertion failed due to lack of memory.  */
151       xalloc_die ();
152     }
153
154   /* Determine whether there was already an entry in the table
155      with a matching key.  If so, free ENT (it wasn't inserted) and
156      return the `name' from the table entry.  */
157   if (ent_from_table != ent)
158     {
159       src_to_dest_free (ent);
160       return (char *) ent_from_table->name;
161     }
162
163   /* New key;  insertion succeeded.  */
164   return NULL;
165 }
166
167 /* Initialize the hash table.  */
168 extern void
169 hash_init (void)
170 {
171   src_to_dest = hash_initialize (INITIAL_TABLE_SIZE, NULL,
172                                  src_to_dest_hash,
173                                  src_to_dest_compare,
174                                  src_to_dest_free);
175   if (src_to_dest == NULL)
176     xalloc_die ();
177 }
178
179 /* Reset the hash structure in the global variable `htab' to
180    contain no entries.  */
181
182 extern void
183 forget_all (void)
184 {
185   hash_free (src_to_dest);
186 }