[Bootcd-user] TMPFS patch

Mark Clarkson Mark Clarkson <markjclarkson@hotmail.com>
Thu, 1 Jul 2004 21:41:18 +0100


Hi bootcd-users!
Okay, I've been going on about this tmpfs patch, so here it is at last!
I've tried to iron out as many bugs as possible but there's still stuff
left to do... I hope it's useful :)

This patch set can enable tmpfs for /var, /home and/or /tmp.  This
allows you to use a large chunk of memory to store /var, /tmp and /home
in. The memory is not actually used until it is required.

If TMPFS=no then bootcd works exactly as before. When tmpfs is switched
on the ramdisk remains on /ram1, and tmpfs is used for /ram2.

It introduces eight new configuration options: 
  TMPFS,
  TMPFS_HOME,
  TMPFS_TMP,
  TMPFS_SIZE,
  TMPFS_SIZE_AUTO_ADD,
  TMPFS_PRUNE_VAR,
  TMPFS_PRUNE_HOME, and
  TMPFS_MIN_FREE.

Four files need to be patched: 
  bootcdwrite, bootcd-check.lib, bootcd2disk and S12bootcdram.

TMPFS will need to be built into the kernel for tmpfs to work but no
checks are made for this.  I think the following are correct:

  At least kernel 2.4.x is required for TMPFS_SIZE options.
  At least kernel 2.2.x is required for TMPFS in general + an entry in
  fstab.

The patches apply against bootcd v2.41, with or without the devfs patch.
As a side effect devfs gets fixed anyway if these patches are applied.

This patch also fixes a bug that surfaces when DEVFS=yes and
FASTBOOT=no. This bug stops files being loaded into memory at boot time.

Five sections follow:

  1. New Configuration entries for bootcdwrite.conf
  2. Patch file for /usr/bin/bootcdwrite
  3. Patch file for /usr/share/bootcd/S12bootcdram
  4. Patch file for /usr/share/bootcd/bootcd-check.lib
  5. Patch file for /usr/share/bootcd/bootcd2disk

Hopefully the lines won't get wrapped in transit; If they do it may not
work, so I've also put the patches here:

  http://www.c2server.co.uk/patches.html

The patches are in a compressed tarball. There is also an installer in
the tarball; to use the installer:

  'patch' must be installed!
  
  $ tar xvfz bootcd_2.41_tmpfs.patch1.tar.gz
  $ cd bootcd_2.41_tmpfs.patch1
  $ su
  $ ./patch_bootcd_2.41.sh

Enjoy!

-------------
Testing/Bugs:
-------------
bootcd2disk did not work for me before the patch, since my kernel uses
the new disk naming scheme, and did not work after the patch. If
bootcd2disk worked for you however, this patch _should_ not break it.

If filenames containing spaces are pruned by TMPFS_PRUNE_ options then
the files will be excluded from RAM (correct), but will not be linked
at run-time(wrong), and will produce an 'ln' error.
 A fix would require modification of some existing bootcd functions.

I have only tried tmpfs with kernel 2.4.26, and 2.6.6
-------------

--------------------------------------------------------------------------
1. New configuration entries for bootcdwrite.conf
--------------------------------------------------------------------------
Notes:
  Copy and paste the lines between '<---- snip ---->' into the
  configuration file /etc/bootcd/bootcdwrite.conf.
  These settings disable tmpfs and need to be changed to enable tmpfs.

<---- snip ---->

# TMPFS=yes|no
# The var partition can be loaded into ram at boot time using the tmpfs
# filesystem. If tmpfs is used ram2 will be mounted on tmpfs rather than on
# the ramdisk, possibly saving some RAM. A considerably larger var filesystem
# is possible using tmpfs that does not need to be the same size as ram1.
#
# Other TMPFS_ options will be possible when TMPFS=yes
# All TMPFS_ options are disabled when TMPFS=no
#
# To use tmpfs:
#TMPFS=yes
TMPFS=no

# TMPFS_HOME=yes|no
# When TMPFS is enabled the /var filesystem will be put on tmpfs.
# If TMPFS_HOME is 'yes' then the /home filesystem will also reside there.
# 
#TMPFS_HOME=yes
TMPFS_HOME=no

# TMPFS_MIN_FREE=<integer>|""
# The amount of RAM that must be set aside for applications can be specified
# using the TMPFS_MIN_FREE option. bootcd will try it's best to honour this
# setting at boot time.
#
# Example: You have worked out that you need 38MB RAM for your apps to run,
# and TMPFS_SIZE is set to 50%. On a system with 96MB RAM all is okay. On a
# system with 64MB RAM however TMPFS_SIZE will be reduced to 26MB leaving
# 38MB for applications. Since we also use RAMDISK you would probably add a
# few MB to TMPFS_MIN_FREE to compensate.
#
# A warning message will be displayed at boot time if the tmpfs size is
# adjusted due to this setting.
#
# Note: If the files set to go into ram2 will not fit, due to memory
# constraints, then the files will not be copied, and only the directories
# will be created. Additionally the files listed in NOT_TO_RAM or pruned
# with TMPFS_PRUNE_ will still be linked in the newly created directories.
#
# Sizes are specified in MB. Negative or zero sizes disable this boot time
# check.
#
#TMPFS_MIN_FREE=32
TMPFS_MIN_FREE=0

