13
FOREIGN FILESYSTEMS

image

FreeBSD supports a variety of filesystems other than ZFS and UFS. You’ll need to be able to interoperate with other hosts by using optical media, flash drives, and the like. Additionally, FreeBSD uses the special-purpose filesystem devfs(5) to manage device nodes. Jail users might need the process filesystem procfs(5). For extremely fast storage that doesn’t need to survive a reboot, you can use system RAM as a filesystem. You can mount filesystems over the network, using either the Unix-style Network File System or Microsoft’s Common Internet File System (CIFS). And no matter how hard you try to avoid it, sometimes you’re stuck mounting ISO images.

Using any of these requires a deeper understanding of mounting filesystems.

FreeBSD Mount Commands

We saw mount(8) earlier when discussing UFS filesystems, but you’ll also use it to attach other filesystems to the directory tree. The mount(8) command assumes that any local partitions use UFS. If you try to mount a non-UFS filesystem, you’ll get an error.

# mount /dev/cd0 /media
mount: /dev/cd0: Invalid argument

The device node /dev/cd0 represents an optical drive. I put a CD in the drive just for this test, so it should work. Trying to mount it gives an error, though. To mount a UFS filesystem, you need the device node and a mount point. Mounting foreign filesystems means adding the filesystem type with -t. CDs use the ISO 9660 filesystem, which FreeBSD calls cd9660. Here, I specify the filesystem to mount that CD on /cdrom:

# mount -t cd9660 /dev/cd0 /media

I can now go to the /media directory and view the contents. Simple enough, eh?

Many filesystems have their own custom variant of the mount(8) command. Get a full list by running apropos mount_. Yes, you need the trailing underscore; all of the mount(8) variants use that as a separator. You’ll find mount_cd9660(8), mount_msdosfs(8), mount_nfs(8), and more. Always use mount -t on filesystems without such a command.

Apply mount options with the -o flag. You’ll need to check each mount command’s man page to see what mount options the filesystem supports. Separate multiple mount options with commas. Here, I mount a FAT32 drive at device node /dev/da1 read-only, assigning the owner and group to user bert:

# mount -t msdosfs -o ro,-gbert,-ubert /dev/da1 /media

You can unmount any mounted filesystem with umount(8):

# umount /media

The umount(8) command doesn’t care about the filesystem type. It just tries to disconnect the disk partition from the filesystem. It does care about whether someone is using the filesystem, however, and refuses to unmount it if even one process uses it. If you have an idle terminal with a shell prompt in the filesystem, umount(8) will refuse to unmount the filesystem.

If you’re constantly connecting and disconnecting filesystems, investigate autofs(5) to handle these mounts automatically.

Supported Foreign Filesystems

Here are some of the most commonly used foreign filesystems, along with a brief description of each and the appropriate mount command.

FAT (MS-DOS)

FreeBSD includes extensive support for FAT, the DOS/Windows 9x File Allocation Table filesystem, commonly used on removable media and some dual-boot systems. This support covers the FAT12, FAT16, and FAT32 varieties. You can format a thumb drive with a non-FAT filesystem, however, so don’t blindly assume that all thumb drives use FAT. As the most common use for a thumb drive these days is transferring files between machines, however, most are FAT32. The mount type is msdosfs (mount -t msdosfs).

If you handle a lot of FAT32 disks, investigate the mtools package, a collection of programs for working with FAT filesystems that offer greater flexibility than the default FreeBSD tools.

ISO 9660

ISO 9660 is the standard filesystem for CDs and is occasionally used on DVDs. FreeBSD supports reading and writing CDs if you have a CD burner. Just about every CD you encounter is formatted with ISO 9660. The mount command is mount -t cd9660.

The cdrtools package, in /usr/ports/sysutils/cdrtools, contains many helpful tools for working with CD images, including tools that build an ISO image from files on disk.

UDF

UDF, or Universal Disk Format, is a replacement for ISO 9660. You’ll find UDF on some DVDs and Blu-Ray disks and on a few thumb drives larger than the 32GB supported under FAT32. As the capacity of removable media increases, you’ll see more and more UDF filesystems. The mount command is mount -t udf.

EXT

The standard Linux filesystems—EXT2, EXT3, and EXT4—support many of the same features as UFS. FreeBSD can safely read from and write to EXT2 and EXT3 filesystems without any problems but can mount EXT4 filesystems only as read-only.

Mounting Linux filesystems is most useful for disaster recovery, dual-boot systems, or system migrations. Despite the name, mount -t ext2fs supports mounting all versions of EXT.

Linux filesystem users might find the tools in /usr/ports/sysutils/e2fsprogs useful. They let you fsck(8) and assess Linux filesystems, among other things.

Permissions and Foreign Filesystems

Permissions of a filesystem depend on the filesystem features and the person who mounts it. FreeBSD tries to support features that aren’t too different from those in UFS or ZFS.

Consider the Linux filesystem, EXT. EXT stores permissions in the filesystem and lets the kernel map them to UIDs. Since EXT permissions behave much like UFS permissions and all the necessary permissions information is available within the filesystem, FreeBSD respects the permissions on these filesystems. EXT doesn’t support BSD file flags, however, so you can’t assign those flags to a file on EXT.

FAT has no permissions system. Even if you mount your FAT32 thumb drive in your FreeBSD host, you can’t apply permissions to files.

By default, only root can mount filesystems, and root owns all non-Unix filesystems. If that’s not your preference, you can use the -u and -g flags to set the user ID and group ID of the owner when you’re mounting a FAT32, ISO 9660, or UDF filesystem. For example, if you’re mounting a FAT32 USB device for the user xistence and want him to be able to edit the contents, use this command:

# mount -t msdosfs -u xistence -g xistence /dev/da5 /mnt

The user xistence now owns the files on the device.

You might get sick of mounting media for your users, especially in a facility with dozens of machines. To let users mount filesystems, set the sysctl vfs.usermount to 1. Users can then mount any device they have permission to access on any mount point they own. While xistence couldn’t mount the removable device on /media, he could mount it on /home/xistence/media.

Using Removable Media

You must be able to manage any removable media that might wander in through the door of your data center. Here, we’ll discuss optical disks and flash drives.

I recommend not plugging removable media willy-nilly into your production servers—for security reasons if nothing else. Who knows what’s actually on that vendor’s USB device? Worse, you can order “USB killer” devices that deliberately damage hardware. Mount suspicious devices on a disposable workstation, examine the contents, and then copy the desired data over to the FreeBSD machine. This isn’t guaranteed to be safe, as the many USB interfaces can inject data into the hardware beneath the OS layer, but it’s as safe as you’ll get. Removable media is just too easy for certain applications, however, and of course the rules change when it’s my personal USB device.

Using the device requires a filesystem type, a device node, and a mount point.

Figuring out a removable drive’s filesystem can require a bit of trial and error. CDs use the ISO 9660 filesystem, while DVDs and Blu-Rays use either a UDF or a combination of ISO 9660 and UDF. When in doubt, try CD9660 first. USB devices and floppy disks are usually FAT32. While it was once expected that large USB devices would use UDF, most of them still use FAT32. Run fstyp(8) on a device node to help identify the filesystem on it, or try gpart show on the disk’s device node.

Removable devices can have a different device node each time you plug them in. Optical drives are a little easier to identify in that most hosts have very few optical drives. If you have one optical drive, it’s /dev/cd0. USB devices appear as the next available unit of /dev/da. When you insert a USB device, a message giving the device node and type appears on the console and in /var/log/messages, or you could check camcontrol devlist for the new device.

FreeBSD provides a /media mount point for general removable media mounts. You can create additional mount points as you like—they’re just directories. For miscellaneous short-term mounts, FreeBSD offers /mnt.

So, to mount your FAT32 USB device /dev/da0 on /media, run:

# mount -t msdosfs /dev/da0 /media

Occasionally, you’ll find a thumb drive with a partition table. These devices will insist you mount /dev/da0s1 or /dev/da0p1 rather than /dev/da0. The device’s formatting dictates this, not anything in FreeBSD. The gpart show command can help you figure out which partitions are on a device and what filesystem is on each partition.

