int make_dir(const char *path, mode_t mode) { int rc; #ifdef HAVE_SELINUX char *secontext = NULL; if (sehandle) { selabel_lookup(sehandle, &secontext, path, mode); setfscreatecon(secontext); } #endif rc = mkdir(path, mode); #ifdef HAVE_SELINUX if (secontext) { int save_errno = errno; freecon(secontext); setfscreatecon(NULL); errno = save_errno; } #endif return rc; }
static int check_mac_perms(const char *name, char *sctx) { if (is_selinux_enabled() <= 0) return 1; char *tctx = NULL; const char *class = "property_service"; const char *perm = "set"; int result = 0; if (!sctx) goto err; if (!sehandle_prop) goto err; if (selabel_lookup(sehandle_prop, &tctx, name, 1) != 0) goto err; if (selinux_check_access(sctx, tctx, class, perm, (void*) name) == 0) result = 1; freecon(tctx); err: return result; }
/* * create_socket - creates a Unix domain socket in ANDROID_SOCKET_DIR * ("/dev/socket") as dictated in init.rc. This socket is inherited by the * daemon. We communicate the file descriptor's value via the environment * variable ANDROID_SOCKET_ENV_PREFIX<name> ("ANDROID_SOCKET_foo"). */ int create_socket(const char *name, int type, mode_t perm, uid_t uid, gid_t gid, const char *socketcon) { struct sockaddr_un addr; int fd, ret; char *filecon; if (socketcon) setsockcreatecon(socketcon); fd = socket(PF_UNIX, type, 0); if (fd < 0) { ERROR("Failed to open socket '%s': %s\n", name, strerror(errno)); return -1; } if (socketcon) setsockcreatecon(NULL); memset(&addr, 0 , sizeof(addr)); addr.sun_family = AF_UNIX; snprintf(addr.sun_path, sizeof(addr.sun_path), ANDROID_SOCKET_DIR"/%s", name); ret = unlink(addr.sun_path); if (ret != 0 && errno != ENOENT) { ERROR("Failed to unlink old socket '%s': %s\n", name, strerror(errno)); goto out_close; } filecon = NULL; if (sehandle) { ret = selabel_lookup(sehandle, &filecon, addr.sun_path, S_IFSOCK); if (ret == 0) setfscreatecon(filecon); } ret = bind(fd, (struct sockaddr *) &addr, sizeof (addr)); if (ret) { ERROR("Failed to bind socket '%s': %s\n", name, strerror(errno)); goto out_unlink; } setfscreatecon(NULL); freecon(filecon); chown(addr.sun_path, uid, gid); chmod(addr.sun_path, perm); INFO("Created socket '%s' with mode '%o', user '%d', group '%d'\n", addr.sun_path, perm, uid, gid); return fd; out_unlink: unlink(addr.sun_path); out_close: close(fd); return -1; }
static int check_mac_perms(const char *name, char *sctx, struct ucred *cr) { if (is_selinux_enabled() <= 0) return 1; char *tctx = NULL; int result = 0; property_audit_data audit_data; if (!sctx) goto err; if (!sehandle_prop) goto err; if (selabel_lookup(sehandle_prop, &tctx, name, 1) != 0) goto err; audit_data.name = name; audit_data.cr = cr; if (selinux_check_access(sctx, tctx, "property_service", "set", reinterpret_cast<void*>(&audit_data)) == 0) result = 1; freecon(tctx); err: return result; }
static int restore(const char *pathname, const struct stat *sb) { char *oldcontext, *newcontext; if(lgetfilecon(pathname, &oldcontext) < 0) { fprintf(stderr, "Could not get context of %s: %s\n", pathname, strerror(errno)); return -1; } if(selabel_lookup(sehandle, &newcontext, pathname, sb->st_mode) < 0) { fprintf(stderr, "Could not lookup context for %s: %s\n", pathname, strerror(errno)); return -1; } if(strcmp(newcontext, "<<none>>") && strcmp(oldcontext, newcontext)) { if(verbose) printf("Relabeling %s from %s to %s.\n", pathname, oldcontext, newcontext); if(!nochange) { if(lsetfilecon(pathname, newcontext) < 0) { fprintf(stderr, "Could not label %s with %s: %s\n", pathname, newcontext, strerror(errno)); return -1; } } } freecon(oldcontext); freecon(newcontext); return 0; }
/* Set fcon to the appropriate label for path and mode, or return -1. */ static int getContext(const char *newpath, mode_t mode, security_context_t *fcon) { #if HAVE_SELINUX_LABEL_H struct selabel_handle *handle = selabel_open(SELABEL_CTX_FILE, NULL, 0); int ret; if (handle == NULL) return -1; ret = selabel_lookup(handle, fcon, newpath, mode); selabel_close(handle); return ret; #else return matchpathcon(newpath, mode, fcon); #endif }
int restorecon(const char *pathname) { char *secontext = NULL; struct stat sb; int i; if (is_selinux_enabled() <= 0 || !sehandle) return 0; if (lstat(pathname, &sb) < 0) return -errno; if (selabel_lookup(sehandle, &secontext, pathname, sb.st_mode) < 0) return -errno; if (lsetfilecon(pathname, secontext) < 0) { freecon(secontext); return -errno; } freecon(secontext); return 0; }
static bool check_mac_perms_from_lookup(pid_t spid, uid_t uid, const char *perm, const char *name) { bool allowed; char *tctx = NULL; if (selinux_enabled <= 0) { return true; } if (!sehandle) { ALOGE("SELinux: Failed to find sehandle. Aborting service_manager.\n"); abort(); } if (selabel_lookup(sehandle, &tctx, name, 0) != 0) { ALOGE("SELinux: No match for %s in service_contexts.\n", name); return false; } allowed = check_mac_perms(spid, uid, tctx, perm, name); freecon(tctx); return allowed; }
int do_restorecon(int nargs, char **args) { #ifdef HAVE_SELINUX char *secontext = NULL; struct stat sb; int i; if (is_selinux_enabled() <= 0 || !sehandle) return 0; for (i = 1; i < nargs; i++) { if (lstat(args[i], &sb) < 0) return -errno; if (selabel_lookup(sehandle, &secontext, args[i], sb.st_mode) < 0) return -errno; if (lsetfilecon(args[i], secontext) < 0) { freecon(secontext); return -errno; } freecon(secontext); } #endif return 0; }
/* * create_socket - creates a Unix domain socket in ANDROID_SOCKET_DIR * ("/dev/socket") as dictated in init.rc. This socket is inherited by the * daemon. We communicate the file descriptor's value via the environment * variable ANDROID_SOCKET_ENV_PREFIX<name> ("ANDROID_SOCKET_foo"). */ int create_socket(const char *name, int type, mode_t perm, uid_t uid, gid_t gid, const char *socketcon) { struct sockaddr_un addr; int fd, ret, savederrno; char *filecon; if (socketcon) { if (setsockcreatecon(socketcon) == -1) { PLOG(ERROR) << "setsockcreatecon(\"" << socketcon << "\") failed"; return -1; } } fd = socket(PF_UNIX, type, 0); if (fd < 0) { PLOG(ERROR) << "Failed to open socket '" << name << "'"; return -1; } if (socketcon) setsockcreatecon(NULL); memset(&addr, 0 , sizeof(addr)); addr.sun_family = AF_UNIX; snprintf(addr.sun_path, sizeof(addr.sun_path), ANDROID_SOCKET_DIR"/%s", name); ret = unlink(addr.sun_path); if (ret != 0 && errno != ENOENT) { PLOG(ERROR) << "Failed to unlink old socket '" << name << "'"; goto out_close; } filecon = NULL; if (sehandle) { ret = selabel_lookup(sehandle, &filecon, addr.sun_path, S_IFSOCK); if (ret == 0) setfscreatecon(filecon); } ret = bind(fd, (struct sockaddr *) &addr, sizeof (addr)); savederrno = errno; setfscreatecon(NULL); freecon(filecon); if (ret) { errno = savederrno; PLOG(ERROR) << "Failed to bind socket '" << name << "'"; goto out_unlink; } ret = lchown(addr.sun_path, uid, gid); if (ret) { PLOG(ERROR) << "Failed to lchown socket '" << addr.sun_path << "'"; goto out_unlink; } ret = fchmodat(AT_FDCWD, addr.sun_path, perm, AT_SYMLINK_NOFOLLOW); if (ret) { PLOG(ERROR) << "Failed to fchmodat socket '" << addr.sun_path << "'"; goto out_unlink; } LOG(INFO) << "Created socket '" << addr.sun_path << "'" << ", mode " << std::oct << perm << std::dec << ", user " << uid << ", group " << gid; return fd; out_unlink: unlink(addr.sun_path); out_close: close(fd); return -1; }
int main(int argc, char *argv[]) { int fixstats = 0; struct stat stats; int opt; char *image; char *dir; char *secontext = NULL; while ((opt = getopt(argc, argv, "fc:s:")) != -1) { switch (opt) { case 'f': fixstats = 1; break; case 'c': chunkSize = (unsigned)strtoul(optarg, NULL, 0); break; case 's': spareSize = (unsigned)strtoul(optarg, NULL, 0); break; default: usage(); exit(1); } } if (!chunkSize || !spareSize) { usage(); exit(1); } if ((argc - optind < 2) || (argc - optind > 4)) { usage(); exit(1); } dir = argv[optind]; seprefixlen = strlen(dir); image = argv[optind + 1]; if (optind + 2 < argc) { if (!strncmp(argv[optind + 2], "convert", strlen("convert"))) convert_endian = 1; else { struct selinux_opt seopts[] = { { SELABEL_OPT_PATH, argv[optind + 2] } }; sehnd = selabel_open(SELABEL_CTX_FILE, seopts, 1); if (!sehnd) { perror(argv[optind + 2]); usage(); exit(1); } if (optind + 3 >= argc) { usage(); exit(1); } mntpoint = argv[optind + 3]; if (optind + 4 < argc) { if (!strncmp(argv[optind + 4], "convert", strlen("convert"))) convert_endian = 1; } } } if(stat(dir,&stats) < 0) { fprintf(stderr,"Could not stat %s\n",dir); exit(1); } if(!S_ISDIR(stats.st_mode)) { fprintf(stderr," %s is not a directory\n",dir); exit(1); } outFile = open(image,O_CREAT | O_TRUNC | O_WRONLY, S_IREAD | S_IWRITE); if(outFile < 0) { fprintf(stderr,"Could not open output file %s\n",image); exit(1); } if (fixstats) { int len = strlen(dir); if((len >= 4) && (!strcmp(dir + len - 4, "data"))) { source_path_len = len - 4; } else if((len >= 6) && (!strcmp(dir + len - 6, "system"))) { source_path_len = len - 6; } else { fprintf(stderr,"Fixstats (-f) option requested but filesystem is not data or android!\n"); exit(1); } fix_stat(dir, &stats); } //printf("Processing directory %s into image file %s\n",dir,image); if (sehnd) { char *sepath = NULL; if (mntpoint[0] == '/') sepath = strdup(mntpoint); else if (asprintf(&sepath, "/%s", mntpoint) < 0) sepath = NULL; if (!sepath) { perror("malloc"); exit(1); } if (selabel_lookup(sehnd, &secontext, sepath, stats.st_mode) < 0) { perror("selabel_lookup"); free(sepath); exit(1); } free(sepath); } error = write_object_header(1, YAFFS_OBJECT_TYPE_DIRECTORY, &stats, 1,"", -1, NULL, secontext); if(error) error = process_directory(YAFFS_OBJECTID_ROOT,dir,fixstats); close(outFile); if(error < 0) { perror("operation incomplete"); exit(1); } else { /* printf("Operation complete.\n" "%d objects in %d directories\n" "%d NAND pages\n",nObjects, nDirectories, nPages); */ } close(outFile); exit(0); }
static int process_directory(int parent, const char *path, int fixstats) { DIR *dir; struct dirent *entry; char *secontext = NULL; nDirectories++; dir = opendir(path); if(dir) { while((entry = readdir(dir)) != NULL) { /* Ignore . and .. */ if(strcmp(entry->d_name,".") && strcmp(entry->d_name,"..")) { char full_name[500]; char *suffix, dest_name[500]; int ret; struct stat stats; int equivalentObj; int newObj; sprintf(full_name,"%s/%s",path,entry->d_name); lstat(full_name,&stats); if (sehnd) { suffix = full_name + seprefixlen; ret = snprintf(dest_name, sizeof dest_name, "%s%s", mntpoint, suffix); if (ret < 0 || (size_t) ret >= sizeof dest_name) { fprintf(stderr, "snprintf failed on %s%s\n", mntpoint, suffix); exit(1); } char *sepath = NULL; if (dest_name[0] == '/') sepath = strdup(dest_name); else if (asprintf(&sepath, "/%s", dest_name) < 0) sepath = NULL; if (!sepath) { perror("malloc"); exit(1); } if (selabel_lookup(sehnd, &secontext, sepath, stats.st_mode) < 0) { perror("selabel_lookup"); free(sepath); exit(1); } free(sepath); } if(S_ISLNK(stats.st_mode) || S_ISREG(stats.st_mode) || S_ISDIR(stats.st_mode) || S_ISFIFO(stats.st_mode) || S_ISBLK(stats.st_mode) || S_ISCHR(stats.st_mode) || S_ISSOCK(stats.st_mode)) { newObj = obj_id++; nObjects++; if (fixstats) { fix_stat(full_name, &stats); } //printf("Object %d, %s is a ",newObj,full_name); /* We're going to create an object for it */ if((equivalentObj = find_obj_in_list(stats.st_dev, stats.st_ino)) > 0) { /* we need to make a hard link */ //printf("hard link to object %d\n",equivalentObj); error = write_object_header(newObj, YAFFS_OBJECT_TYPE_HARDLINK, &stats, parent, entry->d_name, equivalentObj, NULL, secontext); } else { add_obj_to_list(stats.st_dev,stats.st_ino,newObj); if(S_ISLNK(stats.st_mode)) { char symname[500]; memset(symname,0, sizeof(symname)); readlink(full_name,symname,sizeof(symname) -1); //printf("symlink to \"%s\"\n",symname); error = write_object_header(newObj, YAFFS_OBJECT_TYPE_SYMLINK, &stats, parent, entry->d_name, -1, symname, secontext); } else if(S_ISREG(stats.st_mode)) { //printf("file, "); error = write_object_header(newObj, YAFFS_OBJECT_TYPE_FILE, &stats, parent, entry->d_name, -1, NULL, secontext); if(error >= 0) { int h; __u8 bytes[chunkSize]; int nBytes; int chunk = 0; h = open(full_name,O_RDONLY); if(h >= 0) { memset(bytes,0xff,sizeof(bytes)); while((nBytes = read(h,bytes,sizeof(bytes))) > 0) { chunk++; write_chunk(bytes,newObj,chunk,nBytes); memset(bytes,0xff,sizeof(bytes)); } if(nBytes < 0) error = nBytes; //printf("%d data chunks written\n",chunk); } else { perror("Error opening file"); } close(h); } } else if(S_ISSOCK(stats.st_mode)) { //printf("socket\n"); error = write_object_header(newObj, YAFFS_OBJECT_TYPE_SPECIAL, &stats, parent, entry->d_name, -1, NULL, secontext); } else if(S_ISFIFO(stats.st_mode)) { //printf("fifo\n"); error = write_object_header(newObj, YAFFS_OBJECT_TYPE_SPECIAL, &stats, parent, entry->d_name, -1, NULL, secontext); } else if(S_ISCHR(stats.st_mode)) { //printf("character device\n"); error = write_object_header(newObj, YAFFS_OBJECT_TYPE_SPECIAL, &stats, parent, entry->d_name, -1, NULL, secontext); } else if(S_ISBLK(stats.st_mode)) { //printf("block device\n"); error = write_object_header(newObj, YAFFS_OBJECT_TYPE_SPECIAL, &stats, parent, entry->d_name, -1, NULL, secontext); } else if(S_ISDIR(stats.st_mode)) { //printf("directory\n"); error = write_object_header(newObj, YAFFS_OBJECT_TYPE_DIRECTORY, &stats, parent, entry->d_name, -1, NULL, secontext); // NCB modified 10/9/2001 process_directory(1,full_name); process_directory(newObj,full_name,fixstats); } } } else { //printf(" we don't handle this type\n"); } } } closedir(dir); } return 0; }
int make_ext4fs_internal(int fd, const char *directory, char *mountpoint, fs_config_func_t fs_config_func, int gzip, int sparse, int crc, int wipe, int init_itabs, struct selabel_handle *sehnd) { u32 root_inode_num; u16 root_mode; if (setjmp(setjmp_env)) return EXIT_FAILURE; /* Handle a call to longjmp() */ if (info.len <= 0) info.len = get_file_size(fd); if (info.len <= 0) { fprintf(stderr, "Need size of filesystem\n"); return EXIT_FAILURE; } if (info.block_size <= 0) info.block_size = compute_block_size(); /* Round down the filesystem length to be a multiple of the block size */ info.len &= ~((u64)info.block_size - 1); if (info.journal_blocks == 0) info.journal_blocks = compute_journal_blocks(); if (info.no_journal == 0) info.feat_compat = EXT4_FEATURE_COMPAT_HAS_JOURNAL; else info.journal_blocks = 0; if (info.blocks_per_group <= 0) info.blocks_per_group = compute_blocks_per_group(); if (info.inodes <= 0) info.inodes = compute_inodes(); if (info.inode_size <= 0) info.inode_size = 256; if (info.label == NULL) info.label = ""; info.inodes_per_group = compute_inodes_per_group(); info.feat_compat |= EXT4_FEATURE_COMPAT_RESIZE_INODE; info.feat_ro_compat |= EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER | EXT4_FEATURE_RO_COMPAT_LARGE_FILE; info.feat_incompat |= EXT4_FEATURE_INCOMPAT_EXTENTS | EXT4_FEATURE_INCOMPAT_FILETYPE; info.bg_desc_reserve_blocks = compute_bg_desc_reserve_blocks(); printf("Creating filesystem with parameters:\n"); printf(" Size: %llu\n", info.len); printf(" Block size: %d\n", info.block_size); printf(" Blocks per group: %d\n", info.blocks_per_group); printf(" Inodes per group: %d\n", info.inodes_per_group); printf(" Inode size: %d\n", info.inode_size); printf(" Journal blocks: %d\n", info.journal_blocks); printf(" Label: %s\n", info.label); ext4_create_fs_aux_info(); printf(" Blocks: %llu\n", aux_info.len_blocks); printf(" Block groups: %d\n", aux_info.groups); printf(" Reserved block group size: %d\n", info.bg_desc_reserve_blocks); info.sparse_file = sparse_file_new(info.block_size, info.len); block_allocator_init(); ext4_fill_in_sb(); MTK_add_mountpoint(aux_info.sb,mountpoint); if (reserve_inodes(0, 10) == EXT4_ALLOCATE_FAILED) error("failed to reserve first 10 inodes"); if (info.feat_compat & EXT4_FEATURE_COMPAT_HAS_JOURNAL) ext4_create_journal_inode(); if (info.feat_compat & EXT4_FEATURE_COMPAT_RESIZE_INODE) ext4_create_resize_inode(); #ifdef USE_MINGW // Windows needs only 'create an empty fs image' functionality assert(!directory); root_inode_num = build_default_directory_structure(); #else if (directory) root_inode_num = build_directory_structure(directory, mountpoint, 0, fs_config_func, sehnd); else root_inode_num = build_default_directory_structure(); #endif root_mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; inode_set_permissions(root_inode_num, root_mode, 0, 0, 0); #ifdef HAVE_SELINUX if (sehnd) { char *sepath = NULL; char *secontext = NULL; if (mountpoint[0] == '/') sepath = strdup(mountpoint); else asprintf(&sepath, "/%s", mountpoint); if (!sepath) critical_error_errno("malloc"); if (selabel_lookup(sehnd, &secontext, sepath, S_IFDIR) < 0) { error("cannot lookup security context for %s", sepath); } if (secontext) { printf("Labeling %s as %s\n", sepath, secontext); inode_set_selinux(root_inode_num, secontext); } free(sepath); freecon(secontext); } #endif ext4_update_free(); if (init_itabs) init_unused_inode_tables(); ext4_queue_sb(); printf("Created filesystem with %d/%d inodes and %d/%d blocks\n", aux_info.sb->s_inodes_count - aux_info.sb->s_free_inodes_count, aux_info.sb->s_inodes_count, aux_info.sb->s_blocks_count_lo - aux_info.sb->s_free_blocks_count_lo, aux_info.sb->s_blocks_count_lo); if (wipe) wipe_block_device(fd, info.len); write_ext4_image(fd, gzip, sparse, crc); sparse_file_destroy(info.sparse_file); info.sparse_file = NULL; return 0; }
int main(int argc, char** argv) { char buffer[1024]; const char* context_file = NULL; struct selabel_handle* sehnd = NULL; int print_capabilities = 0; int opt; while((opt = getopt(argc, argv, "CS:")) != -1) { switch(opt) { case 'C': print_capabilities = 1; break; case 'S': context_file = optarg; break; default: usage(); exit(EXIT_FAILURE); } } if (context_file != NULL) { sehnd = get_sehnd(context_file); } while (fgets(buffer, 1023, stdin) != NULL) { int is_dir = 0; int i; for (i = 0; i < 1024 && buffer[i]; ++i) { switch (buffer[i]) { case '\n': buffer[i-is_dir] = '\0'; i = 1025; break; case '/': is_dir = 1; break; default: is_dir = 0; break; } } unsigned uid = 0, gid = 0, mode = 0; uint64_t capabilities; fs_config(buffer, is_dir, &uid, &gid, &mode, &capabilities); printf("%s %d %d %o", buffer, uid, gid, mode); if (sehnd != NULL) { size_t buffer_strlen = strnlen(buffer, sizeof(buffer)); if (buffer_strlen >= sizeof(buffer)) { fprintf(stderr, "non null terminated buffer, aborting\n"); exit(EXIT_FAILURE); } size_t full_name_size = buffer_strlen + 2; char* full_name = (char*) malloc(full_name_size); if (full_name == NULL) { perror("malloc"); exit(EXIT_FAILURE); } full_name[0] = '/'; strncpy(full_name + 1, buffer, full_name_size - 1); full_name[full_name_size - 1] = '\0'; char* secontext; if (selabel_lookup(sehnd, &secontext, full_name, ( mode | (is_dir ? S_IFDIR : S_IFREG)))) { secontext = strdup("u:object_r:unlabeled:s0"); } printf(" selabel=%s", secontext); free(full_name); freecon(secontext); } if (print_capabilities) { printf(" capabilities=0x%" PRIx64, capabilities); } printf("\n"); } return 0; }
static u32 build_default_directory_structure() { u32 inode; u32 root_inode; struct dentry dentries = { .filename = "lost+found", .file_type = EXT4_FT_DIR, .mode = S_IRWXU, .uid = 0, .gid = 0, .mtime = 0, }; root_inode = make_directory(0, 1, &dentries, 1); inode = make_directory(root_inode, 0, NULL, 0); *dentries.inode = inode; inode_set_permissions(inode, dentries.mode, dentries.uid, dentries.gid, dentries.mtime); return root_inode; } #ifndef USE_MINGW /* Read a local directory and create the same tree in the generated filesystem. Calls itself recursively with each directory in the given directory. full_path is an absolute or relative path, with a trailing slash, to the directory on disk that should be copied, or NULL if this is a directory that does not exist on disk (e.g. lost+found). dir_path is an absolute path, with trailing slash, to the same directory if the image were mounted at the specified mount point */ static u32 build_directory_structure(const char *full_path, const char *dir_path, u32 dir_inode, fs_config_func_t fs_config_func, struct selabel_handle *sehnd, int verbose) { int entries = 0; struct dentry *dentries; struct dirent **namelist = NULL; struct stat stat; int ret; int i; u32 inode; u32 entry_inode; u32 dirs = 0; bool needs_lost_and_found = false; if (full_path) { entries = scandir(full_path, &namelist, filter_dot, (void*)alphasort); if (entries < 0) { error_errno("scandir"); return EXT4_ALLOCATE_FAILED; } } if (dir_inode == 0) { /* root directory, check if lost+found already exists */ for (i = 0; i < entries; i++) if (strcmp(namelist[i]->d_name, "lost+found") == 0) break; if (i == entries) needs_lost_and_found = true; } dentries = calloc(entries, sizeof(struct dentry)); if (dentries == NULL) critical_error_errno("malloc"); for (i = 0; i < entries; i++) { dentries[i].filename = strdup(namelist[i]->d_name); if (dentries[i].filename == NULL) critical_error_errno("strdup"); asprintf(&dentries[i].path, "%s%s", dir_path, namelist[i]->d_name); asprintf(&dentries[i].full_path, "%s%s", full_path, namelist[i]->d_name); free(namelist[i]); ret = lstat(dentries[i].full_path, &stat); if (ret < 0) { error_errno("lstat"); i--; entries--; continue; } dentries[i].size = stat.st_size; dentries[i].mode = stat.st_mode & (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO); dentries[i].mtime = stat.st_mtime; uint64_t capabilities; if (fs_config_func != NULL) { #ifdef ANDROID unsigned int mode = 0; unsigned int uid = 0; unsigned int gid = 0; int dir = S_ISDIR(stat.st_mode); fs_config_func(dentries[i].path, dir, &uid, &gid, &mode, &capabilities); dentries[i].mode = mode; dentries[i].uid = uid; dentries[i].gid = gid; dentries[i].capabilities = capabilities; #else error("can't set android permissions - built without android support"); #endif } #ifndef USE_MINGW if (sehnd) { if (selabel_lookup(sehnd, &dentries[i].secon, dentries[i].path, stat.st_mode) < 0) { error("cannot lookup security context for %s", dentries[i].path); } if (dentries[i].secon && verbose) printf("Labeling %s as %s\n", dentries[i].path, dentries[i].secon); } #endif if (S_ISREG(stat.st_mode)) { dentries[i].file_type = EXT4_FT_REG_FILE; } else if (S_ISDIR(stat.st_mode)) { dentries[i].file_type = EXT4_FT_DIR; dirs++; } else if (S_ISCHR(stat.st_mode)) { dentries[i].file_type = EXT4_FT_CHRDEV; } else if (S_ISBLK(stat.st_mode)) { dentries[i].file_type = EXT4_FT_BLKDEV; } else if (S_ISFIFO(stat.st_mode)) { dentries[i].file_type = EXT4_FT_FIFO; } else if (S_ISSOCK(stat.st_mode)) { dentries[i].file_type = EXT4_FT_SOCK; } else if (S_ISLNK(stat.st_mode)) { dentries[i].file_type = EXT4_FT_SYMLINK; dentries[i].link = calloc(info.block_size, 1); readlink(dentries[i].full_path, dentries[i].link, info.block_size - 1); } else { error("unknown file type on %s", dentries[i].path); i--; entries--; } } free(namelist); if (needs_lost_and_found) { /* insert a lost+found directory at the beginning of the dentries */ struct dentry *tmp = calloc(entries + 1, sizeof(struct dentry)); memset(tmp, 0, sizeof(struct dentry)); memcpy(tmp + 1, dentries, entries * sizeof(struct dentry)); dentries = tmp; dentries[0].filename = strdup("lost+found"); asprintf(&dentries[0].path, "%slost+found", dir_path); dentries[0].full_path = NULL; dentries[0].size = 0; dentries[0].mode = S_IRWXU; dentries[0].file_type = EXT4_FT_DIR; dentries[0].uid = 0; dentries[0].gid = 0; if (sehnd) { if (selabel_lookup(sehnd, &dentries[0].secon, dentries[0].path, dentries[0].mode) < 0) error("cannot lookup security context for %s", dentries[0].path); } entries++; dirs++; } inode = make_directory(dir_inode, entries, dentries, dirs); for (i = 0; i < entries; i++) { if (dentries[i].file_type == EXT4_FT_REG_FILE) { entry_inode = make_file(dentries[i].full_path, dentries[i].size); } else if (dentries[i].file_type == EXT4_FT_DIR) { char *subdir_full_path = NULL; char *subdir_dir_path; if (dentries[i].full_path) { ret = asprintf(&subdir_full_path, "%s/", dentries[i].full_path); if (ret < 0) critical_error_errno("asprintf"); } ret = asprintf(&subdir_dir_path, "%s/", dentries[i].path); if (ret < 0) critical_error_errno("asprintf"); entry_inode = build_directory_structure(subdir_full_path, subdir_dir_path, inode, fs_config_func, sehnd, verbose); free(subdir_full_path); free(subdir_dir_path); } else if (dentries[i].file_type == EXT4_FT_SYMLINK) { entry_inode = make_link(dentries[i].link); } else { error("unknown file type on %s", dentries[i].path); entry_inode = 0; } *dentries[i].inode = entry_inode; ret = inode_set_permissions(entry_inode, dentries[i].mode, dentries[i].uid, dentries[i].gid, dentries[i].mtime); if (ret) error("failed to set permissions on %s\n", dentries[i].path); /* * It's important to call inode_set_selinux() before * inode_set_capabilities(). Extended attributes need to * be stored sorted order, and we guarantee this by making * the calls in the proper order. * Please see xattr_assert_sane() in contents.c */ ret = inode_set_selinux(entry_inode, dentries[i].secon); if (ret) error("failed to set SELinux context on %s\n", dentries[i].path); ret = inode_set_capabilities(entry_inode, dentries[i].capabilities); if (ret) error("failed to set capability on %s\n", dentries[i].path); free(dentries[i].path); free(dentries[i].full_path); free(dentries[i].link); free((void *)dentries[i].filename); free(dentries[i].secon); } free(dentries); return inode; }
int dirCreateHierarchy(const char *path, int mode, const struct utimbuf *timestamp, bool stripFileName, struct selabel_handle *sehnd) { DirStatus ds; /* Check for an empty string before we bother * making any syscalls. */ if (path[0] == '\0') { errno = ENOENT; return -1; } /* Allocate a path that we can modify; stick a slash on * the end to make things easier. */ size_t pathLen = strlen(path); char *cpath = (char *)malloc(pathLen + 2); if (cpath == NULL) { errno = ENOMEM; return -1; } memcpy(cpath, path, pathLen); if (stripFileName) { /* Strip everything after the last slash. */ char *c = cpath + pathLen - 1; while (c != cpath && *c != '/') { c--; } if (c == cpath) { //xxx test this path /* No directory component. Act like the path was empty. */ errno = ENOENT; free(cpath); return -1; } c[1] = '\0'; // Terminate after the slash we found. } else { /* Make sure that the path ends in a slash. */ cpath[pathLen] = '/'; cpath[pathLen + 1] = '\0'; } /* See if it already exists. */ ds = getPathDirStatus(cpath); if (ds == DDIR) { return 0; } else if (ds == DILLEGAL) { return -1; } /* Walk up the path from the root and make each level. * If a directory already exists, no big deal. */ char *p = cpath; while (*p != '\0') { /* Skip any slashes, watching out for the end of the string. */ while (*p != '\0' && *p == '/') { p++; } if (*p == '\0') { break; } /* Find the end of the next path component. * We know that we'll see a slash before the NUL, * because we added it, above. */ while (*p != '/') { p++; } *p = '\0'; /* Check this part of the path and make a new directory * if necessary. */ ds = getPathDirStatus(cpath); if (ds == DILLEGAL) { /* Could happen if some other process/thread is * messing with the filesystem. */ free(cpath); return -1; } else if (ds == DMISSING) { int err; #ifdef HAVE_SELINUX char *secontext = NULL; if (sehnd) { selabel_lookup(sehnd, &secontext, cpath, mode); setfscreatecon(secontext); } #endif err = mkdir(cpath, mode); #ifdef HAVE_SELINUX if (secontext) { freecon(secontext); setfscreatecon(NULL); } #endif if (err != 0) { free(cpath); return -1; } if (timestamp != NULL && utime(cpath, timestamp)) { free(cpath); return -1; } } // else, this directory already exists. /* Repair the path and continue. */ *p = '/'; } free(cpath); return 0; }
// mount(fs_type, partition_type, location, mount_point) // // fs_type="yaffs2" partition_type="MTD" location=partition // fs_type="ext4" partition_type="EMMC" location=device Value* MountFn(const char* name, State* state, int argc, Expr* argv[]) { char* result = NULL; if (argc != 4) { return ErrorAbort(state, "%s() expects 4 args, got %d", name, argc); } char* fs_type; char* partition_type; char* location; char* mount_point; if (ReadArgs(state, argv, 4, &fs_type, &partition_type, &location, &mount_point) < 0) { return NULL; } if (strlen(fs_type) == 0) { ErrorAbort(state, "fs_type argument to %s() can't be empty", name); goto done; } if (strlen(partition_type) == 0) { ErrorAbort(state, "partition_type argument to %s() can't be empty", name); goto done; } if (strlen(location) == 0) { ErrorAbort(state, "location argument to %s() can't be empty", name); goto done; } if (strlen(mount_point) == 0) { ErrorAbort(state, "mount_point argument to %s() can't be empty", name); goto done; } #ifdef HAVE_SELINUX char *secontext = NULL; if (sehandle) { selabel_lookup(sehandle, &secontext, mount_point, 0755); setfscreatecon(secontext); } #endif mkdir(mount_point, 0755); #ifdef HAVE_SELINUX if (secontext) { freecon(secontext); setfscreatecon(NULL); } #endif if (strcmp(partition_type, "MTD") == 0) { mtd_scan_partitions(); const MtdPartition* mtd; mtd = mtd_find_partition_by_name(location); if (mtd == NULL) { fprintf(stderr, "%s: no mtd partition named \"%s\"", name, location); result = strdup(""); goto done; } if (mtd_mount_partition(mtd, mount_point, fs_type, 0 /* rw */) != 0) { fprintf(stderr, "mtd mount of %s failed: %s\n", location, strerror(errno)); result = strdup(""); goto done; } result = mount_point; } else { if (mount(location, mount_point, fs_type, MS_NOATIME | MS_NODEV | MS_NODIRATIME, "") < 0) { fprintf(stderr, "%s: failed to mount %s at %s: %s\n", name, location, mount_point, strerror(errno)); result = strdup(""); } else { result = mount_point; } } done: free(fs_type); free(partition_type); free(location); if (result != mount_point) free(mount_point); return StringValue(result); }
static u32 build_default_directory_structure() { u32 inode; u32 root_inode; struct dentry dentries = { .filename = "lost+found", .file_type = EXT4_FT_DIR, .mode = S_IRWXU, .uid = 0, .gid = 0, .mtime = 0, }; root_inode = make_directory(0, 1, &dentries, 1); inode = make_directory(root_inode, 0, NULL, 0); *dentries.inode = inode; inode_set_permissions(inode, dentries.mode, dentries.uid, dentries.gid, dentries.mtime); return root_inode; } #ifndef USE_MINGW /* Read a local directory and create the same tree in the generated filesystem. Calls itself recursively with each directory in the given directory */ static u32 build_directory_structure(const char *full_path, const char *dir_path, u32 dir_inode, fs_config_func_t fs_config_func, struct selabel_handle *sehnd) { int entries = 0; struct dentry *dentries; struct dirent **namelist = NULL; struct stat stat; int ret; int i; u32 inode; u32 entry_inode; u32 dirs = 0; bool needs_lost_and_found = false; if (full_path) { entries = scandir(full_path, &namelist, filter_dot, (void*)alphasort); if (entries < 0) { error_errno("scandir"); return EXT4_ALLOCATE_FAILED; } } if (dir_inode == 0) { /* root directory, check if lost+found already exists */ for (i = 0; i < entries; i++) if (strcmp(namelist[i]->d_name, "lost+found") == 0) break; if (i == entries) needs_lost_and_found = true; } dentries = calloc(entries, sizeof(struct dentry)); if (dentries == NULL) critical_error_errno("malloc"); for (i = 0; i < entries; i++) { dentries[i].filename = strdup(namelist[i]->d_name); if (dentries[i].filename == NULL) critical_error_errno("strdup"); asprintf(&dentries[i].path, "%s/%s", dir_path, namelist[i]->d_name); asprintf(&dentries[i].full_path, "%s/%s", full_path, namelist[i]->d_name); free(namelist[i]); ret = lstat(dentries[i].full_path, &stat); if (ret < 0) { error_errno("lstat"); i--; entries--; continue; } dentries[i].size = stat.st_size; dentries[i].mode = stat.st_mode & (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO); dentries[i].mtime = stat.st_mtime; if (fs_config_func != NULL) { #ifdef ANDROID unsigned int mode = 0; unsigned int uid = 0; unsigned int gid = 0; int dir = S_ISDIR(stat.st_mode); fs_config_func(dentries[i].path, dir, &uid, &gid, &mode); dentries[i].mode = mode; dentries[i].uid = uid; dentries[i].gid = gid; #else error("can't set android permissions - built without android support"); #endif } #ifdef HAVE_SELINUX if (sehnd) { char *sepath = NULL; asprintf(&sepath, "/%s", dentries[i].path); if (selabel_lookup(sehnd, &dentries[i].secon, sepath, stat.st_mode) < 0) { error("cannot lookup security context for %s", sepath); } if (dentries[i].secon) printf("Labeling %s as %s\n", sepath, dentries[i].secon); free(sepath); } #endif if (S_ISREG(stat.st_mode)) { dentries[i].file_type = EXT4_FT_REG_FILE; } else if (S_ISDIR(stat.st_mode)) { dentries[i].file_type = EXT4_FT_DIR; dirs++; } else if (S_ISCHR(stat.st_mode)) { dentries[i].file_type = EXT4_FT_CHRDEV; } else if (S_ISBLK(stat.st_mode)) { dentries[i].file_type = EXT4_FT_BLKDEV; } else if (S_ISFIFO(stat.st_mode)) { dentries[i].file_type = EXT4_FT_FIFO; } else if (S_ISSOCK(stat.st_mode)) { dentries[i].file_type = EXT4_FT_SOCK; } else if (S_ISLNK(stat.st_mode)) { dentries[i].file_type = EXT4_FT_SYMLINK; dentries[i].link = calloc(info.block_size, 1); readlink(dentries[i].full_path, dentries[i].link, info.block_size - 1); } else { error("unknown file type on %s", dentries[i].path); i--; entries--; } } free(namelist); if (needs_lost_and_found) { /* insert a lost+found directory at the beginning of the dentries */ struct dentry *tmp = calloc(entries + 1, sizeof(struct dentry)); memset(tmp, 0, sizeof(struct dentry)); memcpy(tmp + 1, dentries, entries * sizeof(struct dentry)); dentries = tmp; dentries[0].filename = strdup("lost+found"); asprintf(&dentries[0].path, "%s/lost+found", dir_path); dentries[0].full_path = NULL; dentries[0].size = 0; dentries[0].mode = S_IRWXU; dentries[0].file_type = EXT4_FT_DIR; dentries[0].uid = 0; dentries[0].gid = 0; #ifdef HAVE_SELINUX if (sehnd) { char *sepath = NULL; asprintf(&sepath, "/%s", dentries[0].path); if (selabel_lookup(sehnd, &dentries[0].secon, sepath, dentries[0].mode) < 0) error("cannot lookup security context for %s", dentries[0].path); free(sepath); } #endif entries++; dirs++; } inode = make_directory(dir_inode, entries, dentries, dirs); for (i = 0; i < entries; i++) { if (dentries[i].file_type == EXT4_FT_REG_FILE) { entry_inode = make_file(dentries[i].full_path, dentries[i].size); } else if (dentries[i].file_type == EXT4_FT_DIR) { entry_inode = build_directory_structure(dentries[i].full_path, dentries[i].path, inode, fs_config_func, sehnd); } else if (dentries[i].file_type == EXT4_FT_SYMLINK) { entry_inode = make_link(dentries[i].full_path, dentries[i].link); } else { error("unknown file type on %s", dentries[i].path); entry_inode = 0; } *dentries[i].inode = entry_inode; ret = inode_set_permissions(entry_inode, dentries[i].mode, dentries[i].uid, dentries[i].gid, dentries[i].mtime); if (ret) error("failed to set permissions on %s\n", dentries[i].path); ret = inode_set_selinux(entry_inode, dentries[i].secon); if (ret) error("failed to set SELinux context on %s\n", dentries[i].path); free(dentries[i].path); free(dentries[i].full_path); free(dentries[i].link); free((void *)dentries[i].filename); free(dentries[i].secon); } free(dentries); return inode; }
int make_ext4fs_internal(int fd, const char *_directory, const char *_target_out_directory, const char *_mountpoint, fs_config_func_t fs_config_func, int gzip, int sparse, int crc, int wipe, int real_uuid, struct selabel_handle *sehnd, int verbose, time_t fixed_time, FILE* block_list_file) { u32 root_inode_num; u16 root_mode; char *mountpoint; char *directory = NULL; char *target_out_directory = NULL; if (setjmp(setjmp_env)) return EXIT_FAILURE; /* Handle a call to longjmp() */ info.block_device = is_block_device_fd(fd); if (info.block_device && (sparse || gzip || crc)) { fprintf(stderr, "No sparse/gzip/crc allowed for block device\n"); return EXIT_FAILURE; } if (_mountpoint == NULL) { mountpoint = strdup(""); } else { mountpoint = canonicalize_abs_slashes(_mountpoint); } if (_directory) { directory = canonicalize_rel_slashes(_directory); } if (_target_out_directory) { target_out_directory = canonicalize_rel_slashes(_target_out_directory); } if (info.len <= 0) info.len = get_file_size(fd); if (info.len <= 0) { fprintf(stderr, "Need size of filesystem\n"); return EXIT_FAILURE; } if (info.block_size <= 0) info.block_size = compute_block_size(); /* Round down the filesystem length to be a multiple of the block size */ info.len &= ~((u64)info.block_size - 1); if (info.journal_blocks == 0) info.journal_blocks = compute_journal_blocks(); if (info.no_journal == 0) info.feat_compat = EXT4_FEATURE_COMPAT_HAS_JOURNAL; else info.journal_blocks = 0; if (info.blocks_per_group <= 0) info.blocks_per_group = compute_blocks_per_group(); if (info.inodes <= 0) info.inodes = compute_inodes(); if (info.inode_size <= 0) info.inode_size = 256; if (info.label == NULL) info.label = ""; info.inodes_per_group = compute_inodes_per_group(); info.feat_compat |= EXT4_FEATURE_COMPAT_RESIZE_INODE | EXT4_FEATURE_COMPAT_EXT_ATTR; info.feat_ro_compat |= EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER | EXT4_FEATURE_RO_COMPAT_LARGE_FILE | EXT4_FEATURE_RO_COMPAT_GDT_CSUM; info.feat_incompat |= EXT4_FEATURE_INCOMPAT_EXTENTS | EXT4_FEATURE_INCOMPAT_FILETYPE; info.bg_desc_reserve_blocks = compute_bg_desc_reserve_blocks(); printf("Creating filesystem with parameters:\n"); printf(" Size: %"PRIu64"\n", info.len); printf(" Block size: %d\n", info.block_size); printf(" Blocks per group: %d\n", info.blocks_per_group); printf(" Inodes per group: %d\n", info.inodes_per_group); printf(" Inode size: %d\n", info.inode_size); printf(" Journal blocks: %d\n", info.journal_blocks); printf(" Label: %s\n", info.label); ext4_create_fs_aux_info(); printf(" Blocks: %"PRIu64"\n", aux_info.len_blocks); printf(" Block groups: %d\n", aux_info.groups); printf(" Reserved block group size: %d\n", info.bg_desc_reserve_blocks); ext4_sparse_file = sparse_file_new(info.block_size, info.len); block_allocator_init(); ext4_fill_in_sb(real_uuid); if (reserve_inodes(0, 10) == EXT4_ALLOCATE_FAILED) error("failed to reserve first 10 inodes"); if (info.feat_compat & EXT4_FEATURE_COMPAT_HAS_JOURNAL) ext4_create_journal_inode(); if (info.feat_compat & EXT4_FEATURE_COMPAT_RESIZE_INODE) ext4_create_resize_inode(); #ifdef USE_MINGW // Windows needs only 'create an empty fs image' functionality assert(!directory); root_inode_num = build_default_directory_structure(mountpoint, sehnd); #else if (directory) root_inode_num = build_directory_structure(directory, mountpoint, target_out_directory, 0, fs_config_func, sehnd, verbose, fixed_time); else root_inode_num = build_default_directory_structure(mountpoint, sehnd); #endif root_mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; inode_set_permissions(root_inode_num, root_mode, 0, 0, 0); #ifndef USE_MINGW if (sehnd) { char *secontext = NULL; if (selabel_lookup(sehnd, &secontext, mountpoint, S_IFDIR) < 0) { error("cannot lookup security context for %s", mountpoint); } if (secontext) { if (verbose) { printf("Labeling %s as %s\n", mountpoint, secontext); } inode_set_selinux(root_inode_num, secontext); } freecon(secontext); } #endif ext4_update_free(); if (block_list_file) { size_t dirlen = directory ? strlen(directory) : 0; struct block_allocation* p = get_saved_allocation_chain(); while (p) { if (directory && strncmp(p->filename, directory, dirlen) == 0) { // substitute mountpoint for the leading directory in the filename, in the output file fprintf(block_list_file, "%s%s", mountpoint, p->filename + dirlen); } else { fprintf(block_list_file, "%s", p->filename); } print_blocks(block_list_file, p); struct block_allocation* pn = p->next; free_alloc(p); p = pn; } } printf("Created filesystem with %d/%d inodes and %d/%d blocks\n", aux_info.sb->s_inodes_count - aux_info.sb->s_free_inodes_count, aux_info.sb->s_inodes_count, aux_info.sb->s_blocks_count_lo - aux_info.sb->s_free_blocks_count_lo, aux_info.sb->s_blocks_count_lo); if (wipe && WIPE_IS_SUPPORTED) { wipe_block_device(fd, info.len); } write_ext4_image(fd, gzip, sparse, crc); sparse_file_destroy(ext4_sparse_file); ext4_sparse_file = NULL; free(mountpoint); free(directory); return 0; }