#include <alloca.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <newt.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <unistd.h>

#include "devices.h"
#include "fs.h"
#include "install.h"
#include "intl.h"
#include "log.h"
#include "mkswap.h"
#include "run.h"
#include "smb.h"
#include "windows.h"

int nfsmount(const char *spec, const char *node, int *flags,
	     char **extra_opts, char **mount_opts);

static int fstabCmp(const void * a, const void * b);
static int mkdirChain(char * chain);
static int mkdirIfNone(char * directory);

int badBlocks = 0;

char * nstrdup(const char * foo) {
    return foo ? strdup(foo) : NULL;
}

static int fstabCmp(const void * a, const void * b) {
    const struct fstabEntry * first = a;
    const struct fstabEntry * second = b;

    if (first->type != second->type) {
	if (first->type == PART_NFS)
	    return 1;
	else if (second->type == PART_NFS)
	    return -1;
    }

    return strcmp(first->mntpoint, second->mntpoint);
}

void fstabSort(struct fstab * fstab) {
    qsort(fstab->entries, fstab->numEntries, 
	  sizeof(*fstab->entries), fstabCmp);
}

void initFstabEntry(struct fstabEntry * e) {
    e->device = NULL;
    e->netHost = NULL;
    e->netPath = NULL;
    e->mntpoint = NULL;
    e->tagName = NULL;
    e->size = 0;
    e->type = PART_OTHER;
    e->isMounted = 0;
    e->doFormat = 0;
}

void addPartitionListbox(struct partitionTable table, int maxHeight, int type, 
			 int (*filter)(struct partition * part),
			 int * numItems, newtGrid * gridPtr, 
			 newtComponent * listboxPtr) {
    newtComponent listbox, label;
    int i, count;
    char buf[80];

    count = 0;
    for (i = 0; i < table.count; i++) 
	if (table.parts[i].type & type)
	    if (!filter || filter(table.parts + i)) count++;

    *listboxPtr = listbox = newtListbox(-1, -1, 
					count < maxHeight ? count : maxHeight,
	NEWT_FLAG_RETURNEXIT | (count > maxHeight ? NEWT_FLAG_SCROLL : 0));

    label = newtLabel(-1, -1, "Device       Size (k)");

    if (numItems) *numItems = 0;

    for (i = 0; i < table.count; i++) {
	if (table.parts[i].type & type) {
	    if (!filter || filter(table.parts + i)) {
		sprintf(buf, "/dev/%-5s  %9d", table.parts[i].device, 
			table.parts[i].size);
		newtListboxAddEntry(listbox, buf, &table.parts[i]);

		if (numItems) (*numItems)++;
	    }
	}
    }

    /* using VStacked right here adds too much padding */
    *gridPtr = newtCreateGrid(1, 2);
    newtGridSetField(*gridPtr, 0, 0, NEWT_GRID_COMPONENT, label,
		     0, 0, 0, 0, 0, 0);
    newtGridSetField(*gridPtr, 0, 1, NEWT_GRID_COMPONENT, listbox,
		     0, 0, 0, 0, 0, 0);
}

static int badMountPoint(unsigned int type, char * item) {
    char * chptr = item;

    if (*chptr != '/') {
	newtWinMessage(_("Bad Mount Point"), _("Ok"),
		    _("Mount points must begin with a leading /."));
	return INST_ERROR;
    } 

    if (*(chptr + 1) && *(chptr + strlen(chptr) - 1) == '/') {
	newtWinMessage(_("Bad Mount Point"), _("Ok"),
		    _("Mount points may not end with a /."));
	return INST_ERROR;
    } 

    while (*chptr && isprint(*chptr)) chptr++;

    if (*chptr) {
	newtWinMessage(_("Bad Mount Point"), _("Ok"),
		    _("Mount points may only printable characters."));
	return INST_ERROR;
    }

    if (type != PART_EXT2 && (
	 !strncmp(item, "/var", 4) ||
	 !strncmp(item, "/tmp", 4) ||
	 !strncmp(item, "/bin", 4) ||
	 !strncmp(item, "/sbin", 4) ||
	 !strncmp(item, "/etc", 4) ||
	 !strncmp(item, "/boot", 4) ||
	 !strncmp(item, "/dev", 4) ||
	 !strncmp(item, "/root", 4) ||
	 !strncmp(item, "/lib", 4))) {
	newtWinMessage(_("Bad Mount Point"), _("Ok"),
		    _("System partitions must be on Linux Native "
		      "partitions."));
	return INST_ERROR;
    }

    if (type != PART_EXT2 && type != PART_NFS &&
	!strncmp(item, "/usr", 4)) {
	newtWinMessage(_("Bad Mount Point"), _("Ok"),
		    _("/usr must be on a Linux Native partition "
		      "or an NFS volume."));
	return INST_ERROR;
    }

    return 0;
}