# TMPFS_SIZE=<integer><M|m|%>|auto|""
# Specify the size of the tmpfs partition. If left blank it will be set to
# the kernel's default size. This value can be specified in MB or as a
# percentage only. Tmpfs will be limited to the requested size by the
# kernel.
#
# If TMPFS_SIZE is set to 'auto', bootcd will try to work out the size of
# /var, and /home (if TMPFS_HOME is set), and add some extra space for it
# to grow  at run-time. It will take NOT_TO_RAM, TMPFS_PRUNE_VAR and
# TMPFS_PRUNE_HOME into consideration when making the calculation.
#
# This size may be altered at run-time depending on the value of
# TMPFS_MIN_FREE.
#
# When using tmpfs, ALL of /var will be loaded into RAM at boot time apart
# from the files/directories listed in NOT_TO_RAM. Also consider using
# TMPFS_PRUNE_VAR to automatically exclude large files from /var.
#
# 'M' or '%' suffixes must be specified unless, either 'auto' is used, or
# it is left blank (kernel default is used).
#
# Only the first non-space character after the number is important
#
# Examples:
#   TMPFS_SIZE=100M
#   TMPFS_SIZE=120m
#   TMPFS_SIZE=400 Mb 
#   TMPFS_SIZE=40%
#   TMPFS_SIZE=auto
#   TMPFS_SIZE=
TMPFS_SIZE=auto

# TMPFS_SIZE_AUTO_ADD=<integer>|""
# When TMPFS_SIZE=auto is set, a default of 5MB will be added to the size of
# the tmpfs filesystem. This can be changed by setting TMPFS_SIZE_AUTO_ADD to a
# value that reflects the amount you expect the tmpfs filesystem (/ram2) to
# grow by.
#
# TMPFS_SIZE_AUTO_ADD is specified in MB and may be changed at run-time if
# the TMPFS_MIN_FREE value is set.
#
# If TMPFS_SIZE is not 'auto' then this setting has no effect.
# TMPFS_SIZE has a minimum value of 5MB.
# TMPFS_SIZE="" will be set to 5MB.
#
# Examples:
# Work out the size /var and/or /home will take up in memory (TMPFS_SIZE=
# auto) and add 100 MB to it.
#   TMPFS_SIZE_AUTO_ADD=100
# Only allow 20MB for TMPFS to grow
#   TMPFS_SIZE_AUTO_ADD=20
TMPFS_SIZE_AUTO_ADD=

# TMPFS_PRUNE_VAR=<integer>|""
# It is possible to automatically prune files in /var which are greater than
# some size, in megabytes, so they will not be loaded into memory at boot time.
# The RAM saving will be shown when bootcdwrite is run, and details will be
# shown in the log.
#
# Leaving this option empty or setting to 0 disables this action.
#
# Note: The minimum value for this setting is 100 kb. 
#
# Note: The number of files excluded from RAM is currently limited to the 30
#       largest files it finds per TMPFS_PRUNE_ option. 
#
# Examples:
# Exclude files in /var that exceed 1MB:
#   TMPFS_PRUNE_VAR=1024
# Exclude files in /var that exceed .5MB (The recommended minimum):
#   TMPFS_PRUNE_VAR=512
# Exclude files in /var that exceed 100kB (The minimum):
#   TMPFS_PRUNE_VAR=100
# Don't exclude any files - just use the NOT_TO_RAM setting:
#   TMPFS_PRUNE_VAR=0
#   TMPFS_PRUNE_VAR=
# 
#TMPFS_PRUNE_VAR=1024
TMPFS_PRUNE_VAR=0

# TMPFS_PRUNE_HOME=<integer>|""
# It is also possible to prune files in /home which are greater than some
# configurable size. This only makes sense if TMPFS_HOME=yes.
#
# The same rules apply as with TMPFS_PRUNE_VAR above.
#
#TMPFS_PRUNE_HOME=1024
TMPFS_PRUNE_HOME=0

# TMPFS_TMP=yes|bind|no
# Some programs need to briefly write big files to /tmp. Set TMPFS_TMP to yes
# to mount tmpfs on /tmp at boot time. The default kernel size is used to mount
# this filesystem. No programs should write large files to /tmp and then not
# delete them as this option does not have the same level of checks performed
# for /var and /home.
#
# It is probably safer to use TMPFS_TMP=bind, which will create /ram2/tmp and
# then bind it to /tmp.
#
# Only use this option (bind or yes) if you really need it. However it is
# probably preferable to use 'bind' rather than increase the size of /ram1.
#
#TMPFS_TMP=yes
#TMPFS_TMP=bind
TMPFS_TMP=no
  
# Example
#TMPFS=yes
#TMPFS_HOME=yes
#TMPFS_MIN_FREE=38
#TMPFS_SIZE=auto
#TMPFS_SIZE_AUTO_ADD=10
#TMPFS_PRUNE_VAR=1024
#TMPFS_PRUNE_HOME=1024
#TMPFS_TMP=bind

<---- snip ---->

--------------------------------------------------------------------------
2. Patch file for /usr/bin/bootcdwrite
--------------------------------------------------------------------------
Notes:
  Save the lines between '<---- snip ---->' to a file named patch2.
  To patch :-
  $ su
  $ cd /usr/bin/
  $ patch -p0 bootcdwrite </path/to/patch2

<---- snip ---->
--- /usr/bin/bootcdwrite	2004-06-03 12:29:41.000000000 +0100
+++ bootcdwrite	2004-07-01 16:47:16.000000000 +0100
@@ -474,7 +474,6 @@
   run mv $VAR/input_dir/etc $VAR/input_dir/etc.ro
   run mv $VAR/input_dir/dev $VAR/input_dir/dev.ro
   run "cd $CHANGES; find . | cpio --quiet -pdum $VAR/input_dir/"
-  [ "$DEVFS" = "yes" ] && run rm -r $VAR/input_dir/dev.ro
 
   echo "--- Compressing input_dir to compressed_dir ---" | tee -a $ERRLOG
   run "rm -rf $VAR/compressed_dir; mkzftree $VAR/input_dir $VAR/compressed_dir"
@@ -598,7 +597,8 @@
 ERRLOG VAR DO_CHECK BLANKING CDSCSI CDSPEED CDDEV DISPLAY FASTBOOT \
 FLOPPY_RUNTIME_DEV FLOPPY_CREATE_DEV BOOTFLOPPY BOOT_ONLY_WITH_FLOPPY \
 CLEAN_VAR ISO_ONLY SYSLINUX_SAVE ISOLINUX ARCH INITRD DEVFS TO_FSTAB TYP
