1 /** 2 * sload.c 3 * 4 * Copyright (C) 2015 Huawei Ltd. 5 * Witten by: 6 * Hou Pengyang <houpengyang@huawei.com> 7 * Liu Shuoran <liushuoran@huawei.com> 8 * Jaegeuk Kim <jaegeuk@kernel.org> 9 * 10 * This program is free software; you can redistribute it and/or modify 11 * it under the terms of the GNU General Public License version 2 as 12 * published by the Free Software Foundation. 13 */ 14 #define _GNU_SOURCE 15 #include "fsck.h" 16 #include <libgen.h> 17 #include <dirent.h> 18 #ifdef HAVE_MNTENT_H 19 #include <mntent.h> 20 #endif 21 22 #ifdef HAVE_LIBSELINUX 23 static struct selabel_handle *sehnd = NULL; 24 #endif 25 26 typedef void (*fs_config_f)(const char *path, int dir, 27 const char *target_out_path, 28 unsigned *uid, unsigned *gid, 29 unsigned *mode, uint64_t *capabilities); 30 31 static fs_config_f fs_config_func = NULL; 32 33 #ifdef WITH_ANDROID 34 #include <selinux/android.h> 35 #include <private/android_filesystem_config.h> 36 #include <private/canned_fs_config.h> 37 #include <private/fs_config.h> 38 #endif 39 40 static int filter_dot(const struct dirent *d) 41 { 42 return (strcmp(d->d_name, "..") && strcmp(d->d_name, ".")); 43 } 44 45 static int f2fs_make_directory(struct f2fs_sb_info *sbi, 46 int entries, struct dentry *de) 47 { 48 int ret = 0; 49 int i = 0; 50 51 for (i = 0; i < entries; i++) { 52 if (de[i].file_type == F2FS_FT_DIR) 53 ret = f2fs_mkdir(sbi, de + i); 54 else if (de[i].file_type == F2FS_FT_REG_FILE) 55 ret = f2fs_create(sbi, de + i); 56 else if (de[i].file_type == F2FS_FT_SYMLINK) 57 ret = f2fs_symlink(sbi, de + i); 58 59 if (ret) 60 break; 61 } 62 63 return ret; 64 } 65 66 #ifdef HAVE_LIBSELINUX 67 static int set_selinux_xattr(struct f2fs_sb_info *sbi, const char *path, 68 nid_t ino, int mode) 69 { 70 char *secontext = NULL; 71 char *mnt_path = NULL; 72 73 if (!sehnd) 74 return 0; 75 76 if (asprintf(&mnt_path, "%s%s", c.mount_point, path) <= 0) { 77 ERR_MSG("cannot allocate security path for %s%s\n", 78 c.mount_point, path); 79 return -ENOMEM; 80 } 81 82 /* set root inode selinux context */ 83 if (selabel_lookup(sehnd, &secontext, mnt_path, mode) < 0) { 84 ERR_MSG("cannot lookup security context for %s\n", mnt_path); 85 free(mnt_path); 86 return -EINVAL; 87 } 88 89 if (secontext) { 90 MSG(2, "%s (%d) -> SELinux context = %s\n", 91 mnt_path, ino, secontext); 92 inode_set_selinux(sbi, ino, secontext); 93 } 94 freecon(secontext); 95 free(mnt_path); 96 return 0; 97 } 98 #else 99 #define set_selinux_xattr(...) 0 100 #endif 101 102 static int set_perms_and_caps(struct dentry *de) 103 { 104 uint64_t capabilities = 0; 105 unsigned int uid = 0, gid = 0, imode = 0; 106 char *mnt_path = NULL; 107 108 if (asprintf(&mnt_path, "%s%s", c.mount_point, de->path) <= 0) { 109 ERR_MSG("cannot allocate mount path for %s%s\n", 110 c.mount_point, de->path); 111 return -ENOMEM; 112 } 113 114 /* Permissions */ 115 if (fs_config_func != NULL) { 116 fs_config_func(mnt_path, de->file_type == F2FS_FT_DIR, 117 c.target_out_dir, &uid, &gid, &imode, 118 &capabilities); 119 de->uid = uid & 0xffff; 120 de->gid = gid & 0xffff; 121 de->mode = (de->mode & S_IFMT) | (imode & 0xffff); 122 de->capabilities = capabilities; 123 } 124 MSG(2, "%s -> mode = 0x%x, uid = 0x%x, gid = 0x%x, " 125 "capabilities = 0x%"PRIx64"\n", 126 mnt_path, de->mode, de->uid, de->gid, de->capabilities); 127 free(mnt_path); 128 return 0; 129 } 130 131 static void set_inode_metadata(struct dentry *de) 132 { 133 struct stat stat; 134 int ret; 135 136 ret = lstat(de->full_path, &stat); 137 if (ret < 0) { 138 ERR_MSG("lstat failure\n"); 139 ASSERT(0); 140 } 141 142 if (S_ISREG(stat.st_mode)) { 143 de->file_type = F2FS_FT_REG_FILE; 144 } else if (S_ISDIR(stat.st_mode)) { 145 de->file_type = F2FS_FT_DIR; 146 } else if (S_ISCHR(stat.st_mode)) { 147 de->file_type = F2FS_FT_CHRDEV; 148 } else if (S_ISBLK(stat.st_mode)) { 149 de->file_type = F2FS_FT_BLKDEV; 150 } else if (S_ISFIFO(stat.st_mode)) { 151 de->file_type = F2FS_FT_FIFO; 152 } else if (S_ISSOCK(stat.st_mode)) { 153 de->file_type = F2FS_FT_SOCK; 154 } else if (S_ISLNK(stat.st_mode)) { 155 de->file_type = F2FS_FT_SYMLINK; 156 de->link = calloc(F2FS_BLKSIZE, 1); 157 ASSERT(de->link); 158 ret = readlink(de->full_path, de->link, F2FS_BLKSIZE - 1); 159 ASSERT(ret >= 0); 160 } else { 161 ERR_MSG("unknown file type on %s", de->path); 162 ASSERT(0); 163 } 164 165 de->size = stat.st_size; 166 de->mode = stat.st_mode & 167 (S_IFMT|S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO); 168 if (c.fixed_time == -1 && c.from_dir) 169 de->mtime = stat.st_mtime; 170 else 171 de->mtime = c.fixed_time; 172 173 set_perms_and_caps(de); 174 } 175 176 static int build_directory(struct f2fs_sb_info *sbi, const char *full_path, 177 const char *dir_path, const char *target_out_dir, 178 nid_t dir_ino) 179 { 180 int entries = 0; 181 struct dentry *dentries; 182 struct dirent **namelist = NULL; 183 int i = 0, ret = 0; 184 185 entries = scandir(full_path, &namelist, filter_dot, (void *)alphasort); 186 if (entries < 0) { 187 ERR_MSG("No entries in %s\n", full_path); 188 return -ENOENT; 189 } 190 191 dentries = calloc(entries, sizeof(struct dentry)); 192 ASSERT(dentries); 193 194 for (i = 0; i < entries; i++) { 195 dentries[i].name = (unsigned char *)strdup(namelist[i]->d_name); 196 if (dentries[i].name == NULL) { 197 ERR_MSG("Skip: ENOMEM\n"); 198 continue; 199 } 200 dentries[i].len = strlen((char *)dentries[i].name); 201 202 ret = asprintf(&dentries[i].path, "%s%s", 203 dir_path, namelist[i]->d_name); 204 ASSERT(ret > 0); 205 ret = asprintf(&dentries[i].full_path, "%s/%s", 206 full_path, namelist[i]->d_name); 207 ASSERT(ret > 0); 208 free(namelist[i]); 209 210 set_inode_metadata(dentries + i); 211 212 dentries[i].pino = dir_ino; 213 } 214 215 free(namelist); 216 217 ret = f2fs_make_directory(sbi, entries, dentries); 218 if (ret) 219 goto out_free; 220 221 for (i = 0; i < entries; i++) { 222 if (dentries[i].file_type == F2FS_FT_REG_FILE) { 223 f2fs_build_file(sbi, dentries + i); 224 } else if (dentries[i].file_type == F2FS_FT_DIR) { 225 char *subdir_full_path = NULL; 226 char *subdir_dir_path = NULL; 227 228 ret = asprintf(&subdir_full_path, "%s", 229 dentries[i].full_path); 230 ASSERT(ret > 0); 231 ret = asprintf(&subdir_dir_path, "%s/", 232 dentries[i].path); 233 ASSERT(ret > 0); 234 235 ret = build_directory(sbi, subdir_full_path, 236 subdir_dir_path, 237 target_out_dir, 238 dentries[i].ino); 239 free(subdir_full_path); 240 free(subdir_dir_path); 241 242 if (ret) 243 goto out_free; 244 } else if (dentries[i].file_type == F2FS_FT_SYMLINK) { 245 /* 246 * It is already done in f2fs_make_directory 247 * f2fs_make_symlink(sbi, dir_ino, &dentries[i]); 248 */ 249 } else { 250 MSG(1, "Error unknown file type\n"); 251 } 252 253 ret = set_selinux_xattr(sbi, dentries[i].path, 254 dentries[i].ino, dentries[i].mode); 255 if (ret) 256 goto out_free; 257 258 free(dentries[i].path); 259 free(dentries[i].full_path); 260 free((void *)dentries[i].name); 261 } 262 out_free: 263 for (; i < entries; i++) { 264 free(dentries[i].path); 265 free(dentries[i].full_path); 266 free((void *)dentries[i].name); 267 } 268 269 free(dentries); 270 return 0; 271 } 272 273 static int configure_files(void) 274 { 275 #ifdef HAVE_LIBSELINUX 276 if (!c.nr_opt) 277 goto skip; 278 #if !defined(__ANDROID__) 279 sehnd = selabel_open(SELABEL_CTX_FILE, c.seopt_file, c.nr_opt); 280 if (!sehnd) { 281 ERR_MSG("Failed to open file contexts \"%s\"", 282 c.seopt_file[0].value); 283 return -EINVAL; 284 } 285 #else 286 sehnd = selinux_android_file_context_handle(); 287 if (!sehnd) { 288 ERR_MSG("Failed to get android file_contexts\n", c.mount_point); 289 return -EINVAL; 290 } 291 #endif 292 skip: 293 #endif 294 #ifdef WITH_ANDROID 295 /* Load the FS config */ 296 if (c.fs_config_file) { 297 int ret = load_canned_fs_config(c.fs_config_file); 298 299 if (ret < 0) { 300 ERR_MSG("Failed to load fs_config \"%s\"", 301 c.fs_config_file); 302 return ret; 303 } 304 fs_config_func = canned_fs_config; 305 } else { 306 fs_config_func = fs_config; 307 } 308 #endif 309 return 0; 310 } 311 312 int f2fs_sload(struct f2fs_sb_info *sbi) 313 { 314 int ret = 0; 315 316 ret = configure_files(); 317 if (ret) { 318 ERR_MSG("Failed to configure files\n"); 319 return ret; 320 } 321 322 /* flush NAT/SIT journal entries */ 323 flush_journal_entries(sbi); 324 325 ret = build_directory(sbi, c.from_dir, "/", 326 c.target_out_dir, F2FS_ROOT_INO(sbi)); 327 if (ret) { 328 ERR_MSG("Failed to build due to %d\n", ret); 329 return ret; 330 } 331 332 ret = set_selinux_xattr(sbi, c.mount_point, 333 F2FS_ROOT_INO(sbi), S_IFDIR); 334 if (ret) { 335 ERR_MSG("Failed to set selinux for root: %d\n", ret); 336 return ret; 337 } 338 339 /* update curseg info; can update sit->types */ 340 move_curseg_info(sbi, SM_I(sbi)->main_blkaddr, 0); 341 zero_journal_entries(sbi); 342 write_curseg_info(sbi); 343 344 /* flush dirty sit entries */ 345 flush_sit_entries(sbi); 346 347 write_checkpoint(sbi); 348 return 0; 349 } 350