static char * restrdup(char * old, char * new) {
    if (old) free(old);
    if (new) return strdup(new); else return NULL;
}

void nfsMountCallback(newtComponent co, void * arg) {
    struct nfsMountCallbackInfo * nfsinfo = arg;
    char * chptr;
    char * copy;
 
    if (!strlen(nfsinfo->netpathVal)) {
	if (strchr(nfsinfo->serverVal, ':')) {
	    chptr = copy = alloca(strlen(nfsinfo->serverVal) + 1);
	    strcpy(copy, nfsinfo->serverVal);
	    chptr = strchr(copy, ':');
	    *chptr = '\0';
	    chptr++;
	    newtEntrySet(nfsinfo->server, copy, 1);
	    newtEntrySet(nfsinfo->netpath, chptr, 1);
	}
    }

    if (nfsinfo->mntpoint) {
	if (strlen(nfsinfo->netpathVal) && !strlen(nfsinfo->mntpointVal)) {
	    newtEntrySet(nfsinfo->mntpoint, nfsinfo->netpathVal, 1);
	}
    }
}

int editNetMountPoint(struct fstabEntry * item) {
    newtComponent form, server, path, point, okay, cancel, answer;
    char * pointValue, * pathValue, * serverValue;
    int done = 0;
    struct nfsMountCallbackInfo nfsinfo;
 
    newtCenteredWindow(50, 10, _("Edit Network Mount Point"));

    form = newtForm(NULL, NULL, 0);

    newtFormAddComponent(form, newtLabel(1, 1, "NFS Server  :"));
    newtFormAddComponent(form, newtLabel(1, 2, "NFS Path    :"));
    newtFormAddComponent(form, newtLabel(1, 3, "Mount point :"));

    server = newtEntry(17, 1, item->netHost, 20, &serverValue, 
		      NEWT_ENTRY_SCROLL | NEWT_ENTRY_RETURNEXIT);
    path = newtEntry(17, 2, item->netPath, 20, &pathValue, 
		      NEWT_ENTRY_SCROLL | NEWT_ENTRY_RETURNEXIT);
    point = newtEntry(17, 3, item->mntpoint, 20, &pointValue, 
		      NEWT_ENTRY_SCROLL | NEWT_ENTRY_RETURNEXIT);

    nfsinfo.server = server;
    nfsinfo.mntpoint = point;
    nfsinfo.netpath = path;

    nfsinfo.serverVal = serverValue;
    nfsinfo.netpathVal = pathValue;
    nfsinfo.mntpointVal = pointValue;

    newtComponentAddCallback(server, nfsMountCallback, &nfsinfo);
    newtComponentAddCallback(path, nfsMountCallback, &nfsinfo);

    okay = newtButton(10, 6, _("Ok"));
    cancel = newtButton(30, 6, _("Cancel"));

    newtFormAddComponents(form, server, path, point, okay, cancel, NULL);

    do {
	answer = newtRunForm(form);

	if (answer == cancel) {
	    done = 1;
	} else if (*pointValue) {
	    if (!badMountPoint(item->type, pointValue)) 
		done = 1;
	}
    } while (!done);

    if (answer != cancel) {
	item->mntpoint = restrdup(item->mntpoint, pointValue);
	item->netPath = restrdup(item->netPath, pathValue);
	item->netHost = restrdup(item->netHost, serverValue);

	if (item->device) free(item->device);
	item->device = malloc(strlen(pathValue) + strlen(serverValue) + 5);
	sprintf(item->device, "%s:%s", serverValue, pathValue);
    }

    newtPopWindow();

    if (answer == cancel)
	return INST_CANCEL;
    return 0;
}	    

