1 /* Copyright (C) 2001 Red Hat, Inc.
3 Written by Jakub Jelinek <jakub@redhat.com>.
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.
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.
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. */
20 /* Changes by Rémy Card to use constants and add option -n. */
23 #include <sys/types.h>
33 #define NHASH 131072 /* Must be a power of 2! */
66 inline unsigned int hash(off_t size
, time_t mtime
)
68 return (size
^ mtime
) & (NHASH
- 1);
71 inline int stcmp(struct stat
*st1
, struct stat
*st2
, int content_only
)
74 return st1
->st_size
!= st2
->st_size
;
75 return st1
->st_mode
!= st2
->st_mode
|| st1
->st_uid
!= st2
->st_uid
||
76 st1
->st_gid
!= st2
->st_gid
|| st1
->st_size
!= st2
->st_size
||
77 st1
->st_mtime
!= st2
->st_mtime
;
80 long long ndirs
, nobjects
, nregfiles
, nmmap
, ncomp
, nlinks
, nsaved
;
85 fprintf(stderr
, "\n\n");
86 fprintf(stderr
, "Directories %lld\n", ndirs
);
87 fprintf(stderr
, "Objects %lld\n", nobjects
);
88 fprintf(stderr
, "IFREG %lld\n", nregfiles
);
89 fprintf(stderr
, "Mmaps %lld\n", nmmap
);
90 fprintf(stderr
, "Comparisons %lld\n", ncomp
);
91 fprintf(stderr
, "%s %lld\n", (no_link
? "Would link" : "Linked"), nlinks
);
92 fprintf(stderr
, "%s %lld\n", (no_link
? "Would save" : "saved"), nsaved
);
99 fprintf (stderr
, "Usage: %s [-cnv] directories...\n", prog
);
103 unsigned int buf
[NBUF
];
104 char nambuf1
[NAMELEN
], nambuf2
[NAMELEN
];
108 struct stat st
, st2
, st3
;
110 if (lstat (name
, &st
))
112 if (S_ISDIR (st
.st_mode
)) {
113 d
* dp
= malloc(sizeof(d
) + 1 + strlen (name
));
115 fprintf(stderr
, "\nOut of memory 3\n");
118 strcpy (dp
->name
, name
);
121 } else if (S_ISREG (st
.st_mode
)) {
127 int cksumsize
= sizeof(buf
);
129 time_t mtime
= content_only
? 0 : st
.st_mtime
;
130 unsigned int hsh
= hash (st
.st_size
, mtime
);
133 fprintf(stderr
, " %s", name
);
134 fd
= open (name
, O_RDONLY
);
136 if (st
.st_size
< sizeof(buf
)) {
137 cksumsize
= st
.st_size
;
138 memset (((char *)buf
) + cksumsize
, 0, (sizeof(buf
) - cksumsize
) % sizeof(buf
[0]));
140 if (read (fd
, buf
, cksumsize
) != cksumsize
) {
143 fprintf(stderr
, "\r%*s\r", (int)strlen(name
)+2, "");
146 cksumsize
= (cksumsize
+ sizeof(buf
[0]) - 1) / sizeof(buf
[0]);
147 for (i
= 0, cksum
= 0; i
< cksumsize
; i
++) {
148 if (cksum
+ buf
[i
] < cksum
)
153 for (hp
= hps
[hsh
]; hp
; hp
= hp
->next
)
154 if (hp
->size
== st
.st_size
&& hp
->mtime
== mtime
)
157 hp
= malloc(sizeof(h
));
159 fprintf(stderr
, "\nOut of memory 1\n");
162 hp
->size
= st
.st_size
;
168 for (fp
= hp
->chain
; fp
; fp
= fp
->next
)
169 if (fp
->cksum
== cksum
)
171 for (fp2
= fp
; fp2
&& fp2
->cksum
== cksum
; fp2
= fp2
->next
)
172 if (fp2
->ino
== st
.st_ino
&& fp2
->dev
== st
.st_dev
) {
175 fprintf(stderr
, "\r%*s\r", (int)strlen(name
)+2, "");
179 p
= mmap (NULL
, st
.st_size
, PROT_READ
, MAP_SHARED
, fd
, 0);
181 if (p
== (void *)-1) {
183 fprintf(stderr
, "\nFailed to mmap %s\n", name
);
187 for (fp2
= fp
; fp2
&& fp2
->cksum
== cksum
; fp2
= fp2
->next
)
188 if (!lstat (fp2
->name
, &st2
) && S_ISREG (st2
.st_mode
) &&
189 !stcmp (&st
, &st2
, content_only
) &&
190 st2
.st_ino
!= st
.st_ino
&&
191 st2
.st_dev
== st
.st_dev
) {
192 int fd2
= open (fp2
->name
, O_RDONLY
);
193 if (fd2
< 0) continue;
194 if (fstat (fd2
, &st2
) || !S_ISREG (st2
.st_mode
)) {
199 q
= mmap (NULL
, st
.st_size
, PROT_READ
, MAP_SHARED
, fd2
, 0);
200 if (q
== (void *)-1) {
202 fprintf(stderr
, "\nFailed to mmap %s\n", fp2
->name
);
205 if (memcmp (p
, q
, st
.st_size
)) {
206 munmap (q
, st
.st_size
);
210 munmap (q
, st
.st_size
);
212 if (lstat (name
, &st3
)) {
213 fprintf(stderr
, "\nCould not stat %s again\n", name
);
214 munmap (p
, st
.st_size
);
218 st3
.st_atime
= st
.st_atime
;
219 if (stcmp (&st
, &st3
, 0)) {
220 fprintf(stderr
, "\nFile %s changed underneath us\n", name
);
221 munmap (p
, st
.st_size
);
228 strcpy (stpcpy (nambuf2
, n2
), ".$$$___cleanit___$$$");
229 if (rename (n2
, nambuf2
)) {
230 fprintf(stderr
, "\nFailed to rename %s to %s\n", n2
, nambuf2
);
234 fprintf(stderr
, "\nFailed to hardlink %s to %s\n", n1
, n2
);
235 if (rename (nambuf2
, n2
)) {
236 fprintf(stderr
, "\nBad bad - failed to rename back %s to %s\n", nambuf2
, n2
);
238 munmap (p
, st
.st_size
);
245 if (st3
.st_nlink
> 1) {
246 /* We actually did not save anything this time, since the link second argument
247 had some other links as well. */
249 fprintf(stderr
, "\r%*s\r%s %s to %s\n", (int)strlen(name
)+2, "", (no_link
? "Would link" : "Linked"), n1
, n2
);
253 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
);
255 munmap (p
, st
.st_size
);
260 munmap (p
, st
.st_size
);
261 fp2
= malloc(sizeof(f
) + 1 + strlen (name
));
263 fprintf(stderr
, "\nOut of memory 2\n");
267 fp2
->ino
= st
.st_ino
;
268 fp2
->dev
= st
.st_dev
;
270 strcpy(fp2
->name
, name
);
272 fp2
->next
= fp
->next
;
275 fp2
->next
= hp
->chain
;
279 fprintf(stderr
, "\r%*s\r", (int)strlen(name
)+2, "");
284 int main(int argc
, char **argv
)
292 while ((ch
= getopt (argc
, argv
, "cnv")) != -1) {
309 for (i
= optind
; i
< argc
; i
++)
314 strcpy (nambuf1
, dp
->name
);
316 strcat (nambuf1
, "/");
317 p
= strchr (nambuf1
, 0);
318 dh
= opendir (nambuf1
);
322 while ((di
= readdir (dh
)) != NULL
) {
325 if (di
->d_name
[0] == '.') {
327 if (!di
->d_name
[1] || !strcmp (di
->d_name
, "..") || !strncmp (di
->d_name
, ".in.", 4))
329 q
= strrchr (di
->d_name
, '.');
330 if (q
&& strlen (q
) == 7 && q
!= di
->d_name
) {
333 fprintf(stderr
, "Skipping %s%s\n", nambuf1
, di
->d_name
);
337 strcpy (p
, di
->d_name
);