Put 'noauto' for the auto-generated NTFS '/etc/fstab' entries.
[captive.git] / src / install / fstab / main.c
1 /* $Id$
2  * /etc/fstab installation utility
3  * Copyright (C) 2003 Jan Kratochvil <project-captive@jankratochvil.net>
4  * 
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; exactly version 2 of June 1991 is required
8  * 
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  * 
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  */
18
19
20 #include "config.h"
21
22 #undef FALSE
23 #undef TRUE
24 #include <ntfs/types.h> /* for 'FALSE'/'TRUE' libntfs definition */
25 #define FALSE FALSE
26 #define TRUE TRUE
27
28 #include <glib/gmessages.h>
29 #include <popt.h>
30 #include <locale.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <mntent.h>
34 #include <glib/ghash.h>
35 #include <glib/gstrfuncs.h>
36 #include <sys/stat.h>
37 #include <errno.h>
38 #include <unistd.h>
39 #include "../libcaptive-install/proc_partitions.h"
40
41 #include <captive/macros.h>
42
43 #include <ntfs/volume.h>
44
45
46 /* Config: */
47 #define FILENAME_PROC_PARTITIONS  "/proc/partitions"
48 #define FILENAME_ETC_FSTAB        "/etc/fstab"
49 #define FILENAME_ETC_FSTAB_BACKUP FILENAME_ETC_FSTAB ".pre-captive"
50 #define FILENAME_ETC_FSTAB_TMP    FILENAME_ETC_FSTAB ".tmp"
51
52
53 static int optarg_verbose;
54 static int optarg_dry;
55 enum optarg_mode {
56                 OPTARG_MODE_UNDEF =0,
57                 OPTARG_MODE_ADD   =1,
58                 OPTARG_MODE_REMOVE=2,
59                 };
60 static int      /* Do not use 'enum optarg_mode' as 'poptOption.arg' is '(int *)'. */
61                 optarg_mode=OPTARG_MODE_UNDEF;
62
63 static const struct poptOption popt_table[]={
64 #define BUG_FSTAB_POPT(shortname,longname,argInfoP,argP,valP,descripP,argDescripP) \
65                 { \
66                         longName: (longname), \
67                         shortName: (shortname), \
68                         argInfo: (argInfoP)|(!(valP) ? 0 : POPT_ARG_VAL), \
69                         arg: (void *)argP, \
70                         val: (valP), \
71                         descrip: (descripP), \
72                         argDescrip: (argDescripP), \
73                 }
74
75                 BUG_FSTAB_POPT('v',"verbose",POPT_ARG_NONE,&optarg_verbose,0,N_("Display additional debug information"),NULL),
76                 BUG_FSTAB_POPT('n',"dry"    ,POPT_ARG_NONE,&optarg_dry    ,0,N_("No real modifications - simulate only"),NULL),
77                 BUG_FSTAB_POPT(0  ,"add"    ,POPT_ARG_NONE,&optarg_mode   ,OPTARG_MODE_ADD   ,N_("Add entries to /etc/fstab"),NULL),
78                 BUG_FSTAB_POPT(0  ,"remove" ,POPT_ARG_NONE,&optarg_mode   ,OPTARG_MODE_REMOVE,N_("Remove entries from /etc/fstab"),NULL),
79
80 #undef BUG_FSTAB_POPT
81                 POPT_AUTOHELP
82                 POPT_TABLEEND
83                 };
84
85
86 /* map: (gchar *)dir -> (gpointer)!NULL */
87 static GHashTable *dirs_used_hash;
88
89 static void dirs_used_hash_key_destroy_func(gchar *dir)
90 {
91         g_return_if_fail(dir!=NULL);
92
93         g_free(dir);
94 }
95
96
97 static void mntent_add_proc_partitions_ntfs_hash_entry
98                 (const gchar *device,const gchar *vol_name,FILE *mntentfilew /* user_data */)
99 {
100 struct mntent mntent_local;
101 gint dir_count;
102
103         g_return_if_fail(device!=NULL);
104         g_return_if_fail(vol_name!=NULL);
105         g_return_if_fail(mntentfilew!=NULL);
106
107         CAPTIVE_MEMZERO(&mntent_local);
108         mntent_local.mnt_fsname=(/* de-const */ gchar *)device;
109         mntent_local.mnt_dir=(/* de-const */ gchar *)captive_printf_alloca("/mnt/captive-%s",vol_name);
110         dir_count=1;
111         while (g_hash_table_lookup(dirs_used_hash,mntent_local.mnt_dir)) {
112                 dir_count++;
113                 mntent_local.mnt_dir=(/* de-const */ gchar *)captive_printf_alloca("/mnt/captive-%s%d",vol_name,dir_count);
114                 }
115         if (optarg_dry || !mkdir("/mnt",0755)) {
116                 if (optarg_verbose)
117                         g_message(_("Created base mount directory: %s"),"/mnt");
118                 }
119         else if (errno!=EEXIST)
120                 g_warning(_("Error creating base mount directory \"%s\" for device \"%s\": %m"),
121                                 "/mnt",mntent_local.mnt_fsname);
122         if (optarg_dry || !mkdir(mntent_local.mnt_dir,0755)) {
123                 if (optarg_verbose)
124                         g_message(_("Created mount directory for device \"%s\": %s"),
125                                         mntent_local.mnt_fsname,mntent_local.mnt_dir);
126                 }
127         else if (errno!=EEXIST)
128                 g_warning(_("Error creating mount directory \"%s\" for device \"%s\": %m"),
129                                 mntent_local.mnt_dir,mntent_local.mnt_fsname);
130         mntent_local.mnt_type="captive-ntfs";
131         mntent_local.mnt_opts="defaults,noauto";        /* 'mntent_local.mnt_opts' must be != NULL ! */
132         if (optarg_verbose)
133                 g_message(_("Creating captive-ntfs mntent: %s -> %s"),mntent_local.mnt_fsname,mntent_local.mnt_dir);
134         if (addmntent(mntentfilew,&mntent_local))
135                 g_warning(_("Error appending mntent for device \"%s\": %m"),mntent_local.mnt_fsname);
136         g_hash_table_insert(dirs_used_hash,g_strdup(mntent_local.mnt_dir),dirs_used_hash);
137 }
138
139
140 int main(int argc,char **argv)
141 {
142 poptContext context;
143 int errint;
144 FILE *mntentfiler,*mntentfilew;
145 gboolean modified=FALSE;
146
147         /* Initialize the i18n stuff */
148         setlocale(LC_ALL,"");
149         bindtextdomain(PACKAGE,LOCALEDIR);
150         textdomain(PACKAGE);
151
152         context=poptGetContext(
153                         PACKAGE,        /* name */
154                         argc,(/*en-const*/const char **)argv,   /* argc,argv */
155                         popt_table,     /* options */
156                         POPT_CONTEXT_POSIXMEHARDER);    /* flags; && !POPT_CONTEXT_KEEP_FIRST */
157         if (context==NULL) {
158                 g_assert_not_reached(); /* argument recognization args_error */
159                 return EXIT_FAILURE;
160                 }
161         errint=poptReadDefaultConfig(context,
162                         TRUE);  /* useEnv */
163         if (errint!=0) {
164                 g_assert_not_reached(); /* argument recognization args_error */
165                 return EXIT_FAILURE;
166                 }
167         errint=poptGetNextOpt(context);
168         if (errint!=-1) {
169                 g_assert_not_reached(); /* some non-callbacked argument reached */
170                 return EXIT_FAILURE;
171                 }
172         if (poptPeekArg(context)) {
173                 g_error(_("No arguments expected"));
174                 return EXIT_FAILURE;
175                 }
176         if (optarg_mode==OPTARG_MODE_UNDEF)
177                 g_error(_("No run mode specified"));
178
179         dirs_used_hash=g_hash_table_new_full(g_str_hash,g_str_equal,
180                         (GDestroyNotify)dirs_used_hash_key_destroy_func,
181                         (GDestroyNotify)NULL);
182
183         /* FIXME: locking! */
184         if (!(mntentfiler=setmntent(FILENAME_ETC_FSTAB,"r")))
185                 g_error(_("Cannot open \"%s\" for reading: %m"),FILENAME_ETC_FSTAB);
186         if (!(mntentfilew=setmntent((optarg_dry ? "/dev/null" : FILENAME_ETC_FSTAB_TMP),"w")))
187                 g_error(_("Cannot open \"%s\" for writing: %m"),FILENAME_ETC_FSTAB_TMP);
188
189         switch (optarg_mode) {
190
191                 case OPTARG_MODE_ADD: {
192 GHashTable *proc_partitions_ntfs_hash;
193 struct mntent *mntent;
194
195                         proc_partitions_ntfs_hash=proc_partitions_ntfs_hash_get(optarg_verbose);
196                         while ((mntent=getmntent(mntentfiler))) {
197                                 if (!g_hash_table_lookup(proc_partitions_ntfs_hash,mntent->mnt_fsname)) {
198                                         if (!strcmp(mntent->mnt_type,"captive-ntfs")
199                                                         && !strncmp(mntent->mnt_fsname,"/dev/",strlen("/dev/"))) {
200                                                 g_warning(_("Dropping no-longer valid captive filesystem mntent from \"%s\" of device: %s"),
201                                                                 FILENAME_ETC_FSTAB,mntent->mnt_fsname);
202                                                 modified=TRUE;
203                                                 continue;
204                                                 }
205                                         if (addmntent(mntentfilew,mntent))
206                                                 g_error(_("Error copying mntent for device \"%s\": %m"),mntent->mnt_fsname);
207                                         g_hash_table_insert(dirs_used_hash,g_strdup(mntent->mnt_dir),dirs_used_hash);
208                                         }
209                                 else {
210                                         /* Original mntent is dropped to be replaced by new one. */
211                                         if (optarg_verbose)
212                                                 g_message(_("Dropping captive mntent to be replaced by new one: %s"),mntent->mnt_fsname);
213                                         modified=TRUE;
214                                         }
215                                 }
216                         if (g_hash_table_size(proc_partitions_ntfs_hash))
217                                 modified=TRUE;
218                         g_hash_table_foreach(proc_partitions_ntfs_hash,
219                                         (GHFunc)mntent_add_proc_partitions_ntfs_hash_entry,     /* func */
220                                         mntentfilew);   /* user_data */
221                         g_hash_table_destroy(proc_partitions_ntfs_hash);
222                         } break;
223
224                 case OPTARG_MODE_REMOVE: {
225 struct mntent *mntent;
226
227                         while ((mntent=getmntent(mntentfiler))) {
228                                 if (!strcmp(mntent->mnt_type,"captive-ntfs")
229                                                 && !strncmp(mntent->mnt_fsname,"/dev/",strlen("/dev/"))) {
230                                         if (optarg_verbose)
231                                                 g_message(_("Dropping captive mntent: %s"),mntent->mnt_fsname);
232                                         if (optarg_dry || !rmdir(mntent->mnt_dir)) {
233                                                 if (optarg_verbose)
234                                                         g_message(_("Deleted mount directory for device \"%s\": %s"),
235                                                                         mntent->mnt_fsname,mntent->mnt_dir);
236                                                 }
237                                         else if (errno!=EEXIST)
238                                                 g_warning(_("Error removing mount directory \"%s\" of device \"%s\": %m"),
239                                                                 mntent->mnt_dir,mntent->mnt_fsname);
240                                         modified=TRUE;
241                                         continue;
242                                         }
243                                 if (addmntent(mntentfilew,mntent))
244                                         g_error(_("Error copying mntent for device \"%s\": %m"),mntent->mnt_fsname);
245                                 }
246                         } break;
247                 }
248
249         if (optarg_verbose)
250                 g_message(_("Modified status: %s"),(modified ? _("YES") : _("NO")));
251         if (1!=endmntent(mntentfiler))
252                 g_error(_("Cannot close \"%s\" after reading: %m"),FILENAME_ETC_FSTAB);
253         if (!optarg_dry) {
254                 if (fchmod(fileno(mntentfilew),0644))
255                         g_error(_("Cannot set permissions for \"%s\" after writing: %m"),FILENAME_ETC_FSTAB_TMP);
256                 }
257         if (1!=endmntent(mntentfilew))
258                 g_error(_("Cannot close \"%s\" after writing: %m"),FILENAME_ETC_FSTAB_TMP);
259         if (!optarg_dry) {
260                 if (modified) {
261                         if (!access(FILENAME_ETC_FSTAB_BACKUP,F_OK)) {
262                                 if (optarg_verbose)
263                                         g_message(_("Backup file exists - keeping it intact: %s"),FILENAME_ETC_FSTAB_BACKUP);
264                                 }
265                         else if (errno==ENOENT) {
266                                 if (optarg_verbose)
267                                         g_message(_("File \"%s\" backed up to: %s"),FILENAME_ETC_FSTAB,FILENAME_ETC_FSTAB_BACKUP);
268                                 if (rename(FILENAME_ETC_FSTAB,FILENAME_ETC_FSTAB_BACKUP))
269                                         g_warning(_("Cannot backup \"%s\" to \"%s\": %m"),FILENAME_ETC_FSTAB,FILENAME_ETC_FSTAB_BACKUP);
270                                 }
271                         else
272                                 g_warning(_("Backup file \"%s\" state unknown: %m"),FILENAME_ETC_FSTAB_BACKUP);
273                         if (rename(FILENAME_ETC_FSTAB_TMP,FILENAME_ETC_FSTAB))
274                                 g_error(_("Cannot move new \"%s\" over old \"%s\": %m"),FILENAME_ETC_FSTAB_TMP,FILENAME_ETC_FSTAB);
275                         }
276                 else {
277                         if (unlink(FILENAME_ETC_FSTAB_TMP))
278                                 g_error(_("Cannot remove new unmodified \"%s\": %m"),FILENAME_ETC_FSTAB_TMP);
279                         }
280                 }
281
282         return EXIT_SUCCESS;
283 }