Line data Source code
1 : /**
2 : * This file is part of rmlint.
3 : *
4 : * rmlint is free software: you can redistribute it and/or modify
5 : * it under the terms of the GNU General Public License as published by
6 : * the Free Software Foundation, either version 3 of the License, or
7 : * (at your option) any later version.
8 : *
9 : * rmlint is distributed in the hope that it will be useful,
10 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 : * GNU General Public License for more details.
13 : *
14 : * You should have received a copy of the GNU General Public License
15 : * along with rmlint. If not, see <http://www.gnu.org/licenses/>.
16 : *
17 : * Authors:
18 : *
19 : * - Christopher <sahib> Pahl 2010-2015 (https://github.com/sahib)
20 : * - Daniel <SeeSpotRun> T. 2014-2015 (https://github.com/SeeSpotRun)
21 : *
22 : * Hosted on http://github.com/sahib/rmlint
23 : *
24 : **/
25 :
26 : #include "config.h"
27 : #include "xattr.h"
28 :
29 : #include <sys/types.h>
30 : #include <string.h>
31 : #include <errno.h>
32 :
33 : #if HAVE_XATTR
34 : #include <sys/xattr.h>
35 : #endif
36 :
37 : #ifndef ENODATA
38 : #define ENODATA ENOMSG
39 : #endif
40 :
41 : ////////////////////////////
42 : // UTILITY FUNCTIONS //
43 : ////////////////////////////
44 :
45 : #if HAVE_XATTR
46 :
47 : /* Compat wrappers for MacOSX and other platforms.
48 : */
49 :
50 : #if RM_IS_APPLE
51 :
52 : ssize_t rm_sys_getxattr(const char *path, const char *name, void *value, size_t size) {
53 : return getxattr(path, name, value, size, 0, 0);
54 : }
55 :
56 : ssize_t rm_sys_setxattr(
57 : const char *path, const char *name, const void *value, size_t size, int flags) {
58 : return setxattr(path, name, value, size, 0, flags);
59 : }
60 :
61 : int rm_sys_removexattr(const char *path, const char *name) {
62 : return removexattr(path, name, 0);
63 : }
64 :
65 : #else
66 :
67 784 : ssize_t rm_sys_getxattr(const char *path, const char *name, void *value, size_t size) {
68 784 : return getxattr(path, name, value, size);
69 : }
70 :
71 784 : ssize_t rm_sys_setxattr(
72 : const char *path, const char *name, const void *value, size_t size, int flags) {
73 784 : return setxattr(path, name, value, size, flags);
74 : }
75 :
76 1568 : int rm_sys_removexattr(const char *path, const char *name) {
77 1568 : return removexattr(path, name);
78 : }
79 :
80 : #endif
81 :
82 3136 : static int rm_xattr_build_key(RmSession *session,
83 : const char *suffix,
84 : char *buf,
85 : size_t buf_size) {
86 3136 : g_assert(session);
87 :
88 : /* Be safe, assume caller is not concentrated. */
89 3136 : memset(buf, 0, sizeof(buf_size));
90 :
91 3136 : const char *digest_name = rm_digest_type_to_string(session->cfg->checksum_type);
92 3136 : if(session->cfg->checksum_type == RM_DIGEST_PARANOID) {
93 224 : digest_name = rm_digest_type_to_string(RM_DEFAULT_DIGEST);
94 : }
95 :
96 3136 : return snprintf(buf, buf_size, "user.rmlint.%s.%s", digest_name, suffix) < 0;
97 : }
98 :
99 392 : static int rm_xattr_build_cksum(RmFile *file, char *buf, size_t buf_size) {
100 392 : g_assert(file);
101 392 : g_assert(file->digest);
102 :
103 392 : memset(buf, '0', buf_size);
104 392 : buf[buf_size - 1] = 0;
105 :
106 392 : if(file->digest->type == RM_DIGEST_PARANOID) {
107 28 : g_assert(file->digest->paranoid->shadow_hash);
108 28 : return rm_digest_hexstring(file->digest->paranoid->shadow_hash, buf);
109 : } else {
110 364 : return rm_digest_hexstring(file->digest, buf);
111 : }
112 : }
113 :
114 3135 : static int rm_xattr_is_fail(const char *name, int rc) {
115 3135 : if(rc != -1) {
116 0 : return 0;
117 : }
118 :
119 3135 : if(errno != ENOTSUP && errno != ENODATA) {
120 0 : rm_log_perror(name);
121 0 : return errno;
122 : }
123 :
124 3136 : return 0;
125 : }
126 :
127 783 : static int rm_xattr_set(RmFile *file,
128 : const char *key,
129 : const char *value,
130 : size_t value_size) {
131 783 : RM_DEFINE_PATH(file);
132 783 : return rm_xattr_is_fail("setxattr",
133 784 : rm_sys_setxattr(file_path, key, value, value_size, 0));
134 : }
135 :
136 784 : static int rm_xattr_get(RmFile *file,
137 : const char *key,
138 : char *out_value,
139 : size_t value_size) {
140 784 : RM_DEFINE_PATH(file);
141 :
142 784 : return rm_xattr_is_fail("getxattr",
143 784 : rm_sys_getxattr(file_path, key, out_value, value_size));
144 : }
145 :
146 1568 : static int rm_xattr_del(RmFile *file, const char *key) {
147 1568 : RM_DEFINE_PATH(file);
148 1568 : return rm_xattr_is_fail("removexattr", rm_sys_removexattr(file_path, key));
149 : }
150 :
151 : #endif
152 :
153 : ////////////////////////////
154 : // ACTUAL API FUNCTIONS //
155 : ////////////////////////////
156 :
157 392 : int rm_xattr_write_hash(RmSession *session, RmFile *file) {
158 392 : g_assert(file);
159 392 : g_assert(file->digest);
160 392 : g_assert(session);
161 :
162 : #if HAVE_XATTR
163 392 : if(file->has_ext_cksum || session->cfg->write_cksum_to_xattr == false) {
164 0 : return EINVAL;
165 : }
166 :
167 : char cksum_key[64], mtime_key[64],
168 392 : cksum_hex_str[rm_digest_get_bytes(file->digest) * 2 + 1], timestamp[64] = {0};
169 :
170 392 : int timestamp_bytes = 0;
171 392 : double actual_time_sec = difftime(file->mtime, 0);
172 :
173 784 : if(0 || rm_xattr_build_key(session, "cksum", cksum_key, sizeof(cksum_key)) ||
174 784 : rm_xattr_build_key(session, "mtime", mtime_key, sizeof(mtime_key)) ||
175 784 : rm_xattr_build_cksum(file, cksum_hex_str, sizeof(cksum_hex_str)) <= 0 ||
176 784 : rm_xattr_set(file, cksum_key, cksum_hex_str, sizeof(cksum_hex_str)) ||
177 392 : (timestamp_bytes = snprintf(
178 392 : timestamp, sizeof(timestamp), "%lld", (long long)actual_time_sec)) == -1 ||
179 392 : rm_xattr_set(file, mtime_key, timestamp, timestamp_bytes)) {
180 0 : return errno;
181 : }
182 : #endif
183 392 : return 0;
184 : }
185 :
186 392 : char *rm_xattr_read_hash(RmSession *session, RmFile *file) {
187 392 : g_assert(file);
188 392 : g_assert(session);
189 :
190 : #if HAVE_XATTR
191 392 : if(session->cfg->read_cksum_from_xattr == false) {
192 0 : return NULL;
193 : }
194 :
195 392 : char cksum_key[64] = {0}, mtime_key[64] = {0}, mtime_buf[64] = {0},
196 392 : cksum_hex_str[512] = {0};
197 :
198 392 : memset(cksum_hex_str, '0', sizeof(cksum_hex_str));
199 392 : cksum_hex_str[sizeof(cksum_hex_str) - 1] = 0;
200 :
201 784 : if(0 || rm_xattr_build_key(session, "cksum", cksum_key, sizeof(cksum_key)) ||
202 784 : rm_xattr_get(file, cksum_key, cksum_hex_str, sizeof(cksum_hex_str) - 1) ||
203 784 : rm_xattr_build_key(session, "mtime", mtime_key, sizeof(mtime_key)) ||
204 392 : rm_xattr_get(file, mtime_key, mtime_buf, sizeof(mtime_buf) - 1)) {
205 0 : return NULL;
206 : }
207 :
208 392 : if(g_ascii_strtoll(mtime_buf, NULL, 10) < file->mtime) {
209 : /* Data is too old and not useful, autoclean it */
210 392 : rm_xattr_clear_hash(session, file);
211 392 : return NULL;
212 : }
213 :
214 : /* remember, this file is special. A unicorn amongst files. */
215 0 : file->has_ext_cksum = true;
216 :
217 0 : return g_strdup(cksum_hex_str);
218 : #else
219 : return NULL;
220 : #endif
221 : }
222 :
223 784 : int rm_xattr_clear_hash(RmSession *session, RmFile *file) {
224 784 : g_assert(file);
225 784 : g_assert(session);
226 :
227 : #if HAVE_XATTR
228 784 : int error = 0;
229 784 : const char *keys[] = {"cksum", "mtime", NULL};
230 :
231 2352 : for(int i = 0; keys[i]; ++i) {
232 1568 : char key[64] = {0};
233 :
234 1568 : if(rm_xattr_build_key(session, keys[i], key, sizeof(key))) {
235 0 : error = EINVAL;
236 0 : continue;
237 : }
238 :
239 1568 : if(rm_xattr_del(file, key)) {
240 0 : error = errno;
241 : }
242 : }
243 :
244 784 : return error;
245 : #else
246 : return EXIT_FAILURE;
247 : #endif
248 : }
|