d6f424ae30507aa8989932f0eea2bf26e9d7dbe6
[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         lsetxattr (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 && name != end_names) {
186                 if (name[0] != '\0') {
187                     size = lgetxattr (from_path, name, NULL, 0);
188                     if(size > 0 &&
189                         (value = g_realloc (value, size)) &&
190                         lgetxattr (from_path, name, value, size) > 0) {
191
192                         if (lsetxattr (to_path, name, value, size, 0) != 0) {
193                             ret = FALSE;
194                             break;
195                         }
196                     }
197                 }
198                 name = strchr (name,'\0') + 1;
199             }
200             g_free (value);
201         }
202         g_free (names);
203     }
204 #endif
205
206     return ret;
207 }
208
209 /**
210  * gum_file_open_db_files:
211  * @source_file_path: (transfer none): the path to source file
212  * @dup_file_path: (transfer none): the path to duplicate file, created from
213  * the source file
214  * @source_file: (transfer none): the file pointer created when source file
215  * is opened in read mode
216  * @dup_file: (transfer none): the file pointer created when duplicate file is
217  * opened in write mode
218  * @error: (transfer none): the #GError which is set in case of an error
219  *
220  * Opens the source file @source_file_path in read mode. Then creates the
221  * duplicate file @dup_file_path in write mode and copies source file attributes
222  * to the duplicate file. Open file handles are set in @source_file and
223  * @dup_file.
224  *
225  * Returns: TRUE if successful, FALSE otherwise and @error is set.
226  */
227 gboolean
228 gum_file_open_db_files (
229         const gchar *source_file_path,
230         const gchar *dup_file_path,
231         FILE **source_file,
232         FILE **dup_file,
233         GError **error)
234 {
235     FILE *source = NULL;
236     FILE *dup = NULL;
237     gboolean retval = TRUE;
238
239     if (!source_file || !dup_file || !source_file_path || !dup_file_path) {
240         GUM_RETURN_WITH_ERROR(GUM_ERROR_FILE_OPEN, "Invalid arguments",
241             error, FALSE);
242     }
243
244     if (!(source = _open_file (source_file_path, "r"))) {
245         GUM_RETURN_WITH_ERROR (GUM_ERROR_FILE_OPEN, "Unable to open orig file",
246                 error, FALSE);
247     }
248
249     if (!(dup = _open_file (dup_file_path, "w+"))) {
250         GUM_SET_ERROR (GUM_ERROR_FILE_OPEN, "Unable to open new file",
251                 error, retval, FALSE);
252         goto _fail;
253     }
254
255     if (!_set_smack64_attr (dup_file_path,
256             GUM_CONFIG_GENERAL_SMACK64_NEW_FILES)) {
257         GUM_SET_ERROR (GUM_ERROR_FILE_ATTRIBUTE,
258                  "Unable to set smack file attributes", error, retval, FALSE);
259         goto _fail;
260     }
261
262     if (!_copy_file_attributes (source_file_path, dup_file_path)) {
263         GUM_SET_ERROR (GUM_ERROR_FILE_ATTRIBUTE,
264                 "Unable to get/set file attributes", error, retval, FALSE);
265         goto _fail;
266     }
267
268     *source_file = source;
269     *dup_file = dup;
270
271     return retval;
272
273 _fail:
274     if (source) fclose(source);
275     if (dup) {
276         fclose(dup);
277         g_unlink(dup_file_path);
278     }
279     *source_file = NULL;
280     *dup_file = NULL;
281
282     return FALSE;
283 }
284
285 /**
286  * gum_file_close_db_files:
287  * @source_file_path: (transfer none): the path to source file
288  * @dup_file_path: (transfer none): the path to duplicate file
289  * @source_file: (transfer none): the source file pointer
290  * @dup_file: (transfer none): the duplicate file pointer
291  * @error: (transfer none): the #GError which is set in case of an error
292  *
293  * Closes the duplicate file @dup_file_path after flushing all the data. Backup
294  * of the source file @source_file_path is created and duplicate file is renamed
295  * to as the source file.
296  *
297  * Returns: TRUE if successful, FALSE otherwise and @error is set.
298  */
299 gboolean
300 gum_file_close_db_files (
301         const gchar *source_file_path,
302         const gchar *dup_file_path,
303         FILE *source_file,
304         FILE *dup_file,
305         GError **error)
306 {
307     gboolean retval = TRUE;
308     gchar *old_file_path = NULL;
309
310     if (source_file) fclose (source_file);
311
312     if (!dup_file ||
313         fflush (dup_file) != 0 ||
314         fsync (fileno (dup_file)) != 0 ||
315         fclose (dup_file) != 0) {
316         GUM_SET_ERROR (GUM_ERROR_FILE_WRITE, "File write failure", error,
317                 retval, FALSE);
318         goto _fail;
319     }
320
321     if (!source_file_path) {
322         GUM_SET_ERROR(GUM_ERROR_FILE_WRITE, "null source file path", error,
323              retval, FALSE);
324         goto _fail;
325     }
326
327     if (!dup_file_path) {
328         GUM_RETURN_WITH_ERROR(GUM_ERROR_FILE_WRITE, "null dest file path",
329              error, FALSE);
330     }
331
332     if ((old_file_path = g_strdup_printf ("%s.old", source_file_path))) {
333         /* delete obsolote backup file if any */
334         g_unlink (old_file_path);
335         /* Move source file to old file and dup file as updated file */
336         if (link (source_file_path, old_file_path) != 0) {
337             WARN("Could not create a backup file for '%s'", source_file_path);
338         }
339     } else {
340         WARN("Could not create a backup file for '%s'", source_file_path);
341     }
342
343     if (g_rename (dup_file_path, source_file_path) != 0) {
344         GUM_SET_ERROR (GUM_ERROR_FILE_MOVE, "Unable to move file", error,
345                 retval, FALSE);
346         g_unlink(old_file_path);
347     }
348
349     g_free (old_file_path);
350
351 _fail:
352     if (dup_file_path) g_unlink (dup_file_path);
353
354     return retval;
355 }
356
357 /**
358  * gum_file_update:
359  * @object: (transfer none): the instance of #GObject; can be NULL
360  * @op: (transfer none): the #GumOpType operation to be done on file entry
361  * the source file
362  * @callback: (transfer none): the callback #GumFileUpdateCB to be invoked
363  * when the source and duplicate files are opened to be handled
364  * @source_file_path: (transfer none): the source file path
365  * @user_data: user data to be passed on to the @callback
366  * @error: (transfer none): the #GError which is set in case of an error
367  *
368  * Opens the files and invokes the callback to do the required operation.
369  * Finally files are flushed and closed.
370  *
371  * Returns: TRUE if successful, FALSE otherwise and @error is set.
372  */
373 gboolean
374 gum_file_update (
375         GObject *object,
376         GumOpType op,
377         GumFileUpdateCB callback,
378         const gchar *source_file_path,
379         gpointer user_data,
380         GError **error)
381 {
382     gboolean retval = TRUE;
383     FILE *source_file = NULL, *dup_file = NULL;
384     gchar *dup_file_path = NULL;
385
386     dup_file_path = g_strdup_printf ("%s-tmp.%lu", source_file_path,
387             (unsigned long)getpid ());
388     retval = gum_file_open_db_files (source_file_path, dup_file_path,
389             &source_file, &dup_file, error);
390     if (!retval) goto _finished;
391
392     /* Update, sync and close file */
393     if (!callback) {
394         GUM_SET_ERROR (GUM_ERROR_FILE_WRITE,
395                 "File write function not specified", error, retval, FALSE);
396         goto _close;
397     }
398
399     retval = (*callback) (object, op, source_file, dup_file, user_data, error);
400     if (!retval) {
401         goto _close;
402     }
403
404     retval = gum_file_close_db_files (source_file_path, dup_file_path,
405             source_file,  dup_file, error);
406
407     goto _finished;
408
409 _close:
410     if (!retval) {
411         if (dup_file) fclose (dup_file);
412         g_unlink (dup_file_path);
413         if (source_file) fclose (source_file);
414     }
415 _finished:
416     g_free (dup_file_path);
417
418     return retval;
419 }
420
421 /**
422  * gum_file_getpwnam:
423  * @username: (transfer none): name of the user
424  * @filename: (transfer none): path to the file
425  *
426  * Gets the passwd structure from the file based on username.
427  *
428  * Returns: (transfer full): passwd structure if successful, NULL otherwise.
429  */
430 struct passwd *
431 gum_file_getpwnam (
432         const gchar *username,
433         const gchar *filename)
434 {
435     struct passwd *pent = NULL;
436     FILE *fp = NULL;
437
438     if (!username || !filename) {
439         return NULL;
440     }
441
442     if (!(fp = _open_file (filename, "r"))) {
443         return NULL;
444     }
445     while ((pent = fgetpwent (fp)) != NULL) {
446         if(g_strcmp0 (username, pent->pw_name) == 0)
447             break;
448         pent = NULL;
449     }
450     fclose (fp);
451
452     return pent;
453 }
454
455 /**
456  * gum_file_getpwuid:
457  * @uid: user id
458  * @filename: (transfer none): path to the file
459  *
460  * Gets the passwd structure from the file based on uid.
461  *
462  * Returns: (transfer full): passwd structure if successful, NULL otherwise.
463  */
464 struct passwd *
465 gum_file_getpwuid (
466         uid_t uid,
467         const gchar *filename)
468 {
469     struct passwd *pent = NULL;
470     FILE *fp = NULL;
471
472     if (!filename) {
473         return NULL;
474     }
475
476     if (!(fp = _open_file (filename, "r"))) {
477         return NULL;
478     }
479
480     while ((pent = fgetpwent (fp)) != NULL) {
481         if(uid == pent->pw_uid)
482             break;
483         pent = NULL;
484     }
485     fclose (fp);
486
487     return pent;
488 }
489
490 /**
491  * gum_file_find_user_by_gid:
492  * @primary_gid: primary gid of the user
493  * @filename: (transfer none): path to the file
494  *
495  * Gets the passwd structure from the file based on the primary group id.
496  *
497  * Returns: (transfer full): passwd structure if successful, NULL otherwise.
498  */
499 struct passwd *
500 gum_file_find_user_by_gid (
501         gid_t primary_gid,
502         const gchar *filename)
503 {
504     struct passwd *pent = NULL;
505     FILE *fp = NULL;
506
507     if (!filename || primary_gid == GUM_GROUP_INVALID_GID) {
508         return NULL;
509     }
510
511     if (!(fp = _open_file (filename, "r"))) {
512         return NULL;
513     }
514
515     while ((pent = fgetpwent (fp)) != NULL) {
516         if(primary_gid == pent->pw_gid)
517             break;
518         pent = NULL;
519     }
520     fclose (fp);
521
522     return pent;
523 }
524
525 /**
526  * gum_file_getspnam:
527  * @username: (transfer none): name of the user
528  * @filename: (transfer none): path to the file
529  *
530  * Gets the spwd structure from the file based on the username.
531  *
532  * Returns: (transfer full): spwd structure if successful, NULL otherwise.
533  */
534 struct spwd *
535 gum_file_getspnam (
536         const gchar *username,
537         const gchar *filename)
538 {
539     struct spwd *spent = NULL;
540     FILE *fp = NULL;
541
542     if (!username || !filename) {
543         return NULL;
544     }
545
546     if (!(fp = _open_file (filename, "r"))) {
547         return NULL;
548     }
549
550     while ((spent = fgetspent (fp)) != NULL) {
551         if(g_strcmp0 (username, spent->sp_namp) == 0)
552             break;
553         spent = NULL;
554     }
555     fclose (fp);
556
557     return spent;
558 }
559
560 /**
561  * gum_file_getgrnam:
562  * @grname: (transfer none): name of the group
563  * @filename: (transfer none): path to the file
564  *
565  * Gets the group structure from the file based on the groupname @grname.
566  *
567  * Returns: (transfer full): group structure if successful, NULL otherwise.
568  */
569 struct group *
570 gum_file_getgrnam (
571         const gchar *grname,
572         const gchar *filename)
573 {
574     struct group *gent = NULL;
575     FILE *fp = NULL;
576
577     if (!grname || !filename) {
578         return NULL;
579     }
580
581     if (!(fp = _open_file (filename, "r"))) {
582         return NULL;
583     }
584     while ((gent = fgetgrent (fp)) != NULL) {
585         if(g_strcmp0 (grname, gent->gr_name) == 0)
586             break;
587         gent = NULL;
588     }
589     fclose (fp);
590
591     return gent;
592 }
593
594 /**
595  * gum_file_getgrgid:
596  * @gid: id of the group
597  * @filename: (transfer none): path to the file
598  *
599  * Gets the group structure from the file based on the gid.
600  *
601  * Returns: (transfer full): group structure if successful, NULL otherwise.
602  */
603 struct group *
604 gum_file_getgrgid (
605         gid_t gid,
606         const gchar *filename)
607 {
608     struct group *gent = NULL;
609     FILE *fp = NULL;
610
611     if (!filename) {
612         return NULL;
613     }
614
615     if (!(fp = _open_file (filename, "r"))) {
616         return NULL;
617     }
618     while ((gent = fgetgrent (fp)) != NULL) {
619         if(gid == gent->gr_gid)
620             break;
621         gent = NULL;
622     }
623     fclose (fp);
624
625     return gent;
626 }
627
628 /**
629  * gum_file_getsgnam:
630  * @grname: (transfer none): name of the group
631  * @filename: (transfer none): path to the file
632  *
633  * Gets the sgrp structure from the file based on the groupname @grname.
634  *
635  * Returns: (transfer full): sgrp structure if successful, NULL otherwise.
636  */
637 struct sgrp *
638 gum_file_getsgnam (
639         const gchar *grname,
640         const gchar *filename)
641 {
642     struct sgrp *sgent = NULL;
643     FILE *fp = NULL;
644
645     if (!grname || !filename) {
646         return NULL;
647     }
648
649     if (!g_file_test (filename, G_FILE_TEST_EXISTS) ||
650         !(fp = _open_file (filename, "r"))) {
651         return NULL;
652     }
653
654     while ((sgent = fgetsgent (fp)) != NULL) {
655         if(g_strcmp0 (grname, sgent->sg_namp) == 0)
656             break;
657         sgent = NULL;
658     }
659     fclose (fp);
660
661     return sgent;
662 }
663
664 /**
665  * gum_file_new_path:
666  * @dir: (transfer none): directory path
667  * @filename: (transfer none): name of the file
668  *
669  * Builds complete file path based on the @filename and @dir.
670  *
671  * Returns: (transfer full): the #GFile if successful, NULL otherwise.
672  */
673 GFile *
674 gum_file_new_path (
675                 const gchar *dir,
676                 const gchar *filename)
677 {
678         GFile *file = NULL;
679         gchar *fn = NULL;
680
681         if (!dir || !filename) {
682                 return NULL;
683         }
684
685         fn = g_build_filename (dir, filename, NULL);
686         if (fn) {
687                 file = g_file_new_for_path (fn);
688                 g_free (fn);
689         }
690
691         return file;
692 }
693
694 static gboolean
695 _copy_dir_recursively (
696         const gchar *src,
697         const gchar *dest,
698         uid_t uid,
699         gid_t gid,
700         guint umask,
701         GError **error)
702 {
703     gboolean retval = TRUE;
704     gboolean stop = FALSE;
705     const gchar *src_fname = NULL;
706     gchar *src_filepath = NULL, *dest_filepath = NULL;
707     GDir *src_dir = NULL;
708     struct stat stat_entry;
709
710     if (!src || !dest) {
711         GUM_RETURN_WITH_ERROR (GUM_ERROR_HOME_DIR_COPY_FAILURE,
712                 "Invalid directory path(s)", error, FALSE);
713     }
714
715     DBG ("copy directory %s -> %s", src, dest);
716     src_dir = g_dir_open (src, 0, NULL);
717     if (!src_dir) {
718         GUM_RETURN_WITH_ERROR (GUM_ERROR_HOME_DIR_COPY_FAILURE,
719                 "Invalid source directory path", error, FALSE);
720     }
721
722     while ((src_fname = g_dir_read_name (src_dir))) {
723         GError *err = NULL;
724         GFile *src_file = NULL, *dest_file = NULL;
725
726         src_filepath = g_build_filename (src, src_fname, NULL);
727         stop = (lstat(src_filepath, &stat_entry) != 0);
728         if (stop) goto _free_data;
729
730         dest_filepath = g_build_filename (dest, src_fname, NULL);
731         src_file = g_file_new_for_path (src_filepath);
732         dest_file = g_file_new_for_path (dest_filepath);
733
734         if (S_ISDIR (stat_entry.st_mode)) {
735             DBG ("copy directory %s", src_filepath);
736             gint mode = GUM_PERM & ~umask;
737             g_mkdir_with_parents (dest_filepath, mode);
738             stop = !_set_smack64_attr (dest_filepath,
739                     GUM_CONFIG_GENERAL_SMACK64_USER_FILES);
740             if (!stop)
741                 stop = !_copy_dir_recursively (src_filepath, dest_filepath, uid,
742                     gid, umask, NULL);
743         } else {
744             DBG ("copy file %s", src_filepath);
745             if (!g_file_copy (src_file, dest_file,
746                     G_FILE_COPY_OVERWRITE | G_FILE_COPY_NOFOLLOW_SYMLINKS, NULL,
747                     NULL, NULL, &err)) {
748                 WARN("File copy failure error %d:%s", err ? err->code : 0,
749                         err ? err->message : "");
750                 if (err) g_error_free (err);
751                 stop = TRUE;
752                 goto _free_data;
753             }
754             stop = !_set_smack64_attr (dest_filepath,
755                     GUM_CONFIG_GENERAL_SMACK64_USER_FILES);
756         }
757         if (!stop) stop = !_copy_file_attributes (src_filepath, dest_filepath);
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 = gum_config_new (NULL);
808         const gchar *skel_dir = NULL;
809
810         skel_dir = gum_config_get_string (config, GUM_CONFIG_GENERAL_SKEL_DIR);
811         g_object_unref (config);
812
813         if (!g_file_test (home_dir, G_FILE_TEST_EXISTS)) {
814             g_mkdir_with_parents (home_dir, mode);
815         }
816
817         if (!g_file_test (home_dir, G_FILE_TEST_IS_DIR)) {
818             GUM_RETURN_WITH_ERROR (GUM_ERROR_HOME_DIR_CREATE_FAILURE,
819                     "Home directory creation failure", error, FALSE);
820         }
821
822         if (!_set_smack64_attr (home_dir,
823                 GUM_CONFIG_GENERAL_SMACK64_USER_FILES)) {
824             GUM_RETURN_WITH_ERROR (GUM_ERROR_FILE_ATTRIBUTE,
825                      "Unable to set smack64 home dir attr", error, FALSE);
826         }
827
828         if (!_copy_file_attributes (skel_dir, home_dir)) {
829             GUM_RETURN_WITH_ERROR (GUM_ERROR_FILE_ATTRIBUTE,
830                     "Unable to get/set dir attributes", error, FALSE);
831         }
832
833         /* when run in test mode, user may not exist */
834 #ifdef ENABLE_TESTS
835         uid = getuid ();
836         gid = getgid ();
837 #endif
838         if (chown (home_dir, uid, gid) < 0) {
839             GUM_RETURN_WITH_ERROR (GUM_ERROR_HOME_DIR_CREATE_FAILURE,
840                     "Home directory chown failure", error, FALSE);
841         }
842
843         retval = _copy_dir_recursively (skel_dir, home_dir, uid, gid, umask,
844                 error);
845         }
846
847         return retval;
848 }
849
850 /**
851  * gum_file_delete_home_dir:
852  * @dir: (transfer none): the path to the directory
853  * @error: (transfer none): the #GError which is set in case of an error
854  *
855  * Deletes the directory and its sub-directories recursively.
856  *
857  * Returns: TRUE if successful, FALSE otherwise and @error is set.
858  */
859 gboolean
860 gum_file_delete_home_dir (
861         const gchar *dir,
862         GError **error)
863 {
864     GDir* gdir = NULL;
865     struct stat sent;
866
867     if (!dir || !(gdir = g_dir_open(dir, 0, NULL))) {
868         GUM_RETURN_WITH_ERROR (GUM_ERROR_HOME_DIR_DELETE_FAILURE,
869                 "Invalid home directory path", error, FALSE);
870     }
871
872     const gchar *fname = NULL;
873     gint retval = 0;
874     gchar *filepath = NULL;
875     while ((fname = g_dir_read_name (gdir)) != NULL) {
876         if (g_strcmp0 (fname, ".") == 0 ||
877             g_strcmp0 (fname, "..") == 0) {
878             continue;
879         }
880         retval = -1;
881         filepath = g_build_filename (dir, fname, NULL);
882         if (filepath) {
883             retval = lstat(filepath, &sent);
884             if (retval == 0) {
885                 /* recurse the directory */
886                 if (S_ISDIR (sent.st_mode)) {
887                     retval = (gint)!gum_file_delete_home_dir (filepath, error);
888                 } else {
889                     retval = g_remove (filepath);
890                 }
891             }
892             g_free (filepath);
893         }
894         if (retval != 0) {
895             g_dir_close (gdir);
896             GUM_RETURN_WITH_ERROR (GUM_ERROR_HOME_DIR_DELETE_FAILURE,
897                 "Unable to delete files in the directory", error, FALSE);
898         }
899     }
900     g_dir_close (gdir);
901
902     if (g_remove (dir) != 0) {
903         GUM_RETURN_WITH_ERROR (GUM_ERROR_HOME_DIR_DELETE_FAILURE,
904                 "Unable to delete home directory", error, FALSE);
905     }
906
907         return TRUE;
908 }