Compare commits
No commits in common. "0.8.5" and "master" have entirely different histories.
555
HOWTO.htm
555
HOWTO.htm
|
|
@ -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>
|
||||
|
|
@ -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
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
beadm
|
||||
=====
|
||||
|
||||
FreeBSD utility to manage Boot Environments on ZFS filesystems.
|
||||
|
|
@ -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
|
||||
Binary file not shown.
|
|
@ -0,0 +1,3 @@
|
|||
USERUMOUNT=YES
|
||||
|
||||
|
||||
|
|
@ -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 &";
|
||||
};
|
||||
|
||||
|
|
@ -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";
|
||||
};
|
||||
|
||||
|
|
@ -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 &";
|
||||
};
|
||||
|
||||
|
|
@ -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 &";
|
||||
};
|
||||
|
||||
|
||||
753
beadm
753
beadm
|
|
@ -1,753 +0,0 @@
|
|||
#!/bin/sh -e
|
||||
|
||||
# Copyright (c) 2012 Slawomir Wojciech Wojtczak (vermaden). All rights reserved.
|
||||
# Copyright (c) 2012 Bryan Drewery (bdrewery). All rights reserved.
|
||||
# Copyright (c) 2012 Mike Clarke (rawthey). 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:/sbin:/usr/sbin
|
||||
|
||||
if [ $( uname -r | cut -d '.' -f1 ) -lt 8 ]
|
||||
then
|
||||
echo "ERROR: beadm works on FreeBSD 8.0 or later"
|
||||
exit 1
|
||||
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 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} \
|
||||
| while read FS
|
||||
do
|
||||
local OPTS=""
|
||||
while read NAME PROPERTY VALUE
|
||||
do
|
||||
local OPTS="-o ${PROPERTY}=${VALUE} ${OPTS}"
|
||||
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}\/ROOT\/${SOURCE##*/}"/"${POOL}\/ROOT\/${2##*/}"/g )
|
||||
if [ "${OPTS}" = "-o = " ]
|
||||
then
|
||||
local OPTS=""
|
||||
fi
|
||||
if __be_snapshot ${1}
|
||||
then
|
||||
zfs clone -o canmount=off ${OPTS} ${FS}@${1##*@} ${DATASET}
|
||||
else
|
||||
zfs clone -o canmount=off ${OPTS} ${FS}@${FMT} ${DATASET}
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
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 ! zfs list ${POOL}/ROOT 1> /dev/null 2> /dev/null
|
||||
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
|
||||
|
||||
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 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 "/ROOT"
|
||||
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}/ROOT/${3}
|
||||
if zfs list -H -o name ${POOL}/ROOT/${4} 1> /dev/null 2> /dev/null
|
||||
then
|
||||
echo "ERROR: Boot environment '${4}' already exists"
|
||||
exit 1
|
||||
fi
|
||||
__be_new ${POOL}/ROOT/${3} ${POOL}/ROOT/${4}
|
||||
;;
|
||||
(2)
|
||||
if __be_snapshot ${2}
|
||||
then
|
||||
if ! zfs snapshot -r ${POOL}/ROOT/${2} 1> /dev/null 2> /dev/null
|
||||
then
|
||||
echo "ERROR: Cannot create '${2}' recursive snapshot"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
__be_new ${ROOTFS} ${POOL}/ROOT/${2}
|
||||
fi
|
||||
;;
|
||||
(*)
|
||||
__usage
|
||||
;;
|
||||
esac
|
||||
echo "Created successfully"
|
||||
;;
|
||||
|
||||
(activate) # ----------------------------------------------------------------
|
||||
if [ ${#} -ne 2 ]
|
||||
then
|
||||
__usage
|
||||
fi
|
||||
__be_exist ${POOL}/ROOT/${2}
|
||||
if [ "${BOOTFS}" = "${POOL}/ROOT/${2}" ]
|
||||
then
|
||||
echo "Already activated"
|
||||
exit 0
|
||||
else
|
||||
if __be_mounted ${POOL}/ROOT/${2}
|
||||
then
|
||||
MNT=$( mount | grep -E "^${POOL}/ROOT/${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}/ROOT/${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}/ROOT/${2}" ]
|
||||
then
|
||||
MOUNT=${MNT}
|
||||
break
|
||||
fi
|
||||
done << EOF
|
||||
$( mount -p )
|
||||
EOF
|
||||
if [ ${MOUNT} -eq 0 ]
|
||||
then
|
||||
zfs set canmount=noauto ${POOL}/ROOT/${2}
|
||||
zfs set mountpoint=${TMPMNT} ${POOL}/ROOT/${2}
|
||||
zfs mount ${POOL}/ROOT/${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}\/ROOT\/${2##*/}\""/g ${LOADER_CONFIGS}
|
||||
if [ ${MOUNT} -eq 0 ]
|
||||
then
|
||||
zfs umount ${POOL}/ROOT/${2}
|
||||
zfs set mountpoint=legacy ${POOL}/ROOT/${2}
|
||||
fi
|
||||
fi
|
||||
if ! zpool set bootfs=${POOL}/ROOT/${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}/ROOT )
|
||||
# disable automatic mount on all inactive boot environments
|
||||
echo "${ZFS_LIST}" \
|
||||
| grep -v "^${POOL}/ROOT/${2}$" \
|
||||
| grep -v "^${POOL}/ROOT/${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}/ROOT/${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}/ROOT/${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}/ROOT/${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}/ROOT/${DESTROY}
|
||||
then
|
||||
# destroy desired snapshot
|
||||
if ! zfs destroy -r ${POOL}/ROOT/${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}/ROOT/${DESTROY}
|
||||
then
|
||||
# promote clones dependent on snapshots used by destroyed boot environment
|
||||
zfs list -H -t all -o name,origin \
|
||||
| while read NAME ORIGIN
|
||||
do
|
||||
if echo "${ORIGIN}" | grep -q -E "${POOL}/ROOT/${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}/ROOT/${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}/ROOT/${DESTROY} )
|
||||
CREATION=$( zfs list -H -o creation ${POOL}/ROOT/${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}/ROOT/${DESTROY}
|
||||
# check if boot environment is a clone
|
||||
if __be_clone ${POOL}/ROOT/${DESTROY}
|
||||
then
|
||||
# promote datasets dependent on origins used by destroyed boot environment
|
||||
ALL_ORIGINS=$( zfs list -H -t all -o name,origin )
|
||||
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 )
|
||||
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
|
||||
echo "Destroyed successfully"
|
||||
;;
|
||||
(*)
|
||||
echo "Boot environment '${DESTROY}' has not been destroyed"
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
|
||||
(rename) # ------------------------------------------------------------------
|
||||
if [ ${#} -ne 3 ]
|
||||
then
|
||||
__usage
|
||||
fi
|
||||
__be_exist ${POOL}/ROOT/${2}
|
||||
if [ "${BOOTFS}" = "${POOL}/ROOT/${2}" ]
|
||||
then
|
||||
echo "ERROR: Renaming active boot environment is not supported"
|
||||
exit 1
|
||||
fi
|
||||
if zfs list -H -o name ${POOL}/ROOT/${3} 2> /dev/null
|
||||
then
|
||||
echo "ERROR: Boot environment '${3}' already exists"
|
||||
exit 1
|
||||
fi
|
||||
zfs rename ${POOL}/ROOT/${2} ${POOL}/ROOT/${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}/ROOT/${2}"
|
||||
if __be_mounted "${POOL}/ROOT/${2}"
|
||||
then
|
||||
MNT=$( mount | grep -E "^${POOL}/ROOT/${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}/ROOT/${2} ${TARGET}
|
||||
then
|
||||
echo "ERROR: Cannot mount '${2}' at '${TARGET}' mountpoint"
|
||||
exit 1
|
||||
fi
|
||||
PREFIX=$( echo ${POOL}/ROOT/${2}/ | sed 's/\//\\\//g' )
|
||||
zfs list -H -o name,mountpoint -r ${POOL}/ROOT/${2} \
|
||||
| grep -v -E "[[:space:]](legacy|none)$" \
|
||||
| sort -n \
|
||||
| grep -E "^${POOL}/ROOT/${2}/" \
|
||||
| while read FS MOUNTPOINT
|
||||
do
|
||||
if [ "{FS}" != "${POOL}/ROOT/${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}/ROOT/${2}"
|
||||
if ! __be_mounted "${POOL}/ROOT/${2}"
|
||||
then
|
||||
echo "Boot environment '${2}' is not mounted"
|
||||
exit 1
|
||||
fi
|
||||
MOUNT=$( mount )
|
||||
MOUNTPOINT=$( echo "${MOUNT}" | grep -m 1 "^${POOL}/ROOT/${2} on " | awk '{print $3}' )
|
||||
echo "${MOUNT}" \
|
||||
| awk '{print $1}' \
|
||||
| grep -E "^${POOL}/ROOT/${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
214
beadm.1
|
|
@ -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 beadmn 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.
|
||||
Loading…
Reference in New Issue