int dirSetHierarchyPermissions(const char *path, int uid, int gid, int dirMode, int fileMode) { struct stat st; if (lstat(path, &st)) { return -1; } /* ignore symlinks */ if (S_ISLNK(st.st_mode)) { return 0; } /* directories and files get different permissions */ if (chown(path, uid, gid) || chmod(path, S_ISDIR(st.st_mode) ? dirMode : fileMode)) { return -1; } /* recurse over directory components */ if (S_ISDIR(st.st_mode)) { DIR *dir = opendir(path); if (dir == NULL) { return -1; } errno = 0; const struct dirent *de; while (errno == 0 && (de = readdir(dir)) != NULL) { if (!strcmp(de->d_name, "..") || !strcmp(de->d_name, ".")) { continue; } char dn[PATH_MAX]; snprintf(dn, sizeof(dn), "%s/%s", path, de->d_name); if (!dirSetHierarchyPermissions(dn, uid, gid, dirMode, fileMode)) { errno = 0; } else if (errno == 0) { errno = -1; } } if (errno != 0) { int save = errno; closedir(dir); errno = save; return -1; } if (closedir(dir)) { return -1; } } return 0; }
/* set_perm <uid> <gid> <mode> <path> [... <pathN>] * set_perm_recursive <uid> <gid> <dir-mode> <file-mode> <path> [... <pathN>] * * Like "chmod", "chown" and "chgrp" all in one, set ownership and permissions * of single files or entire directory trees. Any error causes failure. * User, group, and modes must all be integer values (hex or octal OK). */ static int cmd_set_perm(const char *name, void *cookie, int argc, const char *argv[], PermissionRequestList *permissions) { UNUSED(cookie); CHECK_WORDS(); bool recurse = !strcmp(name, "set_perm_recursive"); int min_args = 4 + (recurse ? 1 : 0); if (argc < min_args) { LOGE("Command %s requires at least %d args\n", name, min_args); return 1; } // All the arguments except the path(s) are numeric. int i, n[min_args - 1]; for (i = 0; i < min_args - 1; ++i) { char *end; n[i] = strtoul(argv[i], &end, 0); if (end[0] != '\0' || argv[i][0] == '\0') { LOGE("Command %s: invalid argument \"%s\"\n", name, argv[i]); return 1; } } for (i = min_args - 1; i < min_args; ++i) { char path[PATH_MAX]; if (translate_root_path(argv[i], path, sizeof(path)) == NULL) { LOGE("Command %s: bad path \"%s\"\n", name, argv[i]); return 1; } if (ensure_root_path_mounted(argv[i])) { LOGE("Can't mount %s\n", argv[i]); return 1; } if (recurse ? dirSetHierarchyPermissions(path, n[0], n[1], n[2], n[3]) : (chown(path, n[0], n[1]) || chmod(path, n[2]))) { LOGE("Can't chown/mod %s\n(%s)\n", path, strerror(errno)); return 1; } } return 0; }
Value* SetPermFn(const char* name, State* state, int argc, Expr* argv[]) { char* result = NULL; bool recursive = (strcmp(name, "set_perm_recursive") == 0); int min_args = 4 + (recursive ? 1 : 0); if (argc < min_args) { return ErrorAbort(state, "%s() expects %d+ args, got %d", name, argc); } char** args = ReadVarArgs(state, argc, argv); if (args == NULL) return NULL; char* end; int i; int bad = 0; int uid = strtoul(args[0], &end, 0); if (*end != '\0' || args[0][0] == 0) { ErrorAbort(state, "%s: \"%s\" not a valid uid", name, args[0]); goto done; } int gid = strtoul(args[1], &end, 0); if (*end != '\0' || args[1][0] == 0) { ErrorAbort(state, "%s: \"%s\" not a valid gid", name, args[1]); goto done; } if (recursive) { int dir_mode = strtoul(args[2], &end, 0); if (*end != '\0' || args[2][0] == 0) { ErrorAbort(state, "%s: \"%s\" not a valid dirmode", name, args[2]); goto done; } int file_mode = strtoul(args[3], &end, 0); if (*end != '\0' || args[3][0] == 0) { ErrorAbort(state, "%s: \"%s\" not a valid filemode", name, args[3]); goto done; } for (i = 4; i < argc; ++i) { dirSetHierarchyPermissions(args[i], uid, gid, dir_mode, file_mode); } } else { int mode = strtoul(args[2], &end, 0); if (*end != '\0' || args[2][0] == 0) { ErrorAbort(state, "%s: \"%s\" not a valid mode", name, args[2]); goto done; } for (i = 3; i < argc; ++i) { if (chown(args[i], uid, gid) < 0) { fprintf(stderr, "%s: chown of %s to %d %d failed: %s\n", name, args[i], uid, gid, strerror(errno)); ++bad; } if (chmod(args[i], mode) < 0) { fprintf(stderr, "%s: chmod of %s to %o failed: %s\n", name, args[i], mode, strerror(errno)); ++bad; } } } result = strdup(""); done: for (i = 0; i < argc; ++i) { free(args[i]); } free(args); if (bad) { free(result); return ErrorAbort(state, "%s: some changes failed", name); } return StringValue(result); }