Ejecting Removable Media

To disconnect removable media from your FreeBSD system, first unmount the filesystem. Your optical drive won’t open until you unmount the disk. You can pull a USB flash drive from its port, but doing so while the filesystem is mounted might damage data on the device. Use umount(8) just as you would for any other filesystem:

# umount /media

On many optical drives, camcontrol eject opens the drive tray.

Removable Media and /etc/fstab

You can update /etc/fstab with entries for removable media to make system maintenance a little easier. If a removable filesystem has an entry in /etc/fstab, you can drop both the filesystem and the device name when mounting it. This means that you don’t have to remember the exact device name or filesystem to mount the device.

When listing removable media in /etc/fstab, be sure to include the noauto flag. Otherwise, whenever you don’t have the removable media in place, your boot will stop in single-user mode because a filesystem is missing.

Here’s an /etc/fstab’s entry for an optical drive:

/dev/cd0   /cdrom   cd9660  ro,noauto       0       0

While I’m sure you’ve already memorized the meaning of every column in /etc/fstab, we’ll remind you that this entry means, “Mount /dev/cd0 on /cdrom, using the ISO 9660 filesystem. Mount it as read-only, and don’t mount it automatically at boot.”

Here’s a similar entry for a thumb drive. I use the large option to support filesystems larger than 128GB, as discussed in mount_msdosfs(8).

/dev/da0    /media    msdosfs  rw,noauto,large     0       0

FreeBSD doesn’t provide these by default, but I find having them to be much easier on systems where I use removable media regularly. Confirm that your next available da device is /dev/da0 , as trying to mount a hard drive that’s already mounted won’t work.

Formatting FAT32 Media

Thumb drives use the FAT32 filesystem but always come preformatted. As thumb drives have a limited number of reads and writes directly proportional to their cheapness, do not reformat them capriciously.1 Only reformat thumb drives when their filesystem becomes corrupt. Use newfs_msdos(8) to create a FAT32 filesystem.

# newfs_msdos /dev/da0

You’ll get a couple lines of output, and you have a new filesystem.

Creating Optical Media

FreeBSD will let you bundle up a bunch of files into an image suitable for burning onto a CD, DVD, or Blu-Ray, using either CD 9660 or UDF formats. You can burn either image onto disk. FreeBSD supports creating ISOs natively, but you’ll need programs from the cdrtools package to create UDF.

In either case, start by putting all of the files and directories you want to burn into a single directory. The image will contain these files and directories exactly as you arrange them. Remember, optical disk images are read-only. You can’t update an image; you can only create a new image, so be sure you have everything exactly as you want it. Later this chapter, you’ll learn to mount these images with mdconfig(8).

In both of these examples, we’re creating an image from the files contained in /home/xistence/cdfiles.

Creating ISOs

Use makefs(8) to create an ISO.

# makefs -t cd9660 -o allow-deep-trees,rockridge image.iso source-files

Start by using -t to specify the type of filesystem to create—in this case, CD 9660. The -o flag lets you specify filesystem-specific options. You can get a whole list of options from the makefs(8) man page, but the ones shown here suffice for most images. We then need the filename for the created image and the source directory for those files.

To make an image containing the files in /home/xistence/cdfiles as bert.iso, run:

# makefs -t cd9660 -o allow-deep-trees,rockridge bert.iso /home/xistence/cdfiles

Bert can now wastefully burn his ISO to physical media.

Creating a UDF

Creating a UDF requires using mkisofs(1) from the cdrtools package. Give the destination image file with -o. Enable the Joliet and Rock Ridge extensions with -J and -R, respectively. (I’m not going to go into what each of these do, but if you want your ISO to behave like a disk from this millennium, you need them.) Add the -udf and -iso-level 3 flags.2

# mkisofs -R -J -udf -iso-level 3 -o bert.udf /home/xistence/cdfiles

You now have a UDF image based on what’s in /home/xistence/cdfiles.

Whichever format you create, I encourage you to mount it and double-check your work before burning a physical disk. If you’re lucky, you’ll remember the stuff you forgot to include on the image.

Burning ISOs to Optical Media

Use cdrecord(1) from the cdrtools package to burn ISO images to the disk. Give the image file as an argument.

# cdrecord bert.iso

Depending on the drive speed and image size, this might take a while.

The cdrecord(1) program defaults to using /dev/cd0. If you have additional optical drives, use the -dev flag to give an alternate device name.

# cdrecord -dev=cd9 bert.iso

You now have a flimsy plastic disk that you’ll use twice before flinging it into the landfill. Congratulations!

Burning UDF to Optical Media

While you can use cdrecord(1) to burn UDF images to media, the growisofs(1) command from the dvd+rw-tools package is generally recommended. You’ll need the -dvd-compat and -Z flags. Then, specify the device and the image file.

# growisofs -dvd-compat -Z /dev/burner=image.udf

Suppose I want to burn bert.udf to the Blu-Ray in /dev/cd0.

# growisofs -dvd-compat -Z /dev/cd0=bert.udf

UDF files can be huge. Go make some tea. Eventually, you’ll have a burned disk.

Writing Images to Thumb Drives

USB thumb drives have increasingly supplanted optical disks, thanks in part to their reusability. FreeBSD supports writing disk images to thumb drives with dd(1).

Be very certain which device node is your thumb drive and which is your system hard drive. Thumb drives show up as /dev/da devices, exactly like many hard drives. Overwriting the wrong hard drive is embarrassing.3

The dd(1) command looks confusing at first glance.

# dd if=inputfile of=outputdevice bs=1M conv=sync

The if= argument gives the file you want to copy. The of= argument is the device node to copy to. The bs= flag gives the amount to copy at one time. Without this, dd(1) copies in 512-byte increments. The conv= argument gives dd(1) instructions about how to convert the incoming file. In this case, sync tells dd(1) to synchronize the size of the incoming and outgoing buffer. To burn bertimage.udf to thumb drive /dev/da9, I would run:

# dd if=bert.udf of=/dev/da9 bs=1m conv=sync

Wait a bit, and you’ll have an imaged thumb drive. Other uses of dd(1) might not need the conv= flag, but always use bs.

Now let’s look at some other filesystems you might find useful.

Memory Filesystems

In addition to putting filesystems on disks or partitions, FreeBSD lets you create partitions from files, pure RAM, and a combination of the two. One of the most popular uses of this feature is for memory filesystems, or memory disks. Reading and writing files to and from memory is much faster than accessing files on disk, which makes a memory-backed filesystem a huge optimization for certain applications. As with everything else in memory, however, you lose the contents of your memory disk at system shutdown.

FreeBSD supports two different memory-backed disks: tmpfs (pronounced “temp f s”) and memory disks. While they have similar concepts behind them, the underlying code is completely different, and they serve different roles. Use tmpfs(5) for memory-backed filesystems on long-running systems. Memory disks are more flexible but better suited for short-term use or mounting disk images.

tmpfs

The tmp in tmpfs(5) doesn’t mean “temporary.” It literally means tmp, as in /tmp. Use tmpfs for a speedy memory-backed /tmp and similar filesystems. Don’t deploy tmpfs everywhere you see a path with tmp in it, though. While /tmp is supposed to be cleared at every boot, /var/tmp is supposed to survive a reboot. You might use tmpfs for application lock files and other ephemeral data where vastly increased speed would improve application performance. While tmpfs has a troubled history, as of FreeBSD 10, it’s widely deployed and considered ready for production.

Create a tmpfs by mounting it.

# mount -t tmpfs tmpfs /tmp

If your system has the sysctl vfs.usermount set to 1, users can create and mount tmpfs filesystems.

tmpfs Options

A tmpfs defaults to the size of the system’s available RAM plus the available swap space. Repeatedly copying a file to /tmp could exhaust system memory. This would be bad. Set a maximum size for your tmpfs with the size option.

# mount -o size=1g -t tmpfs tmpfs /tmp

