summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorColin Walters <walters@verbum.org>2013-07-18 14:19:57 -0400
committerColin Walters <walters@verbum.org>2013-07-18 14:19:57 -0400
commitf56702ef40a5df056097d2e14ee0dac3614b744c (patch)
treeda1baaef999c6a05203dea195fdf4bc4b9b2d873
parent939cd18d39b1abf39543c83c406246b12dbfa03a (diff)
downloadlibgsystem-f56702ef40a5df056097d2e14ee0dac3614b744c.tar.gz
fileutils: Add gs_file_create_with_uidgid()
If the provided mode includes S_ISUID or S_ISGID, we will successfully invoke fchmod(), but then a subsequent chown() will undo any setuid bits, even if we're just calling chown for the already extant uid. This API can reliably create a file with the provided mode and owner.
-rw-r--r--gsystem-file-utils.c76
-rw-r--r--gsystem-file-utils.h8
2 files changed, 84 insertions, 0 deletions
diff --git a/gsystem-file-utils.c b/gsystem-file-utils.c
index 31e7331..5510948 100644
--- a/gsystem-file-utils.c
+++ b/gsystem-file-utils.c
@@ -252,6 +252,82 @@ gs_file_sync_data (GFile *file,
}
/**
+ * gs_file_create_with_uidgid:
+ * @file: Path of file to create
+ * @mode: Unix mode
+ * @uid: Unix uid
+ * @gid: Unix gid
+ * @out_stream: (out) (transfer full) (allow-none): Output stream connected to file descriptor
+ * @cancellable: a #GCancellable
+ * @error: a #GError
+ *
+ * Create @file exclusively; it must not exist already. Ensure the
+ * returned file has mode @mode and has Unix owners corresponding to
+ * the parameters @uid and @gid.
+ *
+ * The parameter @out_stream if provided, will be filled in with a
+ * #GOutputStream connected to the file.
+ *
+ * Returns: %TRUE on success, %FALSE on error.
+ */
+gboolean
+gs_file_create_with_uidgid (GFile *file,
+ int mode,
+ uid_t uid,
+ gid_t gid,
+ GOutputStream **out_stream,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean ret = FALSE;
+ int fd;
+ GOutputStream *ret_stream = NULL;
+ static gsize uidgid_cached;
+ static uid_t myuid;
+ static uid_t mygid;
+
+ /* Ok yes this is lame, but calling these two over and over shows up
+ * in strace. I like my straces to be clean, shoot me.
+ */
+ if (g_once_init_enter (&uidgid_cached))
+ {
+ myuid = getuid ();
+ mygid = getgid ();
+ g_once_init_leave (&uidgid_cached, 1);
+ }
+
+ fd = open_nointr (gs_file_get_path_cached (file), O_WRONLY | O_CREAT | O_EXCL, mode);
+ if (fd < 0)
+ {
+ _set_error_from_errno (error);
+ goto out;
+ }
+
+ if (uid != myuid || gid != mygid)
+ {
+ if (fchown (fd, uid, gid) < 0)
+ {
+ _set_error_from_errno (error);
+ goto out;
+ }
+ }
+
+ if (fchmod (fd, mode) < 0)
+ {
+ _set_error_from_errno (error);
+ goto out;
+ }
+
+ ret_stream = g_unix_output_stream_new (fd, TRUE);
+
+ ret = TRUE;
+ gs_transfer_out_value (out_stream, &ret_stream);
+ out:
+ g_clear_object (&ret_stream);
+ return ret;
+}
+
+/**
* gs_file_create:
* @file: Path to non-existent file
* @mode: Unix access permissions
diff --git a/gsystem-file-utils.h b/gsystem-file-utils.h
index 26ee878..d8cd73d 100644
--- a/gsystem-file-utils.h
+++ b/gsystem-file-utils.h
@@ -61,6 +61,14 @@ gboolean gs_file_create (GFile *file,
GCancellable *cancellable,
GError **error);
+gboolean gs_file_create_with_uidgid (GFile *file,
+ int mode,
+ uid_t uid,
+ gid_t gid,
+ GOutputStream **out_stream,
+ GCancellable *cancellable,
+ GError **error);
+
gboolean gs_file_linkcopy (GFile *src,
GFile *dest,
GFileCopyFlags flags,