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 */
|
||||
static long du(char *filename)
|
||||
{
|
||||
@ -246,7 +174,7 @@ int du_main(int argc, char **argv)
|
||||
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:
|
||||
c-file-style: "linux"
|
||||
|
@ -286,4 +286,8 @@ extern const char * const can_not_create_raw_socket;
|
||||
#define CURRENT_TTY "/dev/tty"
|
||||
#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__ */
|
||||
|
@ -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 \
|
||||
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 \
|
||||
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
|
||||
|
@ -30,7 +30,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "libbb.h"
|
||||
#include "busybox.h"
|
||||
|
||||
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)) {
|
||||
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) {
|
||||
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)
|
||||
perror_msg("unable to preserve ownership of `%s'", dest);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_FEATURE_PRESERVE_HARDLINKS
|
||||
add_to_ino_dev_hashtable(&source_stat, dest);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
} else {
|
||||
error_msg("internal error: unrecognized file type");
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_FEATURE_PRESERVE_HARDLINKS
|
||||
add_to_ino_dev_hashtable(&source_stat, dest);
|
||||
#endif
|
||||
|
||||
end:
|
||||
|
||||
if (flags & FILEUTILS_PRESERVE_STATUS) {
|
||||
|
@ -29,6 +29,13 @@
|
||||
#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];
|
||||
|
||||
/*
|
||||
|
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