Colin Cross | ec0a2e8 | 2010-06-11 14:21:37 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2010 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | #include <sys/stat.h> |
| 18 | #include <string.h> |
| 19 | #include <stdio.h> |
Nick Kralevich | 3ebdcb8 | 2013-02-22 19:39:21 -0800 | [diff] [blame] | 20 | |
Nick Kralevich | 8a63232 | 2013-03-01 13:24:44 -0800 | [diff] [blame] | 21 | #ifdef HAVE_ANDROID_OS |
| 22 | #include <linux/capability.h> |
| 23 | #else |
| 24 | #include <private/android_filesystem_capability.h> |
| 25 | #endif |
| 26 | |
Nick Kralevich | 3ebdcb8 | 2013-02-22 19:39:21 -0800 | [diff] [blame] | 27 | #define XATTR_SELINUX_SUFFIX "selinux" |
| 28 | #define XATTR_CAPS_SUFFIX "capability" |
Colin Cross | ec0a2e8 | 2010-06-11 14:21:37 -0700 | [diff] [blame] | 29 | |
| 30 | #include "ext4_utils.h" |
Colin Cross | ec0a2e8 | 2010-06-11 14:21:37 -0700 | [diff] [blame] | 31 | #include "make_ext4fs.h" |
| 32 | #include "allocate.h" |
| 33 | #include "contents.h" |
| 34 | #include "extent.h" |
| 35 | #include "indirect.h" |
| 36 | |
Raphael Moll | 4605b3f | 2012-02-03 23:02:33 -0800 | [diff] [blame] | 37 | #ifdef USE_MINGW |
| 38 | #define S_IFLNK 0 /* used by make_link, not needed under mingw */ |
| 39 | #endif |
| 40 | |
Doug Zongker | bec598e | 2014-08-12 11:35:37 -0700 | [diff] [blame] | 41 | static struct block_allocation* saved_allocation_head = NULL; |
| 42 | |
| 43 | struct block_allocation* get_saved_allocation_chain() { |
| 44 | return saved_allocation_head; |
| 45 | } |
| 46 | |
Colin Cross | ec0a2e8 | 2010-06-11 14:21:37 -0700 | [diff] [blame] | 47 | static u32 dentry_size(u32 entries, struct dentry *dentries) |
| 48 | { |
| 49 | u32 len = 24; |
| 50 | unsigned int i; |
Colin Cross | 6bd2b5d | 2010-08-05 11:56:38 -0700 | [diff] [blame] | 51 | unsigned int dentry_len; |
Colin Cross | ec0a2e8 | 2010-06-11 14:21:37 -0700 | [diff] [blame] | 52 | |
| 53 | for (i = 0; i < entries; i++) { |
Paul Lawrence | 40ce87a | 2014-03-03 10:51:58 -0800 | [diff] [blame] | 54 | dentry_len = 8 + EXT4_ALIGN(strlen(dentries[i].filename), 4); |
Colin Cross | ec0a2e8 | 2010-06-11 14:21:37 -0700 | [diff] [blame] | 55 | if (len % info.block_size + dentry_len > info.block_size) |
| 56 | len += info.block_size - (len % info.block_size); |
| 57 | len += dentry_len; |
| 58 | } |
| 59 | |
| 60 | return len; |
| 61 | } |
| 62 | |
Colin Cross | ec0a2e8 | 2010-06-11 14:21:37 -0700 | [diff] [blame] | 63 | static struct ext4_dir_entry_2 *add_dentry(u8 *data, u32 *offset, |
| 64 | struct ext4_dir_entry_2 *prev, u32 inode, const char *name, |
| 65 | u8 file_type) |
| 66 | { |
| 67 | u8 name_len = strlen(name); |
Paul Lawrence | 40ce87a | 2014-03-03 10:51:58 -0800 | [diff] [blame] | 68 | u16 rec_len = 8 + EXT4_ALIGN(name_len, 4); |
Colin Cross | ec0a2e8 | 2010-06-11 14:21:37 -0700 | [diff] [blame] | 69 | struct ext4_dir_entry_2 *dentry; |
| 70 | |
| 71 | u32 start_block = *offset / info.block_size; |
Colin Cross | 2ff1c5b | 2011-01-25 21:44:37 -0800 | [diff] [blame] | 72 | u32 end_block = (*offset + rec_len - 1) / info.block_size; |
Colin Cross | ec0a2e8 | 2010-06-11 14:21:37 -0700 | [diff] [blame] | 73 | if (start_block != end_block) { |
| 74 | /* Adding this dentry will cross a block boundary, so pad the previous |
| 75 | dentry to the block boundary */ |
| 76 | if (!prev) |
| 77 | critical_error("no prev"); |
| 78 | prev->rec_len += end_block * info.block_size - *offset; |
| 79 | *offset = end_block * info.block_size; |
| 80 | } |
| 81 | |
| 82 | dentry = (struct ext4_dir_entry_2 *)(data + *offset); |
| 83 | dentry->inode = inode; |
| 84 | dentry->rec_len = rec_len; |
| 85 | dentry->name_len = name_len; |
| 86 | dentry->file_type = file_type; |
| 87 | memcpy(dentry->name, name, name_len); |
| 88 | |
| 89 | *offset += rec_len; |
| 90 | return dentry; |
| 91 | } |
| 92 | |
| 93 | /* Creates a directory structure for an array of directory entries, dentries, |
| 94 | and stores the location of the structure in an inode. The new inode's |
| 95 | .. link is set to dir_inode_num. Stores the location of the inode number |
| 96 | of each directory entry into dentries[i].inode, to be filled in later |
| 97 | when the inode for the entry is allocated. Returns the inode number of the |
| 98 | new directory */ |
| 99 | u32 make_directory(u32 dir_inode_num, u32 entries, struct dentry *dentries, |
Colin Cross | 8aef66d | 2010-06-20 23:22:12 -0700 | [diff] [blame] | 100 | u32 dirs) |
Colin Cross | ec0a2e8 | 2010-06-11 14:21:37 -0700 | [diff] [blame] | 101 | { |
| 102 | struct ext4_inode *inode; |
Colin Cross | 6bd2b5d | 2010-08-05 11:56:38 -0700 | [diff] [blame] | 103 | u32 blocks; |
| 104 | u32 len; |
Colin Cross | ec0a2e8 | 2010-06-11 14:21:37 -0700 | [diff] [blame] | 105 | u32 offset = 0; |
| 106 | u32 inode_num; |
| 107 | u8 *data; |
| 108 | unsigned int i; |
| 109 | struct ext4_dir_entry_2 *dentry; |
| 110 | |
Colin Cross | 6bd2b5d | 2010-08-05 11:56:38 -0700 | [diff] [blame] | 111 | blocks = DIV_ROUND_UP(dentry_size(entries, dentries), info.block_size); |
| 112 | len = blocks * info.block_size; |
| 113 | |
Colin Cross | ec0a2e8 | 2010-06-11 14:21:37 -0700 | [diff] [blame] | 114 | if (dir_inode_num) { |
| 115 | inode_num = allocate_inode(info); |
| 116 | } else { |
| 117 | dir_inode_num = EXT4_ROOT_INO; |
| 118 | inode_num = EXT4_ROOT_INO; |
| 119 | } |
| 120 | |
| 121 | if (inode_num == EXT4_ALLOCATE_FAILED) { |
| 122 | error("failed to allocate inode\n"); |
| 123 | return EXT4_ALLOCATE_FAILED; |
| 124 | } |
| 125 | |
| 126 | add_directory(inode_num); |
| 127 | |
| 128 | inode = get_inode(inode_num); |
| 129 | if (inode == NULL) { |
| 130 | error("failed to get inode %u", inode_num); |
| 131 | return EXT4_ALLOCATE_FAILED; |
| 132 | } |
| 133 | |
| 134 | data = inode_allocate_data_extents(inode, len, len); |
| 135 | if (data == NULL) { |
Colin Cross | 2e905e5 | 2010-12-29 14:20:53 -0800 | [diff] [blame] | 136 | error("failed to allocate %u extents", len); |
Colin Cross | ec0a2e8 | 2010-06-11 14:21:37 -0700 | [diff] [blame] | 137 | return EXT4_ALLOCATE_FAILED; |
| 138 | } |
| 139 | |
| 140 | inode->i_mode = S_IFDIR; |
| 141 | inode->i_links_count = dirs + 2; |
| 142 | inode->i_flags |= aux_info.default_i_flags; |
| 143 | |
| 144 | dentry = NULL; |
| 145 | |
| 146 | dentry = add_dentry(data, &offset, NULL, inode_num, ".", EXT4_FT_DIR); |
| 147 | if (!dentry) { |
| 148 | error("failed to add . directory"); |
| 149 | return EXT4_ALLOCATE_FAILED; |
| 150 | } |
| 151 | |
| 152 | dentry = add_dentry(data, &offset, dentry, dir_inode_num, "..", EXT4_FT_DIR); |
| 153 | if (!dentry) { |
| 154 | error("failed to add .. directory"); |
| 155 | return EXT4_ALLOCATE_FAILED; |
| 156 | } |
| 157 | |
| 158 | for (i = 0; i < entries; i++) { |
Colin Cross | 6bd2b5d | 2010-08-05 11:56:38 -0700 | [diff] [blame] | 159 | dentry = add_dentry(data, &offset, dentry, 0, |
| 160 | dentries[i].filename, dentries[i].file_type); |
| 161 | if (offset > len || (offset == len && i != entries - 1)) |
| 162 | critical_error("internal error: dentry for %s ends at %d, past %d\n", |
| 163 | dentries[i].filename, offset, len); |
Colin Cross | ec0a2e8 | 2010-06-11 14:21:37 -0700 | [diff] [blame] | 164 | dentries[i].inode = &dentry->inode; |
| 165 | if (!dentry) { |
| 166 | error("failed to add directory"); |
| 167 | return EXT4_ALLOCATE_FAILED; |
| 168 | } |
| 169 | } |
| 170 | |
Colin Cross | 8748938 | 2012-11-20 14:07:39 -0800 | [diff] [blame] | 171 | /* pad the last dentry out to the end of the block */ |
| 172 | dentry->rec_len += len - offset; |
Colin Cross | ec0a2e8 | 2010-06-11 14:21:37 -0700 | [diff] [blame] | 173 | |
| 174 | return inode_num; |
| 175 | } |
| 176 | |
| 177 | /* Creates a file on disk. Returns the inode number of the new file */ |
| 178 | u32 make_file(const char *filename, u64 len) |
| 179 | { |
| 180 | struct ext4_inode *inode; |
| 181 | u32 inode_num; |
| 182 | |
| 183 | inode_num = allocate_inode(info); |
| 184 | if (inode_num == EXT4_ALLOCATE_FAILED) { |
| 185 | error("failed to allocate inode\n"); |
| 186 | return EXT4_ALLOCATE_FAILED; |
| 187 | } |
| 188 | |
| 189 | inode = get_inode(inode_num); |
| 190 | if (inode == NULL) { |
| 191 | error("failed to get inode %u", inode_num); |
| 192 | return EXT4_ALLOCATE_FAILED; |
| 193 | } |
| 194 | |
Doug Zongker | bec598e | 2014-08-12 11:35:37 -0700 | [diff] [blame] | 195 | if (len > 0) { |
| 196 | struct block_allocation* alloc = inode_allocate_file_extents(inode, len, filename); |
Doug Zongker | 9922135 | 2014-08-12 16:55:56 -0700 | [diff] [blame] | 197 | if (alloc) { |
| 198 | alloc->filename = strdup(filename); |
| 199 | alloc->next = saved_allocation_head; |
| 200 | saved_allocation_head = alloc; |
| 201 | } |
Doug Zongker | bec598e | 2014-08-12 11:35:37 -0700 | [diff] [blame] | 202 | } |
Colin Cross | ec0a2e8 | 2010-06-11 14:21:37 -0700 | [diff] [blame] | 203 | |
| 204 | inode->i_mode = S_IFREG; |
| 205 | inode->i_links_count = 1; |
| 206 | inode->i_flags |= aux_info.default_i_flags; |
| 207 | |
| 208 | return inode_num; |
| 209 | } |
| 210 | |
| 211 | /* Creates a file on disk. Returns the inode number of the new file */ |
Nick Kralevich | 5446bde | 2013-02-19 19:05:47 -0800 | [diff] [blame] | 212 | u32 make_link(const char *link) |
Colin Cross | ec0a2e8 | 2010-06-11 14:21:37 -0700 | [diff] [blame] | 213 | { |
| 214 | struct ext4_inode *inode; |
| 215 | u32 inode_num; |
| 216 | u32 len = strlen(link); |
| 217 | |
| 218 | inode_num = allocate_inode(info); |
| 219 | if (inode_num == EXT4_ALLOCATE_FAILED) { |
| 220 | error("failed to allocate inode\n"); |
| 221 | return EXT4_ALLOCATE_FAILED; |
| 222 | } |
| 223 | |
| 224 | inode = get_inode(inode_num); |
| 225 | if (inode == NULL) { |
| 226 | error("failed to get inode %u", inode_num); |
| 227 | return EXT4_ALLOCATE_FAILED; |
| 228 | } |
| 229 | |
| 230 | inode->i_mode = S_IFLNK; |
| 231 | inode->i_links_count = 1; |
| 232 | inode->i_flags |= aux_info.default_i_flags; |
| 233 | inode->i_size_lo = len; |
| 234 | |
| 235 | if (len + 1 <= sizeof(inode->i_block)) { |
| 236 | /* Fast symlink */ |
| 237 | memcpy((char*)inode->i_block, link, len); |
| 238 | } else { |
| 239 | u8 *data = inode_allocate_data_indirect(inode, info.block_size, info.block_size); |
| 240 | memcpy(data, link, len); |
| 241 | inode->i_blocks_lo = info.block_size / 512; |
| 242 | } |
| 243 | |
| 244 | return inode_num; |
| 245 | } |
| 246 | |
Colin Cross | de61f98 | 2010-08-04 15:06:09 -0700 | [diff] [blame] | 247 | int inode_set_permissions(u32 inode_num, u16 mode, u16 uid, u16 gid, u32 mtime) |
Colin Cross | ec0a2e8 | 2010-06-11 14:21:37 -0700 | [diff] [blame] | 248 | { |
| 249 | struct ext4_inode *inode = get_inode(inode_num); |
| 250 | |
| 251 | if (!inode) |
| 252 | return -1; |
| 253 | |
| 254 | inode->i_mode |= mode; |
| 255 | inode->i_uid = uid; |
| 256 | inode->i_gid = gid; |
Colin Cross | de61f98 | 2010-08-04 15:06:09 -0700 | [diff] [blame] | 257 | inode->i_mtime = mtime; |
| 258 | inode->i_atime = mtime; |
| 259 | inode->i_ctime = mtime; |
Colin Cross | ec0a2e8 | 2010-06-11 14:21:37 -0700 | [diff] [blame] | 260 | |
| 261 | return 0; |
| 262 | } |
Stephen Smalley | b4eca4b | 2012-01-13 09:00:56 -0500 | [diff] [blame] | 263 | |
Nick Kralevich | 4df62f3 | 2013-02-07 14:21:34 -0800 | [diff] [blame] | 264 | /* |
| 265 | * Returns the amount of free space available in the specified |
| 266 | * xattr region |
| 267 | */ |
| 268 | static size_t xattr_free_space(struct ext4_xattr_entry *entry, char *end) |
Stephen Smalley | b4eca4b | 2012-01-13 09:00:56 -0500 | [diff] [blame] | 269 | { |
Nick Kralevich | 4df62f3 | 2013-02-07 14:21:34 -0800 | [diff] [blame] | 270 | while(!IS_LAST_ENTRY(entry) && (((char *) entry) < end)) { |
| 271 | end -= EXT4_XATTR_SIZE(le32_to_cpu(entry->e_value_size)); |
| 272 | entry = EXT4_XATTR_NEXT(entry); |
| 273 | } |
Stephen Smalley | b4eca4b | 2012-01-13 09:00:56 -0500 | [diff] [blame] | 274 | |
Nick Kralevich | 4df62f3 | 2013-02-07 14:21:34 -0800 | [diff] [blame] | 275 | if (((char *) entry) > end) { |
| 276 | error("unexpected read beyond end of xattr space"); |
Stephen Smalley | b4eca4b | 2012-01-13 09:00:56 -0500 | [diff] [blame] | 277 | return 0; |
Nick Kralevich | 4df62f3 | 2013-02-07 14:21:34 -0800 | [diff] [blame] | 278 | } |
Stephen Smalley | b4eca4b | 2012-01-13 09:00:56 -0500 | [diff] [blame] | 279 | |
Nick Kralevich | 4df62f3 | 2013-02-07 14:21:34 -0800 | [diff] [blame] | 280 | return end - ((char *) entry); |
| 281 | } |
| 282 | |
| 283 | /* |
| 284 | * Returns a pointer to the free space immediately after the |
| 285 | * last xattr element |
| 286 | */ |
| 287 | static struct ext4_xattr_entry* xattr_get_last(struct ext4_xattr_entry *entry) |
| 288 | { |
| 289 | for (; !IS_LAST_ENTRY(entry); entry = EXT4_XATTR_NEXT(entry)) { |
| 290 | // skip entry |
| 291 | } |
| 292 | return entry; |
| 293 | } |
| 294 | |
| 295 | /* |
| 296 | * assert that the elements in the ext4 xattr section are in sorted order |
| 297 | * |
| 298 | * The ext4 filesystem requires extended attributes to be sorted when |
| 299 | * they're not stored in the inode. The kernel ext4 code uses the following |
| 300 | * sorting algorithm: |
| 301 | * |
| 302 | * 1) First sort extended attributes by their name_index. For example, |
| 303 | * EXT4_XATTR_INDEX_USER (1) comes before EXT4_XATTR_INDEX_SECURITY (6). |
| 304 | * 2) If the name_indexes are equal, then sorting is based on the length |
| 305 | * of the name. For example, XATTR_SELINUX_SUFFIX ("selinux") comes before |
| 306 | * XATTR_CAPS_SUFFIX ("capability") because "selinux" is shorter than "capability" |
| 307 | * 3) If the name_index and name_length are equal, then memcmp() is used to determine |
| 308 | * which name comes first. For example, "selinux" would come before "yelinux". |
| 309 | * |
| 310 | * This method is intended to implement the sorting function defined in |
| 311 | * the Linux kernel file fs/ext4/xattr.c function ext4_xattr_find_entry(). |
| 312 | */ |
| 313 | static void xattr_assert_sane(struct ext4_xattr_entry *entry) |
| 314 | { |
| 315 | for( ; !IS_LAST_ENTRY(entry); entry = EXT4_XATTR_NEXT(entry)) { |
| 316 | struct ext4_xattr_entry *next = EXT4_XATTR_NEXT(entry); |
| 317 | if (IS_LAST_ENTRY(next)) { |
| 318 | return; |
| 319 | } |
| 320 | |
| 321 | int cmp = next->e_name_index - entry->e_name_index; |
| 322 | if (cmp == 0) |
| 323 | cmp = next->e_name_len - entry->e_name_len; |
| 324 | if (cmp == 0) |
| 325 | cmp = memcmp(next->e_name, entry->e_name, next->e_name_len); |
| 326 | if (cmp < 0) { |
| 327 | error("BUG: extended attributes are not sorted\n"); |
| 328 | return; |
| 329 | } |
| 330 | if (cmp == 0) { |
| 331 | error("BUG: duplicate extended attributes detected\n"); |
| 332 | return; |
| 333 | } |
| 334 | } |
| 335 | } |
| 336 | |
| 337 | #define NAME_HASH_SHIFT 5 |
| 338 | #define VALUE_HASH_SHIFT 16 |
| 339 | |
| 340 | static void ext4_xattr_hash_entry(struct ext4_xattr_header *header, |
| 341 | struct ext4_xattr_entry *entry) |
| 342 | { |
Colin Cross | 7900c77 | 2014-01-23 12:53:30 -0800 | [diff] [blame] | 343 | u32 hash = 0; |
Nick Kralevich | 4df62f3 | 2013-02-07 14:21:34 -0800 | [diff] [blame] | 344 | char *name = entry->e_name; |
| 345 | int n; |
| 346 | |
| 347 | for (n = 0; n < entry->e_name_len; n++) { |
| 348 | hash = (hash << NAME_HASH_SHIFT) ^ |
| 349 | (hash >> (8*sizeof(hash) - NAME_HASH_SHIFT)) ^ |
| 350 | *name++; |
| 351 | } |
| 352 | |
| 353 | if (entry->e_value_block == 0 && entry->e_value_size != 0) { |
Colin Cross | 7900c77 | 2014-01-23 12:53:30 -0800 | [diff] [blame] | 354 | u32 *value = (u32 *)((char *)header + |
Nick Kralevich | 4df62f3 | 2013-02-07 14:21:34 -0800 | [diff] [blame] | 355 | le16_to_cpu(entry->e_value_offs)); |
| 356 | for (n = (le32_to_cpu(entry->e_value_size) + |
| 357 | EXT4_XATTR_ROUND) >> EXT4_XATTR_PAD_BITS; n; n--) { |
| 358 | hash = (hash << VALUE_HASH_SHIFT) ^ |
| 359 | (hash >> (8*sizeof(hash) - VALUE_HASH_SHIFT)) ^ |
| 360 | le32_to_cpu(*value++); |
| 361 | } |
| 362 | } |
| 363 | entry->e_hash = cpu_to_le32(hash); |
| 364 | } |
| 365 | |
| 366 | #undef NAME_HASH_SHIFT |
| 367 | #undef VALUE_HASH_SHIFT |
| 368 | |
| 369 | static struct ext4_xattr_entry* xattr_addto_range( |
| 370 | void *block_start, |
| 371 | void *block_end, |
| 372 | struct ext4_xattr_entry *first, |
| 373 | int name_index, |
| 374 | const char *name, |
| 375 | const void *value, |
| 376 | size_t value_len) |
| 377 | { |
| 378 | size_t name_len = strlen(name); |
| 379 | if (name_len > 255) |
| 380 | return NULL; |
| 381 | |
| 382 | size_t available_size = xattr_free_space(first, block_end); |
| 383 | size_t needed_size = EXT4_XATTR_LEN(name_len) + EXT4_XATTR_SIZE(value_len); |
| 384 | |
| 385 | if (needed_size > available_size) |
| 386 | return NULL; |
| 387 | |
| 388 | struct ext4_xattr_entry *new_entry = xattr_get_last(first); |
| 389 | memset(new_entry, 0, EXT4_XATTR_LEN(name_len)); |
| 390 | |
| 391 | new_entry->e_name_len = name_len; |
| 392 | new_entry->e_name_index = name_index; |
| 393 | memcpy(new_entry->e_name, name, name_len); |
| 394 | new_entry->e_value_block = 0; |
| 395 | new_entry->e_value_size = cpu_to_le32(value_len); |
| 396 | |
| 397 | char *val = (char *) new_entry + available_size - EXT4_XATTR_SIZE(value_len); |
| 398 | size_t e_value_offs = val - (char *) block_start; |
| 399 | |
| 400 | new_entry->e_value_offs = cpu_to_le16(e_value_offs); |
| 401 | memset(val, 0, EXT4_XATTR_SIZE(value_len)); |
| 402 | memcpy(val, value, value_len); |
| 403 | |
| 404 | xattr_assert_sane(first); |
| 405 | return new_entry; |
| 406 | } |
| 407 | |
| 408 | static int xattr_addto_inode(struct ext4_inode *inode, int name_index, |
| 409 | const char *name, const void *value, size_t value_len) |
| 410 | { |
| 411 | struct ext4_xattr_ibody_header *hdr = (struct ext4_xattr_ibody_header *) (inode + 1); |
| 412 | struct ext4_xattr_entry *first = (struct ext4_xattr_entry *) (hdr + 1); |
| 413 | char *block_end = ((char *) inode) + info.inode_size; |
| 414 | |
| 415 | struct ext4_xattr_entry *result = |
| 416 | xattr_addto_range(first, block_end, first, name_index, name, value, value_len); |
| 417 | |
| 418 | if (result == NULL) |
Stephen Smalley | b4eca4b | 2012-01-13 09:00:56 -0500 | [diff] [blame] | 419 | return -1; |
| 420 | |
Nick Kralevich | 4df62f3 | 2013-02-07 14:21:34 -0800 | [diff] [blame] | 421 | hdr->h_magic = cpu_to_le32(EXT4_XATTR_MAGIC); |
Stephen Smalley | b4eca4b | 2012-01-13 09:00:56 -0500 | [diff] [blame] | 422 | inode->i_extra_isize = cpu_to_le16(sizeof(struct ext4_inode) - EXT4_GOOD_OLD_INODE_SIZE); |
| 423 | |
| 424 | return 0; |
| 425 | } |
Nick Kralevich | 4df62f3 | 2013-02-07 14:21:34 -0800 | [diff] [blame] | 426 | |
| 427 | static int xattr_addto_block(struct ext4_inode *inode, int name_index, |
| 428 | const char *name, const void *value, size_t value_len) |
| 429 | { |
| 430 | struct ext4_xattr_header *header = get_xattr_block_for_inode(inode); |
| 431 | if (!header) |
| 432 | return -1; |
| 433 | |
| 434 | struct ext4_xattr_entry *first = (struct ext4_xattr_entry *) (header + 1); |
| 435 | char *block_end = ((char *) header) + info.block_size; |
| 436 | |
| 437 | struct ext4_xattr_entry *result = |
| 438 | xattr_addto_range(header, block_end, first, name_index, name, value, value_len); |
| 439 | |
| 440 | if (result == NULL) |
| 441 | return -1; |
| 442 | |
| 443 | ext4_xattr_hash_entry(header, result); |
| 444 | return 0; |
| 445 | } |
| 446 | |
| 447 | |
| 448 | static int xattr_add(u32 inode_num, int name_index, const char *name, |
| 449 | const void *value, size_t value_len) |
| 450 | { |
| 451 | if (!value) |
| 452 | return 0; |
| 453 | |
| 454 | struct ext4_inode *inode = get_inode(inode_num); |
| 455 | |
| 456 | if (!inode) |
| 457 | return -1; |
| 458 | |
| 459 | int result = xattr_addto_inode(inode, name_index, name, value, value_len); |
| 460 | if (result != 0) { |
| 461 | result = xattr_addto_block(inode, name_index, name, value, value_len); |
| 462 | } |
| 463 | return result; |
| 464 | } |
| 465 | |
| 466 | int inode_set_selinux(u32 inode_num, const char *secon) |
| 467 | { |
Sungmin Choi | 192238d | 2013-02-25 15:37:51 +0900 | [diff] [blame] | 468 | if (!secon) |
| 469 | return 0; |
| 470 | |
Nick Kralevich | 4df62f3 | 2013-02-07 14:21:34 -0800 | [diff] [blame] | 471 | return xattr_add(inode_num, EXT4_XATTR_INDEX_SECURITY, |
| 472 | XATTR_SELINUX_SUFFIX, secon, strlen(secon) + 1); |
| 473 | } |
| 474 | |
| 475 | int inode_set_capabilities(u32 inode_num, uint64_t capabilities) { |
| 476 | if (capabilities == 0) |
| 477 | return 0; |
| 478 | |
| 479 | struct vfs_cap_data cap_data; |
| 480 | memset(&cap_data, 0, sizeof(cap_data)); |
| 481 | |
| 482 | cap_data.magic_etc = VFS_CAP_REVISION | VFS_CAP_FLAGS_EFFECTIVE; |
| 483 | cap_data.data[0].permitted = (uint32_t) (capabilities & 0xffffffff); |
| 484 | cap_data.data[0].inheritable = 0; |
| 485 | cap_data.data[1].permitted = (uint32_t) (capabilities >> 32); |
| 486 | cap_data.data[1].inheritable = 0; |
| 487 | |
| 488 | return xattr_add(inode_num, EXT4_XATTR_INDEX_SECURITY, |
| 489 | XATTR_CAPS_SUFFIX, &cap_data, sizeof(cap_data)); |
| 490 | } |