diff options
author | Balint Reczey <balint@balintreczey.hu> | 2022-08-20 18:17:16 +0200 |
---|---|---|
committer | Balint Reczey <balint@balintreczey.hu> | 2022-08-20 18:17:16 +0200 |
commit | 675b462b64b213647d0f5c56b1e8440be5890c8a (patch) | |
tree | 1cd63eec7594d03a622575136c61d8c813f59503 /libmisc/chowndir.c | |
parent | 0c04b92a9afe5e09a20307d8a5ec98d97ed00f47 (diff) | |
download | shadow-675b462b64b213647d0f5c56b1e8440be5890c8a.tar.gz |
New upstream version 4.12.2+dfsg1
Diffstat (limited to 'libmisc/chowndir.c')
-rw-r--r-- | libmisc/chowndir.c | 130 |
1 files changed, 49 insertions, 81 deletions
diff --git a/libmisc/chowndir.c b/libmisc/chowndir.c index 3a62d545..d31618a5 100644 --- a/libmisc/chowndir.c +++ b/libmisc/chowndir.c @@ -17,45 +17,28 @@ #include "defines.h" #include <fcntl.h> #include <stdio.h> -/* - * chown_tree - change ownership of files in a directory tree - * - * chown_dir() walks a directory tree and changes the ownership - * of all files owned by the provided user ID. - * - * Only files owned (resp. group-owned) by old_uid (resp. by old_gid) - * will have their ownership (resp. group-ownership) modified, unless - * old_uid (resp. old_gid) is set to -1. - * - * new_uid and new_gid can be set to -1 to indicate that no owner or - * group-owner shall be changed. - */ -int chown_tree (const char *root, +#include <unistd.h> + +static int chown_tree_at (int at_fd, + const char *path, uid_t old_uid, uid_t new_uid, gid_t old_gid, gid_t new_gid) { - char *new_name; - size_t new_name_len; - int rc = 0; - struct DIRECT *ent; - struct stat sb; DIR *dir; + const struct dirent *ent; + struct stat dir_sb; + int dir_fd, rc = 0; - new_name = malloc (1024); - if (NULL == new_name) { + dir_fd = openat (at_fd, path, O_RDONLY | O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC); + if (dir_fd < 0) { return -1; } - new_name_len = 1024; - /* - * Make certain the directory exists. This routine is called - * directly by the invoker, or recursively. - */ - - if (access (root, F_OK) != 0) { - free (new_name); + dir = fdopendir (dir_fd); + if (!dir) { + (void) close (dir_fd); return -1; } @@ -65,68 +48,34 @@ int chown_tree (const char *root, * recursively. If not, it is checked to see if an ownership * shall be changed. */ - - dir = opendir (root); - if (NULL == dir) { - free (new_name); - return -1; - } - while ((ent = readdir (dir))) { - size_t ent_name_len; uid_t tmpuid = (uid_t) -1; gid_t tmpgid = (gid_t) -1; + struct stat ent_sb; /* * Skip the "." and ".." entries */ - if ( (strcmp (ent->d_name, ".") == 0) || (strcmp (ent->d_name, "..") == 0)) { continue; } - /* - * Make the filename for both the source and the - * destination files. - */ - - ent_name_len = strlen (root) + strlen (ent->d_name) + 2; - if (ent_name_len > new_name_len) { - /*@only@*/char *tmp = realloc (new_name, ent_name_len); - if (NULL == tmp) { - rc = -1; - break; - } - new_name = tmp; - new_name_len = ent_name_len; - } - - (void) snprintf (new_name, new_name_len, "%s/%s", root, ent->d_name); - - /* Don't follow symbolic links! */ - if (LSTAT (new_name, &sb) == -1) { - continue; + rc = fstatat (dirfd(dir), ent->d_name, &ent_sb, AT_SYMLINK_NOFOLLOW); + if (rc < 0) { + break; } - if (S_ISDIR (sb.st_mode) && !S_ISLNK (sb.st_mode)) { - + if (S_ISDIR (ent_sb.st_mode)) { /* * Do the entire subdirectory. */ - - rc = chown_tree (new_name, old_uid, new_uid, - old_gid, new_gid); + rc = chown_tree_at (dirfd(dir), ent->d_name, old_uid, new_uid, old_gid, new_gid); if (0 != rc) { break; } } -#ifndef HAVE_LCHOWN - /* don't use chown (follows symbolic links!) */ - if (S_ISLNK (sb.st_mode)) { - continue; - } -#endif + /* * By default, the IDs are not changed (-1). * @@ -136,43 +85,62 @@ int chown_tree (const char *root, * If the file is not group-owned by the group, the * group-owner is not changed. */ - if (((uid_t) -1 == old_uid) || (sb.st_uid == old_uid)) { + if (((uid_t) -1 == old_uid) || (ent_sb.st_uid == old_uid)) { tmpuid = new_uid; } - if (((gid_t) -1 == old_gid) || (sb.st_gid == old_gid)) { + if (((gid_t) -1 == old_gid) || (ent_sb.st_gid == old_gid)) { tmpgid = new_gid; } if (((uid_t) -1 != tmpuid) || ((gid_t) -1 != tmpgid)) { - rc = LCHOWN (new_name, tmpuid, tmpgid); + rc = fchownat (dirfd(dir), ent->d_name, tmpuid, tmpgid, AT_SYMLINK_NOFOLLOW); if (0 != rc) { break; } } } - free (new_name); - (void) closedir (dir); - /* * Now do the root of the tree */ - - if ((0 == rc) && (stat (root, &sb) == 0)) { + if ((0 == rc) && (fstat (dirfd(dir), &dir_sb) == 0)) { uid_t tmpuid = (uid_t) -1; gid_t tmpgid = (gid_t) -1; - if (((uid_t) -1 == old_uid) || (sb.st_uid == old_uid)) { + if (((uid_t) -1 == old_uid) || (dir_sb.st_uid == old_uid)) { tmpuid = new_uid; } - if (((gid_t) -1 == old_gid) || (sb.st_gid == old_gid)) { + if (((gid_t) -1 == old_gid) || (dir_sb.st_gid == old_gid)) { tmpgid = new_gid; } if (((uid_t) -1 != tmpuid) || ((gid_t) -1 != tmpgid)) { - rc = LCHOWN (root, tmpuid, tmpgid); + rc = fchown (dirfd(dir), tmpuid, tmpgid); } } else { rc = -1; } + (void) closedir (dir); + return rc; } +/* + * chown_tree - change ownership of files in a directory tree + * + * chown_dir() walks a directory tree and changes the ownership + * of all files owned by the provided user ID. + * + * Only files owned (resp. group-owned) by old_uid (resp. by old_gid) + * will have their ownership (resp. group-ownership) modified, unless + * old_uid (resp. old_gid) is set to -1. + * + * new_uid and new_gid can be set to -1 to indicate that no owner or + * group-owner shall be changed. + */ +int chown_tree (const char *root, + uid_t old_uid, + uid_t new_uid, + gid_t old_gid, + gid_t new_gid) +{ + return chown_tree_at (AT_FDCWD, root, old_uid, new_uid, old_gid, new_gid); +} |