Make cp and mv optionally preserve hard links.
This commit is contained in:
parent
46ea0e4696
commit
ace02dc9cd
@ -59,78 +59,6 @@ static void print_summary(long size, char *filename)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#define HASH_SIZE 311 /* Should be prime */
|
|
||||||
#define hash_inode(i) ((i) % HASH_SIZE)
|
|
||||||
|
|
||||||
typedef struct ino_dev_hash_bucket_struct {
|
|
||||||
struct ino_dev_hash_bucket_struct *next;
|
|
||||||
ino_t ino;
|
|
||||||
dev_t dev;
|
|
||||||
char name[1];
|
|
||||||
} ino_dev_hashtable_bucket_t;
|
|
||||||
|
|
||||||
static ino_dev_hashtable_bucket_t *ino_dev_hashtable[HASH_SIZE];
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Return 1 if statbuf->st_ino && statbuf->st_dev are recorded in
|
|
||||||
* `ino_dev_hashtable', else return 0
|
|
||||||
*
|
|
||||||
* If NAME is a non-NULL pointer to a character pointer, and there is
|
|
||||||
* a match, then set *NAME to the value of the name slot in that
|
|
||||||
* bucket.
|
|
||||||
*/
|
|
||||||
static int is_in_ino_dev_hashtable(const struct stat *statbuf, char **name)
|
|
||||||
{
|
|
||||||
ino_dev_hashtable_bucket_t *bucket;
|
|
||||||
|
|
||||||
bucket = ino_dev_hashtable[hash_inode(statbuf->st_ino)];
|
|
||||||
while (bucket != NULL) {
|
|
||||||
if ((bucket->ino == statbuf->st_ino) &&
|
|
||||||
(bucket->dev == statbuf->st_dev))
|
|
||||||
{
|
|
||||||
if (name) *name = bucket->name;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
bucket = bucket->next;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Add statbuf to statbuf hash table */
|
|
||||||
static void add_to_ino_dev_hashtable(const struct stat *statbuf, const char *name)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
size_t s;
|
|
||||||
ino_dev_hashtable_bucket_t *bucket;
|
|
||||||
|
|
||||||
i = hash_inode(statbuf->st_ino);
|
|
||||||
s = name ? strlen(name) : 0;
|
|
||||||
bucket = xmalloc(sizeof(ino_dev_hashtable_bucket_t) + s);
|
|
||||||
bucket->ino = statbuf->st_ino;
|
|
||||||
bucket->dev = statbuf->st_dev;
|
|
||||||
if (name)
|
|
||||||
strcpy(bucket->name, name);
|
|
||||||
else
|
|
||||||
bucket->name[0] = '\0';
|
|
||||||
bucket->next = ino_dev_hashtable[i];
|
|
||||||
ino_dev_hashtable[i] = bucket;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Clear statbuf hash table */
|
|
||||||
static void reset_ino_dev_hashtable(void)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
ino_dev_hashtable_bucket_t *bucket;
|
|
||||||
|
|
||||||
for (i = 0; i < HASH_SIZE; i++) {
|
|
||||||
while (ino_dev_hashtable[i] != NULL) {
|
|
||||||
bucket = ino_dev_hashtable[i]->next;
|
|
||||||
free(ino_dev_hashtable[i]);
|
|
||||||
ino_dev_hashtable[i] = bucket;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* tiny recursive du */
|
/* tiny recursive du */
|
||||||
static long du(char *filename)
|
static long du(char *filename)
|
||||||
{
|
{
|
||||||
@ -246,7 +174,7 @@ int du_main(int argc, char **argv)
|
|||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* $Id: du.c,v 1.51 2001/10/24 04:59:27 andersen Exp $ */
|
/* $Id: du.c,v 1.52 2001/12/17 15:26:25 kraai Exp $ */
|
||||||
/*
|
/*
|
||||||
Local Variables:
|
Local Variables:
|
||||||
c-file-style: "linux"
|
c-file-style: "linux"
|
||||||
|
@ -286,4 +286,8 @@ extern const char * const can_not_create_raw_socket;
|
|||||||
#define CURRENT_TTY "/dev/tty"
|
#define CURRENT_TTY "/dev/tty"
|
||||||
#define CONSOLE_DEV "/dev/console"
|
#define CONSOLE_DEV "/dev/console"
|
||||||
|
|
||||||
|
int is_in_ino_dev_hashtable(const struct stat *statbuf, char **name);
|
||||||
|
void add_to_ino_dev_hashtable(const struct stat *statbuf, const char *name);
|
||||||
|
void reset_ino_dev_hashtable(void);
|
||||||
|
|
||||||
#endif /* __LIBCONFIG_H__ */
|
#endif /* __LIBCONFIG_H__ */
|
||||||
|
@ -45,7 +45,7 @@ obj-y += ask_confirmation.o chomp.o concat_path_file.o copy_file.o \
|
|||||||
xgetcwd.o xreadlink.o xregcomp.o interface.o remove_file.o last_char_is.o \
|
xgetcwd.o xreadlink.o xregcomp.o interface.o remove_file.o last_char_is.o \
|
||||||
copyfd.o vherror_msg.o herror_msg.o herror_msg_and_die.o xgethostbyname.o \
|
copyfd.o vherror_msg.o herror_msg.o herror_msg_and_die.o xgethostbyname.o \
|
||||||
dirname.o make_directory.o create_icmp_socket.o u_signal_names.o arith.o \
|
dirname.o make_directory.o create_icmp_socket.o u_signal_names.o arith.o \
|
||||||
simplify_path.o inet_common.o $(LIBBB_MOBJS) $(LIBBB_AROBJS)
|
simplify_path.o inet_common.o inode_hash.o $(LIBBB_MOBJS) $(LIBBB_AROBJS)
|
||||||
|
|
||||||
|
|
||||||
# Hand off to toplevel Rules.mak
|
# Hand off to toplevel Rules.mak
|
||||||
|
@ -30,7 +30,7 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "libbb.h"
|
#include "busybox.h"
|
||||||
|
|
||||||
int copy_file(const char *source, const char *dest, int flags)
|
int copy_file(const char *source, const char *dest, int flags)
|
||||||
{
|
{
|
||||||
@ -131,6 +131,19 @@ int copy_file(const char *source, const char *dest, int flags)
|
|||||||
}
|
}
|
||||||
} else if (S_ISREG(source_stat.st_mode)) {
|
} else if (S_ISREG(source_stat.st_mode)) {
|
||||||
FILE *sfp, *dfp=NULL;
|
FILE *sfp, *dfp=NULL;
|
||||||
|
#ifdef CONFIG_FEATURE_PRESERVE_HARDLINKS
|
||||||
|
char *link_name;
|
||||||
|
|
||||||
|
if (!(flags & FILEUTILS_DEREFERENCE) &&
|
||||||
|
is_in_ino_dev_hashtable(&source_stat, &link_name)) {
|
||||||
|
if (link(link_name, dest) < 0) {
|
||||||
|
perror_msg("unable to link `%s'", dest);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if ((sfp = fopen(source, "r")) == NULL) {
|
if ((sfp = fopen(source, "r")) == NULL) {
|
||||||
perror_msg("unable to open `%s'", source);
|
perror_msg("unable to open `%s'", source);
|
||||||
@ -212,12 +225,21 @@ int copy_file(const char *source, const char *dest, int flags)
|
|||||||
if (lchown(dest, source_stat.st_uid, source_stat.st_gid) < 0)
|
if (lchown(dest, source_stat.st_uid, source_stat.st_gid) < 0)
|
||||||
perror_msg("unable to preserve ownership of `%s'", dest);
|
perror_msg("unable to preserve ownership of `%s'", dest);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_FEATURE_PRESERVE_HARDLINKS
|
||||||
|
add_to_ino_dev_hashtable(&source_stat, dest);
|
||||||
|
#endif
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
error_msg("internal error: unrecognized file type");
|
error_msg("internal error: unrecognized file type");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_FEATURE_PRESERVE_HARDLINKS
|
||||||
|
add_to_ino_dev_hashtable(&source_stat, dest);
|
||||||
|
#endif
|
||||||
|
|
||||||
end:
|
end:
|
||||||
|
|
||||||
if (flags & FILEUTILS_PRESERVE_STATUS) {
|
if (flags & FILEUTILS_PRESERVE_STATUS) {
|
||||||
|
@ -29,6 +29,13 @@
|
|||||||
#define HASH_SIZE 311 /* Should be prime */
|
#define HASH_SIZE 311 /* Should be prime */
|
||||||
#define hash_inode(i) ((i) % HASH_SIZE)
|
#define hash_inode(i) ((i) % HASH_SIZE)
|
||||||
|
|
||||||
|
typedef struct ino_dev_hash_bucket_struct {
|
||||||
|
struct ino_dev_hash_bucket_struct *next;
|
||||||
|
ino_t ino;
|
||||||
|
dev_t dev;
|
||||||
|
char name[1];
|
||||||
|
} ino_dev_hashtable_bucket_t;
|
||||||
|
|
||||||
static ino_dev_hashtable_bucket_t *ino_dev_hashtable[HASH_SIZE];
|
static ino_dev_hashtable_bucket_t *ino_dev_hashtable[HASH_SIZE];
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
6
testsuite/cp/cp-preserves-hard-links
Normal file
6
testsuite/cp/cp-preserves-hard-links
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
# UNSUPPORTED: CONFIG_FEATURE_PRESERVE_HARDLINKS
|
||||||
|
touch foo
|
||||||
|
ln foo bar
|
||||||
|
mkdir baz
|
||||||
|
busybox cp -d foo bar baz
|
||||||
|
test baz/foo -ef baz/bar
|
Loading…
x
Reference in New Issue
Block a user