summaryrefslogtreecommitdiff
blob: cb0efd01b1ba67b29919c761b32959646dfb92eb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
From 6654d41a14da2fc521e889f01669f0dbb89aef15 Mon Sep 17 00:00:00 2001
From: Zac Medico <zmedico@gentoo.org>
Date: Tue, 5 Oct 2021 23:21:53 -0700
Subject: [PATCH] Symlink support

Bug: https://bugs.gentoo.org/815823
Signed-off-by: Zac Medico <zmedico@gentoo.org>
---
 main.c | 37 +++++++++++++++++++++++++++++++++++--
 tar.c  | 16 ++++++++++++++--
 tar.h  |  2 ++
 3 files changed, 51 insertions(+), 4 deletions(-)

diff --git a/main.c b/main.c
index 2c2da3e..448a9d0 100644
--- a/main.c
+++ b/main.c
@@ -257,7 +257,14 @@ main(int argc, char **argv)
 	// no need to seek.  cfile handles resetting streams as needed
 	
 	for(x=0; x < missing_count; x++) {
-		if(copy_whole_file(&tar_cfh, missing[x]) != 0) {
+		if (missing[x]->type == SYMTYPE) {
+			if(copy_symlink(&tar_cfh, missing[x]) != 0) {
+				v0printf("failed transfering symlink %s\n", missing[x]->fullname);
+				exit(9);
+			}
+			continue;
+		}
+		else if(copy_whole_file(&tar_cfh, missing[x]) != 0) {
 			v0printf("failed transfering file %s\n", missing[x]->fullname);
 			exit(9);
 		}
@@ -673,6 +680,8 @@ int
 check_existing_node(const struct dirent *de, const tar_entry *t, struct stat *st)
 {
 	int type;
+	unsigned char linkname[TAR_LINKNAME_LEN];
+	ssize_t linkname_len;
 	type = convert_lstat_type_tar_type(de->d_name, st);
 	if(type < 0)
 		return -1;
@@ -682,6 +691,15 @@ check_existing_node(const struct dirent *de, const tar_entry *t, struct stat *st
 		return 2;
 	if(REGTYPE == type && (st->st_size != t->size || (check_mtime && t->mtime != st->st_mtime)))
 		return 3;
+	if (SYMTYPE == type) {
+		if ((linkname_len = readlink(de->d_name, linkname, TAR_LINKNAME_LEN)) == -1) {
+			return -1;
+		}
+		if(strncmp((const char *)linkname, (const char *)t->linkname, linkname_len) != 0) {
+			remove_node(de->d_name, st);
+			return 3;
+		}
+	}
 	return 0;
 }
 
@@ -703,7 +721,22 @@ enforce_owner(const char *path, const tar_entry *t, struct stat *st)
 	}
 	return 0;
 }
-	
+
+int
+copy_symlink(cfile *tar_cfh, const tar_entry *ttent)
+{
+	v1printf("creating %s\n", ttent->fullname);
+
+	if (symlink(ttent->linkname, ttent->fullname) != 0) {
+		v0printf("failed creating symlink %s -> %s\n", ttent->fullname, ttent->linkname);
+		return -1;
+	}
+	if(lchown(ttent->fullname, ttent->uid, ttent->gid) != 0) {
+		v0printf("failed chown'ing %s\n", ttent->fullname);
+		return -1;
+	}
+	return 0;
+}
 
 int
 copy_whole_file(cfile *tar_cfh, const tar_entry *ttent) 
diff --git a/tar.c b/tar.c
index 42dc8e7..514e5fb 100644
--- a/tar.c
+++ b/tar.c
@@ -214,8 +214,7 @@ read_entry(cfile *src_cfh, off_u64 start, tar_entry *entry)
 		case AREGTYPE:
 			entry->type = REGTYPE;	break;
 		case SYMTYPE:
-			v0printf("symlinks not supported\n");
-			entry->type = TTAR_UNSUPPORTED_TYPE; break;
+			entry->type = SYMTYPE; break;
 		case LNKTYPE:
 			v0printf("hardlinks not supported!\n");
 			entry->type = TTAR_UNSUPPORTED_TYPE; break;
@@ -242,6 +241,17 @@ read_entry(cfile *src_cfh, off_u64 start, tar_entry *entry)
 	if(get_uid(block + TAR_UNAME_LOC, &entry->uid))
 		entry->uid = octal_str2long(block + TAR_UID_LOC, TAR_UID_LOC);
 
+	if (entry->type == SYMTYPE) {
+		name_len = strnlen((char *)block + TAR_LINKNAME_LOC, TAR_LINKNAME_LEN);
+		if((entry->linkname = (char *)malloc(name_len + 1)) == NULL){
+			v0printf("unable to allocate needed memory, bailing\n");
+			return MEM_ERROR;
+		}
+		memcpy(entry->linkname, block + TAR_LINKNAME_LOC, name_len);
+		entry->linkname[name_len] = '\0';
+		entry->linkname_len = name_len;
+	}
+
 //	if(entry->end % 512)
 //		entry->end += 512 - (entry->end % 512);
 	return 0;
@@ -256,6 +266,8 @@ convert_lstat_type_tar_type(const char *path, struct stat *st)
 	if(S_ISREG(st->st_mode)) {
 		if(st->st_nlink == 1)
 			return REGTYPE;
+	} else if(S_ISLNK(st->st_mode)) {
+			return SYMTYPE;
 	} else if(S_ISDIR(st->st_mode))
 			return DIRTYPE;
 
diff --git a/tar.h b/tar.h
index e9d9ee9..95f957c 100644
--- a/tar.h
+++ b/tar.h
@@ -78,6 +78,8 @@ typedef struct {
 	off_u64			size;
 	unsigned int	fullname_len;
 	char			*fullname;
+	unsigned int	linkname_len;
+	char			*linkname;
 	time_t			mtime;
 	uid_t			uid;
 	gid_t			gid;
-- 
2.32.0