1This attempts to accomplish some per-file memory-savings by moving the
2uid+gid items out of the file-list (since their values are common to
3multiple file-list entries) and replacing them with an index to an
4array of structures.
5
6This only saves 4 bytes per file (not counting the overhead of the array).
7
8This probably needs a hashing algorithm to be added if the uid+gid list
9gets to be really large.
10
11To use this patch, run these commands for a successful build:
12
13    patch -p1 <patches/id-pair.diff
14    ./configure                         (optional if already run)
15    make
16
17--- old/flist.c
18+++ new/flist.c
19@@ -54,6 +54,7 @@ extern int copy_unsafe_links;
20 extern int protocol_version;
21 extern int sanitize_paths;
22 extern struct stats stats;
23+extern struct id_pair *id_pairs;
24 extern struct file_list *the_file_list;
25 
26 extern char curr_dir[MAXPATHLEN];
27@@ -351,14 +352,14 @@ static void send_file_entry(struct file_
28 		}
29 	} else if (protocol_version < 28)
30 		rdev = MAKEDEV(0, 0);
31-	if (file->uid == uid)
32+	if (id_pairs[file->id_ndx].uid == uid)
33 		flags |= XMIT_SAME_UID;
34 	else
35-		uid = file->uid;
36-	if (file->gid == gid)
37+		uid = id_pairs[file->id_ndx].uid;
38+	if (id_pairs[file->id_ndx].gid == gid)
39 		flags |= XMIT_SAME_GID;
40 	else
41-		gid = file->gid;
42+		gid = id_pairs[file->id_ndx].gid;
43 	if (file->modtime == modtime)
44 		flags |= XMIT_SAME_TIME;
45 	else
46@@ -609,8 +610,7 @@ static struct file_struct *receive_file_
47 	file->modtime = modtime;
48 	file->length = file_length;
49 	file->mode = mode;
50-	file->uid = uid;
51-	file->gid = gid;
52+	file->id_ndx = id_pair(uid, gid);
53 
54 	if (dirname_len) {
55 		file->dirname = lastdir = bp;
56@@ -862,8 +862,7 @@ struct file_struct *make_file(char *fnam
57 	file->modtime = st.st_mtime;
58 	file->length = st.st_size;
59 	file->mode = st.st_mode;
60-	file->uid = st.st_uid;
61-	file->gid = st.st_gid;
62+	file->id_ndx = id_pair(st.st_uid, st.st_gid);
63 
64 #ifdef SUPPORT_HARD_LINKS
65 	if (flist && flist->hlink_pool) {
66@@ -931,8 +930,7 @@ struct file_struct *make_file(char *fnam
67 			file->modtime = st2.st_mtime;
68 			file->length = st2.st_size;
69 			file->mode = st2.st_mode;
70-			file->uid = st2.st_uid;
71-			file->gid = st2.st_gid;
72+			file->id_ndx = id_pair(st2.st_uid, st2.st_gid);
73 			file->u.link = NULL;
74 		} else
75 			file->mode = save_mode;
76@@ -1380,7 +1378,7 @@ struct file_list *recv_file_list(int f)
77 	clean_flist(flist, relative_paths, 1);
78 
79 	if (f >= 0) {
80-		recv_uid_list(f, flist);
81+		recv_uid_list(f);
82 
83 		/* Recv the io_error flag */
84 		if (lp_ignore_errors(module_id) || ignore_errors)
85@@ -1696,13 +1694,15 @@ static void output_flist(struct file_lis
86 
87 	for (i = 0; i < flist->count; i++) {
88 		file = flist->files[i];
89-		if ((am_root || am_sender) && preserve_uid)
90-			snprintf(uidbuf, sizeof uidbuf, " uid=%ld", (long)file->uid);
91-		else
92+		if ((am_root || am_sender) && preserve_uid) {
93+			snprintf(uidbuf, sizeof uidbuf, " uid=%ld",
94+				(long)id_pairs[file->id_ndx].uid);
95+		} else
96 			*uidbuf = '\0';
97-		if (preserve_gid && file->gid != GID_NONE)
98-			snprintf(gidbuf, sizeof gidbuf, " gid=%ld", (long)file->gid);
99-		else
100+		if (preserve_gid && id_pairs[file->id_ndx].gid != GID_NONE) {
101+			snprintf(gidbuf, sizeof gidbuf, " gid=%ld",
102+				(long)id_pairs[file->id_ndx].gid);
103+		} else
104 			*gidbuf = '\0';
105 		if (!am_sender)
106 			snprintf(depthbuf, sizeof depthbuf, "%d", file->dir.depth);
107--- old/generator.c
108+++ new/generator.c
109@@ -90,6 +90,7 @@ extern dev_t filesystem_dev;
110 extern char *backup_dir;
111 extern char *backup_suffix;
112 extern int backup_suffix_len;
113+extern struct id_pair *id_pairs;
114 extern struct file_list *the_file_list;
115 extern struct filter_list_struct server_filter_list;
116 
117@@ -323,10 +324,12 @@ int unchanged_attrs(struct file_struct *
118 	 && (st->st_mode & CHMOD_BITS) != (file->mode & CHMOD_BITS))
119 		return 0;
120 
121-	if (am_root && preserve_uid && st->st_uid != file->uid)
122+	if (am_root && preserve_uid
123+	 && st->st_uid != id_pairs[file->id_ndx].uid)
124 		return 0;
125 
126-	if (preserve_gid && file->gid != GID_NONE && st->st_gid != file->gid)
127+	if (preserve_gid && id_pairs[file->id_ndx].gid != GID_NONE
128+	 && st->st_gid != id_pairs[file->id_ndx].gid)
129 		return 0;
130 
131 	return 1;
132@@ -339,6 +342,8 @@ void itemize(struct file_struct *file, i
133 		int keep_time = !preserve_times ? 0
134 		    : S_ISDIR(file->mode) ? !omit_dir_times
135 		    : !S_ISLNK(file->mode);
136+		uid_t uid = id_pairs[file->id_ndx].uid;
137+		gid_t gid = id_pairs[file->id_ndx].gid;
138 
139 		if (S_ISREG(file->mode) && file->length != st->st_size)
140 			iflags |= ITEM_REPORT_SIZE;
141@@ -348,10 +353,10 @@ void itemize(struct file_struct *file, i
142 			iflags |= ITEM_REPORT_TIME;
143 		if ((file->mode & CHMOD_BITS) != (st->st_mode & CHMOD_BITS))
144 			iflags |= ITEM_REPORT_PERMS;
145-		if (preserve_uid && am_root && file->uid != st->st_uid)
146+		if (preserve_uid && am_root && uid != st->st_uid)
147 			iflags |= ITEM_REPORT_OWNER;
148-		if (preserve_gid && file->gid != GID_NONE
149-		    && st->st_gid != file->gid)
150+		if (preserve_gid && gid != GID_NONE
151+		    && st->st_gid != gid)
152 			iflags |= ITEM_REPORT_GROUP;
153 	} else
154 		iflags |= ITEM_IS_NEW;
155--- old/log.c
156+++ new/log.c
157@@ -46,6 +46,7 @@ extern char *auth_user;
158 extern char *stdout_format;
159 extern char *logfile_format;
160 extern char *logfile_name;
161+extern struct id_pair *id_pairs;
162 #if defined HAVE_ICONV_OPEN && defined HAVE_ICONV_H
163 extern iconv_t ic_chck;
164 #endif
165@@ -470,16 +471,16 @@ static void log_formatted(enum logcode c
166 		case 'U':
167 			strlcat(fmt, "ld", sizeof fmt);
168 			snprintf(buf2, sizeof buf2, fmt,
169-				 (long)file->uid);
170+				 (long)id_pairs[file->id_ndx].uid);
171 			n = buf2;
172 			break;
173 		case 'G':
174-			if (file->gid == GID_NONE)
175+			if (id_pairs[file->id_ndx].gid == GID_NONE)
176 				n = "DEFAULT";
177 			else {
178 				strlcat(fmt, "ld", sizeof fmt);
179 				snprintf(buf2, sizeof buf2, fmt,
180-					 (long)file->gid);
181+					 (long)id_pairs[file->id_ndx].gid);
182 				n = buf2;
183 			}
184 			break;
185--- old/rsync.c
186+++ new/rsync.c
187@@ -49,6 +49,7 @@ extern int keep_dirlinks;
188 extern int make_backups;
189 extern mode_t orig_umask;
190 extern struct stats stats;
191+extern struct id_pair *id_pairs;
192 extern struct chmod_mode_struct *daemon_chmod_modes;
193 
194 #if defined HAVE_ICONV_OPEN && defined HAVE_ICONV_H
195@@ -130,6 +131,8 @@ int set_file_attrs(char *fname, struct f
196 	STRUCT_STAT st2;
197 	int change_uid, change_gid;
198 	mode_t new_mode = file->mode;
199+	uid_t uid;
200+	gid_t gid;
201 
202 	if (!st) {
203 		if (dry_run)
204@@ -162,9 +165,11 @@ int set_file_attrs(char *fname, struct f
205 			updated = 1;
206 	}
207 
208-	change_uid = am_root && preserve_uid && st->st_uid != file->uid;
209-	change_gid = preserve_gid && file->gid != GID_NONE
210-		&& st->st_gid != file->gid;
211+	uid = id_pairs[file->id_ndx].uid;
212+	gid = id_pairs[file->id_ndx].gid;
213+	change_uid = am_root && preserve_uid && st->st_uid != uid;
214+	change_gid = preserve_gid && gid != GID_NONE
215+		&& st->st_gid != gid;
216 #if !defined HAVE_LCHOWN && !defined CHOWN_MODIFIES_SYMLINK
217 	if (S_ISLNK(st->st_mode))
218 		;
219@@ -176,18 +181,18 @@ int set_file_attrs(char *fname, struct f
220 				rprintf(FINFO,
221 					"set uid of %s from %ld to %ld\n",
222 					fname,
223-					(long)st->st_uid, (long)file->uid);
224+					(long)st->st_uid, (long)uid);
225 			}
226 			if (change_gid) {
227 				rprintf(FINFO,
228 					"set gid of %s from %ld to %ld\n",
229 					fname,
230-					(long)st->st_gid, (long)file->gid);
231+					(long)st->st_gid, (long)gid);
232 			}
233 		}
234 		if (do_lchown(fname,
235-		    change_uid ? file->uid : st->st_uid,
236-		    change_gid ? file->gid : st->st_gid) != 0) {
237+		    change_uid ? uid : st->st_uid,
238+		    change_gid ? gid : st->st_gid) != 0) {
239 			/* shouldn't have attempted to change uid or gid
240 			 * unless have the privilege */
241 			rsyserr(FERROR, errno, "%s %s failed",
242--- old/rsync.h
243+++ new/rsync.h
244@@ -503,6 +503,11 @@ struct hlink {
245 	unsigned short link_dest_used;
246 };
247 
248+struct id_pair {
249+	uid_t uid;
250+	gid_t gid;
251+};
252+
253 #define F_DEV	link_u.idev->dev
254 #define F_INODE	link_u.idev->inode
255 
256@@ -527,8 +532,7 @@ struct file_struct {
257 		struct hlink *links;
258 	} link_u;
259 	time_t modtime;
260-	uid_t uid;
261-	gid_t gid;
262+	int id_ndx;
263 	mode_t mode;
264 	uchar flags;	/* this item MUST remain last */
265 };
266--- old/uidlist.c
267+++ new/uidlist.c
268@@ -38,6 +38,8 @@ extern int preserve_gid;
269 extern int numeric_ids;
270 extern int am_root;
271 
272+struct id_pair *id_pairs;
273+
274 struct idlist {
275 	struct idlist *next;
276 	int id, id2;
277@@ -47,6 +49,8 @@ struct idlist {
278 static struct idlist *uidlist;
279 static struct idlist *gidlist;
280 
281+static int pair_cnt = 0, pair_alloc = 0;
282+
283 static struct idlist *add_to_list(struct idlist **root, int id, char *name,
284 				  int id2)
285 {
286@@ -306,7 +310,7 @@ void send_uid_list(int f)
287 
288 /* recv a complete uid/gid mapping from the peer and map the uid/gid
289  * in the file list to local names */
290-void recv_uid_list(int f, struct file_list *flist)
291+void recv_uid_list(int f)
292 {
293 	int id, i;
294 	char *name;
295@@ -337,11 +341,40 @@ void recv_uid_list(int f, struct file_li
296 
297 	/* Now convert all the uids/gids from sender values to our values. */
298 	if (am_root && preserve_uid && !numeric_ids) {
299-		for (i = 0; i < flist->count; i++)
300-			flist->files[i]->uid = match_uid(flist->files[i]->uid);
301+		for (i = 0; i < pair_cnt; i++)
302+			id_pairs[i].uid = match_uid(id_pairs[i].uid);
303 	}
304 	if (preserve_gid && (!am_root || !numeric_ids)) {
305-		for (i = 0; i < flist->count; i++)
306-			flist->files[i]->gid = match_gid(flist->files[i]->gid);
307+		for (i = 0; i < pair_cnt; i++)
308+			id_pairs[i].gid = match_gid(id_pairs[i].gid);
309 	}
310 }
311+
312+int id_pair(uid_t uid, gid_t gid)
313+{
314+	static int j = 0;
315+
316+	if (pair_cnt) {
317+		int start = j;
318+		/* We start our search where we left off because
319+		 * the IDs usually come in clumps. */
320+		do {
321+			if (uid == id_pairs[j].uid && gid == id_pairs[j].gid)
322+				return j;
323+			if (++j == pair_cnt)
324+				j = 0;
325+		} while (j != start);
326+	}
327+
328+	if (pair_cnt == pair_alloc) {
329+		pair_alloc += 128;
330+		id_pairs = realloc_array(id_pairs, struct id_pair,
331+					  pair_alloc);
332+	}
333+
334+	j = pair_cnt++;
335+	id_pairs[j].uid = uid;
336+	id_pairs[j].gid = gid;
337+
338+	return j;
339+}
340--- old/proto.h
341+++ new/proto.h
342@@ -271,7 +271,8 @@ void see_token(char *data, int32 toklen)
343 void add_uid(uid_t uid);
344 void add_gid(gid_t gid);
345 void send_uid_list(int f);
346-void recv_uid_list(int f, struct file_list *flist);
347+void recv_uid_list(int f);
348+int id_pair(uid_t uid, gid_t gid);
349 void set_nonblocking(int fd);
350 void set_blocking(int fd);
351 int fd_pair(int fd[2]);
352