-COMPRESS DISABLE_CRON"
+COMPRESS DISABLE_CRON TMPFS TMPFS_SIZE TMPFS_PRUNE_VAR TMPFS_HOME \
+TMPFS_MIN_FREE TMPFS_TMP TMPFS_PRUNE_HOME TMPFS_SIZE_AUTO_ADD"
 
 unset $CONFVARS
 . $CONFDIR/bootcdwrite.conf
@@ -678,12 +678,17 @@
 run mkdir -p $VAR/mnt $CHANGES/proc $CHANGES/ram1 $CHANGES/ram2
 
 # at Boottime /etc -> /ram1/etc -> /etc.ro
-DEVEX="etc tmp dev home root"
-[ "$DEVFS" = "yes" ] && DEVEX="etc tmp home root"
-for i in $DEVEX; do run ln -sf /$i.ro $CHANGES/ram1/$i; done
-for i in var; do run ln -sf /$i.ro $CHANGES/ram2/$i; done
-for i in $DEVEX; do  run ln -sf /ram1/$i $CHANGES/$i; done
-for i in var; do  run ln -sf /ram2/$i $CHANGES/$i; done
+if [ "$TMPFS" = "yes" -a "$TMPFS_HOME" = "yes" ]; then
+  TRAM1="etc tmp dev root"
+  TRAM2="var home"
+else
+  TRAM1="etc tmp dev root home"
+  TRAM2="var"
+fi
+for i in $TRAM1; do run ln -sf ../$i.ro $CHANGES/ram1/$i; done
+for i in $TRAM2; do run ln -sf /$i.ro $CHANGES/ram2/$i; done
+for i in $TRAM1; do  run ln -sf ram1/$i $CHANGES/$i; done
+for i in $TRAM2; do  run ln -sf /ram2/$i $CHANGES/$i; done
 
 if [ "$CLEAN_VAR" = "yes" -a ! "$ONLY_FLOPPY" ]; then
   run apt-get clean # to clear some diskspace in /var 
@@ -694,10 +699,8 @@
 run chmod 777 $CHANGES/tmp.ro
 run mkdir $CHANGES/etc.ro
 run chmod 755 $CHANGES/etc.ro
-[ "$DEVFS" = "yes" ] && {
-  run mkdir $CHANGES/dev.ro
-  run chmod 755 $CHANGES/dev.ro
-}
+run mkdir $CHANGES/dev.ro
+run chmod 755 $CHANGES/dev.ro
 
 run ln -sf /proc/mounts $CHANGES/etc.ro/mtab
 mkdir -p $CHANGES/etc.ro/rcS.d
@@ -724,11 +727,240 @@
   fi
 done
 