Control the ownership and permissions on a tmpfs with the uid, gid, and mode options. An actual /tmp directory needs to be world-writable with the sticky bit set, so be sure to use the option mode=1777.

If the tmpfs is for a specific user, even an unprivileged user that runs only a single application, assign that user ownership of the tmpfs.

tmpfs at Boot

Now that you can set a maximum size and the proper permissions, it’s okay to use /etc/fstab to automatically create a tmpfs at boot.

tmpfs  /tmp  tmpfs  rw,mode=1777,size=1G  0  0

For more complicated memory-backed disks, consider a traditional memory disk.

Memory Disks

A memory disk is an ephemeral storage device. Despite the name, a memory disk isn’t always a chunk of memory being treated as a disk. It can be such a device, but it might instead use a file or swap space or some other backing store. No matter what, the memory disk disappears at system shutdown.

Memory Disk Types

Memory disks come in four types: malloc-backed, swap-backed, vnode-backed, and null.

Malloc-backed memory disks are pure memory. Even if your system runs short on memory, FreeBSD won’t swap out the malloc-backed disk. Much like tmpfs(5), using a large malloc-backed disk is a great way to exhaust system memory. Malloc-backed disks are most useful for swapless embedded devices.

Swap-backed memory disks are mostly memory, but they also access the system swap partition. If the system runs out of memory, it moves the least recently used parts of memory to swap, as discussed in Chapter 21. Swap-backed disks are usually the best compromise between speed and performance.

Vnode-backed memory disks are files on disk. While you can use a file as backing for your memory disk, this is mostly useful for mounting disk images and testing.

A null memory disk discards everything sent to it. Any writes are successful, while any reads return zero. If I didn’t mention null memory disks, someone would write to complain, but I’m not giving a disk guaranteed to lose all data any more coverage than this.

Once you know what you want to do, use mdmfs(8) to perform the action.

Creating and Mounting Memory Disks

The mdmfs(8) utility is a handy frontend for several programs, such as mdconfig(8) and newfs(8). It handles the drudgery of configuring devices and creating filesystems on those devices, and makes creating memory disks as easy as possible. You need to know only the size of the disk you want to use, the type of the memory disk, and the mount point.

Swap-backed memory disks are the default. Just tell mdmfs(8) the size of the disk and the mount point. Here, we create a 48MB swap-backed memory disk on /home/mwlucas/test:

# mdmfs -t -s 48m md /home/mwlucas/test

The -s flag gives the size of the disk. If you run mount(8) without any arguments, you’ll see that you now have the memory disk device /dev/md0 mounted on that directory.

The -t flag enables TRIM, which we’ll discuss in the following section, “Memory Disk Headaches.”

To create and mount a malloc-backed disk, add the -M flag.

To mount a vnode-backed memory disk, use the -F flag and the path to the image file.

# mdmfs -F diskimage.file md /mnt

The md entry we’ve been using all along here means, “I don’t care what device name I get; just give me the next free one.” You can also specify a particular device name if you like. Here, I declare I want disk device /dev/md9:

# mdmfs -F diskimage.file md9 /mnt

Memory Disk Headaches

Traditional swap-backed memory disks never returned used memory to the system. Once you wrote to a memory disk, that memory was used up. If you needed a larger memory disk, you had to permanently allocate memory for it. This was one reason FreeBSD included tmpfs(5).

If the filesystem on the memory disk supports TRIM, however, FreeBSD now returns unused memory to the system. TRIM is not an acronym but rather a protocol for telling a disk which sectors are no longer in use. UFS, the default memory disk format, supports TRIM. Enable TRIM in mdmfs with the -t flag. If you’re using a different filesystem on a memory disk, though, be sure it’s strictly temporary.

To free the memory from a memory disk, shut down the memory disk.

Memory Disk Shutdown

To remove a memory disk, you must unmount the partition and destroy the disk device. Destroying the disk device frees the memory used by the device, which is useful when your system is heavily loaded. To find the disk device, run mount(8) and find your memory disk partition. Somewhere in the output, you’ll find a line like this:

/dev/md41 on /mnt (ufs, local, soft-updates)

Here, we see memory disk /dev/md41 mounted on /mnt. Let’s unmount it and destroy it.

# umount /mnt
# mdconfig -d -u 41

Unmounting with umount is done exactly as with other filesystems. The mdconfig(8) call is a new one, however. Use mdconfig(8) to directly manage memory devices. The -d flag means destroy, and the -u flag gives a device number. The above destroys the device /dev/md41, or the md device number 41. The memory used by this device is now freed for other uses.

Memory Disks and /etc/fstab

If you list memory disks in /etc/fstab, FreeBSD automatically creates them at boot time. These entries look more complicated than the other entries but aren’t too bad if you understand the mdmfs(8) commands we’ve been using so far.

We’re allowed to use md as a device name to indicate a memory disk. Choose the mountpoint just as for any other device, and use the filesystem type mfs. Under Options, list rw (for read-write) and the command line options used to create this device. If this is a long-term mount, add -t to enable TRIM. To create our 48MB filesystem mounted at /home/mwlucas/test, use the following /etc/fstab entry:

md      /home/mwlucas/test      mfs      rw,-s48m,-t      0   0

Looks easy, doesn’t it? The only problem is that the long line messes up your nice and even /etc/fstab entry’s appearance. Well, they’re not the only things that will make this file ugly, as we’ll soon see.

Mounting Disk Images

You can use mdmfs(8) to view UFS disk images, but most often you want to examine the contents of an ISO or UDF file without burning it to disk. (FreeBSD’s tar(1) can access the contents of an ISO, but not a UDF.) Just attach a memory disk to a file with the mdconfig(8) command’s -a flag. Here, I attach Bert’s ISO to a memory device:

# mdconfig -a -t vnode -f /home/mwlucas/bert.iso
md0

We tell mdconfig(8) to attach a vnode-backed memory device to the file specified . The mdconfig(8) command responds by telling us the device it’s attached to. Now we just mount the device with the proper mount command for the filesystem:

# mount -t cd9660 /dev/md0 /mnt

I can now verify that the ISO contains Bert’s files, so he doesn’t get to whine that the ISO is busted.

One common mistake people make at this point is mounting the image without specifying the filesystem type. You might get an error, or you might get a successful mount that contains no data—by default, mount(8) assumes that the filesystem is UFS!

When you’re done accessing the data, be sure to unmount the image and destroy the memory disk device just as you would for any other memory device. While vnode-backed memory disks don’t consume system memory, leaving unused memory devices around will confuse you months later when you wonder why they appear in /dev. If you’re not sure what memory devices a system has, use mdconfig -l to view all configured md(5) devices.

# mdconfig -l
md0 md1

I have two memory devices? Add the -u flag and the device number to see what type of memory device it is. Let’s see what memory device 1 (/dev/md1) is:

# mdconfig -l -u 1
md1     vnode     456M  /slice1/usr/home/mwlucas/iso/omsa-51-live.iso

I have an ISO image mounted on this system? Wow. I should probably reboot some month. Nah, that’s too much work; I’ll just unmount the filesystem and destroy the memory device.

Filesystems in Files

One trick used in embedded systems is building complete filesystem images on a local file. In the previous section, we saw how we could use memory disks to mount and access CD disk images. You can use the same techniques to create, update, and access UFS disk images.

To use a filesystem in a file, you must create a file of the proper size, attach the file to a memory device, place a filesystem on the device, and mount the device.

Creating an Empty Filesystem File

Use truncate(1) to create an empty file for a filesystem. These files are sparse files: they’re labeled as having a certain size but don’t actually take up any space until you put something in them. An empty sparse file takes up one filesystem block and grows when you put stuff in it. This means you can create an image for a disk of any size but use up only an amount of space equal to the stuff you put in the image.

Use the -s option and the file size to create an image file. Here, I create a 1GB file:

# truncate -s 1G filesystem.file

The resulting file claims to be pretty large.

# ls -l filesystem.file
-rw-r--r--  1 mwlucas  mwlucas  1073741824 Aug 11 11:31 filesystem.file

But if you check the disk usage, you’ll see something different.

# du filesystem.file
1       filesystem.file

