]>
git.ipfire.org Git - thirdparty/util-linux.git/blob - misc-utils/hardlink.c
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
);
97 void usage(char *prog
)
99 fprintf (stderr
, "Usage: %s [-cnvh] directories...\n", prog
);
100 fprintf (stderr
, " -c When finding candidates for linking, compare only file contents.\n");
101 fprintf (stderr
, " -n Don't actually link anything, just report what would be done.\n");
102 fprintf (stderr
, " -v Operate in verbose mode.\n");
103 fprintf (stderr
, " -h Show help.\n");
107 unsigned int buf
[NBUF
];
108 char nambuf1
[NAMELEN
], nambuf2
[NAMELEN
];
112 struct stat st
, st2
, st3
;
114 if (lstat (name
, &st
))
116 if (S_ISDIR (st
.st_mode
)) {
117 d
* dp
= malloc(sizeof(d
) + 1 + strlen (name
));
119 fprintf(stderr
, "\nOut of memory 3\n");
122 strcpy (dp
->name
, name
);
125 } else if (S_ISREG (st
.st_mode
)) {
131 int cksumsize
= sizeof(buf
);
133 time_t mtime
= content_only
? 0 : st
.st_mtime
;
134 unsigned int hsh
= hash (st
.st_size
, mtime
);
137 fprintf(stderr
, " %s", name
);
138 fd
= open (name
, O_RDONLY
);
140 if (st
.st_size
< sizeof(buf
)) {
141 cksumsize
= st
.st_size
;
142 memset (((char *)buf
) + cksumsize
, 0, (sizeof(buf
) - cksumsize
) % sizeof(buf
[0]));
144 if (read (fd
, buf
, cksumsize
) != cksumsize
) {
147 fprintf(stderr
, "\r%*s\r", (int)strlen(name
)+2, "");
150 cksumsize
= (cksumsize
+ sizeof(buf
[0]) - 1) / sizeof(buf
[0]);
151 for (i
= 0, cksum
= 0; i
< cksumsize
; i
++) {
152 if (cksum
+ buf
[i
] < cksum
)
157 for (hp
= hps
[hsh
]; hp
; hp
= hp
->next
)
158 if (hp
->size
== st
.st_size
&& hp
->mtime
== mtime
)
161 hp
= malloc(sizeof(h
));
163 fprintf(stderr
, "\nOut of memory 1\n");
166 hp
->size
= st
.st_size
;
172 for (fp
= hp
->chain
; fp
; fp
= fp
->next
)
173 if (fp
->cksum
== cksum
)
175 for (fp2
= fp
; fp2
&& fp2
->cksum
== cksum
; fp2
= fp2
->next
)
176 if (fp2
->ino
== st
.st_ino
&& fp2
->dev
== st
.st_dev
) {
179 fprintf(stderr
, "\r%*s\r", (int)strlen(name
)+2, "");
182 if (fp
&& st
.st_size
> 0) {
183 p
= mmap (NULL
, st
.st_size
, PROT_READ
, MAP_SHARED
, fd
, 0);
185 if (p
== (void *)-1) {
187 fprintf(stderr
, "\nFailed to mmap %s\n", name
);
191 for (fp2
= fp
; fp2
&& fp2
->cksum
== cksum
; fp2
= fp2
->next
)
192 if (!lstat (fp2
->name
, &st2
) && S_ISREG (st2
.st_mode
) &&
193 !stcmp (&st
, &st2
, content_only
) &&
194 st2
.st_ino
!= st
.st_ino
&&
195 st2
.st_dev
== st
.st_dev
) {
196 int fd2
= open (fp2
->name
, O_RDONLY
);
197 if (fd2
< 0) continue;
198 if (fstat (fd2
, &st2
) || !S_ISREG (st2
.st_mode
) || st2
.st_size
== 0) {
203 q
= mmap (NULL
, st
.st_size
, PROT_READ
, MAP_SHARED
, fd2
, 0);
204 if (q
== (void *)-1) {
206 fprintf(stderr
, "\nFailed to mmap %s\n", fp2
->name
);
209 if (memcmp (p
, q
, st
.st_size
)) {
210 munmap (q
, st
.st_size
);
214 munmap (q
, st
.st_size
);
216 if (lstat (name
, &st3
)) {
217 fprintf(stderr
, "\nCould not stat %s again\n", name
);
218 munmap (p
, st
.st_size
);
222 st3
.st_atime
= st
.st_atime
;
223 if (stcmp (&st
, &st3
, 0)) {
224 fprintf(stderr
, "\nFile %s changed underneath us\n", name
);
225 munmap (p
, st
.st_size
);
232 strcpy (stpcpy (nambuf2
, n2
), ".$$$___cleanit___$$$");
233 if (rename (n2
, nambuf2
)) {
234 fprintf(stderr
, "\nFailed to rename %s to %s\n", n2
, nambuf2
);
238 fprintf(stderr
, "\nFailed to hardlink %s to %s\n", n1
, n2
);
239 if (rename (nambuf2
, n2
)) {
240 fprintf(stderr
, "\nBad bad - failed to rename back %s to %s\n", nambuf2
, n2
);
242 munmap (p
, st
.st_size
);
249 if (st3
.st_nlink
> 1) {
250 /* We actually did not save anything this time, since the link second argument
251 had some other links as well. */
253 fprintf(stderr
, "\r%*s\r%s %s to %s\n", (int)strlen(name
)+2, "", (no_link
? "Would link" : "Linked"), n1
, n2
);
255 nsaved
+=((st
.st_size
+4095)/4096)*4096;
257 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
);
259 munmap (p
, st
.st_size
);
264 munmap (p
, st
.st_size
);
265 fp2
= malloc(sizeof(f
) + 1 + strlen (name
));
267 fprintf(stderr
, "\nOut of memory 2\n");
271 fp2
->ino
= st
.st_ino
;
272 fp2
->dev
= st
.st_dev
;
274 strcpy(fp2
->name
, name
);
276 fp2
->next
= fp
->next
;
279 fp2
->next
= hp
->chain
;
283 fprintf(stderr
, "\r%*s\r", (int)strlen(name
)+2, "");
288 int main(int argc
, char **argv
)
296 while ((ch
= getopt (argc
, argv
, "cnvh")) != -1) {
314 for (i
= optind
; i
< argc
; i
++)
319 strcpy (nambuf1
, dp
->name
);
321 strcat (nambuf1
, "/");
322 p
= strchr (nambuf1
, 0);
323 dh
= opendir (nambuf1
);
327 while ((di
= readdir (dh
)) != NULL
) {
330 if (di
->d_name
[0] == '.') {
332 if (!di
->d_name
[1] || !strcmp (di
->d_name
, "..") || !strncmp (di
->d_name
, ".in.", 4))
334 q
= strrchr (di
->d_name
, '.');
335 if (q
&& strlen (q
) == 7 && q
!= di
->d_name
) {
338 fprintf(stderr
, "Skipping %s%s\n", nambuf1
, di
->d_name
);
342 strcpy (p
, di
->d_name
);