+# TMPFS initialisations
+if [ "$TMPFS" = "yes" ]; then
+  # define the maximum number of files that will be added to NOT_TO_RAM
+  # when the user asks for files to be pruned with TMPFS_PRUNE_.
+  NTR_MAX=40
+  # First, initialise the variables that will go in thisbootcd.conf
+  TMPFS_AUTO=0
+  TMPFS_TYPE=`echo $TMPFS_SIZE | tr -d '0123456789 '`
+  case $TMPFS_TYPE in
+    M*|m*)
+      TMPFS_SIZE_MB=${TMPFS_SIZE%%[A-Za-z]*}
+      TMPFS_OPTS="\"-o size=$((TMPFS_SIZE_MB*1048576))\""
+      TMPFS_SIZE_PC=
+      ;;
+    %)
+      TMPFS_OPTS=
+      TMPFS_SIZE_PC=${TMPFS_SIZE%%[A-Za-z%]*}
+      ;;
+    auto)
+      # Work out size of var and/or home. Defer this 'till later
+      TMPFS_AUTO=1
+      TMPFS_SIZE_PC=
+      ;;
+    *)
+      TMPFS_OPTS=
+      TMPFS_SIZE_PC=
+      ;;
+  esac
+  TMPFS_VAR_SIZE=0
+  TMPFS_HOME_SIZE=0
+  #
+  # Now exclude files in /var that are above Xmb as requested by user
+  # in the TMPFS_PRUNE_VAR variable. These are added to TMPFS_NOT_TO_RAM
+  # and the size of var is stored in TMPFS_VAR_SIZE
+  #
+  TMPFS_NOT_TO_RAM=
+  : $((++TMPFS_PRUNE_VAR)); : $((--TMPFS_PRUNE_VAR))
+  if [ $TMPFS_PRUNE_VAR -ne 0 ];then
+    [ $TMPFS_PRUNE_VAR -lt 100 ] && TMPFS_PRUNE_VAR=100
+    TMPFS_VAR_PRINT="yes"
+  else
+    # Make TMPFS_PRUNE_VAR stupdily large so it prunes 0 files
+    TMPFS_PRUNE_VAR=10240000
+    TMPFS_VAR_PRINT="no"
+  fi
+  T1=$TMPFS_PRUNE_VAR
+  [ $TMPFS_VAR_PRINT = "yes" ] && {
+    echo -n "--- Pruning /var files exceeding $T1 KB .. Pruned by " \
+    | tee -a $ERRLOG
+  }
+  # Build TMPFS_NOT_TO_RAM for /var and the exclusion list for find
+  TMPFS_EXCL=
+  TMPFS_NOT_TO_RAM=
+  T1=`echo "$NOT_TO_RAM $LOCATEDB $VAR" \
+    | sed "s#\(^\|[[:space:]]\)$SRCDISK# #g;s#\(^\|[[:space:]]\)/# #g"`
+  for i in $T1; do
+    [[ $i == "var/"* ]] && {
+      TMPFS_NOT_TO_RAM=`echo -en "$i\n$TMPFS_NOT_TO_RAM"`
+      TMPFS_EXCL="-path ${i#/} -prune -o $TMPFS_EXCL"
+    }
+  done
+  # Work out the MB saving for pruned files...
+  T1=`cd $SRCDISK; find var $TMPFS_EXCL -type f \
+    -size +${TMPFS_PRUNE_VAR}k -printf "%k %p\n" \
+    | sort -n | tail -$NTR_MAX | sed 's/^[0-9]*[[:space:]]\(.*\)/"\1"/' \
+    | xargs -r du -lskc | tail -1 | sed 's/[[:space:]].*$//'`
+  T1=$((T1/1024))
+  [ $TMPFS_VAR_PRINT = "yes" ] && echo "$T1 MB ---"  | tee -a $ERRLOG
+  # TMPFS_NOT_TO_RAM = $NOT_TO_RAM + pruned files
+  #                    + var/spool/bootcd + locatedb
+  T1="`cd $SRCDISK; find var $TMPFS_EXCL -type f \
+    -size +${TMPFS_PRUNE_VAR}k -printf "%k %p\n" \
+    | sort -n | tail -$NTR_MAX | sed 's/^[0-9]*[[:space:]]\(.*\)/\1/'`"
+  #---
+  TMPFS_VAR_NOT_TO_RAM=`echo -en "$TMPFS_NOT_TO_RAM\n$T1"`
+  TMPFS_NOT_TO_RAM="$TMPFS_VAR_NOT_TO_RAM"
+  #---
+  echo "TMPFS_NOT_TO_RAM (VAR) = <$TMPFS_NOT_TO_RAM>" >>$ERRLOG
+  # MB saving for all excluded /var files
+  TMPFS_SAVED_MB=`echo -e "$TMPFS_NOT_TO_RAM" \
+  | while read -d$'\x0a' file; do cd $SRCDISK; \
+  [ ! -z "$file" ] && du -lsk "$file"; done \
+  | awk 'BEGIN {tot=0};{tot=tot+$1}; END {print (tot)}'`
+  TMPFS_TOTAL_MB=`(cd $SRCDISK; du -lsk var)`
+  TMPFS_TOTAL_MB=${TMPFS_TOTAL_MB%%[[:space:]]*}
+  TMPFS_TOTAL_MB=$((TMPFS_TOTAL_MB/1024))
+  TMPFS_SAVED_MB=$((TMPFS_SAVED_MB/1024))
+  TMPFS_SAVED_MB=${TMPFS_SAVED_MB%%[.A-Za-z]*}
+  TMPFS_TOTAL_MB=${TMPFS_TOTAL_MB%%[.A-Za-z]*}
+  #---
+  TMPFS_VAR_SIZE=$((TMPFS_TOTAL_MB-TMPFS_SAVED_MB+1))
+  #---
+  #
+  # Now exclude files in /home that are above Xmb as requested by user
+  # in the TMPFS_PRUNE_HOME variable. These are added to TMPFS_NOT_TO_RAM
+  # and the size of home is stored in TMPFS_HOME_SIZE
+  #
+  : $((++TMPFS_PRUNE_HOME)); : $((--TMPFS_PRUNE_HOME))
+  [ "$TMPFS_HOME" = "yes" ] && {
+    if [ $TMPFS_PRUNE_HOME -ne 0 ];then
+      [ $TMPFS_PRUNE_HOME -lt 100 ] && TMPFS_PRUNE_VAR=100
+      TMPFS_HOME_PRINT="yes"
+    else
+      # Make TMPFS_PRUNE_VAR stupdily large so it prunes 0 files
+      TMPFS_PRUNE_HOME=10240000
+      TMPFS_HOME_PRINT="no"
+    fi
+    T1=$TMPFS_PRUNE_HOME
+    [ $TMPFS_HOME_PRINT = "yes" ] && {
+      echo -n "--- Pruning /home files exceeding $T1 KB .. Pruned by " \
+      | tee -a $ERRLOG
+    }
+    # Build TMPFS_NOT_TO_RAM for /var and the exclusion list for find
+    TMPFS_EXCL=
+    TMPFS_NOT_TO_RAM=
+    T1=`echo $NOT_TO_RAM | sed "s#\(^\|[[:space:]]\)$SRCDISK# #g;s#\(^\|[[:space:]]\)/# #g"`
+    for i in $T1; do
+      [[ $i == "home/"* ]] && {
+        TMPFS_NOT_TO_RAM=`echo -en "$i\n$TMPFS_NOT_TO_RAM"`
+        TMPFS_EXCL="-path ${i#/} -prune -o $TMPFS_EXCL"
+      }
+    done
+    # Work out the MB saving for pruned files...
+    T1=`cd $SRCDISK; find home $TMPFS_EXCL -type f \
+      -size +${TMPFS_PRUNE_HOME}k -printf "%k %p\n" \
+      | sort -n | tail -$NTR_MAX | sed 's/^[0-9]*[[:space:]]\(.*\)/"\1"/' \
+      | xargs -r du -lskc | tail -1 | sed 's/[[:space:]].*$//'`
+    T1=$((T1/1024))
+    [ $TMPFS_HOME_PRINT = "yes" ] && echo "$T1 MB ---"  | tee -a $ERRLOG
+    # TMPFS_NOT_TO_RAM = $NOT_TO_RAM + pruned files
+    #                    + var/spool/bootcd + locatedb
+    T1="`cd $SRCDISK; find home $TMPFS_EXCL -type f \
+      -size +${TMPFS_PRUNE_HOME}k -printf "%k %p\n" \
+      | sort -n | tail -$NTR_MAX | sed 's/^[0-9]*[[:space:]]\(.*\)/\1/'`"
+    #---
+    TMPFS_HOME_NOT_TO_RAM=`echo -en "$TMPFS_NOT_TO_RAM\n$T1"`
+    TMPFS_NOT_TO_RAM="$TMPFS_HOME_NOT_TO_RAM"
+    #---
+    echo "TMPFS_NOT_TO_RAM (HOME) = <$TMPFS_NOT_TO_RAM>" >>$ERRLOG
+    # MB saving for all excluded /var files
+    TMPFS_SAVED_MB=`echo -e "$TMPFS_NOT_TO_RAM" \
+      | while read -d$'\x0a' file; do cd $SRCDISK; \
+      [ ! -z "$file" ] && du -lsk "$file"; done \
+      | awk 'BEGIN {tot=0};{tot=tot+$1}; END {print (tot)}'`
+    TMPFS_TOTAL_MB=`(cd $SRCDISK; du -lsk home)`
+    TMPFS_TOTAL_MB=${TMPFS_TOTAL_MB%%[[:space:]]*}
+    TMPFS_TOTAL_MB=$((TMPFS_TOTAL_MB/1024))
+    TMPFS_SAVED_MB=$((TMPFS_SAVED_MB/1024))
+    TMPFS_SAVED_MB=${TMPFS_SAVED_MB%%[.A-Za-z]*}
+    TMPFS_TOTAL_MB=${TMPFS_TOTAL_MB%%[.A-Za-z]*}
+    #---
+    TMPFS_HOME_SIZE=$((TMPFS_TOTAL_MB-TMPFS_SAVED_MB+1))
+    #---
+  }
+  #
+  # Make sure $TMPFS_TMP is tidy for inclusion in thisbootcd.conf
+  #
+  if [ "$TMPFS_TMP" = "yes" ]; then
+    TMPFS_TMP=yes
+  elif [ "$TMPFS_TMP" = "bind" ]; then
+    TMPFS_TMP=bind
+  else
+    TMPFS_TMP=no
+  fi
+  #
+  # Make sure $TMPFS_HOME is tidy for inclusion in thisbootcd.conf
+  #
+  if [ "$TMPFS_HOME" = "yes" ]; then
+    TMPFS_HOME=yes
+  else
+    TMPFS_HOME=no
+  fi
+  #
+  # Make sure $TMPFS_MIN_FREE is tidy for inclusion in thisbootcd.conf
+  #
+  if [[ -z "$TMPFS_MIN_FREE" || "$TMPFS_MIN_FREE" == -* ]]; then
+    TMPFS_MIN_FREE=0
+  else
+    : $((++TMPFS_MIN_FREE))
+    : $((--TMPFS_MIN_FREE))
+  fi
+  #
+  # We're using tmpfs - remake bootcdram so it includes TMPFS_NOT_TO_RAM
+  #
+  TMPFS_NOT_TO_RAM=`echo -e "$TMPFS_VAR_NOT_TO_RAM\n$TMPFS_HOME_NOT_TO_RAM"`
+  T2=`for i in $TMPFS_NOT_TO_RAM; do \
+    if [[ "$i" != "$SRCDISK"* ]]; then \
+    echo -n "$SRCDISK/$i "; else \
+    echo -n "$i "; fi; done`
+  run "mk_bootcdram \"$SRCDISK\" \"$T2\" \
+    >$CHANGES/etc.ro/rcS.d/S12bootcdram.sh"
+  #
+  # Now work out the size of RAM2
+  #
+  : $((++TMPFS_SIZE_AUTO_ADD)) 
+  : $((--TMPFS_SIZE_AUTO_ADD)) 
+  [ $TMPFS_SIZE_AUTO_ADD -lt 5 ] && TMPFS_SIZE_AUTO_ADD=5
+  # TMPFS_RAM2_SIZE is stored in thisbootcd.conf for boot time checks
+  echo "--- Size of ram2 is less than $((TMPFS_HOME_SIZE+TMPFS_VAR_SIZE)) MB ---" \
+    | tee -a $ERRLOG
+  TMPFS_RAM2_SIZE=$((TMPFS_HOME_SIZE+TMPFS_VAR_SIZE))
+  #
+  # Tmpfs size calculation, deferred from the case block earlier.
+  # If TMPFS_SIZE=auto is set then we need to set the size
+  # of tmpfs for ram2. This value is stored in thisbootcd.conf.
+  #
+  [ $TMPFS_AUTO -eq 1 ] && {
+    TMPFS_SZ=$((TMPFS_HOME_SIZE+TMPFS_VAR_SIZE+TMPFS_SIZE_AUTO_ADD))
+    TMPFS_OPTS="\"-o size=$((TMPFS_SZ*1024*1024))\""
+    echo "--- TMPFS size (auto) set to $TMPFS_SZ MB ---" \
+      | tee -a $ERRLOG
+  }
+else
+  TMPFS="no"
+  TMPFS_OPTS=
+  TMPFS_SIZE_PC=
+  TMPFS_HOME="no"
+  TMPFS_TMP="no"
+  TMPFS_MIN_FREE=
+  TMPFS_RAM2_SIZE=0
+fi
+
 (
 echo "KERNEL=$REL_KERNEL"
 echo "INITRD=$REL_INITRD"
 echo "DISABLE_CRON=\"$(chnglist -no_mnt "$SRCDISK" "$DISABLE_CRON")\""
 echo "ARCH=$ARCH"
+echo "TMPFS=$TMPFS"
+echo "TMPFS_OPTS=$TMPFS_OPTS"
+echo "TMPFS_SIZE_PC=$TMPFS_SIZE_PC"
+echo "TMPFS_HOME=$TMPFS_HOME"
+echo "TMPFS_TMP=$TMPFS_TMP"
+echo "TMPFS_MIN_FREE=$TMPFS_MIN_FREE"
+echo "TMPFS_RAM2_SIZE=$TMPFS_RAM2_SIZE"
 ) >$CHANGES/etc.ro/bootcd/thisbootcd.conf
 
 run "cat <<END > $CHANGES/etc.ro/fstab