This 1GB file uses one block on the disk.

Sparse files never shrink. They can only grow. If you erase a bunch of files from your disk image, the image file still needs that space.

Also, not all filesystems support sparse files. UFS and ZFS do. If you’re trying to create a sparse file on a FAT32 filesystem, you’re probably solving the wrong problem.

Creating the Filesystem on the File

To get a filesystem on the file, first associate the file with a device with a vnode-backed memory disk. We did exactly this in the last section:

# mdconfig -a -t vnode -f filesystem.file
md0

Now, let’s make a filesystem on this device. This is much like creating a UFS filesystem on a thumb disk with the newfs(8) command. Soft updates journaling is exactly as useful on file-backed filesystems as on disk-backed ones, so enable them with -j.

# newfs -j /dev/md0
/dev/md0: 1024.0MB (2097152 sectors) block size 32768, fragment size 4096
        using 4 cylinder groups of 256.03MB, 8193 blks, 32896 inodes.
        with soft updates
super-block backups (for fsck_ffs -b #) at:
 192, 524544, 1048896, 1573248
Using inode 4 in cg 0 for 8388608 byte journal
newfs: soft updates journaling set

The newfs(8) program prints out basic information about the disk, such as its size, block and fragment sizes, and the inode count.

Now that you have a filesystem, mount it:

# mount /dev/md0 /mnt

Congratulations! You now have a 1GB file-backed filesystem. Copy files to it, dump it to tape, or use it in any way you would use any other filesystem. But in addition to that, you can move it just like any other file.

File-Backed Filesystems and /etc/fstab

You can mount a file-backed filesystem automatically at boot with the proper entry in /etc/fstab, much like you can automatically mount any other memory disk. You simply have to specify the name of the file with -F and use -P to tell the system not to create a new filesystem on this file but just to use the one already there. Here, we mount the file-backed filesystem we created on /mnt automatically at boot time.

md    /mnt     mfs     rw,-P,-F/home/mwlucas/filesystem.file    0    0

I told you we’d see /etc/fstab entries uglier than the one for generic memory disks, didn’t I?

devfs

devfs(5) is a dynamic filesystem for managing device nodes. Remember, in a Unix-like operating system, everything is a file. This includes physical hardware. Almost all devices on the system have a device node under /dev. You’ve seen a bunch of device nodes for disks, but you’ll also see keyboards (/dev/ukbd0 or /dev/kbd0), the console (/dev/console), sound mixers (/dev/mixer0), and more. You’ll also find device nodes for logical devices, like the random number generator (/dev/random), terminal sessions (/dev/ttyv0), and so on.

Once upon a time, the sysadmin was responsible for making these device node files. Lucky sysadmins managed an operating system that came with a shell script to handle device node creation and permissions. If the OS authors hadn’t provided such a shell script, or if the server had unusual hardware not included in that shell script, the sysadmin had to create the node with animal sacrifices and mknod(8). If any little thing went wrong, the device wouldn’t work. The other option was to ship the operating system with device nodes for every piece of hardware imaginable. Sysadmins could be confident—well, mostly confident—that the desired device nodes were available, somewhere, buried within the thousands of files under /dev.

Of course, the kernel knows exactly what characteristics each device node should have. With devfs(5), FreeBSD simply asks the kernel what device nodes the kernel thinks the system should have and provides exactly those—and no more. This works well for most people. You and I are not “most people,” however. We expect odd things from our computers. Perhaps we need to make device nodes available under different names, change device node ownership, or configure our hardware uniquely. FreeBSD breaks the problem of device node management into three pieces: configuring devices present at boot, global availability and permissions, and configuring devices that appear dynamically after boot with devd(8).

/dev at Boot

When device nodes were permanent files on disk, the sysadmin could symlink to those nodes or change their permissions without worrying that his changes would vanish. With an automated, dynamic device filesystem, this assurance disappears. (Of course, you no longer have to worry about occult mknod(8) commands either, so you’re better off in the long run.) The device node changes could include, for example:

  • Making device nodes available under different names
  • Changing ownership of device nodes
  • Concealing device nodes from users

At boot time, devfs(8) creates device nodes in accordance with the rules in /etc/devfs.conf.

devfs.conf

The /etc/devfs.conf file lets you create links, change ownership, and set permissions for devices available at boot. Each rule has the following format:

action      realdevice      desiredvalue

The valid actions are link (create a link), perm (set permissions), and own (set owner). The realdevice entry is a preexisting device node, while the last setting is your desired value. For example, here we create a new name for a device node:

link      cd0           cdrom

We want a symbolic link to the device node /dev/cd0 (an optical drive), and we want this link to be named /dev/cdrom . If we reboot with this entry in /etc/devfs.conf, our optical drive /dev/cd0 also appears as /dev/cdrom, as many desktop multimedia programs expect.

To change the permissions of a device node, give the desired permissions in octal form as the desired value:

perm        cd0            666

Here, we set the permissions on /dev/cd0 (our CD device, again) so that any system user can read or write to the device. Remember, changing the permissions on the /dev/cdrom link won’t change the permissions on the device node, just the symlink.

Finally, we can also change the ownership of a device. Changing a device node’s owner usually indicates that you’re solving a problem the wrong way and that you may need to stop and think. FreeBSD happily lets you mess up your system if you insist, however. Here, we let a particular user have absolute control of the disk device /dev/da20:

own         da20            xistence:xistence

This might not have the desired effect, however, as some programs still think that you must be root to carry out operations on devices. I’ve seen more than one piece of software shut itself down if it’s not run by root, without even trying to access its device nodes. Changing the device node permissions won’t stop those programs’ complaints when they’re run by a regular user.

Configuration with devfs.conf(5) solves many problems, but not all. If you want a device node to simply be invisible and inaccessible, you must use devfs rules.

Global devfs Rules

Every devfs(5) instance behaves according to the rules defined in devfs.rules. The devfs rules apply to both devices present at boot and devices that appear and disappear dynamically. Rules allow you to set ownership and permissions on device nodes and make device nodes visible or invisible. You cannot create symlinks to device nodes with devfs rules.

Similar to /etc/rc.conf and /etc/defaults/rc.conf, FreeBSD uses /etc/devfs.rules and /etc/defaults/devfs.rules. Create an /etc/defvs.rules for your custom rules and leave the entries in the defaults file alone.

devfs Ruleset Format

Each set of devfs rules starts with a name and a ruleset number between square brackets. For example, here’s a devfs rule from the default configuration:

[devfsrules_hide_all=1]
add hide

The first rule in devfs.rules is called devfs_hide_all and is ruleset number 1 . This ruleset contains only one rule .

Once you have a set of devfs rules you like, enable them at boot in /etc/rc.conf. Here, we activate the devfs ruleset named laptoprules:

devfs_system_rulesets="laptoprules"

Remember, devfs rules apply to the devices in the system at boot and the devices configured dynamically after startup.

Ruleset Content

All devfs rules (in files) begin with the word add, to add a rule to the ruleset. You then have either a path keyword and a regex of device names, or a type keyword and a device type. At the end of the rule, you have an action, or a command to perform. Here’s an example of a devfs rule:

add path da* user mwlucas

This rule assigns the user mwlucas ownership of all device nodes with a node name beginning with da. This is probably a bad idea.

Devices specified by path use standard shell regular expressions. If you want to match a variety of devices, use an asterisk as a wildcard. For example, path ada1s1 matches exactly the device /dev/ada1s1, but path ada*s* matches every device node with a name beginning with ada, a character, the letter s, and possibly more characters. You could tell exactly what devices are matched by a wildcard by using it at the command line.

# ls /dev/ada*s*

This lists all MBR slices and partitions on your SATA hard drives, but not the devices for the entire drive.

The type keyword indicates that you want the rule to apply to all devices of a given type. Valid keywords are disk (disk devices), mem (memory devices), tape (tape devices), and tty (terminal devices, including pseudoterminals). The type keyword is rarely used exactly because it’s so sweeping.

If you include neither a path nor a type, devfs applies the action at the end of the rule to all device nodes. In almost all cases, this is undesirable.

The ruleset action can be any one of group, user, mode, hide, and unhide. The group action lets you set the group owner of the device, given as an additional argument. Similarly, the user action assigns the device owner. Here, we set the ownership of da disks to the username desktop and the group usb:

add path da* user desktop
add path da* group usb

The mode action lets you assign permissions to the device in standard octal form.

add path da* mode 664

The hide keyword lets you make device nodes disappear, and unhide makes them reappear. Since no program can use a device node if the device is invisible, this is of limited utility except when the system uses jail(8). Hiding and unhiding makes the most sense when including rules in rules.

Including Rules in Rules

As in so many parts of systems administration, making devfs rules modular so they can be reused is a good way to reduce problems. The default jail rules show exactly how FreeBSD’s devfs supports reuse, through the include keyword.

Here’s the start of the default configuration:

[devfsrules_hide_all=1]
add hide

[devfsrules_unhide_basic=2]
add path log unhide
add path null unhide
add path zero unhide
add path crypto unhide
   --snip--

Rule number one, devfsrules_hide_all , conceals all device nodes .

Rule number two, devfsrules_unhide_basic , contains only a series of unhide statements. This rule does nothing but unhide critical Unix device nodes, like /dev/log , /dev/null , /dev/zero , /dev/crypto , and so on. Most processes won’t run without these devices. These device nodes are already exposed in a standard system, so why would you need a rule just to unhide them? Similarly, ruleset number three, devfsrules_unhide_login, does nothing but unhide device nodes for logged-in users.

The last ruleset leverages all of these.

[devfsrules_jail=4]
add include $devfsrules_hide_all
add include $devfsrules_unhide_basic
add include $devfsrules_unhide_login
add path zfs unhide

This ruleset, devfsrules_jail, uses include statements to pull in the previous rulesets by reference. The last statement also unhides /dev/zfs, allowing ZFS tools to work within jails.

If you want to make additional device nodes available within all of your jails, you could add that device node to the jails ruleset. Or you could define a new ruleset and use it for all your jails. Better still, you could define a ruleset for just the jails that absolutely need that device and assign that ruleset to those jails.

To finish up, let’s look at dynamic devices.

Dynamic Device Management with devd(8)

Hot-swappable hardware is now routine. FreeBSD’s devfs dynamically creates new device nodes when this hardware is plugged in and erases the nodes when the hardware is removed, making using these dynamic devices much simpler. The devd(8) daemon takes this a step further by letting you run userland programs when hardware appears and disappears.

FreeBSD’s default configuration, /etc/devd.conf, handles most modern hardware just fine. If you need to customize devd(8), put your configuration files under /usr/local/etc/devd/ to simplify upgrades. You could also add different rules files for different types of devices if you find your devd(8) configuration becoming very complicated.

devd Configuration

You’ll find four types of devd(8) rules: attach, detach, nomatch, and notify.

The attach rules are triggered when matching hardware is attached to the system. When you plug in a network card, an attach rule configures the card with an IP address and brings up the network.

The detach rules are triggered when matching hardware is removed from the system. detach rules are uncommon, as the kernel automatically marks resources unavailable when the underlying hardware disappears, but you might find uses for them.

The nomatch rules are triggered when new hardware is installed but not attached to a device driver. These devices don’t have device drivers in the current kernel.

devd(8) applies notify rules when the kernel sends a matching event notice to userland. For example, the console message that a network interface has come up is a notify event. Notifications generally appear on the console or in /var/log/messages.

Rules also have priority, with 0 being the lowest. Only the highest matching rule is processed, while lower-priority matching rules are skipped. Here’s a sample devd(8) rule:

notify 0 {
          match "system"          "IFNET";
          match "subsystem"       "!usbus[0-9]";
          match "type"            "ATTACH";
          action "/etc/pccard_ether $subsystem start";
};

This is a notify rule , which means it activates when the kernel sends a message to userland. As a priority 0 rule , this rule can be triggered only if no rule of higher priority matches the criteria we specify. This rule is triggered only if the notification is on the network system IFNET (network) and only if the subsystem doesn’t match the expression usbus[0-9]. It excludes USB network cards. The notification type is ATTACH —in other words, this matches only when someone plugs in a network interface. If all three of these matches hit, devd(8) runs a command to configure the network interface .

Read the devd(8) man page to see about all the options you can put in rules. If you want to automatically mount a particular USB flash disk on a certain mount point, you can do that by checking the serial number of every USB device you put in. If you want to configure Intel network cards differently than Atheros network cards, you can do that by checking the vendor. Whatever you need to write a rule for, it’s probably in there somewhere.

Miscellaneous Filesystems

FreeBSD supports several lesser-known filesystems. Most of them are useful only in bizarre circumstances, but bizarre circumstances arise daily in system administration.

The process filesystem, procfs(5), contains lots of information about processes. It’s considered a security risk and is officially deprecated on modern FreeBSD releases. You can learn a lot about processes from a mounted process filesystem, however. A few older applications still require a process filesystem mounted on /proc; if a server application requires procfs, try to find a similar application that does the job without requiring it.

If you’re using Linux mode (see Chapter 17), you might need the Linux process filesystem linprocfs(5). Much Linux software requires a process filesystem, and FreeBSD suggests installing linprocfs at /compat/linux/proc when you install Linux mode. I’d recommend installing linprocfs only if a piece of software complains it’s not there.

The file descriptor filesystem fdesc(5) offers a filesystem view of file descriptors for each process. Some software, notably Java and the popular Bash shell, requires fdescfs(5). It’s less of a security risk than procfs, but still undesirable. You’ll get instructions on mounting fdescfs(5) when you install a package that requires it.

Now that we’ve talked about local filesystems, let’s look at the network.

The Network File System

A network filesystem allows accessing files on another machine over the network. The two most commonly used network filesystems are the original Network File System (NFS) implemented in Unix and the CIFS (aka SMB) filesystem popularized by Microsoft Windows. We’ll touch on both of these, but start with the old Unix standard of NFS.

Sharing directories and partitions between Unix-like systems is perhaps the simplest Network File System you’ll find. FreeBSD supports the Unix standard Network File System out of the box. Configuring NFS intimidates many junior sysadmins, but after setting up a file share or two, you’ll find it not so terribly difficult.

NFS wasn’t designed as a secure protocol. Do not put NFS servers on the internet without a packet filter or firewall. Merely restricting access at the NFS level is completely inadequate—you must prevent random hosts from poking at the host’s remote procedure call (RPC) services. Restrict access to the host by IP address as well as port number.

Additionally, standard NFS isn’t encrypted. Anyone with a packet sniffer and access to your wire can see all filesystem activity. Once you deploy Kerberos, you can encrypt NFS, but Kerberos requires its own book.

Each NFS connection uses a client-server model. One computer is the server; it offers filesystems to other computers. This is called NFS exporting, and the filesystems offered are called exports. The clients can mount server exports in a manner almost identical to that used to mount local filesystems.

One interesting thing about NFS is its statelessness. NFS doesn’t keep track of the condition of a connection. You can reboot an NFS server and the client won’t crash. It won’t be able to access files on the server’s export while the server is down, but once it returns, you’ll pick up right where things left off. Other network file sharing systems aren’t always so resilient. Of course, statelessness also causes problems; for example, clients can’t know when a file they currently have open is modified by another client.

Both NFS servers and clients require kernel options, but the various NFS commands dynamically load the appropriate kernel modules. FreeBSD’s GENERIC kernel supports NFS, so this isn’t a concern for anyone who doesn’t customize their kernel.

NFS is one of those topics that has entire books written about it. We’re not going to go into the intimate details about NFS, but rather focus on getting basic NFS operations working. If you’re deploying complicated NFS setups, you’ll want to do further research. Even this basic setup lets you accomplish many complicated tasks.

NFS Versions

Modern NFS comes in three versions: NFSv2, NFSv3, and NFSv4. FreeBSD can transparently autodetect and interoperate with versions 2 and 3.

NFSv2 is rather minimal, dating from the time when people were delighted to get file sharing working at all.

NFSv3 contains many incremental improvements over and much better performance than NFSv2. Most of these improvements don’t even require special configuration.

NFSv4 is an entirely different and highly complex protocol that breaks many of the long-standing rules of NFS. It was deliberately designed to resemble Microsoft’s file sharing. Understanding NFSv4 requires understanding filesystem extended ACLs, synchronizing user IDs across the network, and other headaches.

When people say “NFS” they almost always mean NFSv2 or NFSv3. Some folks call these protocols “traditional NFS.” Someone who means NFSv4 usually says “NFSv4.”

This book sticks with the commonly deployed NFSv2 and NFSv3. I devote a couple chapters to NFSv4 and related topics in FreeBSD Mastery: Specialty Filesystems (Tilted Windmill Press, 2016).

Configuring the NFS Server

Turn on NFS server support with the following rc.conf options. While not all of these options are strictly necessary for all environments, turning them all on provides the broadest range of NFS compatibility and decent out-of-the-box performance.

nfs_server_enable="YES"
rpcbind_enable="YES"
mountd_enable="YES"
rpc_lockd_enable="YES"
rpc_statd_enable="YES"

First, tell FreeBSD to load the nfsserver.ko kernel module. Everything will fail if the kernel doesn’t support NFS. The rpcbind(8) daemon maps remote procedure calls (RPCs) into local network addresses. Each NFS client asks the server’s rpcbind(8) daemon where it can find a mountd(8) daemon to connect to. The mountd(8) daemon listens to high-numbered ports for mount requests from clients. Enabling the NFS server also starts nfsd(8), which handles the actual file request. NFS ensures smooth file locking with rpc.lockd(8) , and rpc.statd(8) monitors NFS clients so that the NFS server can free up resources when the host disappears.

While you can start all of these services at the command line, if you’re just learning NFS, it’s best to reboot your system after enabling the NFS server. Once NFS is running, the output of sockstat(1) will show rpc.lockd, rpc.statd, nfsd, mountd, and rpcbind listening. If you don’t see all of these daemons listening to the network, check /var/log/messages for errors.

The NFS server is designed to seamlessly interoperate a whole bunch of different NFS implementations. While it should transparently autonegotiate connections, you might find that you need to tweak your NFS server nfsd(8) to best fit your clients. Tune nfsd(8) at startup with the rc.conf option nfs_server_flags.

NFS can run over TCP or UDP. UDP is the traditional NFS transport protocol. TCP works better over lossy networks and can better cope with irregular network speeds. FreeBSD offers both protocols but defaults to using TCP mounts. Some clients behave better with one protocol or the other. You can explicitly enable only TCP with -t and only UDP with -u.

The NFS server defaults to listening to all IP addresses on a machine. When a server has multiple IP addresses, replies to a UDP request can come from any of those addresses. This can confuse NFS clients. If your NFS server has multiple IP addresses and you have clients that prefer UDP, tell the NFS server to use only a single address with -h and the server IP.

While nfsd(8) works well, highly loaded servers might need additional nfsd(8) processes. While FreeBSD starts four nfsd(8) processes by default, you can start additional processes with the -n flag and the desired number of processes.

This rc.conf entry tells NFS to use only UDP, bind to the IP address 198.51.100.71, and run six instances of nfsd(8).

nfs_server_flags="-uh 198.51.100.71 -n 6"

Before you start tweaking server behavior, though, you really should have some exports.

Configuring NFS Exports

Now tell your server what it can share, or export. You could export all directories and filesystems on the entire server, but any competent security administrator would have a (justified) fit. As with all server configurations, permit as little access as possible while still letting the server fulfill its role. For example, in most environments, clients have no need to remotely mount the NFS server’s root filesystem.

FreeBSD lets you configure exports through two different paths. The traditional method is the file /etc/exports. A ZFS-based server can configure exports through each dataset’s sharenfs property. The server will create the ZFS exports file /etc/zfs/exports based on these properties. Both exports files have the same format.

Choose one method of managing your NFS exports. Either edit /etc/exports, or use zfs(8). Using both methods simultaneously might merely confuse you but will probably break everything. If you use the ZFS method, never edit /etc/zfs/exports by hand. Stick with one method.

No matter which method you choose, though, /etc/exports must exist. If you manage NFS through zfs(8), I recommend creating a one-line /etc/exports that contains only a comment telling people to use zfs(8).

Exports Entries

So how do you configure an export? I’ll start with the exports file /etc/exports, but most everything also applies to using ZFS. I’ll discuss the differences in “Managing NFS with zfs(8)” on page 308, but understanding those limitations requires understanding /etc/exports.

Each exports entry has up to three parts:

  • Directories or partitions to be exported (mandatory)
  • Options on that export
  • Clients that can connect

Each combination of clients and a disk device can only have one line in the exports file. This means that if /usr/ports and /usr/home are on the same partition and you want to export both of them to a particular client, they must both appear in the same line. You can’t export /usr/ports and /usr/home to one client with different permissions. You don’t have to export the entire disk device, mind you; you can export a single directory within a partition. This directory cannot contain either symlinks or double or single dots.

NFS mounts don’t cross partitions. If a host has separate UFS partitions for /usr and /usr/src, exporting /usr doesn’t automatically export /usr/src.

Of the three parts of the /etc/exports entry, only the directory is mandatory. An exports line cannot contain symlinks or periods. To export my home directory to every host on the internet, I could use an /etc/exports line consisting entirely of:

/home/mwlucas

This has no options and no host restrictions. Such an export would be foolish, of course, but I could do it.4

After editing the exports file, tell mountd(8) to reread it:

# service mountd reload

Any problems with mountd(8) appear in /var/log/messages. The log messages are generally enigmatic: while mountd(8) informs you that a line is bad, it usually doesn’t say why. The most common errors I experience involve symlinks. Use pwd(1) in a directory to get a directory’s actual path.

NFS and Users

NFSv2 and NFSv3 identify users by UID. (NFSv4 uses usernames because it assumes you’ve synchronized usernames across the entire network.) For example, on my laptop, the user mwlucas has the UID of 1001. On the NFS server, mwlucas also has the UID 1001. This makes my life easy, as I don’t have to worry too much about file ownership; I have the same privileges on the server as on my laptop.

This can be a problem on a large network, where users have root on their own machines. The best way around this is to create a central repository of authorized users via Kerberos. On a small network or on a network with a limited number of NFS users, this usually isn’t a problem; you can synchronize /etc/master.passwd on your systems or just assign the same UID to each user on each system.

The root user is handled slightly differently, however. An NFS server doesn’t trust root on other machines to execute commands as root on the server. After all, if an intruder breaks into an NFS client, you don’t want the server to automatically go down with it. NFS defaults to mapping requests from a client’s root account to the UID and GID of –2 on the server. This is where the highly unprivileged nobody account originated.

The authors of many other server programs thought the nobody account was a great idea, so they appropriated nobody for their own use. Multiple security entities simultaneously running as nobody creates security issues. FreeBSD’s packages create unprivileged users for all applications that need one. I consider the nobody user tainted and suggest you don’t permit its use.

You can map requests from root to any other username. For example, you might say that all requests from root on a client will run as the nfsroot user on the server. With careful use of groups, you could allow this nfsroot user to have limited file access. Use the maproot option to map root to another user. Here, we map UID 0 (root) on the client to UID 5000 on the server:

/usr/home/mwlucas -maproot=5000

If you really want root on the client to have root privileges on the server, use -maproot to map root to UID 0. This might be suitable on your home network or on a test system.

You can’t arbitrarily remap user accounts to each other. In complex environments, be sure you synchronize user accounts and UIDs on all machines on your network.

NFS users can belong to no more than 16 groups. Some operating systems can break that limit, but they violate the NFS protocols in doing so. If a user can’t access files with group-based access control, check the number of groups that they’re in.

Remember to restart mountd(8) after editing the exports file.

Exporting Multiple Directories

A standard FreeBSD UFS install puts all the files on one partition. You might want to export multiple directories on that partition. List all directories on the same partition on the same line in /etc/exports, right after the first exported directory, separated by spaces. Here’s a sample /etc/exports with multiple exports:

/usr/home/mwlucas /usr/src /var/log /usr/ports/distfiles -maproot=nfsroot

Clients can mount any of these directories, and requests from root get mapped to nfsroot.

There are no identifiers, separators, or delimiters between the parts of the line. Yes, it would be easier to read if we could put each shared directory on its own line, but we can’t—they’re all on the same partition. The FreeBSD team could rewrite this so that it had more structure, but then FreeBSD’s /etc/exports would be incompatible with that from any other Unix.

Perhaps you want clients to be able to mount any directory on a partition. Allow this with the -alldirs option. I wouldn’t do this on a host with a single partition.

/home -alldirs

You can only specify a partition mount point with -alldirs.

Long Lines

As with many other configuration files, you can use a backslash to break a single line of configuration into multiple lines. You might find the preceding configuration more readable as:

/usr/home/mwlucas
    /usr/src
    /usr/obj
    /usr/ports/distfiles
    -maproot = 5000

Once your exports line gets long enough, this style suddenly gets more readable than the alternative.

Restricting Clients

To allow only particular clients to access an NFS export, list them at the end of the /etc/exports entry. Here, we restrict our preceding share to one IP address:

/usr/home/mwlucas /usr/src /usr/obj /usr/ports/distfiles
    -maproot=5000 203.0.113.200

You can also restrict file shares to clients on a particular network by using the -network and -mask qualifiers:

/usr/home/mwlucas /usr/src /usr/obj /usr/ports/distfiles
    -maproot=5000 -network 203.0.113 -mask 255.255.255.0

This lets any client with an IP address beginning in 203.0.113 access your NFS server. I use a setup much like this to upgrade clients quickly. I build a new world and kernel on the NFS server and then let the clients mount those partitions and install the binaries over NFS.

To export to an IPv6 network, include the slash in the address.

/usr/home/mwlucas -network 2001:db8:bad:c0de::/64

You can also list hostnames rather than IP addresses, but this creates a dependency on name resolution. If you lose DNS, you’d lose file sharing. Also, the NFS server looks up the IP address of each host when you start mountd. Changing a client’s IP means reloading both DNS and mountd(8). If you must list hostnames, put them at the end of the line.

/usr/home/mwlucas www1 www2 www3

Assigning NFS on a per-host basis is more labor. Assign NFS permissions as broadly as possible without compromising security.

Combinations of Clients and Exports

Each line in /etc/exports specifies exports from one partition to one network, address, or set of hosts. Different hosts require entirely different export statements. You can change the options for each if you wish.

/usr/home/mwlucas /usr/src /usr/obj /usr/ports/distfiles
    -maproot=5000 203.0.113.200
/usr -maproot=0 203.0.113.201

Here, I’ve exported several subdirectories of /usr to the NFS client at 203.0.113.200. The NFS client at 203.0.113.201 gets to mount the whole of /usr and may even do so as root.

NFS and Firewalls

NFS is famous for not liking firewalls. The dynamic port assignment of services like mountd(8), rpc.lockd(8), and rpc.statd(8) makes packet filtering nearly impossible. You can use the -p flag to assign each of these services a specific TCP port. Here, I use rc.conf entries to nail mountd(8) to port 4046, rpc.lockd(8) to 4045, and rpc.statd(8) to 4047:

mountd_flags="-r -p 4046"
rpc_lockd_flags="-p 4045"
rpc_statd_flags="-p 4047"

I can use these ports in my packet filter rules, providing some protection to my NFS server.

Managing NFS with zfs(8)

Using zfs(8) to manage NFS has advantages and disadvantages. You can configure NFS on a per-dataset basis, and you don’t need to manually restart mountd(8) after each change. Command line configuration is easier to automate, and many folks find it easier to type as well.

Use the sharenfs property to enable, disable, and configure NFS exports. Set this property to on to globally share a dataset and all its descendants. This is equivalent to listing the dataset on its own in /etc/exports. Anyone in the world can mount this dataset or any of its children, with no restrictions and no options, unless you have other access control, such as a firewall.

# zfs set sharenfs=on zroot/home

Similarly, set it to off to unshare the dataset.

You probably want some NFS options on an export, though. Set sharenfs to the desired options for the dataset. This example sets a maproot user and restricts clients to my local network. Put the options in quotes.

# zfs set sharenfs="-network 203.0.113.0/24 -maproot=nfsroot" zroot/home

The problem with using ZFS to manage your NFS exports is that all permitted hosts get the same options. That is, if most of your hosts need to mount /home with -maproot=nfsroot but you have one host that needs root to mount that dataset as root, you can’t use ZFS properties. Similarly, you can define only one permitted network with ZFS properties.

Enabling the NFS Client

Configuring the NFS client is much simpler. In /etc/rc.conf, put:

nfs_client_enable="YES"

You can reboot or run service nfsclient start. Either starts NFS client functions.

Show Available Mounts

One obvious question for an NFS client to ask would be, “What can I mount from that server?” The showmount(8) command lists all exports available to a client. Give the -e flag and the name of the NFS server. Here, I ask the storm server what exports it offers:

# showmount -e storm
Exports list on storm:
/usr/home                          203.0.113.0

This client is allowed to mount /usr/home under the rule that permits the network 203.0.113.0.

Running showmount(8) doesn’t offer any server-side options, like -maproot. These details aren’t readily available to clients, although touch(1) lets you easily test for read-only exports.

Mounting Exports

Now you can mount directories or filesystems exported by NFS servers. Instead of using a device name, use the NFS server’s hostname and the directory you want to mount. For example, to mount the /home/mwlucas directory from my storm server onto the /mnt directory, I would run:

# mount storm:/usr/home/mwlucas /mnt

Afterward, test your mount with df(1).

# df -h
Filesystem            Size    Used   Avail Capacity  Mounted on
--snip--
storm:/usr/home       891G    2.7G    888G     0%    /mnt

The NFS-mounted directory shows up as a normal partition, and I can read and write files on it as I please.

NFS Mount Options

FreeBSD uses conservative NFS defaults so that it can interoperate with any other Unix-like operating system. You can use mount options to adjust how FreeBSD mounts NFS exports. Use these options at the command line with -o or add them to an /etc/fstab entry.

If you need to access a UDP-only NFS server, use the mount option udp to use UDP rather than the default TCP.

Programs expect the filesystem not to disappear, but when you’re using NFS, it’s possible that the server will vanish from the network. This makes programs on the client trying to access the NFS filesystem hang forever. By making your NFS mount interruptible, you’ll be able to interrupt processes hung on unavailable NFS mount with CTRL-C. Set interruptibility with intr.

By using a soft mount, FreeBSD will notify programs that the file they were working on is no longer available. What programs do with that information depends on the program, but they’ll no longer hang forever. Enable soft mounts with the soft option.

If you want a read-only mount, use the ro mount option.

Putting everything together, I might mount my home directory as an interruptible soft mount.5

# mount -o soft,intr storm:/usr/home/mwlucas /mnt

I could add this to /etc/fstab as follows:

storm:/usr/home/mwlucas  /mnt  nfs  rw,soft,intr  0  0

While NFS is pretty straightforward for simple uses, you can spend many hours adjusting, tuning, and enhancing it. If you wish to build a complicated NFS environment, don’t rely entirely on this brief introduction but spend time with a good book on the subject.

Now, let’s look at reading Windows shares.

The Common Internet File System

If you’re on a typical office network, the standard network file sharing protocol is Microsoft’s Common Internet File System (CIFS). You might know CIFS as Server Message Block (SMB), “Network Neighborhood,” or “Why can’t I mount that drive?” While originally provided only by Microsoft Windows systems, this protocol has become something of a pseudostandard.

FreeBSD includes the smbutil(8) program to find, mount, and use CIFS shares as a CIFS client. FreeBSD doesn’t include a CIFS server in the base system, but the open source CIFS server Samba (https://www.samba.org/) works well on FreeBSD.

Use FreeBSD’s CIFS support to interoperate with existing Microsoft infrastructure. Don’t deploy CIFS to support Unix-like systems.

Prerequisites

Before you begin working with Microsoft file shares, gather the following information about your Windows network:

  • Workgroup or Windows domain name
  • Valid Windows username and password
  • IP address of the Windows DNS server

Kernel Support

FreeBSD uses several kernel modules to support CIFS. The smbfs.ko module supports basic CIFS operations. The libmchain.ko and libiconv.ko modules provide supporting functions and load automatically when you load smbfs.ko. You can compile these statically in your kernel as:

options    NETSMB
options    LIBMCHAIN
options    LIBICONV
options    SMBFS

You can load these automatically at boot time with a boot/loader.conf entry.

smbfs_load=YES

You can now configure CIFS.

Configuring CIFS

CIFS relies on a configuration file, either $HOME/.nsmbrc or /etc/nsmb.conf. All settings in /etc/nsmb.conf override the settings in user home directories. The configuration file is divided into sections by labels in square brackets. For example, settings that apply to every CIFS connection are in the [default] section. Create your own sections to specify servers, users, and shares, in one of the following formats:

[servername]
[servername:username]
[servername:username:sharename]

Information that applies to an entire server goes into a section named after the server. Information that applies to a specific user is kept in a username section, and information that applies to only a single share is kept in a section that includes the sharename. You can lump the information for all the shares under a plain [servername] entry if you don’t have more specific per-user or per-share information.

Configuration entries use the values from the CIFS system—for example, Bert’s Windows username is bertjw, but his FreeBSD username is xistence, so I use bertjw in nsmb.conf.

nsmb.conf Keywords

Specify a nsmb.conf configuration with keywords and values under the appropriate section. For example, servers have IP addresses and users don’t, so you would use only an IP address assignment in the server section. To use a keyword, assign a value with an equal sign, as in keyword=value. Here are the common keywords; for a full list, see nsmb.conf(5).

workgroup=string

The workgroup keyword specifies the name of the Windows domain or workgroup you want to access. This is commonly a default setting used for all servers.

workgroup=MegaCorp

addr=a.b.c.d

The addr keyword sets the IP address of a CIFS server. This keyword can appear only under a plain [servername] label. You shouldn’t need this if you have working CIFS name resolution, but reality sometimes disagrees.

nbns=a.b.c.d

The nbns keyword sets the IP address of a NetBIOS (WINS) nameserver. You can put this line in the default section or under a particular server. If you have Active Directory (which is based on DNS), you can use DNS hostnames. Adding a WINS server won’t hurt your configuration, however, and helps in testing basic CIFS setup.

password=string

The password keyword sets a clear-text password for a user or a share. If you must store passwords in /etc/nsmb.conf, be absolutely certain that only root can read the file. Storing a password in $HOME/.nsmbrc is a bad idea on a multiuser system.

You can scramble your Windows password with smbutil crypt, generating a string that you can use for this keyword. The scrambled string has double dollar signs ($$) in front of it. While this helps prevent someone accidentally discovering the password, a malicious user can unscramble it easily.

# smbutil crypt superSecretPassword
$$1624a53302a6d

If the server needs access to a CIFS share to do its routine job, don’t use your account. Ask the Windows team for an account for your server so that problems with your account won’t interrupt the server’s functions.

Sample Configuration

Here, I build an nsmb.conf allowing Bert access to his files on the corporate CIFS fileserver.

[default]
nbns=203.0.113.12
workgroup=BigCorp
[FILESERVER:bertjw]
password=$$1624a53302a6d

With this configuration, Bert should be able to access whatever CIFS shares those tyrannical Windows admins permit.

CIFS Name Resolution

Before FreeBSD can mount a CIFS share, it needs to identify the host the share is on. While Microsoft has used DNS for decades now, typical Windows environments often support a whole panoply of legacy protocols. Verify that smbutil(1) can find CIFS servers with smbutil lookup.

# smbutil lookup fileserver1
Got response from 203.0.113.12
IP address of ntserv1: 203.0.113.4

If this works, you have basic CIFS functionality.

Other smbutil(1) Functions

You can view shares on a host at the command line. Start by logging into your host.

# smbutil login //unix@fileserver1
Password:

So, our configuration is correct. Let’s see what resources this server offers with smbutil’s view command.

# smbutil view //unix@fileserver1
Password:
Share        Type       Comment
-------------------------------
IPC$         pipe       Remote IPC
ADMIN$       disk       Remote Admin
C$           disk       Default share
unix         disk
4 shares listed from 4 available

You’ll get a list of every shared resource on the CIFS server. Now, assuming you’re finished, log out of the server.

# smbutil logout //unix@fileserver

Mounting a Share

Now that you’ve finished investigating, mount a share with mount_smbfs(8). The syntax is as follows:

# mount_smbfs //username@servername/share /mount/point

I have a share on this Windows box called MP3 that I want to access from my FreeBSD system. To mount this as /home/mwlucas/smbmount, I would do this:

# mount_smbfs //unix@fileserver1/MP3 /home/mwlucas/smbmount

The mount(8) and df(1) programs show this share attached to your system, and you can access documents on this server just as you could any other filesystem. Use umount(8) to disconnect from the server.

Other mount_smbfs Options

mount_smbfs includes several options to tweak the behavior of mounted CIFS filesystems. Use the -f option to choose a different file permission mode and the -d option to choose a different directory permission mode. For example, to set a mount so that only I could access the contents of the directory, I would use mount_smbfs -d 700. This would make the FreeBSD permissions far more stringent than the Windows privileges, but that’s perfectly all right with me. I can change the owner of the files with the -u option and the group with the -g option.

Microsoft filesystems are case insensitive, but Unix-like operating systems are case sensitive. CIFS defaults to leaving the case as it finds it, but that may not be desirable. The -c flag makes mount_smbfs(8) change the case on the filesystem: -c l changes everything to lowercase and -c u changes everything to uppercase.

nsmb.conf Options

Here are samples of nsmb.conf entries for different situations. They all assume they’re part of a configuration where you’ve already defined a workgroup, NetBIOS nameserver, and a username with privileges to access the CIFS shares.

Unique Password on a Standalone System

You’d use something like the following if you have a machine named desktop with a password-protected share. Many standalone Windows systems have this sort of password-protection feature.

[desktop:shareusername]
password=$$1789324874ea87

Accessing a Second Domain

In this example, we’re accessing a second domain named development. This domain has a username and password different from those at our default domain.

[development]
workgroup=development
username=support

CIFS File Ownership

Ownership of files between Unix-like and Windows systems can be problematic. For one thing, your FreeBSD usernames probably won’t map to Windows usernames, and Unix has a very different permissions scheme compared to Windows.

Since you’re using a single Windows username to access the share, you have whatever access that account has to the Windows resources, but you must assign the proper FreeBSD permissions for that mounted share. By default, mount_smbfs(8) assigns the new share the same permissions as the mount point. In our earlier example, the directory /home/mwlucas/smbmount is owned by the user mwlucas and has permissions of 755. These permissions say that mwlucas can edit what’s in this directory but nobody else can. Even though FreeBSD says that this user can edit those files, Windows still might not let that particular user edit the files it’s sharing out.

Serving CIFS Shares

Just as FreeBSD can access CIFS shares, it can also serve them to CIFS clients with Samba. You can find several recent versions of Samba in the packages collection. The Samba website at http://www.samba.org/ contains many useful tutorials. Serving CIFS shares from FreeBSD is much more complicated than accessing them, so we’ll end our discussion here before this book grows even thicker.

We’ve now finished our tour of FreeBSD filesystems. While I’ve spent a few chapters on the topic, FreeBSD has several additional filesystems options, an automounter, and even Filesystem in Userspace (FUSE) support for accessing NTFS, Linux’s extfs, and more. It has special iSCSI support and special filesystems like nullfs(5) that make managing jails at scale very powerful. If I spend any more time on filesystems, though, you’ll track me down and use a blunt instrument to express your displeasure, so let’s proceed to some of FreeBSD’s advanced security features.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset