aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@zytor.com>1997-10-06 21:05:49 +0000
committerH. Peter Anvin <hpa@zytor.com>1997-10-06 21:05:49 +0000
commitbddd43e289c8b8b62d7cb3f1f7eb27ca67cae28e (patch)
treeaac5e4a4b924cb13a54cc0760e99f349ea36bd12
downloadautofs3-bddd43e289c8b8b62d7cb3f1f7eb27ca67cae28e.tar.gz
autofs3-bddd43e289c8b8b62d7cb3f1f7eb27ca67cae28e.tar.xz
autofs3-bddd43e289c8b8b62d7cb3f1f7eb27ca67cae28e.zip
Initial revision
-rw-r--r--.version1
-rw-r--r--COPYING340
-rw-r--r--COPYRIGHT15
-rw-r--r--Makefile69
-rw-r--r--Makefile.rules66
-rw-r--r--NEWS176
-rw-r--r--README24
-rw-r--r--TODO14
-rw-r--r--daemon/Makefile28
-rw-r--r--daemon/automount.c746
-rw-r--r--daemon/module.c209
-rw-r--r--daemon/mount.c56
-rw-r--r--daemon/spawn.c156
-rw-r--r--include/automount.h111
-rw-r--r--include/linux/auto_fs.h62
-rw-r--r--kernel-2.0.30.diff1816
-rw-r--r--man/Makefile10
-rw-r--r--man/auto.master.567
-rw-r--r--man/autofs.5136
-rw-r--r--man/autofs.863
-rw-r--r--man/automount.891
-rw-r--r--modules/Makefile30
-rw-r--r--modules/lookup_file.c214
-rw-r--r--modules/lookup_program.c197
-rw-r--r--modules/lookup_yp.c106
-rw-r--r--modules/mount_generic.c78
-rw-r--r--modules/mount_nfs.c164
-rw-r--r--modules/mount_smbfs.c180
-rw-r--r--modules/parse_sun.c502
-rw-r--r--samples/auto.master5
-rw-r--r--samples/auto.misc9
-rw-r--r--samples/rc.autofs225
32 files changed, 5966 insertions, 0 deletions
diff --git a/.version b/.version
new file mode 100644
index 0000000..9e29e10
--- /dev/null
+++ b/.version
@@ -0,0 +1 @@
+0.3.15
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..60549be
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/COPYRIGHT b/COPYRIGHT
new file mode 100644
index 0000000..0352d56
--- /dev/null
+++ b/COPYRIGHT
@@ -0,0 +1,15 @@
+For all software in this distribution:
+
+ Copyright 1997 Transmeta Corporation -- All Rights Reserved
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139,
+ USA; either version 2 of the License, or (at your option) any later
+ version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..7da800c
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,69 @@
+SUBDIRS = daemon modules man
+INCDIRS = include samples
+INCFILES = COPYING COPYRIGHT NEWS README TODO Makefile Makefile.rules \
+ .version *.diff
+
+.PHONY: daemon all kernel clean install install_kernel mrproper
+.PHONY: distclean backup
+
+daemon:
+ for i in $(SUBDIRS); do $(MAKE) -C $$i all; done
+
+kernel:
+ if [ -d kernel ]; then $(MAKE) -C kernel all; fi
+
+all: daemon kernel
+
+clean:
+ for i in $(SUBDIRS) kernel; do \
+ if [ -d $$i ]; then $(MAKE) -C $$i clean; fi; done
+
+install:
+ for i in $(SUBDIRS); do $(MAKE) -C $$i install; done
+
+install_kernel:
+ if [ -d kernel ]; then $(MAKE) -C kernel install; fi
+
+mrproper distclean: clean
+ find . -noleaf \( -name '*~' -o -name '#*' -o -name '*.orig' -o -name '*.rej' -o -name '*.old' \) -print0 | xargs -0 rm -f
+
+TODAY := $(shell date +'%Y%m%d')
+
+backup: mrproper
+ cd .. ; tar cf - autofs | gzip -9 > autofs-bu-$(TODAY).tar.gz
+
+RELEASE := $(shell cat .version)
+
+.PHONY: release newrelease
+
+reldir:
+ -rm -rf autofs-$(RELEASE) autofs-$(RELEASE).tar.gz
+ mkdir autofs-$(RELEASE)
+ cp -R $(SUBDIRS) $(INCDIRS) $(INCFILES) autofs-$(RELEASE)
+ $(MAKE) -C autofs-$(RELEASE) distclean
+ find autofs-$(RELEASE) -noleaf -name \*norel -print0 | xargs -0 rm -rf
+
+release: reldir
+ -rm -f .prerel .lastrel
+ cp .version .lastrel
+ tar cvvf - autofs-$(RELEASE) | gzip -9 > autofs-$(RELEASE).tar.gz
+
+since:
+ mv -f NEWS NEWS.old
+ echo 'Since autofs-$(RELEASE):' > NEWS
+ echo 'Since autofs-$(RELEASE):' | sed -e 's/./-/g' >> NEWS
+ echo '' >> NEWS
+ cat NEWS.old >> NEWS
+
+newrelease: clean since
+ echo `cut -d. -f1-2 < .version`.`expr \`cut -d. -f3 < .version\` + 1` \
+ > .version
+
+newminorrelease: clean since
+ echo `cut -d. -f1 < .version`.`expr \`cut -d. -f2 < .version\` + 1`.0 \
+ > .version
+
+newmajorrelease: clean since
+ echo `expr \`cut -d. -f1 < .version\` + 1`.0.0 > .version
+
+-include Makefile.private
diff --git a/Makefile.rules b/Makefile.rules
new file mode 100644
index 0000000..f340719
--- /dev/null
+++ b/Makefile.rules
@@ -0,0 +1,66 @@
+# -*- makefile -*-
+#
+# Makefile rules
+#
+
+# Compilers, linkers and flags
+# The STRIP defined here *must not* remove any dynamic-loading symbols
+
+ifdef DEBUG
+CFLAGS = -O2 -g -DDEBUG
+LDFLAGS = -g
+STRIP = :
+else
+CFLAGS = -O3 -fomit-frame-pointer -Wall
+LDFLAGS = -s
+STRIP = strip --strip-debug
+endif
+
+CC = gcc
+CXX = g++
+CXXFLAGS = $(CFLAGS)
+LD = ld
+SOLDFLAGS = -shared
+
+# Special parameters for glibc
+# If you're compiling for glibc (libc 6), uncomment these
+#YPLIBS = -lnsl
+
+# Directory for autofs modules
+autofslibdir = $(DESTDIR)/usr/lib/autofs
+
+# Directory to install modules
+moddir = $(DESTDIR)/lib/modules/`uname -r`
+fsmoddir = $(moddir)/fs
+
+# Where to install the automount program
+sbindir = $(DESTDIR)/usr/sbin
+
+# Where to install man pages
+mandir = $(DESTDIR)/usr/man
+
+# Kernel compilation rules (this is for "make kernel" to work; not needed
+# if you're compiling the autofs kernel code as a part of the kernel itself.
+# For "make kernel" to work you need the kernel code to be in the "kernel"
+# directory; not included with the autofs distribution.)
+
+# 1. Comment this out if your kernel is *not* SMP
+KFLAGS = -D__SMP__
+# 2. Set this to your kernel include files
+KINCLUDE = /usr/src/linux/include
+# 3. Comment this out if you do *not* use versioned modules
+MODFLAGS = -DMODVERSIONS -include $(KINCLUDE)/linux/modversions.h
+
+# Standard rules
+
+.SUFFIXES: .c .o .s .so
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
+.c.s:
+ $(CC) $(CFLAGS) -S $<
+
+.c.so:
+ $(CC) $(SOLDFLAGS) $(CFLAGS) -o $*.so $<
+ $(STRIP) $*.so
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000..72a44ef
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,176 @@
+Since autofs-0.3.14:
+--------------------
+* "const-ipated" the source at all module interface points, to keep me
+ from doing stupid things like calling strtok() on a string constant.
+* Minor fix to the autofs(5) man page.
+
+Since autofs-0.3.13:
+--------------------
+* Simplified the signalling/forking structure.
+* When debugging, leave debug-related signals in the default state.
+* Fixed broken timeouts.
+* Fixed multiple problems involving module destruction.
+* "Shouldn't happen" signals now only get caught once, to avoid
+ looping.
+* SIGHUP now causes a "prune" operation, followed by module
+ reinitialization.
+
+Since autofs-0.3.12:
+--------------------
+* Added -p, --pid-file option to daemon.
+* Fixed aestetic bug in new spawn() logic.
+* Added new cleanup_exit() function in the daemon.
+* Added signal handling to some cases that were improperly dealt with.
+* Integrated new sample stuff from Miquel van Smoorenburg and
+ Christoph Lameter.
+
+Since autofs-0.3.11:
+--------------------
+* Rewrote the spawn() logic to not use stdio at all.
+
+Since autofs-0.3.10:
+--------------------
+* Added conversion from : -> / in Sun maps, to handle maps which
+ contain "shared mounts". Can be avoided by \-escaping the colon or
+ by passing the option --no-slashify-colons.
+* ${HOST} corrected (was returning the value for ${ARCH}.)
+* Don't free the YP domain name; the documentation says to it but the
+ documentation is wrong.
+
+Since autofs-0.3.9:
+-------------------
+* Added YPLIBS option to Makefile.rules to support compiling for glibc.
+* Distributing include/linux/auto_fs.h with the daemon one again, to
+ avoid version skew problems.
+* Made \-escaping work (hopefully) correctly.
+* Added dlerror() output to error message when dlopen() returns an error.
+
+Since autofs-0.3.8:
+-------------------
+* Missed at least one place where a SIGCHLD gunked up a waitpid() in
+ 0.3.8. Fixed.
+* More documentation and init script fixes from Christoph (with some
+ edits from yours truly, since I'm an opininated bastard who likes
+ things done my way.)
+* Added check to lookup_program.c: no output at all == error.
+
+Since autofs-0.3.7:
+-------------------
+* Removed kernel code from distribution, except for 2.0.30 diff (will
+ remain until 2.0.31 is released.) Kernel code will now only be
+ included with the main kernel distribution, although Makefiles will
+ continue to support building the kernel code.
+* Tried to eliminate race conditions relating to signals received in
+ the vicinity of a fork(), as well as multiple SIGCHLD signals
+ received while SIGCHLD blocked.
+* Better sample rc file and man pages from Christoph Lameter of the
+ Debian project, with edits from hpa.
+* In mount_smbfs.c: smb_parse_options() is destructive; make it
+ operate on a copy of the options string.
+
+Since autofs-0.3.6:
+-------------------
+* Kernel code should now compile on 2.1.43 or higher.
+* Minor cleanup of stdout/stderr redirect.
+* Fixed this file!
+
+Since autofs-0.3.5:
+-------------------
+* stdout/stderr of subprograms (only stderr for program maps, of
+ course) now is automatically redirected to the syslog. As a result,
+ the spawn[lv]() interface has changed somewhat.
+* kernel: minor bug fixes.
+* daemon: minor cleanup.
+* Now compiles (and runs) on SPARC.
+* daemon: Should now handle extremely short timeouts, or no timeouts,
+ correctly.
+
+Since autofs-0.3.4:
+-------------------
+* kernel: *Hopefully* fixed the wait queue list corruption (kernel
+ NULL pointer dereference) bug.
+
+Since autofs-0.3.3:
+-------------------
+* kernel: *Hopefully* fixed that annoying "filesystem busy" bug.
+
+Since autofs-0.3.2:
+-------------------
+* Added "mount_generic" and "mount_smbfs" modules.
+* Support ":" to escape a mount point beginning with "/" in a Sun map.
+* Support -fstype= option in Sun maps.
+
+Since autofs-0.3.1:
+-------------------
+* kernel: effectivized handling of waits interrupted by a signal.
+* kernel: closed some extremely theoretical security holes.
+* kernel: fixed two problems that could lead to kernel oopses.
+
+Since autofs-0.3.0:
+-------------------
+* The kernel code should once again work on kernel 2.0.30 and 2.1.x
+ for x < 36.
+* No need to fork an expiry process if there is nothing to expire!
+* Attempting to mount a nonexistent directory should no longer leave a
+ pad directory around, causing renewed mount attempts on "ls".
+* Timeout is now adjustible (set to 0 to turn off timeouts completely.)
+
+Since autofs-0.2.2:
+-------------------
+* Support expiry of idle mounts; currently the timeout is fixed at 5
+ minutes.
+* Ship kernel source that can be built either as a part of the autofs
+ or the kernel source trees.
+* Ought to compile on the Alpha, although I cannot test it here.
+
+Since autofs-0.2.1:
+-------------------
+* Can now read options and ignore whitespace and comments in Sun maps.
+* Does closing the file descriptors after forking help avoid hung
+ filesystems?
+
+Since autofs-0.2.0:
+-------------------
+* Cleaned up the interface between modules (not perfect yet; I was
+ even quite tempted to rewrite the interface using C++ classes,
+ but...) Do expect the interface to still change, for example:
+ expect a umount method to be added to the mount module. Strings
+ passed are now managed by the calling module, and can be allocated
+ in any desirable way.
+* Moved mount operation to its own module.
+* Handle options passed on the command line; not options passed in the map yet.
+* Handle $-escapes in Sun style maps, including the Sun predefined
+ variables.
+* New "program" map type: the map is an executable which is passed the
+ key on the command line, and writes the map entry to stdout on
+ success and exit 0, or exit 1 on failure.
+
+Since autofs-970417:
+--------------------
+* Use a real versioning scheme :)
+* Fixed bugs that would make file maps unmountable. Still don't quite
+ understand *why*, which bugs me, though...
+* Man page by B. James Phillippe.
+
+Since autofs-970409:
+--------------------
+* Modified the .so interface slightly, in order to make it possible
+ for future versions to have multiple lookup sessions.
+* Added "file" map type.
+
+Since autofs-970402:
+--------------------
+* The parsing module has been factored out into a separate dynamic
+ module. This should make writing other lookup modules much easier.
+* The automount daemon now accepts kill -USR1 as a "unmount all
+ filesystems and exit if everything could be unmounted" and kill
+ -USR2 "unmount all non-busy filesystems". Prelude to handling
+ timeouts, I hope.
+* The autofs code is now integrated in kernels 2.1.31 and above (a
+ patch for the 2.1.31 code is included, however; 2.1.32 is correct);
+ a patch is included for 2.0.30 and has been submitted to Linus for
+ inclusion in 2.0.31.
+* include/linux/auto_fs.h is no longer shipped in the tools
+ distribution; the one from the kernel should be used, so you need
+ the (appropriately patched) kernel sources in /usr/src/linux.
+* added NEWS and README files.
diff --git a/README b/README
new file mode 100644
index 0000000..2bb1d84
--- /dev/null
+++ b/README
@@ -0,0 +1,24 @@
+autofs is a kernel-based automounter for Linux. It is still under
+development, although it seems to be stabilizing relatively quickly.
+
+To build autofs, please inspect Makefile.rules and include/automount.h
+to see if there are any options you need to change, then you can:
+
+ make ... make the daemon and modules
+ make install ... install the daemon and modules
+
+The kernel code is no longer distributed with the autofs daemon; it is
+now only distributed with the kernel code. A diff is included for
+kernel version 2.0.30; autofs will be included directly into 2.0.31
+when that version is released.
+
+autofs was written by H. Peter Anvin of Transmeta Corporation, see the
+COPYRIGHT file.
+
+If you use or want to help develop autofs, please join the autofs
+mailing list:
+
+ autofs@linux.kernel.org
+
+by sending a message containing the line "subscribe autofs" to
+majordomo@linux.kernel.org.
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..e79abe5
--- /dev/null
+++ b/TODO
@@ -0,0 +1,14 @@
+autofs TODO list
+----------------
+* Support multimount entries. (Quite messy: can't unmount the
+ tree as a unit, probably needs locking while constructing the tree,
+ and needs scaffolding if no entry for /. Appears *very* difficult
+ to do without threading!)
+* /net support: need hostname resolver module (easy) as well as mountd
+ query (easy) plus all the multimount entry stuff...
+* Additional lookup modules: [ng]dbm, db/hash, (hesiod?)...
+* Document the intermodule interface.
+* Clean up the kernel protocol; it is a bit too confining at the
+ moment to allow for independent releases of the daemon and the
+ kernel code.
+
diff --git a/daemon/Makefile b/daemon/Makefile
new file mode 100644
index 0000000..142cbe2
--- /dev/null
+++ b/daemon/Makefile
@@ -0,0 +1,28 @@
+#
+# Makefile for autofs
+#
+
+SRCS = automount.c spawn.c module.c mount.c
+OBJS = automount.o spawn.o module.o mount.o
+
+include ../Makefile.rules
+
+version := $(shell cat ../.version)
+
+CFLAGS += -rdynamic -DAUTOFS_LIB_DIR=\"$(autofslibdir)\" -DVERSION_STRING=\"$(version)\" -I../include
+LDFLAGS += -rdynamic
+LIBS = -ldl
+
+all: automount
+
+automount: $(OBJS)
+ $(CC) $(LDFLAGS) -o automount $(OBJS) $(LIBS)
+ $(STRIP) automount
+
+clean:
+ rm -f *.o *.s automount
+
+install: all
+ install -c automount -m 755 -o root $(sbindir)
+
+
diff --git a/daemon/automount.c b/daemon/automount.c
new file mode 100644
index 0000000..524d256
--- /dev/null
+++ b/daemon/automount.c
@@ -0,0 +1,746 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * automount.c - Linux automounter daemon
+ *
+ * Copyright 1997 Transmeta Corporation - All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139,
+ * USA; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * ----------------------------------------------------------------------- */
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <limits.h>
+#include <paths.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <linux/auto_fs.h>
+
+#include "automount.h"
+
+const char *program; /* Initialized with argv[0] */
+const char *version = VERSION_STRING; /* Program version */
+
+static pid_t my_pgrp; /* The "magic" process group */
+static pid_t my_pid; /* The pid of this process */
+static char *pid_file = NULL; /* File in which to keep pid */
+
+int kproto_version; /* Kernel protocol version used */
+
+static int mounted = 0;
+
+struct pending_mount {
+ pid_t pid; /* Which process is mounting for us */
+ unsigned long wait_queue_token; /* Associated kernel wait token */
+ volatile struct pending_mount *next;
+};
+
+static struct autofs_point {
+ char *path; /* Mount point name */
+ int pipefd; /* File descriptor for pipe */
+ int ioctlfd; /* File descriptor for ioctls */
+ dev_t dev; /* "Device" number assigned by kernel */
+ unsigned exp_timeout; /* Timeout for expiring mounts */
+ unsigned exp_runfreq; /* Frequency for polling for timeouts */
+ volatile pid_t exp_process; /* Process that is currently expiring */
+ volatile struct pending_mount *mounts; /* Pending mount queue */
+ struct lookup_mod *lookup; /* Lookup module */
+} ap;
+
+volatile struct pending_mount *junk_mounts = NULL;
+
+#define DEFAULT_TIMEOUT (5*60) /* 5 minutes */
+#define CHECK_RATIO 4 /* exp_runfreq = exp_timeout/CHECK_RATIO */
+
+static int umount_ent(char *root, char *name)
+{
+ char path_buf[PATH_MAX];
+ struct stat st;
+ int rv = 0;
+
+ sprintf(path_buf, "%s/%s", root, name);
+ if ( !lstat(path_buf,&st) ) {
+ if ( S_ISDIR(st.st_mode) ) {
+ if ( st.st_dev != ap.dev ) {
+ rv = spawnl(LOG_NOTICE, _PATH_UMOUNT,
+ _PATH_UMOUNT, path_buf, NULL);
+ if ( !rv )
+ rmdir(path_buf);
+ } else
+ rmdir(path_buf);
+ } else
+ unlink(path_buf);
+ }
+ return rv;
+}
+
+static int umount_all(int force)
+{
+ int left;
+ struct dirent *de;
+ DIR *dp;
+
+ chdir("/");
+
+ if ( ap.exp_process ) {
+ kill(ap.exp_process, SIGTERM);
+ usleep(100000);
+ }
+
+ dp = opendir(ap.path);
+ if ( !dp ) {
+ syslog(LOG_ERR, "umount_all: opendir: %m");
+ return -1;
+ }
+
+ left = 0;
+
+ while ( (de = readdir(dp)) != NULL ) {
+ if ( strcmp(de->d_name,".") && strcmp(de->d_name,"..") ) {
+ if ( umount_ent(ap.path,de->d_name) ) {
+ if ( force )
+ syslog(LOG_WARNING,
+ "could not unmount %s/%s",
+ ap.path,de->d_name);
+ left++;
+ }
+ }
+ }
+ closedir(dp);
+
+ return left;
+}
+
+static int umount_autofs(int force)
+{
+ int rv;
+
+ if ( !mounted )
+ return -1;
+
+ if ( umount_all(force) && !force )
+ return -1;
+
+ if (ap.ioctlfd >= 0) {
+ ioctl(ap.ioctlfd, AUTOFS_IOC_CATATONIC, 0);
+ close(ap.ioctlfd);
+ }
+ if (ap.pipefd >= 0)
+ close(ap.pipefd);
+ rv = spawnl(LOG_ERR, _PATH_UMOUNT, _PATH_UMOUNT, ap.path, NULL);
+ free(ap.path);
+ mounted = 0;
+ return rv;
+}
+
+static int mount_autofs(char *path)
+{
+ int pipefd[2];
+ char options[128];
+ char our_name[128];
+ struct stat st;
+
+ if ( mounted ) {
+ /* This can't happen */
+ syslog(LOG_ERR, "mount_autofs: already mounted");
+ return -1;
+ }
+
+ if ( path[0] != '/' ) {
+ errno = EINVAL; /* Must be an absolute pathname */
+ return -1;
+ }
+
+ ap.path = strdup(path);
+ if ( !ap.path ) {
+ errno = ENOMEM;
+ return -1;
+ }
+ ap.pipefd = ap.ioctlfd = -1;
+
+ /* In case the directory doesn't exist, try to mkdir it */
+ if ( mkdir(path, 0555) < 0 && errno != EEXIST )
+ return -1;
+
+ if ( pipe(pipefd) < 0 )
+ return -1;
+
+ sprintf(options, "fd=%d,pgrp=%u,minproto=2,maxproto=%d", pipefd[1],
+ (unsigned)my_pgrp, AUTOFS_PROTO_VERSION);
+ sprintf(our_name, "automount(pid%u)", (unsigned)my_pid);
+
+ if ( spawnl(LOG_CRIT, _PATH_MOUNT, _PATH_MOUNT, "-t", "autofs", "-o",
+ options, our_name, path, NULL) ) {
+ close(pipefd[0]);
+ close(pipefd[1]);
+ return -1;
+ }
+
+ close(pipefd[1]); /* Close kernel pipe end */
+ ap.pipefd = pipefd[0];
+
+ chdir(path);
+ ap.ioctlfd = open(".", O_RDONLY); /* Root directory for ioctl()'s */
+ chdir("/");
+ if ( ap.ioctlfd < 0 ) {
+ umount_autofs(1);
+ return -1;
+ }
+
+ stat(path,&st);
+ ap.dev = st.st_dev; /* Device number for mount point checks */
+
+ ap.mounts = NULL; /* No pending mounts */
+ mounted = 1;
+
+ return 0;
+}
+
+static void run_expire(void)
+{
+ pid_t f;
+ struct autofs_packet_expire pkt;
+ sigset_t s, olds;
+
+ if ( ap.exp_process ) /* Another process is already expiring */
+ return; /* Try again next interval */
+
+ if ( ioctl(ap.ioctlfd, AUTOFS_IOC_EXPIRE, &pkt) )
+ return; /* Don't fork if there is nothing do expire */
+
+ /* Temporarily block SIGCHLD and SIGALRM between forking and setting
+ ap.exp_process */
+
+ sigemptyset(&s);
+ sigaddset(&s, SIGCHLD);
+ sigaddset(&s, SIGALRM);
+ sigprocmask(SIG_BLOCK, &s, &olds);
+
+ f = fork();
+ if ( f < 0 )
+ return;
+ if ( f > 0 ) {
+ ap.exp_process = f;
+ sigprocmask(SIG_SETMASK, &olds, NULL);
+ return;
+ }
+
+ /* This is the actual expire run, run as a subprocess */
+
+ reset_signals();
+ close(ap.pipefd);
+
+ syslog(LOG_DEBUG, "running expiration on path %s", ap.path);
+
+ do {
+ if ( pkt.hdr.type == autofs_ptype_expire ) {
+ if ( !umount_ent(ap.path,pkt.name) )
+ syslog(LOG_NOTICE, "expired %s/%s", ap.path,
+ pkt.name);
+ }
+ } while ( ioctl(ap.ioctlfd, AUTOFS_IOC_EXPIRE, &pkt) == 0 );
+ _exit(0);
+}
+
+static sig_atomic_t shutdown = 0;
+static sig_atomic_t prune = 0;
+static sig_atomic_t reinit = 0;
+static sig_atomic_t expire_now = 0;
+
+static void sig_shutdown(int sig)
+{
+ shutdown = sig;
+}
+
+static void sig_prune(int sig)
+{
+ prune = 1;
+}
+
+static void sig_expire(int sig)
+{
+ expire_now = 1;
+}
+
+static void sig_reinit(int sig)
+{
+ prune = 1;
+ reinit = 1;
+}
+
+static int get_pkt(int fd, struct autofs_packet_missing *pkt)
+{
+ char *buf = (char *) pkt;
+ int bytes = sizeof(*pkt);
+
+ do {
+ int i = read(fd, buf, bytes);
+ if (!i)
+ break;
+ if (i < 0) {
+ if (errno == EINTR) {
+ if ( prune ) {
+ umount_all(0);
+ prune = 0;
+ }
+ if ( expire_now ) {
+ run_expire();
+ expire_now = 0;
+ alarm(ap.exp_runfreq);
+ }
+ if ( !shutdown && !reinit )
+ continue;
+ }
+ break;
+ }
+ buf += i;
+ bytes -= i;
+ } while (bytes);
+ return bytes;
+}
+
+static int send_ready(unsigned int wait_queue_token)
+{
+ if ( ioctl(ap.ioctlfd, AUTOFS_IOC_READY, wait_queue_token) < 0 ) {
+ syslog(LOG_ERR, "AUTOFS_IOC_READY: %m");
+ return 1;
+ } else
+ return 0;
+}
+
+static int send_fail(unsigned int wait_queue_token)
+{
+ if ( ioctl(ap.ioctlfd, AUTOFS_IOC_FAIL, wait_queue_token) < 0 ) {
+ syslog(LOG_ERR, "AUTOFS_IOC_READY: %m");
+ return 1;
+ } else
+ return 0;
+}
+
+static int handle_packet(void)
+{
+ struct autofs_packet_missing pkt;
+ struct stat st;
+ sigset_t allsignals, oldsig, chldsig;
+ pid_t f;
+
+ if (get_pkt(ap.pipefd, &pkt))
+ return -1;
+
+ sigemptyset(&chldsig);
+ sigaddset(&chldsig, SIGCHLD);
+ sigfillset(&allsignals);
+
+ chdir(ap.path);
+ if ( lstat(pkt.name,&st) || (S_ISDIR(st.st_mode) && st.st_dev == ap.dev) ) {
+ /* Need to mount or symlink */
+ struct pending_mount *mt;
+
+ /* Block SIGCHLD while mucking with linked lists */
+ sigprocmask(SIG_BLOCK, &chldsig, NULL);
+ if ( (mt = (struct pending_mount *) junk_mounts) ) {
+ junk_mounts = junk_mounts->next;
+ } else if ( !(mt = malloc(sizeof(struct pending_mount))) ) {
+ syslog(LOG_ERR, "handle_packet: malloc: %m");
+ send_fail(pkt.wait_queue_token);
+ return 1;
+ }
+ sigprocmask(SIG_UNBLOCK, &chldsig, NULL);
+
+ syslog(LOG_INFO, "attempting to mount entry %s/%s",
+ ap.path, pkt.name);
+
+ sigprocmask(SIG_BLOCK, &allsignals, &oldsig);
+
+ f = fork();
+ if ( f == -1 ) {
+ sigprocmask(SIG_SETMASK, &oldsig, NULL);
+ syslog(LOG_ERR, "handle_packet: fork: %m");
+ send_fail(pkt.wait_queue_token);
+ return 1;
+ } else if ( !f ) {
+ int i;
+
+ reset_signals(); /* Set up a standard signal environment */
+ close(ap.pipefd);
+ close(ap.ioctlfd);
+
+ i = ap.lookup->lookup_mount(ap.path,pkt.name,pkt.len,ap.lookup->context);
+ _exit(i ? 1 : 0);
+ } else {
+ /* Important: set up data structures while signals still blocked */
+ mt->pid = f;
+ mt->wait_queue_token = pkt.wait_queue_token;
+ mt->next = ap.mounts;
+ ap.mounts = mt;
+
+ sigprocmask(SIG_SETMASK, &oldsig, NULL);
+ }
+ } else {
+ /* Already there (can happen if a process connects to a
+ directory while we're still working on it) */
+ chdir("/");
+ send_ready(pkt.wait_queue_token);
+ }
+ return 0;
+}
+
+static void sig_child(int sig)
+{
+ pid_t pid;
+ int status;
+ volatile struct pending_mount *mt, * volatile *mtp;
+
+ while ( (pid = waitpid(-1, &status, WNOHANG)) > 0 ) {
+ if ( pid == ap.exp_process ) {
+ ap.exp_process = 0;
+ } else {
+ for ( mtp = &ap.mounts ; (mt = *mtp) ; mtp = &mt->next ) {
+ if ( mt->pid == pid ) {
+ if ( !WIFEXITED(status) && !WIFSIGNALED(status) )
+ break;
+ else if ( WIFSIGNALED(status) ||
+ WEXITSTATUS(status) != 0 )
+ send_fail(mt->wait_queue_token);
+ else
+ send_ready(mt->wait_queue_token);
+ *mtp = mt->next; /* Remove from linked list */
+ /* We can't use free() from a signal handler, so put this on
+ a "junk list" which we can reclaim later */
+ mt->next = junk_mounts;
+ junk_mounts = mt;
+ break;
+ }
+ }
+ }
+ }
+}
+
+static void become_daemon(void)
+{
+ FILE *pidfp;
+ pid_t pid;
+ int nullfd;
+
+ /* Don't BUSY any directories unneccessarily */
+
+ chdir("/");
+
+ /* Detach from foreground process */
+
+ pid = fork();
+ if ( pid > 0 )
+ exit(0);
+ else if ( pid < 0 ) {
+ fprintf(stderr, "%s: Could not detach process\n", program);
+ exit(1);
+ }
+
+ /* Open syslog */
+
+ openlog("automount", LOG_PID, LOG_DAEMON);
+
+ /* Initialize global data */
+
+ my_pid = getpid();
+
+ /* Make our own process group for "magic" reason */
+
+ if ( setpgrp() ) {
+ syslog(LOG_CRIT, "setpgrp: %m");
+ exit(1);
+ }
+ my_pgrp = getpgrp();
+
+ /* Redirect all our file descriptors to /dev/null */
+
+ if ( (nullfd = open("/dev/null", O_RDWR)) < 0 ) {
+ syslog(LOG_CRIT, "cannot open /dev/null: %m");
+ exit(1);
+ }
+
+ if ( dup2(nullfd, STDIN_FILENO) < 0 ||
+ dup2(nullfd, STDOUT_FILENO) < 0 ||
+ dup2(nullfd, STDERR_FILENO) < 0 ) {
+ syslog(LOG_CRIT, "redirecting file descriptors failed: %m");
+ exit(1);
+ }
+ close(nullfd);
+
+ /* Write pid file if requested */
+
+ if ( pid_file ) {
+ if ( (pidfp = fopen(pid_file, "wt")) ) {
+ fprintf(pidfp, "%lu\n", (unsigned long) my_pid);
+ fclose(pidfp);
+ } else {
+ syslog(LOG_WARNING, "failed to write pid file %s: %m", pid_file);
+ pid_file = NULL;
+ }
+ }
+}
+
+/*
+ * cleanup_exit() is valid to call once we have daemonized
+ */
+
+static void cleanup_exit(int exit_code)
+{
+ if ( ap.lookup )
+ close_lookup(ap.lookup);
+
+ if ( pid_file )
+ unlink(pid_file);
+
+ closelog();
+
+ exit(exit_code);
+}
+
+static unsigned long getnumopt(char *str, char option)
+{
+ unsigned long val;
+ char *end;
+
+ val = strtoul(str, &end, 0);
+ if ( ! *str || *end ) {
+ fprintf(stderr, "%s: option -%c requires a numeric argument, got %s\n",
+ program, option, str);
+ exit(1);
+ }
+ return val;
+}
+
+static void usage(void)
+{
+ fprintf(stderr, "Usage: %s [options] path map_type [args...]\n", program);
+}
+
+int main(int argc, char *argv[])
+{
+ char *path, *map, *mapfmt;
+ const char **mapargv;
+ struct sigaction sa;
+ int mapargc, opt;
+ static const struct option long_options[] = {
+ {"help", 0, 0, 'h'},
+ {"pid-file", 1, 0, 'p'},
+ {"timeout", 1, 0, 't'},
+ {"version", 0, 0, 'v'},
+ {0,0,0,0}
+ };
+
+ program = argv[0];
+
+ memset(&ap, 0, sizeof ap); /* Initialize ap so we can test for null */
+ ap.exp_timeout = DEFAULT_TIMEOUT;
+
+ opterr = 0;
+ while ( (opt = getopt_long(argc, argv, "+hp:t:v", long_options,
+ NULL)) != EOF ) {
+ switch( opt ) {
+ case 'h':
+ usage();
+ exit(0);
+ case 'p':
+ pid_file = optarg;
+ break;
+ case 't':
+ ap.exp_timeout = getnumopt(optarg, opt);
+ break;
+ case 'v':
+ printf("Linux automount version %s\n", version);
+ exit(0);
+ case '?':
+ case ':':
+ printf("%s: Ambiguous or unknown options\n", program);
+ exit(1);
+ }
+ }
+
+ if ( geteuid() != 0 ) {
+ fprintf(stderr, "%s: This program must be run by root.\n", program);
+ exit(1);
+ }
+
+ /* Remove the options */
+ argv += optind;
+ argc -= optind;
+
+ if ( argc < 2 ) {
+ usage();
+ exit(1);
+ }
+
+ become_daemon();
+
+ path = argv[0];
+ map = argv[1];
+ mapargv = (const char **) &argv[2];
+ mapargc = argc-2;
+
+ syslog(LOG_INFO, "starting automounter version %s, path = %s, "
+ "maptype = %s, mapname = %s", version, path, map,
+ (mapargc < 1) ? "none" : mapargv[0]);
+
+ if ( mapargc ) {
+ int i;
+ syslog(LOG_DEBUG, "Map argc = %d", mapargc);
+ for ( i = 0 ; i < mapargc ; i++ )
+ syslog(LOG_DEBUG, "Map argv[%d] = %s", i, mapargv[i]);
+ }
+
+ if ( (mapfmt = strchr(map,',')) )
+ *(mapfmt++) = '\0';
+
+ if ( !(ap.lookup = open_lookup(map, "", mapfmt, mapargc, mapargv)) )
+ cleanup_exit(1);
+
+ if (mount_autofs(path) < 0) {
+ syslog(LOG_CRIT, "%s: mount failed!", path);
+ cleanup_exit(1);
+ }
+
+ /* The following signals cause a shutdown event to occur */
+ sa.sa_handler = sig_shutdown;
+ sigemptyset(&sa.sa_mask);
+ sigaddset(&sa.sa_mask, SIGCHLD);
+ sigaddset(&sa.sa_mask, SIGALRM);
+ sa.sa_flags = 0;
+ sigaction(SIGIO, &sa, NULL);
+ sigaction(SIGTERM, &sa, NULL);
+ /* USR2 is special, but is handled by the same handler */
+ sigaction(SIGUSR2, &sa, NULL);
+
+ /* The following signals cause a shutdown event to occur, but if we get
+ more than one, permit the signal to proceed so we don't loop. This
+ is basically the complete list of "this shouldn't happen" signals. */
+ sa.sa_flags = SA_ONESHOT;
+ sigaction(SIGXCPU, &sa, NULL);
+ sigaction(SIGXFSZ, &sa, NULL);
+ sigaction(SIGINT, &sa, NULL);
+#ifdef SIGPWR
+ sigaction(SIGPWR, &sa, NULL);
+#endif
+#ifndef DEBUG
+ /* When debugging, these signals should be in the default state; when
+ in production, we want to at least attempt to catch them and shut down. */
+ sigaction(SIGILL, &sa, NULL);
+ sigaction(SIGQUIT, &sa, NULL);
+ sigaction(SIGABRT, &sa, NULL);
+ sigaction(SIGTRAP, &sa, NULL);
+ sigaction(SIGFPE, &sa, NULL);
+ sigaction(SIGSEGV, &sa, NULL);
+ sigaction(SIGBUS, &sa, NULL);
+ sigaction(SIGPROF, &sa, NULL);
+ sigaction(SIGPIPE, &sa, NULL);
+#ifdef SIGSYS
+ sigaction(SIGSYS, &sa, NULL);
+#endif
+#ifdef SIGSTKFLT
+ sigaction(SIGSTKFLT, &sa, NULL);
+#endif
+#ifdef SIGLOST
+ sigaction(SIGLOST, &sa, NULL);
+#endif
+#ifdef SIGEMT
+ sigaction(SIGEMT, &sa, NULL);
+#endif
+#endif /* DEBUG */
+
+ /* The SIGCHLD handler is the bottom handler of the mount request */
+ sa.sa_handler = sig_child;
+ sigemptyset(&sa.sa_mask);
+ sigaddset(&sa.sa_mask, SIGALRM);
+ sa.sa_flags = SA_NOCLDSTOP; /* Don't need info about stopped children */
+ sigaction(SIGCHLD, &sa, NULL);
+
+ /* The SIGALRM handler controls expiration of entries. */
+ sa.sa_handler = sig_expire;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+ sigaction(SIGALRM, &sa, NULL);
+
+ /* SIGUSR1 causes a prune event */
+ sa.sa_handler = sig_prune;
+ sigaction(SIGUSR1, &sa, NULL);
+
+ /* SIGHUP causes complete reinitialization */
+ sa.sa_handler = sig_reinit;
+ sigaction(SIGHUP, &sa, NULL);
+
+ /* The following signals shouldn't occur, and are ignored */
+ sa.sa_handler = SIG_IGN;
+ sigaction(SIGVTALRM, &sa, NULL);
+ sigaction(SIGURG, &sa, NULL);
+ sigaction(SIGWINCH, &sa, NULL);
+#ifdef SIGUNUSED
+ sigaction(SIGUNUSED, &sa, NULL);
+#endif
+
+ /* If this ioctl() doesn't work, it is kernel version 2 */
+ if ( ioctl(ap.ioctlfd, AUTOFS_IOC_PROTOVER, &kproto_version) ) {
+ syslog(LOG_DEBUG, "kproto: %m");
+ kproto_version = 2;
+ }
+
+ syslog(LOG_INFO, "using kernel protocol version %d", kproto_version);
+
+ if ( kproto_version < 3 ) {
+ ap.exp_timeout = ap.exp_runfreq = 0;
+ syslog(LOG_INFO, "kernel does not support timeouts");
+ } else {
+ unsigned long timeout;
+
+ ap.exp_runfreq = (ap.exp_timeout+CHECK_RATIO-1) / CHECK_RATIO;
+
+ timeout = ap.exp_timeout;
+ ioctl(ap.ioctlfd, AUTOFS_IOC_SETTIMEOUT, &timeout);
+
+ /* We often start several automounters at the same time. Add some
+ randomness so we don't all expire at the same time. */
+ if ( ap.exp_timeout )
+ alarm(ap.exp_timeout + my_pid % ap.exp_runfreq);
+ }
+
+ while ( !shutdown ) {
+ if (handle_packet() && errno != EINTR)
+ break;
+ if (shutdown == SIGUSR2 && umount_autofs(0))
+ shutdown = 0;
+ if (reinit) {
+ reinit = 0;
+ close_lookup(ap.lookup);
+ if ( !(ap.lookup = open_lookup(map, "", mapfmt, mapargc, mapargv)) ) {
+ syslog(LOG_CRIT, "reinitialization failed, shutting down");
+ umount_autofs(1);
+ cleanup_exit(1);
+ }
+ }
+ }
+
+ syslog(LOG_INFO, "shutting down, path = %s", path);
+
+ umount_autofs(1);
+ cleanup_exit(0);
+}
diff --git a/daemon/module.c b/daemon/module.c
new file mode 100644
index 0000000..a6c2263
--- /dev/null
+++ b/daemon/module.c
@@ -0,0 +1,209 @@
+/*
+ * module.c
+ *
+ * Common module-management functions
+ */
+
+#include <stdio.h>
+#include <syslog.h>
+#include <dlfcn.h>
+#include <string.h>
+#include <stdlib.h>
+#include "automount.h"
+
+struct lookup_mod *open_lookup(const char *name, const char *err_prefix,
+ const char *mapfmt,
+ int argc, const char * const *argv)
+{
+ struct lookup_mod *mod;
+ char *fnbuf;
+ void *dh;
+ int *ver;
+
+ mod = malloc(sizeof(struct lookup_mod));
+ if ( !mod ) {
+ if ( err_prefix ) syslog(LOG_CRIT, "%s%m", err_prefix);
+ return NULL;
+ }
+ fnbuf = alloca(strlen(name) + strlen(AUTOFS_LIB_DIR) + 13);
+ if ( !fnbuf ) {
+ free(mod);
+ if ( err_prefix ) syslog(LOG_CRIT, "%s%m", err_prefix);
+ return NULL;
+ }
+ sprintf(fnbuf, "%s//lookup_%s.so", AUTOFS_LIB_DIR, name);
+
+ if ( !(dh = dlopen(fnbuf, RTLD_NOW)) ) {
+ if ( err_prefix )
+ syslog(LOG_CRIT, "%scannot open lookup module %s (%s)",
+ err_prefix, name, dlerror());
+ free(mod);
+ return NULL;
+ }
+
+ if ( !(ver = (int *) dlsym(dh, "lookup_version"))
+ || *ver != AUTOFS_LOOKUP_VERSION ) {
+ if ( err_prefix )
+ syslog(LOG_CRIT, "%slookup module %s version mismatch", err_prefix, name);
+ dlclose(dh);
+ free(mod);
+ return NULL;
+ }
+
+ if ( !(mod->lookup_init = (lookup_init_t) dlsym(dh, "lookup_init")) ||
+ !(mod->lookup_mount = (lookup_mount_t) dlsym(dh, "lookup_mount")) ||
+ !(mod->lookup_done = (lookup_done_t) dlsym(dh, "lookup_done")) ) {
+ if ( err_prefix )
+ syslog(LOG_CRIT, "%slookup module %s corrupt", err_prefix, name);
+ dlclose(dh);
+ free(mod);
+ return NULL;
+ }
+
+ if ( mod->lookup_init(mapfmt,argc,argv,&mod->context) ) {
+ dlclose(dh);
+ free(mod);
+ return NULL;
+ }
+ mod->dlhandle = dh;
+ return mod;
+}
+
+int close_lookup(struct lookup_mod *mod)
+{
+ int rv = mod->lookup_done(mod->context);
+ dlclose(mod->dlhandle);
+ free(mod);
+ return rv;
+}
+
+struct parse_mod *open_parse(const char *name, const char *err_prefix,
+ int argc, const char * const *argv)
+{
+ struct parse_mod *mod;
+ char *fnbuf;
+ void *dh;
+ int *ver;
+
+ mod = malloc(sizeof(struct parse_mod));
+ if ( !mod ) {
+ if ( err_prefix ) syslog(LOG_CRIT, "%s%m", err_prefix);
+ return NULL;
+ }
+ fnbuf = alloca(strlen(name) + strlen(AUTOFS_LIB_DIR) + 12);
+ if ( !fnbuf ) {
+ free(mod);
+ if ( err_prefix ) syslog(LOG_CRIT, "%s%m", err_prefix);
+ return NULL;
+ }
+ sprintf(fnbuf, "%s//parse_%s.so", AUTOFS_LIB_DIR, name);
+
+ if ( !(dh = dlopen(fnbuf, RTLD_NOW)) ) {
+ if ( err_prefix )
+ syslog(LOG_CRIT, "%scannot open parse module %s (%s)",
+ err_prefix, name, dlerror());
+ free(mod);
+ return NULL;
+ }
+
+ if ( !(ver = (int *) dlsym(dh, "parse_version"))
+ || *ver != AUTOFS_PARSE_VERSION ) {
+ if ( err_prefix )
+ syslog(LOG_CRIT, "%sparse module %s version mismatch", err_prefix, name);
+ dlclose(dh);
+ free(mod);
+ return NULL;
+ }
+
+ if ( !(mod->parse_init = (parse_init_t) dlsym(dh, "parse_init")) ||
+ !(mod->parse_mount = (parse_mount_t) dlsym(dh, "parse_mount")) ||
+ !(mod->parse_done = (parse_done_t) dlsym(dh, "parse_done")) ) {
+ if ( err_prefix )
+ syslog(LOG_CRIT, "%sparse module %s corrupt", err_prefix, name);
+ dlclose(dh);
+ free(mod);
+ return NULL;
+ }
+
+ if ( mod->parse_init(argc,argv,&mod->context) ) {
+ dlclose(dh);
+ free(mod);
+ return NULL;
+ }
+ mod->dlhandle = dh;
+ return mod;
+}
+
+int close_parse(struct parse_mod *mod)
+{
+ int rv = mod->parse_done(mod->context);
+ dlclose(mod->dlhandle);
+ free(mod);
+ return rv;
+}
+
+struct mount_mod *open_mount(const char *name, const char *err_prefix)
+{
+ struct mount_mod *mod;
+ char *fnbuf;
+ void *dh;
+ int *ver;
+
+ mod = malloc(sizeof(struct mount_mod));
+ if ( !mod ) {
+ if ( err_prefix ) syslog(LOG_CRIT, "%s%m", err_prefix);
+ return NULL;
+ }
+ fnbuf = alloca(strlen(name) + strlen(AUTOFS_LIB_DIR) + 12);
+ if ( !fnbuf ) {
+ free(mod);
+ if ( err_prefix )
+ syslog(LOG_CRIT, "%s%m", err_prefix);
+ return NULL;
+ }
+ sprintf(fnbuf, "%s//mount_%s.so", AUTOFS_LIB_DIR, name);
+
+ if ( !(dh = dlopen(fnbuf, RTLD_NOW)) ) {
+ if ( err_prefix )
+ syslog(LOG_CRIT, "%scannot open mount module %s (%s)",
+ err_prefix, name, dlerror());
+ free(mod);
+ return NULL;
+ }
+
+ if ( !(ver = (int *) dlsym(dh, "mount_version"))
+ || *ver != AUTOFS_MOUNT_VERSION ) {
+ if ( err_prefix )
+ syslog(LOG_CRIT, "%smount module %s version mismatch", err_prefix, name);
+ dlclose(dh);
+ free(mod);
+ return NULL;
+ }
+
+ if ( !(mod->mount_init = (mount_init_t) dlsym(dh, "mount_init")) ||
+ !(mod->mount_mount = (mount_mount_t) dlsym(dh, "mount_mount")) ||
+ !(mod->mount_done = (mount_done_t) dlsym(dh, "mount_done")) ) {
+ if ( err_prefix )
+ syslog(LOG_CRIT, "%smount module %s corrupt", err_prefix, name);
+ dlclose(dh);
+ free(mod);
+ return NULL;
+ }
+
+ if ( mod->mount_init(&mod->context) ) {
+ dlclose(dh);
+ free(mod);
+ return NULL;
+ }
+ mod->dlhandle = dh;
+ return mod;
+}
+
+int close_mount(struct mount_mod *mod)
+{
+ int rv = mod->mount_done(mod->context);
+ dlclose(mod->dlhandle);
+ free(mod);
+ return rv;
+}
+
diff --git a/daemon/mount.c b/daemon/mount.c
new file mode 100644
index 0000000..41cb2f8
--- /dev/null
+++ b/daemon/mount.c
@@ -0,0 +1,56 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * mount.c - Abstract mount code used by modules for an unexpected
+ * filesystem type
+ *
+ * Copyright 1997 Transmeta Corporation - All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139,
+ * USA; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * ----------------------------------------------------------------------- */
+
+#include <syslog.h>
+#include <stdlib.h>
+#include "automount.h"
+
+/* These filesystems are known not to work with the "generic" module */
+static char *not_generic[] = { "nfs", "smbfs", "ncpfs", "userfs",
+ "autofs", NULL };
+
+int do_mount(const char *root, const char *name, int name_len,
+ const char *what, const char *fstype, const char *options)
+{
+ struct mount_mod *mod;
+ const char *modstr;
+ char **ngp;
+ int rv;
+
+ mod = open_mount(modstr = fstype, NULL);
+ if ( !mod ) {
+ for ( ngp = not_generic ; *ngp ; ngp++ ) {
+ if ( !strcmp(fstype, *ngp) ) break;
+ }
+ if ( ! *ngp )
+ mod = open_mount(modstr = "generic", NULL);
+ if ( !mod ) {
+ syslog(LOG_ERR, "cannot find mount method for filesystem %s", fstype);
+ return -1;
+ }
+ }
+ syslog(LOG_DEBUG, "do_mount %s %s/%s type %s options %s using module %s",
+ what, root, name, fstype, options, modstr);
+
+ rv = mod->mount_mount(root,name,name_len,what,fstype,options,mod->context);
+ close_mount(mod);
+
+ return rv;
+}
diff --git a/daemon/spawn.c b/daemon/spawn.c
new file mode 100644
index 0000000..c8a7c23
--- /dev/null
+++ b/daemon/spawn.c
@@ -0,0 +1,156 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * spawn.c - run programs synchronously with output redirected to syslog
+ *
+ * Copyright 1997 Transmeta Corporation - All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139,
+ * USA; either version 2 of the License, or (at your option) any later
+ * version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <sys/wait.h>
+
+#include "automount.h"
+
+/* Used by subprocesses to avoid carrying over the main daemon's rather
+ weird signalling environment */
+void reset_signals(void)
+{
+ struct sigaction sa;
+ sigset_t allsignals;
+ int i;
+
+ sigfillset(&allsignals);
+ sigprocmask(SIG_BLOCK, &allsignals, NULL);
+
+ sa.sa_handler = SIG_DFL;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_flags = 0;
+ for ( i = 1 ; i < NSIG ; i++ )
+ sigaction(i, &sa, NULL);
+
+ sigprocmask(SIG_UNBLOCK, &allsignals, NULL);
+}
+
+#define ERRBUFSIZ 2047 /* Max length of error string excl \0 */
+
+int spawnv(int logpri, const char *prog, const char * const *argv) {
+ pid_t f;
+ int status, pipefd[2];
+ char errbuf[ERRBUFSIZ+1], *p, *sp;
+ int errp, errn;
+ sigset_t allsignals, tmpsig, oldsig;
+
+ sigfillset(&allsignals);
+ sigprocmask(SIG_BLOCK, &allsignals, &oldsig);
+
+ if ( pipe(pipefd) )
+ return -1;
+
+ f = fork();
+ if ( f < 0 ) {
+ sigprocmask(SIG_SETMASK, &oldsig, NULL);
+ return -1;
+ } else if ( f == 0 ) {
+ reset_signals();
+ close(pipefd[0]);
+ dup2(pipefd[1], STDOUT_FILENO);
+ dup2(pipefd[1], STDERR_FILENO);
+ close(pipefd[1]);
+ execv(prog, (char * const *)argv);
+ _exit(255); /* execv() failed */
+ } else {
+ /* Careful here -- if we enable SIGCHLD yet we may not receive the
+ waitpid() at the end */
+
+ tmpsig = oldsig;
+
+ sigaddset(&tmpsig, SIGCHLD);
+ sigprocmask(SIG_SETMASK, &tmpsig, NULL);
+
+ close(pipefd[1]);
+
+ if ( f < 0 ) {
+ close(pipefd[0]);
+ return -1;
+ }
+
+ errp = 0;
+ do {
+ while ( (errn = read(pipefd[0], errbuf+errp, ERRBUFSIZ-errp)) == -1
+ && errno == EINTR );
+
+ if ( errn > 0 ) {
+ errp += errn;
+
+ sp = errbuf;
+ while ( errp && (p = memchr(sp, '\n', errp)) ) {
+ *p++ = '\0';
+ if ( sp[0] ) /* Don't output empty lines */
+ syslog(logpri, ">> %s", sp);
+ errp -= (p-sp);
+ sp = p;
+ }
+
+ if ( errp && sp != errbuf )
+ memmove(errbuf, sp, errp);
+
+ if ( errp >= ERRBUFSIZ ) {
+ /* Line too long, split */
+ errbuf[errp] = '\0';
+ syslog(logpri, ">> %s", errbuf);
+ errp = 0;
+ }
+ }
+ } while ( errn > 0 );
+ close(pipefd[0]);
+
+ if ( errp > 0 ) {
+ /* End of file without \n */
+ errbuf[errp] = '\0';
+ syslog(logpri, ">> %s", errbuf);
+ }
+
+ if ( waitpid(f, &status, 0) != f )
+ status = -1; /* waitpid() failed */
+
+ sigprocmask(SIG_SETMASK, &oldsig, NULL);
+
+ return status;
+ }
+}
+
+int spawnl(int logpri, const char *prog, ...)
+{
+ va_list arg;
+ int argc;
+ char **argv, **p;
+
+ va_start(arg,prog);
+ for ( argc = 1 ; va_arg(arg,char *) ; argc++ );
+ va_end(arg);
+
+ if ( !(argv = alloca(sizeof(char *) * argc)) )
+ return -1;
+
+ va_start(arg,prog);
+ p = argv;
+ while ((*p++ = va_arg(arg,char *)));
+ va_end(arg);
+
+ return spawnv(logpri, prog, (const char **)argv);
+}
+
diff --git a/include/automount.h b/include/automount.h
new file mode 100644
index 0000000..c7b6baf
--- /dev/null
+++ b/include/automount.h
@@ -0,0 +1,111 @@
+/*
+ * automount.h
+ *
+ * Header file for automounter modules
+ *
+ */
+
+#ifndef AUTOMOUNT_H
+#define AUTOMOUNT_H
+
+#include <paths.h>
+
+#ifndef _PATH_MOUNT
+#define _PATH_MOUNT "/bin/mount"
+#endif
+#ifndef _PATH_UMOUNT
+#define _PATH_UMOUNT "/bin/umount"
+#endif
+
+/* If smbmount is unavailable, remote the mount_smbfs module from
+ modules/Makefile */
+
+#ifndef _PATH_SMBMOUNT
+#define _PATH_SMBMOUNT "/usr/bin/smbmount"
+#endif
+
+/* Standard function used by daemon or modules */
+
+int spawnl(int logpri, const char *prog, ...);
+int spawnv(int logpri, const char *prog, const char * const *argv);
+void reset_signals(void);
+int do_mount(const char *root, const char *name, int name_len,
+ const char *what, const char *fstype, const char *options);
+
+/* Prototype for module functions */
+
+/* lookup module */
+
+#define AUTOFS_LOOKUP_VERSION 4
+
+#ifdef MODULE_LOOKUP
+int lookup_init(const char *mapfmt, int argc, const char * const *argv,
+ void **context);
+int lookup_mount(const char *, const char *, int, void *);
+int lookup_done(void *);
+#endif
+typedef (*lookup_init_t)(const char *, int, const char * const *, void **);
+typedef (*lookup_mount_t)(const char *, const char *, int, void *);
+typedef (*lookup_done_t)(void *);
+struct lookup_mod {
+ lookup_init_t lookup_init;
+ lookup_mount_t lookup_mount;
+ lookup_done_t lookup_done;
+ void *dlhandle;
+ void *context;
+};
+struct lookup_mod *open_lookup(const char *name, const char *err_prefix,
+ const char *mapfmt,
+ int argc, const char * const *argv);
+int close_lookup(struct lookup_mod *);
+
+/* parse module */
+
+#define AUTOFS_PARSE_VERSION 3
+
+#ifdef MODULE_PARSE
+int parse_init(int argc, const char * const *argv, void **context);
+int parse_mount(const char *root, const char *name,
+ int name_len, const char *mapent, void *context);
+int parse_done(void *);
+#endif
+typedef (*parse_init_t)(int, const char * const *,void **);
+typedef (*parse_mount_t)(const char *,const char *,int, const char *,void *);
+typedef (*parse_done_t)(void *);
+struct parse_mod {
+ parse_init_t parse_init;
+ parse_mount_t parse_mount;
+ parse_done_t parse_done;
+ void *dlhandle;
+ void *context;
+};
+struct parse_mod *open_parse(const char *name, const char *err_prefix,
+ int argc, const char * const *argv);
+int close_parse(struct parse_mod *);
+
+/* mount module */
+
+#define AUTOFS_MOUNT_VERSION 4
+
+#ifdef MODULE_MOUNT
+int mount_init(void **context);
+int mount_mount(const char *root, const char *name, int name_len,
+ const char *what, const char *fstype, const char *options,
+ void *context);
+int mount_done(void *context);
+#endif
+typedef (*mount_init_t)(void **);
+typedef (*mount_mount_t)(const char *, const char *, int, const char *, const char *, const char *, void *);
+typedef (*mount_done_t)(void *);
+struct mount_mod {
+ mount_init_t mount_init;
+ mount_mount_t mount_mount;
+ mount_done_t mount_done;
+ void *dlhandle;
+ void *context;
+};
+struct mount_mod *open_mount(const char *name, const char *err_prefix);
+int close_mount(struct mount_mod *);
+
+
+#endif /* AUTOMOUNT_H */
diff --git a/include/linux/auto_fs.h b/include/linux/auto_fs.h
new file mode 100644
index 0000000..2b25d29
--- /dev/null
+++ b/include/linux/auto_fs.h
@@ -0,0 +1,62 @@
+/* -*- linux-c -*- ------------------------------------------------------- *
+ *
+ * linux/include/linux/auto_fs.h
+ *
+ * Copyright 1997 Transmeta Corporation - All Rights Reserved
+ *
+ * This file is part of the Linux kernel and is made available under
+ * the terms of the GNU General Public License, version 2, or at your
+ * option, any later version, incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+
+#ifndef _LINUX_AUTO_FS_H
+#define _LINUX_AUTO_FS_H
+
+#include <linux/version.h>
+#include <linux/fs.h>
+#include <linux/limits.h>
+#include <linux/ioctl.h>
+#include <asm/types.h>
+
+#define AUTOFS_PROTO_VERSION 3
+
+enum autofs_packet_type {
+ autofs_ptype_missing, /* Missing entry (mount request) */
+ autofs_ptype_expire, /* Expire entry (umount request) */
+};
+
+struct autofs_packet_hdr {
+ int proto_version; /* Protocol version */
+ enum autofs_packet_type type; /* Type of packet */
+};
+
+struct autofs_packet_missing {
+ struct autofs_packet_hdr hdr;
+ unsigned long wait_queue_token;
+ int len;
+ char name[NAME_MAX+1];
+};
+
+struct autofs_packet_expire {
+ struct autofs_packet_hdr hdr;
+ int len;
+ char name[NAME_MAX+1];
+};
+
+#define AUTOFS_IOC_READY _IO(0x93,0x60)
+#define AUTOFS_IOC_FAIL _IO(0x93,0x61)
+#define AUTOFS_IOC_CATATONIC _IO(0x93,0x62)
+#define AUTOFS_IOC_PROTOVER _IOR(0x93,0x63,int)
+#define AUTOFS_IOC_SETTIMEOUT _IOWR(0x93,0x64,unsigned long)
+#define AUTOFS_IOC_EXPIRE _IOR(0x93,0x65,struct autofs_packet_expire)
+
+#ifdef __KERNEL__
+
+/* Init function */
+int init_autofs_fs(void);
+
+#endif /* __KERNEL__ */
+
+#endif /* _LINUX_AUTO_FS_H */
diff --git a/kernel-2.0.30.diff b/kernel-2.0.30.diff
new file mode 100644
index 0000000..4cbb04f
--- /dev/null
+++ b/kernel-2.0.30.diff
@@ -0,0 +1,1816 @@
+diff -urN 2.0.30/linux/Documentation/Configure.help linux/Documentation/Configure.help
+--- 2.0.30/linux/Documentation/Configure.help Tue Apr 8 08:47:45 1997
++++ linux/Documentation/Configure.help Thu May 1 12:35:18 1997
+@@ -3112,6 +3112,17 @@
+ Documentation/modules.txt. If you haven't heard about all of this
+ before, it's safe to say N.
+
++Kernel automounter support (experimental)
++CONFIG_AUTOFS_FS
++ The automounter is a tool to automatically mount remote filesystems
++ on demand. This implementation is partially kernel-based to reduce
++ overhead in the already-mounted case; this is unlike the BSD
++ automounter (amd), which is only in user space. To use the
++ automounter you also need the user-space tools from
++ ftp.kernel.org:/pub/linux/daemons/autofs. If you are not a part of
++ a fairly large, distributed network, you probably do not need an
++ automounter, and can say N here.
++
+ BSD UFS filesystem support (read only)
+ CONFIG_UFS_FS
+ BSD and derivate versions of Unix (such as SunOS, FreeBSD, NetBSD
+diff -urN 2.0.30/linux/MAINTAINERS linux/MAINTAINERS
+--- 2.0.30/linux/MAINTAINERS Tue Apr 8 08:47:45 1997
++++ linux/MAINTAINERS Thu May 1 12:35:18 1997
+@@ -363,6 +363,18 @@
+ M: jam@acm.org
+ S: Maintained
+
++KERNEL AUTOMOUNTER (AUTOFS)
++P: H. Peter Anvin
++M: hpa@zytor.com
++L: autofs@linux.kernel.org
++S: Maintained
++
++DEVICE NUMBER REGISTRY
++P: H. Peter Anvin
++M: hpa@zytor.com
++L: linux-kernel@vger.rutgers.edu
++S: Maintained
++
+ REST:
+ P: Linus Torvalds
+ S: Buried alive in email
+diff -urN 2.0.30/linux/arch/alpha/defconfig linux/arch/alpha/defconfig
+--- 2.0.30/linux/arch/alpha/defconfig Tue Oct 29 17:42:40 1996
++++ linux/arch/alpha/defconfig Thu May 1 12:35:18 1997
+@@ -196,6 +196,7 @@
+ # CONFIG_HPFS_FS is not set
+ # CONFIG_SYSV_FS is not set
+ # CONFIG_UFS_FS is not set
++# CONFIG_AUTOFS_FS is not set
+
+ #
+ # Character devices
+diff -urN 2.0.30/linux/arch/i386/defconfig linux/arch/i386/defconfig
+--- 2.0.30/linux/arch/i386/defconfig Tue Oct 29 17:42:40 1996
++++ linux/arch/i386/defconfig Thu May 1 12:35:18 1997
+@@ -146,6 +146,7 @@
+ # CONFIG_HPFS_FS is not set
+ # CONFIG_SYSV_FS is not set
+ # CONFIG_UFS_FS is not set
++# CONFIG_AUTOFS_FS is not set
+
+ #
+ # Character devices
+diff -urN 2.0.30/linux/arch/m68k/defconfig linux/arch/m68k/defconfig
+--- 2.0.30/linux/arch/m68k/defconfig Mon May 6 02:44:30 1996
++++ linux/arch/m68k/defconfig Thu May 1 12:35:18 1997
+@@ -122,6 +122,7 @@
+ # CONFIG_ISO9660_FS is not set
+ # CONFIG_HPFS_FS is not set
+ # CONFIG_SYSV_FS is not set
++# CONFIG_AUTOFS_FS is not set
+ # CONFIG_AFFS_FS is not set
+ # CONFIG_UFS_FS is not set
+
+diff -urN 2.0.30/linux/arch/mips/defconfig linux/arch/mips/defconfig
+--- 2.0.30/linux/arch/mips/defconfig Wed Feb 7 19:41:50 1996
++++ linux/arch/mips/defconfig Thu May 1 12:35:18 1997
+@@ -61,6 +61,7 @@
+ # CONFIG_ISO9660_FS is not set
+ # CONFIG_HPFS_FS is not set
+ # CONFIG_SYSV_FS is not set
++# CONFIG_AUTOFS_FS is not set
+ # CONFIG_SMB_FS is not set
+
+ #
+diff -urN 2.0.30/linux/arch/sparc/defconfig linux/arch/sparc/defconfig
+--- 2.0.30/linux/arch/sparc/defconfig Thu Apr 25 03:22:05 1996
++++ linux/arch/sparc/defconfig Thu May 1 12:35:18 1997
+@@ -114,6 +114,7 @@
+ # CONFIG_HPFS_FS is not set
+ # CONFIG_SYSV_FS is not set
+ # CONFIG_AFFS_FS is not set
++# CONFIG_AUTOFS_FS is not set
+ CONFIG_UFS_FS=y
+
+ #
+diff -urN 2.0.30/linux/drivers/sound/Config.in linux/drivers/sound/Config.in
+--- 2.0.30/linux/drivers/sound/Config.in Wed Jul 10 04:34:03 1996
++++ linux/drivers/sound/Config.in Fri May 2 11:33:47 1997
+@@ -2,10 +2,10 @@
+ # Sound driver configuration
+ #
+ #--------
+-# There is another confic script which is compatible with rest of
++# There is another config script which is compatible with rest of
+ # the kernel. It can be activated by running 'make mkscript' in this
+ # directory. Please note that this is an _experimental_ feature which
+-# doesn't work with all cards (PSS, SM Wave, AudioTriX Pro, Maui).
++# doesn't work with all cards (PSS, SM Wave, AudioTrix Pro, Maui).
+ #--------
+ #
+ $MAKE -C drivers/sound config || exit 1
+diff -urN 2.0.30/linux/fs/Config.in linux/fs/Config.in
+--- 2.0.30/linux/fs/Config.in Sun Dec 1 05:58:05 1996
++++ linux/fs/Config.in Thu May 1 12:35:18 1997
+@@ -39,6 +39,9 @@
+ tristate 'OS/2 HPFS filesystem support (read only)' CONFIG_HPFS_FS
+ tristate 'System V and Coherent filesystem support' CONFIG_SYSV_FS
+ if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
++ tristate 'Kernel automounter support (experimental)' CONFIG_AUTOFS_FS
++fi
++if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+ tristate 'Amiga FFS filesystem support (EXPERIMENTAL)' CONFIG_AFFS_FS
+ if [ "$CONFIG_AFFS_FS" != "n" ]; then
+ define_bool CONFIG_AMIGA_PARTITION y
+diff -urN 2.0.30/linux/fs/Makefile linux/fs/Makefile
+--- 2.0.30/linux/fs/Makefile Wed May 8 08:28:01 1996
++++ linux/fs/Makefile Thu May 1 12:35:18 1997
+@@ -17,7 +17,7 @@
+
+ MOD_LIST_NAME := FS_MODULES
+ ALL_SUB_DIRS = minix ext ext2 fat msdos vfat proc isofs nfs xiafs umsdos \
+- hpfs sysv smbfs ncpfs ufs affs
++ hpfs sysv smbfs ncpfs ufs affs autofs
+
+ ifeq ($(CONFIG_QUOTA),y)
+ O_OBJS += dquot.o
+@@ -154,6 +154,14 @@
+ else
+ ifeq ($(CONFIG_AFFS_FS),m)
+ MOD_SUB_DIRS += affs
++ endif
++endif
++
++ifeq ($(CONFIG_AUTOFS_FS),y)
++SUB_DIRS += autofs
++else
++ ifeq ($(CONFIG_AUTOFS_FS),m)
++ MOD_SUB_DIRS += autofs
+ endif
+ endif
+
+diff -urN 2.0.30/linux/fs/autofs/Makefile linux/fs/autofs/Makefile
+--- 2.0.30/linux/fs/autofs/Makefile Wed Dec 31 16:00:00 1969
++++ linux/fs/autofs/Makefile Wed May 28 21:38:35 1997
+@@ -0,0 +1,35 @@
++#
++# Makefile for the linux autofs-filesystem routines.
++#
++# We can build this either out of the kernel tree or the autofs tools tree.
++#
++
++O_TARGET := autofs.o
++O_OBJS := dir.o dirhash.o init.o inode.o root.o symlink.o waitq.o
++
++M_OBJS := $(O_TARGET)
++
++ifdef TOPDIR
++#
++# Part of the kernel code
++#
++include $(TOPDIR)/Rules.make
++else
++#
++# Standalone (handy for development)
++#
++include ../Makefile.rules
++
++CFLAGS += -D__KERNEL__ -DMODULE $(KFLAGS) -I../include -I$(KINCLUDE) $(MODFLAGS)
++
++all: $(O_TARGET)
++
++$(O_TARGET): $(O_OBJS)
++ $(LD) -r -o $(O_TARGET) $(O_OBJS)
++
++install: $(O_TARGET)
++ install -c $(O_TARGET) /lib/modules/`uname -r`/fs
++
++clean:
++ rm -f *.o *.s
++endif
+diff -urN 2.0.30/linux/fs/autofs/autofs_i.h linux/fs/autofs/autofs_i.h
+--- 2.0.30/linux/fs/autofs/autofs_i.h Wed Dec 31 16:00:00 1969
++++ linux/fs/autofs/autofs_i.h Wed May 28 21:38:35 1997
+@@ -0,0 +1,187 @@
++/* -*- linux-c -*- ------------------------------------------------------- *
++ *
++ * linux/fs/autofs/autofs_i.h
++ *
++ * Copyright 1997 Transmeta Corporation - All Rights Reserved
++ *
++ * This file is part of the Linux kernel and is made available under
++ * the terms of the GNU General Public License, version 2, or at your
++ * option, any later version, incorporated herein by reference.
++ *
++ * ----------------------------------------------------------------------- */
++
++/* Internal header file for autofs */
++
++#include <linux/auto_fs.h>
++
++/* This is the range of ioctl() numbers we claim as ours */
++#define AUTOFS_IOC_FIRST AUTOFS_IOC_READY
++#define AUTOFS_IOC_COUNT 32
++
++#include <linux/kernel.h>
++#include <linux/malloc.h>
++#include <linux/sched.h>
++#include <linux/string.h>
++#include <linux/wait.h>
++
++#define kver(a,b,c) (((a) << 16) + ((b) << 8) + (c))
++
++#if LINUX_VERSION_CODE < kver(2,1,0)
++
++/* Segmentation stuff for pre-2.1 kernels */
++#include <asm/segment.h>
++
++static inline int copy_to_user(void *dst, void *src, unsigned long len)
++{
++ int rv = verify_area(VERIFY_WRITE, dst, len);
++ if ( rv )
++ return -1;
++ memcpy_tofs(dst,src,len);
++ return 0;
++}
++
++static inline int copy_from_user(void *dst, void *src, unsigned long len)
++{
++ int rv = verify_area(VERIFY_READ, src, len);
++ if ( rv )
++ return -1;
++ memcpy_fromfs(dst,src,len);
++ return 0;
++}
++
++#else
++
++/* Segmentation stuff for post-2.1 kernels */
++#include <asm/uaccess.h>
++#define register_symtab(x) ((void)0)
++
++#endif
++
++#ifdef DEBUG
++#define DPRINTK(D) printk D;
++#else
++#define DPRINTK(D)
++#endif
++
++#define AUTOFS_SUPER_MAGIC 0x0187
++
++/* Structures associated with the root directory hash */
++
++#define AUTOFS_HASH_SIZE 67
++
++typedef u32 autofs_hash_t; /* Type returned by autofs_hash() */
++
++struct autofs_dir_ent {
++ autofs_hash_t hash;
++ struct autofs_dir_ent *next;
++ struct autofs_dir_ent **back;
++ char *name;
++ int len;
++ ino_t ino;
++ /* The following entries are for the expiry system */
++ unsigned long last_usage;
++ struct autofs_dir_ent *exp_next;
++ struct autofs_dir_ent *exp_prev;
++};
++
++struct autofs_dirhash {
++ struct autofs_dir_ent *h[AUTOFS_HASH_SIZE];
++ struct autofs_dir_ent expiry_head;
++};
++
++struct autofs_wait_queue {
++ unsigned long wait_queue_token;
++ struct wait_queue *queue;
++ struct autofs_wait_queue *next;
++ /* We use the following to see what we are waiting for */
++ autofs_hash_t hash;
++ int len;
++ char *name;
++ /* This is for status reporting upon return */
++ int status;
++ int wait_ctr;
++};
++
++struct autofs_symlink {
++ int len;
++ char *data;
++ time_t mtime;
++};
++
++#define AUTOFS_MAX_SYMLINKS 256
++
++#define AUTOFS_ROOT_INO 1
++#define AUTOFS_FIRST_SYMLINK 2
++#define AUTOFS_FIRST_DIR_INO (AUTOFS_FIRST_SYMLINK+AUTOFS_MAX_SYMLINKS)
++
++#define AUTOFS_SYMLINK_BITMAP_LEN ((AUTOFS_MAX_SYMLINKS+31)/32)
++
++#ifndef END_OF_TIME
++#define END_OF_TIME ((time_t)((unsigned long)((time_t)(~0UL)) >> 1))
++#endif
++
++#define AUTOFS_SBI_MAGIC 0x6d4a556d
++
++struct autofs_sb_info {
++ u32 magic;
++ struct file *pipe;
++ pid_t oz_pgrp;
++ int catatonic;
++ unsigned long exp_timeout;
++ ino_t next_dir_ino;
++ struct autofs_wait_queue *queues; /* Wait queue pointer */
++ struct autofs_dirhash dirhash; /* Root directory hash */
++ struct autofs_symlink symlink[AUTOFS_MAX_SYMLINKS];
++ u32 symlink_bitmap[AUTOFS_SYMLINK_BITMAP_LEN];
++};
++
++/* autofs_oz_mode(): do we see the man behind the curtain? */
++static inline int autofs_oz_mode(struct autofs_sb_info *sbi) {
++ return sbi->catatonic || current->pgrp == sbi->oz_pgrp;
++}
++
++/* Debug the mysteriously disappearing wait list */
++
++#ifdef DEBUG_WAITLIST
++#define CHECK_WAITLIST(S,O) autofs_check_waitlist_integrity(S,O)
++void autofs_check_waitlist_integrity(struct autofs_sb_info *,char *);
++#else
++#define CHECK_WAITLIST(S,O)
++#endif
++
++/* Hash operations */
++
++autofs_hash_t autofs_hash(const char *,int);
++void autofs_initialize_hash(struct autofs_dirhash *);
++struct autofs_dir_ent *autofs_hash_lookup(const struct autofs_dirhash *,autofs_hash_t,const char *,int);
++void autofs_hash_insert(struct autofs_dirhash *,struct autofs_dir_ent *);
++void autofs_hash_delete(struct autofs_dir_ent *);
++struct autofs_dir_ent *autofs_hash_enum(const struct autofs_dirhash *,off_t *);
++void autofs_hash_nuke(struct autofs_dirhash *);
++
++/* Expiration-handling functions */
++
++void autofs_update_usage(struct autofs_dirhash *,struct autofs_dir_ent *);
++struct autofs_dir_ent *autofs_expire(struct autofs_dirhash *,unsigned long);
++
++/* Operations structures */
++
++extern struct inode_operations autofs_root_inode_operations;
++extern struct inode_operations autofs_symlink_inode_operations;
++extern struct inode_operations autofs_dir_inode_operations;
++
++/* Initializing function */
++
++struct super_block *autofs_read_super(struct super_block *, void *,int);
++
++/* Queue management functions */
++
++int autofs_wait(struct autofs_sb_info *,autofs_hash_t,const char *,int);
++int autofs_wait_release(struct autofs_sb_info *,unsigned long,int);
++void autofs_catatonic_mode(struct autofs_sb_info *);
++
++#ifdef DEBUG
++void autofs_say(const char *name, int len);
++#else
++#define autofs_say(n,l)
++#endif
+diff -urN 2.0.30/linux/fs/autofs/dir.c linux/fs/autofs/dir.c
+--- 2.0.30/linux/fs/autofs/dir.c Wed Dec 31 16:00:00 1969
++++ linux/fs/autofs/dir.c Wed May 28 21:38:35 1997
+@@ -0,0 +1,90 @@
++/* -*- linux-c -*- --------------------------------------------------------- *
++ *
++ * linux/fs/autofs/dir.c
++ *
++ * Copyright 1997 Transmeta Corporation -- All Rights Reserved
++ *
++ * This file is part of the Linux kernel and is made available under
++ * the terms of the GNU General Public License, version 2, or at your
++ * option, any later version, incorporated herein by reference.
++ *
++ * ------------------------------------------------------------------------- */
++
++#include "autofs_i.h"
++
++static int autofs_dir_readdir(struct inode *inode, struct file *filp,
++ void *dirent, filldir_t filldir)
++{
++ if (!inode || !S_ISDIR(inode->i_mode))
++ return -ENOTDIR;
++
++ switch((unsigned long) filp->f_pos)
++ {
++ case 0:
++ if (filldir(dirent, ".", 1, 0, inode->i_ino) < 0)
++ return 0;
++ filp->f_pos++;
++ /* fall through */
++ case 1:
++ if (filldir(dirent, "..", 2, 1, AUTOFS_ROOT_INO) < 0)
++ return 0;
++ filp->f_pos++;
++ /* fall through */
++ }
++ return 1;
++}
++
++static int autofs_dir_lookup(struct inode *dir, const char *name, int len,
++ struct inode **result)
++{
++ *result = dir;
++ if (!len)
++ return 0;
++ if (name[0] == '.') {
++ if (len == 1)
++ return 0;
++ if (name[1] == '.' && len == 2) {
++ /* Return the root directory */
++ *result = iget(dir->i_sb,AUTOFS_ROOT_INO);
++ iput(dir);
++ return 0;
++ }
++ }
++ *result = NULL;
++ iput(dir);
++ return -ENOENT; /* No other entries */
++}
++
++static struct file_operations autofs_dir_operations = {
++ NULL, /* lseek */
++ NULL, /* read */
++ NULL, /* write */
++ autofs_dir_readdir, /* readdir */
++ NULL, /* select */
++ NULL, /* ioctl */
++ NULL, /* mmap */
++ NULL, /* open */
++ NULL, /* release */
++ NULL /* fsync */
++};
++
++struct inode_operations autofs_dir_inode_operations = {
++ &autofs_dir_operations, /* file operations */
++ NULL, /* create */
++ autofs_dir_lookup, /* lookup */
++ NULL, /* link */
++ NULL, /* unlink */
++ NULL, /* symlink */
++ NULL, /* mkdir */
++ NULL, /* rmdir */
++ NULL, /* mknod */
++ NULL, /* rename */
++ NULL, /* readlink */
++ NULL, /* follow_link */
++ NULL, /* read_page */
++ NULL, /* writepage */
++ NULL, /* bmap */
++ NULL, /* truncate */
++ NULL /* permission */
++};
++
+diff -urN 2.0.30/linux/fs/autofs/dirhash.c linux/fs/autofs/dirhash.c
+--- 2.0.30/linux/fs/autofs/dirhash.c Wed Dec 31 16:00:00 1969
++++ linux/fs/autofs/dirhash.c Wed May 28 21:38:35 1997
+@@ -0,0 +1,172 @@
++/* -*- linux-c -*- --------------------------------------------------------- *
++ *
++ * linux/fs/autofs/dirhash.c
++ *
++ * Copyright 1997 Transmeta Corporation -- All Rights Reserved
++ *
++ * This file is part of the Linux kernel and is made available under
++ * the terms of the GNU General Public License, version 2, or at your
++ * option, any later version, incorporated herein by reference.
++ *
++ * ------------------------------------------------------------------------- */
++
++#include "autofs_i.h"
++
++/* Functions for maintenance of expiry queue */
++
++static void autofs_init_usage(struct autofs_dirhash *dh,
++ struct autofs_dir_ent *ent)
++{
++ ent->exp_next = &dh->expiry_head;
++ ent->exp_prev = dh->expiry_head.exp_prev;
++ dh->expiry_head.exp_prev->exp_next = ent;
++ dh->expiry_head.exp_prev = ent;
++ ent->last_usage = jiffies;
++}
++
++static void autofs_delete_usage(struct autofs_dir_ent *ent)
++{
++ ent->exp_prev->exp_next = ent->exp_next;
++ ent->exp_next->exp_prev = ent->exp_prev;
++}
++
++void autofs_update_usage(struct autofs_dirhash *dh,
++ struct autofs_dir_ent *ent)
++{
++ autofs_delete_usage(ent); /* Unlink from current position */
++ autofs_init_usage(dh,ent); /* Relink at queue tail */
++}
++
++struct autofs_dir_ent *autofs_expire(struct autofs_dirhash *dh,
++ unsigned long timeout)
++{
++ struct autofs_dir_ent *ent;
++
++ ent = dh->expiry_head.exp_next;
++
++ if ( ent == &(dh->expiry_head) ) return NULL;
++ return (jiffies - ent->last_usage >= timeout) ? ent : NULL;
++}
++
++/* Adapted from the Dragon Book, page 436 */
++/* This particular hashing algorithm requires autofs_hash_t == u32 */
++autofs_hash_t autofs_hash(const char *name, int len)
++{
++ autofs_hash_t h = 0;
++ while ( len-- ) {
++ h = (h << 4) + (unsigned char) (*name++);
++ h ^= ((h & 0xf0000000) >> 24);
++ }
++ return h;
++}
++
++void autofs_initialize_hash(struct autofs_dirhash *dh) {
++ memset(&dh->h, 0, AUTOFS_HASH_SIZE*sizeof(struct autofs_dir_ent *));
++ dh->expiry_head.exp_next = dh->expiry_head.exp_prev =
++ &dh->expiry_head;
++}
++
++struct autofs_dir_ent *autofs_hash_lookup(const struct autofs_dirhash *dh, autofs_hash_t hash, const char *name, int len)
++{
++ struct autofs_dir_ent *dhn;
++
++ DPRINTK(("autofs_hash_lookup: hash = 0x%08x, name = ", hash));
++ autofs_say(name,len);
++
++ for ( dhn = dh->h[hash % AUTOFS_HASH_SIZE] ; dhn ; dhn = dhn->next ) {
++ if ( hash == dhn->hash &&
++ len == dhn->len &&
++ !memcmp(name, dhn->name, len) )
++ break;
++ }
++
++ return dhn;
++}
++
++void autofs_hash_insert(struct autofs_dirhash *dh, struct autofs_dir_ent *ent)
++{
++ struct autofs_dir_ent **dhnp;
++
++ DPRINTK(("autofs_hash_insert: hash = 0x%08x, name = ", ent->hash));
++ autofs_say(ent->name,ent->len);
++
++ autofs_init_usage(dh,ent);
++
++ dhnp = &dh->h[ent->hash % AUTOFS_HASH_SIZE];
++ ent->next = *dhnp;
++ ent->back = dhnp;
++ *dhnp = ent;
++}
++
++void autofs_hash_delete(struct autofs_dir_ent *ent)
++{
++ *(ent->back) = ent->next;
++
++ autofs_delete_usage(ent);
++
++ kfree(ent->name);
++ kfree(ent);
++}
++
++/*
++ * Used by readdir(). We must validate "ptr", so we can't simply make it
++ * a pointer. Values below 0xffff are reserved; calling with any value
++ * <= 0x10000 will return the first entry found.
++ */
++struct autofs_dir_ent *autofs_hash_enum(const struct autofs_dirhash *dh, off_t *ptr)
++{
++ int bucket, ecount, i;
++ struct autofs_dir_ent *ent;
++
++ bucket = (*ptr >> 16) - 1;
++ ecount = *ptr & 0xffff;
++
++ if ( bucket < 0 ) {
++ bucket = ecount = 0;
++ }
++
++ DPRINTK(("autofs_hash_enum: bucket %d, entry %d\n", bucket, ecount));
++
++ ent = NULL;
++
++ while ( bucket < AUTOFS_HASH_SIZE ) {
++ ent = dh->h[bucket];
++ for ( i = ecount ; ent && i ; i-- )
++ ent = ent->next;
++
++ if (ent) {
++ ecount++; /* Point to *next* entry */
++ break;
++ }
++
++ bucket++; ecount = 0;
++ }
++
++#ifdef DEBUG
++ if ( !ent )
++ printk("autofs_hash_enum: nothing found\n");
++ else {
++ printk("autofs_hash_enum: found hash %08x, name", ent->hash);
++ autofs_say(ent->name,ent->len);
++ }
++#endif
++
++ *ptr = ((bucket+1) << 16) + ecount;
++ return ent;
++}
++
++/* Delete everything. This is used on filesystem destruction, so we
++ make no attempt to keep the pointers valid */
++void autofs_hash_nuke(struct autofs_dirhash *dh)
++{
++ int i;
++ struct autofs_dir_ent *ent, *nent;
++
++ for ( i = 0 ; i < AUTOFS_HASH_SIZE ; i++ ) {
++ for ( ent = dh->h[i] ; ent ; ent = nent ) {
++ nent = ent->next;
++ kfree(ent->name);
++ kfree(ent);
++ }
++ }
++}
+diff -urN 2.0.30/linux/fs/autofs/init.c linux/fs/autofs/init.c
+--- 2.0.30/linux/fs/autofs/init.c Wed Dec 31 16:00:00 1969
++++ linux/fs/autofs/init.c Wed May 28 21:38:35 1997
+@@ -0,0 +1,58 @@
++/* -*- linux-c -*- --------------------------------------------------------- *
++ *
++ * linux/fs/autofs/init.c
++ *
++ * Copyright 1997 Transmeta Corporation -- All Rights Reserved
++ *
++ * This file is part of the Linux kernel and is made available under
++ * the terms of the GNU General Public License, version 2, or at your
++ * option, any later version, incorporated herein by reference.
++ *
++ * ------------------------------------------------------------------------- */
++
++#include <linux/module.h>
++#include "autofs_i.h"
++
++#if LINUX_VERSION_CODE < kver(2,1,36)
++#define __initfunc(X) X
++#else
++#include <linux/init.h>
++#endif
++
++static struct file_system_type autofs_fs_type = {
++ autofs_read_super, "autofs", 0, NULL
++};
++
++#ifdef MODULE
++int init_module(void)
++{
++ int status;
++
++ if ((status = register_filesystem(&autofs_fs_type)) == 0)
++ register_symtab(0);
++ return status;
++}
++
++void cleanup_module(void)
++{
++ unregister_filesystem(&autofs_fs_type);
++}
++
++#else /* MODULE */
++
++__initfunc(int init_autofs_fs(void))
++{
++ return register_filesystem(&autofs_fs_type);
++}
++
++#endif /* !MODULE */
++
++#ifdef DEBUG
++void autofs_say(const char *name, int len)
++{
++ printk("(%d: ", len);
++ while ( len-- )
++ printk("%c", *name++);
++ printk(")\n");
++}
++#endif
+diff -urN 2.0.30/linux/fs/autofs/inode.c linux/fs/autofs/inode.c
+--- 2.0.30/linux/fs/autofs/inode.c Wed Dec 31 16:00:00 1969
++++ linux/fs/autofs/inode.c Wed May 28 21:38:35 1997
+@@ -0,0 +1,277 @@
++/* -*- linux-c -*- --------------------------------------------------------- *
++ *
++ * linux/fs/autofs/inode.c
++ *
++ * Copyright 1997 Transmeta Corporation -- All Rights Reserved
++ *
++ * This file is part of the Linux kernel and is made available under
++ * the terms of the GNU General Public License, version 2, or at your
++ * option, any later version, incorporated herein by reference.
++ *
++ * ------------------------------------------------------------------------- */
++
++#include <linux/kernel.h>
++#include <linux/malloc.h>
++#include <linux/file.h>
++#include <linux/locks.h>
++#include <asm/bitops.h>
++#include "autofs_i.h"
++#define __NO_VERSION__
++#include <linux/module.h>
++
++static void autofs_put_inode(struct inode *inode)
++{
++ if (inode->i_nlink)
++ return;
++ inode->i_size = 0;
++}
++
++static void autofs_put_super(struct super_block *sb)
++{
++ struct autofs_sb_info *sbi =
++ (struct autofs_sb_info *) sb->u.generic_sbp;
++ unsigned int n;
++
++ if ( !sbi->catatonic )
++ autofs_catatonic_mode(sbi); /* Free wait queues, close pipe */
++
++ lock_super(sb);
++ autofs_hash_nuke(&sbi->dirhash);
++ for ( n = 0 ; n < AUTOFS_MAX_SYMLINKS ; n++ ) {
++ if ( test_bit(n, sbi->symlink_bitmap) )
++ kfree(sbi->symlink[n].data);
++ }
++
++ sb->s_dev = 0;
++ kfree(sb->u.generic_sbp);
++ unlock_super(sb);
++
++ DPRINTK(("autofs: shutting down\n"));
++
++#ifdef MODULE
++ MOD_DEC_USE_COUNT;
++#endif
++}
++
++static void autofs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz);
++static void autofs_read_inode(struct inode *inode);
++static void autofs_write_inode(struct inode *inode);
++
++static struct super_operations autofs_sops = {
++ autofs_read_inode,
++ NULL,
++ autofs_write_inode,
++ autofs_put_inode,
++ autofs_put_super,
++ NULL,
++ autofs_statfs,
++ NULL
++};
++
++static int parse_options(char *options, int *pipefd, uid_t *uid, gid_t *gid, pid_t *pgrp, int *minproto, int *maxproto)
++{
++ char *this_char, *value;
++
++ *uid = current->uid;
++ *gid = current->gid;
++ *pgrp = current->pgrp;
++
++ *minproto = *maxproto = AUTOFS_PROTO_VERSION;
++
++ *pipefd = -1;
++
++ if ( !options ) return 1;
++ for (this_char = strtok(options,","); this_char; this_char = strtok(NULL,",")) {
++ if ((value = strchr(this_char,'=')) != NULL)
++ *value++ = 0;
++ if (!strcmp(this_char,"fd")) {
++ if (!value || !*value)
++ return 1;
++ *pipefd = simple_strtoul(value,&value,0);
++ if (*value)
++ return 1;
++ }
++ else if (!strcmp(this_char,"uid")) {
++ if (!value || !*value)
++ return 1;
++ *uid = simple_strtoul(value,&value,0);
++ if (*value)
++ return 1;
++ }
++ else if (!strcmp(this_char,"gid")) {
++ if (!value || !*value)
++ return 1;
++ *gid = simple_strtoul(value,&value,0);
++ if (*value)
++ return 1;
++ }
++ else if (!strcmp(this_char,"pgrp")) {
++ if (!value || !*value)
++ return 1;
++ *pgrp = simple_strtoul(value,&value,0);
++ if (*value)
++ return 1;
++ }
++ else if (!strcmp(this_char,"minproto")) {
++ if (!value || !*value)
++ return 1;
++ *minproto = simple_strtoul(value,&value,0);
++ if (*value)
++ return 1;
++ }
++ else if (!strcmp(this_char,"maxproto")) {
++ if (!value || !*value)
++ return 1;
++ *maxproto = simple_strtoul(value,&value,0);
++ if (*value)
++ return 1;
++ }
++ else break;
++ }
++ return (*pipefd < 0);
++}
++
++struct super_block *autofs_read_super(struct super_block *s, void *data,
++ int silent)
++{
++ int pipefd;
++ struct autofs_sb_info *sbi;
++ int minproto, maxproto;
++
++ MOD_INC_USE_COUNT;
++
++ lock_super(s);
++ sbi = (struct autofs_sb_info *) kmalloc(sizeof(struct autofs_sb_info), GFP_KERNEL);
++ if ( !sbi ) {
++ s->s_dev = 0;
++ MOD_DEC_USE_COUNT;
++ return NULL;
++ }
++ DPRINTK(("autofs: starting up, sbi = %p\n",sbi));
++
++ s->u.generic_sbp = sbi;
++ sbi->magic = AUTOFS_SBI_MAGIC;
++ sbi->catatonic = 0;
++ sbi->exp_timeout = 0;
++ sbi->oz_pgrp = current->pgrp;
++ autofs_initialize_hash(&sbi->dirhash);
++ sbi->queues = NULL;
++ memset(sbi->symlink_bitmap, 0, sizeof(u32)*AUTOFS_SYMLINK_BITMAP_LEN);
++ sbi->next_dir_ino = AUTOFS_FIRST_DIR_INO;
++ s->s_blocksize = 1024;
++ s->s_blocksize_bits = 10;
++ s->s_magic = AUTOFS_SUPER_MAGIC;
++ s->s_op = &autofs_sops;
++ unlock_super(s);
++ if (!(s->s_mounted = iget(s, AUTOFS_ROOT_INO))) {
++ s->s_dev = 0;
++ kfree(sbi);
++ printk("autofs: get root inode failed\n");
++ MOD_DEC_USE_COUNT;
++ return NULL;
++ }
++
++ if ( parse_options(data,&pipefd,&s->s_mounted->i_uid,&s->s_mounted->i_gid,&sbi->oz_pgrp,&minproto,&maxproto) ) {
++ iput(s->s_mounted);
++ s->s_dev = 0;
++ kfree(sbi);
++ printk("autofs: called with bogus options\n");
++ MOD_DEC_USE_COUNT;
++ return NULL;
++ }
++
++ if ( minproto > AUTOFS_PROTO_VERSION || maxproto < AUTOFS_PROTO_VERSION ) {
++ iput(s->s_mounted);
++ s->s_dev = 0;
++ kfree(sbi);
++ printk("autofs: kernel does not match daemon version\n");
++ MOD_DEC_USE_COUNT;
++ return NULL;
++ }
++
++ DPRINTK(("autofs: pipe fd = %d, pgrp = %u\n", pipefd, sbi->oz_pgrp));
++ sbi->pipe = fget(pipefd);
++ if ( !sbi->pipe || !sbi->pipe->f_op || !sbi->pipe->f_op->write ) {
++ if ( sbi->pipe ) {
++ fput(sbi->pipe, sbi->pipe->f_inode);
++ printk("autofs: pipe file descriptor does not contain proper ops\n");
++ } else {
++ printk("autofs: could not open pipe file descriptor\n");
++ }
++ iput(s->s_mounted);
++ s->s_dev = 0;
++ kfree(sbi);
++ MOD_DEC_USE_COUNT;
++ return NULL;
++ }
++ return s;
++}
++
++static void autofs_statfs(struct super_block *sb, struct statfs *buf, int bufsiz)
++{
++ struct statfs tmp;
++
++ tmp.f_type = AUTOFS_SUPER_MAGIC;
++ tmp.f_bsize = 1024;
++ tmp.f_blocks = 0;
++ tmp.f_bfree = 0;
++ tmp.f_bavail = 0;
++ tmp.f_files = 0;
++ tmp.f_ffree = 0;
++ tmp.f_namelen = NAME_MAX;
++ copy_to_user(buf, &tmp, bufsiz);
++}
++
++static void autofs_read_inode(struct inode *inode)
++{
++ ino_t ino = inode->i_ino;
++ unsigned int n;
++ struct autofs_sb_info *sbi =
++ (struct autofs_sb_info *) inode->i_sb->u.generic_sbp;
++
++ inode->i_op = NULL;
++ inode->i_mode = 0;
++ inode->i_nlink = 2;
++ inode->i_size = 0;
++ inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
++ inode->i_blocks = 0;
++ inode->i_blksize = 1024;
++
++ if ( ino == AUTOFS_ROOT_INO ) {
++ inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO | S_IWUSR;
++ inode->i_op = &autofs_root_inode_operations;
++ inode->i_uid = inode->i_gid = 0; /* Changed in read_super */
++ return;
++ }
++
++ inode->i_uid = inode->i_sb->s_mounted->i_uid;
++ inode->i_gid = inode->i_sb->s_mounted->i_gid;
++
++ if ( ino >= AUTOFS_FIRST_SYMLINK && ino < AUTOFS_FIRST_DIR_INO ) {
++ /* Symlink inode - should be in symlink list */
++ struct autofs_symlink *sl;
++
++ n = ino - AUTOFS_FIRST_SYMLINK;
++ if ( n >= AUTOFS_MAX_SYMLINKS || !test_bit(n,sbi->symlink_bitmap)) {
++ printk("autofs: Looking for bad symlink inode 0x%08x\n", (unsigned int) ino);
++ return;
++ }
++
++ inode->i_op = &autofs_symlink_inode_operations;
++ sl = &sbi->symlink[n];
++ inode->u.generic_ip = sl;
++ inode->i_mode = S_IFLNK | S_IRWXUGO;
++ inode->i_mtime = inode->i_ctime = sl->mtime;
++ inode->i_size = sl->len;
++ inode->i_nlink = 1;
++ } else {
++ /* All non-root directory inodes look the same */
++ inode->i_op = &autofs_dir_inode_operations;
++ inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO;
++ }
++}
++
++static void autofs_write_inode(struct inode *inode)
++{
++ inode->i_dirt = 0;
++}
+diff -urN 2.0.30/linux/fs/autofs/root.c linux/fs/autofs/root.c
+--- 2.0.30/linux/fs/autofs/root.c Wed Dec 31 16:00:00 1969
++++ linux/fs/autofs/root.c Wed May 28 21:38:35 1997
+@@ -0,0 +1,445 @@
++/* -*- linux-c -*- --------------------------------------------------------- *
++ *
++ * linux/fs/autofs/root.c
++ *
++ * Copyright 1997 Transmeta Corporation -- All Rights Reserved
++ *
++ * This file is part of the Linux kernel and is made available under
++ * the terms of the GNU General Public License, version 2, or at your
++ * option, any later version, incorporated herein by reference.
++ *
++ * ------------------------------------------------------------------------- */
++
++#include <linux/errno.h>
++#include <linux/stat.h>
++#include <linux/param.h>
++#include "autofs_i.h"
++
++static int autofs_root_readdir(struct inode *,struct file *,void *,filldir_t);
++static int autofs_root_lookup(struct inode *,const char *,int,struct inode **);
++static int autofs_root_symlink(struct inode *,const char *,int,const char *);
++static int autofs_root_unlink(struct inode *,const char *,int);
++static int autofs_root_rmdir(struct inode *,const char *,int);
++static int autofs_root_mkdir(struct inode *,const char *,int,int);
++static int autofs_root_ioctl(struct inode *, struct file *,unsigned int,unsigned long);
++
++static struct file_operations autofs_root_operations = {
++ NULL, /* lseek */
++ NULL, /* read */
++ NULL, /* write */
++ autofs_root_readdir, /* readdir */
++ NULL, /* select */
++ autofs_root_ioctl, /* ioctl */
++ NULL, /* mmap */
++ NULL, /* open */
++ NULL, /* release */
++ NULL /* fsync */
++};
++
++struct inode_operations autofs_root_inode_operations = {
++ &autofs_root_operations, /* file operations */
++ NULL, /* create */
++ autofs_root_lookup, /* lookup */
++ NULL, /* link */
++ autofs_root_unlink, /* unlink */
++ autofs_root_symlink, /* symlink */
++ autofs_root_mkdir, /* mkdir */
++ autofs_root_rmdir, /* rmdir */
++ NULL, /* mknod */
++ NULL, /* rename */
++ NULL, /* readlink */
++ NULL, /* follow_link */
++ NULL, /* readpage */
++ NULL, /* writepage */
++ NULL, /* bmap */
++ NULL, /* truncate */
++ NULL /* permission */
++};
++
++static int autofs_root_readdir(struct inode *inode, struct file *filp,
++ void *dirent, filldir_t filldir)
++{
++ struct autofs_dir_ent *ent;
++ struct autofs_dirhash *dirhash;
++ off_t onr, nr;
++
++ if (!inode || !S_ISDIR(inode->i_mode))
++ return -ENOTDIR;
++
++ dirhash = &((struct autofs_sb_info *)inode->i_sb->u.generic_sbp)->dirhash;
++ nr = filp->f_pos;
++
++ switch(nr)
++ {
++ case 0:
++ if (filldir(dirent, ".", 1, nr, inode->i_ino) < 0)
++ return 0;
++ filp->f_pos = ++nr;
++ /* fall through */
++ case 1:
++ if (filldir(dirent, "..", 2, nr, inode->i_ino) < 0)
++ return 0;
++ filp->f_pos = ++nr;
++ /* fall through */
++ default:
++ while ( onr = nr, ent = autofs_hash_enum(dirhash,&nr) ) {
++ if (filldir(dirent,ent->name,ent->len,onr,ent->ino) < 0)
++ return 0;
++ filp->f_pos = nr;
++ }
++ break;
++ }
++
++ return 0;
++}
++
++static int autofs_root_lookup(struct inode *dir, const char *name, int len,
++ struct inode **result)
++{
++ struct autofs_sb_info *sbi;
++ struct autofs_dir_ent *ent;
++ struct inode *res;
++ autofs_hash_t hash;
++ int status, oz_mode;
++
++ DPRINTK(("autofs_root_lookup: name = "));
++ autofs_say(name,len);
++
++ *result = NULL;
++ if (!dir)
++ return -ENOENT;
++ if (!S_ISDIR(dir->i_mode)) {
++ iput(dir);
++ return -ENOTDIR;
++ }
++
++ /* Handle special cases: . and ..; since this is a root directory,
++ they both point to the inode itself */
++ *result = dir;
++ if (!len)
++ return 0;
++ if (name[0] == '.') {
++ if (len == 1)
++ return 0;
++ if (name[1] == '.' && len == 2)
++ return 0;
++ }
++
++ *result = res = NULL;
++ sbi = (struct autofs_sb_info *) dir->i_sb->u.generic_sbp;
++
++ hash = autofs_hash(name,len);
++
++ oz_mode = autofs_oz_mode(sbi);
++ DPRINTK(("autofs_lookup: pid = %u, pgrp = %u, catatonic = %d, oz_mode = %d\n", current->pid, current->pgrp, sbi->catatonic, oz_mode));
++
++ do {
++ while ( !(ent = autofs_hash_lookup(&sbi->dirhash,hash,name,len)) ) {
++ DPRINTK(("lookup failed, pid = %u, pgrp = %u\n", current->pid, current->pgrp));
++
++ if ( oz_mode ) {
++ iput(dir);
++ return -ENOENT;
++ } else {
++ status = autofs_wait(sbi,hash,name,len);
++ DPRINTK(("autofs_wait returned %d\n", status));
++ if ( status ) {
++ iput(dir);
++ return status;
++ }
++ }
++ }
++
++ DPRINTK(("lookup successful, inode = %08x\n", (unsigned int)ent->ino));
++
++ if (!(res = iget(dir->i_sb,ent->ino))) {
++ printk("autofs: iget returned null!\n");
++ iput(dir);
++ return -EACCES;
++ }
++
++ if ( !oz_mode && S_ISDIR(res->i_mode) && res->i_sb == dir->i_sb ) {
++ /* Not a mount point yet, call 1-800-DAEMON */
++ DPRINTK(("autofs: waiting on non-mountpoint dir, inode = %lu, pid = %u, pgrp = %u\n", res->i_ino, current->pid, current->pgrp));
++ iput(res);
++ res = NULL;
++ status = autofs_wait(sbi,hash,name,len);
++ if ( status ) {
++ iput(dir);
++ return status;
++ }
++ }
++ } while(!res);
++ autofs_update_usage(&sbi->dirhash,ent);
++
++ *result = res;
++ iput(dir);
++ return 0;
++}
++
++static int autofs_root_symlink(struct inode *dir, const char *name, int len, const char *symname)
++{
++ struct autofs_sb_info *sbi = (struct autofs_sb_info *) dir->i_sb->u.generic_sbp;
++ struct autofs_dirhash *dh = &sbi->dirhash;
++ autofs_hash_t hash = autofs_hash(name,len);
++ struct autofs_dir_ent *ent;
++ unsigned int n;
++ int slsize;
++ struct autofs_symlink *sl;
++
++ DPRINTK(("autofs_root_symlink: %s <- ", symname));
++ autofs_say(name,len);
++
++ if ( !autofs_oz_mode(sbi) ) {
++ iput(dir);
++ return -EPERM;
++ }
++ if ( autofs_hash_lookup(dh,hash,name,len) ) {
++ iput(dir);
++ return -EEXIST;
++ }
++ n = find_first_zero_bit(sbi->symlink_bitmap,AUTOFS_MAX_SYMLINKS);
++ if ( n >= AUTOFS_MAX_SYMLINKS ) {
++ iput(dir);
++ return -ENOSPC;
++ }
++ set_bit(n,sbi->symlink_bitmap);
++ sl = &sbi->symlink[n];
++ sl->len = strlen(symname);
++ sl->data = kmalloc(slsize = sl->len+1, GFP_KERNEL);
++ if ( !sl->data ) {
++ clear_bit(n,sbi->symlink_bitmap);
++ iput(dir);
++ return -ENOSPC;
++ }
++ ent = kmalloc(sizeof(struct autofs_dir_ent), GFP_KERNEL);
++ if ( !ent ) {
++ kfree(sl->data);
++ clear_bit(n,sbi->symlink_bitmap);
++ iput(dir);
++ return -ENOSPC;
++ }
++ ent->name = kmalloc(len, GFP_KERNEL);
++ if ( !ent->name ) {
++ kfree(sl->data);
++ kfree(ent);
++ clear_bit(n,sbi->symlink_bitmap);
++ iput(dir);
++ return -ENOSPC;
++ }
++ memcpy(sl->data,symname,slsize);
++ sl->mtime = CURRENT_TIME;
++
++ ent->ino = AUTOFS_FIRST_SYMLINK + n;
++ ent->hash = hash;
++ memcpy(ent->name,name,ent->len = len);
++
++ autofs_hash_insert(dh,ent);
++ iput(dir);
++
++ return 0;
++}
++
++static int autofs_root_unlink(struct inode *dir, const char *name, int len)
++{
++ struct autofs_sb_info *sbi = (struct autofs_sb_info *) dir->i_sb->u.generic_sbp;
++ struct autofs_dirhash *dh = &sbi->dirhash;
++ autofs_hash_t hash = autofs_hash(name,len);
++ struct autofs_dir_ent *ent;
++ unsigned int n;
++
++ iput(dir); /* Nothing below can sleep */
++
++ if ( !autofs_oz_mode(sbi) )
++ return -EPERM;
++
++ ent = autofs_hash_lookup(dh,hash,name,len);
++ if ( !ent )
++ return -ENOENT;
++
++ n = ent->ino - AUTOFS_FIRST_SYMLINK;
++ if ( n >= AUTOFS_MAX_SYMLINKS || !test_bit(n,sbi->symlink_bitmap) )
++ return -EINVAL; /* Not a symlink inode, can't unlink */
++
++ autofs_hash_delete(ent);
++ clear_bit(n,sbi->symlink_bitmap);
++ kfree(sbi->symlink[n].data);
++
++ return 0;
++}
++
++static int autofs_root_rmdir(struct inode *dir, const char *name, int len)
++{
++ struct autofs_sb_info *sbi = (struct autofs_sb_info *) dir->i_sb->u.generic_sbp;
++ struct autofs_dirhash *dh = &sbi->dirhash;
++ autofs_hash_t hash = autofs_hash(name,len);
++ struct autofs_dir_ent *ent;
++
++ if ( !autofs_oz_mode(sbi) ) {
++ iput(dir);
++ return -EPERM;
++ }
++ ent = autofs_hash_lookup(dh,hash,name,len);
++ if ( !ent ) {
++ iput(dir);
++ return -ENOENT;
++ }
++ if ( (unsigned int)ent->ino < AUTOFS_FIRST_DIR_INO ) {
++ iput(dir);
++ return -ENOTDIR; /* Not a directory */
++ }
++ autofs_hash_delete(ent);
++ dir->i_nlink--;
++ iput(dir);
++
++ return 0;
++}
++
++static int autofs_root_mkdir(struct inode *dir, const char *name, int len, int mode)
++{
++ struct autofs_sb_info *sbi = (struct autofs_sb_info *) dir->i_sb->u.generic_sbp;
++ struct autofs_dirhash *dh = &sbi->dirhash;
++ autofs_hash_t hash = autofs_hash(name,len);
++ struct autofs_dir_ent *ent;
++
++ if ( !autofs_oz_mode(sbi) ) {
++ iput(dir);
++ return -EPERM;
++ }
++ ent = autofs_hash_lookup(dh,hash,name,len);
++ if ( ent ) {
++ iput(dir);
++ return -EEXIST;
++ }
++ if ( sbi->next_dir_ino < AUTOFS_FIRST_DIR_INO ) {
++ printk("autofs: Out of inode numbers -- what the heck did you do??\n");
++ iput(dir);
++ return -ENOSPC;
++ }
++ ent = kmalloc(sizeof(struct autofs_dir_ent), GFP_KERNEL);
++ if ( !ent ) {
++ iput(dir);
++ return -ENOSPC;
++ }
++ ent->name = kmalloc(len, GFP_KERNEL);
++ if ( !ent->name ) {
++ kfree(ent);
++ iput(dir);
++ return -ENOSPC;
++ }
++ ent->hash = hash;
++ memcpy(ent->name, name, ent->len = len);
++ ent->ino = sbi->next_dir_ino++;
++ autofs_hash_insert(dh,ent);
++ dir->i_nlink++;
++ iput(dir);
++
++ return 0;
++}
++
++/* Get/set timeout ioctl() operation */
++static inline int autofs_get_set_timeout(struct autofs_sb_info *sbi,
++ unsigned long *p)
++{
++ int rv;
++ unsigned long ntimeout;
++
++#if LINUX_VERSION_CODE < kver(2,1,0)
++ if ( (rv = verify_area(VERIFY_WRITE, p, sizeof(unsigned long))) )
++ return rv;
++ ntimeout = get_user(p);
++ put_user(sbi->exp_timeout/HZ, p);
++#else
++ if ( (rv = get_user(ntimeout, p)) ||
++ (rv = put_user(sbi->exp_timeout/HZ, p)) )
++ return rv;
++#endif
++
++ if ( ntimeout > ULONG_MAX/HZ )
++ sbi->exp_timeout = 0;
++ else
++ sbi->exp_timeout = ntimeout * HZ;
++
++ return 0;
++}
++
++/* Return protocol version */
++static inline int autofs_get_protover(int *p)
++{
++#if LINUX_VERSION_CODE < kver(2,1,0)
++ int rv;
++ if ( (rv = verify_area(VERIFY_WRITE, p, sizeof(int))) )
++ return rv;
++ put_user(AUTOFS_PROTO_VERSION, p);
++ return 0;
++#else
++ return put_user(AUTOFS_PROTO_VERSION, p);
++#endif
++}
++
++/* Perform an expiry operation */
++static inline int autofs_expire_run(struct autofs_sb_info *sbi,
++ struct autofs_packet_expire *pkt_p)
++{
++ struct autofs_dir_ent *ent;
++ struct autofs_packet_expire pkt;
++ struct autofs_dirhash *dh = &(sbi->dirhash);
++
++ memset(&pkt,0,sizeof pkt);
++
++ pkt.hdr.proto_version = AUTOFS_PROTO_VERSION;
++ pkt.hdr.type = autofs_ptype_expire;
++
++ if ( !sbi->exp_timeout ||
++ !(ent = autofs_expire(dh,sbi->exp_timeout)) )
++ return -EAGAIN;
++
++ pkt.len = ent->len;
++ memcpy(pkt.name, ent->name, pkt.len);
++ pkt.name[pkt.len] = '\0';
++
++ if ( copy_to_user(pkt_p, &pkt, sizeof(struct autofs_packet_expire)) )
++ return -EFAULT;
++
++ autofs_update_usage(dh,ent);
++
++ return 0;
++}
++
++/*
++ * ioctl()'s on the root directory is the chief method for the daemon to
++ * generate kernel reactions
++ */
++static int autofs_root_ioctl(struct inode *inode, struct file *filp,
++ unsigned int cmd, unsigned long arg)
++{
++ struct autofs_sb_info *sbi =
++ (struct autofs_sb_info *)inode->i_sb->u.generic_sbp;
++
++ DPRINTK(("autofs_ioctl: cmd = 0x%08x, arg = 0x%08lx, sbi = %p, pgrp = %u\n",cmd,arg,sbi,current->pgrp));
++
++ if ( _IOC_TYPE(cmd) != _IOC_TYPE(AUTOFS_IOC_FIRST) ||
++ _IOC_NR(cmd) - _IOC_NR(AUTOFS_IOC_FIRST) >= AUTOFS_IOC_COUNT )
++ return -ENOTTY;
++
++ if ( !autofs_oz_mode(sbi) && !fsuser() )
++ return -EPERM;
++
++ switch(cmd) {
++ case AUTOFS_IOC_READY: /* Wait queue: go ahead and retry */
++ return autofs_wait_release(sbi,arg,0);
++ case AUTOFS_IOC_FAIL: /* Wait queue: fail with ENOENT */
++ return autofs_wait_release(sbi,arg,-ENOENT);
++ case AUTOFS_IOC_CATATONIC: /* Enter catatonic mode (daemon shutdown) */
++ autofs_catatonic_mode(sbi);
++ return 0;
++ case AUTOFS_IOC_PROTOVER: /* Get protocol version */
++ return autofs_get_protover((int *)arg);
++ case AUTOFS_IOC_SETTIMEOUT:
++ return autofs_get_set_timeout(sbi,(unsigned long *)arg);
++ case AUTOFS_IOC_EXPIRE:
++ return autofs_expire_run(sbi,(struct autofs_packet_expire *)arg);
++ default:
++ return -ENOSYS;
++ }
++}
+diff -urN 2.0.30/linux/fs/autofs/symlink.c linux/fs/autofs/symlink.c
+--- 2.0.30/linux/fs/autofs/symlink.c Wed Dec 31 16:00:00 1969
++++ linux/fs/autofs/symlink.c Wed May 28 21:38:35 1997
+@@ -0,0 +1,85 @@
++/* -*- linux-c -*- --------------------------------------------------------- *
++ *
++ * linux/fs/autofs/symlink.c
++ *
++ * Copyright 1997 Transmeta Corporation -- All Rights Reserved
++ *
++ * This file is part of the Linux kernel and is made available under
++ * the terms of the GNU General Public License, version 2, or at your
++ * option, any later version, incorporated herein by reference.
++ *
++ * ------------------------------------------------------------------------- */
++
++#include <linux/string.h>
++#include <linux/sched.h>
++#include "autofs_i.h"
++
++static int autofs_follow_link(struct inode *dir, struct inode *inode,
++ int flag, int mode, struct inode **res_inode)
++{
++ int error;
++ char *link;
++
++ *res_inode = NULL;
++ if (!dir) {
++ dir = current->fs->root;
++ dir->i_count++;
++ }
++ if (!inode) {
++ iput(dir);
++ return -ENOENT;
++ }
++ if (!S_ISLNK(inode->i_mode)) {
++ iput(dir);
++ *res_inode = inode;
++ return 0;
++ }
++ if (current->link_count > 5) {
++ iput(dir);
++ iput(inode);
++ return -ELOOP;
++ }
++ link = ((struct autofs_symlink *)inode->u.generic_ip)->data;
++ current->link_count++;
++ error = open_namei(link,flag,mode,res_inode,dir);
++ current->link_count--;
++ iput(inode);
++ return error;
++}
++
++static int autofs_readlink(struct inode *inode, char *buffer, int buflen)
++{
++ struct autofs_symlink *sl;
++ int len;
++
++ if (!S_ISLNK(inode->i_mode)) {
++ iput(inode);
++ return -EINVAL;
++ }
++ sl = (struct autofs_symlink *)inode->u.generic_ip;
++ len = sl->len;
++ if (len > buflen) len = buflen;
++ copy_to_user(buffer,sl->data,len);
++ iput(inode);
++ return len;
++}
++
++struct inode_operations autofs_symlink_inode_operations = {
++ NULL, /* file operations */
++ NULL, /* create */
++ NULL, /* lookup */
++ NULL, /* link */
++ NULL, /* unlink */
++ NULL, /* symlink */
++ NULL, /* mkdir */
++ NULL, /* rmdir */
++ NULL, /* mknod */
++ NULL, /* rename */
++ autofs_readlink, /* readlink */
++ autofs_follow_link, /* follow_link */
++ NULL, /* readpage */
++ NULL, /* writepage */
++ NULL, /* bmap */
++ NULL, /* truncate */
++ NULL /* permission */
++};
+diff -urN 2.0.30/linux/fs/autofs/waitq.c linux/fs/autofs/waitq.c
+--- 2.0.30/linux/fs/autofs/waitq.c Wed Dec 31 16:00:00 1969
++++ linux/fs/autofs/waitq.c Wed May 28 21:38:35 1997
+@@ -0,0 +1,171 @@
++/* -*- linux-c -*- --------------------------------------------------------- *
++ *
++ * linux/fs/autofs/waitq.c
++ *
++ * Copyright 1997 Transmeta Corporation -- All Rights Reserved
++ *
++ * This file is part of the Linux kernel and is made available under
++ * the terms of the GNU General Public License, version 2, or at your
++ * option, any later version, incorporated herein by reference.
++ *
++ * ------------------------------------------------------------------------- */
++
++#include <linux/malloc.h>
++#include <linux/sched.h>
++#include <linux/signal.h>
++#include <linux/file.h>
++#include "autofs_i.h"
++
++/* We make this a static variable rather than a part of the superblock; it
++ is better if we don't reassign numbers easily even across filesystems */
++static int autofs_next_wait_queue = 1;
++
++void autofs_catatonic_mode(struct autofs_sb_info *sbi)
++{
++ struct autofs_wait_queue *wq, *nwq;
++
++ DPRINTK(("autofs: entering catatonic mode\n"));
++
++ sbi->catatonic = 1;
++ wq = sbi->queues;
++ sbi->queues = NULL; /* Erase all wait queues */
++ while ( wq ) {
++ nwq = wq->next;
++ wq->status = -ENOENT; /* Magic is gone - report failure */
++ kfree(wq->name);
++ wq->name = NULL;
++ wake_up(&wq->queue);
++ wq = nwq;
++ }
++ fput(sbi->pipe, sbi->pipe->f_inode); /* Close the pipe */
++}
++
++static int autofs_write(struct file *file, const void *addr, int bytes)
++{
++ unsigned short fs;
++ unsigned long old_signal;
++ const char *data = (const char *)addr;
++ int written = 0;
++
++ /** WARNING: this is not safe for writing more than PIPE_BUF bytes! **/
++
++ /* Save pointer to user space and point back to kernel space */
++ fs = get_fs();
++ set_fs(KERNEL_DS);
++
++ old_signal = current->signal;
++
++ while ( bytes && (written = file->f_op->write(file->f_inode,file,data,bytes)) > 0 ) {
++ data += written;
++ bytes -= written;
++ }
++
++ if ( written == -EPIPE && !(old_signal & (1 << (SIGPIPE-1))) ) {
++ /* Keep the currently executing process from receiving a
++ SIGPIPE unless it was already supposed to get one */
++ current->signal &= ~(1 << (SIGPIPE-1));
++ }
++ set_fs(fs);
++
++ return (bytes > 0);
++}
++
++static void autofs_notify_daemon(struct autofs_sb_info *sbi, struct autofs_wait_queue *wq)
++{
++ struct autofs_packet_missing pkt;
++
++ DPRINTK(("autofs_wait: wait id = 0x%08lx, name = ", wq->wait_queue_token));
++ autofs_say(wq->name,wq->len);
++
++ memset(&pkt,0,sizeof pkt); /* For security reasons */
++
++ pkt.hdr.proto_version = AUTOFS_PROTO_VERSION;
++ pkt.hdr.type = autofs_ptype_missing;
++ pkt.wait_queue_token = wq->wait_queue_token;
++ pkt.len = wq->len;
++ memcpy(pkt.name, wq->name, pkt.len);
++ pkt.name[pkt.len] = '\0';
++
++ if ( autofs_write(sbi->pipe,&pkt,sizeof(struct autofs_packet_missing)) )
++ autofs_catatonic_mode(sbi);
++}
++
++int autofs_wait(struct autofs_sb_info *sbi, autofs_hash_t hash, const char *name, int len)
++{
++ struct autofs_wait_queue *wq;
++ int status;
++
++ for ( wq = sbi->queues ; wq ; wq = wq->next ) {
++ if ( wq->hash == hash &&
++ wq->len == len &&
++ wq->name && !memcmp(wq->name,name,len) )
++ break;
++ }
++
++ if ( !wq ) {
++ /* Create a new wait queue */
++ wq = kmalloc(sizeof(struct autofs_wait_queue),GFP_KERNEL);
++ if ( !wq )
++ return -ENOMEM;
++
++ wq->name = kmalloc(len,GFP_KERNEL);
++ if ( !wq->name ) {
++ kfree(wq);
++ return -ENOMEM;
++ }
++ wq->wait_queue_token = autofs_next_wait_queue++;
++ init_waitqueue(&wq->queue);
++ wq->hash = hash;
++ wq->len = len;
++ wq->status = -EINTR; /* Status return if interrupted */
++ memcpy(wq->name, name, len);
++ wq->next = sbi->queues;
++ sbi->queues = wq;
++
++ /* autofs_notify_daemon() may block */
++ wq->wait_ctr = 2;
++ autofs_notify_daemon(sbi,wq);
++ } else
++ wq->wait_ctr++;
++
++ if ( wq->name ) {
++ /* wq->name is NULL if and only if the lock is released */
++ interruptible_sleep_on(&wq->queue);
++ } else {
++ DPRINTK(("autofs_wait: skipped sleeping\n"));
++ }
++
++ status = wq->status;
++
++ if ( ! --wq->wait_ctr ) /* Are we the last process to need status? */
++ kfree(wq);
++
++ return status;
++}
++
++
++int autofs_wait_release(struct autofs_sb_info *sbi, unsigned long wait_queue_token, int status)
++{
++ struct autofs_wait_queue *wq, **wql;
++
++ for ( wql = &sbi->queues ; (wq = *wql) ; wql = &wq->next ) {
++ if ( wq->wait_queue_token == wait_queue_token )
++ break;
++ }
++ if ( !wq )
++ return -EINVAL;
++
++ *wql = wq->next; /* Unlink from chain */
++ kfree(wq->name);
++ wq->name = NULL; /* Do not wait on this queue */
++
++ wq->status = status;
++
++ if ( ! --wq->wait_ctr ) /* Is anyone still waiting for this guy? */
++ kfree(wq);
++ else
++ wake_up(&wq->queue);
++
++ return 0;
++}
++
+diff -urN 2.0.30/linux/fs/filesystems.c linux/fs/filesystems.c
+--- 2.0.30/linux/fs/filesystems.c Thu Apr 25 02:32:39 1996
++++ linux/fs/filesystems.c Thu May 1 12:35:18 1997
+@@ -24,6 +24,7 @@
+ #include <linux/ncp_fs.h>
+ #include <linux/affs_fs.h>
+ #include <linux/ufs_fs.h>
++#include <linux/auto_fs.h>
+ #include <linux/major.h>
+
+ extern void device_setup(void);
+@@ -110,6 +111,9 @@
+ init_ufs_fs();
+ #endif
+
++#ifdef CONFIG_AUTOFS_FS
++ init_autofs_fs();
++#endif
+ mount_root();
+ return 0;
+ }
+diff -urN 2.0.30/linux/include/linux/auto_fs.h linux/include/linux/auto_fs.h
+--- 2.0.30/linux/include/linux/auto_fs.h Wed Dec 31 16:00:00 1969
++++ linux/include/linux/auto_fs.h Wed May 28 21:38:59 1997
+@@ -0,0 +1,62 @@
++/* -*- linux-c -*- ------------------------------------------------------- *
++ *
++ * linux/include/linux/auto_fs.h
++ *
++ * Copyright 1997 Transmeta Corporation - All Rights Reserved
++ *
++ * This file is part of the Linux kernel and is made available under
++ * the terms of the GNU General Public License, version 2, or at your
++ * option, any later version, incorporated herein by reference.
++ *
++ * ----------------------------------------------------------------------- */
++
++
++#ifndef _LINUX_AUTO_FS_H
++#define _LINUX_AUTO_FS_H
++
++#include <linux/version.h>
++#include <linux/fs.h>
++#include <linux/limits.h>
++#include <linux/ioctl.h>
++#include <asm/types.h>
++
++#define AUTOFS_PROTO_VERSION 3
++
++enum autofs_packet_type {
++ autofs_ptype_missing, /* Missing entry (mount request) */
++ autofs_ptype_expire, /* Expire entry (umount request) */
++};
++
++struct autofs_packet_hdr {
++ int proto_version; /* Protocol version */
++ enum autofs_packet_type type; /* Type of packet */
++};
++
++struct autofs_packet_missing {
++ struct autofs_packet_hdr hdr;
++ unsigned long wait_queue_token;
++ int len;
++ char name[NAME_MAX+1];
++};
++
++struct autofs_packet_expire {
++ struct autofs_packet_hdr hdr;
++ int len;
++ char name[NAME_MAX+1];
++};
++
++#define AUTOFS_IOC_READY _IO(0x93,0x60)
++#define AUTOFS_IOC_FAIL _IO(0x93,0x61)
++#define AUTOFS_IOC_CATATONIC _IO(0x93,0x62)
++#define AUTOFS_IOC_PROTOVER _IOR(0x93,0x63,int)
++#define AUTOFS_IOC_SETTIMEOUT _IOWR(0x93,0x64,unsigned long)
++#define AUTOFS_IOC_EXPIRE _IOR(0x93,0x65,struct autofs_packet_expire)
++
++#ifdef __KERNEL__
++
++/* Init function */
++int init_autofs_fs(void);
++
++#endif /* __KERNEL__ */
++
++#endif /* _LINUX_AUTO_FS_H */
+diff -urN 2.0.30/linux/kernel/ksyms.c linux/kernel/ksyms.c
+--- 2.0.30/linux/kernel/ksyms.c Tue Mar 11 14:37:16 1997
++++ linux/kernel/ksyms.c Thu May 1 12:35:18 1997
+@@ -46,6 +46,7 @@
+ #include <linux/genhd.h>
+ #include <linux/swap.h>
+ #include <linux/ctype.h>
++#include <linux/file.h>
+
+ extern unsigned char aux_device_present, kbd_read_mask;
+
+@@ -170,6 +171,7 @@
+ X(generic_file_read),
+ X(generic_file_mmap),
+ X(generic_readpage),
++ X(__fput),
+
+ /* device registration */
+ X(register_chrdev),
diff --git a/man/Makefile b/man/Makefile
new file mode 100644
index 0000000..ba92087
--- /dev/null
+++ b/man/Makefile
@@ -0,0 +1,10 @@
+include ../Makefile.rules
+
+install:
+ install -c *.5 -m 644 $(mandir)/man5
+ install -c *.8 -m 644 $(mandir)/man8
+
+all:
+
+clean:
+
diff --git a/man/auto.master.5 b/man/auto.master.5
new file mode 100644
index 0000000..867bb6f
--- /dev/null
+++ b/man/auto.master.5
@@ -0,0 +1,67 @@
+.\" t
+.TH AUTO.MASTER 5 "9 Sep 1997"
+.SH NAME
+/etc/auto.master \- Master Map for automounter
+.SH "DESCRIPTION"
+The
+.B auto.master
+map is consulted when the
+.BR autofs (8)
+script is invoked to set up the necessary mount points for the automounter.
+Each line in this file describes a mount point and points to another file
+describing the file systems to be mounted under this mountpoint. The access
+to those maps is governed by a key.
+.P
+Access to an automounted file system is customarily done using the path scheme:
+.P
+.RI / mountpoint / key / path / file ,
+.P
+where the
+.I mountpoint
+will be listed in the
+.B auto.master
+configuration file. The
+.I key
+is matched in the map file pointed to by the
+master map (See
+.BR autofs (5)).
+The path and the file are referring to the file on the file system mounted.
+.SH "FORMAT"
+The file has three fields separated by an arbitrary number of blanks or
+tabs. Lines beginning with # are comments. The first field is the mount
+point. Second field is the map file to be consulted for this mount-point.
+The third field is optional and can contain options to be applied to all
+entries in the map. Options are cumulative which is a difference to the
+behavior of the SunOS automounter.
+
+The format of the map file and the options are described in
+.BR autofs (5).
+.SH EXAMPLE
+.sp
+.RS +.2i
+.ta 1.0i
+.nf
+/home /etc/auto.home
+/misc /etc/auto.misc
+.fi
+.RE
+.sp
+This will generate two mountpoints
+.I /home
+and
+.IR /misc .
+All accesses to
+.I /home
+will lead to the consultation of the map in
+.IR /etc/auto.home ,
+and all accesses to
+.I /misc
+will consult the map in
+.IR /etc/auto.misc .
+.SH "SEE ALSO"
+.BR automount (8),
+.BR autofs (5),
+.BR autofs (8).
+.SH AUTHOR
+This manual page was written by Christoph Lameter <chris@waterf.org>,
+for the Debian GNU/Linux system. Edited by <hpa@transmeta.com>.
diff --git a/man/autofs.5 b/man/autofs.5
new file mode 100644
index 0000000..fef8f39
--- /dev/null
+++ b/man/autofs.5
@@ -0,0 +1,136 @@
+.\" t
+.TH AUTOFS 5 "29 Sep 1997"
+.SH NAME
+autofs \- Format of the automounter maps
+.SH "DESCRIPTION"
+The automounter maps are files or NIS maps referred to by the master map of
+the automounter (see
+.BR auto.master (5)).
+The automounter maps describe how file systems below the mountpoint of the map
+(given in the auto.master file) are to be mounted.
+
+Maps can be changed on the fly and the automouter will recognize those
+changes on the next operation it performs on that map. This is not
+true for the
+.B auto.master
+map!
+.SH "FORMAT"
+This is a description of the text file format. Other methods of specifying
+these files may exist. All empty lines or lines beginning with # are
+ignored. The basic format of one line in such maps is:
+.P
+.B key [-options] location
+
+.SS key
+is the part of the pathname between the mountpoint and the path into the
+filesystem mounted. Usually you can think about the key as a subdirectory
+name below the mountpoint.
+
+.SS options
+Options are optional. Options can also be given in the
+.B auto.master
+file in which case both values are cumulative (Difference vs. SunOS!).
+The options are a list of comma separated options as customary for the
+.BR mount (8)
+command. There is a special option
+.I -fstype=
+used to specify a filesystem type if the filesystem is not of the default
+NFS type. This option is processed by the automounter and not by the mount
+command.
+
+.SS location
+The location specifies from where the file system is to be mounted. In the
+most cases this will be an NFS volume and the usual notation
+.I host:pathname
+is used to indicate the remote filesystem and path to be mounted. If
+the filesystem to be mounted begins with a / (such as local
+.I /dev
+entries or smbfs shares) a : needs to be prefixed (e.g.
+.IR :/dev/sda1 ).
+.SH EXAMPLE
+.sp
+.RS +.2i
+.ta 1.0i 3.0i
+.nf
+kernel -ro,soft,intr ftp.kernel.org:/pub/linux
+boot -fstype=ext2 :/dev/hda1
+windoze -fstype=smbfs ://windoze/c
+removable -fstype=ext2 :/dev/hdd
+cd -fstype=iso9660,ro :/dev/hdc
+floppy -fstype=auto :/dev/fd0
+.fi
+.RE
+.sp
+In the first line we have a NFS remote mount of the kernel directory on
+.IR ftp.kernel.org .
+This is mounted read-only. The second line mounts an ext2 volume on a
+local ide drive. The third makes a share exported from a Windows
+machine available for automounting. The rest should be fairly
+self-explanatory.
+.SH FEATURES
+.SS Map Key Substitution
+An & character in the
+.B location
+is expanded to the value of the
+.B key
+field that matched the line (which probably only makes sense together with
+a wildchard key).
+.SS Wildcard Key
+A * in the
+.B key
+field matches all keys. An example for the usefulness is the following entry:
+.sp
+.RS +.2i
+.ta 1.0i
+.nf
+* &:/home/&
+.fi
+.RE
+.sp
+This will enable you to access all the home directory of local hosts using
+the path
+.RI / mountpoint / hostname / local-path .
+.SS Variable Substitution
+The following special variables will be substituted in the key and location
+fields of an automounter map if prefixed with $ as customary from shell
+scripts (Curly braces can be used to separate the fieldname):
+.sp
+.RS +.2i
+.ta 1.5i
+.nf
+ARCH Architecture (uname -m)
+CPU Processor Type
+HOST Hostname (uname -n)
+OSNAME Operating System (uname -s)
+OSREL Release of OS (uname -r)
+OSVERS Version of OS (uname -v)
+.fi
+.RE
+.sp
+Additional entries can be defined with the -Dvariable=Value option to
+.BR automount (8).
+.SS Executable Maps
+A map can be marked as executable. Those will be called as a script with the
+key as an argument. The script needs to return one line of a map or no
+output at all if the key cannot be matched.
+.P
+To do this the
+.BR automount (8)
+daemon has to be started with the program type insted of the file
+type. This is implemented in the initialization script.
+.P
+A executable map can return an errorcode to indicate the failure in addition
+to no output at all. All output sent to stderr is logged into the system
+logs.
+.SH UNSUPPORTED
+The automounter does not support SunOS style replicated Filesystem and
+direct maps.
+.SH "SEE ALSO"
+.BR automount (8),
+.BR auto.master (5),
+.BR autofs(8),
+.BR mount(8).
+.SH AUTHOR
+This manual page was written by Christoph Lameter <chris@waterf.org>,
+for the Debian GNU/Linux system. Edited by H. Peter Anvin
+<hpa@transmeta.com>.
diff --git a/man/autofs.8 b/man/autofs.8
new file mode 100644
index 0000000..d2221ed
--- /dev/null
+++ b/man/autofs.8
@@ -0,0 +1,63 @@
+.TH AUTOFS 8 "9 Sep 1997"
+.SH NAME
+/etc/init.d/autofs \- Control Script for automounter
+.SH SYNOPSIS
+.B /etc/init.d/autofs
+.I start|stop|reload
+.SH "DESCRIPTION"
+.B autofs
+control the operation of the
+.BR automount (8)
+daemons running on the Linux system. Usually
+.B autofs
+is invoked at system boot time with the
+.I start
+parameter and at shutdown time with the
+.I stop
+parameter. The
+.B autofs
+script can also manually be invoked by the system administrator to shut
+down, restart or reload the automounters.
+.P
+.SH "OPERATION"
+.B autofs
+will consult a configuration file
+.I /etc/auto.master
+(see
+.BR auto.master (5))
+to find mount points on the system. For each of those mount points a
+.BR automount (8)
+process is started with the appropriate parameters. You can check the
+active mount points for the automounter with the
+.B /etc/init.d/autofs status
+command. After the
+.I auto.master
+configuration file is processed the
+.B autofs
+script will check for an NIS map with the same name. If such a map exists
+then that map will be processed in the same way as the auto.master map. The
+NIS map will be processed last.
+.P
+.B /etc/init.d/autofs reload
+will check the current auto.master map against running daemons. It will kill
+those daemons whose entries have changed and then start daemons for new or
+changed entries.
+.P
+If a map is modified then the change will become effective immediately. If
+the
+.I auto.master
+map is modified then the
+.B autofs
+script must be rerun to activate the changes.
+.P
+.B /etc/init.d/autofs status
+will display the current configuration and a list of currently running
+automount daemons.
+.SH "SEE ALSO"
+.BR automount (8),
+.BR autofs (5),
+.BR auto.master (5).
+.SH AUTHOR
+This manual page was written by Christoph Lameter <chris@waterf.org>,
+for the Debian GNU/Linux system. Edited by H. Peter Anvin
+<hpa@transmeta.com>.
diff --git a/man/automount.8 b/man/automount.8
new file mode 100644
index 0000000..3fa0544
--- /dev/null
+++ b/man/automount.8
@@ -0,0 +1,91 @@
+.\" Linux man page by B. James Phillippe, 1997 <bryan@Terran.ORG>
+.\"
+.\" This page was written to contribute to the Linux kernel autofs
+.\" implementation by H. Peter Anvin (1997). It is loosly based on
+.\" the documentation for mount(8) and amd(8) Linux manpages.
+.\"
+.\" This is free documentation.
+.\"
+.TH AUTOMOUNT 8 "17 Sep 1997"
+.SH NAME
+automount \- configure mount points for autofs
+.SH SYNOPSIS
+\fBautomount\fP [\fIoptions\fP] \fImount-point\fP \fImap-type\fP[,\fIformat\fP] \fImap\fP [\fImap-options\fP]
+.SH DESCRIPTION
+The \fBautomount\fP program is used to configure a mount point for
+autofs, the inlined Linux automounter. \fBautomount\fP works by
+taking a base \fImount-point\fP and \fImap\fP file, and using these
+(combined with other \fIoptions\fP) to automatically mount filesystems
+within the base \fImount-point\fP when they are accessed in any way.
+The filesystems are then autounmounted after a period of inactivity.
+.SH OPTIONS
+.TP
+.I "\-p, \-\-pid-file"
+Write the pid of the daemon to the specified file.
+.TP
+.I "\-t, \-\-timeout"
+Set the minimum timeout, in seconds, until directories are unmounted.
+The default is 5 minutes. Setting the timeout to zero disables
+unmounts completely.
+.TP
+.I "\-v, \-\-version"
+Display the version number, then exit.
+.SH ARGUMENTS
+\fBautomount\fP takes at least three arguments. Mandatory arguments
+include \fImount-point\fP, \fImap-type\fP and \fImap\fP. Both mandatory
+and optional arguments are described below.
+.TP
+\fBmount-point\fP
+Base location for autofs-mounted filesystems to be attached. This is a
+directory name which must already exist.
+.TP
+\fBmap-type\fP
+Type of map used for this invocation of \fBautomount\fP. The following are
+valid map types:
+.RS
+.TP
+.B file
+The map is a regular text file.
+.TP
+.B program
+The map is an executable program, which is passed a key on the command
+line and returns an entry on stdout if successful.
+.TP
+.B yp
+The map is an NIS (YP) database.
+.RE
+.TP
+\fBformat\fP
+Format of the map data; currently the only format
+recognized is \fBsun\fP, which is a subset of the Sun automounter map
+format. The default format is technically map type dependent,
+although currently all map types default to \fBsun\fP format.
+.TP
+\fBmap\fP
+Location of mapfile to use. This is an absolute UNIX pathname in the case
+for maps of types \fBfile\fP and \fBprogram\fP, and the name of an NIS
+database in the case for maps of type \fByp\fP.
+.TP
+\fBoptions\fP
+Any remaining command line arguments without leading dashes (\-) are
+taken as options (\fI\-o\fP) to \fBmount\fP. Arguments with leading
+dashes are considered options for the maps.
+.SH NOTES
+If the \fBautomount\fP daemon catches signal USR1, it will unmount all
+currently unused autofs-mounted filesystems and continue running
+(forced unmount.) If it catches signal USR2 it will unmount all
+unused autofs-mounted filesystems and exit if all filesystems were
+unmounted. Busy filesystems will not be unmounted.
+.SH "SEE ALSO"
+.BR autofs (5),
+.BR mount (8).
+.SH BUGS
+A whole slew of missing desirable features (see \fBTODO\fP file).
+
+The documentation leaves a lot to be desired.
+
+Please report other bugs along with a detailed description to
+<autofs@linux.kernel.org>. To join this mailing list, send a message
+with the line "subscribe autofs" to <majordomo@linux.kernel.org>.
+.SH AUTHOR
+H. Peter Anvin <hpa@transmeta.com>
diff --git a/modules/Makefile b/modules/Makefile
new file mode 100644
index 0000000..ee08b43
--- /dev/null
+++ b/modules/Makefile
@@ -0,0 +1,30 @@
+#
+# Makefile for autofs
+#
+
+SRCS = lookup_yp.c lookup_file.c lookup_program.c \
+ parse_sun.c \
+ mount_generic.c mount_nfs.c mount_smbfs.c
+MODS = lookup_yp.so lookup_file.so lookup_program.so \
+ parse_sun.so \
+ mount_generic.so mount_nfs.so mount_smbfs.so
+
+include ../Makefile.rules
+
+CFLAGS += -I../include -fpic -DAUTOFS_LIB_DIR=\"$(autofslibdir)\"
+
+all: $(MODS)
+
+clean:
+ rm -f *.o *.s *.so
+
+install: all
+ install -d -m 755 $(autofslibdir)
+ install -c $(MODS) -m 644 -o root $(autofslibdir)
+
+#
+# Ad hoc compilation rules for modules which need auxilliary libraries
+#
+lookup_yp.so: lookup_yp.c
+ $(CC) $(SOLDFLAGS) $(CFLAGS) -o lookup_yp.so lookup_yp.c $(YPLIBS)
+ $(STRIP) lookup_yp.so
diff --git a/modules/lookup_file.c b/modules/lookup_file.c
new file mode 100644
index 0000000..17200a0
--- /dev/null
+++ b/modules/lookup_file.c
@@ -0,0 +1,214 @@
+/*
+ * lookup_file.c
+ *
+ * Module for Linux automountd to access a plain file automount map
+ */
+
+#include <stdio.h>
+#include <malloc.h>
+#include <errno.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <syslog.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#define MODULE_LOOKUP
+#include "automount.h"
+
+#define MAPFMT_DEFAULT "sun"
+
+#define MODPREFIX "lookup(file): "
+
+#define MAPENT_MAX_LEN 4095
+
+struct lookup_context {
+ const char *mapname;
+ struct parse_mod *parse;
+};
+
+int lookup_version = AUTOFS_LOOKUP_VERSION; /* Required by protocol */
+
+int lookup_init(const char *mapfmt, int argc, const char * const *argv,
+ void **context)
+{
+ struct lookup_context *ctxt;
+
+ if ( !(*context = ctxt = malloc(sizeof(struct lookup_context))) ) {
+ syslog(LOG_CRIT, MODPREFIX "malloc: %m");
+ return 1;
+ }
+ if ( argc < 1 ) {
+ syslog(LOG_CRIT, MODPREFIX "No map name");
+ return 1;
+ }
+ ctxt->mapname = argv[0];
+
+ if (ctxt->mapname[0] != '/') {
+ syslog(LOG_CRIT, MODPREFIX "file map %s is not an absolute pathname",
+ ctxt->mapname);
+ return 1;
+ }
+
+ if ( access(ctxt->mapname, R_OK) ) {
+ syslog(LOG_WARNING, MODPREFIX "file map %s missing or not readable",
+ ctxt->mapname);
+ }
+
+ if ( !mapfmt )
+ mapfmt = MAPFMT_DEFAULT;
+
+ return !(ctxt->parse = open_parse(mapfmt,MODPREFIX,argc-1,argv+1));
+}
+
+int lookup_mount(const char *root, const char *name, int name_len,
+ void *context)
+{
+ struct lookup_context *ctxt = (struct lookup_context *) context;
+ int ch, nch;
+ char mapent[MAPENT_MAX_LEN+1], *p;
+ const char *nptr;
+ int mapent_len;
+ FILE *f;
+ enum {
+ st_begin, st_compare, st_star, st_badent, st_entspc, st_getent
+ } state;
+ enum { got_nothing, got_star, got_real } getting, gotten;
+ enum { esc_none, esc_char, esc_val } escape;
+
+ syslog(LOG_DEBUG, MODPREFIX "looking up %s", name);
+
+ chdir("/"); /* If this is not here the filesystem stays
+ busy, for some reason... */
+ f = fopen(ctxt->mapname, "r");
+ if ( !f ) {
+ syslog(LOG_ERR, MODPREFIX "could not open map file %s", ctxt->mapname);
+ return 1;
+ }
+
+ state = st_begin;
+ gotten = got_nothing;
+
+ /* Shut up gcc */
+ nptr = p = NULL;
+ mapent_len = 0;
+ getting = got_nothing;
+ escape = esc_none;
+
+ /* This is ugly. We can't just remove \ escape sequences in the value
+ portion of an entry, because the parsing routine may need it. */
+
+ while ( (ch = getc(f)) != EOF ) {
+ switch ( escape ) {
+ case esc_none:
+ if ( ch == '\\' ) {
+ /* Handle continuation lines */
+ if ( (nch = getc(f)) == '\n' )
+ goto next_char;
+ ungetc(nch,f);
+ escape = esc_char;
+ }
+ break;
+
+ case esc_char:
+ escape = esc_val;
+ break;
+
+ case esc_val:
+ escape = esc_none;
+ break;
+ }
+
+ switch(state) {
+ case st_begin:
+ if ( isspace(ch) && !escape )
+ ;
+ else if ( escape == esc_char )
+ ;
+ else if ( ch == '#' && !escape )
+ state = st_badent;
+ else if ( (char)ch == name[0] ) {
+ state = st_compare;
+ nptr = name+1;
+ } else if ( ch == '*' && !escape )
+ state = st_star;
+ else
+ state = st_badent;
+ break;
+
+ case st_compare:
+ if ( ch == '\n' )
+ state = st_begin;
+ else if ( isspace(ch) && !*nptr && !escape ) {
+ getting = got_real;
+ state = st_entspc;
+ } else if ( escape == esc_char )
+ ;
+ else if ( (char)ch != *(nptr++) )
+ state = st_badent;
+ break;
+
+ case st_star:
+ if ( ch == '\n' )
+ state = st_begin;
+ else if ( isspace(ch) && gotten < got_star && !escape ) {
+ getting = got_star;
+ state = st_entspc;
+ } else if ( escape != esc_char )
+ state = st_badent;
+ break;
+
+ case st_badent:
+ if ( ch == '\n' )
+ state = st_begin;
+ break;
+
+ case st_entspc:
+ if ( ch == '\n' )
+ state = st_begin;
+ else if ( !isspace(ch) || escape ) {
+ state = st_getent;
+ p = mapent;
+ gotten = getting;
+ *(p++) = ch;
+ mapent_len = 1;
+ }
+ break;
+
+ case st_getent:
+ if ( ch == '\n' ) {
+ state = st_begin;
+ if ( gotten == got_real )
+ goto got_it; /* No point in parsing the rest of the file */
+ } else if ( mapent_len < MAPENT_MAX_LEN ) {
+ mapent_len++;
+ *(p++) = ch;
+ }
+ break;
+ }
+ next_char: /* End of loop, since we can't continue;
+ inside a switch */
+ }
+
+got_it:
+ fclose(f);
+
+ if ( gotten == got_nothing ) {
+ syslog(LOG_NOTICE, MODPREFIX "lookup for %s failed", name);
+ return 1; /* Didn't found anything */
+ }
+ *p = '\0'; /* Null-terminate */
+
+ syslog(LOG_DEBUG, MODPREFIX "%s -> %s", name, mapent);
+
+ return ctxt->parse->parse_mount(root,name,name_len,mapent,ctxt->parse->context);
+}
+
+int lookup_done(void *context)
+{
+ struct lookup_context *ctxt = (struct lookup_context *) context;
+ int rv = close_parse(ctxt->parse);
+ free(ctxt);
+ return rv;
+}
diff --git a/modules/lookup_program.c b/modules/lookup_program.c
new file mode 100644
index 0000000..ccd8c83
--- /dev/null
+++ b/modules/lookup_program.c
@@ -0,0 +1,197 @@
+/*
+ * lookup_program.c
+ *
+ * Module for Linux automountd to access a automount map via a query program
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <malloc.h>
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/times.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#define MODULE_LOOKUP
+#include "automount.h"
+
+#define MAPFMT_DEFAULT "sun"
+
+#define MODPREFIX "lookup(program): "
+
+#define MAPENT_MAX_LEN 4095
+
+struct lookup_context {
+ const char *mapname;
+ struct parse_mod *parse;
+};
+
+int lookup_version = AUTOFS_LOOKUP_VERSION; /* Required by protocol */
+
+int lookup_init(const char *mapfmt, int argc, const char * const *argv,
+ void **context)
+{
+ struct lookup_context *ctxt;
+
+ if ( !(*context = ctxt = malloc(sizeof(struct lookup_context))) ) {
+ syslog(LOG_CRIT, MODPREFIX "malloc: %m");
+ return 1;
+ }
+ if ( argc < 1 ) {
+ syslog(LOG_CRIT, MODPREFIX "No map name");
+ return 1;
+ }
+ ctxt->mapname = argv[0];
+
+ if (ctxt->mapname[0] != '/') {
+ syslog(LOG_CRIT, MODPREFIX "program map %s is not an absolute pathname",
+ ctxt->mapname);
+ return 1;
+ }
+
+ if ( access(ctxt->mapname, X_OK) ) {
+ syslog(LOG_WARNING, MODPREFIX "program map %s missing or not executable",
+ ctxt->mapname);
+ }
+
+ if ( !mapfmt )
+ mapfmt = MAPFMT_DEFAULT;
+
+ return !(ctxt->parse = open_parse(mapfmt,MODPREFIX,argc-1,argv+1));
+}
+
+int lookup_mount(const char *root, const char *name, int name_len,
+ void *context)
+{
+ struct lookup_context *ctxt = (struct lookup_context *) context;
+ char mapent[MAPENT_MAX_LEN+1], *mapp;
+ char errbuf[1024], *errp;
+ char *p, ch;
+ int pipefd[2], epipefd[2];
+ pid_t f;
+ int files_left;
+ int status;
+ fd_set readfds, ourfds;
+
+ syslog(LOG_DEBUG, MODPREFIX "looking up %s", name);
+
+ /* We don't use popen because we don't want to run /bin/sh plus we
+ want to send stderr to the syslog, and we don't use spawnl()
+ because we need the pipe hooks */
+
+ if ( pipe(pipefd) ) {
+ syslog(LOG_ERR, MODPREFIX "pipe: %m");
+ return 1;
+ }
+ if ( pipe(epipefd) ) {
+ close(pipefd[0]);
+ close(pipefd[1]);
+ return 1;
+ }
+ f = fork();
+ if ( f < 0 ) {
+ close(pipefd[0]);
+ close(pipefd[1]);
+ close(epipefd[0]);
+ close(epipefd[1]);
+ syslog(LOG_ERR, MODPREFIX "fork: %m");
+ return 1;
+ } else if ( f == 0 ) {
+ close(pipefd[0]);
+ close(epipefd[0]);
+ dup2(pipefd[1],STDOUT_FILENO);
+ dup2(epipefd[1],STDERR_FILENO);
+ close(pipefd[1]);
+ close(epipefd[1]);
+ execl(ctxt->mapname, ctxt->mapname, name, NULL);
+ _exit(255); /* execl() failed */
+ }
+ close(pipefd[1]);
+ close(epipefd[1]);
+
+ mapp = mapent;
+ errp = errbuf;
+
+ FD_ZERO(&ourfds);
+ FD_SET(pipefd[0],&ourfds);
+ FD_SET(epipefd[0],&ourfds);
+
+ files_left = 2;
+
+ while (files_left) {
+ readfds = ourfds;
+ if ( select(OPEN_MAX, &readfds, NULL, NULL, NULL) < 0 &&
+ errno != EINTR )
+ break;
+
+ if ( FD_ISSET(pipefd[0],&readfds) ) {
+ if ( read(pipefd[0], &ch, 1) < 1 ) {
+ FD_CLR(pipefd[0], &ourfds);
+ files_left--;
+ } else if ( mapp ) {
+ if ( ch == '\n' ) {
+ *mapp = '\0';
+ mapp = NULL; /* End of line reached */
+ } else if ( mapp-mapent < MAPENT_MAX_LEN )
+ *(mapp++) = ch;
+ }
+ }
+ if ( FD_ISSET(epipefd[0],&readfds) ) {
+ if ( read(epipefd[0], &ch, 1) < 1 ) {
+ FD_CLR(epipefd[0], &ourfds);
+ files_left--;
+ } else if ( ch == '\n' ) {
+ *errp = '\0';
+ if ( errbuf[0] )
+ syslog(LOG_NOTICE, ">> %s", errbuf);
+ errp = errbuf;
+ } else {
+ if ( errp >= &errbuf[1023] ) {
+ *errp = '\0';
+ syslog(LOG_NOTICE, ">> %s", errbuf);
+ errp = errbuf;
+ }
+ *(errp++) = ch;
+ }
+ }
+ }
+
+ if ( mapp )
+ *mapp = '\0';
+ if ( errp > errbuf ) {
+ *errp = '\0';
+ syslog(LOG_NOTICE, ">> %s", errbuf);
+ }
+
+ close(pipefd[0]);
+ close(epipefd[0]);
+
+ if ( waitpid(f,&status,0) != f ) {
+ syslog(LOG_ERR, MODPREFIX "waitpid: %m");
+ return 1;
+ }
+ if ( mapp == mapent || !WIFEXITED(status) || WEXITSTATUS(status) != 0 ) {
+ syslog(LOG_NOTICE, MODPREFIX "lookup for %s failed", name);
+ return 1;
+ }
+ if ( (p = strchr(mapent,'\n')) ) *p = '\0';
+
+ syslog(LOG_DEBUG, MODPREFIX "%s -> %s", name, mapent);
+
+ return ctxt->parse->parse_mount(root,name,name_len,mapent,
+ ctxt->parse->context);
+}
+
+int lookup_done(void *context)
+{
+ struct lookup_context *ctxt = (struct lookup_context *) context;
+ int rv = close_parse(ctxt->parse);
+ free(ctxt);
+ return rv;
+}
diff --git a/modules/lookup_yp.c b/modules/lookup_yp.c
new file mode 100644
index 0000000..93d7c1b
--- /dev/null
+++ b/modules/lookup_yp.c
@@ -0,0 +1,106 @@
+/*
+ * lookup_yp.c
+ *
+ * Module for Linux automountd to access a YP (NIS) automount map
+ */
+
+#include <stdio.h>
+#include <malloc.h>
+#include <errno.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <rpc/rpc.h>
+#include <rpc/xdr.h>
+#include <rpcsvc/yp_prot.h>
+#include <rpcsvc/ypclnt.h>
+
+#define MODULE_LOOKUP
+#include "automount.h"
+
+#define MAPFMT_DEFAULT "sun"
+
+#define MODPREFIX "lookup(yp): "
+
+struct lookup_context {
+ const char *domainname;
+ const char *mapname;
+ struct parse_mod *parse;
+};
+
+int lookup_version = AUTOFS_LOOKUP_VERSION; /* Required by protocol */
+
+int lookup_init(const char *mapfmt, int argc, const char * const *argv,
+ void **context)
+{
+ struct lookup_context *ctxt;
+ int err;
+
+ if ( !(*context = ctxt = malloc(sizeof(struct lookup_context))) ) {
+ syslog(LOG_CRIT, MODPREFIX "%m");
+ return 1;
+ }
+
+ if ( argc < 1 ) {
+ syslog(LOG_CRIT, MODPREFIX "No map name");
+ return 1;
+ }
+ ctxt->mapname = argv[0];
+
+ /* This should, but doesn't, take a const char ** */
+ err = yp_get_default_domain((char **) &ctxt->domainname);
+ if ( err ) {
+ syslog(LOG_CRIT, MODPREFIX "map %s: %s\n", ctxt->mapname, yperr_string(err));
+ return 1;
+ }
+
+ if ( !mapfmt )
+ mapfmt = MAPFMT_DEFAULT;
+
+ return !(ctxt->parse = open_parse(mapfmt,MODPREFIX,argc-1,argv+1));
+}
+
+int lookup_mount(const char *root, const char *name,
+ int name_len, void *context)
+{
+ struct lookup_context *ctxt = (struct lookup_context *) context;
+ char *mapent;
+ int mapent_len;
+ int err, rv;
+
+ syslog(LOG_DEBUG, MODPREFIX "looking up %s", name);
+
+ /* For reasons unknown, the standard YP definitions doesn't define input
+ strings as const char *. However, my understanding is that they will
+ not be modified by the library. */
+ err = yp_match((char *) ctxt->domainname, (char *) ctxt->mapname,
+ (char *) name, name_len, &mapent, &mapent_len);
+ if ( err == YPERR_KEY ) {
+ /* Try to get the "*" entry if there is one - note that we *don't*
+ modify "name" so & -> the name we used, not "*" */
+ err = yp_match((char *) ctxt->domainname, (char *) ctxt->mapname,
+ "*", 1, &mapent, &mapent_len);
+ }
+ if ( err ) {
+ syslog(LOG_NOTICE, MODPREFIX "lookup for %s failed: %s", name, yperr_string(err));
+ return 1;
+ }
+
+ mapent[mapent_len] = '\0';
+
+ syslog(LOG_DEBUG, MODPREFIX "%s -> %s", name, mapent);
+
+ rv = ctxt->parse->parse_mount(root,name,name_len,mapent,ctxt->parse->context);
+ free(mapent);
+ return rv;
+}
+
+int lookup_done(void *context)
+{
+ struct lookup_context *ctxt = (struct lookup_context *) context;
+ int rv = close_parse(ctxt->parse);
+ free(ctxt);
+ return rv;
+}
diff --git a/modules/mount_generic.c b/modules/mount_generic.c
new file mode 100644
index 0000000..9e23c97
--- /dev/null
+++ b/modules/mount_generic.c
@@ -0,0 +1,78 @@
+/*
+ * mount_generic.c
+ *
+ * Module for Linux automountd to mount filesystems for which no special
+ * magic is required
+ *
+ */
+
+#include <stdio.h>
+#include <malloc.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#define MODULE_MOUNT
+#include "automount.h"
+
+#define MODPREFIX "mount(generic): "
+int mount_version = AUTOFS_MOUNT_VERSION; /* Required by protocol */
+
+
+int mount_init(void **context)
+{
+ return 0;
+}
+
+int mount_mount(const char *root, const char *name, int name_len,
+ const char *what, const char *fstype, const char *options,
+ void *context)
+{
+ char *fullpath;
+ int err;
+
+ fullpath = alloca(strlen(root)+name_len+2);
+ if ( !fullpath ) {
+ syslog(LOG_ERR, MODPREFIX "alloca: %m");
+ return 1;
+ }
+ sprintf(fullpath, "%s/%s", root, name);
+
+ syslog(LOG_DEBUG, MODPREFIX "calling mkdir %s", fullpath);
+ if ( mkdir(fullpath, 0555) && errno != EEXIST ) {
+ syslog(LOG_NOTICE, MODPREFIX "mkdir %s failed: %m", name);
+ return 1;
+ }
+
+ if ( options ) {
+ syslog(LOG_DEBUG, MODPREFIX "calling mount -t %s -o %s %s %s",
+ fstype, options, what, fullpath);
+ err = spawnl(LOG_NOTICE, _PATH_MOUNT, _PATH_MOUNT, "-t", fstype,
+ "-o", options, what, fullpath, NULL);
+ } else {
+ syslog(LOG_DEBUG, MODPREFIX "calling mount -t %s %s %s",
+ fstype, what, fullpath);
+ err = spawnl(LOG_NOTICE, _PATH_MOUNT, _PATH_MOUNT, "-t", fstype,
+ what, fullpath, NULL);
+ }
+ if ( err ) {
+ rmdir(fullpath);
+ syslog(LOG_NOTICE, MODPREFIX "failed to mount %s (type %s) on %s",
+ what, fstype, fullpath);
+ return 1;
+ } else {
+ syslog(LOG_DEBUG, MODPREFIX "mounted %s type %s on %s",
+ what, fstype, fullpath);
+ return 0;
+ }
+}
+
+int mount_done(void *context)
+{
+ return 0;
+}
diff --git a/modules/mount_nfs.c b/modules/mount_nfs.c
new file mode 100644
index 0000000..747326f
--- /dev/null
+++ b/modules/mount_nfs.c
@@ -0,0 +1,164 @@
+/*
+ * mount_nfs.c
+ *
+ * Module for Linux automountd to mount an NFS filesystem, with fallback to
+ * symlinking if the path is local
+ *
+ */
+
+#include <stdio.h>
+#include <malloc.h>
+#include <errno.h>
+#include <netdb.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <netinet/in.h>
+
+#define MODULE_MOUNT
+#include "automount.h"
+
+#define MODPREFIX "mount(nfs): "
+int mount_version = AUTOFS_MOUNT_VERSION; /* Required by protocol */
+
+static int udpproto;
+static short port_discard;
+
+int mount_init(void **context)
+{
+ struct protoent *udp;
+ struct servent *port_dis;
+
+ /* These are context independent */
+ udp = getprotobyname("udp");
+ udpproto = udp ? udp->p_proto : 0;
+ port_dis = getservbyname("discard","udp");
+ if ( port_dis )
+ port_discard = port_dis->s_port;
+ else
+ port_discard = htons(9); /* 9 is the standard discard port */
+
+ return 0;
+}
+
+int mount_mount(const char *root, const char *name, int name_len,
+ const char *what, const char *fstype,
+ const char *options, void *context)
+{
+ char *colon, **haddr, *fullpath;
+ struct hostent *he;
+ struct sockaddr_in saddr, laddr;
+ int sock, local, err;
+ size_t len;
+
+ colon = strchr(what, ':');
+ if ( !colon ) {
+ /* No colon, take this as a symlink (local) entry */
+ syslog(LOG_DEBUG, MODPREFIX "entry %s -> %s: no colon, assume local",
+ name, what);
+ chdir(root);
+ err = symlink(what, name);
+ if ( err && errno == EEXIST )
+ err = 0;
+ if ( err )
+ syslog(LOG_NOTICE, MODPREFIX "symlink %s: %m", name);
+ chdir("/");
+ return err ? 1 : 0;
+ }
+
+ *colon = '\0';
+ if ( !(he = gethostbyname(what)) ) {
+ syslog(LOG_NOTICE, MODPREFIX "entry %s: host %s: lookup failure",
+ name, what);
+ return 1; /* No such host */
+ }
+
+ /* Probe to see if we are the local host. Open a UDP socket and see
+ if the local address is the same as the remote one */
+
+ local = 0;
+ for ( haddr = he->h_addr_list ; *haddr ; haddr++ ) {
+ sock = socket(AF_INET, SOCK_DGRAM, udpproto);
+ if ( sock < 0 ) {
+ syslog(LOG_ERR, MODPREFIX "socket: %m");
+ return 1;
+ }
+ saddr.sin_family = AF_INET;
+ bcopy(*haddr, &saddr.sin_addr, he->h_length);
+ saddr.sin_port = port_discard;
+
+ len = sizeof(laddr);
+ if ( connect(sock, (struct sockaddr *) &saddr, sizeof(saddr)) < 0
+ || getsockname(sock, (struct sockaddr *) &laddr, &len) < 0 ) {
+ syslog(LOG_ERR, MODPREFIX "connect+getsockname failed for %s", name);
+ close(sock);
+ return 1;
+ }
+ close(sock);
+
+ if ( !memcmp(&saddr.sin_addr,&laddr.sin_addr,he->h_length) ) {
+ local = 1;
+ break;
+ }
+ }
+
+ if ( local ) {
+ /* Local host -- do a symlink */
+
+ syslog(LOG_DEBUG, MODPREFIX "%s is local, symlinking", name);
+ chdir(root);
+ err = symlink(colon+1, name);
+ if ( err && errno == EEXIST )
+ err = 0;
+ if ( err )
+ syslog(LOG_NOTICE, MODPREFIX "symlink %s: %m", name);
+ chdir("/");
+ return err ? 1 : 0;
+ } else {
+ /* Not a local host - do a mount */
+
+ fullpath = alloca(strlen(root)+name_len+2);
+ if ( !fullpath ) {
+ syslog(LOG_ERR, MODPREFIX "alloca: %m");
+ return 1;
+ }
+ sprintf(fullpath, "%s/%s", root, name);
+
+ *colon = ':';
+ syslog(LOG_DEBUG, MODPREFIX "calling mkdir %s", fullpath);
+ if ( mkdir(fullpath, 0555) && errno != EEXIST ) {
+ syslog(LOG_NOTICE, MODPREFIX "mkdir %s failed: %m", name);
+ return 1;
+ }
+
+ if ( options ) {
+ syslog(LOG_DEBUG, MODPREFIX "calling mount -t nfs -o %s %s %s",
+ options, what, fullpath);
+ err = spawnl(LOG_NOTICE, _PATH_MOUNT, _PATH_MOUNT, "-t", "nfs", "-o",
+ options, what, fullpath, NULL);
+ } else {
+ syslog(LOG_DEBUG, MODPREFIX "calling mount -t nfs %s %s", what, fullpath);
+ err = spawnl(LOG_NOTICE, _PATH_MOUNT, _PATH_MOUNT, "-t", "nfs",
+ what, fullpath, NULL);
+ }
+ if ( err ) {
+ rmdir(fullpath);
+ syslog(LOG_NOTICE, MODPREFIX "nfs: mount failure %s on %s",
+ what, fullpath);
+ return 1;
+ } else {
+ syslog(LOG_DEBUG, MODPREFIX "mounted %s on %s", what, fullpath);
+ return 0;
+ }
+ }
+}
+
+int mount_done(void *context)
+{
+ return 0;
+}
diff --git a/modules/mount_smbfs.c b/modules/mount_smbfs.c
new file mode 100644
index 0000000..200d1f2
--- /dev/null
+++ b/modules/mount_smbfs.c
@@ -0,0 +1,180 @@
+/* ----------------------------------------------------------------------- *
+ *
+ * mount_smbfs.c
+ *
+ * Module for Linux automountd to mount an SMB/CIFS filesystem
+ *
+ * Mount point input format expected is: //server/service[/root_path...]
+ *
+ * Copyright 1997 Transmeta Corporation - All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139,
+ * USA; either version 2 of the License, or (at your option) any later
+ * version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+#include <stdio.h>
+#include <malloc.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <string.h>
+#include <syslog.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#define MODULE_MOUNT
+#include "automount.h"
+
+#define MODPREFIX "mount(smbfs): "
+int mount_version = AUTOFS_MOUNT_VERSION; /* Required by protocol */
+
+struct smb_mount_opt {
+ char *optname; /* mount-style option name */
+ char *optflag; /* command-line flag for smbmount */
+ int hasarg; /* takes argument? */
+ int satisfies_pwd; /* true if this means we don't need a -n */
+};
+
+static struct smb_mount_opt mount_opt_list[] = {
+ { "nocaps", "-C", 0, 0 },
+ { "guest", "-n", 0, 1 },
+ { "passwd", "-P", 1, 1 },
+ { "srvname", "-s", 1, 0 },
+ { "mysmbname", "-c", 1, 0 },
+ { "login", "-U", 1, 0 },
+ { "uid", "-u", 1, 0 },
+ { "gid", "-g", 1, 0 },
+ { "filemod", "-f", 1, 0 },
+ { "dirmod", "-d", 1, 0 },
+ { "port", "-p", 1, 0 },
+ { "maxxmit", "-m", 1, 0 },
+ { NULL, NULL, 0, 0 }
+};
+
+int mount_init(void **context)
+{
+ return 0;
+}
+
+static int smb_parse_options(char *optstr, const char **argv,
+ char *qbuf, int *qbuflen)
+{
+ char *opt;
+ int ln;
+ int argc;
+ int has_pwd;
+ int qbufchr, qln;
+ struct smb_mount_opt *mount_opt;
+
+ has_pwd = 0;
+ qbufchr = 0;
+ argc = 0;
+
+ if ( optstr ) {
+ for ( opt = strtok(optstr, ",") ; opt ; opt = strtok(NULL, ",") ) {
+ for ( mount_opt = mount_opt_list ; mount_opt->optname ; mount_opt++ ) {
+ if ( mount_opt->hasarg ) {
+ ln = strlen(mount_opt->optname);
+ if ( !strncmp(opt, mount_opt->optname, ln) && opt[ln] == '=' ) {
+ qln = strlen(opt)-ln;
+ if ( argv ) {
+ *(argv++) = mount_opt->optflag;
+ memcpy(qbuf, opt+ln+1, qln);
+ *(argv++) = qbuf;
+ qbuf += qln;
+ }
+ qbufchr += qln;
+ has_pwd = has_pwd || mount_opt->satisfies_pwd;
+ argc += 2;
+ break;
+ }
+ } else {
+ if ( !strcmp(opt, mount_opt->optname) ) {
+ if ( argv )
+ *(argv++) = mount_opt->optflag;
+ has_pwd = has_pwd || mount_opt->satisfies_pwd;
+ argc++;
+ break;
+ }
+ }
+ }
+ /* Ignore unknown options */
+ }
+ }
+
+ if ( !has_pwd ) {
+ syslog(LOG_DEBUG, MODPREFIX "no password option, adding -n");
+ if ( argv )
+ *(argv++) = "-n";
+ argc++;
+ }
+
+ if ( argv )
+ *argv = NULL;
+
+ if ( qbuflen )
+ *qbuflen = qbufchr;
+
+ return argc;
+}
+
+int mount_mount(const char *root, const char *name, int name_len,
+ const char *what, const char *fstype, const char *options,
+ void *context)
+{
+ char *fullpath, *optcopy;
+ int err;
+ char *qbuf;
+ int argc, optsize, qbuflen;
+ const char **argv;
+
+ fullpath = alloca(strlen(root)+name_len+2);
+ optcopy = alloca(optsize = strlen(options)+1);
+ if ( !fullpath || !optcopy ) {
+ syslog(LOG_ERR, MODPREFIX "alloca: %m");
+ return 1;
+ }
+ sprintf(fullpath, "%s/%s", root, name);
+
+ memcpy(optcopy, options, optsize);
+ argc = smb_parse_options(optcopy, NULL, NULL, &qbuflen) + 4;
+ argv = alloca(sizeof(char *) * argc);
+ qbuf = alloca(qbuflen);
+ if ( !argv || (qbuflen && !qbuf) ) {
+ syslog(LOG_ERR, MODPREFIX "alloca: %m");
+ return 1;
+ }
+ argv[0] = _PATH_SMBMOUNT;
+ argv[1] = what;
+ argv[2] = fullpath;
+ memcpy(optcopy, options, optsize);
+ smb_parse_options(optcopy, argv+3, qbuf, NULL);
+
+ syslog(LOG_DEBUG, MODPREFIX "calling mkdir %s", fullpath);
+ if ( mkdir(fullpath, 0555) && errno != EEXIST ) {
+ syslog(LOG_NOTICE, MODPREFIX "mkdir %s failed: %m", name);
+ return 1;
+ }
+
+ err = spawnv(LOG_NOTICE, _PATH_SMBMOUNT, argv);
+
+ if ( err ) {
+ rmdir(fullpath);
+ syslog(LOG_NOTICE, MODPREFIX "failed to mount %s on %s", what, fullpath);
+ return 1;
+ } else {
+ syslog(LOG_DEBUG, MODPREFIX "mounted %s on %s", what, fullpath);
+ return 0;
+ }
+}
+
+int mount_done(void *context)
+{
+ return 0;
+}
diff --git a/modules/parse_sun.c b/modules/parse_sun.c
new file mode 100644
index 0000000..3c87f05
--- /dev/null
+++ b/modules/parse_sun.c
@@ -0,0 +1,502 @@
+/*
+ * parse_sun.c
+ *
+ * Module for Linux automountd to parse a Sun-format automounter map
+ */
+
+#include <stdio.h>
+#include <malloc.h>
+#include <errno.h>
+#include <netdb.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <string.h>
+#include <syslog.h>
+#include <ctype.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/utsname.h>
+#include <netinet/in.h>
+
+#define MODULE_PARSE
+#include "automount.h"
+
+#define MODPREFIX "parse(sun): "
+int parse_version = AUTOFS_PARSE_VERSION; /* Required by protocol */
+
+static struct mount_mod *mount_nfs = NULL;
+static int init_ctr = 0;
+
+struct substvar {
+ char *def; /* Define variable */
+ char *val; /* Value to replace with */
+ struct substvar *next;
+};
+
+struct parse_context {
+ char *optstr; /* Mount options */
+ struct substvar *subst; /* $-substitutions */
+ int slashify_colons; /* Change colons to slashes? */
+};
+
+struct utsname un;
+char processor[65]; /* Not defined on Linux, so we make our own */
+
+static struct substvar /* Predefined variables: tail of link chain */
+ sv_arch = { "ARCH", un.machine, NULL },
+ sv_cpu = { "CPU", processor, &sv_arch },
+ sv_host = { "HOST", un.nodename, &sv_cpu },
+ sv_osname = { "OSNAME", un.sysname, &sv_host },
+ sv_osrel = { "OSREL", un.release, &sv_osname },
+ sv_osvers = { "OSVERS", un.version, &sv_osrel };
+
+/* Default context pattern */
+
+static struct parse_context default_context = {
+ NULL, /* No mount options */
+ &sv_osvers, /* The substvar predefined variables */
+ 1 /* Do slashify_colons */
+};
+
+/* Free all storage associated with this context */
+static void kill_context(struct parse_context *ctxt)
+{
+ struct substvar *sv, *nsv;
+
+ sv = ctxt->subst;
+ while ( sv != &sv_osvers ) {
+ nsv = sv->next;
+ free(sv);
+ sv = nsv;
+ }
+
+ if ( ctxt->optstr )
+ free(ctxt->optstr);
+
+ free(ctxt);
+}
+
+/* Find the $-variable matching a certain string fragment */
+static const struct substvar *findvar(const struct substvar *sv,
+ const char *str, int len)
+{
+ while ( sv ) {
+ if ( !strncmp(str,sv->def,len) && sv->def[len] == '\0' )
+ return sv;
+ sv = sv->next;
+ }
+ return NULL;
+}
+
+/* $- and &-expand a Sun-style map entry and return the length of the entry.
+ If "dst" is NULL, just count the length. */
+static int expandsunent(const char *src, char *dst, const char *key,
+ const struct substvar *svc, int slashify_colons)
+{
+ const struct substvar *sv;
+ int len, l, seen_colons;
+ const char *p;
+ char ch;
+
+ len = 0;
+ seen_colons = 0;
+
+ while ( (ch = *src++) ) {
+ switch ( ch ) {
+ case '&':
+ l = strlen(key);
+ if ( dst ) {
+ strcpy(dst,key);
+ dst += l;
+ }
+ len += l;
+ break;
+
+ case '$':
+ if ( *src == '{' ) {
+ p = strchr(++src,'}');
+ if ( !p ) {
+ /* Ignore rest of string */
+ if ( dst ) *dst = '\0';
+ return len;
+ }
+ sv = findvar(svc, src, p-src);
+ if ( sv ) {
+ l = strlen(sv->val);
+ if ( dst ) {
+ strcpy(dst,sv->val);
+ dst += l;
+ }
+ len += l;
+ }
+ src = p+1;
+ } else {
+ p = src;
+ while ( isalnum(*p) || *p == '_' ) p++;
+ sv = findvar(svc, src, p-src);
+ if ( sv ) {
+ l = strlen(sv->val);
+ if ( dst ) {
+ strcpy(dst,sv->val);
+ dst += l;
+ }
+ len += l;
+ }
+ src = p;
+ }
+ break;
+
+ case '\\':
+ ch = *src;
+ if ( ch ) {
+ src++;
+ if ( dst ) *(dst++) = ch;
+ len++;
+ } else {
+ ; /* Terminal backslash, just eat it */
+ }
+ break;
+
+ case ':':
+ if ( dst ) *(dst++) = (seen_colons && slashify_colons) ? '/' : ':';
+ len++;
+ seen_colons = 1;
+ break;
+
+ default:
+ if ( dst ) *(dst++) = ch;
+ len++;
+ break;
+ }
+ }
+ if ( dst ) *dst = '\0';
+ return len;
+}
+
+/* Skip whitespace in a string; if we hit a #, consider the rest of the
+ entry a comment */
+char *skipspace(char *whence)
+{
+ while(1) {
+ switch(*whence) {
+ case ' ':
+ case '\b':
+ case '\t':
+ case '\n':
+ case '\v':
+ case '\f':
+ case '\r':
+ whence++;
+ break;
+ case '\0':
+ case '#':
+ return NULL; /* End of string */
+ default:
+ return whence;
+ }
+ }
+}
+
+/* Get the length of a chunk delimitered by whitespace */
+int chunklen(char *whence)
+{
+ int n = 0;
+ while(1) {
+ switch(*whence) {
+ case ' ':
+ case '\b':
+ case '\t':
+ case '\n':
+ case '\v':
+ case '\f':
+ case '\r':
+ case '#':
+ case '\0':
+ return n;
+ default:
+ break;
+ }
+ n++;
+ whence++;
+ }
+}
+
+/* Compare str with pat. Return 0 if compare equal or str is an abbreviation of
+ pat of no less than mchr characters. */
+int strmcmp(const char *str, const char *pat, int mchr)
+{
+ int nchr = 0;
+
+ while ( *str == *pat ) {
+ if ( ! *str ) return 0;
+ str++; pat++; nchr++;
+ }
+
+ if ( ! *str && nchr > mchr )
+ return 0;
+
+ return *pat - *str;
+}
+
+int parse_init(int argc, const char * const *argv, void **context)
+{
+ struct parse_context *ctxt;
+ struct substvar *sv;
+ char *noptstr;
+ const char *xopt;
+ int optlen, len;
+ int i, bval;
+
+ /* Get processor information for predefined escapes */
+
+ if ( !init_ctr ) {
+ uname(&un);
+ /* uname -p is not defined on Linux. Make it the same as uname -m,
+ except make it return i386 on all x86 (x >= 3) */
+ strcpy(processor, un.machine);
+ if ( processor[0] == 'i' && processor[1] >= '3' &&
+ !strcmp(processor+2, "86") )
+ processor[1] = '3';
+ }
+
+ /* Set up context and escape chain */
+
+ if ( !(ctxt = (struct parse_context *) malloc(sizeof(struct parse_context))) ) {
+ syslog(LOG_CRIT, MODPREFIX "malloc: %m");
+ return 1;
+ }
+ *context = (void *) ctxt;
+
+ *ctxt = default_context;
+ optlen = 0;
+
+ /* Look for options and capture, and create new defines if we need to */
+
+ for ( i = 0 ; i < argc ; i++ ) {
+ if ( argv[i][0] == '-' ) {
+ switch(argv[i][1]) {
+ case 'D':
+ sv = malloc(sizeof(struct substvar));
+ if ( !sv ) {
+ syslog(LOG_ERR, MODPREFIX "malloc: %m");
+ break;
+ }
+ if ( argv[i][2] )
+ sv->def = strdup(argv[i]+2);
+ else if ( ++i < argc )
+ sv->def = strdup(argv[i]);
+ else {
+ free(sv);
+ break;
+ }
+
+ if ( !sv->def ) {
+ syslog(LOG_ERR, MODPREFIX "strdup: %m");
+ free(sv);
+ } else {
+ sv->val = strchr(sv->def, '=');
+ if ( sv->val )
+ *(sv->val++) = '\0';
+ else
+ sv->val = "";
+
+ sv->next = ctxt->subst;
+ ctxt->subst = sv;
+ }
+ break;
+
+ case '-':
+ if ( !strncmp(argv[i]+2, "no-", 3) ) {
+ xopt = argv[i]+5;
+ bval = 0;
+ } else {
+ xopt = argv[i]+2;
+ bval = 1;
+ }
+
+ if ( strmcmp(xopt, "slashify-colons", 1) )
+ ctxt->slashify_colons = bval;
+ else
+ syslog(LOG_ERR, MODPREFIX "unknown option: %s", argv[i]);
+
+ break;
+
+ default:
+ syslog(LOG_ERR, MODPREFIX "unknown option: %s", argv[i]);
+ break;
+ }
+ } else {
+ len = strlen(argv[i]);
+ if ( ctxt->optstr ) {
+ noptstr = (char *) realloc(ctxt->optstr, optlen+len+2);
+ if ( !noptstr )
+ break;
+ noptstr[optlen] = ',';
+ strcpy(noptstr+optlen+1, argv[i]);
+ optlen += len+1;
+ } else {
+ noptstr = (char *) malloc(len+1);
+ strcpy(noptstr, argv[i]);
+ optlen = len;
+ }
+ if ( !noptstr ) {
+ kill_context(ctxt);
+ syslog(LOG_CRIT, MODPREFIX "%m");
+ return 1;
+ }
+ ctxt->optstr = noptstr;
+ syslog(LOG_DEBUG, MODPREFIX "init gathered options: %s", ctxt->optstr);
+ }
+ }
+
+ /* We only need this once. NFS mounts are so common that we cache
+ this module. */
+ if ( !mount_nfs )
+ if ( (mount_nfs = open_mount("nfs", MODPREFIX)) ) {
+ init_ctr++;
+ return 0;
+ } else {
+ kill_context(ctxt);
+ return 1;
+ }
+ else {
+ init_ctr++;
+ return 0;
+ }
+}
+
+int parse_mount(const char *root, const char *name,
+ int name_len, const char *mapent, void *context)
+{
+ struct parse_context *ctxt = (struct parse_context *) context;
+ char *pmapent, *options, *noptions, *ent, *p, *q, *fstype;
+ int mapent_len, rv;
+ int l, optlen;
+
+ mapent_len = expandsunent(mapent,NULL,name,ctxt->subst,ctxt->slashify_colons);
+ pmapent = alloca(mapent_len + 1);
+ if (!pmapent) {
+ syslog(LOG_ERR, MODPREFIX "alloca: %m");
+ return 1;
+ }
+ expandsunent(mapent,pmapent,name,ctxt->subst,ctxt->slashify_colons);
+
+ syslog(LOG_DEBUG, "expanded entry: %s", pmapent);
+
+ options = strdup(ctxt->optstr ? ctxt->optstr : "");
+ if ( !options ) {
+ syslog(LOG_ERR, MODPREFIX "strdup: %m");
+ return 1;
+ }
+ optlen = strlen(options);
+
+ p = pmapent;
+ ent = NULL;
+
+ while ( (p = skipspace(p)) ) {
+ if ( *p == '-' ) {
+ l = chunklen(++p);
+ if ( !l )
+ continue;
+ noptions = realloc(options, optlen+l+(optlen ? 2 : 1));
+ if ( !noptions ) {
+ free(options);
+ syslog(LOG_ERR, MODPREFIX "realloc: %m");
+ return 1;
+ }
+ if ( optlen ) noptions[optlen++] = ',';
+ memcpy(noptions+optlen, p, l);
+ noptions[optlen += l] = '\0';
+ options = noptions;
+ p += l;
+ syslog(LOG_DEBUG, MODPREFIX "gathered options: %s", options);
+ } else if ( *p == '/' ) {
+ /* Uh-oh, a multientry; don't support now, but ****FIXME**** */
+ /* We do tolerate "multientries" that only contain / though... */
+ if ( chunklen(p) == 1 ) {
+ p++;
+ } else {
+ free(options);
+ syslog(LOG_NOTICE, MODPREFIX "entry %s is a multipath entry", name);
+ return 1;
+ }
+ } else {
+ p[chunklen(p)] = '\0'; /* We don't use anything after the entry */
+ if ( *p == ':' ) p++; /* Sun escape for entries starting with / */
+ if ( ! *p ) {
+ syslog(LOG_ERR, MODPREFIX "entry %s is empty!", name);
+ free(options);
+ return 1;
+ }
+ ent = p;
+ syslog(LOG_DEBUG, MODPREFIX "core of entry: %s", ent);
+ break; /* We have the core entry, run w/it */
+ }
+ }
+
+ fstype = "nfs"; /* Default filesystem type */
+
+ /* Scan for -fstype= option, which isn't a real mount option */
+
+ noptions = strdup(options);
+ if ( !noptions ) {
+ syslog(LOG_ERR, MODPREFIX "strdup: %m");
+ free(options);
+ return 1;
+ }
+
+ p = options;
+
+ for ( q = strtok(noptions, ",") ; q ; q = strtok(NULL, ",") ) {
+ if ( !strncmp(q, "fstype=", 7) ) {
+ fstype = alloca(strlen(q+7)+1);
+ if ( !fstype ) {
+ syslog(LOG_ERR, MODPREFIX "alloca: %s");
+ return 1;
+ }
+ strcpy(fstype, q+7);
+ } else {
+ l = strlen(q);
+ memcpy(p, q, l);
+ p += l;
+ *p = ',';
+ p++;
+ }
+ }
+
+ if ( p > options ) {
+ --p; /* Delete last comma */
+ *p = '\0';
+ } else {
+ free(options); /* No options - mount modules want NULL here */
+ options = NULL;
+ }
+ free(noptions);
+
+ if ( !strcmp(fstype, "nfs") ) {
+ rv = mount_nfs->mount_mount(root, name, name_len, ent, fstype, options,
+ mount_nfs->context);
+ } else {
+ /* Generic mount routine */
+ rv = do_mount(root, name, name_len, ent, fstype, options);
+ }
+
+ if (options) free(options);
+ return rv;
+}
+
+int parse_done(void *context)
+{
+ int rv = 0;
+ struct parse_context *ctxt = (struct parse_context *)context;
+
+ if ( --init_ctr == 0 ) {
+ rv = close_mount(mount_nfs);
+ mount_nfs = NULL;
+ }
+ kill_context(ctxt);
+ return rv;
+}
diff --git a/samples/auto.master b/samples/auto.master
new file mode 100644
index 0000000..d5bedc3
--- /dev/null
+++ b/samples/auto.master
@@ -0,0 +1,5 @@
+# Sample auto.master file
+# Format of this file:
+# mountpoint map options
+# For details of the format look at autofs(8).
+/misc /etc/auto.misc
diff --git a/samples/auto.misc b/samples/auto.misc
new file mode 100644
index 0000000..587879a
--- /dev/null
+++ b/samples/auto.misc
@@ -0,0 +1,9 @@
+# This is an automounter map and it has the following format
+# key [ -mount-options-separated-by-comma ] location
+# Details may be found in the autofs(5) manpage
+
+kernel -ro ftp.kernel.org:/pub/linux
+boot -fstype=ext2 :/dev/hda1
+removable -fstype=ext2 :/dev/hdd
+cd -fstype=iso9660,ro :/dev/hdc
+floppy -fstype=auto :/dev/fd0
diff --git a/samples/rc.autofs b/samples/rc.autofs
new file mode 100644
index 0000000..e928792
--- /dev/null
+++ b/samples/rc.autofs
@@ -0,0 +1,225 @@
+#! /bin/bash
+#
+# rc file for automount using a Sun-style "master map".
+# We first look for a local /etc/auto.master, then a YP
+# map with that name
+#
+# On most distributions, this file should be called:
+# /etc/rc.d/init.d/autofs or /etc/init.d/autofs
+#
+
+# This is used in the Debian distribution to determine the proper
+# location for the S- and K-links to this init file.
+# The following value is extracted by debstd to figure out how to
+# generate the postinst script. Edit the field to change the way the
+# script is registered through update-rc.d (see the manpage for
+# update-rc.d!)
+#
+FLAGS="defaults 21"
+
+test -f /usr/sbin/automount || exit 0
+PATH=/sbin:/usr/sbin:/bin:/usr/bin
+export PATH
+
+#
+# We can add local options here
+# e.g. localoptions='rsize=8192,wsize=8192'
+#
+localoptions=''
+
+#
+# This function will build a list of automount commands to execute in
+# order # to activate all the mount points. It is used to figure out
+# the difference of automount points in case of a reload
+#
+function getmounts()
+{
+#
+# Check for local maps to be loaded
+#
+if [ -f /etc/auto.master ]
+then
+ cat /etc/auto.master | sed -e '/^#/d' -e '/^$/d'| (
+ while read dir map options
+ do
+ if [ ! -z "$dir" -a ! -z "$map" \
+ -a x`echo "$map" | cut -c1` != 'x-' ]
+ then
+ map=`echo "/etc/$map" | sed -e 's:^/etc//:/:'`
+ options=`echo "$options" | sed -e 's/\(^\|[ \t]\)-/\1/g'`
+ if [ -x $map ]; then
+ echo "automount $dir program $map $options $localoptions"
+ else
+ echo "automount $dir file $map $options $localoptions"
+ fi
+ fi
+ done
+ )
+fi
+
+#
+# Check for YellowPage maps to be loaded
+#
+if [ -e /usr/bin/ypcat ] && [ `ypcat -k auto.master 2>/dev/null | wc -l` -gt 0 ]
+then
+ ypcat -k auto.master | (
+ while read dir map options
+ do
+ if [ ! -z "$dir" -a ! -z "$map" \
+ -a x`echo "$map" | cut -c1` != 'x-' ]
+ then
+ map=`echo "$map" | sed -e 's/^auto_/auto./'`
+ options=`echo "$options" | sed -e 's/\(^\|[ \t]\)-/\1/g'`
+ echo "automount $dir yp $map $options $localoptions"
+ fi
+ done
+ )
+fi
+}
+
+#
+# Status lister.
+#
+function status()
+{
+ echo "Configured Mount Points:"
+ echo "------------------------"
+ getmounts
+ echo ""
+ echo "Active Mount Points:"
+ echo "--------------------"
+ ps ax|grep "[0-9]:[0-9][0-9] automount " | (
+ while read pid tt stat time command; do echo $command; done
+ )
+}
+
+
+#
+# Redhat start/stop function.
+#
+function redhat()
+{
+
+#
+# See how we were called.
+#
+case "$1" in
+ start)
+ # Check if the automounter is already running?
+ if [ ! -f /var/lock/automount ]; then
+ echo 'Starting automounter: '
+ getmounts | sh
+ touch /var/lock/subsys/automount
+ fi
+ ;;
+ stop)
+ killall -TERM automount
+ rm -f /var/lock/subsys/automount
+ ;;
+ reload|restart)
+ if [ ! -f /var/lock/subsys/automount ]; then
+ echo "Automounter not running"
+ exit 1
+ fi
+ echo "Checking for changes to /etc/auto.master ...."
+ TMP1=`tempfile`;
+ TMP2=`tempfile`;
+ getmounts >$TMP1
+ ps ax|grep "[0-9]:[0-9][0-9] automount " | (
+ while read pid tt stat time command; do
+ echo "$command" >>$TMP2
+ if ! grep -q "^$command" $TMP2; then
+ kill -USR2 $pid
+ echo "Stop $command"
+ fi
+ done
+ )
+ cat $TMP1 | ( while read x; do
+ if ! grep -q "^$x" $TMP2; then
+ $x
+ echo "Start $x"
+ fi
+ done )
+ rm $TMP1 $TMP2
+ ;;
+ status)
+ status
+ ;;
+ *)
+ echo "Usage: /etc/init.d/autofs {start|stop|restart|reload|status}"
+ exit 1
+esac
+}
+
+#
+# Debian start/stop functions.
+#
+function debian()
+{
+
+DAEMON=/usr/sbin/automount
+
+#
+# See how we were called.
+#
+case "$1" in
+ start)
+ echo -n 'Starting automounter:'
+ getmounts | while read cmd mnt rest
+ do
+ echo -n " $mnt"
+ pidfile=/var/run/automount`echo $mnt | sed 's/\//./'`.pid
+ start-stop-daemon --start --pidfile $pidfile --quiet \
+ --exec $DAEMON -- $mnt $rest
+ #
+ # Automount needs a '--pidfile' or '-p' option.
+ # For now we look for the pid ourself.
+ #
+ ps ax | grep "[0-9]:[0-9][0-9] $DAEMON $mnt" | (
+ read pid rest
+ echo $pid > $pidfile
+ echo "$mnt $rest" >> $pidfile
+ )
+ done
+ echo "."
+ ;;
+ stop)
+ echo 'Stopping automounter.'
+ start-stop-daemon --stop --quiet --signal USR2 --exec $DAEMON
+ ;;
+ reload|restart)
+ echo "Reloading automounter: checking for changes ... "
+ TMP=/var/run/automount.tmp
+ getmounts >$TMP
+ for i in /var/run/automount.*.pid
+ do
+ pid=`head -n 1 $i 2>/dev/null`
+ [ "$pid" = "" ] && continue
+ command=`tail +2 $i`
+ if ! grep -q "^$command" $TMP
+ then
+ echo "Stopping automounter: $command"
+ kill -USR2 $pid
+ fi
+ done
+ rm -f $TMP
+ /etc/init.d/autofs start
+ ;;
+ status)
+ status
+ ;;
+ *)
+ echo "Usage: /etc/init.d/autofs {start|stop|restart|reload|status}" >&2
+ exit 1
+ ;;
+esac
+}
+
+if [ -f /etc/debian_version ]
+then
+ debian "$@"
+else
+ redhat "$@"
+fi
+
+exit 0