1 /* SPDX-License-Identifier: GPL-2.0-only */
3 * Various functions to work with meta entries.
5 * Copyright (C) 2007-2008 David Härdeman <david@hardeman.nu>
6 * Copyright (C) 2012-2018 Przemyslaw Pawelczyk <przemoc@gmail.com>
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; only version 2 of the License is applicable.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15 * See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
25 #include <sys/types.h>
29 #if !defined(NO_XATTR) || !NO_XATTR
30 # include <sys/xattr.h>
31 #endif /* !NO_XATTR */
42 #include <sys/param.h>
44 # include <bsd/string.h>
47 #include "metastore.h"
48 #include "metaentry.h"
52 # define PATH_MAX 4096
55 #if !defined(NO_XATTR) || !NO_XATTR
56 /* Free's a metaentry and all its parameters */
58 mentry_free(struct metaentry
*m
)
69 for (i
= 0; i
< m
->xattrs
; i
++) {
70 free(m
->xattr_names
[i
]);
71 free(m
->xattr_values
[i
]);
75 free(m
->xattr_values
);
76 free(m
->xattr_lvalues
);
80 #endif /* !NO_XATTR */
82 /* Allocates an empty metahash table */
83 static struct metahash
*
86 struct metahash
*mhash
;
87 mhash
= xmalloc(sizeof(struct metahash
));
88 memset(mhash
, 0, sizeof(struct metahash
));
92 /* Generates a hash key (using djb2) */
100 hash
= ((hash
<< 5) + hash
) + c
;
102 return hash
% HASH_INDEXES
;
105 /* Allocates an empty metaentry */
106 static struct metaentry
*
109 struct metaentry
*mentry
;
110 mentry
= xmalloc(sizeof(struct metaentry
));
111 memset(mentry
, 0, sizeof(struct metaentry
));
115 /* Does a bisect search for the closest match in a metaentry list */
116 static struct metaentry
*
117 mentry_find(const char *path
, struct metahash
*mhash
)
119 struct metaentry
*base
;
123 msg(MSG_ERROR
, "%s called with empty hash table\n", __func__
);
128 for (base
= mhash
->bucket
[key
]; base
; base
= base
->next
) {
129 if (!strcmp(base
->path
, path
))
136 /* Inserts a metaentry into a metaentry list */
138 mentry_insert(struct metaentry
*mentry
, struct metahash
*mhash
)
142 key
= hash(mentry
->path
);
143 mentry
->next
= mhash
->bucket
[key
];
144 mhash
->bucket
[key
] = mentry
;
148 /* Prints a metaentry */
150 mentry_print(const struct metaentry
*mentry
)
154 if (!mentry
|| !mentry
->path
) {
156 "Incorrect meta entry passed to printmetaentry\n");
160 msg(MSG_DEBUG
, "===========================\n");
161 msg(MSG_DEBUG
, "Dump of metaentry %p\n", mentry
);
162 msg(MSG_DEBUG
, "===========================\n");
164 msg(MSG_DEBUG
, "path\t\t: %s\n", mentry
->path
);
165 msg(MSG_DEBUG
, "owner\t\t: %s\n", mentry
->owner
);
166 msg(MSG_DEBUG
, "group\t\t: %s\n", mentry
->group
);
167 msg(MSG_DEBUG
, "mtime\t\t: %ld\n", (unsigned long)mentry
->mtime
);
168 msg(MSG_DEBUG
, "mtimensec\t: %ld\n", (unsigned long)mentry
->mtimensec
);
169 msg(MSG_DEBUG
, "mode\t\t: %ld\n", (unsigned long)mentry
->mode
);
170 for (i
= 0; i
< mentry
->xattrs
; i
++) {
171 msg(MSG_DEBUG
, "xattr[%i]\t: %s=\"", i
, mentry
->xattr_names
[i
]);
172 binary_print(mentry
->xattr_values
[i
], mentry
->xattr_lvalues
[i
]);
173 msg(MSG_DEBUG
, "\"\n");
176 msg(MSG_DEBUG
, "===========================\n\n");
179 /* Prints all metaentries in a metaentry list */
181 mentries_print(const struct metahash
*mhash
)
183 const struct metaentry
*mentry
;
186 for (index
= 0; index
< HASH_INDEXES
; index
++)
187 for (mentry
= mhash
->bucket
[index
]; mentry
; mentry
= mentry
->next
)
188 mentry_print(mentry
);
190 msg(MSG_DEBUG
, "%i entries in total\n", mhash
->count
);
194 /* Creates a metaentry for the file/dir/etc at path */
196 mentry_create(const char *path
)
198 #if !defined(NO_XATTR) || !NO_XATTR
199 ssize_t lsize
, vsize
;
201 #endif /* !NO_XATTR */
205 #if !defined(NO_XATTR) || !NO_XATTR
207 #endif /* !NO_XATTR */
208 struct metaentry
*mentry
;
210 if (lstat(path
, &sbuf
)) {
211 msg(MSG_ERROR
, "lstat failed for %s: %s\n",
212 path
, strerror(errno
));
216 pbuf
= xgetpwuid(sbuf
.st_uid
);
218 msg(MSG_ERROR
, "getpwuid failed for %s: uid %i not found\n",
219 path
, (int)sbuf
.st_uid
);
223 gbuf
= xgetgrgid(sbuf
.st_gid
);
225 msg(MSG_ERROR
, "getgrgid failed for %s: gid %i not found\n",
226 path
, (int)sbuf
.st_gid
);
230 mentry
= mentry_alloc();
231 mentry
->path
= xstrdup(path
);
232 mentry
->pathlen
= strlen(mentry
->path
);
233 mentry
->owner
= xstrdup(pbuf
->pw_name
);
234 mentry
->group
= xstrdup(gbuf
->gr_name
);
235 mentry
->mode
= sbuf
.st_mode
& 0177777;
236 mentry
->mtime
= sbuf
.st_mtim
.tv_sec
;
237 mentry
->mtimensec
= sbuf
.st_mtim
.tv_nsec
;
239 /* symlinks have no xattrs */
240 if (S_ISLNK(mentry
->mode
))
243 #if !defined(NO_XATTR) || !NO_XATTR
244 lsize
= listxattr(path
, NULL
, 0);
246 /* Perhaps the FS doesn't support xattrs? */
247 if (errno
== ENOTSUP
)
250 msg(MSG_ERROR
, "listxattr failed for %s: %s\n",
251 path
, strerror(errno
));
255 list
= xmalloc(lsize
);
256 lsize
= listxattr(path
, list
, lsize
);
258 msg(MSG_ERROR
, "listxattr failed for %s: %s\n",
259 path
, strerror(errno
));
265 for (attr
= list
; attr
< list
+ lsize
; attr
= strchr(attr
, '\0') + 1) {
275 mentry
->xattr_names
= xmalloc(i
* sizeof(char *));
276 mentry
->xattr_values
= xmalloc(i
* sizeof(char *));
277 mentry
->xattr_lvalues
= xmalloc(i
* sizeof(ssize_t
));
280 for (attr
= list
; attr
< list
+ lsize
; attr
= strchr(attr
, '\0') + 1) {
284 mentry
->xattr_names
[i
] = xstrdup(attr
);
285 mentry
->xattr_values
[i
] = NULL
;
287 vsize
= getxattr(path
, attr
, NULL
, 0);
289 msg(MSG_ERROR
, "getxattr failed for %s: %s\n",
290 path
, strerror(errno
));
292 mentry
->xattrs
= i
+ 1;
297 mentry
->xattr_lvalues
[i
] = vsize
;
298 mentry
->xattr_values
[i
] = xmalloc(vsize
);
300 vsize
= getxattr(path
, attr
, mentry
->xattr_values
[i
], vsize
);
302 msg(MSG_ERROR
, "getxattr failed for %s: %s\n",
303 path
, strerror(errno
));
305 mentry
->xattrs
= i
+ 1;
313 #endif /* !NO_XATTR */
318 /* Cleans up a path and makes it relative to cwd unless it is absolute */
320 normalize_path(const char *orig
)
322 char *real
= realpath(orig
, NULL
);
326 getcwd(cwd
, PATH_MAX
);
330 if (!strncmp(real
, cwd
, strlen(cwd
))) {
331 result
= xmalloc(strlen(real
) - strlen(cwd
) + 1 + 1);
334 strcat(result
, real
+ strlen(cwd
));
336 result
= xstrdup(real
);
343 /* Internal function for the recursive path walk */
345 mentries_recurse(const char *path
, struct metahash
*mhash
, msettings
*st
)
348 struct metaentry
*mentry
;
349 char tpath
[PATH_MAX
];
356 if (lstat(path
, &sbuf
)) {
357 msg(MSG_ERROR
, "lstat failed for %s: %s\n",
358 path
, strerror(errno
));
362 mentry
= mentry_create(path
);
366 mentry_insert(mentry
, mhash
);
368 if (S_ISDIR(sbuf
.st_mode
)) {
371 msg(MSG_ERROR
, "opendir failed for %s: %s\n",
372 path
, strerror(errno
));
376 while ((dent
= readdir(dir
))) {
377 if (!strcmp(dent
->d_name
, ".") ||
378 !strcmp(dent
->d_name
, "..") ||
379 (!st
->do_git
&& !strcmp(dent
->d_name
, ".git"))
382 snprintf(tpath
, PATH_MAX
, "%s/%s", path
, dent
->d_name
);
383 tpath
[PATH_MAX
- 1] = '\0';
384 mentries_recurse(tpath
, mhash
, st
);
391 /* Recurses opath and adds metadata entries to the metaentry list */
393 mentries_recurse_path(const char *opath
, struct metahash
**mhash
, msettings
*st
)
395 char *path
= normalize_path(opath
);
398 *mhash
= mhash_alloc();
399 mentries_recurse(path
, *mhash
, st
);
403 /* Stores metaentries to a file */
405 mentries_tofile(const struct metahash
*mhash
, const char *path
)
408 const struct metaentry
*mentry
;
412 to
= fopen(path
, "w");
414 msg(MSG_CRITICAL
, "Failed to open %s: %s\n",
415 path
, strerror(errno
));
419 write_binary_string(SIGNATURE
, SIGNATURELEN
, to
);
420 write_binary_string(VERSION
, VERSIONLEN
, to
);
422 for (key
= 0; key
< HASH_INDEXES
; key
++) {
423 for (mentry
= mhash
->bucket
[key
]; mentry
; mentry
= mentry
->next
) {
424 write_string(mentry
->path
, to
);
425 write_string(mentry
->owner
, to
);
426 write_string(mentry
->group
, to
);
427 write_int((uint64_t)mentry
->mtime
, 8, to
);
428 write_int((uint64_t)mentry
->mtimensec
, 8, to
);
429 write_int((uint64_t)mentry
->mode
, 2, to
);
430 write_int(mentry
->xattrs
, 4, to
);
431 for (i
= 0; i
< mentry
->xattrs
; i
++) {
432 write_string(mentry
->xattr_names
[i
], to
);
433 write_int(mentry
->xattr_lvalues
[i
], 4, to
);
434 write_binary_string(mentry
->xattr_values
[i
],
435 mentry
->xattr_lvalues
[i
], to
);
443 /* Creates a metaentry list from a file */
445 mentries_fromfile(struct metahash
**mhash
, const char *path
)
447 struct metaentry
*mentry
;
456 *mhash
= mhash_alloc();
458 fd
= open(path
, O_RDONLY
);
460 msg(MSG_CRITICAL
, "Failed to open %s: %s\n",
461 path
, strerror(errno
));
465 if (fstat(fd
, &sbuf
)) {
466 msg(MSG_CRITICAL
, "Failed to stat %s: %s\n",
467 path
, strerror(errno
));
471 if (sbuf
.st_size
< (SIGNATURELEN
+ VERSIONLEN
)) {
472 msg(MSG_CRITICAL
, "File %s has an invalid size\n", path
);
476 mmapstart
= mmap(NULL
, (size_t)sbuf
.st_size
, PROT_READ
,
478 if (mmapstart
== MAP_FAILED
) {
479 msg(MSG_CRITICAL
, "Unable to mmap %s: %s\n",
480 path
, strerror(errno
));
484 max
= mmapstart
+ sbuf
.st_size
;
486 if (strncmp(ptr
, SIGNATURE
, SIGNATURELEN
)) {
487 msg(MSG_CRITICAL
, "Invalid signature for file %s\n", path
);
492 if (strncmp(ptr
, VERSION
, VERSIONLEN
)) {
493 msg(MSG_CRITICAL
, "Invalid version of file %s\n", path
);
498 while (ptr
< mmapstart
+ sbuf
.st_size
) {
500 msg(MSG_CRITICAL
, "Invalid characters in file %s\n",
505 mentry
= mentry_alloc();
506 mentry
->path
= read_string(&ptr
, max
);
507 mentry
->pathlen
= strlen(mentry
->path
);
508 mentry
->owner
= read_string(&ptr
, max
);
509 mentry
->group
= read_string(&ptr
, max
);
510 mentry
->mtime
= (time_t)read_int(&ptr
, 8, max
);
511 mentry
->mtimensec
= (time_t)read_int(&ptr
, 8, max
);
512 mentry
->mode
= (mode_t
)read_int(&ptr
, 2, max
);
513 mentry
->xattrs
= (unsigned)read_int(&ptr
, 4, max
);
515 if (!mentry
->xattrs
) {
516 mentry_insert(mentry
, *mhash
);
520 mentry
->xattr_names
= xmalloc(mentry
->xattrs
* sizeof(char *));
521 mentry
->xattr_lvalues
= xmalloc(mentry
->xattrs
* sizeof(ssize_t
));
522 mentry
->xattr_values
= xmalloc(mentry
->xattrs
* sizeof(char *));
524 for (i
= 0; i
< mentry
->xattrs
; i
++) {
525 mentry
->xattr_names
[i
] = read_string(&ptr
, max
);
526 mentry
->xattr_lvalues
[i
] = (int)read_int(&ptr
, 4, max
);
527 mentry
->xattr_values
[i
] = read_binary_string(
529 mentry
->xattr_lvalues
[i
],
533 mentry_insert(mentry
, *mhash
);
537 munmap(mmapstart
, sbuf
.st_size
);
541 /* Searches haystack for an xattr matching xattr number n in needle */
543 mentry_find_xattr(struct metaentry
*haystack
, struct metaentry
*needle
,
548 for (i
= 0; i
< haystack
->xattrs
; i
++) {
549 if (strcmp(haystack
->xattr_names
[i
], needle
->xattr_names
[n
]))
551 if (haystack
->xattr_lvalues
[i
] != needle
->xattr_lvalues
[n
])
553 if (bcmp(haystack
->xattr_values
[i
], needle
->xattr_values
[n
],
554 needle
->xattr_lvalues
[n
])
562 /* Returns zero if all xattrs in left and right match */
564 mentry_compare_xattr(struct metaentry
*left
, struct metaentry
*right
)
568 if (left
->xattrs
!= right
->xattrs
)
571 /* Make sure all xattrs in left are found in right and vice versa */
572 for (i
= 0; i
< left
->xattrs
; i
++) {
573 if ( mentry_find_xattr(right
, left
, i
) < 0
574 || mentry_find_xattr(left
, right
, i
) < 0
583 /* Compares two metaentries and returns an int with a bitmask of differences */
585 mentry_compare(struct metaentry
*left
, struct metaentry
*right
, msettings
*st
)
587 int retval
= DIFF_NONE
;
589 if (!left
|| !right
) {
590 msg(MSG_ERROR
, "%s called with empty list\n", __func__
);
594 if (strcmp(left
->path
, right
->path
))
597 if (strcmp(left
->owner
, right
->owner
))
598 retval
|= DIFF_OWNER
;
600 if (strcmp(left
->group
, right
->group
))
601 retval
|= DIFF_GROUP
;
603 if ((left
->mode
& 07777) != (right
->mode
& 07777))
606 if ((left
->mode
& S_IFMT
) != (right
->mode
& S_IFMT
))
609 if (st
->do_mtime
&& strcmp(left
->path
, st
->metafile
) &&
610 ( left
->mtime
!= right
->mtime
611 || left
->mtimensec
!= right
->mtimensec
)
613 retval
|= DIFF_MTIME
;
615 if (mentry_compare_xattr(left
, right
)) {
616 retval
|= DIFF_XATTR
;
623 /* Compares lists of real and stored metadata and calls pfunc for each */
625 mentries_compare(struct metahash
*mhashreal
,
626 struct metahash
*mhashstored
,
628 (struct metaentry
*real
, struct metaentry
*stored
, int cmp
),
631 struct metaentry
*real
, *stored
;
634 if (!mhashreal
|| !mhashstored
) {
635 msg(MSG_ERROR
, "%s called with empty list\n", __func__
);
639 for (key
= 0; key
< HASH_INDEXES
; key
++) {
640 for (real
= mhashreal
->bucket
[key
]; real
; real
= real
->next
) {
641 stored
= mentry_find(real
->path
, mhashstored
);
644 pfunc(real
, NULL
, DIFF_ADDED
);
646 pfunc(real
, stored
, mentry_compare(real
, stored
, st
));
649 for (stored
= mhashstored
->bucket
[key
]; stored
; stored
= stored
->next
) {
650 real
= mentry_find(stored
->path
, mhashreal
);
653 pfunc(NULL
, stored
, DIFF_DELE
);
658 /* Dumps given metadata */
660 mentries_dump(struct metahash
*mhash
)
662 const struct metaentry
*mentry
;
663 char mode
[11 + 1] = "";
664 char date
[12 + 2 + 2 + 2*1 + 1 + 2 + 2 + 2 + 2*1 + 1] = "";
665 char zone
[5 + 1] = "";
668 for (int key
= 0; key
< HASH_INDEXES
; key
++) {
669 for (mentry
= mhash
->bucket
[key
]; mentry
; mentry
= mentry
->next
) {
670 strmode(mentry
->mode
, mode
);
671 localtime_r(&mentry
->mtime
, &cal
);
672 strftime(date
, sizeof(date
), "%F %T", &cal
);
673 strftime(zone
, sizeof(zone
), "%z", &cal
);
674 printf("%s\t%s\t%s\t%s.%09ld %s\t%s%s\n",
676 mentry
->owner
, mentry
->group
,
677 date
, mentry
->mtimensec
, zone
,
678 mentry
->path
, S_ISDIR(mentry
->mode
) ? "/" : "");
679 for (unsigned i
= 0; i
< mentry
->xattrs
; i
++) {
680 printf("\t\t\t\t%s%s\t%s=",
681 mentry
->path
, S_ISDIR(mentry
->mode
) ? "/" : "",
682 mentry
->xattr_names
[i
]);
684 for (; p
< mentry
->xattr_lvalues
[i
]; p
++) {
685 const char ch
= mentry
->xattr_values
[i
][p
];
686 if ((unsigned)(ch
- 32) > 126 - 32) {
693 (int)mentry
->xattr_lvalues
[i
],
694 mentry
->xattr_values
[i
]);
697 for (p
= 0; p
< mentry
->xattr_lvalues
[i
]; p
++)
698 printf("%02hhx", (char)mentry
->xattr_values
[i
][p
]);