| Directory: | cvmfs/ |
|---|---|
| File: | cvmfs/acl.cc |
| Date: | 2025-11-09 02:35:23 |
| Exec | Total | Coverage | |
|---|---|---|---|
| Lines: | 138 | 151 | 91.4% |
| Branches: | 104 | 132 | 78.8% |
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | /** | ||
| 2 | * This file is part of the CernVM File System. | ||
| 3 | */ | ||
| 4 | |||
| 5 | #include "acl.h" | ||
| 6 | |||
| 7 | #include <string.h> | ||
| 8 | |||
| 9 | #include <algorithm> | ||
| 10 | #include <cassert> | ||
| 11 | #include <cstring> | ||
| 12 | #include <vector> | ||
| 13 | |||
| 14 | #include "util/posix.h" | ||
| 15 | |||
| 16 | using namespace std; // NOLINT | ||
| 17 | |||
| 18 | #ifdef COMPARE_TO_LIBACL | ||
| 19 | #include "acl/libacl.h" | ||
| 20 | #else // COMPARE_TO_LIBACL | ||
| 21 | |||
| 22 | // ACL permission bits | ||
| 23 | #define ACL_READ (0x04) | ||
| 24 | #define ACL_WRITE (0x02) | ||
| 25 | #define ACL_EXECUTE (0x01) | ||
| 26 | |||
| 27 | // ACL tag types | ||
| 28 | #define ACL_UNDEFINED_TAG (0x00) | ||
| 29 | #define ACL_USER_OBJ (0x01) | ||
| 30 | #define ACL_USER (0x02) | ||
| 31 | #define ACL_GROUP_OBJ (0x04) | ||
| 32 | #define ACL_GROUP (0x08) | ||
| 33 | #define ACL_MASK (0x10) | ||
| 34 | #define ACL_OTHER (0x20) | ||
| 35 | |||
| 36 | // ACL qualifier constants | ||
| 37 | #define ACL_UNDEFINED_ID ((id_t) - 1) | ||
| 38 | |||
| 39 | #endif // COMPARE_TO_LIBACL | ||
| 40 | |||
| 41 | #define ACL_EA_VERSION 0x0002 | ||
| 42 | |||
| 43 | // ACL data structures | ||
| 44 | struct acl_ea_entry { | ||
| 45 | u_int16_t e_tag; | ||
| 46 | u_int16_t e_perm; | ||
| 47 | u_int32_t e_id; | ||
| 48 | |||
| 49 | // implements sorting compatible with libacl | ||
| 50 | 1581 | bool operator<(const acl_ea_entry &other) const { | |
| 51 |
2/2✓ Branch 0 taken 1479 times.
✓ Branch 1 taken 102 times.
|
1581 | if (e_tag != other.e_tag) { |
| 52 | 1479 | return e_tag < other.e_tag; | |
| 53 | } | ||
| 54 | 102 | return e_id < other.e_id; | |
| 55 | } | ||
| 56 | }; | ||
| 57 | |||
| 58 | struct acl_ea_header { | ||
| 59 | u_int32_t a_version; | ||
| 60 | acl_ea_entry a_entries[0]; | ||
| 61 | }; | ||
| 62 | |||
| 63 | 374 | static int acl_from_text_to_string_entries(const string &acl_string, | |
| 64 | vector<string> &string_entries) { | ||
| 65 | 374 | std::size_t entry_pos = 0; | |
| 66 |
2/2✓ Branch 0 taken 1479 times.
✓ Branch 1 taken 238 times.
|
1717 | while (entry_pos != string::npos) { |
| 67 | size_t entry_length; | ||
| 68 | size_t next_pos; | ||
| 69 | 1479 | size_t const sep_pos = acl_string.find_first_of(",\n", entry_pos); | |
| 70 |
2/2✓ Branch 0 taken 374 times.
✓ Branch 1 taken 1105 times.
|
1479 | if (sep_pos == string::npos) { |
| 71 |
2/2✓ Branch 1 taken 238 times.
✓ Branch 2 taken 136 times.
|
374 | if (acl_string.length() > entry_pos) { |
| 72 | 238 | entry_length = acl_string.length() - entry_pos; | |
| 73 | 238 | next_pos = string::npos; | |
| 74 | } else { | ||
| 75 | // we've just looked past a trailing delimiter | ||
| 76 | 136 | break; | |
| 77 | } | ||
| 78 | } else { | ||
| 79 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1105 times.
|
1105 | assert(sep_pos >= entry_pos); |
| 80 | 1105 | entry_length = sep_pos - entry_pos; | |
| 81 | 1105 | next_pos = sep_pos + 1; | |
| 82 | } | ||
| 83 |
2/2✓ Branch 0 taken 187 times.
✓ Branch 1 taken 1156 times.
|
1343 | if (entry_length == 0) { |
| 84 | // libacl tolerates excessive whitespace but not excessive delimiters. | ||
| 85 | // It's simpler for us to treat whitespace as delimiters. | ||
| 86 | 187 | entry_pos = next_pos; | |
| 87 | 187 | continue; | |
| 88 | } | ||
| 89 |
1/2✓ Branch 1 taken 1156 times.
✗ Branch 2 not taken.
|
1156 | string entry(acl_string, entry_pos, entry_length); |
| 90 | 1156 | entry_pos = next_pos; | |
| 91 | |||
| 92 | // search for '#'-starting comment, discard if found | ||
| 93 | 1156 | size_t const comment_pos = entry.find('#'); | |
| 94 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1156 times.
|
1156 | if (comment_pos != string::npos) { |
| 95 | ✗ | entry = string(entry, 0, comment_pos); | |
| 96 | } | ||
| 97 | |||
| 98 | // TODO(autkin): trim whitespace on both ends | ||
| 99 | |||
| 100 | // discard empty lines | ||
| 101 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1156 times.
|
1156 | if (entry.length() == 0) { |
| 102 | ✗ | continue; | |
| 103 | } | ||
| 104 | |||
| 105 |
1/2✓ Branch 1 taken 1156 times.
✗ Branch 2 not taken.
|
1156 | string_entries.push_back(entry); |
| 106 |
1/2✓ Branch 1 taken 1156 times.
✗ Branch 2 not taken.
|
1156 | } |
| 107 | 374 | return 0; | |
| 108 | } | ||
| 109 | |||
| 110 | 1054 | static int acl_parms_from_text(const string &str, u_int16_t *perms) { | |
| 111 | // Currently unsupported syntax features found in setfacl: | ||
| 112 | // - X (capital x) | ||
| 113 | // - numeric syntax | ||
| 114 | // See "man 1 setfacl", "The perms field is..." | ||
| 115 | |||
| 116 | 1054 | *perms = 0; | |
| 117 |
2/2✓ Branch 5 taken 1904 times.
✓ Branch 6 taken 1054 times.
|
2958 | for (const char &c : str) { |
| 118 |
4/5✓ Branch 0 taken 697 times.
✓ Branch 1 taken 425 times.
✓ Branch 2 taken 204 times.
✓ Branch 3 taken 578 times.
✗ Branch 4 not taken.
|
1904 | switch (c) { |
| 119 | 697 | case 'r': | |
| 120 | 697 | *perms |= ACL_READ; | |
| 121 | 697 | break; | |
| 122 | 425 | case 'w': | |
| 123 | 425 | *perms |= ACL_WRITE; | |
| 124 | 425 | break; | |
| 125 | 204 | case 'x': | |
| 126 | 204 | *perms |= ACL_EXECUTE; | |
| 127 | 204 | break; | |
| 128 | 578 | case '-': | |
| 129 | 578 | break; | |
| 130 | ✗ | default: | |
| 131 | ✗ | return EINVAL; | |
| 132 | } | ||
| 133 | } | ||
| 134 | 1054 | return 0; | |
| 135 | } | ||
| 136 | |||
| 137 | 1071 | static int acl_entry_from_text(const string &str, acl_ea_entry &entry) { | |
| 138 | // break down to 3 fields by ':' | ||
| 139 | // type:qualifier:permissions according to terminology | ||
| 140 | // e_tag:e_id:e_perm are acl_ea_entry field names | ||
| 141 | 1071 | size_t sep_pos = str.find(':'); | |
| 142 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1071 times.
|
1071 | if (sep_pos == string::npos) { |
| 143 | ✗ | return EINVAL; | |
| 144 | } | ||
| 145 |
1/2✓ Branch 1 taken 1071 times.
✗ Branch 2 not taken.
|
1071 | string const type(str, 0, sep_pos); |
| 146 | 1071 | size_t next_field_pos = sep_pos + 1; | |
| 147 | 1071 | sep_pos = str.find(':', next_field_pos); | |
| 148 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1071 times.
|
1071 | if (sep_pos == string::npos) { |
| 149 | ✗ | return EINVAL; | |
| 150 | } | ||
| 151 |
1/2✓ Branch 1 taken 1071 times.
✗ Branch 2 not taken.
|
1071 | string const qualifier(str, next_field_pos, sep_pos - next_field_pos); |
| 152 | 1071 | next_field_pos = sep_pos + 1; | |
| 153 |
1/2✓ Branch 2 taken 1071 times.
✗ Branch 3 not taken.
|
1071 | string const permissions(str, next_field_pos); |
| 154 | |||
| 155 |
6/6✓ Branch 1 taken 1054 times.
✓ Branch 2 taken 17 times.
✓ Branch 4 taken 408 times.
✓ Branch 5 taken 646 times.
✓ Branch 6 taken 425 times.
✓ Branch 7 taken 646 times.
|
1071 | if (!type.compare("user") || !type.compare("u")) { |
| 156 |
2/2✓ Branch 1 taken 255 times.
✓ Branch 2 taken 170 times.
|
425 | entry.e_tag = qualifier.empty() ? ACL_USER_OBJ : ACL_USER; |
| 157 |
6/6✓ Branch 1 taken 595 times.
✓ Branch 2 taken 51 times.
✓ Branch 4 taken 255 times.
✓ Branch 5 taken 340 times.
✓ Branch 6 taken 306 times.
✓ Branch 7 taken 340 times.
|
646 | } else if (!type.compare("group") || !type.compare("g")) { |
| 158 |
2/2✓ Branch 1 taken 238 times.
✓ Branch 2 taken 68 times.
|
306 | entry.e_tag = qualifier.empty() ? ACL_GROUP_OBJ : ACL_GROUP; |
| 159 |
6/6✓ Branch 1 taken 323 times.
✓ Branch 2 taken 17 times.
✓ Branch 4 taken 221 times.
✓ Branch 5 taken 102 times.
✓ Branch 6 taken 238 times.
✓ Branch 7 taken 102 times.
|
340 | } else if (!type.compare("other") || !type.compare("o")) { |
| 160 | 238 | entry.e_tag = ACL_OTHER; | |
| 161 |
6/6✓ Branch 1 taken 51 times.
✓ Branch 2 taken 51 times.
✓ Branch 4 taken 34 times.
✓ Branch 5 taken 17 times.
✓ Branch 6 taken 85 times.
✓ Branch 7 taken 17 times.
|
102 | } else if (!type.compare("mask") || !type.compare("m")) { |
| 162 | 85 | entry.e_tag = ACL_MASK; | |
| 163 | } else { | ||
| 164 | 17 | return EINVAL; | |
| 165 | } | ||
| 166 | 1054 | entry.e_tag = htole16(entry.e_tag); | |
| 167 | |||
| 168 |
2/2✓ Branch 1 taken 816 times.
✓ Branch 2 taken 238 times.
|
1054 | if (qualifier.empty()) { |
| 169 | 816 | entry.e_id = ACL_UNDEFINED_ID; | |
| 170 | } else { | ||
| 171 | char *at_null_terminator_if_number; | ||
| 172 | 238 | long number = strtol(qualifier.c_str(), &at_null_terminator_if_number, 10); | |
| 173 |
2/2✓ Branch 0 taken 119 times.
✓ Branch 1 taken 119 times.
|
238 | if (*at_null_terminator_if_number != '\0') { |
| 174 | bool ok; | ||
| 175 |
2/2✓ Branch 1 taken 85 times.
✓ Branch 2 taken 34 times.
|
119 | if (entry.e_tag == htole16(ACL_USER)) { |
| 176 | [[maybe_unused]] gid_t main_gid; | ||
| 177 | uid_t uid; | ||
| 178 |
1/2✓ Branch 1 taken 85 times.
✗ Branch 2 not taken.
|
85 | ok = GetUidOf(qualifier, &uid, &main_gid); |
| 179 | 85 | number = uid; | |
| 180 |
1/2✓ Branch 1 taken 34 times.
✗ Branch 2 not taken.
|
34 | } else if (entry.e_tag == htole16(ACL_GROUP)) { |
| 181 | gid_t gid; | ||
| 182 |
1/2✓ Branch 1 taken 34 times.
✗ Branch 2 not taken.
|
34 | ok = GetGidOf(qualifier, &gid); |
| 183 | 34 | number = gid; | |
| 184 | } else { | ||
| 185 | ✗ | assert(false); | |
| 186 | } | ||
| 187 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 119 times.
|
119 | if (!ok) { |
| 188 | ✗ | return EINVAL; | |
| 189 | } | ||
| 190 | } | ||
| 191 | 238 | entry.e_id = htole32(number); | |
| 192 | } | ||
| 193 | |||
| 194 | // parse perms | ||
| 195 | u_int16_t host_byteorder_perms; | ||
| 196 | int ret; | ||
| 197 | 1054 | ret = acl_parms_from_text(permissions, &host_byteorder_perms); | |
| 198 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1054 times.
|
1054 | if (ret) { |
| 199 | ✗ | return ret; | |
| 200 | } | ||
| 201 | 1054 | entry.e_perm = htole16(host_byteorder_perms); | |
| 202 | |||
| 203 | 1054 | return 0; | |
| 204 | 1071 | } | |
| 205 | |||
| 206 | 357 | static bool acl_valid_builtin(const vector<acl_ea_entry> &entries) { | |
| 207 | // From man acl_valid: | ||
| 208 | // The three required entries ACL_USER_OBJ, ACL_GROUP_OBJ, and ACL_OTHER must | ||
| 209 | // exist exactly once in the ACL. | ||
| 210 | // | ||
| 211 | // If the ACL contains any ACL_USER or ACL_GROUP entries, then an ACL_MASK | ||
| 212 | // entry is also required. | ||
| 213 | // | ||
| 214 | // The ACL may contain at most one ACL_MASK entry. | ||
| 215 | // | ||
| 216 | // The user identifiers must be unique among all entries of type ACL_USER. | ||
| 217 | // The group identifiers must be unique among all entries of type ACL_GROUP. | ||
| 218 | |||
| 219 | 357 | bool types_met[ACL_OTHER + 1] = { | |
| 220 | false, | ||
| 221 | }; | ||
| 222 | |||
| 223 |
2/2✓ Branch 4 taken 986 times.
✓ Branch 5 taken 340 times.
|
1326 | for (auto entry_it = entries.begin(); entry_it != entries.end(); ++entry_it) { |
| 224 | 986 | const acl_ea_entry &e = *entry_it; | |
| 225 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 986 times.
|
986 | assert(e.e_tag <= ACL_OTHER); |
| 226 | 986 | bool &type_met = types_met[e.e_tag]; | |
| 227 |
2/3✓ Branch 0 taken 782 times.
✓ Branch 1 taken 204 times.
✗ Branch 2 not taken.
|
986 | switch (e.e_tag) { |
| 228 | // at most one of these types | ||
| 229 | 782 | case ACL_USER_OBJ: | |
| 230 | case ACL_GROUP_OBJ: | ||
| 231 | case ACL_OTHER: | ||
| 232 | case ACL_MASK: | ||
| 233 |
2/2✓ Branch 0 taken 17 times.
✓ Branch 1 taken 765 times.
|
782 | if (type_met) { |
| 234 | 17 | return false; | |
| 235 | } else { | ||
| 236 | 765 | type_met = true; | |
| 237 | } | ||
| 238 | 765 | break; | |
| 239 | 204 | case ACL_USER: | |
| 240 | case ACL_GROUP: | ||
| 241 | 204 | type_met = true; | |
| 242 | 204 | break; | |
| 243 | ✗ | default: | |
| 244 | ✗ | assert(false); | |
| 245 | } | ||
| 246 | } | ||
| 247 |
4/4✓ Branch 0 taken 221 times.
✓ Branch 1 taken 119 times.
✓ Branch 2 taken 204 times.
✓ Branch 3 taken 17 times.
|
340 | if (!(types_met[ACL_USER_OBJ] && types_met[ACL_GROUP_OBJ] |
| 248 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 204 times.
|
204 | && types_met[ACL_OTHER])) { |
| 249 | 136 | return false; | |
| 250 | } | ||
| 251 |
6/6✓ Branch 0 taken 119 times.
✓ Branch 1 taken 85 times.
✓ Branch 2 taken 17 times.
✓ Branch 3 taken 102 times.
✓ Branch 4 taken 17 times.
✓ Branch 5 taken 85 times.
|
204 | if ((types_met[ACL_USER] || types_met[ACL_GROUP]) && !types_met[ACL_MASK]) { |
| 252 | 17 | return false; | |
| 253 | } | ||
| 254 | // TODO(autkin): ACL_USER, ACL_GROUP uniqueness checks. Not a pressing issue. | ||
| 255 | 187 | return true; | |
| 256 | } | ||
| 257 | |||
| 258 | 374 | int acl_from_text_to_xattr_value(const string &textual_acl, char *&o_binary_acl, | |
| 259 | size_t &o_size, bool &o_equiv_mode) { | ||
| 260 | int ret; | ||
| 261 | |||
| 262 | 374 | o_equiv_mode = true; | |
| 263 | |||
| 264 | // get individual textual entries from one big text | ||
| 265 | 374 | vector<string> string_entries; | |
| 266 |
1/2✓ Branch 1 taken 374 times.
✗ Branch 2 not taken.
|
374 | ret = acl_from_text_to_string_entries(textual_acl, string_entries); |
| 267 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 374 times.
|
374 | if (ret) { |
| 268 | ✗ | return ret; | |
| 269 | } | ||
| 270 | |||
| 271 | // get individual entries in structural form | ||
| 272 | 374 | vector<acl_ea_entry> entries; | |
| 273 | 374 | for (auto string_it = string_entries.begin(); | |
| 274 |
2/2✓ Branch 2 taken 1071 times.
✓ Branch 3 taken 357 times.
|
1428 | string_it != string_entries.end(); |
| 275 | 1054 | ++string_it) { | |
| 276 | acl_ea_entry entry; | ||
| 277 |
1/2✓ Branch 2 taken 1071 times.
✗ Branch 3 not taken.
|
1071 | ret = acl_entry_from_text(*string_it, entry); |
| 278 |
2/2✓ Branch 0 taken 17 times.
✓ Branch 1 taken 1054 times.
|
1071 | if (ret) { |
| 279 | 17 | return ret; | |
| 280 | } | ||
| 281 |
4/4✓ Branch 0 taken 986 times.
✓ Branch 1 taken 68 times.
✓ Branch 2 taken 170 times.
✓ Branch 3 taken 816 times.
|
1054 | if (entry.e_tag == ACL_GROUP || entry.e_tag == ACL_USER) { |
| 282 | 238 | o_equiv_mode = false; | |
| 283 | } | ||
| 284 |
1/2✓ Branch 1 taken 1054 times.
✗ Branch 2 not taken.
|
1054 | entries.push_back(entry); |
| 285 | } | ||
| 286 | |||
| 287 | // sort entries as libacl does, to be able to use it in testing as a reference | ||
| 288 |
1/2✓ Branch 3 taken 357 times.
✗ Branch 4 not taken.
|
357 | sort(entries.begin(), entries.end()); |
| 289 | |||
| 290 | // reject what acl_valid() rejects, to be able to use it in testing | ||
| 291 |
2/2✓ Branch 1 taken 170 times.
✓ Branch 2 taken 187 times.
|
357 | if (!acl_valid_builtin(entries)) { |
| 292 | 170 | return EINVAL; | |
| 293 | } | ||
| 294 | |||
| 295 | // if nothing but usual u,g,o bits, don't produce a binary. Mimicking libacl. | ||
| 296 |
2/2✓ Branch 0 taken 102 times.
✓ Branch 1 taken 85 times.
|
187 | if (o_equiv_mode) { |
| 297 | 102 | o_binary_acl = NULL; | |
| 298 | 102 | o_size = 0; | |
| 299 | 102 | return 0; | |
| 300 | } | ||
| 301 | |||
| 302 | // get one big buffer with all the entries in the "on-disk" xattr format | ||
| 303 | 85 | size_t const acl_entry_count = entries.size(); | |
| 304 | 85 | size_t const buf_size = sizeof(acl_ea_header) | |
| 305 | 85 | + (acl_entry_count * sizeof(acl_ea_entry)); | |
| 306 | 85 | char *buf = static_cast<char *>(malloc(buf_size)); | |
| 307 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 85 times.
|
85 | if (!buf) { |
| 308 | ✗ | return ENOMEM; | |
| 309 | } | ||
| 310 | 85 | acl_ea_header *header = reinterpret_cast<acl_ea_header *>(buf); | |
| 311 | 85 | header->a_version = htole32(ACL_EA_VERSION); | |
| 312 | 85 | acl_ea_entry *ext_entry = reinterpret_cast<acl_ea_entry *>(header + 1); | |
| 313 |
2/2✓ Branch 3 taken 493 times.
✓ Branch 4 taken 85 times.
|
578 | for (auto entry_it = entries.begin(); entry_it != entries.end(); ++entry_it) { |
| 314 | 493 | *ext_entry = *entry_it; | |
| 315 | 493 | ext_entry += 1; | |
| 316 | } | ||
| 317 | |||
| 318 | 85 | o_binary_acl = buf; | |
| 319 | 85 | o_size = buf_size; | |
| 320 | 85 | return 0; | |
| 321 | 374 | } | |
| 322 | |||
| 323 | #ifdef COMPARE_TO_LIBACL | ||
| 324 | int acl_from_text_to_xattr_value_libacl(const string textual_acl, | ||
| 325 | char *&o_binary_acl, size_t &o_size, | ||
| 326 | bool &o_equiv_mode) { | ||
| 327 | acl_t acl = acl_from_text( | ||
| 328 | textual_acl.c_str()); // Convert ACL string to acl_t object | ||
| 329 | if (!acl) { | ||
| 330 | return EINVAL; | ||
| 331 | } | ||
| 332 | if (acl_valid(acl) != 0) { | ||
| 333 | acl_free(acl); | ||
| 334 | return EINVAL; | ||
| 335 | } | ||
| 336 | |||
| 337 | // check if the ACL string contains more than the synthetic ACLs | ||
| 338 | int equiv = acl_equiv_mode(acl, NULL); | ||
| 339 | assert(equiv != -1); | ||
| 340 | |||
| 341 | o_equiv_mode = equiv == 0; | ||
| 342 | if (!o_equiv_mode) { | ||
| 343 | o_binary_acl = (char *)acl_to_xattr(acl, &o_size); | ||
| 344 | } else { | ||
| 345 | o_binary_acl = NULL; | ||
| 346 | o_size = 0; | ||
| 347 | } | ||
| 348 | acl_free(acl); | ||
| 349 | return 0; | ||
| 350 | } | ||
| 351 | |||
| 352 | int acl_from_text_to_xattr_value_both_impl(const string textual_acl, | ||
| 353 | char *&o_binary_acl, size_t &o_size, | ||
| 354 | bool &o_equiv_mode) { | ||
| 355 | struct impl_result { | ||
| 356 | int ret; | ||
| 357 | size_t binary_size; | ||
| 358 | char *binary_acl; | ||
| 359 | bool equiv_mode; | ||
| 360 | } b, l; | ||
| 361 | b.ret = acl_from_text_to_xattr_value(textual_acl, b.binary_acl, b.binary_size, | ||
| 362 | b.equiv_mode); | ||
| 363 | l.ret = acl_from_text_to_xattr_value_libacl(textual_acl, l.binary_acl, | ||
| 364 | l.binary_size, l.equiv_mode); | ||
| 365 | assert(b.ret == l.ret); | ||
| 366 | if (!l.ret) { | ||
| 367 | assert(b.binary_size == l.binary_size); | ||
| 368 | assert(0 == memcmp(b.binary_acl, l.binary_acl, b.binary_size)); | ||
| 369 | assert(b.equiv_mode == l.equiv_mode); | ||
| 370 | free(l.binary_acl); | ||
| 371 | } | ||
| 372 | o_binary_acl = b.binary_acl; | ||
| 373 | o_size = b.binary_size; | ||
| 374 | o_equiv_mode = b.equiv_mode; | ||
| 375 | return b.ret; | ||
| 376 | } | ||
| 377 | #endif // COMPARE_TO_LIBACL | ||
| 378 |