sysusers: split make_files()
authorFranck Bui <fbui@suse.com>
Tue, 9 May 2017 07:37:37 +0000 (09:37 +0200)
committerFranck Bui <fbui@suse.com>
Wed, 10 May 2017 08:06:20 +0000 (10:06 +0200)
This patch extracts the code which is in charge to write the new users or
groups into temporary files and move it into 4 dedicated functions.

This part was previously inlined in makes_files() making this function quite
big and hard to read and maintain.

There should be no functional change.

src/sysusers/sysusers.c

index 4a0a49f..5423978 100644 (file)
@@ -370,341 +370,416 @@ static int rename_and_apply_smack(const char *temp_path, const char *dest_path)
         return r;
 }
 
-static int write_files(void) {
-
-        _cleanup_fclose_ FILE *passwd = NULL, *group = NULL, *shadow = NULL, *gshadow = NULL;
-        _cleanup_free_ char *passwd_tmp = NULL, *group_tmp = NULL, *shadow_tmp = NULL, *gshadow_tmp = NULL;
-        const char *passwd_path = NULL, *group_path = NULL, *shadow_path = NULL, *gshadow_path = NULL;
-        bool group_changed = false;
+static int write_temporary_passwd(const char *passwd_path, FILE **tmpfile, char **tmpfile_path) {
+        _cleanup_fclose_ FILE *original = NULL, *passwd = NULL;
+        _cleanup_free_ char *passwd_tmp = NULL;
         Iterator iterator;
         Item *i;
         int r;
 
-        if (hashmap_size(todo_gids) > 0 || hashmap_size(members) > 0) {
-                _cleanup_fclose_ FILE *original = NULL;
-
-                /* First we update the actual group list file */
-                group_path = prefix_roota(arg_root, "/etc/group");
-                r = fopen_temporary_label("/etc/group", group_path, &group, &group_tmp);
-                if (r < 0)
-                        goto finish;
+        if (hashmap_size(todo_uids) == 0)
+                return 0;
 
-                original = fopen(group_path, "re");
-                if (original) {
-                        struct group *gr;
+        r = fopen_temporary_label("/etc/passwd", passwd_path, &passwd, &passwd_tmp);
+        if (r < 0)
+                return r;
 
-                        r = sync_rights(original, group);
-                        if (r < 0)
-                                goto finish;
+        original = fopen(passwd_path, "re");
+        if (original) {
+                struct passwd *pw;
 
-                        errno = 0;
-                        while ((gr = fgetgrent(original))) {
-                                /* Safety checks against name and GID
-                                 * collisions. Normally, this should
-                                 * be unnecessary, but given that we
-                                 * look at the entries anyway here,
-                                 * let's make an extra verification
-                                 * step that we don't generate
-                                 * duplicate entries. */
-
-                                i = hashmap_get(groups, gr->gr_name);
-                                if (i && i->todo_group) {
-                                        log_error("%s: Group \"%s\" already exists.", group_path, gr->gr_name);
-                                        r = -EEXIST;
-                                        goto finish;
-                                }
+                r = sync_rights(original, passwd);
+                if (r < 0)
+                        goto fail;
 
-                                if (hashmap_contains(todo_gids, GID_TO_PTR(gr->gr_gid))) {
-                                        log_error("%s: Detected collision for GID " GID_FMT ".", group_path, gr->gr_gid);
-                                        r = -EEXIST;
-                                        goto finish;
-                                }
+                errno = 0;
+                while ((pw = fgetpwent(original))) {
 
-                                r = putgrent_with_members(gr, group);
-                                if (r < 0)
-                                        goto finish;
-                                if (r > 0)
-                                        group_changed = true;
+                        i = hashmap_get(users, pw->pw_name);
+                        if (i && i->todo_user) {
+                                log_error("%s: User \"%s\" already exists.", passwd_path, pw->pw_name);
+                                r = -EEXIST;
+                                goto fail;
+                        }
 
-                                errno = 0;
+                        if (hashmap_contains(todo_uids, UID_TO_PTR(pw->pw_uid))) {
+                                log_error("%s: Detected collision for UID " UID_FMT ".", passwd_path, pw->pw_uid);
+                                r = -EEXIST;
+                                goto fail;
                         }
-                        if (!IN_SET(errno, 0, ENOENT)) {
-                                r = -errno;
-                                goto finish;
+
+                        errno = 0;
+                        if (putpwent(pw, passwd) < 0) {
+                                r = errno ? -errno : -EIO;
+                                goto fail;
                         }
 
-                } else if (errno != ENOENT) {
-                        r = -errno;
-                        goto finish;
-                } else if (fchmod(fileno(group), 0644) < 0) {
+                        errno = 0;
+                }
+                if (!IN_SET(errno, 0, ENOENT)) {
                         r = -errno;
-                        goto finish;
+                        goto fail;
                 }
 
-                HASHMAP_FOREACH(i, todo_gids, iterator) {
-                        struct group n = {
-                                .gr_name = i->name,
-                                .gr_gid = i->gid,
-                                .gr_passwd = (char*) "x",
-                        };
+        } else if (errno != ENOENT) {
+                r = -errno;
+                goto fail;
+        } else if (fchmod(fileno(passwd), 0644) < 0) {
+                r = -errno;
+                goto fail;
+        }
 
-                        r = putgrent_with_members(&n, group);
-                        if (r < 0)
-                                goto finish;
+        HASHMAP_FOREACH(i, todo_uids, iterator) {
+                struct passwd n = {
+                        .pw_name = i->name,
+                        .pw_uid = i->uid,
+                        .pw_gid = i->gid,
+                        .pw_gecos = i->description,
 
-                        group_changed = true;
-                }
+                        /* "x" means the password is stored in the shadow file */
+                        .pw_passwd = (char*) "x",
 
-                r = fflush_and_check(group);
-                if (r < 0)
-                        goto finish;
+                        /* We default to the root directory as home */
+                        .pw_dir = i->home ? i->home : (char*) "/",
+
+                        /* Initialize the shell to nologin, with one exception:
+                         * for root we patch in something special */
+                        .pw_shell = i->uid == 0 ? (char*) "/bin/sh" : (char*) "/sbin/nologin",
+                };
 
-                if (original) {
-                        fclose(original);
-                        original = NULL;
+                errno = 0;
+                if (putpwent(&n, passwd) != 0) {
+                        r = errno ? -errno : -EIO;
+                        goto fail;
                 }
+        }
 
-                /* OK, now also update the shadow file for the group list */
-                gshadow_path = prefix_roota(arg_root, "/etc/gshadow");
-                r = fopen_temporary_label("/etc/gshadow", gshadow_path, &gshadow, &gshadow_tmp);
-                if (r < 0)
-                        goto finish;
+        r = fflush_and_check(passwd);
+        if (r < 0)
+                goto fail;
 
-                original = fopen(gshadow_path, "re");
-                if (original) {
-                        struct sgrp *sg;
+        *tmpfile = passwd;
+        *tmpfile_path = passwd_tmp;
+        passwd = NULL;
+        passwd_tmp = NULL;
+        return 0;
 
-                        r = sync_rights(original, gshadow);
-                        if (r < 0)
-                                goto finish;
+fail:
+        unlink(passwd_tmp);
+        return r;
+}
 
-                        errno = 0;
-                        while ((sg = fgetsgent(original))) {
+static int write_temporary_shadow(const char *shadow_path, FILE **tmpfile, char **tmpfile_path) {
+        _cleanup_fclose_ FILE *original = NULL, *shadow = NULL;
+        _cleanup_free_ char *shadow_tmp = NULL;
+        Iterator iterator;
+        long lstchg;
+        Item *i;
+        int r;
 
-                                i = hashmap_get(groups, sg->sg_namp);
-                                if (i && i->todo_group) {
-                                        log_error("%s: Group \"%s\" already exists.", gshadow_path, sg->sg_namp);
-                                        r = -EEXIST;
-                                        goto finish;
-                                }
+        if (hashmap_size(todo_uids) == 0)
+                return 0;
 
-                                r = putsgent_with_members(sg, gshadow);
-                                if (r < 0)
-                                        goto finish;
-                                if (r > 0)
-                                        group_changed = true;
+        r = fopen_temporary_label("/etc/shadow", shadow_path, &shadow, &shadow_tmp);
+        if (r < 0)
+                return r;
+
+        lstchg = (long) (now(CLOCK_REALTIME) / USEC_PER_DAY);
+
+        original = fopen(shadow_path, "re");
+        if (original) {
+                struct spwd *sp;
+
+                r = sync_rights(original, shadow);
+                if (r < 0)
+                        goto fail;
+
+                errno = 0;
+                while ((sp = fgetspent(original))) {
+
+                        i = hashmap_get(users, sp->sp_namp);
+                        if (i && i->todo_user) {
+                                /* we will update the existing entry */
+                                sp->sp_lstchg = lstchg;
 
-                                errno = 0;
+                                /* only the /etc/shadow stage is left, so we can
+                                 * safely remove the item from the todo set */
+                                i->todo_user = false;
+                                hashmap_remove(todo_uids, UID_TO_PTR(i->uid));
                         }
-                        if (!IN_SET(errno, 0, ENOENT)) {
-                                r = -errno;
-                                goto finish;
+
+                        errno = 0;
+                        if (putspent(sp, shadow) < 0) {
+                                r = errno ? -errno : -EIO;
+                                goto fail;
                         }
 
-                } else if (errno != ENOENT) {
-                        r = -errno;
-                        goto finish;
-                } else if (fchmod(fileno(gshadow), 0000) < 0) {
+                        errno = 0;
+                }
+                if (!IN_SET(errno, 0, ENOENT)) {
                         r = -errno;
-                        goto finish;
+                        goto fail;
                 }
+        } else if (errno != ENOENT) {
+                r = -errno;
+                goto fail;
+        } else if (fchmod(fileno(shadow), 0000) < 0) {
+                r = -errno;
+                goto fail;
+        }
 
-                HASHMAP_FOREACH(i, todo_gids, iterator) {
-                        struct sgrp n = {
-                                .sg_namp = i->name,
-                                .sg_passwd = (char*) "!!",
-                        };
+        HASHMAP_FOREACH(i, todo_uids, iterator) {
+                struct spwd n = {
+                        .sp_namp = i->name,
+                        .sp_pwdp = (char*) "!!",
+                        .sp_lstchg = lstchg,
+                        .sp_min = -1,
+                        .sp_max = -1,
+                        .sp_warn = -1,
+                        .sp_inact = -1,
+                        .sp_expire = -1,
+                        .sp_flag = (unsigned long) -1, /* this appears to be what everybody does ... */
+                };
 
-                        r = putsgent_with_members(&n, gshadow);
-                        if (r < 0)
-                                goto finish;
-
-                        group_changed = true;
+                errno = 0;
+                if (putspent(&n, shadow) != 0) {
+                        r = errno ? -errno : -EIO;
+                        goto fail;
                 }
-
-                r = fflush_and_check(gshadow);
-                if (r < 0)
-                        goto finish;
         }
 
-        if (hashmap_size(todo_uids) > 0) {
-                _cleanup_fclose_ FILE *original = NULL;
-                long lstchg;
+        r = fflush_and_check(shadow);
+        if (r < 0)
+                goto fail;
 
-                /* First we update the user database itself */
-                passwd_path = prefix_roota(arg_root, "/etc/passwd");
-                r = fopen_temporary_label("/etc/passwd", passwd_path, &passwd, &passwd_tmp);
-                if (r < 0)
-                        goto finish;
+        *tmpfile = shadow;
+        *tmpfile_path = shadow_tmp;
+        shadow = NULL;
+        shadow_tmp = NULL;
+        return 0;
 
-                original = fopen(passwd_path, "re");
-                if (original) {
-                        struct passwd *pw;
+fail:
+        unlink(shadow_tmp);
+        return r;
+}
 
-                        r = sync_rights(original, passwd);
-                        if (r < 0)
-                                goto finish;
+static int write_temporary_group(const char *group_path, FILE **tmpfile, char **tmpfile_path) {
+        _cleanup_fclose_ FILE *original = NULL, *group = NULL;
+        _cleanup_free_ char *group_tmp = NULL;
+        bool group_changed = false;
+        Iterator iterator;
+        Item *i;
+        int r;
 
-                        errno = 0;
-                        while ((pw = fgetpwent(original))) {
+        if (hashmap_size(todo_gids) == 0 && hashmap_size(members) == 0)
+                return 0;
 
-                                i = hashmap_get(users, pw->pw_name);
-                                if (i && i->todo_user) {
-                                        log_error("%s: User \"%s\" already exists.", passwd_path, pw->pw_name);
-                                        r = -EEXIST;
-                                        goto finish;
-                                }
+        r = fopen_temporary_label("/etc/group", group_path, &group, &group_tmp);
+        if (r < 0)
+                return r;
 
-                                if (hashmap_contains(todo_uids, UID_TO_PTR(pw->pw_uid))) {
-                                        log_error("%s: Detected collision for UID " UID_FMT ".", passwd_path, pw->pw_uid);
-                                        r = -EEXIST;
-                                        goto finish;
-                                }
+        original = fopen(group_path, "re");
+        if (original) {
+                struct group *gr;
 
-                                errno = 0;
-                                if (putpwent(pw, passwd) < 0) {
-                                        r = errno ? -errno : -EIO;
-                                        goto finish;
-                                }
+                r = sync_rights(original, group);
+                if (r < 0)
+                        goto fail;
 
-                                errno = 0;
+                errno = 0;
+                while ((gr = fgetgrent(original))) {
+                        /* Safety checks against name and GID collisions. Normally,
+                         * this should be unnecessary, but given that we look at the
+                         * entries anyway here, let's make an extra verification
+                         * step that we don't generate duplicate entries. */
+
+                        i = hashmap_get(groups, gr->gr_name);
+                        if (i && i->todo_group) {
+                                log_error("%s: Group \"%s\" already exists.", group_path, gr->gr_name);
+                                r = -EEXIST;
+                                goto fail;
                         }
-                        if (!IN_SET(errno, 0, ENOENT)) {
-                                r = -errno;
-                                goto finish;
+
+                        if (hashmap_contains(todo_gids, GID_TO_PTR(gr->gr_gid))) {
+                                log_error("%s: Detected collision for GID " GID_FMT ".", group_path, gr->gr_gid);
+                                r = -EEXIST;
+                                goto fail;
                         }
 
-                } else if (errno != ENOENT) {
-                        r = -errno;
-                        goto finish;
-                } else if (fchmod(fileno(passwd), 0644) < 0) {
+                        r = putgrent_with_members(gr, group);
+                        if (r < 0)
+                                goto fail;
+                        if (r > 0)
+                                group_changed = true;
+
+                        errno = 0;
+                }
+                if (!IN_SET(errno, 0, ENOENT)) {
                         r = -errno;
-                        goto finish;
+                        goto fail;
                 }
 
-                HASHMAP_FOREACH(i, todo_uids, iterator) {
-                        struct passwd n = {
-                                .pw_name = i->name,
-                                .pw_uid = i->uid,
-                                .pw_gid = i->gid,
-                                .pw_gecos = i->description,
+        } else if (errno != ENOENT) {
+                r = -errno;
+                goto fail;
+        } else if (fchmod(fileno(group), 0644) < 0) {
+                r = -errno;
+                goto fail;
+        }
 
-                                /* "x" means the password is stored in
-                                 * the shadow file */
-                                .pw_passwd = (char*) "x",
+        HASHMAP_FOREACH(i, todo_gids, iterator) {
+                struct group n = {
+                        .gr_name = i->name,
+                        .gr_gid = i->gid,
+                        .gr_passwd = (char*) "x",
+                };
 
-                                /* We default to the root directory as home */
-                                .pw_dir = i->home ? i->home : (char*) "/",
+                r = putgrent_with_members(&n, group);
+                if (r < 0)
+                        goto fail;
 
-                                /* Initialize the shell to nologin,
-                                 * with one exception: for root we
-                                 * patch in something special */
-                                .pw_shell = i->uid == 0 ? (char*) "/bin/sh" : (char*) "/sbin/nologin",
-                        };
+                group_changed = true;
+        }
 
-                        errno = 0;
-                        if (putpwent(&n, passwd) != 0) {
-                                r = errno ? -errno : -EIO;
-                                goto finish;
-                        }
-                }
+        r = fflush_and_check(group);
+        if (r < 0)
+                goto fail;
 
-                r = fflush_and_check(passwd);
-                if (r < 0)
-                        goto finish;
+        if (group_changed) {
+                *tmpfile = group;
+                *tmpfile_path = group_tmp;
+                group = NULL;
+                group_tmp = NULL;
+        }
+        return 0;
 
-                if (original) {
-                        fclose(original);
-                        original = NULL;
-                }
+fail:
+        unlink(group_tmp);
+        return r;
+}
 
-                /* The we update the shadow database */
-                shadow_path = prefix_roota(arg_root, "/etc/shadow");
-                r = fopen_temporary_label("/etc/shadow", shadow_path, &shadow, &shadow_tmp);
-                if (r < 0)
-                        goto finish;
+static int write_temporary_gshadow(const char * gshadow_path, FILE **tmpfile, char **tmpfile_path) {
+        _cleanup_fclose_ FILE *original = NULL, *gshadow = NULL;
+        _cleanup_free_ char *gshadow_tmp = NULL;
+        bool group_changed = false;
+        Iterator iterator;
+        Item *i;
+        int r;
 
-                lstchg = (long) (now(CLOCK_REALTIME) / USEC_PER_DAY);
+        if (hashmap_size(todo_gids) == 0 && hashmap_size(members) == 0)
+                return 0;
 
-                original = fopen(shadow_path, "re");
-                if (original) {
-                        struct spwd *sp;
+        r = fopen_temporary_label("/etc/gshadow", gshadow_path, &gshadow, &gshadow_tmp);
+        if (r < 0)
+                return r;
 
-                        r = sync_rights(original, shadow);
-                        if (r < 0)
-                                goto finish;
+        original = fopen(gshadow_path, "re");
+        if (original) {
+                struct sgrp *sg;
 
-                        errno = 0;
-                        while ((sp = fgetspent(original))) {
+                r = sync_rights(original, gshadow);
+                if (r < 0)
+                        goto fail;
 
-                                i = hashmap_get(users, sp->sp_namp);
-                                if (i && i->todo_user) {
-                                        /* we will update the existing entry */
-                                        sp->sp_lstchg = lstchg;
+                errno = 0;
+                while ((sg = fgetsgent(original))) {
 
-                                        /* only the /etc/shadow stage is left, so we can
-                                         * safely remove the item from the todo set */
-                                        i->todo_user = false;
-                                        hashmap_remove(todo_uids, UID_TO_PTR(i->uid));
-                                }
+                        i = hashmap_get(groups, sg->sg_namp);
+                        if (i && i->todo_group) {
+                                log_error("%s: Group \"%s\" already exists.", gshadow_path, sg->sg_namp);
+                                r = -EEXIST;
+                                goto fail;
+                        }
 
-                                errno = 0;
-                                if (putspent(sp, shadow) < 0) {
-                                        r = errno ? -errno : -EIO;
-                                        goto finish;
-                                }
+                        r = putsgent_with_members(sg, gshadow);
+                        if (r < 0)
+                                goto fail;
+                        if (r > 0)
+                                group_changed = true;
 
-                                errno = 0;
-                        }
-                        if (!IN_SET(errno, 0, ENOENT)) {
-                                r = -errno;
-                                goto finish;
-                        }
-                } else if (errno != ENOENT) {
-                        r = -errno;
-                        goto finish;
-                } else if (fchmod(fileno(shadow), 0000) < 0) {
+                        errno = 0;
+                }
+                if (!IN_SET(errno, 0, ENOENT)) {
                         r = -errno;
-                        goto finish;
+                        goto fail;
                 }
 
-                HASHMAP_FOREACH(i, todo_uids, iterator) {
-                        struct spwd n = {
-                                .sp_namp = i->name,
-                                .sp_pwdp = (char*) "!!",
-                                .sp_lstchg = lstchg,
-                                .sp_min = -1,
-                                .sp_max = -1,
-                                .sp_warn = -1,
-                                .sp_inact = -1,
-                                .sp_expire = -1,
-                                .sp_flag = (unsigned long) -1, /* this appears to be what everybody does ... */
-                        };
+        } else if (errno != ENOENT) {
+                r = -errno;
+                goto fail;
+        } else if (fchmod(fileno(gshadow), 0000) < 0) {
+                r = -errno;
+                goto fail;
+        }
 
-                        errno = 0;
-                        if (putspent(&n, shadow) != 0) {
-                                r = errno ? -errno : -EIO;
-                                goto finish;
-                        }
-                }
+        HASHMAP_FOREACH(i, todo_gids, iterator) {
+                struct sgrp n = {
+                        .sg_namp = i->name,
+                        .sg_passwd = (char*) "!!",
+                };
 
-                r = fflush_and_check(shadow);
+                r = putsgent_with_members(&n, gshadow);
                 if (r < 0)
-                        goto finish;
+                        goto fail;
+
+                group_changed = true;
         }
 
-        /* Make a backup of the old files */
+        r = fflush_and_check(gshadow);
+        if (r < 0)
+                goto fail;
+
         if (group_changed) {
-                if (group) {
-                        r = make_backup("/etc/group", group_path);
-                        if (r < 0)
-                                goto finish;
-                }
-                if (gshadow) {
-                        r = make_backup("/etc/gshadow", gshadow_path);
-                        if (r < 0)
-                                goto finish;
-                }
+                *tmpfile = gshadow;
+                *tmpfile_path = gshadow_tmp;
+                gshadow = NULL;
+                gshadow_tmp = NULL;
+        }
+        return 0;
+
+fail:
+        unlink(gshadow_tmp);
+        return r;
+}
+
+static int write_files(void) {
+
+        _cleanup_fclose_ FILE *passwd = NULL, *group = NULL, *shadow = NULL, *gshadow = NULL;
+        _cleanup_free_ char *passwd_tmp = NULL, *group_tmp = NULL, *shadow_tmp = NULL, *gshadow_tmp = NULL;
+        const char *passwd_path = NULL, *group_path = NULL, *shadow_path = NULL, *gshadow_path = NULL;
+        int r;
+
+        passwd_path = prefix_roota(arg_root, "/etc/passwd");
+        shadow_path = prefix_roota(arg_root, "/etc/shadow");
+        group_path = prefix_roota(arg_root, "/etc/group");
+        gshadow_path = prefix_roota(arg_root, "/etc/gshadow");
+
+        r = write_temporary_group(group_path, &group, &group_tmp);
+        if (r < 0)
+                goto finish;
+
+        r = write_temporary_gshadow(gshadow_path, &gshadow, &gshadow_tmp);
+        if (r < 0)
+                goto finish;
+
+        r = write_temporary_passwd(passwd_path, &passwd, &passwd_tmp);
+        if (r < 0)
+                goto finish;
+
+        r = write_temporary_shadow(shadow_path, &shadow, &shadow_tmp);
+        if (r < 0)
+                goto finish;
+
+        /* Make a backup of the old files */
+        if (group) {
+                r = make_backup("/etc/group", group_path);
+                if (r < 0)
+                        goto finish;
+        }
+        if (gshadow) {
+                r = make_backup("/etc/gshadow", gshadow_path);
+                if (r < 0)
+                        goto finish;
         }
 
         if (passwd) {
@@ -719,21 +794,19 @@ static int write_files(void) {
         }
 
         /* And make the new files count */
-        if (group_changed) {
-                if (group) {
-                        r = rename_and_apply_smack(group_tmp, group_path);
-                        if (r < 0)
-                                goto finish;
+        if (group) {
+                r = rename_and_apply_smack(group_tmp, group_path);
+                if (r < 0)
+                        goto finish;
 
-                        group_tmp = mfree(group_tmp);
-                }
-                if (gshadow) {
-                        r = rename_and_apply_smack(gshadow_tmp, gshadow_path);
-                        if (r < 0)
-                                goto finish;
+                group_tmp = mfree(group_tmp);
+        }
+        if (gshadow) {
+                r = rename_and_apply_smack(gshadow_tmp, gshadow_path);
+                if (r < 0)
+                        goto finish;
 
-                        gshadow_tmp = mfree(gshadow_tmp);
-                }
+                gshadow_tmp = mfree(gshadow_tmp);
         }
 
         if (passwd) {