/* This is the source for linuxrc.
*
*  It mounts the dos, win or winnt partition and associates the image file 
*  \linux\linuxdsk.img with the loop device loop0.
*  If \linux\linuxswp.img exists it is associated with loop1.
*
*  It is reported to run at least on linux kernel 2.4.28 and 2.6.9.
*  
*  You create the initrd like above, but you only have this on it.
*
*  drwxr-xr-x  2 root root  4096 2005-01-22 18:00 dev/
*  crw--w--w-  1 root tty   5, 1 2005-01-22 18:00 dev/console
*  brw-rw----  1 root disk  3, 1 2005-01-22 18:00 dev/hda1
*  brw-rw----  1 root disk  7, 0 2005-01-22 18:00 dev/loop0
*  brw-rw----  1 root disk  7, 1 2005-01-22 18:00 dev/loop1
*  -rwx------  1 root root 23544 2005-01-22 18:00 linuxrc
*  drwxr-xr-x  2 root root  4096 2005-01-22 18:00 mnt/
*  lrwxrwxrwx  1 root root     8 2005-01-22 18:00 win -> dev/hda1
*
*  In the above example I have a win9x partition with \linux\linuxdsk.img
*  on /dev/hda1, so I make a link called win to it 'ln -sf dev/hda1 win'.
*  
*  If it were a dos partition it would be 'ln -sf dev/hda1 dos'.
*  If it were a ntfs partition it would be 'ln -sf dev/hda1 winnt'.
*
*  CAVE: Unlike in the 1.x versions the LINK (dos,win,winnt) HAS TO BE IN /
*
*
*  DEVFS:
*  ======
*
*  If You are using devfs the directory structure should be the following:
*
*  drwxr-xr-x  2 root root  4096 2005-01-22 18:00 dev/
*  -rwx------  1 root root 23480 2005-01-22 18:00 linuxrc
*  drwxr-xr-x  2 root root  4096 2005-01-22 18:00 mnt/
*  lrwxrwxrwx  1 root root    21 2005-01-22 18:00 win -> dev/discs/disc0/part1
*
*
* I took the original file from The Loopback Root Filesystem HOWTO
*
*  by Andrew M. Bishop, amb@gedanken.demon.co.uk
*  v1.1.2, 23 Mar 2000
*
*  v1.1.11, 7 Sep 2002
*  Kent Robotti <robotti@godmail.com> 
*
*  v1.2.0, 31 Oct 2004
*  Bodo Giannone <http://www.giannone.de>
*
*  v2.0.0, 22 Jan 2005
*  Bodo Giannone <http://www.giannone.de>
*
*
* You can compile it this way.
* gcc -Os -mcpu=i386 -D_FILE_OFFSET_BITS=64 -static -s -o linuxrc linuxrc.c
*
* It does compile also with uClibc (a very small libc) 
* with the command line from above.
* http://www.uclibc.org
*
* You could use a smaller static libc, called dietlibc.
* http://www.fefe.de/dietlibc/
*
* dietlibc# make
* dietlibc/bin-i386# ./diet gcc -Os -m386 -D_FILE_OFFSET_BITS=64 \
* -static -s -o linuxrc linuxrc.c
*/

#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/stat.h>
#include <linux/fs.h>

#include <linux/posix_types.h>
#undef dev_t
#define dev_t __kernel_dev_t
#include <linux/loop.h>
#undef dev_t

void die(char *s) {
  write(2,s,strlen(s));
}

int set_loop(const char *device, const char *file, int offset, int loopro)
{
	struct loop_info loopinfo;
	int	fd, ffd, mode, i;
	char	*pass;

	mode = loopro ? O_RDONLY : O_RDWR;
	if ((ffd = open (file, mode)) < 0 && !loopro
	    && (errno != EROFS || (ffd = open (file, mode = O_RDONLY)) < 0)) {
	  perror (file);
	  return 0;
	}
	if ((fd = open (device, mode)) < 0) {
	  perror (device);
	  return 0;
	}
	loopro = (mode == O_RDONLY);

	memset(&loopinfo, 0, sizeof(loopinfo));
	strncpy(loopinfo.lo_name, file, LO_NAME_SIZE);
	loopinfo.lo_name[LO_NAME_SIZE-1] = 0;
	loopinfo.lo_offset = offset;
	loopinfo.lo_encrypt_key_size = 0;
	if (ioctl(fd, LOOP_SET_FD, ffd) < 0) {
		perror("ioctl: LOOP_SET_FD");
		exit(1);
	}
	if (ioctl(fd, LOOP_SET_STATUS, &loopinfo) < 0) {
		(void) ioctl(fd, LOOP_CLR_FD, 0);
		perror("ioctl: LOOP_SET_STATUS");
		exit(1);
	}
	close(fd);
	close(ffd);
	return 1;
}