void freeFstabEntry( struct fstabEntry *e ) {
    	if (e->mntpoint) free(e->mntpoint);
	if (e->device)   free(e->device);
	if (e->netPath)  free(e->netPath);
	if (e->netHost)  free(e->netHost);

}

void freeFstab(struct fstab fstab) {
    int i;

    for (i = 0; i < fstab.numEntries; i++) {
	freeFstabEntry( &fstab.entries[i] );
    }

    if (fstab.numEntries) free(fstab.entries);
}

struct fstab copyFstab(struct fstab * fstab) {
    struct fstab newfstab;
    int i, j;

    if (!fstab->numEntries) {
	newfstab.numEntries = 0;
	newfstab.entries = malloc(1);
	return newfstab;
    }

    /* duplicate the current fstab */
    newfstab.numEntries = fstab->numEntries;
    newfstab.entries = malloc(fstab->numEntries * sizeof(struct fstabEntry));
    for (i = j = 0; i < newfstab.numEntries; i++) {
	if (fstab->entries[i].mntpoint) {
	    newfstab.entries[j] = fstab->entries[i];
	    newfstab.entries[j].mntpoint = nstrdup(fstab->entries[i].mntpoint);
	    newfstab.entries[j].device = nstrdup(fstab->entries[i].device);
	    newfstab.entries[j].netPath = nstrdup(fstab->entries[i].netPath);
	    newfstab.entries[j].netHost = nstrdup(fstab->entries[i].netHost);
	    j++;
	}
    }

    newfstab.numEntries = j;

    /* return the memory we don't actually need */
    newfstab.entries = realloc(newfstab.entries, j * sizeof(struct fstabEntry));

    return newfstab;
}

static int mkExt2Filesystem(char * dev) {
    char * mke2fsargs[] = { "mke2fs", NULL, NULL, NULL};
    int rc;
    char message[80];

    mke2fsargs[1] = alloca(strlen(dev) + 6);
    strcpy(mke2fsargs[1], "/tmp/");
    strcat(mke2fsargs[1], dev);

    if (badBlocks)
	mke2fsargs[2] = "-c";

    sprintf(message, _("Making ext2 filesystem on /dev/%s..."), dev);
    winStatus(60, 3, _("Running"), message);

    devMakeInode(dev, mke2fsargs[1]);
    rc = runProgram(RUN_LOG, "/usr/bin/mke2fs", mke2fsargs);
    devRemoveInode(mke2fsargs[1]);

    newtPopWindow();

    if (rc)
	return INST_ERROR;
    else
	return 0;
}

int queryFormatFilesystems(struct fstab * fstab) {
    newtComponent form, checkList, okay, cancel, sb, text, answer;
    newtComponent checkbox, blank;
    newtGrid grid, subgrid, checkgrid, buttons;
    char * states;
    char doCheck = ' ';
    newtComponent * checks;
    char buf[80];
    int i, top;

    form = newtForm(NULL, NULL, 0);

    if (fstab->numEntries > 4) {
	sb = newtVerticalScrollbar(47, 7, 4, 9, 10);
    } else
	sb = NULL;

    checkList = newtForm(sb, NULL, 0);
    if (sb) newtFormSetHeight(checkList, 4);

    text = newtTextboxReflowed(-1, -1, _("What partitions would you like to "
		       "format? We strongly suggest formatting all of the "
		       "system partitions, including /, /usr, and /var. There "
		       "is no need to format /home or /usr/local if they "
		       "have already been configured during a previous "
		       "install."), 55, 0, 15, 0);

    checks = alloca(sizeof(newtComponent) * fstab->numEntries);
    states = alloca(sizeof(char) * fstab->numEntries);
    for (i = 0, top = 0; i < fstab->numEntries; i++) {
	if (fstab->entries[i].doFormat)
	    states[i] = '*';
	else
	    states[i] = ' ';

	if (fstab->entries[i].type == PART_EXT2) {
	    sprintf(buf, "/dev/%-5s  %-33s", fstab->entries[i].device, 
		   fstab->entries[i].mntpoint);
	    checks[i] = newtCheckbox(-1, top++, buf, states[i], NULL, 
				     &states[i]);
	    newtFormAddComponent(checkList, checks[i]);
	} else {
	    checks[i] = NULL;
	}
    }

    if (top > 4) {
	blank = newtForm(NULL, NULL, 0);
	newtFormSetWidth(blank, 2);
	newtFormSetHeight(blank, 4);
	newtFormSetBackground(blank, NEWT_COLORSET_CHECKBOX);
	checkgrid = newtGridHCloseStacked(
			    NEWT_GRID_COMPONENT, checkList,
			    NEWT_GRID_COMPONENT, blank,
			    NEWT_GRID_COMPONENT, sb, NULL);
    } else {
	checkgrid = newtGridHCloseStacked(NEWT_GRID_COMPONENT, checkList,
					  NULL);
    }

    checkbox = newtCheckbox(10, 12, _("Check for bad blocks during format"),
				badBlocks ? '*' : ' ', NULL, &doCheck);

    buttons = newtButtonBar(_("Ok"), &okay, _("Back"), &cancel, NULL);
    subgrid = newtCreateGrid(1, 3);
    subgrid = newtGridVStacked(NEWT_GRID_SUBGRID, checkgrid,
			       NEWT_GRID_COMPONENT, checkbox, NULL);
    grid = newtGridBasicWindow(text, subgrid, buttons);
    newtGridAddComponentsToForm(grid, form, 1);
    newtGridWrappedWindow(grid, _("Partitions To Format"));
    newtGridFree(grid, 1);

    answer = newtRunForm(form);

    newtFormDestroy(form);
    newtPopWindow();

    if (answer == cancel) return INST_CANCEL;

    for (i = 0; i < fstab->numEntries; i++) {
	fstab->entries[i].doFormat = (states[i] != ' ');
    }

    if (doCheck == ' ')
	badBlocks = 0;
    else
	badBlocks = 1;

    return 0;
}

