Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F102685547
D45348.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
5 KB
Referenced Files
None
Subscribers
None
D45348.diff
View Options
diff --git a/usr.sbin/pw/pw.8 b/usr.sbin/pw/pw.8
--- a/usr.sbin/pw/pw.8
+++ b/usr.sbin/pw/pw.8
@@ -741,6 +741,9 @@
the user, or symbolic links owned by anyone under the user's home directory.
Finally, after deleting all contents owned by the user only empty directories
will be removed.
+If the home directory is a ZFS dataset and has been emptied,
+the dataset will be destroyed.
+ZFS datasets within the home directory and snapshots are not handled.
If any additional cleanup work is required, this is left to the administrator.
.El
.Pp
@@ -1077,7 +1080,8 @@
.Xr passwd 5 ,
.Xr pw.conf 5 ,
.Xr pwd_mkdb 8 ,
-.Xr vipw 8
+.Xr vipw 8 ,
+.Xr zfs 8
.Sh HISTORY
The
.Nm
diff --git a/usr.sbin/pw/pw_user.c b/usr.sbin/pw/pw_user.c
--- a/usr.sbin/pw/pw_user.c
+++ b/usr.sbin/pw/pw_user.c
@@ -28,7 +28,7 @@
*/
#include <sys/param.h>
-#include <sys/types.h>
+#include <sys/wait.h>
#include <assert.h>
#include <ctype.h>
@@ -669,6 +669,7 @@
while ((e = readdir(d)) != NULL) {
struct stat st;
+ pid_t pid;
if (strncmp(e->d_name, ".lock", 5) != 0 &&
stat(e->d_name, &st) == 0 &&
@@ -679,11 +680,12 @@
e->d_name,
NULL
};
- if (posix_spawn(NULL, argv[0], NULL, NULL,
+ if (posix_spawn(&pid, argv[0], NULL, NULL,
(char *const *) argv, environ)) {
warn("Failed to execute '%s %s'",
argv[0], argv[1]);
- }
+ } else
+ (void) waitpid(pid, NULL, 0);
}
}
closedir(d);
@@ -919,11 +921,14 @@
"-r",
NULL
};
- if (posix_spawnp(NULL, argv[0], NULL, NULL,
+ pid_t pid;
+
+ if (posix_spawnp(&pid, argv[0], NULL, NULL,
(char *const *) argv, environ)) {
warn("Failed to execute '%s %s'",
argv[0], argv[1]);
- }
+ } else
+ (void) waitpid(pid, NULL, 0);
}
}
diff --git a/usr.sbin/pw/pwupd.h b/usr.sbin/pw/pwupd.h
--- a/usr.sbin/pw/pwupd.h
+++ b/usr.sbin/pw/pwupd.h
@@ -139,7 +139,7 @@
void copymkdir(int rootfd, char const * dir, int skelfd, mode_t mode, uid_t uid,
gid_t gid, int flags);
-void rm_r(int rootfd, char const * dir, uid_t uid);
+bool rm_r(int rootfd, char const * dir, uid_t uid);
__END_DECLS
#endif /* !_PWUPD_H */
diff --git a/usr.sbin/pw/rm_r.c b/usr.sbin/pw/rm_r.c
--- a/usr.sbin/pw/rm_r.c
+++ b/usr.sbin/pw/rm_r.c
@@ -26,35 +26,58 @@
* SUCH DAMAGE.
*/
+#include <sys/param.h>
+#include <sys/mount.h>
#include <sys/stat.h>
+#include <sys/wait.h>
#include <dirent.h>
+#include <err.h>
+#include <errno.h>
#include <fcntl.h>
+#include <libgen.h>
+#include <libutil.h>
+#include <spawn.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "pwupd.h"
-void
+static bool try_dataset_remove(const char *home);
+extern char **environ;
+
+/*
+ * "rm -r" a directory tree. If the top-level directory cannot be removed
+ * due to EBUSY, indicating that it is a ZFS dataset, and we have emptied
+ * it, destroy the dataset. Return true if any files or directories
+ * remain.
+ */
+bool
rm_r(int rootfd, const char *path, uid_t uid)
{
int dirfd;
DIR *d;
struct dirent *e;
struct stat st;
+ const char *fullpath;
+ bool skipped = false;
+ fullpath = path;
if (*path == '/')
path++;
dirfd = openat(rootfd, path, O_DIRECTORY);
if (dirfd == -1) {
- return;
+ return (true);
}
d = fdopendir(dirfd);
if (d == NULL) {
(void)close(dirfd);
- return;
+ return (true);
}
while ((e = readdir(d)) != NULL) {
if (strcmp(e->d_name, ".") == 0 || strcmp(e->d_name, "..") == 0)
@@ -62,16 +85,84 @@
if (fstatat(dirfd, e->d_name, &st, AT_SYMLINK_NOFOLLOW) != 0)
continue;
- if (S_ISDIR(st.st_mode))
- rm_r(dirfd, e->d_name, uid);
- else if (S_ISLNK(st.st_mode) || st.st_uid == uid)
+ if (S_ISDIR(st.st_mode)) {
+ if (rm_r(dirfd, e->d_name, uid) == true)
+ skipped = true;
+ } else if (S_ISLNK(st.st_mode) || st.st_uid == uid)
unlinkat(dirfd, e->d_name, 0);
+ else
+ skipped = true;
}
closedir(d);
if (fstatat(rootfd, path, &st, AT_SYMLINK_NOFOLLOW) != 0)
- return;
- if (S_ISLNK(st.st_mode))
- unlinkat(rootfd, path, 0);
- else if (st.st_uid == uid)
- unlinkat(rootfd, path, AT_REMOVEDIR);
+ return (skipped);
+ if (S_ISLNK(st.st_mode)) {
+ if (unlinkat(rootfd, path, 0) == -1)
+ skipped = true;
+ } else if (st.st_uid == uid) {
+ if (unlinkat(rootfd, path, AT_REMOVEDIR) == -1) {
+ if (errno == EBUSY && skipped == false)
+ skipped = try_dataset_remove(fullpath);
+ else
+ skipped = true;
+ }
+ } else
+ skipped = true;
+
+ return (skipped);
+}
+
+/*
+ * If the home directory is a ZFS dataset, attempt to destroy it.
+ * Return true if the dataset is not destroyed.
+ * This would be more straightforward as a shell script.
+ */
+static bool
+try_dataset_remove(const char *path)
+{
+ bool skipped = true;
+ struct statfs stat;
+ const char *argv[] = {
+ "/sbin/zfs",
+ "destroy",
+ NULL,
+ NULL
+ };
+ int status;
+ pid_t pid;
+
+ /* see if this is an absolute path (top-level directory) */
+ if (*path != '/')
+ return (skipped);
+ /* see if ZFS is loaded */
+ if (kld_isloaded("zfs") == 0)
+ return (skipped);
+ /* This won't work if root dir is not / (-R option) */
+ if (strcmp(conf.rootdir, "/") != 0) {
+ warnx("cannot destroy home dataset when -R was used");
+ return (skipped);
+ }
+ /* if so, find dataset name */
+ if (statfs(path, &stat) != 0) {
+ warn("statfs %s", path);
+ return (skipped);
+ }
+ /*
+ * Check that the path refers to the dataset itself,
+ * not a subdirectory.
+ */
+ if (strcmp(stat.f_mntonname, path) != 0)
+ return (skipped);
+ argv[2] = stat.f_mntfromname;
+ if ((skipped = posix_spawn(&pid, argv[0], NULL, NULL,
+ (char *const *) argv, environ)) != 0) {
+ warn("Failed to execute '%s %s %s'",
+ argv[0], argv[1], argv[2]);
+ } else {
+ if (waitpid(pid, &status, 0) != -1 && status != 0) {
+ warnx("'%s %s %s' exit status %d\n",
+ argv[0], argv[1], argv[2], status);
+ }
+ }
+ return (skipped);
}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sat, Nov 16, 8:42 PM (21 h, 2 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
14664240
Default Alt Text
D45348.diff (5 KB)
Attached To
Mode
D45348: pw userdel: destroy home dataset if empty
Attached
Detach File
Event Timeline
Log In to Comment