@@ -781,13 +1013,15 @@
 
   run mkdir $VAR/ram1
   mkdir $VAR/ram1/tmp; chmod 777 $VAR/ram1/tmp
-  FG=$(mk_grep $(chnglist -rel -no_mnt "$SRCDISK" "$NOT_TO_RAM $NOT_TO_CD"))
+  FG=$(mk_grep $(chnglist -rel -no_mnt "$SRCDISK" "${TMPFS_LOCATEDB#/} $NOT_TO_RAM $NOT_TO_CD"))
   echo "FG (FASTBOOT GREP) = <$FG>" >>$ERRLOG
 
   if [ "$DEVFS" = "yes" ]; then 
     CPIODIR="home root etc"
+    [ "$TMPFS_HOME" = "yes" ] && CPIODIR="root etc"
   else
     CPIODIR="home root etc dev"
+    [ "$TMPFS_HOME" = "yes" ] && CPIODIR="root etc dev"
   fi
   for i in $CPIODIR; do 
     run "cd $SRCDISK; find $i | $FG | cpio --quiet -pdum $VAR/ram1"
@@ -806,9 +1040,18 @@
   run "rm -r $VAR/ram1"
   
   run mkdir $VAR/ram2
-  run "cd $SRCDISK
-       find var -type d -or -type l | grep -v -e '^var/spool/bootcd' |
-         $FG | cpio --quiet -pdum $VAR/ram2"
+  if [ ! "$TMPFS" = "yes" ]; then
+    run "cd $SRCDISK
+         find var -type d -or -type l | grep -v -e '^var/spool/bootcd' |
+           $FG | cpio --quiet -pdum $VAR/ram2"
+  else
+    FG=$(mk_grep $(chnglist -rel -no_mnt "$SRCDISK" "$TMPFS_NOT_TO_RAM $NOT_TO_CD"))
+    TRAM2="var home"
+    [ ! "$TMPFS_HOME" = "yes" ] && TRAM2="var"
+    run "cd $SRCDISK
+         find $TRAM2 -type f -or -type d -or -type l | grep -v -e '^var/spool/bootcd' |
+           $FG | cpio --quiet -pdum $VAR/ram2"
+  fi
 
   ignore "cpio: .*: truncating inode number"
   run "cd $VAR/ram2