int formatFilesystems(struct fstab * fstab) {
    int i;

    for (i = 0; i < fstab->numEntries; i++) {
        if (fstab->entries[i].doFormat)
	    mkExt2Filesystem(fstab->entries[i].device);
    }

    return 0;
}

int doMount(char * dev, char * where, char * fs, int rdonly, int istty) {
    return doPwMount(dev, where, fs, rdonly, istty, NULL, NULL);
}

int doPwMount(char * dev, char * where, char * fs, int rdonly, int istty,
		char * acct, char * pw) { 
    char * buf = NULL;
    int isnfs = 0;
    char * mount_opt = NULL;
    long int flag;
    char * chptr;

    if (!strcmp(fs, "nfs")) isnfs = 1;

    logMessage("mounting %s on %s as type %s", dev, where, fs);

    if (testing) {
	newtWinMessage("Test mount", "Ok", "I would mount /dev/%s on %s",
		"using a(n) %s filesystem.", dev, where, fs);
    } else if (!strcmp(fs, "smb")) {
	mkdirChain(where);

	if (!acct) acct = "guest";
	if (!pw) pw = "";

	buf = alloca(strlen(dev) + 1);
	strcpy(buf, dev);
	chptr = buf;
	while (*chptr && *chptr != ':') chptr++;
	if (!*chptr) {
	    logMessage("bad smb mount point %s", where);
	    return 0;
	} 
	
	*chptr = '\0';
	chptr++;

#ifdef __i386__
	logMessage("mounting smb filesystem from %s path %s on %s",
			buf, chptr, where);
	return smbmount(buf, chptr, acct, pw, "localhost", where);
#else 
	errorWindow("smbfs only works on Intel machines");
#endif
    } else {
	mkdirChain(where);

  	if (!isnfs && *dev == '/') {
	    buf = dev;
	} else if (!isnfs) {
	    buf = alloca(200);
	    strcpy(buf, "/tmp/");
	    strcat(buf, dev);

	    if (devMakeInode(dev, buf)) return 1;
	} else {
	    char * junk = NULL;
	    int morejunk = 0;

	    buf = dev;
	    logMessage("calling nfsmount(%s, %s, &morejunk, &junk, &mount_opt)",
			buf, where);

	    if (nfsmount(buf, where, &morejunk, &junk, &mount_opt))
		logMessage("\tnfsmount returned non-zero");
	}

	flag = MS_MGC_VAL;
	if (rdonly)
	    flag |= MS_RDONLY;
	
	logMessage("calling mount(%s, %s, %s, %ld, %p)", buf, where, fs, 
			flag, mount_opt);

	if (mount(buf, where, fs, flag, mount_opt)) {
	    if (istty) {
		fprintf(stderr, "mount failed: %s\n", strerror(errno));
	    } else {
		newtWinMessage(_("Error"), _("Ok"), 
				_("mount failed: %s"), strerror(errno));
	    }
	    return 1;
	}

	if (!isnfs) devRemoveInode(buf);
    }

    return 0;
}

int mountFilesystems(struct fstab * fstab) {
    int i;
    char buf[1000];

    /* don't bother mounting odd (non-ext2) filesystems - we don't need
       them for installs */

    /* what about NFS? we should probably mount them to check mount integrity,
       but we don't know if networking is working well enough for us to do
       this */
   
    chdir("/");

    for (i = 0; i < fstab->numEntries; i++) {
	strcpy(buf, "/mnt");
	strcat(buf, fstab->entries[i].mntpoint);

	if (fstab->entries[i].type == PART_EXT2 ||
	    fstab->entries[i].type == PART_NFS ) {
	    if (fstab->entries[i].type == PART_EXT2 &&
		!doMount(fstab->entries[i].device, buf, "ext2", 0, 0))
		fstab->entries[i].isMounted = 1;
	    else if (fstab->entries[i].type == PART_NFS &&
		!doMount(fstab->entries[i].device, buf, "nfs", 0, 0))
		fstab->entries[i].isMounted = 1;
	    else {
		logMessage("unmounting all filesystems due to mount error");
		umountFilesystems(fstab);
		return INST_ERROR;
	    }
	} else {
	    logMessage("creating directory %s", buf);
	    mkdirChain(buf);
	}
    }

    return 0;
}

int umountFilesystems(struct fstab * fstab) {
    char buf[1000];
    int i;
    int olderrno;

    logMessage("unmounting all filesystems");

    chdir("/");

    if (testing) return 0;

    for (i = fstab->numEntries - 1; i >= 0; i--) {
	if (fstab->entries[i].isMounted) {
	    strcpy(buf, "/mnt");
	    strcat(buf, fstab->entries[i].mntpoint);

	    fstab->entries[i].isMounted = 0;
	    if (umount(buf)) {
		olderrno = errno;
		logMessage("error unmounting %s: %s\n", buf, strerror(errno));
		errno = olderrno;
		errorWindow("error unmounting filesystem: %s");
	    }
	}    
    }

    return 0;
}

static int mkdirChain(char * origChain) {
    char * chain;
    char * chptr;

    chain = alloca(strlen(origChain) + 1);
    strcpy(chain, origChain);
    chptr = chain;

    if (testing) return 0;

    while ((chptr = strchr(chptr, '/'))) {
	*chptr = '\0';
	if (mkdirIfNone(chain)) {
	    *chptr = '/';
	    return INST_ERROR;
	}

	*chptr = '/';
	chptr++;
    }

    if (mkdirIfNone(chain))
	return INST_ERROR;

    return 0;
}

static int mkdirIfNone(char * directory) {
    int rc, mkerr;
    char * chptr;

    /* If the file exists it *better* be a directory -- I'm not going to
       actually check or anything */
    if (!access(directory, X_OK)) return 0;

    /* if the path is '/' we get ENOFILE not found" from mkdir, rather
       then EEXIST which is weird */
    for (chptr = directory; *chptr; chptr++)
        if (*chptr != '/') break;
    if (!*chptr) return 0;

    rc = mkdir(directory, 0755);
    mkerr = errno;

    logMessage("creating directory %s rc = %d", directory, rc);

    if (!rc || mkerr == EEXIST) return 0;

    logMessage("    error: %s", strerror(mkerr));

    return INST_ERROR;
}