int check_loop(void) {

	int i,notmounted;
	FILE *distfile;
	char *loop0;
	char *loop1;

	while (1) {
		distfile=fopen("/mnt/linux/linuxdsk.img","r");
		if (distfile==NULL) {
			die("\n\tERROR!\tCould not find \\linux\\linuxdsk.img on the system!\n");
        		die("\n\t\tPress Ctrl-Alt-Del to reboot!\n");
			umount("/mnt");
     		        pause();
 		        exit(1);
		}
		break;
	}

	fclose(distfile);

        umask(0);

        loop0="/dev/loop0";
        
        /* Does /dev/loop0 exist? If not, check /dev/loop/0 and if not again, create it! */

       	distfile=fopen("/dev/loop0","r");
       	if (distfile==NULL) 
	  {
	    distfile=fopen("/dev/loop/0","r");
	    if (distfile==NULL)
	      {
	        mknod("/dev/loop0",0660|S_IFBLK,7 * 256 + 0);
	        distfile=fopen("/dev/loop0","r");
	        if (distfile==NULL)
	          {
	            die("\n\tERROR!\tNeither /dev/loop0 nor /dev/loop/0\n");
	            die("\t\texists and creating failed!\n");
	            die("\n\t\tPress Ctrl-Alt-Del to reboot!\n");
	            pause();
	            exit(1);
                  }
              }
            else loop0="/dev/loop/0";
          }
        
        fclose(distfile);  	
        
        
	if(!(set_loop(loop0,"/mnt/linux/linuxdsk.img",0,0))) {
		die("\n\tERROR!\tCan't associate \\linux\\linuxdsk.img\n");
		die("\t\twith the loop device loop0!\n");
		die("\n\t\tPress Ctrl-Alt-Del to reboot!\n");
		umount("/mnt");
		pause();
		exit(1);
	}

	distfile=fopen("/mnt/linux/linuxswp.img","r");
	if (distfile==NULL) 
	    {
	        die("\nINITRD: No swapfile present!\n");
            }
        else
            {

                fclose(distfile);

                loop1="/dev/loop1";
                
                /* Does /dev/loop1 exist? If not, check /dev/loop/0 and if not again, create it! */

                distfile=fopen("/dev/loop1","r");
                if (distfile==NULL) 
                  {
                    distfile=fopen("/dev/loop/1","r");
                    if (distfile==NULL)
                      {
                      mknod("/dev/loop1",0660|S_IFBLK,7 * 256 + 1);
                      distfile=fopen("/dev/loop1","r");
                      if (distfile==NULL) 
                        {
                          loop1=NULL;
                          die("\n\tERROR!\tNeither /dev/loop1 nor /dev/loop/1\n");
                          die("\t\texists and creating failed!\n");
                          die("\t\tNo swap device on loop1!\n\n");
                        }
                      else fclose(distfile);
                      }
                    else {
                            loop1="/dev/loop/1";
                            fclose(distfile);
                         }   
                  }
                else fclose(distfile);  	
        
                if (!(loop1==NULL))
                  if(!(set_loop(loop1,"/mnt/linux/linuxswp.img",0,0))) 
                    {
		      die("\n\tERROR!\tCan't associate \\linux\\linuxswp.img\n");
		      die("\t\twith the loop device loop1!\n");
		      die("\t\tNo swap device on loop1!\n\n");
                    }
            }

	exit(0);
}


int loopdos(void) {

	int i,notmounted;
              
	while (1) {
		notmounted=mount("/dos","/mnt","msdos",MS_MGC_VAL | MS_NOEXEC,"quiet",0);
		if (!notmounted)
			break;
		else

		die("\n\tERROR!\tCould not mount the dos partition!\n");
		die("\n\t\tPress Ctrl-Alt-Del to reboot!\n");
		pause();
		exit(1);
	}
	die("INITRD: Mounted dos partition under /initrd/mnt directory...\n");

	check_loop();

}
 
int loopwin(void) {

	int i,notmounted;
              
	while (1) {
		notmounted=mount("/win","/mnt","vfat",MS_MGC_VAL | MS_NOEXEC,"quiet",0);
		if (!notmounted)
			break;
		else

		die("\n\tERROR!\tCould not mount the win partition!\n");
		die("\n\t\tPress Ctrl-Alt-Del to reboot!\n");
		pause();
		exit(1);
	}
	die("INITRD: Mounted win partition under /initrd/mnt directory...\n");

	check_loop();

}

int loopwinnt(void) {

	int i,notmounted;
              
	while (1) {
//		notmounted=mount("/dev/winnt","/mnt","ntfs",MS_MGC_VAL | MS_NOEXEC,"quiet",0);
		notmounted=mount("/winnt","/mnt","ntfs",MS_MGC_VAL | MS_NOEXEC,0);
		if (!notmounted)
			break;
		else

		die("\n\tERROR!\tCould not mount the winnt partition!\n");
		die("\n\t\tPress Ctrl-Alt-Del to reboot!\n");
		pause();
		exit(1);
	}
	die("INITRD: Mounted winnt partition under /initrd/mnt directory...\n");

	check_loop();

}


int main(void) {

	int i,notmounted;
	FILE *distfile;

		distfile=fopen("/dos","r");
                if (!(distfile==NULL))
		  {
		    fclose(distfile);
		    loopdos();
                  }
                else
                  {
                    distfile=fopen("/win","r");
                    if (!(distfile==NULL))
                      {
                        fclose(distfile);
                        loopwin();
                      }
                    else
                      {        
                        distfile=fopen("/winnt","r");
                        if (!(distfile==NULL))
                          {
                            fclose(distfile);
                            loopwinnt();
                          }
                        else
                          {
                            die("\n\tERROR!\tCould not find a device /dos, /win,\n");
                            die("\t\tor /winnt to mount!\n");
                            //	die("\n\tERROR!\tCould not find a device /dos, /win, or /winnt to mount!\n");
                            die("\n\t\tPress Ctrl-Alt-Del to reboot!\n");
                            pause();
                            exit(1);
                          }
                      }
                  }

}

/* End of linuxrc.c */