<---- snip ---->

--------------------------------------------------------------------------
3. Patch file for /usr/share/bootcd/S12bootcdram
--------------------------------------------------------------------------
Notes:
  Save the lines between '<---- snip ---->' to a file named patch3.
  To patch :-
  $ su
  $ cd /usr/share/bootcd
  $ patch -p0 S12bootcdram </path/to/patch3

<---- snip ---->
--- /usr/share/bootcd/S12bootcdram.sh	2004-06-03 12:29:41.000000000 +0100
+++ S12bootcdram.sh	2004-07-01 03:28:31.000000000 +0100
@@ -5,18 +5,35 @@
 CHNG="<CHNG>"
 CHNGGREP="<CHNGGREP>"
 
+# Total Memory in mb
+MTOT=`cat /proc/meminfo | grep -i memtotal | awk '{print $2}'`
+MTOT=$((MTOT/1024))
+
+DO_NOT_COPY=0
+
+# Source per cd configuration
+[ -f /etc/bootcd/thisbootcd.conf ] && {
+  . /etc/bootcd/thisbootcd.conf
+}
+
 # INODES are expensive (8192 INODES need 1 MB RAM)
 echo -n "Minimum of needed INODES: "
 if [ -f /ram1.cpio.gz ]; then
   I1=$(zcat /ram1.cpio.gz | cpio -it | wc -l)
 else
-  I1=$(find /home.ro /root.ro /etc.ro /dev.ro | $CHNGGREP | wc -l)
+  HOME="/home.ro"
+  [ "$TMPFS" = "yes" -a "$TMPFS_HOME" = "yes" ] && HOME=
+  I1=$(find $HOME /root.ro /etc.ro /dev.ro | $CHNGGREP | wc -l)
 fi
 
-if [ -f /ram2.cpio.gz ]; then
-  I2=$(zcat /ram2.cpio.gz | cpio -it | wc -l)
+if [ ! "$TMPFS" = "yes" ]; then
+  if [ -f /ram2.cpio.gz ]; then
+    I2=$(zcat /ram2.cpio.gz | cpio -it | wc -l)
+  else
+    I2=$(find /var.ro -type d | $CHNGGREP | wc -l)
+  fi
 else
-  I2=$(find /var.ro -type d | $CHNGGREP | wc -l)
+  I2=0
 fi
 
 I=$I1; [ $I2 -gt $I ] && I=$I2
@@ -28,7 +45,11 @@
 # Use at least $I inodes per ramdisk
 if [ $RAMDISK_SIZE -le $I ]; then 
   INODES="-N $I"
-  echo "Creating ram1 and ram2 with exactly $I INODES each"
+  if [ ! "$TMPFS" = "yes" ]; then
+    echo "Creating ram1 and ram2 with exactly $I INODES each"
+  else
+    echo "Creating ram1 with exactly $I INODES"
+  fi
 else
   INODES="-i 1024"
   echo "Creating ram with 1024 INODES per MB"
@@ -50,18 +71,142 @@
   cd /ram1; zcat /ram1.cpio.gz | cpio -idum
 else
   mkdir /ram1/tmp; chmod 777 /ram1/tmp
