2 * /etc/fstab installation utility
3 * Copyright (C) 2003 Jan Kratochvil <project-captive@jankratochvil.net>
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
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.
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
20 #define _GNU_SOURCE 1 /* for memrchr() */
26 #include <ntfs/types.h> /* for 'FALSE'/'TRUE' libntfs definition */
30 #include <glib/gmessages.h>
36 #include <glib/ghash.h>
37 #include <glib/gstrfuncs.h>
41 #include "../libcaptive-install/proc_partitions.h"
44 #include <captive/macros.h>
46 #include <ntfs/volume.h>
50 #define FILENAME_PROC_PARTITIONS "/proc/partitions"
51 #define FILENAME_ETC_FSTAB "/etc/fstab"
52 #define FILENAME_ETC_FSTAB_BACKUP FILENAME_ETC_FSTAB ".pre-captive"
53 #define FILENAME_ETC_FSTAB_TMP FILENAME_ETC_FSTAB ".tmp"
56 static int optarg_verbose;
57 static int optarg_dry;
63 static int /* Do not use 'enum optarg_mode' as 'poptOption.arg' is '(int *)'. */
64 optarg_mode=OPTARG_MODE_UNDEF;
66 static const struct poptOption popt_table[]={
67 #define BUG_FSTAB_POPT(shortname,longname,argInfoP,argP,valP,descripP,argDescripP) \
69 longName: (longname), \
70 shortName: (shortname), \
71 argInfo: (argInfoP)|(!(valP) ? 0 : POPT_ARG_VAL), \
74 descrip: (descripP), \
75 argDescrip: (argDescripP), \
78 BUG_FSTAB_POPT('v',"verbose",POPT_ARG_NONE,&optarg_verbose,0,N_("Display additional debug information"),NULL),
79 BUG_FSTAB_POPT('n',"dry" ,POPT_ARG_NONE,&optarg_dry ,0,N_("No real modifications - simulate only"),NULL),
80 BUG_FSTAB_POPT(0 ,"add" ,POPT_ARG_NONE,&optarg_mode ,OPTARG_MODE_ADD ,N_("Add entries to /etc/fstab"),NULL),
81 BUG_FSTAB_POPT(0 ,"remove" ,POPT_ARG_NONE,&optarg_mode ,OPTARG_MODE_REMOVE,N_("Remove entries from /etc/fstab"),NULL),
89 /* map: (gchar *)dir -> (gpointer)!NULL */
90 static GHashTable *dirs_used_hash;
92 static void dirs_used_hash_key_destroy_func(gchar *dir)
94 g_return_if_fail(dir!=NULL);
100 static void mntent_add_proc_partitions_ntfs_hash_entry
101 (const gchar *device,const gchar *vol_name,FILE *mntentfilew /* user_data */)
103 struct mntent mntent_local;
107 g_return_if_fail(device!=NULL);
108 g_return_if_fail(vol_name!=NULL);
109 g_return_if_fail(mntentfilew!=NULL);
111 vol_name=captive_strdup_alloca(vol_name);
112 for (s=(/* de-const */ gchar *)vol_name;*s;s++) {
121 CAPTIVE_MEMZERO(&mntent_local);
122 mntent_local.mnt_fsname=(/* de-const */ gchar *)device;
123 mntent_local.mnt_dir=(/* de-const */ gchar *)captive_printf_alloca("/mnt/captive-%s",vol_name);
125 while (g_hash_table_lookup(dirs_used_hash,mntent_local.mnt_dir)) {
127 mntent_local.mnt_dir=(/* de-const */ gchar *)captive_printf_alloca("/mnt/captive-%s%d",vol_name,dir_count);
129 if (optarg_dry || !mkdir("/mnt",0755)) {
131 g_message(_("Created base mount directory: %s"),"/mnt");
133 else if (errno!=EEXIST)
134 g_warning(_("Error creating base mount directory \"%s\" for device \"%s\": %m"),
135 "/mnt",mntent_local.mnt_fsname);
136 if (optarg_dry || !mkdir(mntent_local.mnt_dir,0755)) {
138 g_message(_("Created mount directory for device \"%s\": %s"),
139 mntent_local.mnt_fsname,mntent_local.mnt_dir);
141 else if (errno!=EEXIST)
142 g_warning(_("Error creating mount directory \"%s\" for device \"%s\": %m"),
143 mntent_local.mnt_dir,mntent_local.mnt_fsname);
144 mntent_local.mnt_type="captive-ntfs";
145 mntent_local.mnt_opts="defaults,noauto"; /* 'mntent_local.mnt_opts' must be != NULL ! */
147 g_message(_("Creating captive-ntfs mntent: %s -> %s"),mntent_local.mnt_fsname,mntent_local.mnt_dir);
148 if (addmntent(mntentfilew,&mntent_local))
149 g_warning(_("Error appending mntent for device \"%s\": %m"),mntent_local.mnt_fsname);
150 g_hash_table_insert(dirs_used_hash,g_strdup(mntent_local.mnt_dir),dirs_used_hash);
154 int main(int argc,char **argv)
158 FILE *mntentfiler,*mntentfilew;
159 gboolean modified=FALSE;
160 GHashTable *proc_partitions_ntfs_hash;
161 struct mntent *mntent;
162 gchar *mntent_mem=NULL;
163 size_t mntent_mem_alloc=0;
165 /* Initialize the i18n stuff */
166 setlocale(LC_ALL,"");
167 bindtextdomain(PACKAGE,LOCALEDIR);
170 context=poptGetContext(
172 argc,(/*en-const*/const char **)argv, /* argc,argv */
173 popt_table, /* options */
174 POPT_CONTEXT_POSIXMEHARDER); /* flags; && !POPT_CONTEXT_KEEP_FIRST */
176 g_assert_not_reached(); /* argument recognization args_error */
179 errint=poptReadDefaultConfig(context,
182 g_assert_not_reached(); /* argument recognization args_error */
185 errint=poptGetNextOpt(context);
187 g_assert_not_reached(); /* some non-callbacked argument reached */
190 if (poptPeekArg(context)) {
191 g_error(_("No arguments expected"));
194 if (optarg_mode==OPTARG_MODE_UNDEF)
195 g_error(_("No run mode specified"));
197 dirs_used_hash=g_hash_table_new_full(g_str_hash,g_str_equal,
198 (GDestroyNotify)dirs_used_hash_key_destroy_func,
199 (GDestroyNotify)NULL);
201 /* FIXME: locking! */
202 if (!(mntentfiler=setmntent(FILENAME_ETC_FSTAB,"r")))
203 g_error(_("Cannot open \"%s\" for reading: %m"),FILENAME_ETC_FSTAB);
204 if (!(mntentfilew=setmntent((optarg_dry ? "/dev/null" : FILENAME_ETC_FSTAB_TMP),"w")))
205 g_error(_("Cannot open \"%s\" for writing: %m"),FILENAME_ETC_FSTAB_TMP);
207 proc_partitions_ntfs_hash=proc_partitions_ntfs_hash_get(optarg_verbose);
209 long mntent_offset_start,mntent_offset_end;
210 gchar *mntent_mem_last_line;
211 size_t mntent_mem_len, mntent_mem_last_line_len;
213 mntent_offset_start=ftell(mntentfiler);
214 mntent=getmntent(mntentfiler);
215 mntent_offset_end=ftell(mntentfiler);
216 g_assert(mntent_offset_end>=mntent_offset_start);
217 mntent_mem_len=mntent_offset_end-mntent_offset_start;
218 if (mntent_mem_len>mntent_mem_alloc) {
220 mntent_mem_alloc=2*mntent_mem_len;
221 mntent_mem=g_malloc(mntent_mem_alloc);
223 if (fseek(mntentfiler,mntent_offset_start,SEEK_SET))
224 g_warning(_("Error seeking in \"%s\": %m"),FILENAME_ETC_FSTAB);
225 if (mntent_mem_len!=fread(mntent_mem,1,mntent_mem_len,mntentfiler))
226 g_warning(_("Error reading \"%s\": %m"),FILENAME_ETC_FSTAB);
227 mntent_mem_last_line=NULL;
228 if (mntent_offset_end!=ftell(mntentfiler))
229 g_warning(_("Invalid position in \"%s\" after fread(3): %m"),FILENAME_ETC_FSTAB);
230 if (mntent_mem_len) {
233 if (mntent_mem[mntent_mem_len-1]!='\n')
234 g_warning(_("mntent memory block not newline-terminated from \"%s\""),FILENAME_ETC_FSTAB);
235 if ((mntent_mem_last_line=memrchr(mntent_mem,'\n',mntent_mem_len-1))) {
236 mntent_mem_last_line++;
237 comments_len=mntent_mem_last_line-mntent_mem;
238 mntent_mem_last_line_len=mntent_mem_len-comments_len;
239 if (comments_len!=fwrite(mntent_mem,1,comments_len,mntentfilew))
240 g_error(_("Error copying comments before device \"%s\" to \"%s\": %m"),
241 (!mntent ? "<none>" : mntent->mnt_fsname),FILENAME_ETC_FSTAB_TMP);
244 if (!mntent_mem_last_line) {
245 mntent_mem_last_line=mntent_mem;
246 mntent_mem_last_line_len=mntent_mem_len;
249 && !strcmp(mntent->mnt_type,"captive-ntfs")
250 && !strncmp(mntent->mnt_fsname,"/dev/",strlen("/dev/"))) {
251 switch (optarg_mode) {
253 case OPTARG_MODE_REMOVE:
255 g_message(_("Dropping captive mntent: %s"),mntent->mnt_fsname);
256 if (optarg_dry || !rmdir(mntent->mnt_dir)) {
258 g_message(_("Deleted mount directory for device \"%s\": %s"),
259 mntent->mnt_fsname,mntent->mnt_dir);
261 else if (errno!=EEXIST)
262 g_warning(_("Error removing mount directory \"%s\" of device \"%s\": %m"),
263 mntent->mnt_dir,mntent->mnt_fsname);
268 case OPTARG_MODE_ADD:
269 if (!g_hash_table_lookup(proc_partitions_ntfs_hash,mntent->mnt_fsname))
270 g_warning(_("Dropping no-longer valid captive filesystem mntent from \"%s\" of device: %s"),
271 FILENAME_ETC_FSTAB,mntent->mnt_fsname);
273 /* Original mntent is dropped to be replaced by new one. */
275 g_message(_("Dropping captive mntent to be replaced by new one: %s"),mntent->mnt_fsname);
281 default: g_assert_not_reached();
284 if (mntent_mem_last_line_len!=fwrite(mntent_mem_last_line,1,mntent_mem_last_line_len,mntentfilew))
285 g_error(_("Error copying mntent for device \"%s\" to \"%s\": %m"),mntent->mnt_fsname,FILENAME_ETC_FSTAB_TMP);
287 g_hash_table_insert(dirs_used_hash,g_strdup(mntent->mnt_dir),dirs_used_hash);
290 if (optarg_mode==OPTARG_MODE_ADD) {
291 if (g_hash_table_size(proc_partitions_ntfs_hash))
293 g_hash_table_foreach(proc_partitions_ntfs_hash,
294 (GHFunc)mntent_add_proc_partitions_ntfs_hash_entry, /* func */
295 mntentfilew); /* user_data */
297 g_hash_table_destroy(proc_partitions_ntfs_hash);
300 g_message(_("Modified status: %s"),(modified ? _("YES") : _("NO")));
301 if (1!=endmntent(mntentfiler))
302 g_error(_("Cannot close \"%s\" after reading: %m"),FILENAME_ETC_FSTAB);
304 if (fchmod(fileno(mntentfilew),0644))
305 g_error(_("Cannot set permissions for \"%s\" after writing: %m"),FILENAME_ETC_FSTAB_TMP);
307 if (1!=endmntent(mntentfilew))
308 g_error(_("Cannot close \"%s\" after writing: %m"),FILENAME_ETC_FSTAB_TMP);
311 if (!access(FILENAME_ETC_FSTAB_BACKUP,F_OK)) {
313 g_message(_("Backup file exists - keeping it intact: %s"),FILENAME_ETC_FSTAB_BACKUP);
315 else if (errno==ENOENT) {
317 g_message(_("File \"%s\" backed up to: %s"),FILENAME_ETC_FSTAB,FILENAME_ETC_FSTAB_BACKUP);
318 if (rename(FILENAME_ETC_FSTAB,FILENAME_ETC_FSTAB_BACKUP))
319 g_warning(_("Cannot backup \"%s\" to \"%s\": %m"),FILENAME_ETC_FSTAB,FILENAME_ETC_FSTAB_BACKUP);
322 g_warning(_("Backup file \"%s\" state unknown: %m"),FILENAME_ETC_FSTAB_BACKUP);
323 if (rename(FILENAME_ETC_FSTAB_TMP,FILENAME_ETC_FSTAB))
324 g_error(_("Cannot move new \"%s\" over old \"%s\": %m"),FILENAME_ETC_FSTAB_TMP,FILENAME_ETC_FSTAB);
327 if (unlink(FILENAME_ETC_FSTAB_TMP))
328 g_error(_("Cannot remove new unmodified \"%s\": %m"),FILENAME_ETC_FSTAB_TMP);