emul64: use public init method for gumd service
[platform/upstream/gumd.git] / src / common / gum-file.c
1 /* vi: set et sw=4 ts=4 cino=t0,(0: */
2 /* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 /*
4  * This file is part of gum
5  *
6  * Copyright (C) 2013 Intel Corporation.
7  *
8  * Contact: Imran Zaman <imran.zaman@intel.com>
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public
12  * License as published by the Free Software Foundation; either
13  * version 2.1 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful, but
16  * WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with this library; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
23  * 02110-1301 USA
24  */
25 #include "config.h"
26
27 #include <stdio.h>
28 #include <sys/stat.h>
29 #if defined HAVE_SYS_XATTR_H
30 #include <sys/xattr.h>
31 #endif
32 #include <linux/xattr.h>
33
34 #include <unistd.h>
35 #include <string.h>
36 #include <errno.h>
37 #include <glib/gstdio.h>
38
39 #include "common/gum-file.h"
40 #include "common/gum-string-utils.h"
41 #include "common/gum-defines.h"
42 #include "common/gum-log.h"
43 #include "common/gum-error.h"
44 #include "common/gum-config.h"
45
46 /**
47  * SECTION:gum-file
48  * @short_description: Utility functions for file handling
49  * @title: Gum File
50  * @include: gum/common/gum-file.h
51  *
52  * Below is the code snippet, which demonstrate how can file update function be
53  * used to add, remove or modify an entry in the user/group database file
54  * (e.g. /etc/passwd).
55  *
56  * |[
57  *
58  *   gboolean _custom_update_file_entries (GObject *obj, GumOpType op,
59  *      FILE *source_file, FILE *dup_file, gpointer user_data, GError **error)
60  *   {
61  *      //loop through the file entries and modify as per operation type
62  *      return TRUE;
63  *   }
64  *
65  *   void update_file_entries ()
66  *   {
67  *      GObject* obj = NULL;
68  *      GError *error = NULL;
69  *      const gchar *source_file = "/tmp/passwd";
70  *      gum_file_update (obj, GUM_OPTYPE_ADD,
71  *       (GumFileUpdateCB)_custom_update_file_entries, source_file, NULL,
72  *       &error);
73  *   }
74  *
75  * ]|
76  */
77
78 /**
79  * FILE:
80  *
81  * Data structure that contains information about file stream as defined in
82  * stdio.h.
83  */
84
85 /**
86  * GumOpType:
87  * @GUM_OPTYPE_ADD: add an entry
88  * @GUM_OPTYPE_DELETE: delete an entry
89  * @GUM_OPTYPE_MODIFY: modify an entry
90  *
91  * This enumeration lists the operations on file entry.
92  */
93
94 /**
95  * GumFileUpdateCB:
96  * @object: (transfer none): the instance of #GObject
97  * @op: (transfer none): the #GumOpType operation to be done on the file entry
98  * @source_file: (transfer none): the source file pointer
99  * @dup_file: (transfer none): the duplicate file pointer
100  * @user_data: the user data
101  * @error: (transfer none): the #GError which is set in case of an error
102  *
103  * Callback can be used for adding, deleting or modifying file entries. It is
104  * invoked in #gum_file_update function.
105  *
106  * Returns: TRUE if successful, FALSE otherwise and @error is set.
107  */
108
109 #define GUM_PERM 0777
110
111 static gboolean
112 _set_smack64_attr (
113         const gchar *path,
114         const gchar *key)
115 {
116 #if defined(HAVE_LSETXATTR)
117     GumConfig *config = NULL;
118     ssize_t len = 0;
119
120     config = gum_config_new (NULL);
121     const gchar *smack_label = gum_config_get_string (config, key);
122     if (smack_label) {
123         len = strlen (smack_label);
124         DBG ("Set smack label %s for path %s with len %d",smack_label, path,
125                 (int)len);
126     }
127     /*
128      * Set smack64 extended attribute (when provided in the config file)
129      */
130     if (smack_label &&
131         len > 0 &&
132         setxattr (path, XATTR_NAME_SMACK, smack_label, len, 0) != 0) {
133         g_object_unref (config);
134         return FALSE;
135     }
136     g_object_unref (config);
137 #endif
138     return TRUE;
139 }
140
141 static FILE *
142 _open_file (
143         const gchar *fn,
144         const gchar *mode)
145 {
146     FILE *fp = NULL;
147     if (!fn || !(fp = fopen (fn, mode))) {
148         if (!fp)
149             WARN ("Could not open file '%s', error: %s", fn, strerror(errno));
150         return NULL;
151     }
152     return fp;
153 }
154
155 static gboolean
156 _copy_file_attributes (
157         const gchar *from_path,
158         const gchar *to_path)
159 {
160     gboolean ret = TRUE;
161     ssize_t attrs_size = 0;
162     struct stat from_stat;
163
164     if (stat (from_path, &from_stat) < 0 ||
165         chmod (to_path, from_stat.st_mode) < 0 ||
166         chown (to_path, from_stat.st_uid, from_stat.st_gid) < 0) {
167         return FALSE;
168     }
169
170     /* copy extended attributes */
171 #if defined(HAVE_LLISTXATTR) && \
172     defined(HAVE_LGETXATTR) && \
173     defined(HAVE_LSETXATTR)
174     attrs_size = llistxattr (from_path, NULL, 0);
175     if (attrs_size > 0) {
176
177         gchar *names = g_new0 (gchar, attrs_size + 1);
178         if (names && llistxattr (from_path, names, attrs_size) > 0) {
179
180             gchar *name = names, *value = NULL;
181             gchar *end_names = names + attrs_size;
182             names[attrs_size] = '\0';
183             ssize_t size = 0;
184
185             while (name != end_names) {
186                 if (name)
187                     name = strchr (name,'\0')+1;
188                 if (name && name[0] != '\0') {
189                     size = lgetxattr (from_path, name, NULL, 0);
190                     if(size > 0 &&
191                         (value = g_realloc (value, size)) &&
192                         lgetxattr (from_path, name, value, size) > 0) {
193
194                         if (lsetxattr (to_path, name, value, size, 0) != 0) {
195                             ret = FALSE;
196                             break;
197                         }
198                     }
199                 }
200             }
201             g_free (value);
202         }
203         g_free (names);
204     }
205 #endif
206
207     return ret;
208 }
209
210 /**
211  * gum_file_open_db_files:
212  * @source_file_path: (transfer none): the path to source file
213  * @dup_file_path: (transfer none): the path to duplicate file, created from
214  * the source file
215  * @source_file: (transfer none): the file pointer created when source file
216  * is opened in read mode
217  * @dup_file: (transfer none): the file pointer created when duplicate file is
218  * opened in write mode
219  * @error: (transfer none): the #GError which is set in case of an error
220  *
221  * Opens the source file @source_file_path in read mode. Then creates the
222  * duplicate file @dup_file_path in write mode and copies source file attributes
223  * to the duplicate file. Open file handles are set in @source_file and
224  * @dup_file.
225  *
226  * Returns: TRUE if successful, FALSE otherwise and @error is set.
227  */
228 gboolean
229 gum_file_open_db_files (
230         const gchar *source_file_path,
231         const gchar *dup_file_path,
232         FILE **source_file,
233         FILE **dup_file,
234         GError **error)
235 {
236     FILE *source = NULL;
237     FILE *dup = NULL;
238     gboolean retval = TRUE;
239
240     if (!source_file || !dup_file || !source_file_path || !dup_file_path) {
241         GUM_RETURN_WITH_ERROR(GUM_ERROR_FILE_OPEN, "Invalid arguments",
242             error, FALSE);
243     }
244
245     if (!(source = _open_file (source_file_path, "r"))) {
246         GUM_RETURN_WITH_ERROR (GUM_ERROR_FILE_OPEN, "Unable to open orig file",
247                 error, FALSE);
248     }
249
250     if (!(dup = _open_file (dup_file_path, "w+"))) {
251         GUM_SET_ERROR (GUM_ERROR_FILE_OPEN, "Unable to open new file",
252                 error, retval, FALSE);
253         goto _fail;
254     }
255
256     if (!_copy_file_attributes (source_file_path, dup_file_path)) {
257         GUM_SET_ERROR (GUM_ERROR_FILE_ATTRIBUTE,
258                 "Unable to get/set file attributes", error, retval, FALSE);
259         goto _fail;
260     }
261
262     if (!_set_smack64_attr (dup_file_path,
263             GUM_CONFIG_GENERAL_SMACK64_NEW_FILES)) {
264         GUM_SET_ERROR (GUM_ERROR_FILE_ATTRIBUTE,
265                  "Unable to set smack file attributes", error, retval, FALSE);
266         goto _fail;
267     }
268
269     *source_file = source;
270     *dup_file = dup;
271
272     return retval;
273
274 _fail:
275     if (source) fclose(source);
276     if (dup) {
277         fclose(dup);
278         g_unlink(dup_file_path);
279     }
280     *source_file = NULL;
281     *dup_file = NULL;
282
283     return FALSE;
284 }
285
286 /**
287  * gum_file_close_db_files:
288  * @source_file_path: (transfer none): the path to source file
289  * @dup_file_path: (transfer none): the path to duplicate file
290  * @source_file: (transfer none): the source file pointer
291  * @dup_file: (transfer none): the duplicate file pointer
292  * @error: (transfer none): the #GError which is set in case of an error
293  *
294  * Closes the duplicate file @dup_file_path after flushing all the data. Backup
295  * of the source file @source_file_path is created and duplicate file is renamed
296  * to as the source file.
297  *
298  * Returns: TRUE if successful, FALSE otherwise and @error is set.
299  */
300 gboolean
301 gum_file_close_db_files (
302         const gchar *source_file_path,
303         const gchar *dup_file_path,
304         FILE *source_file,
305         FILE *dup_file,
306         GError **error)
307 {
308     gboolean retval = TRUE;
309     gchar *old_file_path = NULL;
310
311     if (source_file) fclose (source_file);
312
313     if (!dup_file ||
314         fflush (dup_file) != 0 ||
315         fsync (fileno (dup_file)) != 0 ||
316         fclose (dup_file) != 0) {
317         GUM_SET_ERROR (GUM_ERROR_FILE_WRITE, "File write failure", error,
318                 retval, FALSE);
319         goto _fail;
320     }
321
322     if (!source_file_path) {
323         GUM_SET_ERROR(GUM_ERROR_FILE_WRITE, "null source file path", error,
324              retval, FALSE);
325         goto _fail;
326     }
327
328     if (!dup_file_path) {
329         GUM_RETURN_WITH_ERROR(GUM_ERROR_FILE_WRITE, "null dest file path",
330              error, FALSE);
331     }
332
333     if ((old_file_path = g_strdup_printf ("%s.old", source_file_path))) {
334         /* delete obsolote backup file if any */
335         g_unlink (old_file_path);
336         /* Move source file to old file and dup file as updated file */
337         if (link (source_file_path, old_file_path) != 0) {
338             WARN("Could not create a backup file for '%s'", source_file_path);
339         }
340     } else {
341         WARN("Could not create a backup file for '%s'", source_file_path);
342     }
343
344     if (g_rename (dup_file_path, source_file_path) != 0) {
345         GUM_SET_ERROR (GUM_ERROR_FILE_MOVE, "Unable to move file", error,
346                 retval, FALSE);
347         g_unlink(old_file_path);
348     }
349
350     g_free (old_file_path);
351
352 _fail:
353     if (dup_file_path) g_unlink (dup_file_path);
354
355     return retval;
356 }
357
358 /**
359  * gum_file_update:
360  * @object: (transfer none): the instance of #GObject; can be NULL
361  * @op: (transfer none): the #GumOpType operation to be done on file entry
362  * the source file
363  * @callback: (transfer none): the callback #GumFileUpdateCB to be invoked
364  * when the source and duplicate files are opened to be handled
365  * @source_file_path: (transfer none): the source file path
366  * @user_data: user data to be passed on to the @callback
367  * @error: (transfer none): the #GError which is set in case of an error
368  *
369  * Opens the files and invokes the callback to do the required operation.
370  * Finally files are flushed and closed.
371  *
372  * Returns: TRUE if successful, FALSE otherwise and @error is set.
373  */
374 gboolean
375 gum_file_update (
376         GObject *object,
377         GumOpType op,
378         GumFileUpdateCB callback,
379         const gchar *source_file_path,
380         gpointer user_data,
381         GError **error)
382 {
383     gboolean retval = TRUE;
384     FILE *source_file = NULL, *dup_file = NULL;
385     gchar *dup_file_path = NULL;
386
387     dup_file_path = g_strdup_printf ("%s-tmp.%lu", source_file_path,
388             (unsigned long)getpid ());
389     retval = gum_file_open_db_files (source_file_path, dup_file_path,
390             &source_file, &dup_file, error);
391     if (!retval) goto _finished;
392
393     /* Update, sync and close file */
394     if (!callback) {
395         GUM_SET_ERROR (GUM_ERROR_FILE_WRITE,
396                 "File write function not specified", error, retval, FALSE);
397         goto _close;
398     }
399
400     retval = (*callback) (object, op, source_file, dup_file, user_data, error);
401     if (!retval) {
402         goto _close;
403     }
404
405     retval = gum_file_close_db_files (source_file_path, dup_file_path,
406             source_file,  dup_file, error);
407
408     goto _finished;
409
410 _close:
411     if (!retval) {
412         if (dup_file) fclose (dup_file);
413         g_unlink (dup_file_path);
414         if (source_file) fclose (source_file);
415     }
416 _finished:
417     g_free (dup_file_path);
418
419     return retval;
420 }
421
422 /**
423  * gum_file_getpwnam:
424  * @username: (transfer none): name of the user
425  * @filename: (transfer none): path to the file
426  *
427  * Gets the passwd structure from the file based on username.
428  *
429  * Returns: (transfer full): passwd structure if successful, NULL otherwise.
430  */
431 struct passwd *
432 gum_file_getpwnam (
433         const gchar *username,
434         const gchar *filename)
435 {
436     struct passwd *pent = NULL;
437     FILE *fp = NULL;
438
439     if (!username || !filename) {
440         return NULL;
441     }
442
443     if (!(fp = _open_file (filename, "r"))) {
444         return NULL;
445     }
446     while ((pent = fgetpwent (fp)) != NULL) {
447         if(g_strcmp0 (username, pent->pw_name) == 0)
448             break;
449         pent = NULL;
450     }
451     fclose (fp);
452
453     return pent;
454 }
455
456 /**
457  * gum_file_getpwuid:
458  * @uid: user id
459  * @filename: (transfer none): path to the file
460  *
461  * Gets the passwd structure from the file based on uid.
462  *
463  * Returns: (transfer full): passwd structure if successful, NULL otherwise.
464  */
465 struct passwd *
466 gum_file_getpwuid (
467         uid_t uid,
468         const gchar *filename)
469 {
470     struct passwd *pent = NULL;
471     FILE *fp = NULL;
472
473     if (!filename) {
474         return NULL;
475     }
476
477     if (!(fp = _open_file (filename, "r"))) {
478         return NULL;
479     }
480
481     while ((pent = fgetpwent (fp)) != NULL) {
482         if(uid == pent->pw_uid)
483             break;
484         pent = NULL;
485     }
486     fclose (fp);
487
488     return pent;
489 }
490
491 /**
492  * gum_file_find_user_by_gid:
493  * @primary_gid: primary gid of the user
494  * @filename: (transfer none): path to the file
495  *
496  * Gets the passwd structure from the file based on the primary group id.
497  *
498  * Returns: (transfer full): passwd structure if successful, NULL otherwise.
499  */
500 struct passwd *
501 gum_file_find_user_by_gid (
502         gid_t primary_gid,
503         const gchar *filename)
504 {
505     struct passwd *pent = NULL;
506     FILE *fp = NULL;
507
508     if (!filename || primary_gid == GUM_GROUP_INVALID_GID) {
509         return NULL;
510     }
511
512     if (!(fp = _open_file (filename, "r"))) {
513         return NULL;
514     }
515
516     while ((pent = fgetpwent (fp)) != NULL) {
517         if(primary_gid == pent->pw_gid)
518             break;
519         pent = NULL;
520     }
521     fclose (fp);
522
523     return pent;
524 }
525
526 /**
527  * gum_file_getspnam:
528  * @username: (transfer none): name of the user
529  * @filename: (transfer none): path to the file
530  *
531  * Gets the spwd structure from the file based on the username.
532  *
533  * Returns: (transfer full): spwd structure if successful, NULL otherwise.
534  */
535 struct spwd *
536 gum_file_getspnam (
537         const gchar *username,
538         const gchar *filename)
539 {
540     struct spwd *spent = NULL;
541     FILE *fp = NULL;
542
543     if (!username || !filename) {
544         return NULL;
545     }
546
547     if (!(fp = _open_file (filename, "r"))) {
548         return NULL;
549     }
550
551     while ((spent = fgetspent (fp)) != NULL) {
552         if(g_strcmp0 (username, spent->sp_namp) == 0)
553             break;
554         spent = NULL;
555     }
556     fclose (fp);
557
558     return spent;
559 }
560
561 /**
562  * gum_file_getgrnam:
563  * @grname: (transfer none): name of the group
564  * @filename: (transfer none): path to the file
565  *
566  * Gets the group structure from the file based on the groupname @grname.
567  *
568  * Returns: (transfer full): group structure if successful, NULL otherwise.
569  */
570 struct group *
571 gum_file_getgrnam (
572         const gchar *grname,
573         const gchar *filename)
574 {
575     struct group *gent = NULL;
576     FILE *fp = NULL;
577
578     if (!grname || !filename) {
579         return NULL;
580     }
581
582     if (!(fp = _open_file (filename, "r"))) {
583         return NULL;
584     }
585     while ((gent = fgetgrent (fp)) != NULL) {
586         if(g_strcmp0 (grname, gent->gr_name) == 0)
587             break;
588         gent = NULL;
589     }
590     fclose (fp);
591
592     return gent;
593 }
594
595 /**
596  * gum_file_getgrgid:
597  * @gid: id of the group
598  * @filename: (transfer none): path to the file
599  *
600  * Gets the group structure from the file based on the gid.
601  *
602  * Returns: (transfer full): group structure if successful, NULL otherwise.
603  */
604 struct group *
605 gum_file_getgrgid (
606         gid_t gid,
607         const gchar *filename)
608 {
609     struct group *gent = NULL;
610     FILE *fp = NULL;
611
612     if (!filename) {
613         return NULL;
614     }
615
616     if (!(fp = _open_file (filename, "r"))) {
617         return NULL;
618     }
619     while ((gent = fgetgrent (fp)) != NULL) {
620         if(gid == gent->gr_gid)
621             break;
622         gent = NULL;
623     }
624     fclose (fp);
625
626     return gent;
627 }
628
629 /**
630  * gum_file_getsgnam:
631  * @grname: (transfer none): name of the group
632  * @filename: (transfer none): path to the file
633  *
634  * Gets the sgrp structure from the file based on the groupname @grname.
635  *
636  * Returns: (transfer full): sgrp structure if successful, NULL otherwise.
637  */
638 struct sgrp *
639 gum_file_getsgnam (
640         const gchar *grname,
641         const gchar *filename)
642 {
643     struct sgrp *sgent = NULL;
644     FILE *fp = NULL;
645
646     if (!grname || !filename) {
647         return NULL;
648     }
649
650     if (!g_file_test (filename, G_FILE_TEST_EXISTS) ||
651         !(fp = _open_file (filename, "r"))) {
652         return NULL;
653     }
654
655     while ((sgent = fgetsgent (fp)) != NULL) {
656         if(g_strcmp0 (grname, sgent->sg_namp) == 0)
657             break;
658         sgent = NULL;
659     }
660     fclose (fp);
661
662     return sgent;
663 }
664
665 /**
666  * gum_file_new_path:
667  * @dir: (transfer none): directory path
668  * @filename: (transfer none): name of the file
669  *
670  * Builds complete file path based on the @filename and @dir.
671  *
672  * Returns: (transfer full): the #GFile if successful, NULL otherwise.
673  */
674 GFile *
675 gum_file_new_path (
676                 const gchar *dir,
677                 const gchar *filename)
678 {
679         GFile *file = NULL;
680         gchar *fn = NULL;
681
682         if (!dir || !filename) {
683                 return NULL;
684         }
685
686         fn = g_build_filename (dir, filename, NULL);
687         if (fn) {
688                 file = g_file_new_for_path (fn);
689                 g_free (fn);
690         }
691
692         return file;
693 }
694
695 static gboolean
696 _copy_dir_recursively (
697         const gchar *src,
698         const gchar *dest,
699         uid_t uid,
700         gid_t gid,
701         guint umask,
702         GError **error)
703 {
704     gboolean retval = TRUE;
705     gboolean stop = FALSE;
706     const gchar *src_fname = NULL;
707     gchar *src_filepath = NULL, *dest_filepath = NULL;
708     GDir *src_dir = NULL;
709     struct stat stat_entry;
710
711     if (!src || !dest) {
712         GUM_RETURN_WITH_ERROR (GUM_ERROR_HOME_DIR_COPY_FAILURE,
713                 "Invalid directory path(s)", error, FALSE);
714     }
715
716     DBG ("copy directory %s -> %s", src, dest);
717     src_dir = g_dir_open (src, 0, NULL);
718     if (!src_dir) {
719         GUM_RETURN_WITH_ERROR (GUM_ERROR_HOME_DIR_COPY_FAILURE,
720                 "Invalid source directory path", error, FALSE);
721     }
722
723     while ((src_fname = g_dir_read_name (src_dir))) {
724         GError *err = NULL;
725         GFile *src_file = NULL, *dest_file = NULL;
726
727         src_filepath = g_build_filename (src, src_fname, NULL);
728         stop = (lstat(src_filepath, &stat_entry) != 0);
729         if (stop) goto _free_data;
730
731         dest_filepath = g_build_filename (dest, src_fname, NULL);
732         src_file = g_file_new_for_path (src_filepath);
733         dest_file = g_file_new_for_path (dest_filepath);
734
735         if (S_ISDIR (stat_entry.st_mode)) {
736             DBG ("copy directory %s", src_filepath);
737             gint mode = GUM_PERM & ~umask;
738             g_mkdir_with_parents (dest_filepath, mode);
739             stop = !_set_smack64_attr (dest_filepath,
740                     GUM_CONFIG_GENERAL_SMACK64_USER_FILES);
741             if (!stop)
742                 stop = !_copy_dir_recursively (src_filepath, dest_filepath, uid,
743                     gid, umask, NULL);
744         } else {
745             DBG ("copy file %s", src_filepath);
746             if (!g_file_copy (src_file, dest_file,
747                     G_FILE_COPY_OVERWRITE | G_FILE_COPY_NOFOLLOW_SYMLINKS, NULL,
748                     NULL, NULL, &err)) {
749                 WARN("File copy failure error %d:%s", err ? err->code : 0,
750                         err ? err->message : "");
751                 if (err) g_error_free (err);
752                 stop = TRUE;
753                 goto _free_data;
754             }
755             stop = !_set_smack64_attr (dest_filepath,
756                     GUM_CONFIG_GENERAL_SMACK64_USER_FILES);
757         }
758         if (!stop) stop = (chown (dest_filepath, uid, gid) < 0);
759
760 _free_data:
761         g_free (src_filepath);
762         g_free (dest_filepath);
763         GUM_OBJECT_UNREF (src_file);
764         GUM_OBJECT_UNREF (dest_file);
765         if (stop) {
766             GUM_SET_ERROR (GUM_ERROR_HOME_DIR_COPY_FAILURE,
767                     "Home directory copy failure", error, retval, FALSE);
768             break;
769         }
770     }
771
772     g_dir_close (src_dir);
773     return retval;
774 }
775
776 /**
777  * gum_file_create_home_dir:
778  * @home_dir: path to the user home directory
779  * @uid: id of the user
780  * @gid: group id of the user
781  * @umask: the umask to be used for setting the mode of the files/directories
782  * @error: (transfer none): the #GError which is set in case of an error
783  *
784  * Creates the home directory of the user. All the files from the
785  * #GUM_CONFIG_GENERAL_SKEL_DIR are copied (recursively) to the user home
786  * directory.
787  *
788  * Returns: TRUE if successful, FALSE otherwise and @error is set.
789  */
790 gboolean
791 gum_file_create_home_dir (
792         const gchar *home_dir,
793         uid_t uid,
794         gid_t gid,
795         guint umask,
796         GError **error)
797 {
798         gboolean retval = TRUE;
799         gint mode = GUM_PERM & ~umask;
800
801     if (!home_dir) {
802         GUM_RETURN_WITH_ERROR (GUM_ERROR_HOME_DIR_CREATE_FAILURE,
803                 "Invalid home directory path", error, FALSE);
804     }
805
806     if (g_access (home_dir, F_OK) != 0) {
807         GumConfig *config = NULL;
808
809         if (!g_file_test (home_dir, G_FILE_TEST_EXISTS)) {
810             g_mkdir_with_parents (home_dir, mode);
811         }
812
813         if (!g_file_test (home_dir, G_FILE_TEST_IS_DIR)) {
814             GUM_RETURN_WITH_ERROR (GUM_ERROR_HOME_DIR_CREATE_FAILURE,
815                     "Home directory creation failure", error, FALSE);
816         }
817
818         if (!_set_smack64_attr (home_dir,
819                 GUM_CONFIG_GENERAL_SMACK64_USER_FILES)) {
820             GUM_RETURN_WITH_ERROR (GUM_ERROR_FILE_ATTRIBUTE,
821                      "Unable to set smack64 home dir attr", error, FALSE);
822         }
823
824         /* when run in test mode, user may not exist */
825 #ifdef ENABLE_TESTS
826         uid = getuid ();
827         gid = getgid ();
828 #endif
829         if (chown (home_dir, uid, gid) < 0) {
830             GUM_RETURN_WITH_ERROR (GUM_ERROR_HOME_DIR_CREATE_FAILURE,
831                     "Home directory chown failure", error, FALSE);
832         }
833
834         config = gum_config_new (NULL);
835         retval = _copy_dir_recursively (gum_config_get_string (config,
836             GUM_CONFIG_GENERAL_SKEL_DIR), home_dir, uid, gid, umask, error);
837         g_object_unref (config);
838
839         }
840
841         return retval;
842 }
843
844 /**
845  * gum_file_delete_home_dir:
846  * @dir: (transfer none): the path to the directory
847  * @error: (transfer none): the #GError which is set in case of an error
848  *
849  * Deletes the directory and its sub-directories recursively.
850  *
851  * Returns: TRUE if successful, FALSE otherwise and @error is set.
852  */
853 gboolean
854 gum_file_delete_home_dir (
855         const gchar *dir,
856         GError **error)
857 {
858     GDir* gdir = NULL;
859     struct stat sent;
860
861     if (!dir || !(gdir = g_dir_open(dir, 0, NULL))) {
862         GUM_RETURN_WITH_ERROR (GUM_ERROR_HOME_DIR_DELETE_FAILURE,
863                 "Invalid home directory path", error, FALSE);
864     }
865
866     const gchar *fname = NULL;
867     gint retval = 0;
868     gchar *filepath = NULL;
869     while ((fname = g_dir_read_name (gdir)) != NULL) {
870         if (g_strcmp0 (fname, ".") == 0 ||
871             g_strcmp0 (fname, "..") == 0) {
872             continue;
873         }
874         retval = -1;
875         filepath = g_build_filename (dir, fname, NULL);
876         if (filepath) {
877             retval = lstat(filepath, &sent);
878             if (retval == 0) {
879                 /* recurse the directory */
880                 if (S_ISDIR (sent.st_mode)) {
881                     retval = (gint)!gum_file_delete_home_dir (filepath, error);
882                 } else {
883                     retval = g_remove (filepath);
884                 }
885             }
886             g_free (filepath);
887         }
888         if (retval != 0) {
889             g_dir_close (gdir);
890             GUM_RETURN_WITH_ERROR (GUM_ERROR_HOME_DIR_DELETE_FAILURE,
891                 "Unable to delete files in the directory", error, FALSE);
892         }
893     }
894     g_dir_close (gdir);
895
896     if (g_remove (dir) != 0) {
897         GUM_RETURN_WITH_ERROR (GUM_ERROR_HOME_DIR_DELETE_FAILURE,
898                 "Unable to delete home directory", error, FALSE);
899     }
900
901         return TRUE;
902 }