Compare commits

...

No commits in common. "1.0" and "master" have entirely different histories.
1.0 ... master

13 changed files with 1374 additions and 1572 deletions

555
HOWTO.htm
View File

@ -1,555 +0,0 @@
<style>
body { margin-left: 16px;
margin-right: 16px;
font-size: 12px;
font-family: georgia;
}
tt { color: #444444; }
pre { color: #444444;
background: #eeeeee;
border-left: solid 5px #2222dd;
padding: 1px 1px 1px 7px; }
pre b, tt b { color: #2222dd; }
</style>
<h1>FreeBSD ZFS Madness</h1>
<h3>Slawomir Wojtczak (vermaden)</h3>
<h4>2012/04/27</h4>
<p>Some time ago I found a good, reliable way of using and installing FreeBSD
and described it in my <i>Modern FreeBSD Install</i> <b>[1] [2]</b> HOWTO. Now, more
then a year later I come back with my experiences about that setup and a
proposal of newer and probably better way of doing it.</p>
<hr style="border: 1px solid lightgray">
<h3>1. Introduction</h3>
<p>Same as year ago, I assume that You would want to create fresh installation of
FreeBSD using one or more hard disks, but also with (laptops) and without GELI
based full disk encryption.</p>
<p>This guide was written when FreeBSD 9.0 and 8.3 were available and definitely
works for 9.0, but I did not try all this on the older 8.3, if You find some
issues on 8.3, let me know I will try to address them in this guide.</p>
<p>Earlier, I was not that confident about booting from the ZFS pool, but there
is some very neat feature that made me think ZFS boot is now mandatory. If You
just smiled, You know that I am thinking about <i>Boot Environments</i> feature
from Illumos/Solaris systems.</p> In case You are not familiar with the <i>Boot
Environments</i> feature, check the <i>Managing Boot Environments with Solaris
11 Express</i> PDF white paper <b>[3]</b>. Illumos/Solaris has the
<tt>beadm(1M)</tt> <b>[4]</b> utility and while Philipp Wuensche wrote the
<tt>manageBE</tt> script as replacement <b>[5]</b>, it uses older style used
at times when OpenSolaris (and SUN) were still having a great time.</p>
<p>I spent last couple of days writing an up-to-date replacement for FreeBSD
compatible <tt>beadm</tt> utility, and with some tweaks from today I just made
it available at <i>SourceForge</i> <b>[6]</b> if You wish to test it. Currently its
about 200 lines long, si it should be pretty simple to take a look at it. I
tried to make it as compatible as possible with the 'upstream' version, along
with some small improvements, it currently supports basic functions like list,
create, destroy and activate.</p>
<pre># <b>beadm</b>
usage:
beadm subcommand cmd_options
subcommands:
beadm activate beName
beadm create [-e nonActiveBe | beName@snapshot] beName
beadm create beName@snapshot
beadm destroy beName
beadm destroy beName@snapshot
beadm list</pre>
<p>There are several subtle differences between mine implementation and
Philipp's one, he defines and then relies upon ZFS property called
<tt>freebsd:boot-environment=1</tt> for each boot environment, I do not set
any other additional ZFS properties. There is already <tt>org.freebsd:swap</tt>
property used for SWAP on FreeBSD, so we may use <tt>org.freebsd:be</tt> in the
future, but is just a thought, right now its not used. My version also supports
activating boot environments received with <tt>zfs recv</tt> command from other
systems (it just updates appreciate <tt>/boot/zfs/zpool.cache</tt> file).</p>
My implementation is also style compatible with current Illumos/Solaris
<tt>beadm(1M)</tt> which is like the example below.</p>
<pre># <b>beadm create -e default upgrade-test</b>
Created successfully
# <b>beadm list</b>
BE Active Mountpoint Space Policy Created
default N / 1.06M static 2012-02-03 15:08
upgrade-test R - 560M static 2012-04-24 22:22
new - - 8K static 2012-04-24 23:40
# <b>zfs list -r sys/ROOT</b>
NAME USED AVAIL REFER MOUNTPOINT
sys/ROOT 562M 8.15G 144K none
sys/ROOT/default 1.48M 8.15G 558M legacy
sys/ROOT/new 8K 8.15G 558M none
sys/ROOT/upgrade-test 560M 8.15G 558M none
# <b>beadm activate default</b>
Activated successfully
# <b>beadm list</b>
BE Active Mountpoint Space Policy Created
default NR / 1.06M static 2012-02-03 15:08
upgrade-test - - 560M static 2012-04-24 22:22
new - - 8K static 2012-04-24 23:40</pre>
<p>The boot environments are located in the same plase as in Illumos/Solaris, at
<tt>pool/ROOT/environment</tt> place.</p>
<h3>2. Now You're Thinking with Portals</h3>
<p>The main purpose of the <i>Boot Environments</i> concept is to make all
risky tasks harmless, to provide an easy way back from possible troubles.
Think about upgrading the system to newer version, an update of 30+ installed
packages to latest versions, testing software or various solutions before
taking the final decision, and much more. All these tasks are now harmless
thanks to the <i>Boot Environments</i>, but this is just the tip of the
iceberg.</p>
<p>You can now move desired boot environment to other machine, physical or
virtual and check how it will behave there, check hardware support on the other
hardware for example or make a painless hardware upgrade. You may also clone
Your desired boot environment and ... start it as a Jail for some more
experiments or move Your old physical server install into FreeBSD Jail because
its not that heavily used anymore but it still have to be available.</p>
<p>Other good example may be just created server on Your laptop inside
VirtualBox virtual machine. After you finish the creation process and tests,
You may move this boot environment to the real server and put it into
production. Or even move it into VMware ESX/vSphere virtual machine and use
it there.</p>
<p>As You see the possibilities with <i>Boot Environments</i> are unlimited.</p>
<h3>3. The Install Process</h3>
<p>I created 3 possible schemas which should cover most demands, choose one
and continue to the next step.</p>
<h4>3.1. Server with Two Disks</h4>
<p>I assume that this server has 2 disks and we will create ZFS mirror across
them, so if any of them will be gone the system will still work as usual.
I also assume that these disks are <tt>ada0</tt> and <tt>ada1</tt>. If You
have SCSI/SAS drives there, they may be named <tt>da0</tt> and <tt>da1</tt>
accordingly. The <b>procedures below will wipe all data on these disks</b>,
You have been warned.</p>
<ol>
<li>Boot from the FreeBSD USB/DVD.</li>
<li>Select the '<tt>Live CD</tt>' option.</li>
<li><tt>login: root</tt></li>
<li><tt># sh</tt></li>
<li><tt># DISKS="<b>ada0 ada1</b>"</tt></li>
<li><tt># for I in ${DISKS}; do<br>
> NUMBER=$( echo ${I} | tr -c -d '0-9' )<br>
> gpart destroy -F ${I}<br>
> gpart create -s GPT ${I}<br>
> gpart add -t freebsd-boot -l bootcode${NUMBER} -s 128k ${I}<br>
> gpart add -t freebsd-zfs -l sys${NUMBER} ${I}<br>
> gpart bootcode -b /boot/pmbr -p /boot/gptzfsboot -i 1 ${I}<br>
> done</tt></li>
<li><tt># zpool create -f -o cachefile=/tmp/zpool.cache sys mirror /dev/gpt/sys*</tt></li>
<li><tt># zfs set mountpoint=none sys</tt></li>
<li><tt># zfs set checksum=fletcher4 sys</tt></li>
<li><tt># zfs set atime=off sys</tt></li>
<li><tt># zfs create sys/ROOT</tt></li>
<li><tt># zfs create -o mountpoint=/mnt sys/ROOT/default</tt></li>
<li><tt># zpool set bootfs=sys/ROOT/default sys</tt></li>
<li><tt># cd /usr/freebsd-dist/</tt></li>
<li><tt># for I in base.txz kernel.txz; do<br>
> tar --unlink -xvpJf ${I} -C /mnt<br>
> done</tt></li>
<li><tt># cp /tmp/zpool.cache /mnt/boot/zfs/</tt></li>
<li><tt># cat << EOF >> /mnt/boot/loader.conf<br>
> zfs_load=YES<br>
> vfs.root.mountfrom="zfs:sys/ROOT/default"<br>
> EOF</tt></li>
<li><tt># cat << EOF >> /mnt/etc/rc.conf<br>
> zfs_enable=YES<br>
> EOF</tt></li>
<li><tt># :> /mnt/etc/fstab</tt></li>
<li><tt># zfs umount -a</tt></li>
<li><tt># zfs set mountpoint=legacy sys/ROOT/default</tt></li>
<li><tt># reboot</tt></li>
</ol>
<p>After these instructions and reboot we have these GPT partitions available,
this example is on a 512MB disk.</p>
<pre># <b>gpart show</b>
=> 34 1048509 ada0 GPT (512M)
34 256 1 freebsd-boot (128k)
290 1048253 2 freebsd-zfs (511M)
=> 34 1048509 ada1 GPT (512M)
34 256 1 freebsd-boot (128k)
290 1048253 2 freebsd-zfs (511M)
# <b>gpart list | grep label</b>
label: bootcode0
label: sys0
label: bootcode1
label: sys1
# <b>zpool status</b>
pool: sys
state: ONLINE
scan: none requested
config:
NAME STATE READ WRITE CKSUM
sys ONLINE 0 0 0
mirror-0 ONLINE 0 0 0
gpt/sys0 ONLINE 0 0 0
gpt/sys1 ONLINE 0 0 0
errors: No known data errors</pre>
<h4>3.2. Server with One Disk</h4>
<p>If Your server configuration has only one disk, lets assume its
<tt>ada0</tt>, then You need different points 5. and 7. to make, use these
instead of the ones above.</p>
<ol start="5">
<li><tt># DISKS="<b>ada0</b>"</tt></li>
</ol>
<ol start="7">
<li><tt># zpool create -f -o cachefile=/tmp/zpool.cache sys /dev/gpt/sys*</tt></li>
</ol>
<p>All other steps are the same.</p>
<h4>3.3. Read Warrior Laptop</h4>
<p>The procedure is quite diffrent for Laptop because we will use the full disk
encryption mechanism provided by GELI and then setup the ZFS pool. Its not
currently possible to boot off from the ZFS pool on top of encrypted GELI
provider, so we will use setup similar to the <i>Server with ...</i> one but
with additional <tt>local</tt> pool for <tt>/home</tt> and <tt>/root</tt>
partitions. It will be password based and You will be asked to type-in that
password at every boot. The install process is generally the same with new
instructions added for the GELI encrypted <tt>local</tt> pool, I put them with
<b style="color:green">different color</b> to make the difference more visible.</p>
<ol>
<li>Boot from the FreeBSD USB/DVD.</li>
<li>Select the '<tt>Live CD</tt>' option.</li>
<li><tt>login: root</tt></li>
<li><tt># sh</tt></li>
<li><tt># DISKS="<b>ada0</b>"</tt></li>
<li><tt># for I in ${DISKS}; do<br>
> NUMBER=$( echo ${I} | tr -c -d '0-9' )<br>
> gpart destroy -F ${I}<br>
> gpart create -s GPT ${I}<br>
> gpart add -t freebsd-boot -l bootcode${NUMBER} -s 128k ${I}<br>
> gpart add -t freebsd-zfs -l sys${NUMBER} -s 10G ${I}<br>
> gpart add -t freebsd-zfs -l local${NUMBER} ${I}<br>
> gpart bootcode -b /boot/pmbr -p /boot/gptzfsboot -i 1 ${I}<br>
> done</tt></li>
<li><tt># zpool create -f -o cachefile=/tmp/zpool.cache sys /dev/gpt/sys0</tt></li>
<li><tt># zfs set mountpoint=none sys</tt></li>
<li><tt># zfs set checksum=fletcher4 sys</tt></li>
<li><tt># zfs set atime=off sys</tt></li>
<li><tt># zfs create sys/ROOT</tt></li>
<li><tt># zfs create -o mountpoint=/mnt sys/ROOT/default</tt></li>
<li><tt># zpool set bootfs=sys/ROOT/default sys</tt></li>
<li><tt style="color:green;font-weight:bold"># geli init -b -s 4096 -e AES-CBC -l 128 /dev/gpt/local0</tt></li>
<li><tt style="color:green;font-weight:bold"># geli attach /dev/gpt/local0</tt></li>
<li><tt style="color:green;font-weight:bold"># zpool create -f -o cachefile=/tmp/zpool.cache local /dev/gpt/local0.eli</tt></li>
<li><tt style="color:green;font-weight:bold"># zfs set mountpoint=none local</tt></li>
<li><tt style="color:green;font-weight:bold"># zfs set checksum=fletcher4 local</tt></li>
<li><tt style="color:green;font-weight:bold"># zfs set atime=off local</tt></li>
<li><tt style="color:green;font-weight:bold"># zfs create local/home</tt></li>
<li><tt style="color:green;font-weight:bold"># zfs create -o mountpoint=/mnt/root local/root</tt></li>
<li><tt># cd /usr/freebsd-dist/</tt></li>
<li><tt># for I in base.txz kernel.txz; do<br>
> tar --unlink -xvpJf ${I} -C /mnt<br>
> done</tt></li>
<li><tt># cp /tmp/zpool.cache /mnt/boot/zfs/</tt></li>
<li><tt># cat << EOF >> /mnt/boot/loader.conf<br>
> zfs_load=YES<br>
<tt style="color:green;font-weight:bold">> geom_eli_load=YES</tt><br>
> vfs.root.mountfrom="zfs:sys/ROOT/default"<br>
> EOF</tt></li>
<li><tt># cat << EOF >> /mnt/etc/rc.conf<br>
> zfs_enable=YES<br>
> EOF</tt></li>
<li><tt># :> /mnt/etc/fstab</tt></li>
<li><tt># zfs umount -a</tt></li>
<li><tt># zfs set mountpoint=legacy sys/ROOT/default</tt></li>
<li><tt style="color:green;font-weight:bold"># zfs set mountpoint=/home local/home</tt></li>
<li><tt style="color:green;font-weight:bold"># zfs set mountpoint=/root local/root</tt></li>
<li><tt># reboot</tt></li>
</ol>
<p>After these instructions and reboot we have these GPT partitions available,
this example is on a 4GB disk.</p>
<pre># <b>gpart show</b>
=> 34 8388541 ada0 GPT (4.0G)
34 256 1 freebsd-boot (128k)
290 2097152 2 freebsd-zfs (1.0G)
2097442 6291133 3 freebsd-zfs (3G)
# <b>gpart list | grep label</b>
label: bootcode0
label: sys0
label: local0
# <b>zpool status</b>
pool: local
state: ONLINE
scan: none requested
config:
NAME STATE READ WRITE CKSUM
sys ONLINE 0 0 0
gpt/local0.eli ONLINE 0 0 0
errors: No known data errors
pool: sys
state: ONLINE
scan: none requested
config:
NAME STATE READ WRITE CKSUM
sys ONLINE 0 0 0
gpt/sys0 ONLINE 0 0 0
errors: No known data errors</pre>
<h3>4. Basic Setup after Install</h3>
<ol>
<li>Login as <b>root</b> with empty password.</li>
<pre>login: <b>root</b>
password: <b>[ENTER]</b></pre>
<li>Create initial <b>snapshot</b> after install.</li>
<tt># zfs snapshot -r sys/ROOT/default@install</tt>
<li>Set new <b>root</b> password.</li>
<tt># passwd</tt>
<li>Set machine's <b>hostname</b>.</li>
<tt># echo hostname=hostname.domain.com >> /etc/rc.conf</tt>
<li>Set proper <b>timezone</b>.</li>
<tt># tzsetup</tt>
<li>Add some <b>swap</b> space.</li>
<p>If You used the <i>Server with ...</i> type, then use this to add swap.</p>
<pre># <b>zfs create -V 1G -o org.freebsd:swap=on \
-o checksum=off \
-o sync=disabled \
-o primarycache=none \
-o secondarycache=none sys/swap</b>
# <b>swapon /dev/zvol/sys/swap</b></pre>
<p>If You used the <i>Road Warrior Laptop</i> one, then use this one below,
this was the swap space will also be encrypted.</p>
<pre># <b>zfs create -V 1G -o org.freebsd:swap=on \
-o checksum=off \
-o sync=disabled \
-o primarycache=none \
-o secondarycache=none local/swap</b>
# <b>swapon /dev/zvol/local/swap</b></pre>
<li>Create <b>snapshot</b> called <tt>configured</tt> or <tt>production</tt></li>
<p>After You configured Your fresh FreeBSD system, added needed packages
and services, create <b>snapshot</b> called <tt>configured</tt> or
<tt>production</tt> so if You mess something, You can always go back in time
to bring working configuration back. mess something.</p>
<tt># zfs snapshot -r sys/ROOT/default@configured</tt>
</ol>
<h3>5. Enable Boot Environments</h3>
<p>Here are some simple instructions on how to download and enable the
<tt>beadm</tt> command line utility for easy <i>Boot Environments</i>
administration. </p>
<pre># <b>fetch https://downloads.sourceforge.net/project/beadm/beadm -o /usr/sbin/beadm</b>
# <b>chmod +x /usr/sbin/beadm</b>
# <b>rehash</b>
# <b>beadm list</b>
BE Active Mountpoint Space Policy Created
default NR / 592M static 2012-04-25 02:03</pre>
<h3>6. WYSIWTF</h3>
<p>Now we have a working ZFS only FreeBSD system, I will put some example here
about what You now can do with this type of installation and of course the
<i>Boot Environments</i> feature.</p>
<h4>6.1. Create New Boot Environmnent Before Upgrade</h4>
<ol>
<li>Create new environment from the current one.</li>
<tt># beadm create upgrade</tt>
<li>Activate it.</li>
<tt># beadm activate upgrade</tt>
<li>Reboot into it.</li>
<tt># shutdown -r now</tt>
<li>Mess with it.</li>
<p>You are now free to do anything You like fo or the upgrade process,
but even if You break everything, You still have a working <tt>default</tt>
working environment.</p>
</ol>
<h4>6.2. Perform Upgrade within a Jail</h4>
<p>This concept is about creating new boot environment from the
desired one, lets call it <tt>jailed</tt>, then start that new environment
inside a FreeBSD Jail and perform upgrade there. After You have finished all
tasks related to this upgrade and You are satisfied with the achieved results,
shutdown that Jail, set the boot environment into that just upgraded Jail
called <tt>jailed</tt> and reboot into just upgraded system without any
risks.</p>
<ol>
<li>Create new boot environment called <tt>jailed</tt>.</li>
<pre># <b>beadm create -e default jailed</b>
Created successfully</pre>
<li>Create <tt>/usr/jails</tt> directory.</li>
<tt># <b>mkdir /usr/jails</b></tt>
<li>Set mount point of new boot environment to <tt>/usr/jails/jailed</tt> dir.</li>
<tt># <b>zfs set mountpoint=/usr/jails/jailed sys/ROOT/jailed</b></tt>
<li>Enable FreeBSD Jails mechanism and the <tt>jailed</tt> Jail in
<tt>/etc/rc.conf</tt> file.</li>
<tt># <b>cat << EOF >> /etc/rc.conf</b><br>
> <b>jail_enable=YES</b><br>
> <b>jail_list="jailed"</b><br>
> <b>jail_jailed_rootdir="/usr/jails/jailed"</b><br>
> <b>jail_jailed_hostname="jailed"</b><br>
> <b>jail_jailed_ip="10.20.30.40"</b><br>
> <b>jail_jailed_devfs_enable="YES"</b><br>
> <b>EOF</b></tt>
<li>Start the Jails mechanism.</li>
<pre># <b>/etc/rc.d/jail start</b>
Configuring jails:.
Starting jails: jailed.</pre>
<li>Check if the <tt>jailed</tt> Jail started.</li>
<pre># <b>jls</b>
JID IP Address Hostname Path
1 10.20.30.40 jailed /usr/jails/jailed</pre>
<li>Login into the <tt>jailed</tt> Jail.</li>
<tt># <b>jexec 1 tcsh</b></tt>
<li><b>PERFORM ACTUAL UPGRADE.</b></li>
<li>Stop the <tt>jailed</tt> Jail.</li>
<pre># <b>/etc/rc.d/jail stop</b>
Stopping jails: jailed.</pre>
<li>Disable Jails mechanism in <tt>/etc/rc.conf</tt> file.</li>
<tt># <b>sed -i '' -E s/"^jail_enable.*$"/"jail_enable=NO"/g /etc/rc.conf</b></tt>
<li>Activate just upgraded <tt>jailed</tt> boot environment.</li>
<pre># <b>bootfs-beadm activate jailed</b>
Activated successfully</pre>
<li>Restart the system into upgraded system.</li>
<tt># <b>shutdown -r now</b></tt>
</ol>
<h4>6.3. Import Boot Environmnent from Other Machine</h4>
<p>Lets assume, that You need to upgrade or do some major modification to
some of Your servers, You will then create new boot environment from the
default one, move it to other 'free' machine, perform these tasks there
and after everything is done, move the modified boot environment to the
production without any risks. You may as well trasnport that environment
into You laptop/workstation and upgrade it in a Jail like in step 6.2 of
this guide.</p>
<ol>
<li>Create new environment on the <i>production</i> server.</li>
<pre># <b>beadm create upgrade</b>
Created successfully.</pre>
<li>Send the <tt>upgrade</tt> environment to <i>test</i> server.</li>
<tt># <b>zfs send sys/ROOT/upgrade | ssh TEST zfs recv -u sys/ROOT/upgrade</b></tt>
<li>Activate the <tt>upgrade</tt> environment on the <i>test</i> server.</li>
<pre># <b>beadm activate upgrade</b>
Activated successfully.</pre>
<li>Reboot into the <tt>upgrade</tt> environment on the <i>test</i> server.</li>
<tt># <b>shutdown -r now</b></tt>
<li><b>PERFORM ACTUAL UPGRADE AFTER REBOOT.</b></li>
<li>Sent the upgraded <tt>upgrade</tt> environment onto <i>production</i> server.</li>
<tt># <b>zfs send sys/ROOT/upgrade | ssh PRODUCTION zfs recv -u sys/ROOT/upgrade</b></tt>
<li>Activate upgraded <tt>upgrade</tt> environment on the <i>production</i> server.</li>
<pre># <b>beadm activate upgrade</b>
Activated successfully.</pre>
<li>Reboot into the <tt>upgrade</tt> environment on the <i>production</i> server.</li>
<tt># <b>shutdown -r now</b></tt>
</ol>
<h3>7. References</h3>
<ul>
<li><b><tt>[1]</tt></b> <a style="color:blue">http://forums.freebsd.org/showthread.php?t=10334</a></li>
<li><b><tt>[2]</tt></b> <a style="color:blue">http://forums.freebsd.org/showthread.php?t=12082</a></li>
<li><b><tt>[3]</tt></b> <a style="color:blue">http://docs.oracle.com/cd/E19963-01/pdf/820-6565.pdf</a></li>
<li><b><tt>[4]</tt></b> <a style="color:blue">http://docs.oracle.com/cd/E19963-01/html/821-1462/beadm-1m.html</a></li>
<li><b><tt>[5]</tt></b> <a style="color:blue">http://anonsvn.h3q.com/projects/freebsd-patches/wiki/manageBE</a></li>
<li><b><tt>[6]</tt></b> <a style="color:blue">https://sourceforge.net/projects/beadm/</a></li>
</ul>
</tt>
<p>The last part of the HOWTO remains the same as Year ago ...</p>
<p>You can now add your users, services and packages as usual on any FreeBSD system, have fun ;)</p>
<br>
<br>
<br>

499
README Normal file
View File

@ -0,0 +1,499 @@
___ /\ ___
__/ /_ / \ _\ \__
____ _____/_ __/__ / _/\ ___ ___ ____ ______ __\__ _\
/ \ / / // // \ /\_/ \ / / \ / \\ \ \ / \\ \
/ / // / // // / // \\ \ \ \\ \ \\ \ \\ \ \\ \_
\_____\\____/ \__\\____//__________\\__\__\__\\____/ \_____\\__\__\\___\
The 'automount' is a devd(8) based automounter for FreeBSD.
It supports most popular file systems:
NTFS/MSDOS/exFAT/EXT2/EXT3/EXT4/UFS/XFS/HFS/MTP/ISO9660
-------------------------------------------------------------------------------
I N S T A L L
===================
Use provided FreeBSD Ports/packages from here:
* sysutils/automount
.. or make manual unstallation:
# cp automount.conf /usr/local/etc/automount.conf
# cp automount_devd.conf /usr/local/etc/devd/automount_devd.conf
# cp automount /usr/local/sbin/automount
# chmod +x /usr/local/sbin/automount
# /etc/rc.d/devd restart
Now plugin Your USB thumb drive and have fun ;)
These ports/packages are needed for all filesystems:
* sysutils/e2fsprogs // EXT2/EXT3/EXT4 fsck(8)
* sysutils/xfsprogs // XFS fsck(8)
* sysutils/exfat-utils // exFAT exfatfsck(8)
* sysutils/fusefs-exfat // exFAT
* sysutils/fusefs-ntfs // NTFS (read write support)
* sysutils/fusefs-hfsfuse // HFS
* sysutils/fusefs-lkl // XFS/EXT2/EXT3/EXT4
* sysutils/fusefs-simple-mtpfs // MTP
All of the above are available as pkg(8) packages.
Shortcut:
# pkg install -y \
sysutils/e2fsprogs \
sysutils/xfsprogs \
sysutils/exfat-utils \
sysutils/fusefs-exfat \
sysutils/fusefs-ntfs \
sysutils/fusefs-hfsfuse \
sysutils/fusefs-lkl \
sysutils/fusefs-simple-mtpfs
-------------------------------------------------------------------------------
C H A N G E L O G
=========================
VERSION 1.7.9 (CURRENT)
Fix XORG detection.
Implement proposed BLACKLIST_REGEX option.
Use 'sysutils/fusefs-lkl' for all ext2/ext3/ext4 mounts.
Fix exFAT detection.
Fix small problem with checking the mount state.
Implement better old directory cleanup.
-------------------------------------------------------------------------------
VERSION 1.7.8
Fix harmless gpart(8) rant about ugen(4) devices.
-------------------------------------------------------------------------------
VERSION 1.7.7
Add option to ignore system partitions like EFI or MSR.
Fix removal of dirs from unmounted filesystems.
Fix mount permissions for FAT filesystems.
Add spaces and comments in the code.
-------------------------------------------------------------------------------
VERSION 1.7.6
Added UZIP images support.
Added another try to mount device in read only mode.
Added optional ada(4) disks support.
Added optional md(4) devices support.
Added automatic kernel modules loading.
-------------------------------------------------------------------------------
VERSION 1.7.5
Add REMOVEDIRS option as default.
Add NICENAMES option to use labels instead of device names.
Use procstat(1) for faster DISPLAY environemnt searching.
-------------------------------------------------------------------------------
VERSION 1.7.4
Add new logo.
-------------------------------------------------------------------------------
VERSION 1.7.3
Use 755 permissions for FAT mounts.
-------------------------------------------------------------------------------
VERSION 1.7.2
Phase out support for sysutils/fusefs-ext4fuse port.
Fix UMASK for exFAT filesystems.
Fix ISO9660 mount options.
-------------------------------------------------------------------------------
VERSION 1.7.1
Fix exFAT mount rights.
Use USER option in config file.
Make MTP detection and mount better.
-------------------------------------------------------------------------------
VERSION 1.7.0
The automount has now a new co-author - Rozhuk Ivan.
New options available in automount.conf config file.
Filesystem detection/mounting reworked totally with file(1)/dd(1)/fstyp(8) as backends.
Notifications are now possible with libnotify.
Automatic detection of DISPLAY variable.
New automatic wait for device appearance.
New detection if device is a block device.
Introduction of CD-ROM support.
Automatic detection of File Manager with exo-open(1).
Option REMOVEDIRS is deprecated now.
Handle '-o large' option for FAT under FreeBSD 11.x and 12.x versions.
-------------------------------------------------------------------------------
VERSION 1.6.1
Fix MBR/msdosfs partition unmount issue.
-------------------------------------------------------------------------------
VERSION 1.6.0
Fix long boot with devd(8) because of ugen(4) devices.
Add fsck.exfat to the exFAT filesystem.
Set fsck.ext2 instead of e2fsck to the ext2 filesystem.
Set fsck.ext3 instead of e2fsck to the ext3 filesystem.
Set fsck.ext4 instead of e2fsck to the ext4 filesystem.
-------------------------------------------------------------------------------
VERSION 1.5.9
Decrease DELAY for sleep from '1' to '0.1' for faster mounting.
Remove __random_wait() at 'attach'.
Implement MTP mounting.
Added XFS and HFS support.
Various fixes and cleanups.
Remove '-o large' option for FAT (not supported on FreeBSD 12).
-------------------------------------------------------------------------------
VERSION 1.5.8
Omit GVFS filesystem in the mount(8) listing.
Improve exFAT mount options.
Add mount_msdosfs(8) fallback fix.
Set caja as file manager in example config.
Add version argument.
-------------------------------------------------------------------------------
VERSION 1.5.7
Fix FAT32 mount.
Add extended options for EXFAT mounts.
Add -version option.
-------------------------------------------------------------------------------
VERSION 1.5.6
Implement --version option.
-------------------------------------------------------------------------------
VERSION 1.5.5
Rework NTFS/MSDOS/FAT detection.
Check for NTFS before FAT.
-------------------------------------------------------------------------------
VERSION 1.5.4
Added notification via notify-send/libnotify and wall(1). Minor bug fix.
Change 'boot sector' detection.
-------------------------------------------------------------------------------
VERSION 1.5.3
Fix small harmless bug - variable WAIT without default value.
-------------------------------------------------------------------------------
VERSION 1.5.2
Introduce smarter fstype() function to better determine filesystem.
-------------------------------------------------------------------------------
VERSION 1.5.1
Add -k flag to file(1) command.
Set new --version and date(1).
-------------------------------------------------------------------------------
VERSION 1.5.0
Add new NTFS options.
Add nested NTFS mount attempt.
Fix devd(8) config.
Improve log messages.
Use random wait only on ATTACH action, not needed on DETACH action.
Implement random wait to eliminate race.
Implement BOOTDELAY option to wait for boot process to complete.
Fix devd(8) config (LARKIND) to match all needed devices and their partitions.
Fix typo in NTFS error message.
-------------------------------------------------------------------------------
VERSION 1.4.3
Only style(9) changes.
Force longnames option for msdosfs.
Remove -u option for debug.
Fix a bug when ATIME is enabled.
Add -o remove_hiberfile to NTFS-3G mount options.
Fix typo at /var/log/automount.log error message.
Use /sbin/e2fsck from FreeBSD base system.
Fix typo at /var/log/automount.log error message.
Force longnames option for msdosfs.
-------------------------------------------------------------------------------
VERSION 1.4.2
Implement active sleep/wait for devices that could not appear.
Add more useful information to /var/log/automount.log file.
Implement BLACKLIST option to ignore problematic devices.
-------------------------------------------------------------------------------
VERSION 1.4.1
Improved checking for already mounted devices.
More readable log format.
Added logging of fsck(8) output.
Added adding setuid also to /sbin/mount* when USERUMOUNT set to YES.
Added error logging of failed mounts.
-------------------------------------------------------------------------------
VERSION 1.4.0
Wait for smartphone to attach device, rewrite all &&-|| into if-then-else-fi syntax.
-------------------------------------------------------------------------------
VERSION 1.3.1
Fixed the 'detach' section (s/PREFIX/MNTPREFIX/g).
Fixed removing directories of manually (properly) unmounted filesystems.
-------------------------------------------------------------------------------
VERSION 1.3
Fixed inproper exFAT detection, now mounts fine.
Fixed creating mount dirs for attached devices no matter if needed or not.
Revised 'detach' section, now removes only directory that is unmounted.
Simplified FAT/NTFS sections, removed additional checks as they break
some MP3 players automount.
-------------------------------------------------------------------------------
VERSION 1.2.1
Added the --help page.
Removed some small bugs.
Added more options to configure features.
% /usr/local/sbin/automount --help
AUTOMOUNT is a devd(8) based automounter for FreeBSD.
It supports following file systems:
UFS/FAT/exFAT/NTFS/EXT2/EXT3/EXT4/MTP/HFS/ISO9660
Add these to mount NTFS/exFAT/EXT4/HFS/XFS/MTP respectively:
o sysutils/fusefs-ntfs
o sysutils/fusefs-exfat
o sysutils/fusefs-ext4fuse
o sysutils/fusefs-hfsfuse
o sysutils/fusefs-lkl
o sysutils/fusefs-simple-mtpfs
By default it mounts/unmounts all removable media but
it is possible to set some additional options at the
/usr/local/etc/automount.conf config file.
Below is a list of possible options with description.
MNT_PREFIX (set to /media by default)
With this options You can alter the default root
for mounting the removable media, for example to
the /mnt directory.
example: MNT_PREFIX='/media'
MNT_GROUP (wheel by default)
If set to some group name, the mount command will
chown(1) the mount directory with the group.
example: group='operator'
MNT_MODE (set to 775 by default)
Value for chmod on mount point.
FAT_ENCODING (set to en_US.UTF-8 by default)
Only used with FAT32 mounts, specifies which
encoding to use at the mount.
example: FAT_ENCODING='en_US.ISO8859-1'
FAT_CODEPAGE (set to CP866 by default)
Only used with FAT32 mounts, specifies which
code page to use at the mount.
example: FAT_CODEPAGE='cp437'
ISO9660_CODEPAGE (set to UTF-8 by default)
Only used with cd9660 mounts, specifies which
code page to use at the mount.
ATIME (set to NO by default)
When set to NO it will mount filesystems with
noatime option when possible.
example: ATIME='YES'
RETRY_COUNT (set to 3 by default)
How many times try to get file system type or try to mount.
example: RETRY_COUNT='1'
RETRY_DELAY (set to 1 second by default)
Delay beetwin retry attempt.
example: RETRY_DELAY='2.5'
USERUMOUNT (set to NO by default)
When set to YES it will 'chmod +s /sbin/umount'
which would allow an USER to unmount the file
system with their selected file manager.
example: USERUMOUNT='YES'
NOTIFY (set to NO by default)
Use 'notify-send' and 'libnotify' to show notifications
of mounting and unmounting devices on the desktop.
example: NOTIFY='YES'
WALL (set to NO by default)
Use wall(1) to show notifications of mounting and
unmounting devices on terminals of logged in users.
example: WALL='YES'
FM ('exo-open --launch FileManager' by default)
If set to file manager command, the mount will
launch the specified command after successful
mount. Works only if USER parameter is also set.
example: FM='nautilus --browser --no-desktop'
BLACKLIST (unset by default)
The automount will ignore devices defined here.
example: BLACKLIST='da0 da3s1a'
USER (root by default)
If set to some username, the mount command will
chown(1) the mount directory with the user and
its primary user group. If used with FM option
allows to launch the specified file manager after
a successful mount.
example: USER="vermaden"
-------------------------------------------------------------------------------
VERSION 1.2
FAT/NTFS detection improvements.
The 'chown' now uses sets user's group.
MSDOS filesystem is now mounted with -m 644 -M 755 options by default.
Removed POPUP=YES option, just use USER + FM for simplicity.
NTFS filesystem, when mounted by mount_ntfs(8) uses -u and -g options.
Even more simplified 'detach' section.
------------------------------------------------------------------------------
VERSION 1.1
I removed the state_lock and stat_unlock mechanisms as they appeared to be
not needed, I have shufled with 3 drives all the time and the 'integrity'
has not been lost, at it was a lot faster, because the lock always had to
wait for the 'slowest' drive (in term of initializing the device, like USB
hard drive).
I simplified the 'attach' section a lot, now each filesystem contains only
check/fsck (if possible), mount and log info.
I also simplified and improved the 'detach' section a little.
I added an option to automatically launch the set-up in config file manager.
These are options that I currently successfully use for NAUTILUS file
manager. You need to set-up all three of them to make it work.
| POPUP=YES
| FM="nautilus --browser --no-desktop"
| USER=vermaden
My whole config looks like that now:
| USERUMOUNT=YES
| POPUP=YES
| FM="nautilus --browser --no-desktop"
| USER=vermaden
| ENCODING=pl_PL.ISO8859-2
| CODEPAGE=cp852
All latest updates are available at GITHUB repository:
https://github.com/vermaden/automount
------------------------------------------------------------------------------
VERSION 1.0
AUTOMOUNT is devd(8) based flexible yet very simple automounter for FreeBSD.
Currently it supports these file systems:
-- NTFS requires sysutils/fusefs-ntfs for R/W
-- FAT/FAT32
-- exFAT requires sysutils/fusefs-exfat
-- EXT2
-- EXT3
-- EXT4 requires sysutils/fusefs-ext4fuse
-- UFS
It keeps state of the mounted devices at /var/run/automount.state and logs
all activities to /var/log/automount.log file.
The place for the script is at /usr/local/sbin/automount.sh executable.
The only additional configuration it requires is to add these lines as
/usr/local/etc/devd/automount_devd.conf file, which would allow it to work.
Remember to restart /etc/rc.d/devd daemon after adding
/usr/local/etc/devd/automount_devd.conf file.
------------------------------------------------------------------------------
Have Fun ;)
vermaden