int writeFstab(struct fstab * fstab) {
    int i;
    FILE * f;
    int fd;
    char * fs = NULL;
    int freq = 0;
    int passno = 0;
    int bad;
    char buf[4096];
    char * chptr, * cddev = NULL;
    char * devFormat = "/dev/%-18s %-23s %-7s %-15s %d %d\n";
    char * nfsFormat = "%-23s %-23s %-7s %-15s %d %d\n";
    char * procFormat = "%-23s %-23s %-7s %-15s %d %d\n";
    char * options;
    char * format;

    logMessage("scanning /proc/mounts for iso9660 filesystems");
    fd = open("/proc/mounts", O_RDONLY);
    if (fd < 0) {
	logMessage("\terror opening /proc/mounts -- skipping check: %s",
			strerror(errno));
    } else {
	i = read(fd, buf, sizeof(buf) - 1);
	if (i < 0) {
	    logMessage("\terror reading /proc/mounts -- skipping check: %s",
			    strerror(errno));
	} else {
	    buf[i] = 0;
	    if ((chptr = strstr(buf, "iso9660"))) {
		chptr--;

		/* skip the mount point */
		while (*chptr == ' ') chptr--;
		while (*chptr != ' ') chptr--;
		while (*chptr == ' ') chptr--;

		chptr++;
		*chptr = '\0';
		while (*chptr != '/') chptr--;
		cddev = strdup(chptr + 1);
		
		logMessage("found mounted cdrom drive %s", cddev);
	    }
	}
    }

    #ifndef __sparc__
    if (!cddev) {
	if (findAtapi(&cddev)) cddev = NULL;
    }
    #endif

    /* cd-rom rooted installs have the cdrom mounted on /dev/root which */
    /* is not what we want to symlink to /dev/cdrom.                    */

    if (!cddev || !strncmp(cddev, "root", 4)) {
	if (findSCSIcdrom(&cddev)) cddev = NULL;
    }

    if (testing) {
	if (cddev) free(cddev);
	return 0;
    }

    logMessage("touching /etc/mtab");
    f = fopen("/mnt/etc/mtab", "w+");
    if (!f) {
	errorWindow("error touching /mnt/etc/mtab");
    } else {
	fclose(f);
    }

    logMessage("creating /etc/fstab");

    f = fopen("/mnt/etc/fstab", "w");
    if (!f) {
	if (cddev) free(cddev);
	errorWindow("error creating /mnt/etc/fstab");
	return INST_ERROR;
    }

    for (i = 0; i < fstab->numEntries; i++) {
	if (!fstab->entries[i].mntpoint) continue;

	passno = 0;
	freq = 0;
	format = devFormat;
        options = "defaults";

	bad = 0;
	switch (fstab->entries[i].type) {
	  case PART_EXT2:
	    freq = 1;
	    fs = "ext2";
	    if (!strcmp(fstab->entries[i].mntpoint, "/"))
		passno = 1;
	    else
		passno = 2;
	  
	    break;

	  case PART_NFS:
	    fs = "nfs";
	    options = "ro";
	    format = nfsFormat;
	    break;

	  case PART_SWAP:
	    fs = "swap";
	    break;

	  case PART_DOS:
	    fs = "vfat";
	    break;

	  case PART_HPFS:
	    fs = "hpfs";
	    break;

	  default:
	    bad = 1;
	}

	if (!bad)
	    fprintf(f, format, fstab->entries[i].device, 
		    fstab->entries[i].mntpoint, fs, options, freq, passno);
    }

    fprintf(f, devFormat, "fd0", "/mnt/floppy", "ext2", "noauto", 0, 0);
    if (cddev) {
	if (mkdir("/mnt/mnt/cdrom", 0755)) 
	    logMessage("failed to mkdir /mnt/mnt/cdrom: %s", strerror(errno));

	if (symlink(cddev, "/mnt/dev/cdrom"))
	    logMessage("failed to symlink /mnt/dev/cdrom: %s", strerror(errno));

	fprintf(f, devFormat, "cdrom", "/mnt/cdrom", "iso9660", "noauto,ro", 
		0, 0);
	free(cddev);
    }

    fprintf(f, procFormat, "none", "/proc", "proc", "defaults", 0, 0);

    fclose(f);


    return 0;
}

int addFstabEntry(struct fstab * fstab, struct fstabEntry entry) {
    int i;

    for (i = 0; i < fstab->numEntries; i++) 
	if (!strcmp(entry.device, fstab->entries[i].device))
	    break;

    if (i == fstab->numEntries) {
	fstab->numEntries++;
	if (fstab->numEntries > 1)
	    fstab->entries = realloc(fstab->entries, 
				sizeof(entry) * fstab->numEntries);
	else
	    fstab->entries = malloc(sizeof(entry));
    }

    fstab->entries[i] = entry;

    return i;
}