-  for i in home root etc dev; do find /$i.ro | $CHNGGREP | cpio -pdm /ram1; done
-  for i in home root etc dev; do mv /ram1/$i.ro /ram1/$i; done
+  TRAM1="home root etc"
+  [ "$TMPFS_HOME" = "yes" ] && TRAM1="root etc"
+  [ ! -c /dev/.devfsd ] && TRAM1="$TRAM1 dev"
+  for i in $TRAM1; do find /$i.ro | $CHNGGREP | cpio -pdm /ram1; done
+  for i in $TRAM1; do mv /ram1/$i.ro /ram1/$i; done
+fi
+
+# Work out a percentage of total RAM.
+# Stores number of bytes in MP.
+# E.g. pc_mem 50
+MPC=
+pc_mem() {
+  MPC=$(((MTOT*$1)/100))
+  return 0;
+}
+
+# Not enough ram available to allow requested size _and_ leave
+# TMPFS_MIN_FREE ram available. recalc tmpfs size to honour TMPFS_MIN_FREE
+recalc_tmpfs() {
+  local tmpfs_size=$((MTOT-TMPFS_MIN_FREE))
+  local need=$((TMPFS_MIN_FREE+$1+(RAMDISK_SIZE/1024)))
+  [ $tmpfs_size -le 0 ] && {
+    echo "***"
+    echo -n "*** WARNING: Not enough RAM available to honour"
+    echo " memory requirements."
+    echo "***          You need at least ${need} MB Physical RAM."
+    echo "***"
+    echo "***          Aborting copy. Files will not fit in RAM!"
+    echo "***          Only the directory structures will be created."
+    echo "***"
+
+    /bin/sleep 10
+
+    mount -t tmpfs ram2fs /ram2
+
+    DO_NOT_COPY=1
+    
+    return 0;
+  }
+  
+  echo "***"
+  echo "*** WARNING: Not enough RAM available to honour memory requirements."
+  echo "***          I have allowed ${tmpfs_size} MB Physical RAM for tmpfs."
+  echo "***          ( The configuration requested $1 MB for tmpfs. )"
+  
+  [[ $tmpfs_size -lt $TMPFS_RAM2_SIZE ]] && {
+    echo "***"
+    echo "***          Aborting copy. Files will not fit in RAM!"
+    echo "***          Only the directory structures will be created."
+    DO_NOT_COPY=1
+  }
+
+  echo "***"
+
+  /bin/sleep 10
+
+  mount -t tmpfs ram2fs /ram2 -o size=$((tmpfs_size*1024*1024))
+
+  return 0;
+}
+
+# See if enough ram available to allow requested size _and_ leave
+# TMPFS_MIN_FREE ram available.
+checkram() {
+  [ "$TMPFS_MIN_FREE" -le 0 ] && return 1;
+  local req=$1
+  local need=$((MTOT-req-TMPFS_MIN_FREE))
+  [ $need -lt 0 ] && return 0;
+  return 1;
+}
+
+if [ ! "$TMPFS" = "yes" ]; then
+  echo "DEBUG: Making RAM2"
+  mke2fs -q $INODES /dev/ram2 # Size is defined by ramdisk_size at boottime
+  mount /dev/ram2 /ram2 -o defaults,rw
+else
+  [ "$TMPFS_TMP" = "yes" ] && {
+    echo "Mounting tmpfs on /tmp ( => /ram1/tmp )"
+    mount -t tmpfs tmpfs /tmp
+  }
+  echo "Using dynamic memory filesystem (tmpfs) for ram2"
+  if [ -z "$TMPFS_OPTS" ]; then
+    if [ -z "$TMPFS_SIZE_PC" ]; then
+      pc_mem 50
+      if checkram "$((MPC))" ; then 
+        recalc_tmpfs "$((MPC))"
+      else
+        mount -t tmpfs ram2fs /ram2
+      fi
+    else
+      pc_mem "$TMPFS_SIZE_PC"
+      if checkram "$MPC" ; then
+        recalc_tmpfs "$MPC"
+      else
+        mount -t tmpfs ram2fs /ram2 -o size=$((MPC*1024*1024))
+      fi
+    fi
+  else
+    T0=${TMPFS_OPTS##*=}
+    T1=$((T0/1024/1024))
+    if checkram "$T1" ; then
+      recalc_tmpfs "$T1"
+    else
+      mount -t tmpfs ram2fs /ram2 $TMPFS_OPTS
+    fi
+  fi
+  [ "$TMPFS_TMP" = "bind" ] && {
+    echo "Binding /tmp to /ram2/tmp"
+    mkdir /ram2/tmp
+    chmod 1777 /ram2/tmp
+    mount --bind /ram2/tmp /tmp
+  }
 fi
 
-mke2fs -q $INODES /dev/ram2 # Size is defined by ramdisk_size at boottime
-mount /dev/ram2 /ram2 -o defaults,rw
-if [ -f /ram2.cpio.gz ]; then
+
+if [ $DO_NOT_COPY -eq 1 ]; then
+  find /var.ro -type d | $CHNGGREP | cpio -pdm /ram2
+  [ "$TMPFS_HOME" = "yes" ] && \
+    find /home.ro -type d | $CHNGGREP | cpio -pdm /ram2
+  HOME=
+  [ "$TMPFS" = yes -a "$TMPFS_HOME" = yes ] && HOME="home"
+  for i in var $HOME; do mv /ram2/$i.ro /ram2/$i; done
+elif [ -f /ram2.cpio.gz ]; then
   echo 'Extracting /ram2.cpio.gz'
   cd /ram2; zcat /ram2.cpio.gz | cpio -idum
 else
-  find /var.ro -type d | $CHNGGREP | cpio -pdm /ram2
-  for i in var; do mv /ram2/$i.ro /ram2/$i; done
+  if [ ! "$TMPFS" = "yes" ]; then
+    find /var.ro -type d | $CHNGGREP | cpio -pdm /ram2
+  else
+    find /var.ro -type d -o -type f | $CHNGGREP | cpio -pdm /ram2
+    [ "$TMPFS_HOME" = "yes" ] && \
+      find /home.ro -type d -o -type f | $CHNGGREP | cpio -pdm /ram2
+  fi
+  HOME=
+  [ "$TMPFS" = yes -a "$TMPFS_HOME" = yes ] && HOME="home"
+  for i in var $HOME; do mv /ram2/$i.ro /ram2/$i; done
 fi
 
 # enable wtmp and lastlog record keeping
@@ -74,3 +219,9 @@
   mkdir -p $(dirname $j)
   ln -s $i $j
 done
+
+# If /ram1/root was in NOT_TO_RAM then we need to make it now.
+[ ! -d /ram1/root ] && {
+  mkdir -p /ram1/root
+  chmod 0750 /ram1/root
+}
<---- snip ---->

--------------------------------------------------------------------------
4. patch file for /usr/share/bootcd/bootcd-check.lib
--------------------------------------------------------------------------
notes:
  save the lines between '<---- snip ---->' to a file named patch4.
  to patch :-
  $ su
  $ cd /usr/share/bootcd
  $ patch -p0 bootcd-check.lib </path/to/patch4

<---- snip ---->
--- /usr/share/bootcd/bootcd-check.lib	2004-06-03 12:29:41.000000000 +0100
+++ bootcd-check.lib	2004-06-25 23:22:00.000000000 +0100
@@ -224,8 +224,13 @@
   [ "$NOT_TO_RAM$NOT_TO_CD" ] && S_NOT_TO_RAMCD=$(du_dir $NOT_TO_RAM $NOT_TO_CD)
   echo "S_NOT_TO_RAMCD=<$S_NOT_TO_RAMCD>" >> $ERRLOG
 
-  S_RAM_ALL=$(du_dir $SRCDI/etc $SRCDI/home $SRCDI/root $NOT_TO_RAM $NOT_TO_CD)
-  echo "S_RAM_ALL=<$S_RAM_ALL>" >> $ERRLOG
+  if [ "$TMPFS" = "yes" -a "$TMPFS_HOME" = "yes" ];then
+    S_RAM_ALL=$(du_dir $SRCDI/etc $SRCDI/root $NOT_TO_RAM $NOT_TO_CD)
+    echo "S_RAM_ALL=<$S_RAM_ALL>" >> $ERRLOG
+  else
+    S_RAM_ALL=$(du_dir $SRCDI/etc $SRCDI/home $SRCDI/root $NOT_TO_RAM $NOT_TO_CD)
+    echo "S_RAM_ALL=<$S_RAM_ALL>" >> $ERRLOG
+  fi
 
   S_NEED_RAM=$(expr $S_RAM_ALL - $S_NOT_TO_RAMCD)
   echo "S_NEED_RAM=<$S_NEED_RAM>" >> $ERRLOG
@@ -590,8 +595,13 @@
   echo -n "NOT_TO_RAMCD (NOT_TO_RAM v NOT_TO_CD) =  . . . . " | tee -a $ERRLOG
   echo ". . . . . . . . . $S_NOT_TO_RAMCD"| tee -a $ERRLOG
 
-  echo -n "RAM_ALL (etc v home v root v NOT_TO_RAM v NOT_TO_" | tee -a $ERRLOG
-  echo "CD) = . . . . . . $S_RAM_ALL" | tee -a $ERRLOG
+  if [ "$TMPFS" = "yes" -a "$TMPFS_HOME" = "yes" ];then
+    echo -n "RAM_ALL (etc v root v NOT_TO_RAM v NOT_TO_CD) =  " | tee -a $ERRLOG
+    echo ". . . . . . . . . $S_RAM_ALL" | tee -a $ERRLOG
+  else
+    echo -n "RAM_ALL (etc v home v root v NOT_TO_RAM v NOT_TO_" | tee -a $ERRLOG
+    echo "CD) = . . . . . . $S_RAM_ALL" | tee -a $ERRLOG
+  fi
 
   echo -n "Needed RAM (RAM_ALL - NOT_TO_RAMCD) =  . . . . . " | tee -a $ERRLOG
   echo ". . . . . . . . . $S_NEED_RAM" | tee -a $ERRLOG
<---- snip ---->

--------------------------------------------------------------------------
5. patch file for /usr/share/bootcd/bootcd2disk
--------------------------------------------------------------------------
notes:
  save the lines between '<---- snip ---->' to a file named patch4.
  to patch :-
  $ su
  $ cd /usr/share/bootcd
  $ patch -p0 bootcd2disk </path/to/patch5

<---- snip ---->
--- /usr/share/bootcd/bootcd2disk	2004-06-03 12:29:41.000000000 +0100
+++ bootcd2disk	2004-06-21 13:17:51.000000000 +0100
@@ -27,7 +27,7 @@
 done
 
 CONFVARS="DISK SFDISK EXT2FS EXT3 SWAP MOUNT UMOUNT FSTAB LILO SSHHOSTKEY \
-  VFAT ELILO"
+  VFAT ELILO TMPFS TMPFS_OPTS TMPFS_SIZE_PC TMPFS_HOME"
 CREATEVARS="KERNEL INITRD DISABLE_CRON ARCH"
 unset $CONFVARS $CREATEVARS
 
@@ -302,7 +302,12 @@
 run rm /mnt/etc /mnt/tmp /mnt/dev /mnt/var /mnt/home /mnt/root
 run rm -r /mnt/etc.ro /mnt/tmp.ro /mnt/dev.ro /mnt/home.ro /mnt/root.ro
 run mv /mnt/var.ro /mnt/var
-run cp -a -x /ram1/etc /ram1/tmp /ram1/dev /ram1/home /ram1/root /mnt
+if [ "$TMPFS" == "yes" -a "$TMPFS_HOME" == "yes" ]; then
+  run cp -a -x /ram1/etc /ram1/tmp /ram1/dev /ram1/root /mnt
+  run cp -a -x /ram2/home /mnt
+else
+  run cp -a -x /ram1/etc /ram1/tmp /ram1/dev /ram1/home /ram1/root /mnt
+fi
 run rm -f /mnt/fastboot /mnt/cdboot.catalog /mnt/cdboot.img /mnt/ram1.cpio.gz /mnt/ram2.cpio.gz
 run rm -rf /mnt/rr_moved/ /mnt/isolinux/
 run rm /mnt/etc/rcS.d/S12bootcdram.sh \
<---- snip ---->

END