View File

@ -1,4 +0,0 @@
beadm
=====
FreeBSD utility to manage Boot Environments on ZFS filesystems.

779
automount Executable file
View File

@ -0,0 +1,779 @@
#!/bin/sh
# Copyright (c) 2012-2024 Slawomir Wojciech Wojtczak <vermaden@interia.pl>
# Copyright (c) 2019 Rozhuk Ivan <rozhuk.im@gmail.com>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that following conditions are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS 'AS IS' AND ANY
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
PATH=${PATH}:/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin
__usage() {
cat << EOF
AUTOMOUNT is a devd(8) based automounter for FreeBSD.
It supports following file systems:
UFS/FAT/exFAT/NTFS/EXT2/EXT3/EXT4/MTP/HFS/ISO9660
Add these to mount NTFS/exFAT/EXT4/HFS/XFS/MTP respectively:
o sysutils/fusefs-ntfs
o sysutils/fusefs-exfat
o sysutils/fusefs-hfsfuse
o sysutils/fusefs-lkl
o sysutils/fusefs-simple-mtpfs
By default it mounts/unmounts all removable media but
it is possible to set some additional options at the
/usr/local/etc/automount.conf config file.
Below is a list of possible options with description.
MNT_PREFIX (set to /media by default)
With this options You can alter the default root
for mounting the removable media, for example to
the /mnt directory.
example: MNT_PREFIX='/media'
MNT_GROUP (wheel by default)
If set to some group name, the mount command will
chown(1) the mount directory with the group.
example: group='operator'
MNT_MODE (set to 775 by default)
Value for chmod on mount point.
FAT_ENCODING (set to en_US.UTF-8 by default)
Only used with FAT32 mounts, specifies which
encoding to use at the mount.
example: FAT_ENCODING='en_US.ISO8859-1'
FAT_CODEPAGE (set to CP866 by default)
Only used with FAT32 mounts, specifies which
code page to use at the mount.
example: FAT_CODEPAGE='cp437'
ISO9660_CODEPAGE (set to UTF-8 by default)
Only used with cd9660 mounts, specifies which
code page to use at the mount.
ATIME (set to NO by default)
When set to NO it will mount filesystems with
noatime option when possible.
example: ATIME='YES'
RETRY_COUNT (set to 3 by default)
How many times try to get file system type or try to mount.
example: RETRY_COUNT='1'
RETRY_DELAY (set to 1 second by default)
Delay beetwin retry attempt.
example: RETRY_DELAY='2.5'
USERUMOUNT (set to NO by default)
When set to YES it will 'chmod +s /sbin/umount'
which would allow an USER to unmount the file
system with their selected file manager.
example: USERUMOUNT='YES'
NOTIFY (set to NO by default)
Use 'notify-send' and 'libnotify' to show notifications
of mounting and unmounting devices on the desktop.
example: NOTIFY='YES'
WALL (set to NO by default)
Use wall(1) to show notifications of mounting and
unmounting devices on terminals of logged in users.
example: WALL='YES'
FM ('exo-open --launch FileManager' by default)
If set to file manager command, the mount will
launch the specified command after successful
mount. Works only if USER parameter is also set.
example: FM='nautilus --browser --no-desktop'
BLACKLIST (unset by default)
The automount will ignore devices defined here.
example: BLACKLIST='da0 da3s1a'
BLACKLIST_REGEX (unset by default)
The boolean flag option complements the above BLACKLIST option
if one wants regex match instead of exact match for ignoring devices.
Below will ignore all partitions ada0p1/ada0p2/... of ada0 device.
example: BLACKLIST='ada0'
BLACKLIST_REGEX=true
USER (root by default)
If set to some username, the mount command will
chown(1) the mount directory with the user and
its primary user group. If used with FM option
allows to launch the specified file manager after
a successful mount.
example: USER="vermaden"
REMOVEDIRS (set to YES by default)
If set to YES the automount(8) will remove /media dir after unmount.
example: REMOVEDIRS=NO
NICENAMES (set to NO by default)
If set to YES the device/filesystem label will be used for /media dir name.
example: NICENAMES=YES
IGNORE_SYS_PARTS (set to NO by default)
If set to YES automount(8) will ignore system partitions like EFI or MSR.
example: IGNORE_SYS_PARTS=YES
EOF
exit 0
}
# display version if needed
if [ "${1}" = '--version' -o \
"${1}" = '-version' -o \
"${1}" = 'version' -o \
"${1}" = '-v' ]
then
echo
echo " ___ /\ ___ "
echo " __/ /_ / \ _\ \__ "
echo " ____ _____/_ __/__ / _/\ ___ ___ ____ ______ __\__ _\ "
echo " / \ / / // // \ /\_/ \ / / \ / \\\ \ \ / \\\ \ "
echo " / / // / // // / // \\\ \ \ \\\ \ \\\ \ \\\ \ \\\ \_ "
echo " \_____\\\____/ \__\\\____//__________\\\__\__\__\\\____/ \_____\\\__\__\\\___\ "
echo
echo "automount 1.8.0 2024/03/05"
exit 0
fi
# display help if needed
if [ "${1}" = "-h" -o \
"${1}" = "--h" -o \
"${1}" = "-help" -o \
"${1}" = "--help" -o \
"${#}" -eq "0" -o \
"${#}" -eq "1" ]
then
__usage
fi
# read configuration file
if [ -f /usr/local/etc/automount.conf ] ; then
. /usr/local/etc/automount.conf
fi
# default values for global variables
: ${MNT_PREFIX='/media'} # mount prefix
: ${MNT_GROUP='wheel'} # use WHEEL group for popup
: ${MNT_MODE='775'} # mount point mode
: ${FAT_ENCODING='en_US.UTF-8'} # US/Canada
: ${FAT_CODEPAGE='cp437'} # US/Canada
: ${ISO9660_CODEPAGE='UTF-8'} # UTF-8
: ${ATIME='NO'} # when NO mount with noatime
: ${RETRY_COUNT='5'} # retry count
: ${RETRY_DELAY='2'} # retry delay time
: ${USERUMOUNT='NO'} # when YES add suid bit to umount(8)
: ${NOTIFY='NO'} # use notify-send(1) (devel/libnotify)
: ${WALL='NO'} # use wall(1)
: ${FM='exo-open --launch FileManager'} # which file manager to use
: ${LOG_FILE='/var/log/automount.log'} # log file
: ${LOG_DATEFMT='%Y-%m-%d %H:%M:%S'} # 2012-02-20 07:49:09
: ${STATE="/var/run/automount.state"} # current state file
: ${USER="root"} # which user to use for popup
: ${REMOVEDIRS='YES'} # remove /media dir after unmount
: ${NICENAMES='NO'} # use device label for /media dir name
: ${IGNORE_SYS_PARTS='NO'} # ignore system partitions like EFI or MSR
# init of main variables
DEV="/dev/${1}"
UID=$( id -u ${USER} )
GID=$( pw group show -n ${MNT_GROUP} | awk -F':' '{print $3}' )
if [ ${?} -ne 0 ]
then
__log "${MNT_GROUP}: invalid group"
exit 1
fi
# process ${USERUMOUNT} option
case ${USERUMOUNT} in
([Yy][Ee][Ss])
chmod u+s /sbin/umount 1> /dev/null 2>&1 # WHEEL group member
chmod u+s /sbin/mount* 1> /dev/null 2>&1 # WHEEL group member
sysctl -q vfs.usermount=1 1> /dev/null 2>&1 # allow user to mount
;;
esac
# read only filesystem types for __guess_fs_type() function
readonly FS_TYPE_UNKNOWN=0
readonly FS_TYPE_ISO9660=1
readonly FS_TYPE_UFS=8
readonly FS_TYPE_EXT2=9
readonly FS_TYPE_EXT3=10
readonly FS_TYPE_EXT4=11
readonly FS_TYPE_XFS=12
readonly FS_TYPE_HFS=13
readonly FS_TYPE_FAT=32
readonly FS_TYPE_EXFAT=33
readonly FS_TYPE_NTFS=34
readonly FS_TYPE_MTP=128
# FUNCTION: guess filesystem type from device
__guess_fs_type() { # 1=DEV
# first time guess with file(1) tool
unset FS_TYPE
local FS_TYPE=$( file -r -b -L -s ${1} 2> /dev/null | sed -E 's/label:\ \".*\"//g' )
case ${FS_TYPE} in
(*ISO\ 9660*) return ${FS_TYPE_ISO9660} ;;
(*Unix\ Fast\ File*) return ${FS_TYPE_UFS} ;;
(*ext2*) return ${FS_TYPE_EXT2} ;;
(*ext3*) return ${FS_TYPE_EXT3} ;;
(*ext4*) return ${FS_TYPE_EXT4} ;;
(*SGI\ XFS*) return ${FS_TYPE_XFS} ;;
(*Macintosh\ HFS*) return ${FS_TYPE_HFS} ;;
esac
# second time guess with file(1) tool with -k option
# (do not stop at the first match and keep going)
unset FS_TYPE
local FS_TYPE=$( file -k -r -b -L -s ${1} 2> /dev/null | tr '\n' ' ' | sed -E 's/label:\ \".*\"//g' )
case ${FS_TYPE} in
(*Unix\ Fast\ File*) return ${FS_TYPE_UFS} ;;
(*NTFS*) return ${FS_TYPE_NTFS} ;;
(*ExFAT*) return ${FS_TYPE_EXFAT} ;;
(*\ FAT\ *|*MSDOS*) return ${FS_TYPE_FAT} ;;
esac
# try with fstyp(8) last (exFAT on UFS issue)
unset FS_TYPE
local FS_TYPE=$( fstyp ${1} 2> /dev/null )
case ${FS_TYPE} in
(cd9660) return ${FS_TYPE_ISO9660} ;;
(ufs) return ${FS_TYPE_UFS} ;;
(ext2fs) return ${FS_TYPE_EXT2} ;;
(msdosfs) return ${FS_TYPE_FAT} ;;
(exfat) return ${FS_TYPE_EXFAT} ;;
(ntfs) return ${FS_TYPE_NTFS} ;;
esac
# magic detection code with dd(8)
unset FS_TYPE
local FS_TYPE=$( dd if="${1}" conv=sync count=1 bs=1k 2> /dev/null | strings | head -1 )
case ${FS_TYPE} in
(*EXFAT*) return ${FS_TYPE_EXFAT} ;;
esac
return ${FS_TYPE_UNKNOWN}
}
# FUNCTION: add state to the ${STATE} file
__state_add() { # 1=DEV 2=PROVIDER 3=MNT
if [ -f ${STATE} ]
then
if grep -E "${3}$" ${STATE} 1> /dev/null 2> /dev/null
then
__log "${1}: duplicated '${STATE}'"
exit 0
fi
fi
echo "${1} ${2} ${3}" >> ${STATE}
if [ "${NOTIFY}" = YES ]
then
__show_message "Device '${1}' mounted on '${3}' directory."
fi
if [ "${WALL}" = YES ]
then
echo "automount: Device '${1}' mounted on '${3}' directory." | wall
fi
}
# FUNCTION: remove state from the ${STATE} file
__state_remove() { # 1=MNT
if [ -f ${STATE} ]
then
# backslash the slashes ;)
BSMNT=$( echo ${1} | sed 's/\//\\\//g' )
sed -i '' "/${BSMNT}\$/d" ${STATE}
if [ "${NOTIFY}" = YES ]
then
__show_message "Device '${1}' unmounted from '${3}' directory."
fi
if [ "${WALL}" = YES ]
then
echo "automount: Device '${1}' unmounted from '${3}' directory." | wall
fi
fi
}
# FUNCTION: add message to the ${LOG_FILE} file
__log() { # @=MESSAGE
echo $( date +"${LOG_DATEFMT}" ) "${@}" >> "${LOG_FILE}"
}
# FUNCTION: remove temp mount dir from ${MNT_PREFIX} path (like /media/da0 dir)
__remove_dir() { # 1=TARGET
if [ "${REMOVEDIRS}" = YES ]
then
if [ -d "${1}" ]
then
sleep 1
# find "${1}" -type d -empty -maxdepth 1 -exec rm -r {} '+' 2> /dev/null
find "${MNT_PREFIX}" -depth 1 -empty -prune -delete 2> /dev/null
fi
fi
}
# FUNCTION: display wall(1) and/or notify-send(1) message
__show_message() { # 1=MESSAGE
case ${WALL} in
([Yy][Ee][Ss])
echo "automount: ${1}" | wall
;;
esac
case ${NOTIFY} in
([Yy][Ee][Ss])
local __DISPLAY_IDS=$( ps aew | sed -n 's|.*DISPLAY=\([-_a-zA-Z0-9:.]*\).*|\1|p' | sort -u | tr '\n' ' ' )
for __DISPLAY_ID in ${__DISPLAY_IDS}
do
local __USER=$( ps aewj | grep "DISPLAY=${__DISPLAY_ID}" | awk '{print $1;}' | sort -u | tr -cd '[:print:]' )
if [ -z "${__USER}" ]
then
continue
fi
su -l "${__USER}" -c "env DISPLAY=${__DISPLAY_ID} notify-send automount '${1}' &" 1> /dev/null 2>&1
done
;;
esac
}
# FUNCTION: check if device or mountpoint not already mounted
__check_already_mounted() { # 1=DEV 2=MNT
local MOUNT=$( mount )
if echo "${MOUNT}" | grep -q "^${1} on "
then
local MOUNT_POINT=$( echo "${MOUNT}" | grep "^${1} on " | cut -d ' ' -f 3-255 | cut -d '(' -f 1 | sed s/.$// )
__log "${DEV}: already mounted on '${MOUNT_POINT}' mount point"
exit 1
fi
if echo "${MOUNT}" | grep -q " on ${2} "
then
local DEVICE=$( echo "${MOUNT}" | grep " on ${2} " | awk '{print $1}' )
__log "${DEVICE}: already mounted on '${2}' mount point"
exit 1
fi
}
# FUNCTION: wait for device to appear (sometimes needed)
__wait_for_device() { # 1=DEV
# do not wait for MTP and CD-ROM devices
case ${1} in
(*ugen*|iso9660*)
return
;;
esac
# try to read from device to ensure that it alive
local COUNT=0
while ! dd if="${1}" of=/dev/null conv=sync count=1 bs=8k 1> /dev/null 2>&1
do
if [ ! -e "${1}" ]
then
__log "${1}: device gone"
exit 1
fi
COUNT=$(( ${COUNT} + 1 ))
if [ ${COUNT} -ge ${RETRY_COUNT} ]
then
return
fi
sleep "${RETRY_DELAY}"
__log "${1}: wait for device retry ${COUNT}/${RETRY_COUNT}"
done
}
# FUNCTION: check if device is a block device
__check_block_device() { # 1=DEV
# first check if its block device
if ! fstyp ${1} 1> /dev/null 2>&1
then
__log "${DEV}: not a block device"
exit 0
fi
}
# main ATTACH/DETACH block
case ${2} in
(attach)
# check if device still exists
if [ ! -e "${DEV}" ]
then
__log "${DEV}: device does not exist"
exit 1
fi
__log "${DEV}: attach"
# ignore system partitions like EFI or MSR
if [ "${IGNORE_SYS_PARTS}" = 'YES' ]
then
SYS_DEV=$( echo ${1} | grep -E -o '^[a-z]+[0-9]+' )
SYS_GPART=$( gpart show -p -r ${SYS_DEV} 2> /dev/null | sed 's@=>@@g' | grep " ${1} " | awk '{print $4}' )
case ${SYS_GPART} in
(c12a7328-f81f-11d2-ba4b-00a0c93ec93b) exit 0 ;;
(e3c9e316-0b5c-4db8-817d-f92df00215ae) exit 0 ;;
esac
fi
# code for NICENAMES mounting instead of the /dev/${DEV} default
MNT_CANDIDATE=$( fstyp -l "/dev/${1}" 2> /dev/null | cut -d " " -f 2-99 | tr ' ' '-' )
if [ "${NICENAMES}" = "YES" -a -n "${MNT_CANDIDATE}" ]
then
# check if dir exists
if [ -e "${MNT_PREFIX}/${MNT_CANDIDATE}" ]
then
# check if something is already mounted there and increment if it is
if mount | grep -q " ${MNT_PREFIX}/${MNT_CANDIDATE} "
then
COUNT=1
while true
do
COUNT=$(( ${COUNT} + 1 ))
[ ! -e "${MNT_PREFIX}/${MNT_CANDIDATE}-${COUNT}" ] && break
done
MNT="${MNT_PREFIX}/${MNT_CANDIDATE}-${COUNT}"
else
# dir exists but its not mounted
MNT="${MNT_PREFIX}/${MNT_CANDIDATE}"
fi
else
# dir does not exist
MNT="${MNT_PREFIX}/${MNT_CANDIDATE}"
fi
else
# device/filesystem without label
MNT="${MNT_PREFIX}/${1}"
fi
# blacklist check
if [ -n "${BLACKLIST}" ]
then
for I in ${BLACKLIST}
do
if [ "${1}" = "${I}" ]
then
__log "${DEV}: device blocked by BLACKLIST option"
exit 0
elif [ -n "${BLACKLIST_REGEX}" ] && echo ${DEV} | grep -q "${I}" 1> /dev/null 2> /dev/null
then
__log "${DEV}: device blocked by BLACKLIST_REGEX option"
exit 0
fi
done
fi
# check is device already mounted
__check_already_mounted "${DEV}" "${MNT}"
# make sure that data can be read from device
__wait_for_device "${DEV}"
# load needed kernel modules
kldload fusefs 1> /dev/null 2> /dev/null
kldload geom_uzip 1> /dev/null 2> /dev/null
# detect filesysytem type
case ${1} in
(iso9660*)
FS_TYPE=${FS_TYPE_ISO9660}
;;
(ugen*)
FS_TYPE=${FS_TYPE_MTP}
;;
(cd*)
__guess_fs_type "${DEV}"
FS_TYPE=${?}
;;
(md*.uzip|md*|ada*|da*|mmcsd*)
__check_block_device "${DEV}"
__guess_fs_type "${DEV}"
FS_TYPE=${?}
;;
esac
# process ATIME option
case ${ATIME} in
([Nn][Oo]) OPTS="-o noatime" ;;
esac
# filesystem options abstraction layer
case ${FS_TYPE} in
(${FS_TYPE_ISO9660})
FS_CHECK_CMD=''
FS_CHECK_ARGS=''
FS_MOUNT_CMD='mount'
FS_MOUNT_ARGS="-t cd9660 -o -e,-C=${ISO9660_CODEPAGE} ${DEV} ${MNT}"
;;
(${FS_TYPE_UFS})
FS_CHECK_CMD='fsck_ufs'
FS_CHECK_ARGS="-C -y"
FS_MOUNT_CMD='mount'
FS_MOUNT_ARGS="-t ufs ${OPTS} ${DEV} ${MNT}"
;;
(${FS_TYPE_EXT2})
FS_CHECK_PORT='sysutils/e2fsprogs'
FS_CHECK_CMD='fsck.ext2'
FS_CHECK_ARGS="-y"
FS_MOUNT_PORT='sysutils/fusefs-lkl'
FS_MOUNT_CMD='lklfuse'
FS_MOUNT_ARGS="-o type=ext2 -o allow_other -o intr -o uid=${UID} -o gid=${GID} -o umask=002 ${DEV} ${MNT}"
;;
(${FS_TYPE_EXT3})
FS_CHECK_PORT='sysutils/e2fsprogs'
FS_CHECK_CMD='fsck.ext3'
FS_CHECK_ARGS="-y"
FS_MOUNT_PORT='sysutils/fusefs-lkl'
FS_MOUNT_CMD='lklfuse'
FS_MOUNT_ARGS="-o type=ext3 -o allow_other -o intr -o uid=${UID} -o gid=${GID} -o umask=002 ${DEV} ${MNT}"
;;
(${FS_TYPE_EXT4})
FS_CHECK_PORT='sysutils/e2fsprogs'
FS_CHECK_CMD='fsck.ext4'
FS_CHECK_ARGS="-y"
FS_MOUNT_PORT='sysutils/fusefs-lkl'
FS_MOUNT_CMD='lklfuse'
FS_MOUNT_ARGS="-o type=ext4 -o allow_other -o intr -o uid=${UID} -o gid=${GID} -o umask=002 ${DEV} ${MNT}"
;;
(${FS_TYPE_XFS})
FS_CHECK_PORT='sysutils/xfsprogs'
FS_CHECK_CMD='xfs_repair'
FS_CHECK_ARGS="-d"
FS_MOUNT_CMD='lklfuse'
FS_MOUNT_ARGS="-o type=xfs -o allow_other -o uid=${UID} -o gid=${GID} ${DEV} ${MNT}"
FS_MOUNT_PORT='sysutils/fusefs-lkl'
;;
(${FS_TYPE_HFS})
FS_CHECK_CMD=''
FS_CHECK_ARGS=''
FS_MOUNT_CMD='hfsfuse'
FS_MOUNT_ARGS="--force ${OPTS} ${DEV} ${MNT}"
FS_MOUNT_PORT='sysutils/fusefs-hfsfuse'
;;
(${FS_TYPE_FAT})
# FreeBSD 12.x and later does not support/need '-o large' option
case $( sysctl -n kern.osrelease ) in
(10*) LARGE="-o large" ;;
(11*) LARGE="-o large" ;;
(*) LARGE="" ;;
esac
FS_CHECK_CMD='fsck_msdosfs'
FS_CHECK_ARGS="-C -y"
FS_MOUNT_CMD='mount_msdosfs'
FS_MOUNT_ARGS="-o longnames -m ${MNT_MODE} -M ${MNT_MODE} -D ${FAT_CODEPAGE} -L ${FAT_ENCODING} -u ${UID} -g ${GID} ${OPTS} ${LARGE} ${DEV} ${MNT}"
;;
(${FS_TYPE_EXFAT})
FS_CHECK_PORT='sysutils/exfat-utils'
FS_CHECK_CMD='fsck.exfat'
FS_CHECK_ARGS="-y"
FS_MOUNT_CMD='mount.exfat'
FS_MOUNT_UMASK=$( printf "%03o" $((~0775&0777)) )
FS_MOUNT_ARGS="-o uid=${UID} -o gid=${GID} -o umask=${FS_MOUNT_UMASK} ${OPTS} ${DEV} ${MNT}"
FS_MOUNT_PORT='sysutils/fusefs-exfat'
;;
(${FS_TYPE_NTFS})
FS_CHECK_CMD=''
FS_CHECK_ARGS=''
if /usr/bin/which -s ntfs-3g
then
FS_MOUNT_CMD='ntfs-3g'
FS_MOUNT_ARGS="-o recover ${OPTS} ${DEV} ${MNT}"
FS_MOUNT_PORT='sysutils/fusefs-ntfs'
else
FS_MOUNT_CMD='mount_ntfs'
FS_MOUNT_ARGS="-u root -g ${MNT_GROUP} ${OPTS} ${DEV} ${MNT}"
fi
;;
(${FS_TYPE_MTP})
FS_PORT='sysutils/fusefs-simple-mtpfs'
FS_CHECK_CMD=''
FS_CHECK_ARGS=''
FS_MOUNT_CMD='simple-mtpfs'
if ! /usr/bin/which -s "${FS_MOUNT_CMD}"
then
__log "command '${FS_MOUNT_CMD}' not found"
exit 1
fi
PHONEDEV=$( simple-mtpfs --list-devices -d ${DEV} 2> /dev/null )
if [ "${PHONEDEV}" = "No raw devices found." ]
then
__log "${DEV}: no raw devices found"
exit 0
fi
PHONEDEV=$( echo "${PHONEDEV}" | awk '{print $1}' | tr -d ':' )
if [ ! ${PHONEDEV} ]
then
__log "${DEV}: no MTP devices found"
exit 0
fi
FS_MOUNT_ARGS="--device ${PHONEDEV} ${MNT} -o allow_other -o uid=${UID} -o gid=${GID}"
;;
(*)
__log "${DEV}: filesystem not supported or no filesystem"
exit 0
;;
esac
# create mount point
mkdir -m "${MNT_MODE}" -p "${MNT}"
__log "${DEV}: create '${MNT}' dir"
# check file system before mount
if [ -n "${FS_CHECK_CMD}" ]
then
if ! /usr/bin/which -s "${FS_CHECK_CMD}"
then
__log "command '${FS_CHECK_CMD}' not found"
__log "please install '${FS_CHECK_PORT}' port or package"
exit 1
fi
${FS_CHECK_CMD} ${FS_CHECK_ARGS} ${DEV} \
| while read LINE
do
__log "${DEV}: ${FS_CHECK_CMD} ${LINE}"
done
fi
# check is device already mounted
__check_already_mounted "${DEV}" "${MNT}"
# try to mount
if ! /usr/bin/which -s "${FS_MOUNT_CMD}"
then
__log "command '${FS_MOUNT_CMD}' not found"
__log "please install '${FS_MOUNT_PORT}' port or package"
exit 1
fi
__wait_for_device "${DEV}"
# execute appropriate mount(8) command
COUNT=0
while ! ${FS_MOUNT_CMD} ${FS_MOUNT_ARGS} 2> /dev/null
do
if [ ! -e "${DEV}" ]
then
__log "${DEV}: device gone"
exit 1
fi
COUNT=$(( ${COUNT} + 1 ))
if [ ${COUNT} -gt ${RETRY_COUNT} ]
then
# BEGIN | try to mount read only
FS_MOUNT_ARGS="-o ro ${FS_MOUNT_ARGS}"
${FS_MOUNT_CMD} ${FS_MOUNT_ARGS}
if [ ${?} -eq 0 ]
then
__log "${DEV}: mount OK: '${FS_MOUNT_CMD} ${FS_MOUNT_ARGS}'"
break
fi
# END | try to mount read only
__log "${DEV}: mount FAIL: '${FS_MOUNT_CMD} ${FS_MOUNT_ARGS}'"
exit 1
fi
sleep "${RETRY_DELAY}"
__log "${DEV}: filesystem mount retry: ${COUNT}/${RETRY_COUNT}"
done
__log "${DEV}: mount OK: '${FS_MOUNT_CMD} ${FS_MOUNT_ARGS}'"
# add needed rights
chown "${USER}:${MNT_GROUP}" "${MNT}"
__log "${DEV}: chown '${MNT}' dir with '${USER}:${MNT_GROUP}' rights"
# add state
PROVIDER=$( mount | grep -m 1 " ${MNT} " | awk '{printf $1}' )
__state_add ${DEV} ${PROVIDER} ${MNT}
# open file manager and display message
__show_message "Device '${DEV}' mounted on '${MNT}' directory."
if [ -n "${FM}" ]
then
GROUP_USERS=$( pw group show ${MNT_GROUP} | sed -e 's|.*:||' -e 's|,| |g' )
for I in ${GROUP_USERS}
do
[ "${I}" = "root" ] && continue
XORG_PID=$( pgrep Xorg )
[ "${XORG_PID}" = "" ] && continue
DISPLAY_ID=$( procstat pargs ${XORG_PID} | grep "^argv\[1\]:" | awk '{print $NF}' )
[ -z "${DISPLAY_ID}" ] && continue
__log "${DEV}: starting '${FM}' file manager"
su -l "${I}" -c "env DISPLAY=${DISPLAY_ID} ${FM} ${MNT} &" 1> /dev/null 2>&1
done
fi
;;
(detach)
__log "${DEV}: detach"
if [ -f ${STATE} ]
then
grep -E "^/dev/${1} " ${STATE} \
| while read DEV PROVIDER MNT
do
TARGET=$( mount | grep -v \.gvfs | grep -m 1 -E "^${PROVIDER} " | awk '{print $3}' )
__state_remove ${MNT}
if [ -z ${TARGET} ]
then
continue
fi
( # put entire umount/find/rm block into background
# umount(8) two times to make sure its unmounted
umount -f "${TARGET}" 1> /dev/null 2>&1
umount -f "${TARGET}" 1> /dev/null 2>&1
__log "${DEV}: (state) umount '${TARGET}'"
__remove_dir "${TARGET}" &
__log "${DEV}: (state) mount point '${TARGET}' removed"
) &
unset TARGET
done
# code for NICENAMES mounting instead of the /dev/${DEV} default
if [ "${NICENAMES}" != YES ]
then
# umount(8) two times to make sure its unmounted
umount -f "${MNT_PREFIX}/${1}" 1> /dev/null 2>&1
umount -f "${MNT_PREFIX}/${1}" 1> /dev/null 2>&1
__log "${DEV}: (direct) umount '${MNT_PREFIX}/${1}'"
__remove_dir "${MNT_PREFIX}/${1}" &
__log "${DEV}: (direct) mount point '${MNT_PREFIX}/${1}' removed"
fi
__show_message "Device '${DEV}' unmounted from '${MNT}' directory."
fi
;;
esac

BIN
automount-1.7.9.tar.gz Normal file

Binary file not shown.

3
automount.conf Normal file
View File

@ -0,0 +1,3 @@
USERUMOUNT=YES

45
automount_devd.conf Normal file
View File

@ -0,0 +1,45 @@
# PENDRIVE/PHONE/SDCARD insert
notify 100 {
match "system" "DEVFS";
match "type" "CREATE";
match "cdev" "(da|mmcsd|ugen)[0-9]+.*";
action "/usr/local/sbin/automount $cdev attach &";
};
# PENDRIVE/PHONE/SDCARD remove
notify 100 {
match "system" "DEVFS";
match "type" "DESTROY";
match "cdev" "(da|mmcsd|ugen)[0-9]+.*";
action "/usr/local/sbin/automount $cdev detach &";
};
# CD-ROM media inject
notify 100 {
match "system" "DEVFS";
match "type" "CREATE|MEDIACHANGE";
match "cdev" "(cd)[0-9]+.*";
action "/usr/local/sbin/automount $cdev attach &";
};
# CD-ROM media eject
notify 100 {
match "system" "DEVFS";
match "type" "DESTROY";
match "cdev" "(cd)[0-9]+.*";
action "/usr/local/sbin/automount $cdev detach &";
};
# CD-ROM no media
notify 100 {
match "system" "CAM";
match "subsystem" "periph";
match "type" "error";
match "cam_status" "0xcc";
match "scsi_status" "2";
match "scsi_sense" "70 02 3a 02";
match "device" "(cd)[0-9]+.*";
action "/usr/local/sbin/automount $device detach &";
};

14
automount_devd_DEBUG.conf Normal file
View File

@ -0,0 +1,14 @@
notify 200 {
match "system" "DEVFS";
match "type" "CREATE";
match "cdev" "(da|mmcsd)[0-9]+.*";
action "/bin/sh -xe /usr/local/sbin/automount $cdev attach >> /root/DEBUG.$cdev.attach 2>&1";
};
notify 200 {
match "system" "DEVFS";
match "type" "DESTROY";
match "cdev" "(da|mmcsd)[0-9]+.*";
action "/bin/sh -xe /usr/local/sbin/automount $cdev detach >> /root/DEBUG.$cdev.detach 2>&1";
};

View File

@ -0,0 +1,16 @@
# SUPPORT md(4) IMAGES/DISKS attach
notify 100 {
match "system" "DEVFS";
match "type" "CREATE";
match "cdev" "(md)[0-9]+.*";
action "/usr/local/sbin/automount $cdev attach &";
};
# SUPPORT md(4) IMAGES/DISKS detach
notify 100 {
match "system" "DEVFS";
match "type" "DESTROY";
match "cdev" "(md)[0-9]+.*";
action "/usr/local/sbin/automount $cdev detach &";
};

View File

@ -0,0 +1,18 @@
# LOCAL DISKS attach
notify 100 {
match "system" "DEVFS";
match "type" "CREATE";
match "cdev" "(ada)[0-9]+.*";
action "/usr/local/sbin/automount $cdev attach &";
};
# LOCAL DISKS remove
notify 100 {
match "system" "DEVFS";
match "type" "DESTROY";
match "cdev" "(ada)[0-9]+.*";
action "/usr/local/sbin/automount $cdev detach &";
};

797
beadm
View File

@ -1,797 +0,0 @@
#!/bin/sh -e
# Copyright (c) 2012-2013 Slawomir Wojciech Wojtczak (vermaden)
# Copyright (c) 2012-2013 Bryan Drewery (bdrewery)
# Copyright (c) 2012-2013 Mike Clarke (rawthey)
# Copyright (c) 2013 Dancho Penev (dpslavov)
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that following conditions are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS 'AS IS' AND ANY
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
unset LC_ALL
unset LANG
PATH=${PATH}:/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin:/usr/local/sbin
if [ $( uname -r | cut -d '-' -f1 | cut -d '.' -f1 ) -lt 8 ]
then
echo "ERROR: beadm works on FreeBSD 8.0 or later"
exit 1
fi
if [ "${1}" = "--version" -o "${1}" = "version" ]
then
echo "beadm 1.0 2013/10/13"
exit 0
fi
__usage() {
local NAME=${0##*/}
echo "usage:"
echo " ${NAME} activate <beName>"
echo " ${NAME} create [-e nonActiveBe | -e beName@snapshot] <beName>"
echo " ${NAME} create <beName@snapshot>"
echo " ${NAME} destroy [-F] <beName | beName@snapshot>"
echo " ${NAME} list [-a] [-s] [-D] [-H]"
echo " ${NAME} rename <origBeName> <newBeName>"
echo " ${NAME} mount <beName> [mountpoint]"
echo " ${NAME} { umount | unmount } [-f] <beName>"
exit 1
}
# check if system has a grub.cfg file and update it
__update_grub() {
if [ -e /boot/grub/grub.cfg ]
then
grub-mkconfig -o /boot/grub/grub.cfg
fi
}
# check if boot environment exists
__be_exist() { # 1=DATASET
if ! zfs list -H -o name ${1} 1> /dev/null 2> /dev/null
then
echo "ERROR: Boot environment '${1##*/}' does not exist"
exit 1
fi
}
# check if argument is a snapshot
__be_snapshot() { # 1=DATASET/SNAPSHOT
echo "${1}" | grep -q "@" 2> /dev/null
}
# check if boot environment is mounted
__be_mounted() { # 1=BE
mount 2> /dev/null | grep -q -E "^${1} " 2> /dev/null
}
# check if boot environment is a clone
__be_clone() { # 1=DATASET
if zfs list ${1} 1> /dev/null 2> /dev/null
then
local ORIGIN="$( zfs list -H -o origin ${1} )"
if [ "${ORIGIN}" = "-" ]
then
# boot environment is not a clone
return 1
else
# boot environment is a clone
return 0
fi
else
# boot environment does not exist
return 2
fi
}
# create new boot environment
__be_new() { # 1=SOURCE 2=TARGET
local SOURCE=$( echo ${1} | cut -d '@' -f 1 )
if __be_snapshot ${1}
then
# create boot environment from snapshot
local SNAPSHOT=$( echo ${1} | cut -d '@' -f 2 )
zfs list -r -H -t filesystem -o name ${SOURCE} \
| while read FS
do
if ! zfs list -H -o name ${FS}@${SNAPSHOT} 1> /dev/null 2> /dev/null
then
echo "ERROR: Child snapshot '${FS}@${SNAPSHOT}' does not exist"
exit 1
fi
done
else
# create boot environment from other boot environment
if zfs list -H -o name ${1}@${2##*/} 1> /dev/null 2> /dev/null
then
echo "ERROR: Snapshot '${1}@${2##*/}' already exists"
exit 1
fi
# snapshot format
FMT=$( date "+%Y-%m-%d-%H:%M:%S" )
if ! zfs snapshot -r ${1}@${FMT} 1> /dev/null 2> /dev/null
then
echo "ERROR: Cannot create snapshot '${1}@${FMT}'"
exit 1
fi
fi
# clone properties of source boot environment
zfs list -H -o name -r ${SOURCE} \
| grep -v '@' \
| while read FS
do
local OPTS=""
while read NAME PROPERTY VALUE
do
if [ "${PROPERTY}" = "sharenfs" ]
then
local OPTS="-o ${PROPERTY}=\"${VALUE}\" ${OPTS}"
else
local OPTS="-o ${PROPERTY}=${VALUE} ${OPTS}"
fi
done << EOF
$( zfs get -o name,property,value -s local,received -H all ${FS} | awk '!/[\t ]canmount[\t ]/' )
EOF
DATASET=$( echo ${FS} | awk '{print $1}' | sed -E s/"^${POOL}\/${BEDS}\/${SOURCE##*/}"/"${POOL}\/${BEDS}\/${2##*/}"/g )
if [ "${OPTS}" = "-o = " ]
then
local OPTS=""
fi
if __be_snapshot ${1}
then
eval "zfs clone -o canmount=off ${OPTS} ${FS}@${1##*@} ${DATASET}"
else
eval "zfs clone -o canmount=off ${OPTS} ${FS}@${FMT} ${DATASET}"
fi
done
# check if we need to update grub
if [ "${GRUB}" = YES ]
then
__update_grub
fi
}
ROOTFS=$( mount | awk '/ \/ / {print $1}' )
if echo ${ROOTFS} | grep -q -m 1 -E "^/dev/"
then
echo "ERROR: This system does not boot from ZFS pool"
exit 1
fi
POOL=$( echo ${ROOTFS} | awk -F '/' '{print $1}' )
if [ $( echo ${ROOTFS} | awk -F '/' '{print NF}' ) -lt 3 ]
then
echo "ERROR: This system is not configured for boot environments"
exit 1
fi
BOOTFS=$( zpool list -H -o bootfs ${POOL} )
if [ -z "${BOOTFS}" -o "${BOOTFS}" = "-" ]
then
echo "ERROR: ZFS boot pool '${POOL}' has unset 'bootfs' property"
exit 1
fi
if [ -f /usr/local/etc/beadm.conf ]
then
. /usr/local/etc/beadm.conf
fi
# update GRUB bootloader instead of FreeBSD's loader(8)
: ${GRUB="NO"}
# use other prefix then the 'pool/ROOT/bename' default
: ${BEDS="$( echo ${ROOTFS} | awk -F '/' '{print $2}' )"}
case ${1} in
(list) # --------------------------------------------------------------------
OPTION_a=0
OPTION_D=0
OPTION_s=0
shift
while getopts "aDHs" OPT
do
case ${OPT} in
(a) OPTION_a=1 ;;
(D) OPTION_D=1 ;;
(H) OPTION_H=1 ;;
(s) OPTION_s=1
OPTION_a=1 ;;
(*) __usage ;;
esac
done
awk -v POOL="${POOL}" \
-v BEDS="${BEDS}" \
-v ROOTFS="${ROOTFS}" \
-v BOOTFS="${BOOTFS}" \
-v OPTION_a="${OPTION_a}" \
-v OPTION_D="${OPTION_D}" \
-v OPTION_H="${OPTION_H}" \
-v OPTION_s="${OPTION_s}" \
'function __normalize(VALUE) {
if(VALUE == "-" || VALUE == 0)
return 0
else
return substr(VALUE, 1, length(VALUE) - 1) * MULTIPLIER[substr(VALUE, length(VALUE))]
}
function __show_units(VALUE) {
if(VALUE < 1024) { UNIT = "K"; }
else if(VALUE < 1048576) { VALUE /= 1024; UNIT = "M"; }
else if(VALUE < 1073741824) { VALUE /= 1048576; UNIT = "G"; }
else if(VALUE < 1099511627776) { VALUE /= 1073741824; UNIT = "T"; }
else if(VALUE < 1125899906842624) { VALUE /= 1099511627776; UNIT = "P"; }
else if(VALUE < 1152921504606846976) { VALUE /= 1125899906842624; UNIT = "E"; }
else { VALUE /= 1152921504606846976; UNIT = "Z"; }
return sprintf("%.1f%s", VALUE, UNIT)
}
function __get_bename(BENAME) {
sub(BENAME_BEGINS_WITH "\/", "", BENAME)
sub("/.*", "", BENAME)
return BENAME
}
function __convert_date(DATE) {
CMD_DATE = "date -j -f \"%a %b %d %H:%M %Y\" \"" DATE "\" +\"%Y-%m-%d %H:%M\""
CMD_DATE | getline NEW
close(CMD_DATE)
return NEW
}
BEGIN {
BENAME_BEGINS_WITH = POOL "/" BEDS
MULTIPLIER["K"] = 1
MULTIPLIER["M"] = 1024
MULTIPLIER["G"] = 1048576
MULTIPLIER["T"] = 1073741824
MULTIPLIER["P"] = 1099511627776
MULTIPLIER["E"] = 1125899906842624
MULTIPLIER["Z"] = 1152921504606846976
MOUNTPOINT_LENGTH = 10
FSNAME_LENGTH = 2
if(OPTION_a == 1)
FSNAME_LENGTH = 19
CMD_MOUNT="mount"
while(CMD_MOUNT | getline)
if($1 ~ "^" BENAME_BEGINS_WITH)
MOUNTS[$1] = $3
close(CMD_MOUNT)
FS = "\\t"
CMD_ZFS_LIST = "zfs list -H -t all -s creation -o name,used,usedds,usedbysnapshots,usedrefreserv,refer,creation,origin -r "
while(CMD_ZFS_LIST BENAME_BEGINS_WITH | getline) {
if($1 != BENAME_BEGINS_WITH) {
FSNAME = $1
FSNAMES[length(FSNAMES) + 1] = FSNAME
USED = __normalize($2)
USEDBYDATASET = __normalize($3)
USEDBYSNAPSHOTS = __normalize($4)
USEDREFRESERV = __normalize($5)
REFER[FSNAME] = __normalize($6)
CREATIONS[FSNAME] = $7
ORIGINS[FSNAME] = $8
if(FSNAME ~ /@/)
SPACES[FSNAME] = USED
else {
SPACES[FSNAME] = USEDBYDATASET + USEDREFRESERV
if(OPTION_D != 1)
SPACES[FSNAME] += USEDBYSNAPSHOTS
BE = " " __get_bename(FSNAME) " "
if(index(BELIST, BE) == 0)
BELIST = BELIST " " BE
MOUNTPOINT = MOUNTS[FSNAME]
if(MOUNTPOINT) {
if((OPTION_a == 0 && FSNAME == (BENAME_BEGINS_WITH "/" __get_bename(FSNAME))) || (OPTION_a == 1)) {
LM = length(MOUNTPOINT)
if(LM > MOUNTPOINT_LENGTH)
MOUNTPOINT_LENGTH = LM
}
}
else
MOUNTPOINT = "-"
}
if(OPTION_a == 1)
LF = length(FSNAME)
else if(FSNAME !~ /@/)
LF = length(__get_bename(FSNAME))
if(LF > FSNAME_LENGTH)
FSNAME_LENGTH = LF
}
}
close(CMD_ZFS_LIST)
split(BELIST, BENAMES, " ")
if(OPTION_a == 1) {
BE_HEAD = "BE/Dataset/Snapshot"
printf "%-" FSNAME_LENGTH + 2 "s %-6s %-" MOUNTPOINT_LENGTH "s %6s %s\n", BE_HEAD, "Active", "Mountpoint", "Space", "Created"
}
else if(OPTION_H == 1)
BE_HEAD = ""
else {
BE_HEAD = "BE"
printf "%-" FSNAME_LENGTH "s %-6s %-" MOUNTPOINT_LENGTH "s %6s %s\n", BE_HEAD, "Active", "Mountpoint", "Space", "Created"
}
if(OPTION_s != 1)
SNAPSHOT_FILTER = "(/[^@]*)?$"
for(I = 1; I <= length(BENAMES); I++) {
BENAME = BENAMES[I]
if(OPTION_a == 1) {
printf "\n"
print BENAME
for(J = 1; J <= length(FSNAMES); J++) {
FSNAME = FSNAMES[J]
if(FSNAME ~ "^" BENAME_BEGINS_WITH "/" BENAME SNAPSHOT_FILTER) {
ACTIVE = ""
if(FSNAME == ROOTFS)
ACTIVE = ACTIVE "N"
if(FSNAME == BOOTFS)
ACTIVE = ACTIVE "R"
if(! ACTIVE)
ACTIVE = "-"
MOUNTPOINT = MOUNTS[FSNAME]
if(! MOUNTPOINT)
MOUNTPOINT = "-"
printf " %-" FSNAME_LENGTH "s %-6s %-" MOUNTPOINT_LENGTH "s %6s %s\n", FSNAME, ACTIVE, MOUNTPOINT, __show_units(SPACES[FSNAME]), __convert_date(CREATIONS[FSNAME])
ORIGIN = ORIGINS[FSNAME]
ORIGIN_DISPLAY = ORIGIN
sub(BENAME_BEGINS_WITH "/", "", ORIGIN_DISPLAY)
if(ORIGIN != "-") {
if(OPTION_D == 1)
SPACE = REFER[ORIGIN]
else
SPACE = SPACES[ORIGIN]
printf " %-" FSNAME_LENGTH "s %-6s %-" MOUNTPOINT_LENGTH "s %6s %s\n", " " ORIGIN_DISPLAY, "-", "-", __show_units(SPACE), __convert_date(CREATIONS[ORIGIN])
}
}
}
}
else {
SPACE = 0
ACTIVE = ""
NAME = BENAME_BEGINS_WITH "/" BENAME
if(NAME == ROOTFS)
ACTIVE = ACTIVE "N"
if(NAME == BOOTFS)
ACTIVE = ACTIVE "R"
if(! ACTIVE)
ACTIVE = "-"
for(J = 1; J <= length(FSNAMES); J++) {
FSNAME = FSNAMES[J]
if(FSNAME ~ "^" BENAME_BEGINS_WITH "/" BENAME "(/[^@]*)?$") {
if((BENAME_BEGINS_WITH "/" BENAME) == FSNAME) {
MOUNTPOINT = MOUNTS[FSNAME]
if(! MOUNTPOINT)
MOUNTPOINT = "-"
CREATION = __convert_date(CREATIONS[FSNAME])
}
ORIGIN = ORIGINS[FSNAME]
if(ORIGIN == "-")
SPACE += SPACES[FSNAME]
else {
if(OPTION_D == 1)
SPACE += REFER[FSNAME]
else
SPACE += SPACES[FSNAME] + SPACES[ORIGIN]
}
}
}
if(OPTION_H == 1)
printf "%s\t%s\t%s\t%s\t%s\n", BENAME, ACTIVE, MOUNTPOINT, __show_units(SPACE), CREATION
else
printf "%-" FSNAME_LENGTH "s %-6s %-" MOUNTPOINT_LENGTH "s %6s %s\n", BENAME, ACTIVE, MOUNTPOINT, __show_units(SPACE), CREATION
}
}
}'
;;
(create) # ------------------------------------------------------------------
case ${#} in
(4)
if ! [ ${2} = "-e" ]
then
__usage
fi
# check if argument for -e option is full path dataset
# argument for -e option must be 'beName' or 'beName@snapshot'
if echo ${3} | grep -q "/" 2> /dev/null
then
__usage
fi
__be_exist ${POOL}/${BEDS}/${3}
if zfs list -H -o name ${POOL}/${BEDS}/${4} 1> /dev/null 2> /dev/null
then
echo "ERROR: Boot environment '${4}' already exists"
exit 1
fi
__be_new ${POOL}/${BEDS}/${3} ${POOL}/${BEDS}/${4}
;;
(2)
if __be_snapshot ${2}
then
if ! zfs snapshot -r ${POOL}/${BEDS}/${2} 1> /dev/null 2> /dev/null
then
echo "ERROR: Cannot create '${2}' recursive snapshot"
exit 1
fi
else
__be_new ${ROOTFS} ${POOL}/${BEDS}/${2}
fi
;;
(*)
__usage
;;
esac
echo "Created successfully"
;;
(activate) # ----------------------------------------------------------------
if [ ${#} -ne 2 ]
then
__usage
fi
__be_exist ${POOL}/${BEDS}/${2}
if [ "${BOOTFS}" = "${POOL}/${BEDS}/${2}" ]
then
echo "Already activated"
exit 0
else
if __be_mounted ${POOL}/${BEDS}/${2}
then
MNT=$( mount | grep -E "^${POOL}/${BEDS}/${2} " | awk '{print $3}' )
if [ "${MNT}" != "/" ]
then
# boot environment is not current root and its mounted
echo "ERROR: Boot environment '${2}' is mounted at '${MNT}'"
echo "ERROR: Cannot activate manually mounted boot environment"
exit 1
fi
fi
# do not change root (/) mounted boot environment mountpoint
if [ "${ROOTFS}" != "${POOL}/${BEDS}/${2}" ]
then
TMPMNT=$( mktemp -d -t BE-${2} )
if ! mkdir -p ${TMPMNT} 2> /dev/null
then
echo "ERROR: Cannot create '${TMPMNT}' directory"
exit 1
fi
MOUNT=0
while read FS MNT TYPE OPTS DUMP FSCK;
do
if [ "${FS}" = "${POOL}/${BEDS}/${2}" ]
then
MOUNT=${MNT}
break
fi
done << EOF
$( mount -p )
EOF
if [ ${MOUNT} -eq 0 ]
then
zfs set canmount=noauto ${POOL}/${BEDS}/${2}
zfs set mountpoint=${TMPMNT} ${POOL}/${BEDS}/${2}
zfs mount ${POOL}/${BEDS}/${2}
else
TMPMNT=${MOUNT}
fi
cp /boot/zfs/zpool.cache ${TMPMNT}/boot/zfs/zpool.cache
LOADER_CONFIGS=${TMPMNT}/boot/loader.conf
if [ -f ${TMPMNT}/boot/loader.conf.local ]
then
LOADER_CONFIGS="${LOADER_CONFIGS} ${TMPMNT}/boot/loader.conf.local"
fi
sed -i '' -E s/"^vfs.root.mountfrom=.*$"/"vfs.root.mountfrom=\"zfs:${POOL}\/${BEDS}\/${2##*/}\""/g ${LOADER_CONFIGS}
if [ ${MOUNT} -eq 0 ]
then
zfs umount ${POOL}/${BEDS}/${2}
zfs set mountpoint=legacy ${POOL}/${BEDS}/${2}
fi
fi
if ! zpool set bootfs=${POOL}/${BEDS}/${2} ${POOL} 1> /dev/null 2> /dev/null
then
echo "ERROR: Failed to activate '${2}' boot environment"
exit 1
fi
fi
# execute ZFS LIST only once
ZFS_LIST=$( zfs list -H -o name -r ${POOL}/${BEDS} )
# disable automatic mount on all inactive boot environments
echo "${ZFS_LIST}" \
| grep -v "^${POOL}/${BEDS}/${2}$" \
| grep -v "^${POOL}/${BEDS}/${2}/" \
| while read NAME
do
zfs set canmount=noauto ${NAME}
done
# enable automatic mount for active boot environment and promote it
echo "${ZFS_LIST}" \
| grep -E "^${POOL}/${BEDS}/${2}(/|$)" \
| while read NAME
do
zfs set canmount=on ${NAME}
while __be_clone ${NAME}
do
zfs promote ${NAME}
done
done
echo "Activated successfully"
;;
(destroy) # -----------------------------------------------------------------
if [ "${2}" != "-F" ]
then
DESTROY=${2}
else
DESTROY=${3}
fi
__be_exist ${POOL}/${BEDS}/${DESTROY}
case ${#} in
(2)
echo "Are you sure you want to destroy '${2}'?"
echo -n "This action cannot be undone (y/[n]): "
read CHOICE
;;
(3)
if [ "${2}" != "-F" ]
then
__usage
fi
CHOICE=Y
;;
(*)
__usage
;;
esac
if [ "${BOOTFS}" = "${POOL}/${BEDS}/${DESTROY}" ]
then
echo "ERROR: Cannot destroy active boot environment"
exit 1
fi
case ${CHOICE} in
(Y|y|[Yy][Ee][Ss])
# destroy snapshot or boot environment
if __be_snapshot ${POOL}/${BEDS}/${DESTROY}
then
# destroy desired snapshot
if ! zfs destroy -r ${POOL}/${BEDS}/${DESTROY} 1> /dev/null 2> /dev/null
then
echo "ERROR: Snapshot '${2}' is origin for other boot environment"
exit 1
fi
else
if __be_clone ${POOL}/${BEDS}/${DESTROY}
then
# promote clones dependent on snapshots used by destroyed boot environment
zfs list -H -t all -o name,origin -r ${POOL} \
| while read NAME ORIGIN
do
if echo "${ORIGIN}" | grep -q -E "${POOL}/${BEDS}/${DESTROY}(/.*@|@)" 2> /dev/null
then
zfs promote ${NAME}
fi
done
# get origins used by destroyed boot environment
ORIGIN_SNAPSHOTS=$( zfs list -H -t all -o origin -r ${POOL}/${BEDS}/${DESTROY} | grep -v '^-$' | awk -F "@" '{print $2}' | sort -u )
fi
# check if boot environment was created from existing snapshot
ORIGIN=$( zfs list -H -o origin ${POOL}/${BEDS}/${DESTROY} )
CREATION=$( zfs list -H -o creation ${POOL}/${BEDS}/${DESTROY} )
CREATION=$( date -j -f "%a %b %d %H:%M %Y" "${CREATION}" +"%Y-%m-%d-%H:%M" )
SNAPSHOT_NAME=$( echo "${ORIGIN}" | cut -d '@' -f 2 | sed -E 's/:[0-9]{2}$//g' )
if [ "${2}" = "-F" ]
then
CHOICE=1
elif [ "${SNAPSHOT_NAME}" != "${CREATION}" ]
then
ORIGIN=$( basename ${ORIGIN} )
echo "Boot environment '${DESTROY}' was created from existing snapshot"
echo -n "Destroy '${ORIGIN}' snapshot? (y/[n]): "
read CHOICE
case ${CHOICE} in
(Y|y|[Yy][Ee][Ss])
CHOICE=1
;;
(*)
CHOICE=0
echo "Origin snapshot '${ORIGIN}' will be preserved"
;;
esac
else
CHOICE=1
fi
# destroy boot environment
zfs destroy -r ${POOL}/${BEDS}/${DESTROY}
# check if boot environment is a clone
if __be_clone ${POOL}/${BEDS}/${DESTROY}
then
# promote datasets dependent on origins used by destroyed boot environment
ALL_ORIGINS=$( zfs list -H -t all -o name,origin -r ${POOL} )
echo "${ORIGIN_SNAPSHOTS}" \
| while read S
do
echo "${ALL_ORIGINS}" \
| grep "${S}" \
| awk '{print $1}' \
| while read I
do
zfs promote ${I}
done
done
fi
# destroy origins used by destroyed boot environment
SNAPSHOTS=$( zfs list -H -t snapshot -o name -r ${POOL} )
echo "${ORIGIN_SNAPSHOTS}" \
| while read S
do
echo "${SNAPSHOTS}" \
| grep "@${S}$" \
| while read I
do
if [ ${CHOICE} -eq 1 ]
then
zfs destroy ${I}
fi
done
done
fi
# check if we need to update grub
if [ "${GRUB}" = YES ]
then
__update_grub
fi
echo "Destroyed successfully"
;;
(*)
echo "Boot environment '${DESTROY}' has not been destroyed"
;;
esac
;;
(rename) # ------------------------------------------------------------------
if [ ${#} -ne 3 ]
then
__usage
fi
__be_exist ${POOL}/${BEDS}/${2}
if [ "${BOOTFS}" = "${POOL}/${BEDS}/${2}" ]
then
echo "ERROR: Renaming active boot environment is not supported"
exit 1
fi
if zfs list -H -o name ${POOL}/${BEDS}/${3} 2> /dev/null
then
echo "ERROR: Boot environment '${3}' already exists"
exit 1
fi
zfs rename ${POOL}/${BEDS}/${2} ${POOL}/${BEDS}/${3}
echo "Renamed successfully"
;;
(mount) # ------------------------------------------------------------
if [ ${#} -eq 2 ]
then
TARGET=$( mktemp -d -t BE-${2} )
elif [ ${#} -eq 3 ]
then
TARGET=${3}
else
__usage
fi
__be_exist "${POOL}/${BEDS}/${2}"
if __be_mounted "${POOL}/${BEDS}/${2}"
then
MNT=$( mount | grep -E "^${POOL}/${BEDS}/${2} " | awk '{print $3}' )
echo "Boot environment '${2}' is already mounted at '${MNT}'"
exit 1
fi
if ! mkdir -p ${TARGET} 2> /dev/null
then
echo "ERROR: Cannot create '${TARGET}' mountpoint"
exit 1
fi
if ! mount -t zfs ${POOL}/${BEDS}/${2} ${TARGET}
then
echo "ERROR: Cannot mount '${2}' at '${TARGET}' mountpoint"
exit 1
fi
PREFIX=$( echo ${POOL}/${BEDS}/${2}/ | sed 's/\//\\/g' )
zfs list -H -o name,mountpoint -r ${POOL}/${BEDS}/${2} \
| grep -v -E "[[:space:]](legacy|none)$" \
| sort -n \
| grep -E "^${POOL}/${BEDS}/${2}/" \
| while read FS MOUNTPOINT
do
if [ "{FS}" != "${POOL}/${BEDS}/${2}" ]
then
INHERIT=$( zfs get -H -o source mountpoint ${FS} )
if [ "${INHERIT}" = "local" ]
then
case ${MOUNTPOINT} in
(legacy|none)
continue
;;
(*)
MOUNTPOINT="/$( echo "${FS}" | sed s/"${PREFIX}"//g )"
;;
esac
fi
fi
if ! mkdir -p ${TARGET}${MOUNTPOINT} 1> /dev/null 2> /dev/null
then
echo "ERROR: Cannot create '${TARGET}${MOUNTPOINT}' mountpoint"
exit 1
fi
if ! mount -t zfs ${FS} ${TARGET}${MOUNTPOINT} 1> /dev/null 2> /dev/null
then
echo "ERROR: Cannot mount '${FS}' at '${TARGET}${MOUNTPOINT}' mountpoint"
exit 1
fi
done
echo "Mounted successfully on '${TARGET}'"
;;
(umount|unmount) # ----------------------------------------------------------
if [ ${#} -eq 2 ]
then
# we need this empty section for argument checking
:
elif [ ${#} -eq 3 -a "${2}" = "-f" ]
then
OPTS="-f"
shift
else
__usage
fi
__be_exist "${POOL}/${BEDS}/${2}"
if ! __be_mounted "${POOL}/${BEDS}/${2}"
then
echo "Boot environment '${2}' is not mounted"
exit 1
fi
MOUNT=$( mount )
MOUNTPOINT=$( echo "${MOUNT}" | grep -m 1 "^${POOL}/${BEDS}/${2} on " | awk '{print $3}' )
echo "${MOUNT}" \
| awk '{print $1}' \
| grep -E "^${POOL}/${BEDS}/${2}(/|$)" \
| sort -n -r \
| while read FS
do
if ! umount ${OPTS} ${FS} 1> /dev/null 2> /dev/null
then
echo "ERROR: Cannot umount '${FS}' dataset"
exit 1
fi
done
echo "Unmounted successfully"
# only delete the temporary mountpoint directory
if echo "${MOUNTPOINT}" | grep -q -E "/BE-${2}\.[a-zA-Z0-9]{8}" 1> /dev/null 2> /dev/null
then
# delete only when it is an empty directory
if [ $( find ${MOUNTPOINT} | head | wc -l | bc ) -eq 1 ]
then
rm -r ${MOUNTPOINT}
fi
fi
;;
(*) # -----------------------------------------------------------------------
__usage
;;
esac

214
beadm.1
View File

@ -1,214 +0,0 @@
.\"
.\" beadm - Illumos/Solaris-like utility for FreeBSD to manage
.\" Boot Environments on ZFS filesystems
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\"
.\"
.\" @(#)beadm.1
.\" $FreeBSD$
.\"
.Dd September 4, 2012
.Dt BEADM 1
.Os FreeBSD
.Sh NAME
.Nm beadm
.Nd Utility to manage Boot Environments with ZFS
.Sh SYNOPSIS
.Nm
activate
.Ao Ar beName Ac
.Nm
create
.Op Fl e Ar nonActiveBe | Fl e Ar beName@snapshot
.Ao Ar beName Ac
.Nm
create
.Ao Ar beName@snapshot Ac
.Nm
destroy
.Op Fl F
.Ao Ar beName | beName@snapshot Ac
.Nm
list
.Op Fl a
.Op Fl D
.Op Fl H
.Op Fl s
.Nm
mount
.Ao Ar beName Ac
.Op mountpoint
.Nm
rename
.Ao Ar origBeName Ac
.Ao Ar newBeName Ac
.Nm
{ umount | unmount }
.Op Fl f
.Ao Ar beName Ac
.Sh DESCRIPTION
The
.Nm
command is used to setup and interact with Boot Environments with ZFS.
.Pp
.Em Boot Environments
allows the system to be upgraded, while preserving the old system environment in a separate ZFS dataset.
.Pp
.Sh COMMANDS
The following commands are supported by
.Nm :
.Bl -tag -width indent
.It Ic activate Ar <beName>
.Pp
Activate the given
.Ar beName
for the next boot.
.Pp
.It Ic create
.Op Fl e Ar nonActiveBe | Fl e Ar beName@snapshot
.Ao Ar beName Ac
.Pp
Creates a new boot environment named
.Ar beName .
If the -e param is specified, the new environment will be cloned from the given
.Ar nonActiveBe | Ar beName@snapshot .
.Pp
.It Ic create
.Ao Ar beName@snapshot Ac
.Pp
Creates a snapshot of the existing boot environment named
.Ar beName .
.Pp
.It
.Ic destroy Op Fl F
.Ao Ar beName | beName@snapshot Ac
.Pp
Destroys the given
.Ar beName
boot environment or
.Ar beName@snapshot
snapshot.
Specifying
.Fl F
will automatically unmount without confirmation.
.Pp
.It Ic list
.Op Fl a
.Op Fl D
.Op Fl H
.Op Fl s
.Pp
Displays all boot environments.
The Active field indicates whether the boot environment is active now (N); active on reboot (R); or both (NR).
.PP
If
.Fl a
is used, display all datasets.
If
.Fl D
is used, display the full space usage for each boot environment, assuming all other boot environments were destroyed.
The
.Fl H
option is used for scripting. It does not print headers and separate fields by a single tab instead of arbitrary white space.
If
.Fl s
is used, display all snapshots as well.
.Pp
.It Ic mount
.Ao Ar beName Ac
.Op mountpoint
.Pp
Temporarily mount the boot environment.
Mount at the specified
.Ar mountpoint
if provided.
.Pp
.It Ic rename Ao Ar origBeName Ac Ao Ar newBeName Ac
.Pp
Renames the given nonactive
.Ar origBeName
to the given
.Ar newBeName
.Pp
.It Ic umount
.Op Fl f
.Ao Ar beName Ac
.Pp
Unmount the given boot environment, if it is mounted.
Specifying
.Fl f
will force the unmount if busy.
.Pp
.El
.Sh EXAMPLES
.Bl -bullet
.It
Perform a system upgrade in a
.Xr jail 8
.Pp
Create a new boot environment called
.Em jailed :
.Pp
.Dl beadm create -e default jailed
.Pp
Set mountpoint for new jail to
.Pa /usr/jails/jailed :
.Pp
.Dl beadm mount jailed /usr/jails/jailed
.Pp
The currently active boot environment is now replicated into the jailed system and ready for upgrade.
Startup the jail, login and perform the normal upgrade process.
Once this is done, stop the jail and disable it in
.Pa /etc/rc.conf.
.Pp
Now activate the boot environment for the next boot
.Pp
.Dl beadm activate jailed
.Pp
Reboot into the new environment
.Pp
.Dl reboot
.El
.Sh HOWTO
A HOWTO guide is posted at the FreeBSD forums:
.Bl -bullet
.It
.Ar http://forums.freebsd.org/showthread.php?t=31662
.El
.Pp
.Sh SEE ALSO
.Xr jail 8 ,
.Xr zfs 8 ,
.Xr zpool 8
.Sh HISTORY
.Xr beadm 1M
originally appeared in Solaris.
.Sh AUTHORS
.Bl -bullet
.It
Slawomir Wojciech Wojtczak (vermaden)
.Ar vermaden@interia.pl
.Pp
Creator and maintainer of
.Nm .
.It
Bryan Drewery (bdrewery)
.Ar bryan@shatow.net
.Pp
Wrote this manual page and contributed child dataset fixes.
.It
Mike Clarke (rawthey)
.Ar jmc-fbsd@milibyte.co.uk
.Pp
Wrote fast implementation of
.Nm Ar list .
.Pp
Contributed a lot of fixes and usability changes.

View File

@ -1,2 +0,0 @@
GRUB=YES