Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use *at functions for directory creation #124

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 70 additions & 6 deletions sbin/create_dirs_from_rpmdb.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include <config.h>
#endif

#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
Expand Down Expand Up @@ -221,6 +222,48 @@ int check_package(rpmts ts, Header h) {
return ec;
}

// Open the parent of path without dereferencing symlinks.
int open_parent_dir(const char *path) {
// strdup for use with strtok
char *local_path = strdup(path);
if (!local_path) {
return -1;
}

int dirfd = open("/", O_RDONLY | O_DIRECTORY | O_NOFOLLOW);
if (dirfd < 0) {
free(local_path);
return -1;
}

char *saveptr = NULL;
const char *component = strtok_r(local_path, "/", &saveptr);
while (component) {
int nextdirfd = openat(dirfd, component, O_RDONLY | O_DIRECTORY | O_NOFOLLOW);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would move the error check for this right here to keep it together

int saved_errno = errno;

component = strtok_r(NULL, "/", &saveptr);
if (component == NULL) {
// previous component was the last part, return dirfd
if (nextdirfd >= 0)
close(nextdirfd);

break;
}

close(dirfd);
dirfd = nextdirfd;

if (dirfd < 0) {
errno = saved_errno;
break;
}
}

free(local_path);
return dirfd;
}

int create_dirs(struct node *node, size_t size) {
int rc = 0;
size_t i;
Expand Down Expand Up @@ -270,19 +313,38 @@ int create_dirs(struct node *node, size_t size) {
if (verbose_flag)
printf("Create %s\n", node->dirname);

if (mkdir(node->dirname, node->fmode) < 0) {
int dirfd = open_parent_dir(node->dirname);
if (dirfd < 0) {
fprintf(stderr, "Failed to traverse path for '%s': %m\n", node->dirname);
rc = 1;
continue;
}

const char *node_basename = strrchr(node->dirname, '/');
// Make sure there's a name
if (!node_basename || node_basename[1] == '\0') {
fprintf(stderr, "Failed to get basename of '%s'", node->dirname);
rc = 1;
continue;
}
node_basename = node_basename + 1; // Skip /

// Ok to assign mode before chown?
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes

if (mkdirat(dirfd, node_basename, node->fmode) < 0) {
fprintf(stderr, "Failed to create directory '%s': %m\n", node->dirname);
rc = 1;
continue;
}

if (chown(node->dirname, node->user_id, node->group_id) < 0) {
// Ok to assign ownership before se labels?
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no, it would be better to first change the labels, then set the owner

if (fchownat(dirfd, node_basename, node->user_id, node->group_id, AT_SYMLINK_NOFOLLOW) < 0) {
fprintf(stderr, "Failed to set owner/group for '%s': %m\n", node->dirname);
/* wrong permissions are bad, remove dir and continue */
rmdir(node->dirname);
unlinkat(dirfd, node_basename, AT_REMOVEDIR);
rc = 1;
continue;
}

/* ignore errors here, time stamps are not critical */
utimes(node->dirname, stamps);

Expand All @@ -295,7 +357,7 @@ int create_dirs(struct node *node, size_t size) {
}

fprintf(stderr, "Failed to get default context for directory '%s': %m\n", node->dirname);
rmdir(node->dirname);
unlinkat(dirfd, node_basename, AT_REMOVEDIR);
rc = 1;
continue;
}
Expand All @@ -307,7 +369,7 @@ int create_dirs(struct node *node, size_t size) {
if (errno != ENODATA) {
fprintf(stderr, "Failed to get old context for '%s': %m\n", node->dirname);
freecon(newcon);
rmdir(node->dirname);
unlinkat(dirfd, node_basename, AT_REMOVEDIR);
rc = 1;
continue;
}
Expand All @@ -319,14 +381,16 @@ int create_dirs(struct node *node, size_t size) {
printf("Set SELinux file context to %s\n", newcon);
if (setfilecon_raw(node->dirname, newcon) < 0) {
fprintf(stderr, "Failed to set new context for '%s': %m\n", node->dirname);
rmdir(node->dirname);
unlinkat(dirfd, node_basename, AT_REMOVEDIR);
rc = 1;
}
}

freecon(curcon);
freecon(newcon);
}

close(dirfd);
}

if (use_selinux)
Expand Down