Commit | Line | Data |
---|---|---|
c44efccb WD |
1 | Depends-On-Patch: acls.diff |
2 | Depends-On-Patch: xattrs.diff | |
3 | ||
4 | This patch adds a new option: --fake-super, which tells rsync to copy in a | |
5 | fake super-user mode that stores various file attributes in an extended- | |
6 | attribute value instead of as real file-system attributes. The items | |
7 | affected are: | |
8 | ||
9 | mode the real mode of a file is always (666 & umask) while | |
10 | the real mode of a directory is always (777 & umask). | |
11 | ||
12 | rdev devices and special files are created as zero-length | |
13 | normal files. | |
14 | ||
15 | uid the real owner is always left unchanged. | |
16 | ||
17 | gid the real group is always left unchanged. | |
18 | ||
19 | A daemon can set "fake super = yes" in the rsync.conf file for any module | |
20 | that you'd like to run without root perms while pretending it has them (the | |
21 | client cannot affect this). | |
22 | ||
23 | The --fake-super option only affects the side where the option is used. To | |
24 | affect the remote side of a remote-shell connection, specify an rsync path: | |
25 | ||
26 | rsync -av --rsync-path='rsync --fake-super' /src/ host:/dest/ | |
27 | ||
28 | For a local copy where you want to affect only one side or the other, | |
29 | you'll need to turn the copy into a remote copy to localhost. | |
30 | ||
31 | After applying this patch, run these commands for a successful build: | |
32 | ||
33 | ./prepare-source | |
1e883fdf WD |
34 | ./configure --enable-xattr-support |
35 | make | |
36 | ||
37 | or, if you want ACL support too: | |
38 | ||
39 | ./prepare-source | |
40 | ./configure --enable-acl-support --enable-xattr-support | |
c44efccb WD |
41 | make |
42 | ||
e66b9b1f WD |
43 | --- old/backup.c |
44 | +++ new/backup.c | |
45 | @@ -129,7 +129,7 @@ static int make_bak_dir(char *fullpath) | |
46 | if (p >= rel) { | |
47 | /* Try to transfer the directory settings of the | |
48 | * actual dir that the files are coming from. */ | |
49 | - if (do_stat(rel, &sx.st) < 0) { | |
50 | + if (x_stat(rel, &sx.st, NULL) < 0) { | |
51 | rsyserr(FERROR, errno, | |
52 | "make_bak_dir stat %s failed", | |
53 | full_fname(rel)); | |
54 | @@ -200,7 +200,7 @@ static int keep_backup(char *fname) | |
55 | int ret_code; | |
c44efccb | 56 | |
e66b9b1f WD |
57 | /* return if no file to keep */ |
58 | - if (do_lstat(fname, &sx.st) < 0) | |
59 | + if (x_lstat(fname, &sx.st, NULL) < 0) | |
60 | return 1; | |
61 | #ifdef SUPPORT_ACLS | |
62 | sx.acc_acl = sx.def_acl = NULL; | |
c44efccb WD |
63 | --- old/clientserver.c |
64 | +++ new/clientserver.c | |
611f9f3c WD |
65 | @@ -625,6 +625,11 @@ static int rsync_module(int f_in, int f_ |
66 | ret = parse_arguments(&argc, (const char ***) &argv, 0); | |
67 | quiet = 0; /* Don't let someone try to be tricky. */ | |
c44efccb | 68 | |
611f9f3c | 69 | + if (lp_fake_super(i)) |
c44efccb | 70 | + am_root = -1; |
611f9f3c WD |
71 | + else if (am_root < 0) /* Treat --fake-super from client as --super. */ |
72 | + am_root = 2; | |
c44efccb | 73 | + |
611f9f3c WD |
74 | if (filesfrom_fd == 0) |
75 | filesfrom_fd = f_in; | |
76 | ||
e66b9b1f WD |
77 | --- old/flist.c |
78 | +++ new/flist.c | |
79 | @@ -181,7 +181,7 @@ static int readlink_stat(const char *pat | |
80 | } | |
81 | return 0; | |
82 | #else | |
83 | - return do_stat(path, stp); | |
84 | + return x_stat(path, stp, NULL); | |
85 | #endif | |
86 | } | |
87 | ||
88 | @@ -189,17 +189,17 @@ int link_stat(const char *path, STRUCT_S | |
89 | { | |
90 | #ifdef SUPPORT_LINKS | |
91 | if (copy_links) | |
92 | - return do_stat(path, stp); | |
93 | - if (do_lstat(path, stp) < 0) | |
94 | + return x_stat(path, stp, NULL); | |
95 | + if (x_lstat(path, stp, NULL) < 0) | |
96 | return -1; | |
97 | if (follow_dirlinks && S_ISLNK(stp->st_mode)) { | |
98 | STRUCT_STAT st; | |
99 | - if (do_stat(path, &st) == 0 && S_ISDIR(st.st_mode)) | |
100 | + if (x_stat(path, &st, NULL) == 0 && S_ISDIR(st.st_mode)) | |
101 | *stp = st; | |
102 | } | |
103 | return 0; | |
104 | #else | |
105 | - return do_stat(path, stp); | |
106 | + return x_stat(path, stp, NULL); | |
107 | #endif | |
108 | } | |
109 | ||
110 | @@ -793,7 +793,7 @@ struct file_struct *make_file(char *fnam | |
111 | if (save_errno == ENOENT) { | |
112 | #ifdef SUPPORT_LINKS | |
113 | /* Avoid "vanished" error if symlink points nowhere. */ | |
114 | - if (copy_links && do_lstat(thisname, &st) == 0 | |
115 | + if (copy_links && x_lstat(thisname, &st, NULL) == 0 | |
116 | && S_ISLNK(st.st_mode)) { | |
117 | io_error |= IOERR_GENERAL; | |
118 | rprintf(FERROR, "symlink has no referent: %s\n", | |
119 | @@ -963,7 +963,7 @@ struct file_struct *make_file(char *fnam | |
120 | int save_mode = file->mode; | |
121 | file->mode = S_IFDIR; /* Find a directory with our name. */ | |
122 | if (flist_find(the_file_list, file) >= 0 | |
123 | - && do_stat(thisname, &st2) == 0 && S_ISDIR(st2.st_mode)) { | |
124 | + && x_stat(thisname, &st2, NULL) == 0 && S_ISDIR(st2.st_mode)) { | |
125 | file->modtime = st2.st_mtime; | |
126 | file->length = st2.st_size; | |
127 | file->mode = st2.st_mode; | |
c44efccb WD |
128 | --- old/generator.c |
129 | +++ new/generator.c | |
130 | @@ -1510,13 +1510,14 @@ void generate_files(int f_out, struct fi | |
131 | recv_generator(fbuf, file, i, itemizing, maybe_ATTRS_REPORT, | |
132 | code, f_out); | |
133 | ||
134 | - /* We need to ensure that any dirs we create have writeable | |
135 | + /* We need to ensure that any dirs we create have rwx | |
136 | * permissions during the time we are putting files within | |
137 | * them. This is then fixed after the transfer is done. */ | |
138 | #ifdef HAVE_CHMOD | |
139 | - if (!am_root && S_ISDIR(file->mode) && !(file->mode & S_IWUSR) | |
140 | + if (am_root <= 0 && S_ISDIR(file->mode) | |
141 | + && (file->mode & S_IRWXU) != S_IRWXU | |
142 | && dir_tweaking) { | |
143 | - mode_t mode = file->mode | S_IWUSR; /* user write */ | |
144 | + mode_t mode = file->mode | S_IRWXU; /* user rwx */ | |
145 | char *fname = local_name ? local_name : fbuf; | |
146 | if (do_chmod(fname, mode) < 0) { | |
147 | rsyserr(FERROR, errno, | |
148 | --- old/loadparm.c | |
149 | +++ new/loadparm.c | |
150 | @@ -150,6 +150,7 @@ typedef struct | |
151 | int syslog_facility; | |
152 | int timeout; | |
153 | ||
154 | + BOOL fake_super; | |
155 | BOOL ignore_errors; | |
156 | BOOL ignore_nonreadable; | |
157 | BOOL list; | |
158 | @@ -197,6 +198,7 @@ static service sDefault = | |
159 | /* syslog_facility; */ LOG_DAEMON, | |
160 | /* timeout; */ 0, | |
161 | ||
162 | + /* fake_super; */ False, | |
163 | /* ignore_errors; */ False, | |
164 | /* ignore_nonreadable; */ False, | |
165 | /* list; */ True, | |
166 | @@ -298,6 +300,7 @@ static struct parm_struct parm_table[] = | |
167 | {"dont compress", P_STRING, P_LOCAL, &sDefault.dont_compress, NULL,0}, | |
168 | {"exclude from", P_STRING, P_LOCAL, &sDefault.exclude_from, NULL,0}, | |
169 | {"exclude", P_STRING, P_LOCAL, &sDefault.exclude, NULL,0}, | |
170 | + {"fake super", P_BOOL, P_LOCAL, &sDefault.fake_super, NULL,0}, | |
171 | {"filter", P_STRING, P_LOCAL, &sDefault.filter, NULL,0}, | |
172 | {"gid", P_STRING, P_LOCAL, &sDefault.gid, NULL,0}, | |
173 | {"hosts allow", P_STRING, P_LOCAL, &sDefault.hosts_allow, NULL,0}, | |
174 | @@ -412,6 +415,7 @@ FN_LOCAL_INTEGER(lp_max_connections, max | |
175 | FN_LOCAL_INTEGER(lp_max_verbosity, max_verbosity) | |
176 | FN_LOCAL_INTEGER(lp_timeout, timeout) | |
177 | ||
178 | +FN_LOCAL_BOOL(lp_fake_super, fake_super) | |
179 | FN_LOCAL_BOOL(lp_ignore_errors, ignore_errors) | |
180 | FN_LOCAL_BOOL(lp_ignore_nonreadable, ignore_nonreadable) | |
181 | FN_LOCAL_BOOL(lp_list, list) | |
182 | @@ -816,7 +820,7 @@ BOOL lp_load(char *pszFname, int globals | |
183 | ||
184 | if (pszFname) | |
185 | pstrcpy(n2,pszFname); | |
186 | - else if (am_server && !am_root) | |
187 | + else if (am_server && am_root <= 0) | |
188 | pstrcpy(n2,RSYNCD_USERCONF); | |
189 | else | |
190 | pstrcpy(n2,RSYNCD_SYSCONF); | |
191 | --- old/options.c | |
192 | +++ new/options.c | |
193 | @@ -73,7 +73,7 @@ int protocol_version = PROTOCOL_VERSION; | |
194 | int sparse_files = 0; | |
195 | int do_compression = 0; | |
196 | int def_compress_level = Z_DEFAULT_COMPRESSION; | |
197 | -int am_root = 0; | |
198 | +int am_root = 0; /* 0 = normal, 1 = super, 2 = --super, -1 = --fake-super */ | |
199 | int am_server = 0; | |
200 | int am_sender = 0; | |
201 | int am_generator = 0; | |
202 | @@ -330,6 +330,7 @@ void usage(enum logcode F) | |
203 | rprintf(F," -t, --times preserve times\n"); | |
204 | rprintf(F," -O, --omit-dir-times omit directories when preserving times\n"); | |
205 | rprintf(F," --super receiver attempts super-user activities\n"); | |
206 | + rprintf(F," --fake-super fake root by storing/reading ownership/etc in EAs\n"); | |
207 | rprintf(F," -S, --sparse handle sparse files efficiently\n"); | |
208 | rprintf(F," -n, --dry-run show what would have been transferred\n"); | |
209 | rprintf(F," -W, --whole-file copy files whole (without rsync algorithm)\n"); | |
210 | @@ -454,6 +455,7 @@ static struct poptOption long_options[] | |
211 | {"modify-window", 0, POPT_ARG_INT, &modify_window, OPT_MODIFY_WINDOW, 0, 0 }, | |
212 | {"super", 0, POPT_ARG_VAL, &am_root, 2, 0, 0 }, | |
213 | {"no-super", 0, POPT_ARG_VAL, &am_root, 0, 0, 0 }, | |
214 | + {"fake-super", 0, POPT_ARG_VAL, &am_root, -1, 0, 0 }, | |
215 | {"owner", 'o', POPT_ARG_VAL, &preserve_uid, 1, 0, 0 }, | |
216 | {"no-owner", 0, POPT_ARG_VAL, &preserve_uid, 0, 0, 0 }, | |
217 | {"no-o", 0, POPT_ARG_VAL, &preserve_uid, 0, 0, 0 }, | |
e66b9b1f WD |
218 | --- old/receiver.c |
219 | +++ new/receiver.c | |
220 | @@ -528,7 +528,7 @@ int recv_files(int f_in, struct file_lis | |
221 | if (fd1 == -1) { | |
222 | st.st_mode = 0; | |
223 | st.st_size = 0; | |
224 | - } else if (do_fstat(fd1,&st) != 0) { | |
225 | + } else if (x_fstat(fd1, &st, NULL) != 0) { | |
226 | rsyserr(FERROR, errno, "fstat %s failed", | |
227 | full_fname(fnamecmp)); | |
228 | discard_receive_data(f_in, file->length); | |
c44efccb WD |
229 | --- old/rsync.c |
230 | +++ new/rsync.c | |
35d8f76e WD |
231 | @@ -49,7 +49,6 @@ extern int preserve_gid; |
232 | extern int inplace; | |
233 | extern int keep_dirlinks; | |
234 | extern int make_backups; | |
235 | -extern mode_t orig_umask; | |
236 | extern struct stats stats; | |
237 | extern struct chmod_mode_struct *daemon_chmod_modes; | |
238 | ||
239 | @@ -197,7 +196,9 @@ int set_file_attrs(char *fname, struct f | |
c44efccb WD |
240 | (long)sxp->st.st_gid, (long)file->gid); |
241 | } | |
242 | } | |
3ae9c1ee WD |
243 | - if (do_lchown(fname, |
244 | + if (am_root < 0) | |
245 | + ; | |
246 | + else if (do_lchown(fname, | |
c44efccb WD |
247 | change_uid ? file->uid : sxp->st.st_uid, |
248 | change_gid ? file->gid : sxp->st.st_gid) != 0) { | |
3ae9c1ee | 249 | /* shouldn't have attempted to change uid or gid |
35d8f76e | 250 | @@ -206,7 +207,7 @@ int set_file_attrs(char *fname, struct f |
c44efccb WD |
251 | change_uid ? "chown" : "chgrp", |
252 | full_fname(fname)); | |
253 | goto cleanup; | |
254 | - } | |
255 | + } else | |
256 | /* a lchown had been done - we have to re-stat if the | |
257 | * destination had the setuid or setgid bits set due | |
258 | * to the side effect of the chown call */ | |
35d8f76e | 259 | @@ -224,6 +225,24 @@ int set_file_attrs(char *fname, struct f |
e66b9b1f WD |
260 | if (preserve_xattrs && set_xattr(fname, file, sxp) == 0) |
261 | updated = 1; | |
c44efccb | 262 | #endif |
e66b9b1f WD |
263 | + |
264 | + if (am_root < 0 && !S_ISLNK(file->mode)) { | |
3ae9c1ee WD |
265 | + switch (set_stat_xattr(fname, file)) { |
266 | + case 0: | |
267 | + break; | |
268 | + case -1: | |
c44efccb WD |
269 | + rsyserr(FERROR, errno, |
270 | + "write of stat xattr failed for %s", | |
271 | + full_fname(fname)); | |
3ae9c1ee WD |
272 | + break; |
273 | + case -2: | |
274 | + rsyserr(FERROR, errno, | |
275 | + "delete of stat xattr failed for %s", | |
276 | + full_fname(fname)); | |
277 | + break; | |
c44efccb WD |
278 | + } |
279 | + } | |
280 | + | |
e66b9b1f WD |
281 | #ifdef SUPPORT_ACLS |
282 | /* It's OK to call set_acl() now, even for a dir, as the generator | |
283 | * will enable owner-writability using chmod, if necessary. | |
35d8f76e | 284 | @@ -237,7 +256,7 @@ int set_file_attrs(char *fname, struct f |
c44efccb | 285 | |
e66b9b1f WD |
286 | #ifdef HAVE_CHMOD |
287 | if ((sxp->st.st_mode & CHMOD_BITS) != (new_mode & CHMOD_BITS)) { | |
288 | - int ret = do_chmod(fname, new_mode); | |
9b91764c | 289 | + int ret = am_root < 0 ? 0 : do_chmod(fname, new_mode); |
e66b9b1f WD |
290 | if (ret < 0) { |
291 | rsyserr(FERROR, errno, | |
292 | "failed to set permissions on %s", | |
c44efccb WD |
293 | --- old/syscall.c |
294 | +++ new/syscall.c | |
e66b9b1f | 295 | @@ -28,6 +28,7 @@ |
c44efccb WD |
296 | #endif |
297 | ||
298 | extern int dry_run; | |
299 | +extern int am_root; | |
300 | extern int read_only; | |
301 | extern int list_only; | |
302 | extern int preserve_perms; | |
e66b9b1f | 303 | @@ -79,6 +80,15 @@ int do_mknod(char *pathname, mode_t mode |
c44efccb WD |
304 | { |
305 | if (dry_run) return 0; | |
306 | RETURN_ERROR_IF_RO_OR_LO; | |
307 | + | |
308 | + /* For --fake-super, we create a normal file with mode 0600. */ | |
309 | + if (am_root < 0) { | |
310 | + int fd = open(pathname, O_WRONLY|O_CREAT|O_TRUNC, S_IWUSR|S_IRUSR); | |
311 | + if (fd < 0 || close(fd) < 0) | |
312 | + return -1; | |
313 | + return 0; | |
314 | + } | |
315 | + | |
316 | #if !defined MKNOD_CREATES_FIFOS && defined HAVE_MKFIFO | |
317 | if (S_ISFIFO(mode)) | |
318 | return mkfifo(pathname, mode); | |
c44efccb WD |
319 | --- old/t_unsafe.c |
320 | +++ new/t_unsafe.c | |
321 | @@ -24,7 +24,11 @@ | |
322 | ||
323 | #include "rsync.h" | |
324 | ||
325 | -int dry_run, read_only, list_only, verbose; | |
326 | +int dry_run = 0; | |
327 | +int am_root = 0; | |
328 | +int read_only = 0; | |
329 | +int list_only = 0; | |
330 | +int verbose = 0; | |
331 | int preserve_perms = 0; | |
332 | ||
333 | int | |
334 | --- old/tls.c | |
335 | +++ new/tls.c | |
336 | @@ -39,6 +39,7 @@ | |
337 | ||
338 | /* These are to make syscall.o shut up. */ | |
339 | int dry_run = 0; | |
340 | +int am_root = 0; /* TODO: add option to set this to -1. */ | |
341 | int read_only = 1; | |
342 | int list_only = 0; | |
343 | int preserve_perms = 0; | |
344 | --- old/trimslash.c | |
345 | +++ new/trimslash.c | |
346 | @@ -23,6 +23,7 @@ | |
347 | ||
348 | /* These are to make syscall.o shut up. */ | |
349 | int dry_run = 0; | |
350 | +int am_root = 0; | |
351 | int read_only = 1; | |
352 | int list_only = 0; | |
353 | int preserve_perms = 0; | |
354 | --- old/xattr.c | |
355 | +++ new/xattr.c | |
e66b9b1f | 356 | @@ -26,11 +26,15 @@ |
c44efccb WD |
357 | #ifdef SUPPORT_XATTRS |
358 | ||
359 | extern int dry_run; | |
360 | +extern int am_root; | |
e66b9b1f | 361 | +extern mode_t orig_umask; |
c44efccb WD |
362 | extern unsigned int file_struct_len; |
363 | ||
364 | #define RSYNC_XAL_INITIAL 5 | |
e66b9b1f WD |
365 | #define RSYNC_XAL_LIST_INITIAL 100 |
366 | ||
367 | +#define FAKE_XATTR "user.rsync%stat" | |
368 | + | |
369 | typedef struct { | |
370 | char *name; | |
371 | char *datum; | |
372 | @@ -130,9 +134,15 @@ static int rsync_xal_get(const char *fna | |
c44efccb WD |
373 | if (name_size == 0) |
374 | return 0; | |
375 | for (left = name_size, name = namebuf; left > 0 ; left -= len, name += len) { | |
376 | - rsync_xa *rxas = EXPAND_ITEM_LIST(xalp, rsync_xa, RSYNC_XAL_INITIAL); | |
377 | + rsync_xa *rxas; | |
378 | ||
379 | len = strlen(name) + 1; | |
380 | + if (am_root < 0 && len == sizeof FAKE_XATTR | |
d30eff93 | 381 | + && name[10] == '%' && strcmp(name, FAKE_XATTR) == 0) |
c44efccb WD |
382 | + continue; |
383 | + | |
384 | + rxas = EXPAND_ITEM_LIST(xalp, rsync_xa, RSYNC_XAL_INITIAL); | |
385 | + | |
386 | datum_size = sys_lgetxattr(fname, name, NULL, 0); | |
387 | if (datum_size < 0) { | |
388 | if (errno == ENOTSUP) | |
e66b9b1f | 389 | @@ -285,10 +295,19 @@ void receive_xattr(struct file_struct *f |
c44efccb WD |
390 | out_of_memory("receive_xattr"); |
391 | read_buf(f, ptr, name_len); | |
392 | read_buf(f, ptr + name_len, datum_len); | |
393 | + | |
394 | + if (am_root < 0 && name_len == sizeof FAKE_XATTR | |
d30eff93 | 395 | + && ptr[10] == '%' && strcmp(ptr, FAKE_XATTR) == 0) { |
c44efccb WD |
396 | + free(ptr); |
397 | + temp_xattr.count--; | |
398 | + continue; | |
399 | + } | |
400 | + | |
401 | rxa->name_len = name_len; | |
402 | rxa->datum_len = datum_len; | |
403 | rxa->name = ptr; | |
404 | rxa->datum = ptr + name_len; | |
405 | + | |
406 | #ifdef HAVE_OSX_XATTRS | |
1e883fdf WD |
407 | if (strncmp(rxa->name, UNIQUE_PREFIX, UPRE_LEN) == 0) { |
408 | rxa->name_len -= UPRE_LEN; | |
35d8f76e | 409 | @@ -365,4 +384,103 @@ int set_xattr(const char *fname, const s |
e66b9b1f WD |
410 | return rsync_xal_set(fname, lst + ndx); /* TODO: This needs to return 1 if no xattrs changed! */ |
411 | } | |
412 | ||
413 | +int get_stat_xattr(const char *fname, int fd, STRUCT_STAT *st) | |
414 | +{ | |
415 | + int mode, rdev_major, rdev_minor, uid, gid, len; | |
416 | + char buf[256]; | |
417 | + | |
418 | + if (fname) | |
419 | + len = sys_lgetxattr(fname, FAKE_XATTR, buf, sizeof buf - 1); | |
420 | + else | |
421 | + len = sys_fgetxattr(fd, FAKE_XATTR, buf, sizeof buf - 1); | |
422 | + if (len < 0 || len >= (int)sizeof buf) { | |
423 | + if (errno == ENOTSUP || errno == ENOATTR) | |
424 | + return -1; | |
425 | + return -1; | |
426 | + } | |
427 | + buf[len] = '\0'; | |
428 | + | |
429 | + if (sscanf(buf, "%o %d,%d %d:%d", | |
430 | + &mode, &rdev_major, &rdev_minor, &uid, &gid) != 5) { | |
431 | + errno = EINVAL; | |
432 | + return -1; | |
433 | + } | |
434 | + | |
435 | + st->st_mode = mode; | |
436 | + st->st_rdev = MAKEDEV(rdev_major, rdev_minor); | |
437 | + st->st_uid = uid; | |
438 | + st->st_gid = gid; | |
439 | + | |
440 | + return 0; | |
441 | +} | |
442 | + | |
443 | +int set_stat_xattr(const char *fname, struct file_struct *file) | |
444 | +{ | |
445 | + STRUCT_STAT fst, xst; | |
446 | + dev_t rdev; | |
35d8f76e | 447 | + mode_t mode; |
e66b9b1f WD |
448 | + |
449 | + if (dry_run) | |
450 | + return 0; | |
451 | + | |
452 | + if (x_stat(fname, &fst, &xst) < 0) | |
453 | + return -1; | |
454 | + | |
455 | + if (IS_DEVICE(file->mode) || IS_SPECIAL(file->mode)) | |
456 | + rdev = file->u.rdev; | |
457 | + else | |
458 | + rdev = 0; | |
459 | + | |
35d8f76e WD |
460 | + /* Force the real file's mode to our liking. */ |
461 | + mode = (fst.st_mode & ~CHMOD_BITS) | |
462 | + | ((S_ISDIR(fst.st_mode) ? 0777 : 0666) & (~orig_umask | S_IRWXU)); | |
463 | + if (fst.st_mode != mode) | |
464 | + do_chmod(fname, mode); | |
e66b9b1f WD |
465 | + if (!IS_DEVICE(fst.st_mode) && !IS_SPECIAL(fst.st_mode)) |
466 | + fst.st_rdev = 0; /* just in case */ | |
467 | + | |
35d8f76e | 468 | + if (mode == file->mode && fst.st_rdev == rdev |
e66b9b1f WD |
469 | + && fst.st_uid == file->uid && fst.st_gid == file->gid) { |
470 | + /* xst.st_mode will be 0 if there's no current stat xattr */ | |
471 | + if (xst.st_mode && sys_lremovexattr(fname, FAKE_XATTR) < 0) | |
472 | + return -2; | |
473 | + return 0; | |
474 | + } | |
475 | + | |
476 | + if (xst.st_mode != file->mode || xst.st_rdev != rdev | |
477 | + || xst.st_uid != file->uid || xst.st_gid != file->gid) { | |
478 | + char buf[256]; | |
479 | + int len = snprintf(buf, sizeof buf, "%o %u,%u %u:%u", | |
480 | + (int)file->mode, | |
481 | + (int)major(rdev), (int)minor(rdev), | |
482 | + (int)file->uid, (int)file->gid); | |
483 | + return sys_lsetxattr(fname, FAKE_XATTR, buf, len, 0); | |
484 | + } | |
485 | + return 0; | |
486 | +} | |
487 | + | |
488 | +int x_stat(const char *fname, STRUCT_STAT *fst, STRUCT_STAT *xst) | |
489 | +{ | |
490 | + int ret = do_stat(fname, fst); | |
491 | + if (get_stat_xattr(fname, -1, xst? xst : fst) != 0 && xst) | |
492 | + xst->st_mode = 0; | |
493 | + return ret; | |
494 | +} | |
495 | + | |
496 | +int x_lstat(const char *fname, STRUCT_STAT *fst, STRUCT_STAT *xst) | |
497 | +{ | |
498 | + int ret = do_lstat(fname, fst); | |
499 | + if (get_stat_xattr(fname, -1, xst? xst : fst) != 0 && xst) | |
500 | + xst->st_mode = 0; | |
501 | + return ret; | |
502 | +} | |
503 | + | |
504 | +int x_fstat(int fd, STRUCT_STAT *fst, STRUCT_STAT *xst) | |
505 | +{ | |
506 | + int ret = do_fstat(fd, fst); | |
507 | + if (get_stat_xattr(NULL, fd, xst? xst : fst) != 0 && xst) | |
508 | + xst->st_mode = 0; | |
509 | + return ret; | |
510 | +} | |
511 | + | |
512 | #endif /* SUPPORT_XATTRS */ |