/[pkgs]/devel/hardlink/hardlink.c
ViewVC logotype

Contents of /devel/hardlink/hardlink.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.7 - (show annotations) (download) (as text)
Sun Oct 29 07:19:35 2006 UTC (3 years ago) by jnovy
Branch: MAIN
CVS Tags: F-12-split, hardlink-1_0-6_fc9, F-7-split, hardlink-1_0-2_fc7, F-10-split, hardlink-1_0-9_fc12, F-11-split, F-9-split, F-8-split, hardlink-1_0-5_fc8, hardlink-1_0-4_fc7, hardlink-1_0-7_fc9, hardlink-1_0-8_fc11, HEAD
Changes since 1.6: +2 -1 lines
File MIME type: text/x-chdr
- update docs to describe highest verbosity -vv option (#210816)
- use dist
Resolves: 210816
1 /* Copyright (C) 2001 Red Hat, Inc.
2
3 Written by Jakub Jelinek <jakub@redhat.com>.
4
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License as
7 published by the Free Software Foundation; either version 2 of the
8 License, or (at your option) 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 GNU
13 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; see the file COPYING. If not,
17 write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA. */
19
20 /* Changes by Rémy Card to use constants and add option -n. */
21 /* Changes by Jindrich Novy to add option -h. */
22
23 #define _GNU_SOURCE
24 #include <sys/types.h>
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <unistd.h>
28 #include <sys/stat.h>
29 #include <sys/mman.h>
30 #include <string.h>
31 #include <dirent.h>
32 #include <fcntl.h>
33
34 #define NHASH 131072 /* Must be a power of 2! */
35 #define NAMELEN 4096
36 #define NBUF 64
37
38 struct _f;
39 typedef struct _h {
40 struct _h *next;
41 struct _f *chain;
42 off_t size;
43 time_t mtime;
44 } h;
45
46 typedef struct _d {
47 struct _d *next;
48 char name[0];
49 } d;
50
51 d *dirs;
52
53 h *hps[NHASH];
54
55 int no_link = 0;
56 int verbose = 0;
57 int content_only = 0;
58
59 typedef struct _f {
60 struct _f *next;
61 ino_t ino;
62 dev_t dev;
63 unsigned int cksum;
64 char name[0];
65 } f;
66
67 inline unsigned int hash(off_t size, time_t mtime)
68 {
69 return (size ^ mtime) & (NHASH - 1);
70 }
71
72 inline int stcmp(struct stat *st1, struct stat *st2, int content_only)
73 {
74 if (content_only)
75 return st1->st_size != st2->st_size;
76 return st1->st_mode != st2->st_mode || st1->st_uid != st2->st_uid ||
77 st1->st_gid != st2->st_gid || st1->st_size != st2->st_size ||
78 st1->st_mtime != st2->st_mtime;
79 }
80
81 long long ndirs, nobjects, nregfiles, nmmap, ncomp, nlinks, nsaved;
82
83 void doexit(int i)
84 {
85 if (verbose) {
86 fprintf(stderr, "\n\n");
87 fprintf(stderr, "Directories %lld\n", ndirs);
88 fprintf(stderr, "Objects %lld\n", nobjects);
89 fprintf(stderr, "IFREG %lld\n", nregfiles);
90 fprintf(stderr, "Mmaps %lld\n", nmmap);
91 fprintf(stderr, "Comparisons %lld\n", ncomp);
92 fprintf(stderr, "%s %lld\n", (no_link ? "Would link" : "Linked"), nlinks);
93 fprintf(stderr, "%s %lld\n", (no_link ? "Would save" : "saved"), nsaved);
94 }
95 exit(i);
96 }
97
98 void usage(char *prog)
99 {
100 fprintf (stderr, "Usage: %s [-cnvh] directories...\n", prog);
101 fprintf (stderr, " -c When finding candidates for linking, compare only file contents.\n");
102 fprintf (stderr, " -n Don't actually link anything, just report what would be done.\n");
103 fprintf (stderr, " -v Print summary after hardlinking.\n");
104 fprintf (stderr, " -vv Print every hardlinked file and bytes saved + summary.\n");
105 fprintf (stderr, " -h Show help.\n");
106 exit(255);
107 }
108
109 unsigned int buf[NBUF];
110 char nambuf1[NAMELEN], nambuf2[NAMELEN];
111
112 void rf (char *name)
113 {
114 struct stat st, st2, st3;
115 nobjects++;
116 if (lstat (name, &st))
117 return;
118 if (S_ISDIR (st.st_mode)) {
119 d * dp = malloc(sizeof(d) + 1 + strlen (name));
120 if (!dp) {
121 fprintf(stderr, "\nOut of memory 3\n");
122 doexit(3);
123 }
124 strcpy (dp->name, name);
125 dp->next = dirs;
126 dirs = dp;
127 } else if (S_ISREG (st.st_mode)) {
128 int fd, i;
129 f * fp, * fp2;
130 h * hp;
131 char *p = NULL, *q;
132 char *n1, *n2;
133 int cksumsize = sizeof(buf);
134 unsigned int cksum;
135 time_t mtime = content_only ? 0 : st.st_mtime;
136 unsigned int hsh = hash (st.st_size, mtime);
137 nregfiles++;
138 if (verbose > 1)
139 fprintf(stderr, " %s", name);
140 fd = open (name, O_RDONLY);
141 if (fd < 0) return;
142 if (st.st_size < sizeof(buf)) {
143 cksumsize = st.st_size;
144 memset (((char *)buf) + cksumsize, 0, (sizeof(buf) - cksumsize) % sizeof(buf[0]));
145 }
146 if (read (fd, buf, cksumsize) != cksumsize) {
147 close(fd);
148 if (verbose > 1)
149 fprintf(stderr, "\r%*s\r", (int)strlen(name)+2, "");
150 return;
151 }
152 cksumsize = (cksumsize + sizeof(buf[0]) - 1) / sizeof(buf[0]);
153 for (i = 0, cksum = 0; i < cksumsize; i++) {
154 if (cksum + buf[i] < cksum)
155 cksum += buf[i] + 1;
156 else
157 cksum += buf[i];
158 }
159 for (hp = hps[hsh]; hp; hp = hp->next)
160 if (hp->size == st.st_size && hp->mtime == mtime)
161 break;
162 if (!hp) {
163 hp = malloc(sizeof(h));
164 if (!hp) {
165 fprintf(stderr, "\nOut of memory 1\n");
166 doexit(1);
167 }
168 hp->size = st.st_size;
169 hp->mtime = mtime;
170 hp->chain = NULL;
171 hp->next = hps[hsh];
172 hps[hsh] = hp;
173 }
174 for (fp = hp->chain; fp; fp = fp->next)
175 if (fp->cksum == cksum)
176 break;
177 for (fp2 = fp; fp2 && fp2->cksum == cksum; fp2 = fp2->next)
178 if (fp2->ino == st.st_ino && fp2->dev == st.st_dev) {
179 close(fd);
180 if (verbose > 1)
181 fprintf(stderr, "\r%*s\r", (int)strlen(name)+2, "");
182 return;
183 }
184 if (fp && st.st_size > 0) {
185 p = mmap (NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
186 nmmap++;
187 if (p == (void *)-1) {
188 close(fd);
189 fprintf(stderr, "\nFailed to mmap %s\n", name);
190 return;
191 }
192 }
193 for (fp2 = fp; fp2 && fp2->cksum == cksum; fp2 = fp2->next)
194 if (!lstat (fp2->name, &st2) && S_ISREG (st2.st_mode) &&
195 !stcmp (&st, &st2, content_only) &&
196 st2.st_ino != st.st_ino &&
197 st2.st_dev == st.st_dev) {
198 int fd2 = open (fp2->name, O_RDONLY);
199 if (fd2 < 0) continue;
200 if (fstat (fd2, &st2) || !S_ISREG (st2.st_mode) || st2.st_size == 0) {
201 close (fd2);
202 continue;
203 }
204 ncomp++;
205 q = mmap (NULL, st.st_size, PROT_READ, MAP_SHARED, fd2, 0);
206 if (q == (void *)-1) {
207 close(fd2);
208 fprintf(stderr, "\nFailed to mmap %s\n", fp2->name);
209 continue;
210 }
211 if (memcmp (p, q, st.st_size)) {
212 munmap (q, st.st_size);
213 close(fd2);
214 continue;
215 }
216 munmap (q, st.st_size);
217 close(fd2);
218 if (lstat (name, &st3)) {
219 fprintf(stderr, "\nCould not stat %s again\n", name);
220 munmap (p, st.st_size);
221 close(fd);
222 return;
223 }
224 st3.st_atime = st.st_atime;
225 if (stcmp (&st, &st3, 0)) {
226 fprintf(stderr, "\nFile %s changed underneath us\n", name);
227 munmap (p, st.st_size);
228 close(fd);
229 return;
230 }
231 n1 = fp2->name;
232 n2 = name;
233 if (!no_link) {
234 strcpy (stpcpy (nambuf2, n2), ".$$$___cleanit___$$$");
235 if (rename (n2, nambuf2)) {
236 fprintf(stderr, "\nFailed to rename %s to %s\n", n2, nambuf2);
237 continue;
238 }
239 if (link (n1, n2)) {
240 fprintf(stderr, "\nFailed to hardlink %s to %s\n", n1, n2);
241 if (rename (nambuf2, n2)) {
242 fprintf(stderr, "\nBad bad - failed to rename back %s to %s\n", nambuf2, n2);
243 }
244 munmap (p, st.st_size);
245 close(fd);
246 return;
247 }
248 unlink (nambuf2);
249 }
250 nlinks++;
251 if (st3.st_nlink > 1) {
252 /* We actually did not save anything this time, since the link second argument
253 had some other links as well. */
254 if (verbose > 1)
255 fprintf(stderr, "\r%*s\r%s %s to %s\n", (int)strlen(name)+2, "", (no_link ? "Would link" : "Linked"), n1, n2);
256 } else {
257 nsaved+=((st.st_size+4095)/4096)*4096;
258 if (verbose > 1)
259 fprintf(stderr, "\r%*s\r%s %s to %s, %s %ld\n", (int)strlen(name)+2, "", (no_link ? "Would link" : "Linked"), n1, n2, (no_link ? "would save" : "saved"), st.st_size);
260 }
261 munmap (p, st.st_size);
262 close(fd);
263 return;
264 }
265 if (fp)
266 munmap (p, st.st_size);
267 fp2 = malloc(sizeof(f) + 1 + strlen (name));
268 if (!fp2) {
269 fprintf(stderr, "\nOut of memory 2\n");
270 doexit(2);
271 }
272 close(fd);
273 fp2->ino = st.st_ino;
274 fp2->dev = st.st_dev;
275 fp2->cksum = cksum;
276 strcpy(fp2->name, name);
277 if (fp) {
278 fp2->next = fp->next;
279 fp->next = fp2;
280 } else {
281 fp2->next = hp->chain;
282 hp->chain = fp2;
283 }
284 if (verbose > 1)
285 fprintf(stderr, "\r%*s\r", (int)strlen(name)+2, "");
286 return;
287 }
288 }
289
290 int main(int argc, char **argv)
291 {
292 int ch;
293 int i;
294 char *p;
295 d * dp;
296 DIR *dh;
297 struct dirent *di;
298 while ((ch = getopt (argc, argv, "cnvh")) != -1) {
299 switch (ch) {
300 case 'n':
301 no_link++;
302 break;
303 case 'v':
304 verbose++;
305 break;
306 case 'c':
307 content_only++;
308 break;
309 case 'h':
310 default:
311 usage(argv[0]);
312 }
313 }
314 if (optind >= argc)
315 usage(argv[0]);
316 for (i = optind; i < argc; i++)
317 rf(argv[i]);
318 while (dirs) {
319 dp = dirs;
320 dirs = dp->next;
321 strcpy (nambuf1, dp->name);
322 free (dp);
323 strcat (nambuf1, "/");
324 p = strchr (nambuf1, 0);
325 dh = opendir (nambuf1);
326 if (dh == NULL)
327 continue;
328 ndirs++;
329 while ((di = readdir (dh)) != NULL) {
330 if (!di->d_name[0])
331 continue;
332 if (di->d_name[0] == '.') {
333 char *q;
334 if (!di->d_name[1] || !strcmp (di->d_name, "..") || !strncmp (di->d_name, ".in.", 4))
335 continue;
336 q = strrchr (di->d_name, '.');
337 if (q && strlen (q) == 7 && q != di->d_name) {
338 *p = 0;
339 if (verbose)
340 fprintf(stderr, "Skipping %s%s\n", nambuf1, di->d_name);
341 continue;
342 }
343 }
344 strcpy (p, di->d_name);
345 rf(nambuf1);
346 }
347 closedir(dh);
348 }
349 doexit(0);
350 return 0;
351 }

admin@fedoraproject.org
ViewVC Help
Powered by ViewVC 1.1.2